~ubuntu-branches/ubuntu/hoary/psi/hoary

« back to all changes in this revision

Viewing changes to src/filetransdlg.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2004-06-15 00:10:41 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040615001041-enywb6pcpe4sjsw6
Tags: 0.9.2-1
* New upstream release
* Set KDEDIR for ./configure so kde specific files get installed
* Don't install libpsiwidgets.so. It got installed in /usr/share
  where it doesn't belong. May be included (at a better location)
  later.

Show diffs side-by-side

added added

removed removed

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