You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktorrent/plugins/infowidget/chunkbar.cpp

313 lines
7.9 KiB

/***************************************************************************
* Copyright (C) 2005 by *
* Joris Guisson <joris.guisson@gmail.com> *
* Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include <qpainter.h>
#include <qpen.h>
#include <qbrush.h>
#include <qvaluelist.h>
#include <qpixmap.h>
#include <math.h>
#include <qtooltip.h>
#include <klocale.h>
#include <qmime.h>
#include <qimage.h>
#include <util/log.h>
#include <interfaces/torrentinterface.h>
#include <util/bitset.h>
#include <torrent/globals.h>
#include "chunkbar.h"
using namespace bt;
using namespace kt;
namespace kt
{
struct Range
{
int first,last;
int fac;
};
static void FillAndFrameBlack(QImage* image, uint color, int size)
{
image->fill(color);
for (int i = 0; i < size; i++)
{
image->setPixel(0, i, 0);
image->setPixel(size - 1, i, 0);
image->setPixel(i, 0, 0);
image->setPixel(i, size - 1, 0);
}
}
static void InitializeToolTipImages(ChunkBar* bar)
{
static bool images_initialized = false;
if (images_initialized)
return;
images_initialized = true;
QMimeSourceFactory* factory = QMimeSourceFactory::defaultFactory();
QImage excluded(16, 16, 32);
FillAndFrameBlack(&excluded, bar->colorGroup().color(QColorGroup::Mid).pixel(), 16);
factory->setImage("excluded_color", excluded);
QImage available(16, 16, 32);
FillAndFrameBlack(&available, bar->colorGroup().highlight().pixel(), 16);
factory->setImage("available_color", available);
QImage unavailable(16, 16, 32);
FillAndFrameBlack(&unavailable, bar->colorGroup().base().pixel(), 16);
factory->setImage("unavailable_color", unavailable);
}
ChunkBar::ChunkBar(QWidget *parent, const char *name)
: QFrame(parent, name),curr_tc(0)
{
setFrameShape(StyledPanel);
setFrameShadow(Sunken);
setLineWidth(3);
setMidLineWidth(3);
show_excluded = false;
InitializeToolTipImages(this);
QToolTip::add(this, i18n("<img src=\"available_color\">&nbsp; - Downloaded Chunks<br><img src=\"unavailable_color\">&nbsp; - Chunks to Download<br><img src=\"excluded_color\">&nbsp; - Excluded Chunks"));
}
ChunkBar::~ChunkBar()
{}
void ChunkBar::updateBar()
{
const BitSet & bs = getBitSet();
QSize s = contentsRect().size();
bool changed = !(curr == bs);
if (show_excluded && curr_tc)
{
BitSet ebs = curr_tc->excludedChunksBitSet();
ebs.orBitSet(curr_tc->onlySeedChunksBitSet()),
changed = changed || !(curr_ebs == ebs);
curr_ebs = ebs;
}
if (changed || pixmap.isNull() || pixmap.width() != s.width())
{
// PROFILE("ChunkBar::updateBar");
// Out() << "Pixmap : " << s.width() << " " << s.height() << endl;
pixmap.resize(s);
pixmap.fill(colorGroup().color(QColorGroup::Base));
QPainter painter(&pixmap);
drawBarContents(&painter);
update();
}
}
void ChunkBar::drawContents(QPainter *p)
{
// first draw background
if (isEnabled())
p->setBrush(colorGroup().base());
else
p->setBrush(colorGroup().background());
p->setPen(Qt::NoPen);
p->drawRect(contentsRect());
if (isEnabled())
p->drawPixmap(contentsRect(),pixmap);
}
void ChunkBar::setTC(kt::TorrentInterface* tc)
{
curr_tc = tc;
QSize s = contentsRect().size();
//Out() << "Pixmap : " << s.width() << " " << s.height() << endl;
pixmap.resize(s);
pixmap.fill(colorGroup().color(QColorGroup::Base));
QPainter painter(&pixmap);
drawBarContents(&painter);
update();
}
void ChunkBar::drawBarContents(QPainter *p)
{
p->saveWorldMatrix();
if (curr_tc)
{
const TorrentStats & s = curr_tc->getStats();
Uint32 w = contentsRect().width();
const BitSet & bs = getBitSet();
curr = bs;
if (bs.allOn())
drawAllOn(p,colorGroup().highlight());
else if (s.total_chunks > w)
drawMoreChunksThenPixels(p,bs,colorGroup().highlight());
else
drawEqual(p,bs,colorGroup().highlight());
if (show_excluded && s.num_chunks_excluded > 0)
{
QColor c = colorGroup().color(QColorGroup::Mid);
if (curr_ebs.allOn())
drawAllOn(p,c);
else if (s.total_chunks > w)
drawMoreChunksThenPixels(p,curr_ebs,c);
else
drawEqual(p,curr_ebs,c);
}
}
p->restoreWorldMatrix();
}
void ChunkBar::drawEqual(QPainter *p,const BitSet & bs,const QColor & color)
{
//p->setPen(QPen(colorGroup().highlight(),1,Qt::SolidLine));
QColor c = color;
Uint32 w = contentsRect().width();
double scale = 1.0;
Uint32 total_chunks = curr_tc->getStats().total_chunks;
if (curr_tc->getStats().total_chunks != w)
scale = (double)w / total_chunks;
p->setPen(QPen(c,1,Qt::SolidLine));
p->setBrush(c);
QValueList<Range> rs;
for (Uint32 i = 0;i < bs.getNumBits();i++)
{
if (!bs.get(i))
continue;
if (rs.empty())
{
Range r = {i,i,0};
rs.append(r);
}
else
{
Range & l = rs.last();
if (l.last == int(i - 1))
{
l.last = i;
}
else
{
Range r = {i,i,0};
rs.append(r);
}
}
}
QRect r = contentsRect();
for (QValueList<Range>::iterator i = rs.begin();i != rs.end();++i)
{
Range & ra = *i;
int rw = ra.last - ra.first + 1;
p->drawRect((int)(scale * ra.first),0,(int)(rw * scale),r.height());
}
}
void ChunkBar::drawMoreChunksThenPixels(QPainter *p,const BitSet & bs,const QColor & color)
{
Uint32 w = contentsRect().width();
double chunks_per_pixel = (double)bs.getNumBits() / w;
QValueList<Range> rs;
for (Uint32 i = 0;i < w;i++)
{
Uint32 num_dl = 0;
Uint32 jStart = (Uint32) (i*chunks_per_pixel);
Uint32 jEnd = (Uint32) ((i+1)*chunks_per_pixel+0.5);
for (Uint32 j = jStart;j < jEnd;j++)
if (bs.get(j))
num_dl++;
if (num_dl == 0)
continue;
int fac = int(100*((double)num_dl / (jEnd - jStart)) + 0.5);
if (rs.empty())
{
Range r = {i,i,fac};
rs.append(r);
}
else
{
Range & l = rs.last();
if (l.last == int(i - 1) && l.fac == fac)
{
l.last = i;
}
else
{
Range r = {i,i,fac};
rs.append(r);
}
}
}
QRect r = contentsRect();
for (QValueList<Range>::iterator i = rs.begin();i != rs.end();++i)
{
Range & ra = *i;
int rw = ra.last - ra.first + 1;
int fac = ra.fac;
QColor c = color;
if (fac < 100)
{
// do some rounding off
if (fac <= 25)
fac = 25;
else if (fac <= 50)
fac = 45;
else
fac = 65;
c = color.light(200-fac);
}
p->setPen(QPen(c,1,Qt::SolidLine));
p->setBrush(c);
p->drawRect(ra.first,0,rw,r.height());
}
}
void ChunkBar::drawAllOn(QPainter *p,const QColor & color)
{
p->setPen(QPen(color,1,Qt::SolidLine));
p->setBrush(color);
QSize s = contentsRect().size();
p->drawRect(0,0,s.width(),s.height());
}
}
#include "chunkbar.moc"