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

Last change on this file since 13859 was 13859, checked in by jordan, 9 years ago

(qt) #5248 'fails to create torrent with multiple trackers': patch by rb07

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