/* * This file Copyright (C) Mnemosyne LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "formatter.h" #include "hig.h" #include "make-dialog.h" #include "session.h" #include "utils.h" /*** **** ***/ void MakeDialog :: onNewDialogDestroyed (QObject * o) { Q_UNUSED (o); myTimer.stop (); } void MakeDialog :: onNewButtonBoxClicked (QAbstractButton * button) { switch (myNewButtonBox->standardButton (button)) { case QDialogButtonBox::Open: mySession.addNewlyCreatedTorrent (myTarget, QFileInfo(QString::fromUtf8(myBuilder->top)).dir().path()); break; case QDialogButtonBox::Abort: myBuilder->abortFlag = true; break; default: // QDialogButtonBox::Ok: break; } myNewDialog->deleteLater (); } void MakeDialog :: onProgress () { // progress bar const tr_metainfo_builder * b = myBuilder; const double denom = b->pieceCount ? b->pieceCount : 1; myNewProgress->setValue ((int) ((100.0 * b->pieceIndex) / denom)); // progress label const QString top = QString::fromLocal8Bit (myBuilder->top); const QString base (QFileInfo(top).completeBaseName()); QString str; if (!b->isDone) str = tr ("Creating \"%1\"").arg (base); else if (b->result == TR_MAKEMETA_OK) str = tr ("Created \"%1\"!").arg (base); else if (b->result == TR_MAKEMETA_URL) str = tr ("Error: invalid announce URL \"%1\"").arg (QString::fromLocal8Bit (b->errfile)); else if (b->result == TR_MAKEMETA_CANCELLED) str = tr ("Cancelled"); else if (b->result == TR_MAKEMETA_IO_READ) str = tr ("Error reading \"%1\": %2").arg (QString::fromLocal8Bit(b->errfile)).arg (QString::fromLocal8Bit(strerror(b->my_errno))); else if (b->result == TR_MAKEMETA_IO_WRITE) str = tr ("Error writing \"%1\": %2").arg (QString::fromLocal8Bit(b->errfile)).arg (QString::fromLocal8Bit(strerror(b->my_errno))); myNewLabel->setText (str); // buttons (myNewButtonBox->button(QDialogButtonBox::Abort))->setEnabled (!b->isDone); (myNewButtonBox->button(QDialogButtonBox::Ok))->setEnabled (b->isDone); (myNewButtonBox->button(QDialogButtonBox::Open))->setEnabled (b->isDone && !b->result); } void MakeDialog :: makeTorrent () { if (!myBuilder) return; // get the tiers int tier = 0; QVector trackers; foreach (QString line, myTrackerEdit->toPlainText().split("\n")) { line = line.trimmed (); if (line.isEmpty ()) { ++tier; } else { tr_tracker_info tmp; tmp.announce = tr_strdup (line.toUtf8().constData ()); tmp.tier = tier; trackers.append (tmp); } } // pop up the dialog QDialog * dialog = new QDialog (this); dialog->setWindowTitle (tr ("New Torrent")); myNewDialog = dialog; QVBoxLayout * top = new QVBoxLayout (dialog); top->addWidget( (myNewLabel = new QLabel)); top->addWidget( (myNewProgress = new QProgressBar)); QDialogButtonBox * buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Open | QDialogButtonBox::Abort); myNewButtonBox = buttons; connect (buttons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(onNewButtonBoxClicked(QAbstractButton*))); top->addWidget (buttons); onProgress (); dialog->show (); connect (dialog, SIGNAL(destroyed(QObject*)), this, SLOT(onNewDialogDestroyed(QObject*))); myTimer.start (100); // the file to create const QString path = QString::fromUtf8 (myBuilder->top); const QString torrentName = QFileInfo(path).completeBaseName() + ".torrent"; myTarget = QDir (myDestination).filePath (torrentName); // comment QString comment; if (myCommentCheck->isChecked()) comment = myCommentEdit->text(); // start making the torrent tr_makeMetaInfo (myBuilder, myTarget.toUtf8().constData(), (trackers.isEmpty() ? NULL : trackers.data()), trackers.size(), (comment.isEmpty() ? NULL : comment.toUtf8().constData()), myPrivateCheck->isChecked()); } /*** **** ***/ void MakeDialog :: onFileClicked () { QFileDialog * d = new QFileDialog (this, tr ("Select File")); d->setFileMode (QFileDialog::ExistingFile); d->setAttribute (Qt::WA_DeleteOnClose); connect (d, SIGNAL(filesSelected(const QStringList&)), this, SLOT(onFileSelected(const QStringList&))); d->show (); } void MakeDialog :: onFileSelected (const QStringList& list) { if (!list.empty ()) onFileSelected (list.front ()); } void MakeDialog :: onFileSelected (const QString& filename) { myFile = Utils::removeTrailingDirSeparator (filename); myFileButton->setText (QFileInfo(myFile).fileName()); onSourceChanged (); } void MakeDialog :: onFolderClicked () { QFileDialog * d = new QFileDialog (this, tr ("Select Folder")); d->setFileMode (QFileDialog::Directory); d->setOption (QFileDialog::ShowDirsOnly); d->setAttribute (Qt::WA_DeleteOnClose); connect (d, SIGNAL(filesSelected(const QStringList&)), this, SLOT(onFolderSelected(const QStringList&))); d->show (); } void MakeDialog :: onFolderSelected (const QStringList& list) { if (!list.empty ()) onFolderSelected (list.front ()); } void MakeDialog :: onFolderSelected (const QString& filename) { myFolder = Utils::removeTrailingDirSeparator (filename); myFolderButton->setText (QFileInfo(myFolder).fileName()); onSourceChanged (); } void MakeDialog :: onDestinationClicked () { QFileDialog * d = new QFileDialog (this, tr ("Select Folder")); d->setFileMode (QFileDialog::Directory); d->setOption (QFileDialog::ShowDirsOnly); d->setAttribute (Qt::WA_DeleteOnClose); connect (d, SIGNAL(filesSelected(const QStringList&)), this, SLOT(onDestinationSelected(const QStringList&))); d->show (); } void MakeDialog :: onDestinationSelected (const QStringList& list) { if (!list.empty ()) onDestinationSelected (list.front()); } void MakeDialog :: onDestinationSelected (const QString& filename) { myDestination = Utils::removeTrailingDirSeparator (filename); myDestinationButton->setText (QFileInfo(myDestination).fileName()); } void MakeDialog :: enableBuddyWhenChecked (QRadioButton * box, QWidget * buddy) { connect (box, SIGNAL(toggled(bool)), buddy, SLOT(setEnabled(bool))); buddy->setEnabled (box->isChecked ()); } void MakeDialog :: enableBuddyWhenChecked (QCheckBox * box, QWidget * buddy) { connect (box, SIGNAL(toggled(bool)), buddy, SLOT(setEnabled(bool))); buddy->setEnabled (box->isChecked ()); } QString MakeDialog :: getSource () const { return myFileRadio->isChecked () ? myFile : myFolder; } void MakeDialog :: onButtonBoxClicked (QAbstractButton * button) { switch (myButtonBox->standardButton (button)) { case QDialogButtonBox::Ok: makeTorrent (); break; default: // QDialogButtonBox::Close: deleteLater (); break; } } /*** **** ***/ void MakeDialog :: onSourceChanged () { if (myBuilder) { tr_metaInfoBuilderFree (myBuilder); myBuilder = 0; } const QString filename = getSource (); if (!filename.isEmpty ()) myBuilder = tr_metaInfoBuilderCreate (filename.toUtf8().constData()); QString text; if (!myBuilder) { text = tr ("No source selected"); } else { QString files = tr ("%Ln File(s)", 0, myBuilder->fileCount); QString pieces = tr ("%Ln Piece(s)", 0, myBuilder->pieceCount); text = tr ("%1 in %2; %3 @ %4") .arg (Formatter::sizeToString (myBuilder->totalSize)) .arg (files) .arg (pieces) .arg (Formatter::sizeToString (myBuilder->pieceSize)); } mySourceLabel->setText (text); } // bah, there doesn't seem to be any cleaner way to override // QPlainTextEdit's default desire to be 12 lines tall class ShortPlainTextEdit: public QPlainTextEdit { public: virtual ~ShortPlainTextEdit () {} ShortPlainTextEdit (QWidget * parent = 0): QPlainTextEdit(parent) {} virtual QSize sizeHint () const { return QSize (256, 50); } }; MakeDialog :: MakeDialog (Session & session, QWidget * parent): QDialog (parent, Qt::Dialog), mySession (session), myBuilder (0) { setAcceptDrops (true); connect (&myTimer, SIGNAL(timeout()), this, SLOT(onProgress())); setWindowTitle (tr ("New Torrent")); QVBoxLayout * top = new QVBoxLayout (this); top->setSpacing (HIG :: PAD); HIG * hig = new HIG; hig->setContentsMargins (0, 0, 0, 0); hig->addSectionTitle (tr ("Files")); QFileIconProvider iconProvider; const int iconSize (style()->pixelMetric (QStyle::PM_SmallIconSize)); const QIcon folderIcon = iconProvider.icon (QFileIconProvider::Folder); const QPixmap folderPixmap = folderIcon.pixmap (iconSize); QPushButton * b = new QPushButton; b->setIcon (folderPixmap); b->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5")); myDestination = QDir::homePath(); b->setText (myDestination); connect (b, SIGNAL(clicked(bool)), this, SLOT(onDestinationClicked(void))); myDestinationButton = b; hig->addRow (tr ("Sa&ve to:"), b); myFolderRadio = new QRadioButton (tr ("Source F&older:")); connect (myFolderRadio, SIGNAL(toggled(bool)), this, SLOT(onSourceChanged())); myFolderButton = new QPushButton; myFolderButton->setIcon (folderPixmap); myFolderButton->setText (tr ("(None)")); myFolderButton->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5")); connect (myFolderButton, SIGNAL(clicked(bool)), this, SLOT(onFolderClicked(void))); hig->addRow (myFolderRadio, myFolderButton); enableBuddyWhenChecked (myFolderRadio, myFolderButton); const QIcon fileIcon = iconProvider.icon (QFileIconProvider::File); const QPixmap filePixmap = fileIcon.pixmap (iconSize); myFileRadio = new QRadioButton (tr ("Source &File:")); myFileRadio->setChecked (true); connect (myFileRadio, SIGNAL(toggled(bool)), this, SLOT(onSourceChanged())); myFileButton = new QPushButton; myFileButton->setText (tr ("(None)")); myFileButton->setIcon (filePixmap); myFileButton->setStyleSheet (QString::fromUtf8 ("text-align: left; padding-left: 5; padding-right: 5")); connect (myFileButton, SIGNAL(clicked(bool)), this, SLOT(onFileClicked(void))); hig->addRow (myFileRadio, myFileButton); enableBuddyWhenChecked (myFileRadio, myFileButton); mySourceLabel = new QLabel (this); hig->addRow (tr (""), mySourceLabel); hig->addSectionDivider (); hig->addSectionTitle (tr ("Properties")); hig->addWideControl (myTrackerEdit = new ShortPlainTextEdit); const int height = fontMetrics().size (0, QString::fromUtf8("\n\n\n\n")).height (); myTrackerEdit->setMinimumHeight (height); hig->addTallRow (tr ("&Trackers:"), myTrackerEdit); 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.")); l->setAlignment (Qt::AlignLeft); hig->addRow (tr (""), l); myTrackerEdit->resize (500, height); myCommentCheck = new QCheckBox (tr ("Co&mment")); myCommentEdit = new QLineEdit (); hig->addRow (myCommentCheck, myCommentEdit); enableBuddyWhenChecked (myCommentCheck, myCommentEdit); myPrivateCheck = hig->addWideCheckBox (tr ("&Private torrent"), false); hig->finish (); top->addWidget (hig, 1); myButtonBox = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Close); connect (myButtonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(onButtonBoxClicked(QAbstractButton*))); top->addWidget (myButtonBox); onSourceChanged (); } MakeDialog :: ~MakeDialog () { if (myBuilder) tr_metaInfoBuilderFree (myBuilder); } /*** **** ***/ void MakeDialog :: dragEnterEvent (QDragEnterEvent * event) { const QMimeData * mime = event->mimeData (); if (mime->urls().size() && QFile(mime->urls().front().path()).exists ()) event->acceptProposedAction(); } void MakeDialog :: dropEvent (QDropEvent * event) { const QString filename = event->mimeData()->urls().front().path(); const QFileInfo fileInfo (filename); if (fileInfo.exists ()) { if (fileInfo.isDir ()) { myFolderRadio->setChecked (true); onFolderSelected (filename ); } else // it's a file { myFileRadio->setChecked (true); onFileSelected (filename); } } }