~ubuntu-branches/ubuntu/jaunty/psi/jaunty

« back to all changes in this revision

Viewing changes to src/filetransdlg.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

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