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 QStringList *activeFiles = 0;
53
static void active_file_add(const QString &s)
56
activeFiles = new QStringList;
57
activeFiles->append(s);
58
//printf("added: [%s]\n", s.latin1());
61
static void active_file_remove(const QString &s)
65
activeFiles->remove(s);
66
//printf("removed: [%s]\n", s.latin1());
69
static bool active_file_check(const QString &s)
73
return activeFiles->contains(s);
76
static QString clean_filename(const QString &s)
79
QString badchars = "\\/|?*:\"<>";
81
for(uint n = 0; n < s.length(); ++n) {
83
for(uint b = 0; b < badchars.length(); ++b) {
84
if(s.at(n) == badchars.at(b)) {
100
//----------------------------------------------------------------------------
101
// FileTransferHandler
102
//----------------------------------------------------------------------------
103
class FileTransferHandler::Private
110
QString fileName, saveName;
111
Q_LLONG fileSize, sent, offset, length;
119
FileTransferHandler::FileTransferHandler(PsiAccount *pa, FileTransfer *ft)
127
d->peer = ft->peer();
128
d->fileName = clean_filename(ft->fileName());
129
d->fileSize = ft->fileSize();
130
d->desc = ft->description();
131
d->shift = calcShift(d->fileSize);
133
Jid proxy = d->pa->userAccount().dtProxy;
135
d->ft->setProxy(proxy);
144
FileTransferHandler::~FileTransferHandler()
146
if(!d->activeFile.isEmpty())
147
active_file_remove(d->activeFile);
156
void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
163
d->fileName = fi.fileName();
164
d->fileSize = fi.size(); // TODO: large file support
166
d->shift = calcShift(d->fileSize);
168
d->ft = d->pa->client()->fileTransferManager()->createTransfer();
169
Jid proxy = d->pa->userAccount().dtProxy;
171
d->ft->setProxy(proxy);
175
d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc);
178
PsiAccount *FileTransferHandler::account() const
183
int FileTransferHandler::mode() const
185
return (d->sending ? Sending : Receiving);
188
Jid FileTransferHandler::peer() const
193
QString FileTransferHandler::fileName() const
198
Q_LLONG FileTransferHandler::fileSize() const
203
QString FileTransferHandler::description() const
208
Q_LLONG FileTransferHandler::offset() const
213
int FileTransferHandler::totalSteps() const
215
return (d->fileSize >> d->shift);
218
bool FileTransferHandler::resumeSupported() const
221
return d->ft->rangeSupported();
226
QString FileTransferHandler::saveName() const
231
void FileTransferHandler::accept(const QString &saveName, const QString &fileName, Q_LLONG offset)
235
d->fileName = fileName;
236
d->saveName = saveName;
238
d->length = d->fileSize;
239
d->f.setName(saveName);
240
d->ft->accept(offset);
243
void FileTransferHandler::s5b_proxyQuery()
245
statusMessage(tr("Quering proxy..."));
248
void FileTransferHandler::s5b_proxyResult(bool b)
251
statusMessage(tr("Proxy query successful."));
253
statusMessage(tr("Proxy query failed!"));
256
void FileTransferHandler::s5b_requesting()
258
statusMessage(tr("Requesting data transfer channel..."));
261
void FileTransferHandler::s5b_accepted()
263
statusMessage(tr("Peer accepted request."));
266
void FileTransferHandler::s5b_tryingHosts(const StreamHostList &)
268
statusMessage(tr("Connecting to peer..."));
271
void FileTransferHandler::s5b_proxyConnect()
273
statusMessage(tr("Connecting to proxy..."));
276
void FileTransferHandler::s5b_waitingForActivation()
278
statusMessage(tr("Waiting for peer activation..."));
281
void FileTransferHandler::ft_accepted()
283
d->offset = d->ft->offset();
284
d->length = d->ft->length();
286
d->c = d->ft->s5bConnection();
287
connect(d->c, SIGNAL(proxyQuery()), SLOT(s5b_proxyQuery()));
288
connect(d->c, SIGNAL(proxyResult(bool)), SLOT(s5b_proxyResult(bool)));
289
connect(d->c, SIGNAL(requesting()), SLOT(s5b_requesting()));
290
connect(d->c, SIGNAL(accepted()), SLOT(s5b_accepted()));
291
connect(d->c, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(s5b_tryingHosts(const StreamHostList &)));
292
connect(d->c, SIGNAL(proxyConnect()), SLOT(s5b_proxyConnect()));
293
connect(d->c, SIGNAL(waitingForActivation()), SLOT(s5b_waitingForActivation()));
299
void FileTransferHandler::ft_connected()
304
// open the file, and set the correct offset
306
if(d->f.open(IO_ReadOnly)) {
311
if(d->f.at(d->offset))
318
error(ErrFile, 0, "");
322
if(d->sent == d->fileSize)
323
QTimer::singleShot(0, this, SLOT(doFinish()));
325
QTimer::singleShot(0, this, SLOT(trySend()));
328
// open the file, truncating if offset is zero, otherwise set the correct offset
329
int m = IO_ReadWrite;
338
if(d->f.at(d->offset))
345
error(ErrFile, 0, "");
349
d->activeFile = d->f.name();
350
active_file_add(d->activeFile);
352
// done already? this means a file size of zero
353
if(d->sent == d->fileSize)
354
QTimer::singleShot(0, this, SLOT(doFinish()));
360
void FileTransferHandler::ft_readyRead(const QByteArray &a)
363
//printf("%d bytes read\n", a.size());
364
int r = d->f.writeBlock(a.data(), a.size());
369
error(ErrFile, 0, "");
377
void FileTransferHandler::ft_bytesWritten(int x)
380
//printf("%d bytes written\n", x);
382
if(d->sent == d->fileSize) {
388
QTimer::singleShot(0, this, SLOT(trySend()));
389
progress(d->sent >> d->shift, d->sent);
393
void FileTransferHandler::ft_error(int x)
400
if(x == FileTransfer::ErrReject)
401
error(ErrReject, x, "");
402
else if(x == FileTransfer::ErrNeg)
403
error(ErrTransfer, x, tr("Unable to negotiate transfer."));
404
else if(x == FileTransfer::ErrConnect)
405
error(ErrTransfer, x, tr("Unable to connect to peer for data transfer."));
406
else if(x == FileTransfer::ErrProxy)
407
error(ErrTransfer, x, tr("Unable to connect to proxy for data transfer."));
408
else if(x == FileTransfer::ErrStream)
409
error(ErrTransfer, x, tr("Lost connection / Cancelled."));
412
void FileTransferHandler::trySend()
414
int blockSize = d->ft->dataSizeNeeded();
415
QByteArray a(blockSize);
416
int r = d->f.readBlock(a.data(), a.size());
421
error(ErrFile, 0, "");
424
if(r < (int)a.size())
426
d->ft->writeFileData(a);
429
void FileTransferHandler::doFinish()
431
if(d->sent == d->fileSize) {
436
progress(d->sent >> d->shift, d->sent);
439
void FileTransferHandler::mapSignals()
441
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
442
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
443
connect(d->ft, SIGNAL(readyRead(const QByteArray &)), SLOT(ft_readyRead(const QByteArray &)));
444
connect(d->ft, SIGNAL(bytesWritten(int)), SLOT(ft_bytesWritten(int)));
445
connect(d->ft, SIGNAL(error(int)), SLOT(ft_error(int)));
448
//----------------------------------------------------------------------------
450
//----------------------------------------------------------------------------
451
static QString lastPath, lastSavePath;
453
class FileRequestDlg::Private
458
AccountsComboBox *cb_ident;
459
QLabel *lb_ident, *lb_time;
462
FileTransferHandler *ft;
469
FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa)
470
:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
480
QHBox *hb = new QHBox(this);
481
new QLabel(tr("Identity: "), hb);
482
d->cb_ident = d->psi->accountsComboBox(hb);
483
connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
484
d->cb_ident->setAccount(pa);
485
replaceWidget(lb_accountlabel, hb);
486
setTabOrder(d->cb_ident, le_to);
488
d->te = new ChatView(this);
489
d->te->setReadOnly(false);
490
d->te->setTextFormat(PlainText);
491
replaceWidget(te_desc, d->te);
492
setTabOrder(le_fname, d->te);
493
setTabOrder(d->te, pb_stop);
495
setCaption(tr("Send File"));
496
setIcon(IconsetFactory::icon("psi/upload"));
498
le_to->setText(d->jid.full());
499
le_to->setReadOnly(false);
500
pb_start->setText(tr("&Send"));
501
pb_stop->setText(tr("&Close"));
503
connect(tb_browse, SIGNAL(clicked()), SLOT(chooseFile()));
504
connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
505
connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
507
lb_status->setText(tr("Ready"));
510
d->psi->dialogRegister(this);
511
QTimer::singleShot(0, this, SLOT(chooseFile()));
514
FileRequestDlg::FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa)
515
:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
521
d->ft = new FileTransferHandler(pa, ft);
525
d->fileName = ft->fileName();
526
d->fileSize = ft->fileSize();
529
QHBox *hb = new QHBox(this);
530
new QLabel(tr("Identity: "), hb);
531
d->lb_ident = d->pa->accountLabel(hb);
532
d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
533
new QLabel(tr("Time:"), hb);
534
d->lb_time = new QLabel(ts.time().toString(LocalDate), hb);
535
d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
537
replaceWidget(lb_accountlabel, hb);
539
d->te = new ChatView(this);
540
d->te->setTextFormat(PlainText);
541
replaceWidget(te_desc, d->te);
542
setTabOrder(le_fname, d->te);
543
setTabOrder(d->te, pb_stop);
545
lb_to->setText(tr("From:"));
546
setCaption(tr("Receive File"));
547
setIcon(IconsetFactory::icon("psi/download"));
549
le_to->setText(d->jid.full());
550
le_fname->setText(d->fileName);
551
lb_size->setText(tr("%1 byte(s)").arg(d->fileSize));
552
d->te->setReadOnly(true);
553
d->te->setText(ft->description());
554
pb_start->setText(tr("&Accept"));
555
pb_stop->setText(tr("&Reject"));
559
connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
560
connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
562
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
564
lb_status->setText(tr("Ready"));
566
pb_start->setFocus();
567
d->pa->dialogRegister(this);
570
FileRequestDlg::~FileRequestDlg()
574
d->psi->dialogUnregister(this);
576
d->pa->dialogUnregister(this);
580
void FileRequestDlg::done(int r)
582
if(busy->isActive()) {
583
int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the transfer?"), tr("&Yes"), tr("&No"));
587
// close/reject FT if there is one
597
void FileRequestDlg::keyPressEvent(QKeyEvent *e)
599
if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) ) {
600
if(pb_start->isEnabled())
604
QDialog::keyPressEvent(e);
607
void FileRequestDlg::updateIdentity(PsiAccount *pa)
610
disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
618
connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
621
void FileRequestDlg::pa_disconnected()
623
//if(busy->isActive()) {
629
void FileRequestDlg::blockWidgets()
632
d->cb_ident->setEnabled(false);
633
le_to->setEnabled(false);
634
le_fname->setEnabled(false);
635
tb_browse->setEnabled(false);
636
d->te->setEnabled(false);
637
pb_start->setEnabled(false);
640
void FileRequestDlg::unblockWidgets()
643
d->cb_ident->setEnabled(true);
644
le_to->setEnabled(true);
645
le_fname->setEnabled(true);
646
tb_browse->setEnabled(true);
647
d->te->setEnabled(true);
648
pb_start->setEnabled(true);
651
void FileRequestDlg::chooseFile()
654
if(lastPath.isEmpty())
655
lastPath = QDir::homeDirPath();
656
QString str = QFileDialog::getOpenFileName(lastPath, tr("All files (*)"), this, 0, tr("Choose a file"));
660
QMessageBox::information(this, tr("Error"), tr("The file specified does not exist."));
663
lastPath = fi.dirPath();
664
le_fname->setText(QDir::convertSeparators(fi.filePath()));
665
lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
671
void FileRequestDlg::doStart()
673
if(!d->pa->checkConnected(this))
677
Jid to = le_to->text();
679
QMessageBox::information(this, tr("Error"), tr("The Jabber ID specified is not valid. Correct this and try again."));
683
QFileInfo fi(le_fname->text());
685
QMessageBox::information(this, tr("Error"), tr("The file specified does not exist. Choose a correct file name before sending."));
691
pb_stop->setText(tr("&Cancel"));
694
lb_status->setText(tr("Requesting..."));
696
d->fileName = fi.fileName();
697
d->fileSize = fi.size(); // TODO: large file support
699
d->ft = new FileTransferHandler(d->pa);
700
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
701
connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
702
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
703
connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
704
d->ft->send(le_to->text(), le_fname->text(), d->te->text());
707
QString fname, savename;
708
bool overwrite = false;
710
if(lastSavePath.isEmpty())
711
lastSavePath = QDir::homeDirPath();
712
fname = QFileDialog::getSaveFileName(QDir(lastSavePath).filePath(d->fileName), tr("All files (*)"), this, 0, tr("Save As"));
713
if(!fname.isEmpty()) {
716
int x = QMessageBox::information(this, tr("Error"), tr("File already exists. Overwrite?"), tr("&Yes"), tr("&No"));
721
lastSavePath = fi.dirPath();
722
savename = fname + ".part";
723
fname = fi.fileName();
731
if(active_file_check(savename)) {
732
QMessageBox::information(this, tr("Error"), tr("This file is being transferred already!"));
736
Q_LLONG resume_offset = 0;
738
// supports resume? check for a .part
739
if(d->ft->resumeSupported()) {
740
QFileInfo fi(savename);
742
resume_offset = fi.size();
746
pb_start->setEnabled(false);
748
le_fname->setText(fname);
749
pb_stop->setText(tr("&Cancel"));
752
lb_status->setText(tr("Accepting..."));
754
d->t.start(30000, true);
756
connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
757
connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
758
connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
759
connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
760
d->ft->accept(savename, fname, resume_offset);
764
void FileRequestDlg::ft_accepted()
766
lb_status->setText(tr("Accepted!"));
769
void FileRequestDlg::ft_statusMessage(const QString &s)
771
lb_status->setText(s);
773
// stop the timer at first activity
778
void FileRequestDlg::ft_connected()
782
FileTransDlg *w = d->pa->psi()->ftdlg();
783
FileTransferHandler *h = d->ft;
789
w->takeTransfer(h, 0, 0);
792
void FileRequestDlg::ft_error(int x, int fx, const QString &)
804
pb_stop->setText(tr("&Close"));
805
lb_status->setText(tr("Ready"));
809
if(x == FileTransferHandler::ErrReject)
810
str = tr("File was rejected by remote user.");
811
else if(x == FileTransferHandler::ErrTransfer) {
812
if(fx == FileTransfer::ErrNeg)
814
"Unable to negotiate transfer.\n\n"
815
"This can happen if the contact did not understand our request, or if the\n"
816
"contact is offline."
818
else if(fx == FileTransfer::ErrConnect)
820
"Unable to connect to peer for data transfer.\n\n"
821
"Ensure that your Data Transfer settings are proper. If you are behind\n"
822
"a NAT router or firewall then you'll need to open the proper TCP port\n"
823
"or specify a Data Transfer Proxy in your account settings."
825
else if(fx == FileTransfer::ErrProxy)
827
"Failure to either connect to, or activate, the Data Transfer Proxy.\n\n"
828
"This means that the Proxy service is either not functioning or it is\n"
829
"unreachable. If you are behind a firewall, then you'll need to ensure\n"
830
"that outgoing TCP connections are allowed."
834
str = tr("File I/O error");
835
QMessageBox::information(this, tr("Error"), str);
837
if(!d->sending || x == FileTransferHandler::ErrReject)
841
void FileRequestDlg::t_timeout()
849
QString str = tr("Unable to accept the file. Perhaps the sender has cancelled the request.");
850
QMessageBox::information(this, tr("Error"), str);
854
//----------------------------------------------------------------------------
856
//----------------------------------------------------------------------------
857
class FileTransItem : public QListViewItem
875
FileTransItem(QListView *parent, const QString &_name, Q_LLONG _size, const QString &_peer, bool _sending)
876
:QListViewItem(parent)
883
rate = FileTransDlg::tr("N/A");
889
void niceUnit(Q_LLONG n, Q_LLONG *div, QString *unit)
891
Q_LLONG gb = 1024 * 1024 * 1024;
892
Q_LLONG mb = 1024 * 1024;
896
*unit = QString("GB");
900
*unit = QString("MB");
904
*unit = QString("KB");
908
*unit = QString("B");
912
QString roundedNumber(Q_LLONG n, Q_LLONG div)
914
bool decimal = false;
919
Q_LLONG x_long = n / div;
922
double f = (double)x;
924
return QString::number(f, 'f', 1);
927
return QString::number(x);
930
bool setProgress(int _progress, Q_LLONG _sent, int _bps)
932
progress = _progress;
937
Q_LLONG rest_long = size - sent;
939
int maxtime = (23 * 60 * 60) + (59 * 60) + (59); // 23h59m59s
940
if(rest_long > maxtime)
942
timeRemaining = (int)rest_long;
946
dist = progressBarDist(progressBarWidth());
959
niceUnit(size, &div, &unit);
961
s = roundedNumber(sent, div) + '/' + roundedNumber(size, div) + unit;
965
s += QString(" ") + FileTransDlg::tr("[Done]");
967
s += QString(" ") + FileTransDlg::tr("[Error: %1]").arg(error);
972
s += QString(" ") + FileTransDlg::tr("[Stalled]");
974
niceUnit(bps, &div, &unit);
975
s += QString(" @ ") + FileTransDlg::tr("%1%2/s").arg(roundedNumber(bps, div)).arg(unit);
978
QTime t = QTime().addSecs(timeRemaining);
979
s += FileTransDlg::tr("%1h%2m%3s remaining").arg(t.hour()).arg(t.minute()).arg(t.second());
985
int progressBarWidth() const
988
int w = listView()->columnWidth(0);
989
//int pw = (w - (3 * m)) / 2;
990
int pw = (w - (3 * m)) * 2 / 3;
994
int progressBarDist(int width) const
996
int xsize = width - 2;
997
return (progress * xsize / 8192);
1000
void drawProgressBar(QPainter *p, const QColorGroup &cg, int x, int y, int width, int height) const
1004
p->setPen(cg.highlightedText());
1006
p->setPen(cg.text());
1007
p->drawRect(x, y, width, height);
1010
int xsize = width - 2;
1011
int ysize = height - 2;
1013
int dist = progressBarDist(width);
1014
p->fillRect(xoff, yoff, dist, ysize, cg.brush(QColorGroup::Highlight));
1015
p->fillRect(xoff + dist, yoff, width - 2 - dist, ysize, cg.brush(QColorGroup::Base));
1017
int percent = progress * 100 / 8192;
1018
QString s = QString::number(percent) + '%';
1020
QFontMetrics fm(p->font());
1021
int ty = ((height - fm.height()) / 2) + fm.ascent() + y;
1022
int textwidth = fm.width(s);
1023
int center = xoff + (xsize / 2);
1026
p->setPen(cg.highlightedText());
1027
p->setClipRect(xoff, yoff, dist, ysize, QPainter::CoordPainter);
1028
p->drawText(center - (textwidth / 2), ty, s);
1032
p->setPen(cg.text());
1033
p->setClipRect(xoff + dist, yoff, width - 2 - dist, ysize, QPainter::CoordPainter);
1034
p->drawText(center - (textwidth / 2), ty, s);
1042
QListView *lv = listView();
1044
QFontMetrics fm = lv->fontMetrics();
1047
int ph = fm.height() + 2 + (pm * 2);
1048
int h = (ph * 2) + (m * 3);
1050
h += lv->itemMargin() * 2;
1052
// ensure an even number
1059
QString chopString(const QString &s, const QFontMetrics &fm, int len) const
1061
if(fm.width(s) <= len)
1065
uint n = s.length();
1067
str = s.mid(0, --n) + "...";
1068
} while(n > 0 && fm.width(str) > len);
1072
void paintCell(QPainter *mp, const QColorGroup &_cg, int, int width, int)
1074
QColorGroup cg = _cg;
1078
// tint the background
1079
/*//QColor base = Qt::black; //cg.base();
1080
QColor base = Qt::white;
1081
int red = base.red();
1082
int green = base.green();
1083
int blue = base.blue();
1084
bool light = false;//true;
1087
green = green * 15 / 16;
1088
blue = blue * 15 / 16;
1092
red = red * 15 / 16;
1098
red = red * 15 / 16;
1099
blue = blue * 15 / 16;
1102
green = 255 - green;
1103
green = green * 15 / 16;
1104
green = 255 - green;
1107
base.setRgb(red, green, blue);
1108
cg.setColor(QColorGroup::Base, base);*/
1111
QPainter *p = new QPainter(&pix);
1112
QFont font = mp->font();
1113
QFont boldFont = font;
1114
boldFont.setBold(true);
1115
QFontMetrics fm(font);
1116
QFontMetrics fmbold(boldFont);
1119
// m = margin, pm = progress margin, ph = progress height, yoff = text y offset,
1120
// tt = text top, tb = text bottom, pw = progress width, px = progress x coord
1123
int ph = fm.height() + 2 + (pm * 2);
1125
int tt = m + yoff + fm.ascent();
1126
int tb = (m * 2) + ph + yoff + fm.ascent();
1127
//int pw = (w - (3 * m)) / 2;
1128
int pw = (w - (3 * m)) * 2 / 3;
1129
int tw = (w - (3 * m)) - pw;
1130
int px = (m * 2) + tw;
1132
// clear out the background
1134
br = cg.brush(QColorGroup::Highlight);
1136
br = cg.brush(QColorGroup::Base);
1137
p->fillRect(0, 0, width, h, br);
1140
p->drawPixmap(m, m + yoff, icon);
1141
int tm = m + icon.width() + 4;
1142
tw = tw - (icon.width() + 4);
1146
p->setPen(cg.highlightedText());
1148
p->setPen(cg.text());
1149
p->setFont(boldFont);
1150
QString s1 = FileTransDlg::tr("File") + ": ";
1151
QString s2 = FileTransDlg::tr("To") + ": ";
1152
QString s3 = FileTransDlg::tr("From") + ": ";
1154
int lw = QMAX(QMAX(fmbold.width(s1), fmbold.width(s2)), fmbold.width(s3));
1156
p->drawText(tm, tt, s1);
1157
p->drawText(tm, tb, sending ? s2 : s3);
1159
p->drawText(tm + lw, tt, chopString(name, fm, left));
1160
p->drawText(tm + lw, tb, chopString(peer, fm, left));
1163
p->setFont(boldFont);
1164
s1 = FileTransDlg::tr("Status") + ": ";
1165
lw = fmbold.width(s1);
1167
p->drawText(px, tb, s1);
1169
p->drawText(px + lw, tb, chopString(rate, fm, left));
1172
drawProgressBar(p, cg, px, m, pw, ph);
1175
mp->drawPixmap(0, 0, pix);
1178
QString makeTip() const
1180
QTime t = QTime().addSecs(timeRemaining);
1181
QString rem = FileTransDlg::tr("%1h%2m%3s").arg(t.hour()).arg(t.minute()).arg(t.second());
1184
s += FileTransDlg::tr("Filename") + QString(": %1").arg(name);
1185
s += QString("\n") + FileTransDlg::tr("Type") + QString(": %1").arg(sending ? FileTransDlg::tr("Upload") : FileTransDlg::tr("Download"));
1186
s += QString("\n") + FileTransDlg::tr("Peer") + QString(": %1").arg(peer);
1187
s += QString("\n") + FileTransDlg::tr("Size") + QString(": %1").arg(size);
1189
s += QString("\n") + FileTransDlg::tr("[Done]");
1192
s += QString("\n") + FileTransDlg::tr("Transferred") + QString(": %1").arg(sent);
1194
s += QString("\n") + FileTransDlg::tr("Time remaining") + QString(": %1").arg(rem);
1201
class FileTransView : public QListView, public QToolTip
1205
FileTransView(QWidget *parent=0, const char *name=0)
1206
:QListView(parent, name), QToolTip(viewport())
1208
connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this, SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
1211
void maybeTip(const QPoint &pos)
1213
FileTransItem *i = static_cast<FileTransItem*>(itemAt(pos));
1216
QRect r(itemRect(i));
1217
tip(r, i->makeTip());
1220
void resizeEvent(QResizeEvent *e)
1222
QListView::resizeEvent(e);
1224
if(e->oldSize().width() != e->size().width())
1229
void itemCancel(int id);
1230
void itemOpenDest(int id);
1231
void itemClear(int id);
1234
void qlv_contextMenuRequested(QListViewItem *item, const QPoint &pos, int)
1239
FileTransItem *i = static_cast<FileTransItem*>(item);
1242
p.insertItem(tr("&Cancel"), 0);
1243
p.insertSeparator();
1244
//p.insertItem(tr("&Open Destination Folder"), 1);
1245
p.insertItem(tr("Cl&ear"), 2);
1248
p.setItemEnabled(0, false);
1251
//p.setItemEnabled(1, false);
1252
p.setItemEnabled(2, false);
1255
int x = p.exec(pos);
1257
// TODO: what if item is deleted during exec?
1264
itemOpenDest(i->id);
1272
QListViewItemIterator it(this);
1273
for(QListViewItem *i; (i = it.current()); ++it)
1278
class TransferMapping
1281
FileTransferHandler *h;
1302
// if we're at the end, shift down to make room
1304
for(int n = 0; n < at - 1; ++n)
1305
last[n] = last[n + 1];
1312
class FileTransDlg::Private
1315
FileTransDlg *parent;
1318
QPtrList<TransferMapping> transferList;
1321
Private(FileTransDlg *_parent)
1324
transferList.setAutoDelete(true);
1332
QListViewItemIterator it(lv);
1333
for(QListViewItem *i; (i = it.current()); ++it) {
1334
FileTransItem *fi = static_cast<FileTransItem*>(i);
1347
FileTransItem *findItem(int id)
1349
QListViewItemIterator it(lv);
1350
for(QListViewItem *i; (i = it.current()); ++it) {
1351
FileTransItem *fi = static_cast<FileTransItem*>(i);
1358
QPtrList<FileTransItem> getFinished()
1360
QPtrList<FileTransItem> list;
1361
QListViewItemIterator it(lv);
1362
for(QListViewItem *i; (i = it.current()); ++it) {
1363
FileTransItem *fi = static_cast<FileTransItem*>(i);
1370
TransferMapping *findMapping(FileTransferHandler *h)
1372
QPtrListIterator<TransferMapping> it(transferList);
1373
for(TransferMapping *i; (i = it.current()); ++it) {
1380
TransferMapping *findMapping(int id)
1382
QPtrListIterator<TransferMapping> it(transferList);
1383
for(TransferMapping *i; (i = it.current()); ++it) {
1390
void updateProgress(TransferMapping *i, bool updateAll=true)
1392
bool done = (i->p == i->h->totalSteps());
1396
if(!done && i->at >= 2) {
1397
int seconds = i->at - 1;
1398
Q_LLONG average = i->last[i->at-1] - i->last[0];
1399
bps = ((int)average) / seconds;
1403
FileTransItem *fi = findItem(i->id);
1407
parent->setProgress(i->id, i->p, i->h->totalSteps(), i->sent, bps, updateAll);
1410
bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1411
QString fname, savename;
1413
fname = i->h->fileName();
1414
savename = i->h->saveName();
1417
PsiAccount *pa = i->h->account();
1418
transferList.removeRef(i);
1421
//printf("fname: [%s], savename: [%s]\n", fname.latin1(), savename.latin1());
1423
// rename .part to original filename
1424
QFileInfo fi(savename);
1425
QDir dir = fi.dir();
1426
if(dir.exists(fname))
1428
if(!dir.rename(fi.fileName(), fname)) {
1429
// TODO: display some error about renaming
1433
pa->playSound(option.onevent[eFTComplete]);
1438
FileTransDlg::FileTransDlg(PsiCon *psi)
1439
:QDialog(0, 0, false, psi_dialog_flags)
1441
d = new Private(this);
1443
//d->psi->dialogRegister(this);
1445
connect(&d->t, SIGNAL(timeout()), SLOT(updateItems()));
1447
setCaption(tr("Transfer Manager"));
1448
setIcon(IconsetFactory::icon("psi/filemanager"));
1450
QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
1451
d->lv = new FileTransView(this);
1452
connect(d->lv, SIGNAL(itemCancel(int)), SLOT(itemCancel(int)));
1453
connect(d->lv, SIGNAL(itemOpenDest(int)), SLOT(itemOpenDest(int)));
1454
connect(d->lv, SIGNAL(itemClear(int)), SLOT(itemClear(int)));
1455
vb->addWidget(d->lv);
1456
QHBoxLayout *hb = new QHBoxLayout(vb);
1458
QPushButton *pb_clear = new QPushButton(tr("Clear &Finished"), this);
1459
connect(pb_clear, SIGNAL(clicked()), SLOT(clearFinished()));
1460
hb->addWidget(pb_clear);
1461
QPushButton *pb_close = new QPushButton(tr("&Hide"), this);
1462
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
1463
hb->addWidget(pb_close);
1465
pb_close->setDefault(true);
1466
pb_close->setFocus();
1468
d->lv->addColumn("");
1469
d->lv->header()->hide();
1471
d->lv->setResizeMode(QListView::LastColumn);
1472
d->lv->setAllColumnsShowFocus(true);
1473
d->lv->setSorting(-1);
1478
FileTransDlg::~FileTransDlg()
1480
//d->psi->dialogUnregister(this);
1484
int FileTransDlg::addItem(const QString &filename, Q_LLONG size, const QString &peer, bool sending)
1486
int id = d->findFreeId();
1487
FileTransItem *i = new FileTransItem(d->lv, filename, size, peer, sending);
1489
i->icon = IconsetFactory::icon("psi/upload").impix().pixmap();
1491
i->icon = IconsetFactory::icon("psi/download").impix().pixmap();
1497
void FileTransDlg::setProgress(int id, int step, int total, Q_LLONG sent, int bytesPerSecond, bool updateAll)
1499
FileTransItem *i = d->findItem(id);
1501
// convert steps/total into a word
1504
progress = step * 8192 / total;
1508
bool do_repaint = i->setProgress(progress, sent, bytesPerSecond);
1518
void FileTransDlg::removeItem(int id)
1520
FileTransItem *i = d->findItem(id);
1523
if(d->lv->childCount() == 0)
1527
void FileTransDlg::setError(int id, const QString &reason)
1529
FileTransItem *i = d->findItem(id);
1536
d->lv->ensureItemVisible(i);
1537
QMessageBox::information(this, tr("Transfer Error"), tr("Transfer of %1 with %2 failed.\nReason: %3").arg(i->name).arg(i->peer).arg(reason));
1541
void FileTransDlg::takeTransfer(FileTransferHandler *h, int p, Q_LLONG sent)
1544
UserListItem *u = h->account()->findFirstRelavent(h->peer());
1546
peer = jidnick(u->jid().full(), u->name());
1548
peer = h->peer().full();
1550
TransferMapping *i = new TransferMapping;
1552
i->id = addItem(h->fileName(), h->fileSize(), peer, (h->mode() == FileTransferHandler::Sending));
1555
d->transferList.append(i);
1557
FileTransItem *fi = d->findItem(i->id);
1558
d->lv->ensureItemVisible(fi);
1560
if(p == i->h->totalSteps()) {
1561
d->updateProgress(i, true);
1564
connect(h, SIGNAL(progress(int, Q_LLONG)), SLOT(ft_progress(int, Q_LLONG)));
1565
connect(h, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
1569
void FileTransDlg::clearFinished()
1571
QPtrList<FileTransItem> list = d->getFinished();
1573
// remove related transfer mappings
1574
QPtrListIterator<FileTransItem> it(list);
1575
for(FileTransItem *fi; (fi = it.current()); ++it) {
1576
TransferMapping *i = d->findMapping(fi->id);
1577
d->transferList.removeRef(i);
1580
list.setAutoDelete(true);
1584
void FileTransDlg::ft_progress(int p, Q_LLONG sent)
1586
TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1589
if(p == i->h->totalSteps())
1590
d->updateProgress(i, true);
1592
d->updateProgress(i, false);
1595
void FileTransDlg::ft_error(int x, int, const QString &s)
1597
TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1599
d->transferList.removeRef(i);
1602
//if(x == FileTransferHandler::ErrReject)
1603
// str = tr("File was rejected by remote user.");
1604
if(x == FileTransferHandler::ErrTransfer)
1607
str = tr("File I/O error");
1611
void FileTransDlg::updateItems()
1613
// operate on a copy so that we can delete items in updateProgress
1614
QPtrList<TransferMapping> list = d->transferList;
1615
QPtrListIterator<TransferMapping> it(list);
1616
for(TransferMapping *i; (i = it.current()); ++it) {
1619
d->updateProgress(i);
1624
void FileTransDlg::itemCancel(int id)
1626
FileTransItem *fi = d->findItem(id);
1627
TransferMapping *i = d->findMapping(id);
1628
d->transferList.removeRef(i);
1632
void FileTransDlg::itemOpenDest(int id)
1634
TransferMapping *i = d->findMapping(id);
1637
bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1639
path = QFileInfo(i->h->saveName()).dirPath();
1641
path = QFileInfo(i->h->fileName()).dirPath();
1643
//printf("item open dest: [%s]\n", path.latin1());
1646
void FileTransDlg::itemClear(int id)
1648
FileTransItem *fi = d->findItem(id);
1649
TransferMapping *i = d->findMapping(id);
1650
d->transferList.removeRef(i);
1654
void FileTransDlg::killTransfers(PsiAccount *pa)
1656
QPtrList<TransferMapping> list = d->transferList;
1657
QPtrListIterator<TransferMapping> it(list);
1658
for(TransferMapping *i; (i = it.current()); ++it) {
1660
if(i->h->account() == pa) {
1661
FileTransItem *fi = d->findItem(i->id);
1662
d->transferList.removeRef(i);
1668
#include"filetransdlg.moc"