source: trunk/qt/make-dialog.cc @ 14385

Last change on this file since 14385 was 14385, checked in by mikedld, 8 years ago

Do not use void to denote argumentless functions in C++ code

  • Property svn:keywords set to Date Rev Author Id
File size: 13.0 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU Public License v2 or v3 licenses,
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: make-dialog.cc 14385 2014-12-14 11:57:23Z mikedld $
8 */
9
10#include <cassert>
11#include <iostream>
12
13#include <QCheckBox>
14#include <QDialogButtonBox>
15#include <QFileDialog>
16#include <QFileIconProvider>
17#include <QHBoxLayout>
18#include <QLabel>
19#include <QLineEdit>
20#include <QList>
21#include <QMimeData>
22#include <QPlainTextEdit>
23#include <QProgressBar>
24#include <QPushButton>
25#include <QRadioButton>
26#include <QSize>
27#include <QStyle>
28#include <QTimer>
29#include <QVBoxLayout>
30
31#include <libtransmission/transmission.h>
32#include <libtransmission/makemeta.h>
33#include <libtransmission/utils.h>
34
35#include "formatter.h"
36#include "hig.h"
37#include "make-dialog.h"
38#include "session.h"
39#include "utils.h"
40
41/***
42****
43***/
44
45void
46MakeDialog::onNewDialogDestroyed (QObject * o)
47{
48  Q_UNUSED (o);
49
50  myTimer.stop ();
51}
52
53void
54MakeDialog::onNewButtonBoxClicked (QAbstractButton * button)
55{
56  switch (myNewButtonBox->standardButton (button))
57    {
58      case QDialogButtonBox::Open:
59        mySession.addNewlyCreatedTorrent (myTarget, QFileInfo(QString::fromUtf8(myBuilder->top)).dir().path());
60        break;
61
62      case QDialogButtonBox::Abort:
63        myBuilder->abortFlag = true;
64        break;
65
66      default: // QDialogButtonBox::Ok:
67        break;
68    }
69
70  myNewDialog->deleteLater ();
71}
72
73void
74MakeDialog::onProgress ()
75{
76  // progress bar
77  const tr_metainfo_builder * b = myBuilder;
78  const double denom = b->pieceCount ? b->pieceCount : 1;
79  myNewProgress->setValue ((int) ((100.0 * b->pieceIndex) / denom));
80
81  // progress label
82  const QString top = QString::fromLocal8Bit (myBuilder->top);
83  const QString base (QFileInfo(top).completeBaseName());
84  QString str;
85  if (!b->isDone)
86    str = tr ("Creating \"%1\"").arg (base);
87  else if (b->result == TR_MAKEMETA_OK)
88    str = tr ("Created \"%1\"!").arg (base);
89  else if (b->result == TR_MAKEMETA_URL)
90    str = tr ("Error: invalid announce URL \"%1\"").arg (QString::fromLocal8Bit (b->errfile));
91  else if (b->result == TR_MAKEMETA_CANCELLED)
92    str = tr ("Cancelled");
93  else if (b->result == TR_MAKEMETA_IO_READ)
94    str = tr ("Error reading \"%1\": %2").arg (QString::fromLocal8Bit(b->errfile)).arg (QString::fromLocal8Bit(strerror(b->my_errno)));
95  else if (b->result == TR_MAKEMETA_IO_WRITE)
96    str = tr ("Error writing \"%1\": %2").arg (QString::fromLocal8Bit(b->errfile)).arg (QString::fromLocal8Bit(strerror(b->my_errno)));
97  myNewLabel->setText (str);
98
99  // buttons
100  (myNewButtonBox->button(QDialogButtonBox::Abort))->setEnabled (!b->isDone);
101  (myNewButtonBox->button(QDialogButtonBox::Ok))->setEnabled (b->isDone);
102  (myNewButtonBox->button(QDialogButtonBox::Open))->setEnabled (b->isDone && !b->result);
103}
104
105
106void
107MakeDialog::makeTorrent ()
108{
109  if (!myBuilder)
110    return;
111
112  // get the tiers
113  int tier = 0;
114  QVector<tr_tracker_info> trackers;
115  foreach (QString line, myTrackerEdit->toPlainText().split("\n"))
116    {
117      line = line.trimmed ();
118      if (line.isEmpty ())
119        {
120          ++tier;
121        }
122      else
123        {
124          tr_tracker_info tmp;
125          tmp.announce = tr_strdup (line.toUtf8().constData ());
126          tmp.tier = tier;
127          trackers.append (tmp);
128        }
129    }
130
131  // pop up the dialog
132  QDialog * dialog = new QDialog (this);
133  dialog->setWindowTitle (tr ("New Torrent"));
134  myNewDialog = dialog;
135  QVBoxLayout * top = new QVBoxLayout (dialog);
136  top->addWidget( (myNewLabel = new QLabel));
137  top->addWidget( (myNewProgress = new QProgressBar));
138  QDialogButtonBox * buttons = new QDialogButtonBox (QDialogButtonBox::Ok
139                                                   | QDialogButtonBox::Open
140                                                   | QDialogButtonBox::Abort);
141  myNewButtonBox = buttons;
142  connect (buttons, SIGNAL(clicked(QAbstractButton*)),
143           this, SLOT(onNewButtonBoxClicked(QAbstractButton*)));
144  top->addWidget (buttons);
145  onProgress ();
146  dialog->show ();
147  connect (dialog, SIGNAL(destroyed(QObject*)),
148           this, SLOT(onNewDialogDestroyed(QObject*)));
149  myTimer.start (100);
150
151  // the file to create
152  const QString path = QString::fromUtf8 (myBuilder->top);
153  const QString torrentName = QFileInfo(path).completeBaseName() + ".torrent";
154  myTarget = QDir (myDestination).filePath (torrentName);
155
156  // comment
157  QString comment;
158  if (myCommentCheck->isChecked())
159    comment = myCommentEdit->text();
160
161  // start making the torrent
162  tr_makeMetaInfo (myBuilder,
163                   myTarget.toUtf8().constData(),
164                   (trackers.isEmpty() ? NULL : trackers.data()),
165                   trackers.size(),
166                   (comment.isEmpty() ? NULL : comment.toUtf8().constData()),
167                   myPrivateCheck->isChecked());
168}
169
170/***
171****
172***/
173
174void
175MakeDialog::onFileClicked ()
176{
177  QFileDialog * d = new QFileDialog (this, tr ("Select File"));
178  d->setFileMode (QFileDialog::ExistingFile);
179  d->setAttribute (Qt::WA_DeleteOnClose);
180  connect (d, SIGNAL(filesSelected(QStringList)),
181           this, SLOT(onFileSelected(QStringList)));
182  d->show ();
183}
184void
185MakeDialog::onFileSelected (const QStringList& list)
186{
187  if (!list.empty ())
188    onFileSelected (list.front ());
189}
190void
191MakeDialog::onFileSelected (const QString& filename)
192{
193  myFile = Utils::removeTrailingDirSeparator (filename);
194  myFileButton->setText (QFileInfo(myFile).fileName());
195  onSourceChanged ();
196}
197
198void
199MakeDialog::onFolderClicked ()
200{
201  QFileDialog * d = new QFileDialog (this, tr ("Select Folder"));
202  d->setFileMode (QFileDialog::Directory);
203  d->setOption (QFileDialog::ShowDirsOnly);
204  d->setAttribute (Qt::WA_DeleteOnClose);
205  connect (d, SIGNAL(filesSelected(QStringList)),
206           this, SLOT(onFolderSelected(QStringList)));
207  d->show ();
208}
209
210void
211MakeDialog::onFolderSelected (const QStringList& list)
212{
213  if (!list.empty ())
214    onFolderSelected (list.front ());
215}
216
217void
218MakeDialog::onFolderSelected (const QString& filename)
219{
220  myFolder = Utils::removeTrailingDirSeparator (filename);
221  myFolderButton->setText (QFileInfo(myFolder).fileName());
222  onSourceChanged ();
223}
224
225void
226MakeDialog::onDestinationClicked ()
227{
228  QFileDialog * d = new QFileDialog (this, tr ("Select Folder"));
229  d->setFileMode (QFileDialog::Directory);
230  d->setOption (QFileDialog::ShowDirsOnly);
231  d->setAttribute (Qt::WA_DeleteOnClose);
232  connect (d, SIGNAL(filesSelected(QStringList)),
233           this, SLOT(onDestinationSelected(QStringList)));
234  d->show ();
235}
236void
237MakeDialog::onDestinationSelected (const QStringList& list)
238{
239  if (!list.empty ())
240    onDestinationSelected (list.front());
241}
242void
243MakeDialog::onDestinationSelected (const QString& filename)
244{
245  myDestination = Utils::removeTrailingDirSeparator (filename);
246  myDestinationButton->setText (QFileInfo(myDestination).fileName());
247}
248
249void
250MakeDialog::enableBuddyWhenChecked (QRadioButton * box, QWidget * buddy)
251{
252  connect (box, SIGNAL(toggled(bool)), buddy, SLOT(setEnabled(bool)));
253  buddy->setEnabled (box->isChecked ());
254}
255void
256MakeDialog::enableBuddyWhenChecked (QCheckBox * box, QWidget * buddy)
257{
258  connect (box, SIGNAL(toggled(bool)), buddy, SLOT(setEnabled(bool)));
259  buddy->setEnabled (box->isChecked ());
260}
261
262QString
263MakeDialog::getSource () const
264{
265  return myFileRadio->isChecked () ? myFile : myFolder;
266}
267
268void
269MakeDialog::onButtonBoxClicked (QAbstractButton * button)
270{
271  switch (myButtonBox->standardButton (button))
272    {
273      case QDialogButtonBox::Ok:
274        makeTorrent ();
275        break;
276
277      default: // QDialogButtonBox::Close:
278        deleteLater ();
279        break;
280    }
281}
282
283/***
284****
285***/
286
287void
288MakeDialog::onSourceChanged ()
289{
290  if (myBuilder)
291    {
292      tr_metaInfoBuilderFree (myBuilder);
293      myBuilder = 0;
294    }
295
296  const QString filename = getSource ();
297  if (!filename.isEmpty ())
298    myBuilder = tr_metaInfoBuilderCreate (filename.toUtf8().constData());
299
300  QString text;
301  if (!myBuilder)
302    {
303      text = tr ("<i>No source selected<i>");
304    }
305  else
306    {
307      QString files = tr ("%Ln File(s)", 0, myBuilder->fileCount);
308      QString pieces = tr ("%Ln Piece(s)", 0, myBuilder->pieceCount);
309      text = tr ("%1 in %2; %3 @ %4")
310               .arg (Formatter::sizeToString (myBuilder->totalSize))
311               .arg (files)
312               .arg (pieces)
313               .arg (Formatter::sizeToString (myBuilder->pieceSize));
314    }
315
316  mySourceLabel->setText (text);
317}
318
319
320// bah, there doesn't seem to be any cleaner way to override
321// QPlainTextEdit's default desire to be 12 lines tall
322class ShortPlainTextEdit: public QPlainTextEdit
323{
324  public:
325    virtual ~ShortPlainTextEdit () {}
326    ShortPlainTextEdit (QWidget * parent = 0): QPlainTextEdit(parent) {}
327    virtual QSize sizeHint  () const { return QSize (256, 50); }
328};
329
330MakeDialog::MakeDialog (Session& session, QWidget * parent):
331  QDialog (parent, Qt::Dialog),
332  mySession (session),
333  myBuilder (0)
334{
335  setAcceptDrops (true);
336
337  connect (&myTimer, SIGNAL(timeout()), this, SLOT(onProgress()));
338
339  setWindowTitle (tr ("New Torrent"));
340  QVBoxLayout * top = new QVBoxLayout (this);
341  top->setSpacing (HIG::PAD);
342
343  HIG * hig = new HIG;
344  hig->setContentsMargins (0, 0, 0, 0);
345  hig->addSectionTitle (tr ("Files"));
346
347    QFileIconProvider iconProvider;
348    const int iconSize (style()->pixelMetric (QStyle::PM_SmallIconSize));
349    const QIcon folderIcon = iconProvider.icon (QFileIconProvider::Folder);
350    const QPixmap folderPixmap = folderIcon.pixmap (iconSize);
351    QPushButton * b = new QPushButton;
352    b->setIcon (folderPixmap);
353    b->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5"));
354    myDestination = QDir::homePath();
355    b->setText (myDestination);
356    connect (b, SIGNAL(clicked(bool)),
357             this, SLOT(onDestinationClicked()));
358    myDestinationButton = b;
359    hig->addRow (tr ("Sa&ve to:"), b);
360
361    myFolderRadio = new QRadioButton (tr ("Source F&older:"));
362    connect (myFolderRadio, SIGNAL(toggled(bool)),
363             this, SLOT(onSourceChanged()));
364    myFolderButton = new QPushButton;
365    myFolderButton->setIcon (folderPixmap);
366    myFolderButton->setText (tr ("(None)"));
367    myFolderButton->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5"));
368    connect (myFolderButton, SIGNAL(clicked(bool)),
369             this, SLOT(onFolderClicked()));
370    hig->addRow (myFolderRadio, myFolderButton);
371    enableBuddyWhenChecked (myFolderRadio, myFolderButton);
372
373    const QIcon fileIcon = iconProvider.icon (QFileIconProvider::File);
374    const QPixmap filePixmap = fileIcon.pixmap (iconSize);
375    myFileRadio = new QRadioButton (tr ("Source &File:"));
376    myFileRadio->setChecked (true);
377    connect (myFileRadio, SIGNAL(toggled(bool)),
378             this, SLOT(onSourceChanged()));
379    myFileButton = new QPushButton;
380    myFileButton->setText (tr ("(None)"));
381    myFileButton->setIcon (filePixmap);
382    myFileButton->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5"));
383    connect (myFileButton, SIGNAL(clicked(bool)),
384             this, SLOT(onFileClicked()));
385    hig->addRow (myFileRadio, myFileButton);
386    enableBuddyWhenChecked (myFileRadio, myFileButton);
387
388    mySourceLabel = new QLabel (this);
389    hig->addRow (tr (""), mySourceLabel);
390
391  hig->addSectionDivider ();
392  hig->addSectionTitle (tr ("Properties"));
393
394    hig->addWideControl (myTrackerEdit = new ShortPlainTextEdit);
395    const int height = fontMetrics().size (0, QString::fromUtf8("\n\n\n\n")).height ();
396    myTrackerEdit->setMinimumHeight (height);
397    hig->addTallRow (tr ("&Trackers:"), myTrackerEdit);
398    QLabel * l = new QLabel (tr ("To add a backup URL, add it on the line after the primary URL.\nTo add another primary URL, add it after a blank line."));
399    l->setAlignment (Qt::AlignLeft);
400    hig->addRow (tr (""), l);
401    myTrackerEdit->resize (500, height);
402
403    myCommentCheck = new QCheckBox (tr ("Co&mment"));
404    myCommentEdit = new QLineEdit ();
405    hig->addRow (myCommentCheck, myCommentEdit);
406    enableBuddyWhenChecked (myCommentCheck, myCommentEdit);
407
408    myPrivateCheck = hig->addWideCheckBox (tr ("&Private torrent"), false);
409
410  hig->finish ();
411  top->addWidget (hig, 1);
412
413  myButtonBox = new QDialogButtonBox (QDialogButtonBox::Ok
414                                    | QDialogButtonBox::Close);
415  connect (myButtonBox, SIGNAL(clicked(QAbstractButton*)),
416           this, SLOT(onButtonBoxClicked(QAbstractButton*)));
417
418  top->addWidget (myButtonBox);
419  onSourceChanged ();
420}
421
422MakeDialog::~MakeDialog ()
423{
424  if (myBuilder)
425    tr_metaInfoBuilderFree (myBuilder);
426}
427
428/***
429****
430***/
431
432void
433MakeDialog::dragEnterEvent (QDragEnterEvent * event)
434{
435  const QMimeData * mime = event->mimeData ();
436
437  if (mime->urls().size() && QFile(mime->urls().front().path()).exists ())
438    event->acceptProposedAction();
439}
440
441void
442MakeDialog::dropEvent (QDropEvent * event)
443{
444  const QString filename = event->mimeData()->urls().front().path();
445  const QFileInfo fileInfo (filename);
446
447  if (fileInfo.exists ())
448    {
449      if (fileInfo.isDir ())
450        {
451          myFolderRadio->setChecked (true);
452          onFolderSelected (filename );
453        }
454      else // it's a file
455        {
456          myFileRadio->setChecked (true);
457          onFileSelected (filename);
458        }
459    }
460}
Note: See TracBrowser for help on using the repository browser.