1
#include"filetransdlg.h"
5
#include<qpushbutton.h>
7
#include<qfiledialog.h>
9
#include<qprogressbar.h>
18
#include<qpopupmenu.h>
21
#include"psiaccount.h"
24
#include"iconwidget.h"
25
#include"busywidget.h"
26
#include"filetransfer.h"
28
#include"psiiconset.h"
31
#if QT_VERSION >= 0x030200
32
typedef Q_UINT64 LARGE_TYPE;
34
typedef Q_UINT32 LARGE_TYPE;
37
#define CSMAX (sizeof(LARGE_TYPE)*8)
39
static int calcShift(Q_LLONG big)
43
for(int n = CSMAX - CSMIN; n > 0; --n) {
51
static int calcComplement(Q_LLONG big, int shift)
53
int block = 1 << shift;
54
Q_LLONG rem = big % block;
58
return (block - (int)rem);
61
static int calcTotalSteps(Q_LLONG big, int shift)
65
return ((big - 1) >> shift) + 1;
68
static int calcProgressStep(Q_LLONG big, int complement, int shift)
70
return ((big + complement) >> shift);
73
static QStringList *activeFiles = 0;
75
static void active_file_add(const QString &s)
78
activeFiles = new QStringList;
79
activeFiles->append(s);
80
//printf("added: [%s]\n", s.latin1());
83
static void active_file_remove(const QString &s)
87
activeFiles->remove(s);
88
//printf("removed: [%s]\n", s.latin1());
91
static bool active_file_check(const QString &s)
95
return activeFiles->contains(s);
98
static QString clean_filename(const QString &s)
101
QString badchars = "\\/|?*:\"<>";
103
for(uint n = 0; n < s.length(); ++n) {
105
for(uint b = 0; b < badchars.length(); ++b) {
106
if(s.at(n) == badchars.at(b)) {
122
//----------------------------------------------------------------------------
123
// FileTransferHandler
124
//----------------------------------------------------------------------------
125
class FileTransferHandler::Private
132
QString fileName, saveName;
133
Q_LLONG fileSize, sent, offset, length;
142
FileTransferHandler::FileTransferHandler(PsiAccount *pa, FileTransfer *ft)
150
d->peer = ft->peer();
151
d->fileName = clean_filename(ft->fileName());
152
d->fileSize = ft->fileSize();
153
d->desc = ft->description();
154
d->shift = calcShift(d->fileSize);
155
d->complement = calcComplement(d->fileSize, d->shift);
157
Jid proxy = d->pa->userAccount().dtProxy;
159
d->ft->setProxy(proxy);
168
FileTransferHandler::~FileTransferHandler()
170
if(!d->activeFile.isEmpty())
171
active_file_remove(d->activeFile);
180
void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
187
d->fileName = fi.fileName();
188
d->fileSize = fi.size(); // TODO: large file support
190
d->shift = calcShift(d->fileSize);
191
d->complement = calcComplement(d->fileSize, d->shift);
193
d->ft = d->pa->client()->fileTransferManager()->createTransfer();
194
Jid proxy = d->pa->userAccount().dtProxy;
196
d->ft->setProxy(proxy);
200
d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc);
203
PsiAccount *FileTransferHandler::account() const
208
int FileTransferHandler::mode() const
210
return (d->sending ? Sending : Receiving);
213
Jid FileTransferHandler::peer() const
218
QString FileTransferHandler::fileName() const
223
Q_LLONG FileTransferHandler::fileSize() const
228
QString FileTransferHandler::description() const
233
Q_LLONG FileTransferHandler::offset() const
238
int FileTransferHandler::totalSteps() const
240
return calcTotalSteps(d->fileSize, d->shift);
243
bool FileTransferHandler::resumeSupported() const
246
return d->ft->rangeSupported();
251
QString FileTransferHandler::saveName() const
256
void FileTransferHandler::accept(const QString &saveName, const QString &fileName, Q_LLONG offset)
260
d->fileName = fileName;
261
d->saveName = saveName;
263
d->length = d->fileSize;
264
d->f.setName(saveName);
265
d->ft->accept(offset);
268
void FileTransferHandler::s5b_proxyQuery()
270
statusMessage(tr("Quering proxy..."));
273
void FileTransferHandler::s5b_proxyResult(bool b)
276
statusMessage(tr("Proxy query successful."));
278
statusMessage(tr("Proxy query failed!"));
281
void FileTransferHandler::s5b_requesting()
283
statusMessage(tr("Requesting data transfer channel..."));
286
void FileTransferHandler::s5b_accepted()
288
statusMessage(tr("Peer accepted request."));
291
void FileTransferHandler::s5b_tryingHosts(const StreamHostList &)
293
statusMessage(tr("Connecting to peer..."));
296
void FileTransferHandler::s5b_proxyConnect()
298
statusMessage(tr("Connecting to proxy..."));
301
void FileTransferHandler::s5b_waitingForActivation()
303
statusMessage(tr("Waiting for peer activation..."));
306
void FileTransferHandler::ft_accepted()
308
d->offset = d->ft->offset();
309
d->length = d->ft->length();
311
d->c = d->ft->s5bConnection();
312
connect(d->c, SIGNAL(proxyQuery()), SLOT(s5b_proxyQuery()));
313
connect(d->c, SIGNAL(proxyResult(bool)), SLOT(s5b_proxyResult(bool)));
314
connect(d->c, SIGNAL(requesting()), SLOT(s5b_requesting()));
315
connect(d->c, SIGNAL(accepted()), SLOT(s5b_accepted()));
316
connect(d->c, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(s5b_tryingHosts(const StreamHostList &)));
317
connect(d->c, SIGNAL(proxyConnect()), SLOT(s5b_proxyConnect()));
318
connect(d->c, SIGNAL(waitingForActivation()), SLOT(s5b_waitingForActivation()));
324
void FileTransferHandler::ft_connected()
329
// open the file, and set the correct offset
331
if(d->f.open(IO_ReadOnly)) {
336
if(d->f.at(d->offset))
343
error(ErrFile, 0, "");
347
if(d->sent == d->fileSize)
348
QTimer::singleShot(0, this, SLOT(doFinish()));
350
QTimer::singleShot(0, this, SLOT(trySend()));
353
// open the file, truncating if offset is zero, otherwise set the correct offset
354
int m = IO_ReadWrite;
363
if(d->f.at(d->offset))
370
error(ErrFile, 0, "");
374
d->activeFile = d->f.name();
375
active_file_add(d->activeFile);
377
// done already? this means a file size of zero
378
if(d->sent == d->fileSize)
379
QTimer::singleShot(0, this, SLOT(doFinish()));
385
void FileTransferHandler::ft_readyRead(const QByteArray &a)
388
//printf("%d bytes read\n", a.size());
389
int r = d->f.writeBlock(a.data(), a.size());
394
error(ErrFile, 0, "");
402
void FileTransferHandler::ft_bytesWritten(int x)
405
//printf("%d bytes written\n", x);
407
if(d->sent == d->fileSize) {
413
QTimer::singleShot(0, this, SLOT(trySend()));
414
progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
418
void FileTransferHandler::ft_error(int x)
425
if(x == FileTransfer::ErrReject)
426
error(ErrReject, x, "");
427
else if(x == FileTransfer::ErrNeg)
428
error(ErrTransfer, x, tr("Unable to negotiate transfer."));
429
else if(x == FileTransfer::ErrConnect)
430
error(ErrTransfer, x, tr("Unable to connect to peer for data transfer."));
431
else if(x == FileTransfer::ErrProxy)
432
error(ErrTransfer, x, tr("Unable to connect to proxy for data transfer."));
433
else if(x == FileTransfer::ErrStream)
434
error(ErrTransfer, x, tr("Lost connection / Cancelled."));
437
void FileTransferHandler::trySend()
439
int blockSize = d->ft->dataSizeNeeded();
440
QByteArray a(blockSize);
441
int r = d->f.readBlock(a.data(), a.size());
446
error(ErrFile, 0, "");
449
if(r < (int)a.size())
451
d->ft->writeFileData(a);
454
void FileTransferHandler::doFinish()
456
if(d->sent == d->fileSize) {
461
progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
464
void FileTransferHandler::mapSignals()
466
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
467
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
468
connect(d->ft, SIGNAL(readyRead(const QByteArray &)), SLOT(ft_readyRead(const QByteArray &)));
469
connect(d->ft, SIGNAL(bytesWritten(int)), SLOT(ft_bytesWritten(int)));
470
connect(d->ft, SIGNAL(error(int)), SLOT(ft_error(int)));
473
//----------------------------------------------------------------------------
475
//----------------------------------------------------------------------------
476
static QString lastPath, lastSavePath;
478
class FileRequestDlg::Private
483
AccountsComboBox *cb_ident;
484
QLabel *lb_ident, *lb_time;
487
FileTransferHandler *ft;
495
FileRequestDlg::FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa)
496
:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
499
FileRequestDlg(j, psi, pa, l);
503
FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa, const QStringList& files)
504
:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
514
QHBox *hb = new QHBox(this);
515
new QLabel(tr("Identity: "), hb);
516
d->cb_ident = d->psi->accountsComboBox(hb);
517
connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
518
d->cb_ident->setAccount(pa);
519
replaceWidget(lb_accountlabel, hb);
520
setTabOrder(d->cb_ident, le_to);
522
d->te = new ChatView(this);
523
d->te->setReadOnly(false);
524
d->te->setTextFormat(PlainText);
525
replaceWidget(te_desc, d->te);
526
setTabOrder(le_fname, d->te);
527
setTabOrder(d->te, pb_stop);
529
setCaption(tr("Send File"));
531
setIcon(IconsetFactory::icon("psi/upload"));
534
le_to->setText(d->jid.full());
535
le_to->setReadOnly(false);
536
pb_start->setText(tr("&Send"));
537
pb_stop->setText(tr("&Close"));
539
connect(tb_browse, SIGNAL(clicked()), SLOT(chooseFile()));
540
connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
541
connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
543
lb_status->setText(tr("Ready"));
546
d->psi->dialogRegister(this);
548
if (files.isEmpty()) {
549
QTimer::singleShot(0, this, SLOT(chooseFile()));
552
// TODO: Once sending of multiple files is supported, change this
553
QFileInfo fi(files.first());
555
// Check if the file is legal
557
QMessageBox::critical(this, tr("Error"), QString("The file '%1' does not exist.").arg(files.first()));
558
QTimer::singleShot(0, this, SLOT(reject()));
562
QMessageBox::critical(this, tr("Error"), tr("Sending folders is not supported."));
563
QTimer::singleShot(0, this, SLOT(reject()));
567
lastPath = fi.dirPath();
568
le_fname->setText(QDir::convertSeparators(fi.filePath()));
569
lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
573
FileRequestDlg::FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa)
574
:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
580
d->ft = new FileTransferHandler(pa, ft);
584
d->fileName = ft->fileName();
585
d->fileSize = ft->fileSize();
588
QHBox *hb = new QHBox(this);
589
new QLabel(tr("Identity: "), hb);
590
d->lb_ident = d->pa->accountLabel(hb);
591
d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
592
new QLabel(tr("Time:"), hb);
593
d->lb_time = new QLabel(ts.time().toString(LocalDate), hb);
594
d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
596
replaceWidget(lb_accountlabel, hb);
598
d->te = new ChatView(this);
599
d->te->setTextFormat(PlainText);
600
replaceWidget(te_desc, d->te);
601
setTabOrder(le_fname, d->te);
602
setTabOrder(d->te, pb_stop);
604
lb_to->setText(tr("From:"));
605
setCaption(tr("Receive File"));
607
setIcon(IconsetFactory::icon("psi/download"));
610
le_to->setText(d->jid.full());
611
le_fname->setText(d->fileName);
612
lb_size->setText(tr("%1 byte(s)").arg(d->fileSize));
613
d->te->setReadOnly(true);
614
d->te->setText(ft->description());
615
pb_start->setText(tr("&Accept"));
616
pb_stop->setText(tr("&Reject"));
620
connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
621
connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
623
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
625
lb_status->setText(tr("Ready"));
627
pb_start->setFocus();
628
d->pa->dialogRegister(this);
631
FileRequestDlg::~FileRequestDlg()
635
d->psi->dialogUnregister(this);
637
d->pa->dialogUnregister(this);
641
void FileRequestDlg::done(int r)
643
if(busy->isActive()) {
644
int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the transfer?"), tr("&Yes"), tr("&No"));
648
// close/reject FT if there is one
658
void FileRequestDlg::keyPressEvent(QKeyEvent *e)
660
if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) ) {
661
if(pb_start->isEnabled())
665
QDialog::keyPressEvent(e);
668
void FileRequestDlg::updateIdentity(PsiAccount *pa)
671
disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
679
connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
682
void FileRequestDlg::pa_disconnected()
684
//if(busy->isActive()) {
690
void FileRequestDlg::blockWidgets()
693
d->cb_ident->setEnabled(false);
694
le_to->setEnabled(false);
695
le_fname->setEnabled(false);
696
tb_browse->setEnabled(false);
697
d->te->setEnabled(false);
698
pb_start->setEnabled(false);
701
void FileRequestDlg::unblockWidgets()
704
d->cb_ident->setEnabled(true);
705
le_to->setEnabled(true);
706
le_fname->setEnabled(true);
707
tb_browse->setEnabled(true);
708
d->te->setEnabled(true);
709
pb_start->setEnabled(true);
712
void FileRequestDlg::chooseFile()
715
if(lastPath.isEmpty())
716
lastPath = QDir::homeDirPath();
717
QString str = QFileDialog::getOpenFileName(lastPath, tr("All files (*)"), this, 0, tr("Choose a file"));
721
QMessageBox::information(this, tr("Error"), tr("The file specified does not exist."));
724
lastPath = fi.dirPath();
725
le_fname->setText(QDir::convertSeparators(fi.filePath()));
726
lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
732
void FileRequestDlg::doStart()
734
if(!d->pa->checkConnected(this))
738
Jid to = le_to->text();
740
QMessageBox::information(this, tr("Error"), tr("The Jabber ID specified is not valid. Correct this and try again."));
744
QFileInfo fi(le_fname->text());
746
QMessageBox::information(this, tr("Error"), tr("The file specified does not exist. Choose a correct file name before sending."));
752
pb_stop->setText(tr("&Cancel"));
755
lb_status->setText(tr("Requesting..."));
757
d->fileName = fi.fileName();
758
d->fileSize = fi.size(); // TODO: large file support
760
d->ft = new FileTransferHandler(d->pa);
761
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
762
connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
763
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
764
connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
765
d->ft->send(le_to->text(), le_fname->text(), d->te->text());
768
QString fname, savename;
769
bool overwrite = false;
771
if(lastSavePath.isEmpty())
772
lastSavePath = QDir::homeDirPath();
773
fname = QFileDialog::getSaveFileName(QDir(lastSavePath).filePath(d->fileName), tr("All files (*)"), this, 0, tr("Save As"));
774
if(!fname.isEmpty()) {
777
int x = QMessageBox::information(this, tr("Error"), tr("File already exists. Overwrite?"), tr("&Yes"), tr("&No"));
782
lastSavePath = fi.dirPath();
783
savename = fname + ".part";
784
fname = fi.fileName();
792
if(active_file_check(savename)) {
793
QMessageBox::information(this, tr("Error"), tr("This file is being transferred already!"));
797
Q_LLONG resume_offset = 0;
799
// supports resume? check for a .part
800
if(d->ft->resumeSupported()) {
801
QFileInfo fi(savename);
803
resume_offset = fi.size();
807
pb_start->setEnabled(false);
809
le_fname->setText(fname);
810
pb_stop->setText(tr("&Cancel"));
813
lb_status->setText(tr("Accepting..."));
815
d->t.start(30000, true);
817
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
818
connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
819
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
820
connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
821
d->ft->accept(savename, fname, resume_offset);
825
void FileRequestDlg::ft_accepted()
827
lb_status->setText(tr("Accepted!"));
830
void FileRequestDlg::ft_statusMessage(const QString &s)
832
lb_status->setText(s);
834
// stop the timer at first activity
839
void FileRequestDlg::ft_connected()
843
FileTransDlg *w = d->pa->psi()->ftdlg();
844
FileTransferHandler *h = d->ft;
850
w->takeTransfer(h, 0, 0);
853
void FileRequestDlg::ft_error(int x, int fx, const QString &)
865
pb_stop->setText(tr("&Close"));
866
lb_status->setText(tr("Ready"));
870
if(x == FileTransferHandler::ErrReject)
871
str = tr("File was rejected by remote user.");
872
else if(x == FileTransferHandler::ErrTransfer) {
873
if(fx == FileTransfer::ErrNeg)
875
"Unable to negotiate transfer.\n\n"
876
"This can happen if the contact did not understand our request, or if the\n"
877
"contact is offline."
879
else if(fx == FileTransfer::ErrConnect)
881
"Unable to connect to peer for data transfer.\n\n"
882
"Ensure that your Data Transfer settings are proper. If you are behind\n"
883
"a NAT router or firewall then you'll need to open the proper TCP port\n"
884
"or specify a Data Transfer Proxy in your account settings."
886
else if(fx == FileTransfer::ErrProxy)
888
"Failure to either connect to, or activate, the Data Transfer Proxy.\n\n"
889
"This means that the Proxy service is either not functioning or it is\n"
890
"unreachable. If you are behind a firewall, then you'll need to ensure\n"
891
"that outgoing TCP connections are allowed."
895
str = tr("File I/O error");
896
QMessageBox::information(this, tr("Error"), str);
898
if(!d->sending || x == FileTransferHandler::ErrReject)
902
void FileRequestDlg::t_timeout()
910
QString str = tr("Unable to accept the file. Perhaps the sender has cancelled the request.");
911
QMessageBox::information(this, tr("Error"), str);
915
//----------------------------------------------------------------------------
917
//----------------------------------------------------------------------------
918
class FileTransItem : public QListViewItem
936
FileTransItem(QListView *parent, const QString &_name, Q_LLONG _size, const QString &_peer, bool _sending)
937
:QListViewItem(parent)
944
rate = FileTransDlg::tr("N/A");
950
void niceUnit(Q_LLONG n, Q_LLONG *div, QString *unit)
952
Q_LLONG gb = 1024 * 1024 * 1024;
953
Q_LLONG mb = 1024 * 1024;
957
*unit = QString("GB");
961
*unit = QString("MB");
965
*unit = QString("KB");
969
*unit = QString("B");
973
QString roundedNumber(Q_LLONG n, Q_LLONG div)
975
bool decimal = false;
980
Q_LLONG x_long = n / div;
983
double f = (double)x;
985
return QString::number(f, 'f', 1);
988
return QString::number(x);
991
bool setProgress(int _progress, Q_LLONG _sent, int _bps)
993
progress = _progress;
998
Q_LLONG rest_long = size - sent;
1000
int maxtime = (23 * 60 * 60) + (59 * 60) + (59); // 23h59m59s
1001
if(rest_long > maxtime)
1002
rest_long = maxtime;
1003
timeRemaining = (int)rest_long;
1006
int lastDist = dist;
1007
dist = progressBarDist(progressBarWidth());
1008
if(dist != lastDist)
1020
niceUnit(size, &div, &unit);
1022
s = roundedNumber(sent, div) + '/' + roundedNumber(size, div) + unit;
1026
s += QString(" ") + FileTransDlg::tr("[Done]");
1028
s += QString(" ") + FileTransDlg::tr("[Error: %1]").arg(error);
1033
s += QString(" ") + FileTransDlg::tr("[Stalled]");
1035
niceUnit(bps, &div, &unit);
1036
s += QString(" @ ") + FileTransDlg::tr("%1%2/s").arg(roundedNumber(bps, div)).arg(unit);
1039
QTime t = QTime().addSecs(timeRemaining);
1040
s += FileTransDlg::tr("%1h%2m%3s remaining").arg(t.hour()).arg(t.minute()).arg(t.second());
1046
int progressBarWidth() const
1049
int w = listView()->columnWidth(0);
1050
//int pw = (w - (3 * m)) / 2;
1051
int pw = (w - (3 * m)) * 2 / 3;
1055
int progressBarDist(int width) const
1057
int xsize = width - 2;
1058
return (progress * xsize / 8192);
1061
void drawProgressBar(QPainter *p, const QColorGroup &cg, int x, int y, int width, int height) const
1065
p->setPen(cg.highlightedText());
1067
p->setPen(cg.text());
1068
p->drawRect(x, y, width, height);
1071
int xsize = width - 2;
1072
int ysize = height - 2;
1074
int dist = progressBarDist(width);
1075
p->fillRect(xoff, yoff, dist, ysize, cg.brush(QColorGroup::Highlight));
1076
p->fillRect(xoff + dist, yoff, width - 2 - dist, ysize, cg.brush(QColorGroup::Base));
1078
int percent = progress * 100 / 8192;
1079
QString s = QString::number(percent) + '%';
1081
QFontMetrics fm(p->font());
1082
int ty = ((height - fm.height()) / 2) + fm.ascent() + y;
1083
int textwidth = fm.width(s);
1084
int center = xoff + (xsize / 2);
1087
p->setPen(cg.highlightedText());
1088
p->setClipRect(xoff, yoff, dist, ysize, QPainter::CoordPainter);
1089
p->drawText(center - (textwidth / 2), ty, s);
1093
p->setPen(cg.text());
1094
p->setClipRect(xoff + dist, yoff, width - 2 - dist, ysize, QPainter::CoordPainter);
1095
p->drawText(center - (textwidth / 2), ty, s);
1103
QListView *lv = listView();
1105
QFontMetrics fm = lv->fontMetrics();
1108
int ph = fm.height() + 2 + (pm * 2);
1109
int h = (ph * 2) + (m * 3);
1111
h += lv->itemMargin() * 2;
1113
// ensure an even number
1120
QString chopString(const QString &s, const QFontMetrics &fm, int len) const
1122
if(fm.width(s) <= len)
1126
uint n = s.length();
1128
str = s.mid(0, --n) + "...";
1129
} while(n > 0 && fm.width(str) > len);
1133
void paintCell(QPainter *mp, const QColorGroup &_cg, int, int width, int)
1135
QColorGroup cg = _cg;
1139
// tint the background
1140
/*//QColor base = Qt::black; //cg.base();
1141
QColor base = Qt::white;
1142
int red = base.red();
1143
int green = base.green();
1144
int blue = base.blue();
1145
bool light = false;//true;
1148
green = green * 15 / 16;
1149
blue = blue * 15 / 16;
1153
red = red * 15 / 16;
1159
red = red * 15 / 16;
1160
blue = blue * 15 / 16;
1163
green = 255 - green;
1164
green = green * 15 / 16;
1165
green = 255 - green;
1168
base.setRgb(red, green, blue);
1169
cg.setColor(QColorGroup::Base, base);*/
1172
QPainter *p = new QPainter(&pix);
1173
QFont font = mp->font();
1174
QFont boldFont = font;
1175
boldFont.setBold(true);
1176
QFontMetrics fm(font);
1177
QFontMetrics fmbold(boldFont);
1180
// m = margin, pm = progress margin, ph = progress height, yoff = text y offset,
1181
// tt = text top, tb = text bottom, pw = progress width, px = progress x coord
1184
int ph = fm.height() + 2 + (pm * 2);
1186
int tt = m + yoff + fm.ascent();
1187
int tb = (m * 2) + ph + yoff + fm.ascent();
1188
//int pw = (w - (3 * m)) / 2;
1189
int pw = (w - (3 * m)) * 2 / 3;
1190
int tw = (w - (3 * m)) - pw;
1191
int px = (m * 2) + tw;
1193
// clear out the background
1195
br = cg.brush(QColorGroup::Highlight);
1197
br = cg.brush(QColorGroup::Base);
1198
p->fillRect(0, 0, width, h, br);
1201
p->drawPixmap(m, m + yoff, icon);
1202
int tm = m + icon.width() + 4;
1203
tw = tw - (icon.width() + 4);
1207
p->setPen(cg.highlightedText());
1209
p->setPen(cg.text());
1210
p->setFont(boldFont);
1211
QString s1 = FileTransDlg::tr("File") + ": ";
1212
QString s2 = FileTransDlg::tr("To") + ": ";
1213
QString s3 = FileTransDlg::tr("From") + ": ";
1215
int lw = QMAX(QMAX(fmbold.width(s1), fmbold.width(s2)), fmbold.width(s3));
1217
p->drawText(tm, tt, s1);
1218
p->drawText(tm, tb, sending ? s2 : s3);
1220
p->drawText(tm + lw, tt, chopString(name, fm, left));
1221
p->drawText(tm + lw, tb, chopString(peer, fm, left));
1224
p->setFont(boldFont);
1225
s1 = FileTransDlg::tr("Status") + ": ";
1226
lw = fmbold.width(s1);
1228
p->drawText(px, tb, s1);
1230
p->drawText(px + lw, tb, chopString(rate, fm, left));
1233
drawProgressBar(p, cg, px, m, pw, ph);
1236
mp->drawPixmap(0, 0, pix);
1239
QString makeTip() const
1241
QTime t = QTime().addSecs(timeRemaining);
1242
QString rem = FileTransDlg::tr("%1h%2m%3s").arg(t.hour()).arg(t.minute()).arg(t.second());
1245
s += FileTransDlg::tr("Filename") + QString(": %1").arg(name);
1246
s += QString("\n") + FileTransDlg::tr("Type") + QString(": %1").arg(sending ? FileTransDlg::tr("Upload") : FileTransDlg::tr("Download"));
1247
s += QString("\n") + FileTransDlg::tr("Peer") + QString(": %1").arg(peer);
1248
s += QString("\n") + FileTransDlg::tr("Size") + QString(": %1").arg(size);
1250
s += QString("\n") + FileTransDlg::tr("[Done]");
1253
s += QString("\n") + FileTransDlg::tr("Transferred") + QString(": %1").arg(sent);
1255
s += QString("\n") + FileTransDlg::tr("Time remaining") + QString(": %1").arg(rem);
1262
class FileTransView : public QListView, public QToolTip
1266
FileTransView(QWidget *parent=0, const char *name=0)
1267
:QListView(parent, name), QToolTip(viewport())
1269
connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this, SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
1272
void maybeTip(const QPoint &pos)
1274
FileTransItem *i = static_cast<FileTransItem*>(itemAt(pos));
1277
QRect r(itemRect(i));
1278
tip(r, i->makeTip());
1281
void resizeEvent(QResizeEvent *e)
1283
QListView::resizeEvent(e);
1285
if(e->oldSize().width() != e->size().width())
1290
void itemCancel(int id);
1291
void itemOpenDest(int id);
1292
void itemClear(int id);
1295
void qlv_contextMenuRequested(QListViewItem *item, const QPoint &pos, int)
1300
FileTransItem *i = static_cast<FileTransItem*>(item);
1303
p.insertItem(tr("&Cancel"), 0);
1304
p.insertSeparator();
1305
//p.insertItem(tr("&Open Destination Folder"), 1);
1306
p.insertItem(tr("Cl&ear"), 2);
1309
p.setItemEnabled(0, false);
1312
//p.setItemEnabled(1, false);
1313
p.setItemEnabled(2, false);
1316
int x = p.exec(pos);
1318
// TODO: what if item is deleted during exec?
1325
itemOpenDest(i->id);
1333
QListViewItemIterator it(this);
1334
for(QListViewItem *i; (i = it.current()); ++it)
1339
class TransferMapping
1342
FileTransferHandler *h;
1363
// if we're at the end, shift down to make room
1365
for(int n = 0; n < at - 1; ++n)
1366
last[n] = last[n + 1];
1373
class FileTransDlg::Private
1376
FileTransDlg *parent;
1379
QPtrList<TransferMapping> transferList;
1382
Private(FileTransDlg *_parent)
1385
transferList.setAutoDelete(true);
1393
QListViewItemIterator it(lv);
1394
for(QListViewItem *i; (i = it.current()); ++it) {
1395
FileTransItem *fi = static_cast<FileTransItem*>(i);
1408
FileTransItem *findItem(int id)
1410
QListViewItemIterator it(lv);
1411
for(QListViewItem *i; (i = it.current()); ++it) {
1412
FileTransItem *fi = static_cast<FileTransItem*>(i);
1419
QPtrList<FileTransItem> getFinished()
1421
QPtrList<FileTransItem> list;
1422
QListViewItemIterator it(lv);
1423
for(QListViewItem *i; (i = it.current()); ++it) {
1424
FileTransItem *fi = static_cast<FileTransItem*>(i);
1431
TransferMapping *findMapping(FileTransferHandler *h)
1433
QPtrListIterator<TransferMapping> it(transferList);
1434
for(TransferMapping *i; (i = it.current()); ++it) {
1441
TransferMapping *findMapping(int id)
1443
QPtrListIterator<TransferMapping> it(transferList);
1444
for(TransferMapping *i; (i = it.current()); ++it) {
1451
void updateProgress(TransferMapping *i, bool updateAll=true)
1453
bool done = (i->p == i->h->totalSteps());
1457
if(!done && i->at >= 2) {
1458
int seconds = i->at - 1;
1459
Q_LLONG average = i->last[i->at-1] - i->last[0];
1460
bps = ((int)average) / seconds;
1464
FileTransItem *fi = findItem(i->id);
1468
parent->setProgress(i->id, i->p, i->h->totalSteps(), i->sent, bps, updateAll);
1471
bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1472
QString fname, savename;
1474
fname = i->h->fileName();
1475
savename = i->h->saveName();
1478
PsiAccount *pa = i->h->account();
1479
transferList.removeRef(i);
1482
//printf("fname: [%s], savename: [%s]\n", fname.latin1(), savename.latin1());
1484
// rename .part to original filename
1485
QFileInfo fi(savename);
1486
QDir dir = fi.dir();
1487
if(dir.exists(fname))
1489
if(!dir.rename(fi.fileName(), fname)) {
1490
// TODO: display some error about renaming
1494
pa->playSound(option.onevent[eFTComplete]);
1499
FileTransDlg::FileTransDlg(PsiCon *psi)
1500
:QDialog(0, 0, false, psi_dialog_flags)
1502
d = new Private(this);
1504
//d->psi->dialogRegister(this);
1506
connect(&d->t, SIGNAL(timeout()), SLOT(updateItems()));
1508
setCaption(tr("Transfer Manager"));
1510
setIcon(IconsetFactory::icon("psi/filemanager"));
1513
QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
1514
d->lv = new FileTransView(this);
1515
connect(d->lv, SIGNAL(itemCancel(int)), SLOT(itemCancel(int)));
1516
connect(d->lv, SIGNAL(itemOpenDest(int)), SLOT(itemOpenDest(int)));
1517
connect(d->lv, SIGNAL(itemClear(int)), SLOT(itemClear(int)));
1518
vb->addWidget(d->lv);
1519
QHBoxLayout *hb = new QHBoxLayout(vb);
1521
QPushButton *pb_clear = new QPushButton(tr("Clear &Finished"), this);
1522
connect(pb_clear, SIGNAL(clicked()), SLOT(clearFinished()));
1523
hb->addWidget(pb_clear);
1524
QPushButton *pb_close = new QPushButton(tr("&Hide"), this);
1525
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
1526
hb->addWidget(pb_close);
1528
pb_close->setDefault(true);
1529
pb_close->setFocus();
1531
d->lv->addColumn("");
1532
d->lv->header()->hide();
1534
d->lv->setResizeMode(QListView::LastColumn);
1535
d->lv->setAllColumnsShowFocus(true);
1536
d->lv->setSorting(-1);
1541
FileTransDlg::~FileTransDlg()
1543
//d->psi->dialogUnregister(this);
1547
int FileTransDlg::addItem(const QString &filename, Q_LLONG size, const QString &peer, bool sending)
1549
int id = d->findFreeId();
1550
FileTransItem *i = new FileTransItem(d->lv, filename, size, peer, sending);
1552
i->icon = IconsetFactory::icon("psi/upload").impix().pixmap();
1554
i->icon = IconsetFactory::icon("psi/download").impix().pixmap();
1560
void FileTransDlg::setProgress(int id, int step, int total, Q_LLONG sent, int bytesPerSecond, bool updateAll)
1562
FileTransItem *i = d->findItem(id);
1564
// convert steps/total into a word
1567
progress = step * 8192 / total;
1571
bool do_repaint = i->setProgress(progress, sent, bytesPerSecond);
1581
void FileTransDlg::removeItem(int id)
1583
FileTransItem *i = d->findItem(id);
1586
if(d->lv->childCount() == 0)
1590
void FileTransDlg::setError(int id, const QString &reason)
1592
FileTransItem *i = d->findItem(id);
1599
d->lv->ensureItemVisible(i);
1600
QMessageBox::information(this, tr("Transfer Error"), tr("Transfer of %1 with %2 failed.\nReason: %3").arg(i->name).arg(i->peer).arg(reason));
1604
void FileTransDlg::takeTransfer(FileTransferHandler *h, int p, Q_LLONG sent)
1607
UserListItem *u = h->account()->findFirstRelavent(h->peer());
1609
peer = jidnick(u->jid().full(), u->name());
1611
peer = h->peer().full();
1613
TransferMapping *i = new TransferMapping;
1615
i->id = addItem(h->fileName(), h->fileSize(), peer, (h->mode() == FileTransferHandler::Sending));
1618
d->transferList.append(i);
1620
FileTransItem *fi = d->findItem(i->id);
1621
d->lv->ensureItemVisible(fi);
1623
if(p == i->h->totalSteps()) {
1624
d->updateProgress(i, true);
1627
connect(h, SIGNAL(progress(int, Q_LLONG)), SLOT(ft_progress(int, Q_LLONG)));
1628
connect(h, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
1632
void FileTransDlg::clearFinished()
1634
QPtrList<FileTransItem> list = d->getFinished();
1636
// remove related transfer mappings
1637
QPtrListIterator<FileTransItem> it(list);
1638
for(FileTransItem *fi; (fi = it.current()); ++it) {
1639
TransferMapping *i = d->findMapping(fi->id);
1640
d->transferList.removeRef(i);
1643
list.setAutoDelete(true);
1647
void FileTransDlg::ft_progress(int p, Q_LLONG sent)
1649
TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1652
if(p == i->h->totalSteps())
1653
d->updateProgress(i, true);
1655
d->updateProgress(i, false);
1658
void FileTransDlg::ft_error(int x, int, const QString &s)
1660
TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1662
d->transferList.removeRef(i);
1665
//if(x == FileTransferHandler::ErrReject)
1666
// str = tr("File was rejected by remote user.");
1667
if(x == FileTransferHandler::ErrTransfer)
1670
str = tr("File I/O error");
1674
void FileTransDlg::updateItems()
1676
// operate on a copy so that we can delete items in updateProgress
1677
QPtrList<TransferMapping> list = d->transferList;
1678
QPtrListIterator<TransferMapping> it(list);
1679
for(TransferMapping *i; (i = it.current()); ++it) {
1682
d->updateProgress(i);
1687
void FileTransDlg::itemCancel(int id)
1689
FileTransItem *fi = d->findItem(id);
1690
TransferMapping *i = d->findMapping(id);
1691
d->transferList.removeRef(i);
1695
void FileTransDlg::itemOpenDest(int id)
1697
TransferMapping *i = d->findMapping(id);
1700
bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1702
path = QFileInfo(i->h->saveName()).dirPath();
1704
path = QFileInfo(i->h->fileName()).dirPath();
1706
//printf("item open dest: [%s]\n", path.latin1());
1709
void FileTransDlg::itemClear(int id)
1711
FileTransItem *fi = d->findItem(id);
1712
TransferMapping *i = d->findMapping(id);
1713
d->transferList.removeRef(i);
1717
void FileTransDlg::killTransfers(PsiAccount *pa)
1719
QPtrList<TransferMapping> list = d->transferList;
1720
QPtrListIterator<TransferMapping> it(list);
1721
for(TransferMapping *i; (i = it.current()); ++it) {
1723
if(i->h->account() == pa) {
1724
FileTransItem *fi = d->findItem(i->id);
1725
d->transferList.removeRef(i);
1731
#include"filetransdlg.moc"