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

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

support qt5 in transmission-qt

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