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

Last change on this file since 11098 was 11098, checked in by charles, 11 years ago

(trunk) add drag-and-drop support to the "create new torrent" dialogs in the Qt and GTK+ clients

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