1/*
2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation; either version 2 of the License, or
5 (at your option) any later version.
6*/
7
8/*
9 begin: Mit Aug 7 2002
10 copyright: (C) 2002 by Dario Abatianni
11 email: eisfuchs@tigress.com
12*/
13/*
14 Copyright (C) 2004-2008 Shintaro Matsuoka <shin@shoegazed.org>
15 Copyright (C) 2009,2010 Bernd Buschinski <b.buschinski@web.de>
16*/
17
18#include "transferpanel.h"
19#include "application.h"
20#include "transferdetailedinfopanel.h"
21#include "transfermanager.h"
22#include "transfersend.h"
23#include "preferences.h"
24#include "transferview.h"
25#include "transferlistmodel.h"
26
27#include <QSplitter>
28
29#include <KGlobal>
30#include <KMessageBox>
31#include <KMenu>
32#include <KRun>
33#include <KAuthorized>
34#include <KFileMetaInfo>
35#include <KToolBar>
36
37namespace Konversation
38{
39 namespace DCC
40 {
41 TransferPanel::TransferPanel(QWidget *parent)
42 : ChatWindow(parent)
43 {
44 setType(ChatWindow::DccTransferPanel);
45 setName(i18n("DCC Status"));
46
47 initGUI();
48
49 connect(Application::instance()->getDccTransferManager(), SIGNAL(newTransferAdded(Konversation::DCC::Transfer*)),
50 this, SLOT(slotNewTransferAdded(Konversation::DCC::Transfer*)));
51 }
52
53 TransferPanel::~TransferPanel()
54 {
55 KConfigGroup config(KGlobal::config(), "DCC Settings");
56 const QByteArray state = m_splitter->saveState();
57 config.writeEntry(QString("PanelSplitter"), state.toBase64());
58 }
59
60 void TransferPanel::initGUI()
61 {
62 setSpacing(0);
63 m_toolBar = new KToolBar(this, true, true);
64 m_toolBar->setObjectName("dccstatus_toolbar");
65
66 m_splitter = new QSplitter(this);
67 m_splitter->setOrientation(Qt::Vertical);
68
69 m_transferView = new TransferView(m_splitter);
70
71 connect(m_transferView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
72 this, SLOT(updateButton()));
73 connect(m_transferView, SIGNAL(runSelectedTransfers()),
74 this, SLOT(runDcc()));
75
76 // detailed info panel
77 m_detailPanel = new TransferDetailedInfoPanel(m_splitter);
78
79 m_splitter->setStretchFactor(0, QSizePolicy::Expanding);
80
81 // popup menu
82 m_popup = new KMenu(this);
83 m_selectAll = m_popup->addAction(i18n("&Select All Items"), this, SLOT(selectAll()));
84 m_selectAllCompleted = m_popup->addAction(i18n("S&elect All Completed Items"), this, SLOT(selectAllCompleted()));
85 m_popup->addSeparator(); // -----
86 m_accept = m_popup->addAction(KIcon("media-playback-start"), i18n("&Accept"), this, SLOT(acceptDcc()));
87 m_accept->setStatusTip(i18n("Start receiving"));
88 m_abort = m_popup->addAction(KIcon("process-stop"),i18n("A&bort"), this, SLOT(abortDcc()));
89 m_abort->setStatusTip(i18n("Abort the transfer(s)"));
90 m_popup->addSeparator(); // -----
91 m_resend = m_popup->addAction(KIcon("edit-redo"),i18n("Resend"), this, SLOT(resendFile()));
92 m_clear = m_popup->addAction(KIcon("edit-delete"),i18nc("clear selected dcctransfer","&Clear"), this, SLOT(clearDcc()));
93 m_clear->setStatusTip(i18n("Clear all selected Items"));
94 m_clearCompleted = m_popup->addAction(KIcon("edit-clear-list"),i18n("Clear Completed"), this, SLOT(clearCompletedDcc()));
95 m_clearCompleted->setStatusTip(i18n("Clear Completed Items"));
96 m_popup->addSeparator(); // -----
97 m_open = m_popup->addAction(KIcon("system-run"), i18n("&Open File"), this, SLOT(runDcc()));
98 m_open->setStatusTip(i18n("Run the file"));
99 m_openLocation = m_popup->addAction(KIcon("document-open-folder"), i18n("Open Location"), this, SLOT(openLocation()));
100 m_openLocation->setStatusTip(i18n("Open the file location"));
101 m_info = m_popup->addAction(KIcon("dialog-information"), i18n("File &Information"), this, SLOT(showFileInfo()));
102
103 m_transferView->setContextMenuPolicy(Qt::CustomContextMenu);
104 connect(m_transferView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(popupRequested(QPoint)));
105
106 // misc.
107 connect(m_transferView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(doubleClicked(QModelIndex)));
108 connect(m_transferView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
109 this, SLOT(setDetailPanelItem(QItemSelection,QItemSelection)));
110
111 m_toolBar->addAction(m_accept);
112 m_toolBar->addAction(m_abort);
113 m_toolBar->addAction(m_clear);
114 m_toolBar->addAction(m_clearCompleted);
115 m_toolBar->addAction(m_open);
116 m_toolBar->addAction(m_openLocation);
117
118 KConfigGroup config(KGlobal::config(), "DCC Settings");
119 QByteArray state;
120 if (config.hasKey("PanelSplitter"))
121 {
122 state = config.readEntry("PanelSplitter", state);
123 state = QByteArray::fromBase64(state);
124 m_splitter->restoreState(state);
125 }
126
127 updateButton();
128 }
129
130 void TransferPanel::slotNewTransferAdded(Transfer *transfer)
131 {
132 connect(transfer, SIGNAL(statusChanged(Konversation::DCC::Transfer*,int,int)), this, SLOT(slotTransferStatusChanged()));
133 m_transferView->addTransfer(transfer);
134 if (m_transferView->itemCount() == 1)
135 {
136 m_transferView->selectAll();
137 m_detailPanel->setTransfer(transfer);
138 updateButton();
139 }
140 }
141
142 void TransferPanel::slotTransferStatusChanged()
143 {
144 updateButton();
145 activateTabNotification(Konversation::tnfSystem);
146 }
147
148 void TransferPanel::updateButton()
149 {
150 bool accept = false,
151 abort = false,
152 clear = false,
153 info = false,
154 open = false,
155 openLocation = false,
156 resend = false,
157 selectAll = false,
158 selectAllCompleted = false;
159
160 QItemSelectionModel *selectionModel = m_transferView->selectionModel();
161 foreach (const QModelIndex &index, m_transferView->rowIndexes())
162 {
163 Transfer::Type type = (Transfer::Type)index.data(TransferListModel::TransferType).toInt();
164 Transfer::Status status = (Transfer::Status)index.data(TransferListModel::TransferStatus).toInt();
165
166 selectAll = true;
167 selectAllCompleted |= (status >= Transfer::Done);
168
169 if (selectionModel->isRowSelected(index.row(), QModelIndex()))
170 {
171 accept |= (status == Transfer::Queued);
172
173 abort |= (status < Transfer::Done);
174
175 clear |= (status >= Transfer::Done);
176
177 info |= (type == Transfer::Send ||
178 status == Transfer::Done);
179
180 open |= (type == Transfer::Send ||
181 status == Transfer::Done);
182
183 openLocation = true;
184
185 resend |= (type == Transfer::Send &&
186 status >= Transfer::Done);
187 }
188 }
189
190 if (!KAuthorized::authorizeKAction("allow_downloading"))
191 {
192 accept = false;
193 }
194
195 m_selectAll->setEnabled(selectAll);
196 m_selectAllCompleted->setEnabled(selectAllCompleted);
197 m_accept->setEnabled(accept);
198 m_abort->setEnabled(abort);
199 m_clear->setEnabled(clear);
200 m_clearCompleted->setEnabled(selectAllCompleted);
201 m_open->setEnabled(open);
202 m_openLocation->setEnabled(openLocation);
203 m_resend->setEnabled(resend);
204 m_info->setEnabled(info);
205 }
206
207 void TransferPanel::setDetailPanelItem (const QItemSelection &/*newindex*/, const QItemSelection &/*oldindex*/)
208 {
209 QModelIndex index;
210
211 if (m_transferView->selectionModel()->selectedRows().contains(m_transferView->selectionModel()->currentIndex()))
212 {
213 index = m_transferView->selectionModel()->currentIndex();
214 }
215 else if (!m_transferView->selectionModel()->selectedRows().isEmpty())
216 {
217 index = m_transferView->selectionModel()->selectedRows().first();
218 }
219
220 if (index.isValid())
221 {
222 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
223 if (transfer)
224 {
225 m_detailPanel->setTransfer(transfer);
226 }
227 }
228 }
229
230 void TransferPanel::acceptDcc()
231 {
232 foreach (const QModelIndex &index, m_transferView->selectedRows())
233 {
234 if (index.data(TransferListModel::TransferType).toInt() == Transfer::Receive &&
235 index.data(TransferListModel::TransferStatus).toInt() == Transfer::Queued)
236 {
237 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
238 if (transfer)
239 {
240 transfer->start();
241 }
242 }
243 }
244 updateButton();
245 }
246
247 void TransferPanel::abortDcc()
248 {
249 foreach (const QModelIndex &index, m_transferView->selectedRows())
250 {
251 if (index.data(TransferListModel::TransferStatus).toInt() < Transfer::Done)
252 {
253 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
254 if (transfer)
255 {
256 transfer->abort();
257 }
258 }
259 }
260 updateButton();
261 }
262
263 void TransferPanel::resendFile()
264 {
265 QList<Transfer*> transferList;
266 foreach (const QModelIndex &index, m_transferView->selectedRows())
267 {
268 if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send &&
269 index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done)
270 {
271 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
272 if (!transfer)
273 {
274 continue;
275 }
276 transferList.append(transfer);
277 }
278 }
279
280 foreach (Transfer* transfer, transferList)
281 {
282 TransferSend *newTransfer = Application::instance()->getDccTransferManager()->newUpload();
283
284 newTransfer->setConnectionId(transfer->getConnectionId());
285 newTransfer->setPartnerNick(transfer->getPartnerNick());
286 newTransfer->setFileURL(transfer->getFileURL());
287 newTransfer->setFileName(transfer->getFileName());
288 newTransfer->setReverse(transfer->isReverse());
289
290 if (newTransfer->queue())
291 {
292 newTransfer->start();
293 }
294 }
295 }
296
297 //sort QModelIndexList descending
298 bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
299 {
300 return index1.row() >= index2.row();
301 }
302
303 void TransferPanel::clearDcc()
304 {
305 //selected item
306 Transfer *transfer = m_detailPanel->transfer();
307 if (transfer && transfer->getStatus() >= Transfer::Done)
308 {
309 //item will be gone
310 transfer = 0;
311 }
312
313 QModelIndexList indexes = m_transferView->selectedRows();
314 QModelIndexList indexesToRemove;
315
316 foreach (const QModelIndex &index, indexes)
317 {
318 if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done)
319 {
320 indexesToRemove.append(index);
321 }
322 }
323
324 //sort QModelIndexList descending
325 //NOTE: selectedRows() returned an unsorted list
326 qSort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan);
327
328 //remove from last to first item, to keep a valid row
329 foreach (const QModelIndex &index, indexesToRemove)
330 {
331 m_transferView->model()->removeRow(index.row(), QModelIndex());
332 //needed, otherwise valid rows "can be treated" as invalid,
333 //proxymodel does not keep up with changes
334 m_transferView->updateModel();
335 }
336
337 //remove all gone items
338 foreach (const QModelIndex &index, indexesToRemove)
339 {
340 indexes.removeOne(index);
341 }
342
343 m_transferView->clearSelection();
344 QList<int> toSelectList;
345 //select everything that got not removed
346 foreach (const QModelIndex &index, indexes)
347 {
348 int offset = 0;
349 foreach (const QModelIndex &removedIndex, indexesToRemove)
350 {
351 if (removedIndex.row() < index.row())
352 {
353 ++offset;
354 }
355 }
356 toSelectList.append(index.row() - offset);
357 }
358 m_transferView->selectRows(toSelectList);
359
360 if (transfer)
361 {
362 m_detailPanel->setTransfer(transfer);
363 }
364 else if (!transfer || m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0)
365 {
366 m_detailPanel->clear();
367 }
368
369 updateButton();
370 }
371
372 void TransferPanel::clearCompletedDcc()
373 {
374 //save selected item
375 Transfer *transfer = m_detailPanel->transfer();
376 if (transfer && transfer->getStatus() >= Transfer::Done)
377 {
378 //item will be gone
379 transfer = 0;
380 }
381
382 QModelIndexList indexesToRemove;
383 QModelIndexList selectedIndexes = m_transferView->selectedRows();
384
385 foreach (const QModelIndex &index, m_transferView->rowIndexes())
386 {
387 if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done)
388 {
389 indexesToRemove.append(index);
390 }
391 }
392
393 //sort QModelIndexList descending
394 //NOTE: selectedRows() returned an unsorted list
395 qSort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan);
396
397 //remove from last to first item, to keep a valid row
398 foreach (const QModelIndex &index, indexesToRemove)
399 {
400 m_transferView->model()->removeRow(index.row(), QModelIndex());
401 //needed, otherwise valid rows "can be treated" as invalid,
402 //proxymodel does not keep up with changes
403 m_transferView->updateModel();
404 }
405
406 //remove all gone items
407 foreach (const QModelIndex &index, indexesToRemove)
408 {
409 selectedIndexes.removeOne(index);
410 }
411
412 m_transferView->clearSelection();
413 QList<int> toSelectList;
414 //select everything that got not removed
415 foreach (const QModelIndex &index, selectedIndexes)
416 {
417 int offset = 0;
418 foreach (const QModelIndex &removedIndex, indexesToRemove)
419 {
420 if (removedIndex.row() < index.row())
421 {
422 ++offset;
423 }
424 }
425 toSelectList.append(index.row() - offset);
426 }
427 m_transferView->selectRows(toSelectList);
428
429 if (transfer)
430 {
431 m_detailPanel->setTransfer(transfer);
432 }
433 else if (m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0 || !transfer)
434 {
435 m_detailPanel->clear();
436 }
437
438 updateButton();
439 }
440
441 void TransferPanel::runDcc()
442 {
443 const int selectedRows = m_transferView->selectedRows().count();
444 if (selectedRows > 3)
445 {
446 int ret = KMessageBox::questionYesNo(this,
447 i18np("You have selected %1 file to execute, are you sure you want to continue?",
448 "You have selected %1 files to execute, are you sure you want to continue?",
449 selectedRows),
450 i18np("Execute %1 file",
451 "Execute %1 files",
452 selectedRows)
453 );
454
455 if (ret == KMessageBox::No)
456 {
457 return;
458 }
459 }
460
461 foreach (const QModelIndex &index, m_transferView->selectedRows())
462 {
463 if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send ||
464 index.data(TransferListModel::TransferStatus).toInt() == Transfer::Done)
465 {
466 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
467 if (transfer)
468 {
469 transfer->runFile();
470 }
471 }
472 }
473 }
474
475 void TransferPanel::openLocation()
476 {
477 foreach (const QModelIndex &index, m_transferView->selectedRows())
478 {
479 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
480 if (transfer)
481 {
482 openLocation(transfer);
483 }
484 }
485 }
486
487 void TransferPanel::showFileInfo()
488 {
489 foreach (const QModelIndex &index, m_transferView->selectedRows())
490 {
491 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
492 if (transfer)
493 {
494 openFileInfoDialog(transfer);
495 }
496 }
497 }
498
499 void TransferPanel::selectAll()
500 {
501 m_transferView->selectAll();
502 updateButton();
503 }
504
505 void TransferPanel::selectAllCompleted()
506 {
507 m_transferView->selectAllCompleted();
508 updateButton();
509 }
510
511 void TransferPanel::popupRequested(const QPoint &pos)
512 {
513 updateButton();
514 m_popup->popup(QWidget::mapToGlobal(m_transferView->viewport()->mapTo(this, pos)));
515 }
516
517 void TransferPanel::doubleClicked(const QModelIndex &index)
518 {
519 Transfer *transfer = static_cast<Transfer*>(qVariantValue<QObject*>(index.data(TransferListModel::TransferPointer)));
520 if (transfer)
521 {
522 transfer->runFile();
523 }
524 }
525
526 // virtual
527 void TransferPanel::childAdjustFocus()
528 {
529 }
530
531 TransferView *TransferPanel::getTransferView()
532 {
533 return m_transferView;
534 }
535
536 void TransferPanel::openLocation(Transfer *transfer)
537 {
538 QString urlString = transfer->getFileURL().path();
539 if (!urlString.isEmpty())
540 {
541 KUrl url(urlString);
542 url.setFileName(QString());
543 new KRun(url, 0, 0, true, true);
544 }
545 }
546
547 void TransferPanel::openFileInfoDialog(Transfer *transfer)
548 {
549 if (transfer->getType() == Transfer::Send || transfer->getStatus() == Transfer::Done)
550 {
551 QPointer<FileMetaDataDialog> fileDialog = new FileMetaDataDialog(transfer->getFileURL(), this);
552 fileDialog->exec();
553 delete fileDialog;
554 }
555 }
556
557 }
558}
559
560#include "transferpanel.moc"
561