source: psi/trunk/src/filetransdlg.cpp@ 82

Last change on this file since 82 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 38.7 KB
Line 
1#include"filetransdlg.h"
2
3#include<qlabel.h>
4#include<qlineedit.h>
5#include<qpushbutton.h>
6#include<qtimer.h>
7#include<qfiledialog.h>
8#include<qfile.h>
9#include<qprogressbar.h>
10#include<qdir.h>
11#include<qlistview.h>
12#include<qlayout.h>
13#include<qhbox.h>
14#include<qdatetime.h>
15#include<qpainter.h>
16#include<qheader.h>
17#include<qtooltip.h>
18#include<qpopupmenu.h>
19#include<qtextedit.h>
20#include"psicon.h"
21#include"psiaccount.h"
22#include"userlist.h"
23#include"common.h"
24#include"iconwidget.h"
25#include"busywidget.h"
26#include"filetransfer.h"
27#include"profiles.h"
28#include"psiiconset.h"
29#include"msgmle.h"
30
31#if QT_VERSION >= 0x030200
32typedef Q_UINT64 LARGE_TYPE;
33#else
34typedef Q_UINT32 LARGE_TYPE;
35#endif
36
37#define CSMAX (sizeof(LARGE_TYPE)*8)
38#define CSMIN 16
39static int calcShift(Q_LLONG big)
40{
41 LARGE_TYPE val = 1;
42 val <<= CSMAX - 1;
43 for(int n = CSMAX - CSMIN; n > 0; --n) {
44 if(big & val)
45 return n;
46 val >>= 1;
47 }
48 return 0;
49}
50
51static int calcComplement(Q_LLONG big, int shift)
52{
53 int block = 1 << shift;
54 Q_LLONG rem = big % block;
55 if(rem == 0)
56 return 0;
57 else
58 return (block - (int)rem);
59}
60
61static int calcTotalSteps(Q_LLONG big, int shift)
62{
63 if(big < 1)
64 return 0;
65 return ((big - 1) >> shift) + 1;
66}
67
68static int calcProgressStep(Q_LLONG big, int complement, int shift)
69{
70 return ((big + complement) >> shift);
71}
72
73static QStringList *activeFiles = 0;
74
75static void active_file_add(const QString &s)
76{
77 if(!activeFiles)
78 activeFiles = new QStringList;
79 activeFiles->append(s);
80 //printf("added: [%s]\n", s.latin1());
81}
82
83static void active_file_remove(const QString &s)
84{
85 if(!activeFiles)
86 return;
87 activeFiles->remove(s);
88 //printf("removed: [%s]\n", s.latin1());
89}
90
91static bool active_file_check(const QString &s)
92{
93 if(!activeFiles)
94 return false;
95 return activeFiles->contains(s);
96}
97
98static QString clean_filename(const QString &s)
99{
100//#ifdef Q_OS_WIN
101 QString badchars = "\\/|?*:\"<>";
102 QString str;
103 for(uint n = 0; n < s.length(); ++n) {
104 bool found = false;
105 for(uint b = 0; b < badchars.length(); ++b) {
106 if(s.at(n) == badchars.at(b)) {
107 found = true;
108 break;
109 }
110 }
111 if(!found)
112 str += s;
113 }
114 if(str.isEmpty())
115 str = "unnamed";
116 return str;
117//#else
118// return s;
119//#endif
120}
121
122//----------------------------------------------------------------------------
123// FileTransferHandler
124//----------------------------------------------------------------------------
125class FileTransferHandler::Private
126{
127public:
128 PsiAccount *pa;
129 FileTransfer *ft;
130 S5BConnection *c;
131 Jid peer;
132 QString fileName, saveName;
133 Q_LLONG fileSize, sent, offset, length;
134 QString desc;
135 bool sending;
136 QFile f;
137 int shift;
138 int complement;
139 QString activeFile;
140};
141
142FileTransferHandler::FileTransferHandler(PsiAccount *pa, FileTransfer *ft)
143{
144 d = new Private;
145 d->pa = pa;
146 d->c = 0;
147
148 if(ft) {
149 d->sending = false;
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);
156 d->ft = ft;
157 Jid proxy = d->pa->userAccount().dtProxy;
158 if(proxy.isValid())
159 d->ft->setProxy(proxy);
160 mapSignals();
161 }
162 else {
163 d->sending = true;
164 d->ft = 0;
165 }
166}
167
168FileTransferHandler::~FileTransferHandler()
169{
170 if(!d->activeFile.isEmpty())
171 active_file_remove(d->activeFile);
172
173 if(d->ft) {
174 d->ft->close();
175 delete d->ft;
176 }
177 delete d;
178}
179
180void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
181{
182 if(!d->sending)
183 return;
184
185 d->peer = to;
186 QFileInfo fi(fname);
187 d->fileName = fi.fileName();
188 d->fileSize = fi.size(); // TODO: large file support
189 d->desc = desc;
190 d->shift = calcShift(d->fileSize);
191 d->complement = calcComplement(d->fileSize, d->shift);
192
193 d->ft = d->pa->client()->fileTransferManager()->createTransfer();
194 Jid proxy = d->pa->userAccount().dtProxy;
195 if(proxy.isValid())
196 d->ft->setProxy(proxy);
197 mapSignals();
198
199 d->f.setName(fname);
200 d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc);
201}
202
203PsiAccount *FileTransferHandler::account() const
204{
205 return d->pa;
206}
207
208int FileTransferHandler::mode() const
209{
210 return (d->sending ? Sending : Receiving);
211}
212
213Jid FileTransferHandler::peer() const
214{
215 return d->peer;
216}
217
218QString FileTransferHandler::fileName() const
219{
220 return d->fileName;
221}
222
223Q_LLONG FileTransferHandler::fileSize() const
224{
225 return d->fileSize;
226}
227
228QString FileTransferHandler::description() const
229{
230 return d->desc;
231}
232
233Q_LLONG FileTransferHandler::offset() const
234{
235 return d->offset;
236}
237
238int FileTransferHandler::totalSteps() const
239{
240 return calcTotalSteps(d->fileSize, d->shift);
241}
242
243bool FileTransferHandler::resumeSupported() const
244{
245 if(d->ft)
246 return d->ft->rangeSupported();
247 else
248 return false;
249}
250
251QString FileTransferHandler::saveName() const
252{
253 return d->saveName;
254}
255
256void FileTransferHandler::accept(const QString &saveName, const QString &fileName, Q_LLONG offset)
257{
258 if(d->sending)
259 return;
260 d->fileName = fileName;
261 d->saveName = saveName;
262 d->offset = offset;
263 d->length = d->fileSize;
264 d->f.setName(saveName);
265 d->ft->accept(offset);
266}
267
268void FileTransferHandler::s5b_proxyQuery()
269{
270 statusMessage(tr("Quering proxy..."));
271}
272
273void FileTransferHandler::s5b_proxyResult(bool b)
274{
275 if(b)
276 statusMessage(tr("Proxy query successful."));
277 else
278 statusMessage(tr("Proxy query failed!"));
279}
280
281void FileTransferHandler::s5b_requesting()
282{
283 statusMessage(tr("Requesting data transfer channel..."));
284}
285
286void FileTransferHandler::s5b_accepted()
287{
288 statusMessage(tr("Peer accepted request."));
289}
290
291void FileTransferHandler::s5b_tryingHosts(const StreamHostList &)
292{
293 statusMessage(tr("Connecting to peer..."));
294}
295
296void FileTransferHandler::s5b_proxyConnect()
297{
298 statusMessage(tr("Connecting to proxy..."));
299}
300
301void FileTransferHandler::s5b_waitingForActivation()
302{
303 statusMessage(tr("Waiting for peer activation..."));
304}
305
306void FileTransferHandler::ft_accepted()
307{
308 d->offset = d->ft->offset();
309 d->length = d->ft->length();
310
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()));
319
320 if(d->sending)
321 accepted();
322}
323
324void FileTransferHandler::ft_connected()
325{
326 d->sent = d->offset;
327
328 if(d->sending) {
329 // open the file, and set the correct offset
330 bool ok = false;
331 if(d->f.open(IO_ReadOnly)) {
332 if(d->offset == 0) {
333 ok = true;
334 }
335 else {
336 if(d->f.at(d->offset))
337 ok = true;
338 }
339 }
340 if(!ok) {
341 delete d->ft;
342 d->ft = 0;
343 error(ErrFile, 0, "");
344 return;
345 }
346
347 if(d->sent == d->fileSize)
348 QTimer::singleShot(0, this, SLOT(doFinish()));
349 else
350 QTimer::singleShot(0, this, SLOT(trySend()));
351 }
352 else {
353 // open the file, truncating if offset is zero, otherwise set the correct offset
354 int m = IO_ReadWrite;
355 if(d->offset == 0)
356 m |= IO_Truncate;
357 bool ok = false;
358 if(d->f.open(m)) {
359 if(d->offset == 0) {
360 ok = true;
361 }
362 else {
363 if(d->f.at(d->offset))
364 ok = true;
365 }
366 }
367 if(!ok) {
368 delete d->ft;
369 d->ft = 0;
370 error(ErrFile, 0, "");
371 return;
372 }
373
374 d->activeFile = d->f.name();
375 active_file_add(d->activeFile);
376
377 // done already? this means a file size of zero
378 if(d->sent == d->fileSize)
379 QTimer::singleShot(0, this, SLOT(doFinish()));
380 }
381
382 connected();
383}
384
385void FileTransferHandler::ft_readyRead(const QByteArray &a)
386{
387 if(!d->sending) {
388 //printf("%d bytes read\n", a.size());
389 int r = d->f.writeBlock(a.data(), a.size());
390 if(r < 0) {
391 d->f.close();
392 delete d->ft;
393 d->ft = 0;
394 error(ErrFile, 0, "");
395 return;
396 }
397 d->sent += a.size();
398 doFinish();
399 }
400}
401
402void FileTransferHandler::ft_bytesWritten(int x)
403{
404 if(d->sending) {
405 //printf("%d bytes written\n", x);
406 d->sent += x;
407 if(d->sent == d->fileSize) {
408 d->f.close();
409 delete d->ft;
410 d->ft = 0;
411 }
412 else
413 QTimer::singleShot(0, this, SLOT(trySend()));
414 progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
415 }
416}
417
418void FileTransferHandler::ft_error(int x)
419{
420 if(d->f.isOpen())
421 d->f.close();
422 delete d->ft;
423 d->ft = 0;
424
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."));
435}
436
437void FileTransferHandler::trySend()
438{
439 int blockSize = d->ft->dataSizeNeeded();
440 QByteArray a(blockSize);
441 int r = d->f.readBlock(a.data(), a.size());
442 if(r < 0) {
443 d->f.close();
444 delete d->ft;
445 d->ft = 0;
446 error(ErrFile, 0, "");
447 return;
448 }
449 if(r < (int)a.size())
450 a.resize(r);
451 d->ft->writeFileData(a);
452}
453
454void FileTransferHandler::doFinish()
455{
456 if(d->sent == d->fileSize) {
457 d->f.close();
458 delete d->ft;
459 d->ft = 0;
460 }
461 progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
462}
463
464void FileTransferHandler::mapSignals()
465{
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)));
471}
472
473//----------------------------------------------------------------------------
474// FileRequestDlg
475//----------------------------------------------------------------------------
476
477class FileRequestDlg::Private
478{
479public:
480 PsiCon *psi;
481 PsiAccount *pa;
482 AccountsComboBox *cb_ident;
483 QLabel *lb_ident, *lb_time;
484 ChatView *te;
485 Jid jid;
486 FileTransferHandler *ft;
487 QString fileName;
488 Q_LLONG fileSize;
489 bool sending;
490 QTimer t;
491};
492
493
494FileRequestDlg::FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa)
495:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
496{
497 QStringList l;
498 FileRequestDlg(j, psi, pa, l);
499}
500
501
502FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa, const QStringList& files)
503:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
504{
505 d = new Private;
506 d->psi = psi;
507 d->pa = 0;
508 d->jid = jid;
509 d->ft = 0;
510 d->sending = true;
511 updateIdentity(pa);
512
513 QHBox *hb = new QHBox(this);
514 new QLabel(tr("Identity: "), hb);
515 d->cb_ident = d->psi->accountsComboBox(hb);
516 connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
517 d->cb_ident->setAccount(pa);
518 replaceWidget(lb_accountlabel, hb);
519 setTabOrder(d->cb_ident, le_to);
520
521 d->te = new ChatView(this);
522 d->te->setReadOnly(false);
523 d->te->setTextFormat(PlainText);
524 replaceWidget(te_desc, d->te);
525 setTabOrder(le_fname, d->te);
526 setTabOrder(d->te, pb_stop);
527
528 setCaption(tr("Send File"));
529#ifndef Q_WS_MAC
530 setIcon(IconsetFactory::icon("psi/upload"));
531#endif
532
533 le_to->setText(d->jid.full());
534 le_to->setReadOnly(false);
535 pb_start->setText(tr("&Send"));
536 pb_stop->setText(tr("&Close"));
537
538 connect(tb_browse, SIGNAL(clicked()), SLOT(chooseFile()));
539 connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
540 connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
541
542 lb_status->setText(tr("Ready"));
543
544 d->te->setFocus();
545 d->psi->dialogRegister(this);
546
547 if (files.isEmpty()) {
548 QTimer::singleShot(0, this, SLOT(chooseFile()));
549 }
550 else {
551 // TODO: Once sending of multiple files is supported, change this
552 QFileInfo fi(files.first());
553
554 // Check if the file is legal
555 if(!fi.exists()) {
556 QMessageBox::critical(this, tr("Error"), QString("The file '%1' does not exist.").arg(files.first()));
557 QTimer::singleShot(0, this, SLOT(reject()));
558 return;
559 }
560 if(fi.isDir()) {
561 QMessageBox::critical(this, tr("Error"), tr("Sending folders is not supported."));
562 QTimer::singleShot(0, this, SLOT(reject()));
563 return;
564 }
565
566 option.lastPath = fi.dirPath();
567 le_fname->setText(QDir::convertSeparators(fi.filePath()));
568 lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
569 }
570}
571
572FileRequestDlg::FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa)
573:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
574{
575 d = new Private;
576 d->psi = 0;
577 d->pa = 0;
578 d->jid = ft->peer();
579 d->ft = new FileTransferHandler(pa, ft);
580 d->sending = false;
581 updateIdentity(pa);
582
583 d->fileName = ft->fileName();
584 d->fileSize = ft->fileSize();
585
586 d->cb_ident = 0;
587 QHBox *hb = new QHBox(this);
588 new QLabel(tr("Identity: "), hb);
589 d->lb_ident = d->pa->accountLabel(hb);
590 d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
591 new QLabel(tr("Time:"), hb);
592 d->lb_time = new QLabel(ts.time().toString(LocalDate), hb);
593 d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
594
595 replaceWidget(lb_accountlabel, hb);
596
597 d->te = new ChatView(this);
598 d->te->setTextFormat(PlainText);
599 replaceWidget(te_desc, d->te);
600 setTabOrder(le_fname, d->te);
601 setTabOrder(d->te, pb_stop);
602
603 lb_to->setText(tr("From:"));
604 setCaption(tr("Receive File"));
605#ifndef Q_WS_MAC
606 setIcon(IconsetFactory::icon("psi/download"));
607#endif
608
609 le_to->setText(d->jid.full());
610 le_fname->setText(d->fileName);
611 lb_size->setText(tr("%1 byte(s)").arg(d->fileSize));
612 d->te->setReadOnly(true);
613 d->te->setText(ft->description());
614 pb_start->setText(tr("&Accept"));
615 pb_stop->setText(tr("&Reject"));
616
617 tb_browse->hide();
618
619 connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
620 connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
621
622 connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
623
624 lb_status->setText(tr("Ready"));
625
626 pb_start->setFocus();
627 d->pa->dialogRegister(this);
628}
629
630FileRequestDlg::~FileRequestDlg()
631{
632 delete d->ft;
633 if(d->psi)
634 d->psi->dialogUnregister(this);
635 else
636 d->pa->dialogUnregister(this);
637 delete d;
638}
639
640void FileRequestDlg::done(int r)
641{
642 if(busy->isActive()) {
643 int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the transfer?"), tr("&Yes"), tr("&No"));
644 if(n != 0)
645 return;
646
647 // close/reject FT if there is one
648 if(d->ft) {
649 delete d->ft;
650 d->ft = 0;
651 }
652 }
653
654 QDialog::done(r);
655}
656
657void FileRequestDlg::keyPressEvent(QKeyEvent *e)
658{
659 if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) ) {
660 if(pb_start->isEnabled())
661 doStart();
662 }
663 else
664 QDialog::keyPressEvent(e);
665}
666
667void FileRequestDlg::updateIdentity(PsiAccount *pa)
668{
669 if(d->pa)
670 disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
671
672 if(!pa) {
673 close();
674 return;
675 }
676
677 d->pa = pa;
678 connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
679}
680
681void FileRequestDlg::pa_disconnected()
682{
683 //if(busy->isActive()) {
684 // busy->stop();
685 // close();
686 //}
687}
688
689void FileRequestDlg::blockWidgets()
690{
691 if(d->cb_ident)
692 d->cb_ident->setEnabled(false);
693 le_to->setEnabled(false);
694 le_fname->setEnabled(false);
695 tb_browse->setEnabled(false);
696 d->te->setEnabled(false);
697 pb_start->setEnabled(false);
698}
699
700void FileRequestDlg::unblockWidgets()
701{
702 if(d->cb_ident)
703 d->cb_ident->setEnabled(true);
704 le_to->setEnabled(true);
705 le_fname->setEnabled(true);
706 tb_browse->setEnabled(true);
707 d->te->setEnabled(true);
708 pb_start->setEnabled(true);
709}
710
711void FileRequestDlg::chooseFile()
712{
713 while(1) {
714 if(option.lastPath.isEmpty())
715 option.lastPath = QDir::homeDirPath();
716 QString str = QFileDialog::getOpenFileName(option.lastPath, tr("All files (*)"), this, 0, tr("Choose a file"));
717 if(!str.isEmpty()) {
718 QFileInfo fi(str);
719 if(!fi.exists()) {
720 QMessageBox::information(this, tr("Error"), tr("The file specified does not exist."));
721 continue;
722 }
723 option.lastPath = fi.dirPath();
724 le_fname->setText(QDir::convertSeparators(fi.filePath()));
725 lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
726 }
727 break;
728 }
729}
730
731void FileRequestDlg::doStart()
732{
733 if(!d->pa->checkConnected(this))
734 return;
735
736 if(d->sending) {
737 Jid to = le_to->text();
738 if(!to.isValid()) {
739 QMessageBox::information(this, tr("Error"), tr("The Jabber ID specified is not valid. Correct this and try again."));
740 return;
741 }
742
743 QFileInfo fi(le_fname->text());
744 if(!fi.exists()) {
745 QMessageBox::information(this, tr("Error"), tr("The file specified does not exist. Choose a correct file name before sending."));
746 return;
747 }
748
749 blockWidgets();
750
751 pb_stop->setText(tr("&Cancel"));
752 pb_stop->setFocus();
753 busy->start();
754 lb_status->setText(tr("Requesting..."));
755
756 d->fileName = fi.fileName();
757 d->fileSize = fi.size(); // TODO: large file support
758
759 d->ft = new FileTransferHandler(d->pa);
760 connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
761 connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
762 connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
763 connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
764 d->ft->send(le_to->text(), le_fname->text(), d->te->text());
765 }
766 else {
767 QString fname, savename;
768 bool overwrite = false;
769 while(1) {
770 if(option.lastSavePath.isEmpty())
771 option.lastSavePath = QDir::homeDirPath();
772 fname = QFileDialog::getSaveFileName(QDir(option.lastSavePath).filePath(d->fileName), tr("All files (*)"), this, 0, tr("Save As"));
773 if(!fname.isEmpty()) {
774 QFileInfo fi(fname);
775 if(fi.exists()) {
776 int x = QMessageBox::information(this, tr("Error"), tr("File already exists. Overwrite?"), tr("&Yes"), tr("&No"));
777 if(x != 0)
778 continue;
779 overwrite = true;
780 }
781 option.lastSavePath = fi.dirPath();
782 savename = fname + ".part";
783 fname = fi.fileName();
784 }
785 else
786 return;
787
788 break;
789 }
790
791 if(active_file_check(savename)) {
792 QMessageBox::information(this, tr("Error"), tr("This file is being transferred already!"));
793 return;
794 }
795
796 Q_LLONG resume_offset = 0;
797 if(!overwrite) {
798 // supports resume? check for a .part
799 if(d->ft->resumeSupported()) {
800 QFileInfo fi(savename);
801 if(fi.exists())
802 resume_offset = fi.size();
803 }
804 }
805
806 pb_start->setEnabled(false);
807
808 le_fname->setText(fname);
809 pb_stop->setText(tr("&Cancel"));
810 pb_stop->setFocus();
811 busy->start();
812 lb_status->setText(tr("Accepting..."));
813
814 d->t.start(30000, true);
815
816 connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
817 connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
818 connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
819 connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
820 d->ft->accept(savename, fname, resume_offset);
821 }
822}
823
824void FileRequestDlg::ft_accepted()
825{
826 lb_status->setText(tr("Accepted!"));
827}
828
829void FileRequestDlg::ft_statusMessage(const QString &s)
830{
831 lb_status->setText(s);
832
833 // stop the timer at first activity
834 if(d->t.isActive())
835 d->t.stop();
836}
837
838void FileRequestDlg::ft_connected()
839{
840 d->t.stop();
841 busy->stop();
842 FileTransDlg *w = d->pa->psi()->ftdlg();
843 FileTransferHandler *h = d->ft;
844 d->ft = 0;
845 closeDialogs(this);
846 close();
847 bringToFront(w);
848
849 w->takeTransfer(h, 0, 0);
850}
851
852void FileRequestDlg::ft_error(int x, int fx, const QString &)
853{
854 d->t.stop();
855 busy->stop();
856
857 delete d->ft;
858 d->ft = 0;
859
860 closeDialogs(this);
861
862 if(d->sending) {
863 unblockWidgets();
864 pb_stop->setText(tr("&Close"));
865 lb_status->setText(tr("Ready"));
866 }
867
868 QString str;
869 if(x == FileTransferHandler::ErrReject)
870 str = tr("File was rejected by remote user.");
871 else if(x == FileTransferHandler::ErrTransfer) {
872 if(fx == FileTransfer::ErrNeg)
873 str = tr(
874 "Unable to negotiate transfer.\n\n"
875 "This can happen if the contact did not understand our request, or if the\n"
876 "contact is offline."
877 );
878 else if(fx == FileTransfer::ErrConnect)
879 str = tr(
880 "Unable to connect to peer for data transfer.\n\n"
881 "Ensure that your Data Transfer settings are proper. If you are behind\n"
882 "a NAT router or firewall then you'll need to open the proper TCP port\n"
883 "or specify a Data Transfer Proxy in your account settings."
884 );
885 else if(fx == FileTransfer::ErrProxy)
886 str = tr(
887 "Failure to either connect to, or activate, the Data Transfer Proxy.\n\n"
888 "This means that the Proxy service is either not functioning or it is\n"
889 "unreachable. If you are behind a firewall, then you'll need to ensure\n"
890 "that outgoing TCP connections are allowed."
891 );
892 }
893 else
894 str = tr("File I/O error");
895 QMessageBox::information(this, tr("Error"), str);
896
897 if(!d->sending || x == FileTransferHandler::ErrReject)
898 close();
899}
900
901void FileRequestDlg::t_timeout()
902{
903 delete d->ft;
904 d->ft = 0;
905
906 busy->stop();
907 closeDialogs(this);
908
909 QString str = tr("Unable to accept the file. Perhaps the sender has cancelled the request.");
910 QMessageBox::information(this, tr("Error"), str);
911 close();
912}
913
914//----------------------------------------------------------------------------
915// FileTransDlg
916//----------------------------------------------------------------------------
917class FileTransItem : public QListViewItem
918{
919public:
920 QPixmap icon;
921 bool sending;
922 QString name;
923 Q_LLONG size;
924 QString peer;
925 QString rate;
926 int progress;
927 Q_LLONG sent;
928 int bps;
929 int timeRemaining;
930 int id;
931 int dist;
932 bool done;
933 QString error;
934
935 FileTransItem(QListView *parent, const QString &_name, Q_LLONG _size, const QString &_peer, bool _sending)
936 :QListViewItem(parent)
937 {
938 done = false;
939 sending = _sending;
940 name = _name;
941 size = _size;
942 peer = _peer;
943 rate = FileTransDlg::tr("N/A");
944 sent = 0;
945 progress = 0;
946 dist = -1;
947 }
948
949 void niceUnit(Q_LLONG n, Q_LLONG *div, QString *unit)
950 {
951 Q_LLONG gb = 1024 * 1024 * 1024;
952 Q_LLONG mb = 1024 * 1024;
953 Q_LLONG kb = 1024;
954 if(n >= gb) {
955 *div = gb;
956 *unit = QString("GB");
957 }
958 else if(n >= mb) {
959 *div = mb;
960 *unit = QString("MB");
961 }
962 else if(n >= kb) {
963 *div = kb;
964 *unit = QString("KB");
965 }
966 else {
967 *div = 1;
968 *unit = QString("B");
969 }
970 }
971
972 QString roundedNumber(Q_LLONG n, Q_LLONG div)
973 {
974 bool decimal = false;
975 if(div >= 1024) {
976 div /= 10;
977 decimal = true;
978 }
979 Q_LLONG x_long = n / div;
980 int x = (int)x_long;
981 if(decimal) {
982 double f = (double)x;
983 f /= 10;
984 return QString::number(f, 'f', 1);
985 }
986 else
987 return QString::number(x);
988 }
989
990 bool setProgress(int _progress, Q_LLONG _sent, int _bps)
991 {
992 progress = _progress;
993 sent = _sent;
994 bps = _bps;
995
996 if(bps > 0) {
997 Q_LLONG rest_long = size - sent;
998 rest_long /= bps;
999 int maxtime = (23 * 60 * 60) + (59 * 60) + (59); // 23h59m59s
1000 if(rest_long > maxtime)
1001 rest_long = maxtime;
1002 timeRemaining = (int)rest_long;
1003 }
1004
1005 int lastDist = dist;
1006 dist = progressBarDist(progressBarWidth());
1007 if(dist != lastDist)
1008 return true;
1009 else
1010 return false;
1011 }
1012
1013 void updateRate()
1014 {
1015 QString s;
1016 {
1017 Q_LLONG div;
1018 QString unit;
1019 niceUnit(size, &div, &unit);
1020
1021 s = roundedNumber(sent, div) + '/' + roundedNumber(size, div) + unit;
1022
1023 if(done) {
1024 if(error.isEmpty())
1025 s += QString(" ") + FileTransDlg::tr("[Done]");
1026 else
1027 s += QString(" ") + FileTransDlg::tr("[Error: %1]").arg(error);
1028 }
1029 else if(bps == -1)
1030 s += "";
1031 else if(bps == 0)
1032 s += QString(" ") + FileTransDlg::tr("[Stalled]");
1033 else {
1034 niceUnit(bps, &div, &unit);
1035 s += QString(" @ ") + FileTransDlg::tr("%1%2/s").arg(roundedNumber(bps, div)).arg(unit);
1036
1037 s += ", ";
1038 QTime t = QTime().addSecs(timeRemaining);
1039 s += FileTransDlg::tr("%1h%2m%3s remaining").arg(t.hour()).arg(t.minute()).arg(t.second());
1040 }
1041 }
1042 rate = s;
1043 }
1044
1045 int progressBarWidth() const
1046 {
1047 int m = 4;
1048 int w = listView()->columnWidth(0);
1049 //int pw = (w - (3 * m)) / 2;
1050 int pw = (w - (3 * m)) * 2 / 3;
1051 return pw;
1052 }
1053
1054 int progressBarDist(int width) const
1055 {
1056 int xsize = width - 2;
1057 return (progress * xsize / 8192);
1058 }
1059
1060 void drawProgressBar(QPainter *p, const QColorGroup &cg, int x, int y, int width, int height) const
1061 {
1062 p->save();
1063 if(isSelected())
1064 p->setPen(cg.highlightedText());
1065 else
1066 p->setPen(cg.text());
1067 p->drawRect(x, y, width, height);
1068 int xoff = x + 1;
1069 int yoff = y + 1;
1070 int xsize = width - 2;
1071 int ysize = height - 2;
1072
1073 int dist = progressBarDist(width);
1074 p->fillRect(xoff, yoff, dist, ysize, cg.brush(QColorGroup::Highlight));
1075 p->fillRect(xoff + dist, yoff, width - 2 - dist, ysize, cg.brush(QColorGroup::Base));
1076
1077 int percent = progress * 100 / 8192;
1078 QString s = QString::number(percent) + '%';
1079
1080 QFontMetrics fm(p->font());
1081 int ty = ((height - fm.height()) / 2) + fm.ascent() + y;
1082 int textwidth = fm.width(s);
1083 int center = xoff + (xsize / 2);
1084
1085 p->save();
1086 p->setPen(cg.highlightedText());
1087 p->setClipRect(xoff, yoff, dist, ysize, QPainter::CoordPainter);
1088 p->drawText(center - (textwidth / 2), ty, s);
1089 p->restore();
1090
1091 p->save();
1092 p->setPen(cg.text());
1093 p->setClipRect(xoff + dist, yoff, width - 2 - dist, ysize, QPainter::CoordPainter);
1094 p->drawText(center - (textwidth / 2), ty, s);
1095 p->restore();
1096 p->restore();
1097 }
1098
1099 void setup()
1100 {
1101 widthChanged();
1102 QListView *lv = listView();
1103
1104 QFontMetrics fm = lv->fontMetrics();
1105 int m = 4;
1106 int pm = 2;
1107 int ph = fm.height() + 2 + (pm * 2);
1108 int h = (ph * 2) + (m * 3);
1109
1110 h += lv->itemMargin() * 2;
1111
1112 // ensure an even number
1113 if(h & 1)
1114 ++h;
1115
1116 setHeight(h);
1117 }
1118
1119 QString chopString(const QString &s, const QFontMetrics &fm, int len) const
1120 {
1121 if(fm.width(s) <= len)
1122 return s;
1123
1124 QString str;
1125 uint n = s.length();
1126 do {
1127 str = s.mid(0, --n) + "...";
1128 } while(n > 0 && fm.width(str) > len);
1129 return str;
1130 }
1131
1132 void paintCell(QPainter *mp, const QColorGroup &_cg, int, int width, int)
1133 {
1134 QColorGroup cg = _cg;
1135 int w = width;
1136 int h = height();
1137
1138 // tint the background
1139 /*//QColor base = Qt::black; //cg.base();
1140 QColor base = Qt::white;
1141 int red = base.red();
1142 int green = base.green();
1143 int blue = base.blue();
1144 bool light = false;//true;
1145 if(sending) {
1146 if(light) {
1147 green = green * 15 / 16;
1148 blue = blue * 15 / 16;
1149 }
1150 else {
1151 red = 255 - red;
1152 red = red * 15 / 16;
1153 red = 255 - red;
1154 }
1155 }
1156 else {
1157 if(light) {
1158 red = red * 15 / 16;
1159 blue = blue * 15 / 16;
1160 }
1161 else {
1162 green = 255 - green;
1163 green = green * 15 / 16;
1164 green = 255 - green;
1165 }
1166 }
1167 base.setRgb(red, green, blue);
1168 cg.setColor(QColorGroup::Base, base);*/
1169
1170 QPixmap pix(w, h);
1171 QPainter *p = new QPainter(&pix);
1172 QFont font = mp->font();
1173 QFont boldFont = font;
1174 boldFont.setBold(true);
1175 QFontMetrics fm(font);
1176 QFontMetrics fmbold(boldFont);
1177 QBrush br;
1178
1179 // m = margin, pm = progress margin, ph = progress height, yoff = text y offset,
1180 // tt = text top, tb = text bottom, pw = progress width, px = progress x coord
1181 int m = 4;
1182 int pm = 2;
1183 int ph = fm.height() + 2 + (pm * 2);
1184 int yoff = 1 + pm;
1185 int tt = m + yoff + fm.ascent();
1186 int tb = (m * 2) + ph + yoff + fm.ascent();
1187 //int pw = (w - (3 * m)) / 2;
1188 int pw = (w - (3 * m)) * 2 / 3;
1189 int tw = (w - (3 * m)) - pw;
1190 int px = (m * 2) + tw;
1191
1192 // clear out the background
1193 if(isSelected())
1194 br = cg.brush(QColorGroup::Highlight);
1195 else
1196 br = cg.brush(QColorGroup::Base);
1197 p->fillRect(0, 0, width, h, br);
1198
1199 // icon
1200 p->drawPixmap(m, m + yoff, icon);
1201 int tm = m + icon.width() + 4;
1202 tw = tw - (icon.width() + 4);
1203
1204 // filename / peer
1205 if(isSelected())
1206 p->setPen(cg.highlightedText());
1207 else
1208 p->setPen(cg.text());
1209 p->setFont(boldFont);
1210 QString s1 = FileTransDlg::tr("File") + ": ";
1211 QString s2 = FileTransDlg::tr("To") + ": ";
1212 QString s3 = FileTransDlg::tr("From") + ": ";
1213
1214 int lw = QMAX(QMAX(fmbold.width(s1), fmbold.width(s2)), fmbold.width(s3));
1215 int left = tw - lw;
1216 p->drawText(tm, tt, s1);
1217 p->drawText(tm, tb, sending ? s2 : s3);
1218 p->setFont(font);
1219 p->drawText(tm + lw, tt, chopString(name, fm, left));
1220 p->drawText(tm + lw, tb, chopString(peer, fm, left));
1221
1222 // rate
1223 p->setFont(boldFont);
1224 s1 = FileTransDlg::tr("Status") + ": ";
1225 lw = fmbold.width(s1);
1226 left = pw - lw;
1227 p->drawText(px, tb, s1);
1228 p->setFont(font);
1229 p->drawText(px + lw, tb, chopString(rate, fm, left));
1230
1231 // progress bar
1232 drawProgressBar(p, cg, px, m, pw, ph);
1233
1234 delete p;
1235 mp->drawPixmap(0, 0, pix);
1236 }
1237
1238 QString makeTip() const
1239 {
1240 QTime t = QTime().addSecs(timeRemaining);
1241 QString rem = FileTransDlg::tr("%1h%2m%3s").arg(t.hour()).arg(t.minute()).arg(t.second());
1242
1243 QString s;
1244 s += FileTransDlg::tr("Filename") + QString(": %1").arg(name);
1245 s += QString("\n") + FileTransDlg::tr("Type") + QString(": %1").arg(sending ? FileTransDlg::tr("Upload") : FileTransDlg::tr("Download"));
1246 s += QString("\n") + FileTransDlg::tr("Peer") + QString(": %1").arg(peer);
1247 s += QString("\n") + FileTransDlg::tr("Size") + QString(": %1").arg(size);
1248 if(done) {
1249 s += QString("\n") + FileTransDlg::tr("[Done]");
1250 }
1251 else {
1252 s += QString("\n") + FileTransDlg::tr("Transferred") + QString(": %1").arg(sent);
1253 if(bps > 0)
1254 s += QString("\n") + FileTransDlg::tr("Time remaining") + QString(": %1").arg(rem);
1255 }
1256
1257 return s;
1258 }
1259};
1260
1261class FileTransView : public QListView, public QToolTip
1262{
1263 Q_OBJECT
1264public:
1265 FileTransView(QWidget *parent=0, const char *name=0)
1266 :QListView(parent, name), QToolTip(viewport())
1267 {
1268 connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this, SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
1269 }
1270
1271 void maybeTip(const QPoint &pos)
1272 {
1273 FileTransItem *i = static_cast<FileTransItem*>(itemAt(pos));
1274 if(!i)
1275 return;
1276 QRect r(itemRect(i));
1277 tip(r, i->makeTip());
1278 }
1279
1280 void resizeEvent(QResizeEvent *e)
1281 {
1282 QListView::resizeEvent(e);
1283
1284 if(e->oldSize().width() != e->size().width())
1285 doResize();
1286 }
1287
1288signals:
1289 void itemCancel(int id);
1290 void itemOpenDest(int id);
1291 void itemClear(int id);
1292
1293private slots:
1294 void qlv_contextMenuRequested(QListViewItem *item, const QPoint &pos, int)
1295 {
1296 if(!item)
1297 return;
1298
1299 FileTransItem *i = static_cast<FileTransItem*>(item);
1300
1301 QPopupMenu p;
1302 p.insertItem(tr("&Cancel"), 0);
1303 p.insertSeparator();
1304 //p.insertItem(tr("&Open Destination Folder"), 1);
1305 p.insertItem(tr("Cl&ear"), 2);
1306
1307 if(i->done) {
1308 p.setItemEnabled(0, false);
1309 }
1310 else {
1311 //p.setItemEnabled(1, false);
1312 p.setItemEnabled(2, false);
1313 }
1314
1315 int x = p.exec(pos);
1316
1317 // TODO: what if item is deleted during exec?
1318
1319 if(x == 0) {
1320 if(!i->done)
1321 itemCancel(i->id);
1322 }
1323 else if(x == 1)
1324 itemOpenDest(i->id);
1325 else if(x == 2)
1326 itemClear(i->id);
1327 }
1328
1329private:
1330 void doResize()
1331 {
1332 QListViewItemIterator it(this);
1333 for(QListViewItem *i; (i = it.current()); ++it)
1334 i->setup();
1335 }
1336};
1337
1338class TransferMapping
1339{
1340public:
1341 FileTransferHandler *h;
1342 int id;
1343 int p;
1344 Q_LLONG sent;
1345
1346 int at;
1347 Q_LLONG last[10];
1348
1349 TransferMapping()
1350 {
1351 h = 0;
1352 at = 0;
1353 }
1354
1355 ~TransferMapping()
1356 {
1357 delete h;
1358 }
1359
1360 void logSent()
1361 {
1362 // if we're at the end, shift down to make room
1363 if(at == 10) {
1364 for(int n = 0; n < at - 1; ++n)
1365 last[n] = last[n + 1];
1366 --at;
1367 }
1368 last[at++] = sent;
1369 }
1370};
1371
1372class FileTransDlg::Private
1373{
1374public:
1375 FileTransDlg *parent;
1376 PsiCon *psi;
1377 FileTransView *lv;
1378 QPtrList<TransferMapping> transferList;
1379 QTimer t;
1380
1381 Private(FileTransDlg *_parent)
1382 {
1383 parent = _parent;
1384 transferList.setAutoDelete(true);
1385 }
1386
1387 int findFreeId()
1388 {
1389 int n = 0;
1390 while(1) {
1391 bool found = false;
1392 QListViewItemIterator it(lv);
1393 for(QListViewItem *i; (i = it.current()); ++it) {
1394 FileTransItem *fi = static_cast<FileTransItem*>(i);
1395 if(fi->id == n) {
1396 found = true;
1397 break;
1398 }
1399 }
1400 if(!found)
1401 break;
1402 ++n;
1403 }
1404 return n;
1405 }
1406
1407 FileTransItem *findItem(int id)
1408 {
1409 QListViewItemIterator it(lv);
1410 for(QListViewItem *i; (i = it.current()); ++it) {
1411 FileTransItem *fi = static_cast<FileTransItem*>(i);
1412 if(fi->id == id)
1413 return fi;
1414 }
1415 return 0;
1416 }
1417
1418 QPtrList<FileTransItem> getFinished()
1419 {
1420 QPtrList<FileTransItem> list;
1421 QListViewItemIterator it(lv);
1422 for(QListViewItem *i; (i = it.current()); ++it) {
1423 FileTransItem *fi = static_cast<FileTransItem*>(i);
1424 if(fi->done)
1425 list.append(fi);
1426 }
1427 return list;
1428 }
1429
1430 TransferMapping *findMapping(FileTransferHandler *h)
1431 {
1432 QPtrListIterator<TransferMapping> it(transferList);
1433 for(TransferMapping *i; (i = it.current()); ++it) {
1434 if(i->h == h)
1435 return i;
1436 }
1437 return 0;
1438 }
1439
1440 TransferMapping *findMapping(int id)
1441 {
1442 QPtrListIterator<TransferMapping> it(transferList);
1443 for(TransferMapping *i; (i = it.current()); ++it) {
1444 if(i->id == id)
1445 return i;
1446 }
1447 return 0;
1448 }
1449
1450 void updateProgress(TransferMapping *i, bool updateAll=true)
1451 {
1452 bool done = (i->p == i->h->totalSteps());
1453
1454 // calculate bps
1455 int bps = -1;
1456 if(!done && i->at >= 2) {
1457 int seconds = i->at - 1;
1458 Q_LLONG average = i->last[i->at-1] - i->last[0];
1459 bps = ((int)average) / seconds;
1460 }
1461
1462 if(done) {
1463 FileTransItem *fi = findItem(i->id);
1464 fi->done = true;
1465 }
1466
1467 parent->setProgress(i->id, i->p, i->h->totalSteps(), i->sent, bps, updateAll);
1468
1469 if(done) {
1470 bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1471 QString fname, savename;
1472 if(recv) {
1473 fname = i->h->fileName();
1474 savename = i->h->saveName();
1475 }
1476
1477 PsiAccount *pa = i->h->account();
1478 transferList.removeRef(i);
1479
1480 if(recv) {
1481 //printf("fname: [%s], savename: [%s]\n", fname.latin1(), savename.latin1());
1482
1483 // rename .part to original filename
1484 QFileInfo fi(savename);
1485 QDir dir = fi.dir();
1486 if(dir.exists(fname))
1487 dir.remove(fname);
1488 if(!dir.rename(fi.fileName(), fname)) {
1489 // TODO: display some error about renaming
1490 }
1491 }
1492
1493 pa->playSound(option.onevent[eFTComplete]);
1494 }
1495 }
1496};
1497
1498FileTransDlg::FileTransDlg(PsiCon *psi)
1499:QDialog(0, 0, false, psi_dialog_flags)
1500{
1501 d = new Private(this);
1502 d->psi = psi;
1503 //d->psi->dialogRegister(this);
1504
1505 connect(&d->t, SIGNAL(timeout()), SLOT(updateItems()));
1506
1507 setCaption(tr("Transfer Manager"));
1508#ifndef Q_WS_MAC
1509 setIcon(IconsetFactory::icon("psi/filemanager"));
1510#endif
1511
1512 QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
1513 d->lv = new FileTransView(this);
1514 connect(d->lv, SIGNAL(itemCancel(int)), SLOT(itemCancel(int)));
1515 connect(d->lv, SIGNAL(itemOpenDest(int)), SLOT(itemOpenDest(int)));
1516 connect(d->lv, SIGNAL(itemClear(int)), SLOT(itemClear(int)));
1517 vb->addWidget(d->lv);
1518 QHBoxLayout *hb = new QHBoxLayout(vb);
1519 hb->addStretch(1);
1520 QPushButton *pb_clear = new QPushButton(tr("Clear &Finished"), this);
1521 connect(pb_clear, SIGNAL(clicked()), SLOT(clearFinished()));
1522 hb->addWidget(pb_clear);
1523 QPushButton *pb_close = new QPushButton(tr("&Hide"), this);
1524 connect(pb_close, SIGNAL(clicked()), SLOT(close()));
1525 hb->addWidget(pb_close);
1526
1527 pb_close->setDefault(true);
1528 pb_close->setFocus();
1529
1530 d->lv->addColumn("");
1531 d->lv->header()->hide();
1532
1533 d->lv->setResizeMode(QListView::LastColumn);
1534 d->lv->setAllColumnsShowFocus(true);
1535 d->lv->setSorting(-1);
1536
1537 resize(560, 240);
1538}
1539
1540FileTransDlg::~FileTransDlg()
1541{
1542 //d->psi->dialogUnregister(this);
1543 delete d;
1544}
1545
1546int FileTransDlg::addItem(const QString &filename, Q_LLONG size, const QString &peer, bool sending)
1547{
1548 int id = d->findFreeId();
1549 FileTransItem *i = new FileTransItem(d->lv, filename, size, peer, sending);
1550 if(sending)
1551 i->icon = IconsetFactory::icon("psi/upload").impix().pixmap();
1552 else
1553 i->icon = IconsetFactory::icon("psi/download").impix().pixmap();
1554 i->id = id;
1555 d->t.start(1000);
1556 return id;
1557}
1558
1559void FileTransDlg::setProgress(int id, int step, int total, Q_LLONG sent, int bytesPerSecond, bool updateAll)
1560{
1561 FileTransItem *i = d->findItem(id);
1562 if(i) {
1563 // convert steps/total into a word
1564 int progress;
1565 if(total > 0)
1566 progress = step * 8192 / total;
1567 else
1568 progress = 8192;
1569
1570 bool do_repaint = i->setProgress(progress, sent, bytesPerSecond);
1571 if(updateAll) {
1572 i->updateRate();
1573 do_repaint = true;
1574 }
1575 if(do_repaint)
1576 i->repaint();
1577 }
1578}
1579
1580void FileTransDlg::removeItem(int id)
1581{
1582 FileTransItem *i = d->findItem(id);
1583 if(i)
1584 delete i;
1585 if(d->lv->childCount() == 0)
1586 d->t.stop();
1587}
1588
1589void FileTransDlg::setError(int id, const QString &reason)
1590{
1591 FileTransItem *i = d->findItem(id);
1592 if(i) {
1593 i->done = true;
1594 i->error = reason;
1595 i->updateRate();
1596 i->repaint();
1597 show();
1598 d->lv->ensureItemVisible(i);
1599 QMessageBox::information(this, tr("Transfer Error"), tr("Transfer of %1 with %2 failed.\nReason: %3").arg(i->name).arg(i->peer).arg(reason));
1600 }
1601}
1602
1603void FileTransDlg::takeTransfer(FileTransferHandler *h, int p, Q_LLONG sent)
1604{
1605 QString peer;
1606 UserListItem *u = h->account()->findFirstRelavent(h->peer());
1607 if(u)
1608 peer = jidnick(u->jid().full(), u->name());
1609 else
1610 peer = h->peer().full();
1611
1612 TransferMapping *i = new TransferMapping;
1613 i->h = h;
1614 i->id = addItem(h->fileName(), h->fileSize(), peer, (h->mode() == FileTransferHandler::Sending));
1615 i->p = p;
1616 i->sent = sent;
1617 d->transferList.append(i);
1618
1619 FileTransItem *fi = d->findItem(i->id);
1620 d->lv->ensureItemVisible(fi);
1621
1622 if(p == i->h->totalSteps()) {
1623 d->updateProgress(i, true);
1624 }
1625 else {
1626 connect(h, SIGNAL(progress(int, Q_LLONG)), SLOT(ft_progress(int, Q_LLONG)));
1627 connect(h, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
1628 }
1629}
1630
1631void FileTransDlg::clearFinished()
1632{
1633 QPtrList<FileTransItem> list = d->getFinished();
1634 {
1635 // remove related transfer mappings
1636 QPtrListIterator<FileTransItem> it(list);
1637 for(FileTransItem *fi; (fi = it.current()); ++it) {
1638 TransferMapping *i = d->findMapping(fi->id);
1639 d->transferList.removeRef(i);
1640 }
1641 }
1642 list.setAutoDelete(true);
1643 list.clear();
1644}
1645
1646void FileTransDlg::ft_progress(int p, Q_LLONG sent)
1647{
1648 TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1649 i->p = p;
1650 i->sent = sent;
1651 if(p == i->h->totalSteps())
1652 d->updateProgress(i, true);
1653 else
1654 d->updateProgress(i, false);
1655}
1656
1657void FileTransDlg::ft_error(int x, int, const QString &s)
1658{
1659 TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
1660 int id = i->id;
1661 d->transferList.removeRef(i);
1662
1663 QString str;
1664 //if(x == FileTransferHandler::ErrReject)
1665 // str = tr("File was rejected by remote user.");
1666 if(x == FileTransferHandler::ErrTransfer)
1667 str = s;
1668 else
1669 str = tr("File I/O error");
1670 setError(id, str);
1671}
1672
1673void FileTransDlg::updateItems()
1674{
1675 // operate on a copy so that we can delete items in updateProgress
1676 QPtrList<TransferMapping> list = d->transferList;
1677 QPtrListIterator<TransferMapping> it(list);
1678 for(TransferMapping *i; (i = it.current()); ++it) {
1679 if(i->h) {
1680 i->logSent();
1681 d->updateProgress(i);
1682 }
1683 }
1684}
1685
1686void FileTransDlg::itemCancel(int id)
1687{
1688 FileTransItem *fi = d->findItem(id);
1689 TransferMapping *i = d->findMapping(id);
1690 d->transferList.removeRef(i);
1691 delete fi;
1692}
1693
1694void FileTransDlg::itemOpenDest(int id)
1695{
1696 TransferMapping *i = d->findMapping(id);
1697
1698 QString path;
1699 bool recv = (i->h->mode() == FileTransferHandler::Receiving);
1700 if(recv)
1701 path = QFileInfo(i->h->saveName()).dirPath();
1702 else
1703 path = QFileInfo(i->h->fileName()).dirPath();
1704
1705 //printf("item open dest: [%s]\n", path.latin1());
1706}
1707
1708void FileTransDlg::itemClear(int id)
1709{
1710 FileTransItem *fi = d->findItem(id);
1711 TransferMapping *i = d->findMapping(id);
1712 d->transferList.removeRef(i);
1713 delete fi;
1714}
1715
1716void FileTransDlg::killTransfers(PsiAccount *pa)
1717{
1718 QPtrList<TransferMapping> list = d->transferList;
1719 QPtrListIterator<TransferMapping> it(list);
1720 for(TransferMapping *i; (i = it.current()); ++it) {
1721 // this account?
1722 if(i->h->account() == pa) {
1723 FileTransItem *fi = d->findItem(i->id);
1724 d->transferList.removeRef(i);
1725 delete fi;
1726 }
1727 }
1728}
1729
1730#include"filetransdlg.moc"
Note: See TracBrowser for help on using the repository browser.