19eeeff57f46d07c33a277f0ef3ccda352345d50
[vlc.git] / modules / gui / qt4 / dialogs_provider.cpp
1 /*****************************************************************************
2  * dialogs_provider.cpp : Dialog Provider
3  *****************************************************************************
4  * Copyright (C) 2006-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <QEvent>
29 #include <QApplication>
30 #include <QSignalMapper>
31 #include <QFileDialog>
32
33 #include <vlc_common.h>
34 #include "qt4.hpp"
35 #include "dialogs_provider.hpp"
36 #include "main_interface.hpp"
37 #include "menus.hpp"
38 #include <vlc_intf_strings.h>
39 #include "input_manager.hpp"
40
41 /* The dialogs */
42 #include "dialogs/playlist.hpp"
43 #include "dialogs/bookmarks.hpp"
44 #include "dialogs/preferences.hpp"
45 #include "dialogs/mediainfo.hpp"
46 #include "dialogs/messages.hpp"
47 #include "dialogs/extended.hpp"
48 #include "dialogs/vlm.hpp"
49 #include "dialogs/sout.hpp"
50 #include "dialogs/open.hpp"
51 #include "dialogs/help.hpp"
52 #include "dialogs/gototime.hpp"
53 #include "dialogs/podcast_configuration.hpp"
54
55 DialogsProvider* DialogsProvider::instance = NULL;
56
57 DialogsProvider::DialogsProvider( intf_thread_t *_p_intf ) :
58                                   QObject( NULL ), p_intf( _p_intf )
59 {
60     fixed_timer = new QTimer( this );
61     fixed_timer->start( 150 /* milliseconds */ );
62
63     menusMapper = new QSignalMapper();
64     CONNECT( menusMapper, mapped(QObject *), this, menuAction( QObject *) );
65
66     menusUpdateMapper = new QSignalMapper();
67     CONNECT( menusUpdateMapper, mapped(QObject *),
68              this, menuUpdateAction( QObject *) );
69
70     SDMapper = new QSignalMapper();
71     CONNECT( SDMapper, mapped (QString), this, SDMenuAction( QString ) );
72 }
73
74 DialogsProvider::~DialogsProvider()
75 {
76     msg_Dbg( p_intf, "Destroying the Dialog Provider" );
77     PlaylistDialog::killInstance();
78     MediaInfoDialog::killInstance();
79     MessagesDialog::killInstance();
80     ExtendedDialog::killInstance();
81     BookmarksDialog::killInstance();
82     HelpDialog::killInstance();
83 #ifdef UPDATE_CHECK
84     UpdateDialog::killInstance();
85 #endif
86
87     fixed_timer->stop();
88     delete menusMapper;
89     delete menusUpdateMapper;
90     delete SDMapper;
91 }
92
93 void DialogsProvider::quit()
94 {
95     vlc_object_kill( p_intf->p_libvlc );
96     QApplication::closeAllWindows();
97     QApplication::quit();
98 }
99
100 void DialogsProvider::customEvent( QEvent *event )
101 {
102     if( event->type() == DialogEvent_Type )
103     {
104         DialogEvent *de = static_cast<DialogEvent*>(event);
105         switch( de->i_dialog )
106         {
107         case INTF_DIALOG_FILE_SIMPLE:
108         case INTF_DIALOG_FILE:
109             openDialog(); break;
110         case INTF_DIALOG_FILE_GENERIC:
111             openFileGenericDialog( de->p_arg ); break;
112         case INTF_DIALOG_DISC:
113             openDiscDialog(); break;
114         case INTF_DIALOG_NET:
115             openNetDialog(); break;
116         case INTF_DIALOG_SAT:
117         case INTF_DIALOG_CAPTURE:
118             openCaptureDialog(); break;
119         case INTF_DIALOG_DIRECTORY:
120             PLAppendDir(); break;
121         case INTF_DIALOG_PLAYLIST:
122             playlistDialog(); break;
123         case INTF_DIALOG_MESSAGES:
124             messagesDialog(); break;
125         case INTF_DIALOG_FILEINFO:
126            mediaInfoDialog(); break;
127         case INTF_DIALOG_PREFS:
128            prefsDialog(); break;
129         case INTF_DIALOG_BOOKMARKS:
130            bookmarksDialog(); break;
131         case INTF_DIALOG_EXTENDED:
132            extendedDialog(); break;
133 #ifdef ENABLE_VLM
134         case INTF_DIALOG_VLM:
135            vlmDialog(); break;
136 #endif
137         case INTF_DIALOG_INTERACTION:
138            doInteraction( de->p_arg ); break;
139         case INTF_DIALOG_POPUPMENU:
140            QVLCMenu::PopupMenu( p_intf, (de->i_arg != 0) ); break;
141         case INTF_DIALOG_AUDIOPOPUPMENU:
142            QVLCMenu::AudioPopupMenu( p_intf ); break;
143         case INTF_DIALOG_VIDEOPOPUPMENU:
144            QVLCMenu::VideoPopupMenu( p_intf ); break;
145         case INTF_DIALOG_MISCPOPUPMENU:
146            QVLCMenu::MiscPopupMenu( p_intf ); break;
147         case INTF_DIALOG_WIZARD:
148         case INTF_DIALOG_STREAMWIZARD:
149             openThenStreamingDialogs(); break;
150 #ifdef UPDATE_CHECK
151         case INTF_DIALOG_UPDATEVLC:
152             updateDialog(); break;
153 #endif
154         case INTF_DIALOG_EXIT:
155             quit(); break;
156         default:
157            msg_Warn( p_intf, "unimplemented dialog" );
158         }
159     }
160 }
161
162 /****************************************************************************
163  * Individual simple dialogs
164  ****************************************************************************/
165 void DialogsProvider::playlistDialog()
166 {
167     PlaylistDialog::getInstance( p_intf )->toggleVisible();
168 }
169
170 void DialogsProvider::prefsDialog()
171 {
172     PrefsDialog::getInstance( p_intf )->toggleVisible();
173 }
174
175 void DialogsProvider::extendedDialog()
176 {
177     ExtendedDialog::getInstance( p_intf )->toggleVisible();
178 }
179
180 void DialogsProvider::messagesDialog()
181 {
182     MessagesDialog::getInstance( p_intf )->toggleVisible();
183 }
184
185 void DialogsProvider::gotoTimeDialog()
186 {
187     GotoTimeDialog::getInstance( p_intf )->toggleVisible();
188 }
189
190 #ifdef ENABLE_VLM
191 void DialogsProvider::vlmDialog()
192 {
193     VLMDialog::getInstance( p_intf )->toggleVisible();
194 }
195 #endif
196
197 void DialogsProvider::helpDialog()
198 {
199     HelpDialog::getInstance( p_intf )->toggleVisible();
200 }
201
202 #ifdef UPDATE_CHECK
203 void DialogsProvider::updateDialog()
204 {
205     UpdateDialog::getInstance( p_intf )->toggleVisible();
206 }
207 #endif
208
209 void DialogsProvider::aboutDialog()
210 {
211     AboutDialog::getInstance( p_intf )->toggleVisible();
212 }
213
214 void DialogsProvider::mediaInfoDialog()
215 {
216     MediaInfoDialog::getInstance( p_intf )->toggleVisible();
217 }
218
219 void DialogsProvider::mediaCodecDialog()
220 {
221     MediaInfoDialog::getInstance( p_intf )->showTab( 2 );
222 }
223
224 void DialogsProvider::bookmarksDialog()
225 {
226     BookmarksDialog::getInstance( p_intf )->toggleVisible();
227 }
228
229 void DialogsProvider::podcastConfigureDialog()
230 {
231     PodcastConfigDialog::getInstance( p_intf )->toggleVisible();
232 }
233
234
235 /****************************************************************************
236  * All the open/add stuff
237  * Open Dialog first - Simple Open then
238  ****************************************************************************/
239
240 void DialogsProvider::openDialog( int i_tab )
241 {
242     OpenDialog::getInstance( p_intf->p_sys->p_mi , p_intf )->showTab( i_tab );
243 }
244 void DialogsProvider::openDialog()
245 {
246     openDialog( OPEN_FILE_TAB );
247 }
248 void DialogsProvider::openFileGenericDialog( intf_dialog_args_t *p_arg )
249 {
250     if( p_arg == NULL )
251     {
252         msg_Warn( p_intf, "openFileGenericDialog() called with NULL arg" );
253         return;
254     }
255
256     /* Replace the extensions to a Qt format */
257     int i = 0;
258     QString extensions = qfu( p_arg->psz_extensions );
259     while ( ( i = extensions.indexOf( "|", i ) ) != -1 )
260     {
261         if( ( extensions.count( "|" ) % 2 ) == 0 )
262             extensions.replace( i, 1, ");;" );
263         else
264             extensions.replace( i, 1, "(" );
265     }
266     extensions.replace(QString(";*"), QString(" *"));
267     extensions.append( ")" );
268
269     /* Save */
270     if( p_arg->b_save )
271     {
272         QString file = QFileDialog::getSaveFileName( NULL, p_arg->psz_title,
273                             qfu( p_intf->p_sys->psz_filepath ), extensions );
274         if( !file.isEmpty() )
275         {
276             p_arg->i_results = 1;
277             p_arg->psz_results = (char **)malloc( p_arg->i_results * sizeof( char * ) );
278             p_arg->psz_results[0] = strdup( qtu( file ) );
279         }
280         else
281             p_arg->i_results = 0;
282     }
283     else /* non-save mode */
284     {
285         QStringList files = QFileDialog::getOpenFileNames( NULL,
286                 p_arg->psz_title, qfu( p_intf->p_sys->psz_filepath ),
287                 extensions );
288         p_arg->i_results = files.count();
289         p_arg->psz_results = (char **)malloc( p_arg->i_results * sizeof( char * ) );
290         i = 0;
291         foreach( QString file, files )
292             p_arg->psz_results[i++] = strdup( qtu( file ) );
293     }
294
295     /* Callback */
296     if( p_arg->pf_callback )
297         p_arg->pf_callback( p_arg );
298
299     /* Clean afterwards */
300     if( p_arg->psz_results )
301     {
302         for( i = 0; i < p_arg->i_results; i++ )
303             free( p_arg->psz_results[i] );
304         free( p_arg->psz_results );
305     }
306     free( p_arg->psz_title );
307     free( p_arg->psz_extensions );
308     free( p_arg );
309 }
310
311 void DialogsProvider::openFileDialog()
312 {
313     openDialog( OPEN_FILE_TAB );
314 }
315 void DialogsProvider::openDiscDialog()
316 {
317     openDialog( OPEN_DISC_TAB );
318 }
319 void DialogsProvider::openNetDialog()
320 {
321     openDialog( OPEN_NETWORK_TAB );
322 }
323 void DialogsProvider::openCaptureDialog()
324 {
325     openDialog( OPEN_CAPTURE_TAB );
326 }
327
328 /* Same as the open one, but force the enqueue */
329 void DialogsProvider::PLAppendDialog()
330 {
331     OpenDialog::getInstance( p_intf->p_sys->p_mi, p_intf, false, OPEN_AND_ENQUEUE)
332                             ->showTab( OPEN_FILE_TAB );
333 }
334
335 /* Unimplemmented yet - Usefull ? */
336 void DialogsProvider::MLAppendDialog()
337 {}
338
339 /**
340  * Simple open
341  ***/
342 QStringList DialogsProvider::showSimpleOpen( QString help,
343                                              int filters,
344                                              QString path )
345 {
346     QString fileTypes = "";
347     if( filters & EXT_FILTER_MEDIA ) {
348         ADD_FILTER_MEDIA( fileTypes );
349     }
350     if( filters & EXT_FILTER_VIDEO ) {
351         ADD_FILTER_VIDEO( fileTypes );
352     }
353     if( filters & EXT_FILTER_AUDIO ) {
354         ADD_FILTER_AUDIO( fileTypes );
355     }
356     if( filters & EXT_FILTER_PLAYLIST ) {
357         ADD_FILTER_PLAYLIST( fileTypes );
358     }
359     if( filters & EXT_FILTER_SUBTITLE ) {
360         ADD_FILTER_SUBTITLE( fileTypes );
361     }
362     ADD_FILTER_ALL( fileTypes );
363     fileTypes.replace(QString(";*"), QString(" *"));
364     return QFileDialog::getOpenFileNames( NULL,
365         help.isNull() ? qfu(I_OP_SEL_FILES ) : help,
366         path.isNull() ? qfu( p_intf->p_sys->psz_filepath ) : path,
367         fileTypes );
368 }
369
370 /**
371  * Open a file,
372  * pl helps you to choose from playlist or media library,
373  * go to start or enqueue
374  **/
375 void DialogsProvider::addFromSimple( bool pl, bool go)
376 {
377     QStringList files = DialogsProvider::showSimpleOpen();
378     int i = 0;
379     foreach( QString file, files )
380     {
381         const char * psz_utf8 = qtu( file );
382         playlist_Add( THEPL, psz_utf8, NULL,
383                       go ? ( PLAYLIST_APPEND | ( i ? 0 : PLAYLIST_GO ) |
384                                                ( i ? PLAYLIST_PREPARSE : 0 ) )
385                          : ( PLAYLIST_APPEND | PLAYLIST_PREPARSE ),
386                       PLAYLIST_END,
387                       pl ? true : false, false );
388         i++;
389     }
390 }
391
392 void DialogsProvider::simpleOpenDialog()
393 {
394     addFromSimple( true, true ); /* Playlist and Go */
395 }
396
397 void DialogsProvider::simplePLAppendDialog()
398 {
399     addFromSimple( true, false );
400 }
401
402 void DialogsProvider::simpleMLAppendDialog()
403 {
404     addFromSimple( false, false );
405 }
406
407 /* Directory */
408 /**
409  * Open a directory,
410  * pl helps you to choose from playlist or media library,
411  * go to start or enqueue
412  **/
413 static void openDirectory( intf_thread_t *p_intf, bool pl, bool go )
414 {
415     QString dir = QFileDialog::getExistingDirectory( 0, qtr("Open Directory") );
416     if (!dir.isEmpty()) {
417         input_item_t *p_input = input_item_NewExt( THEPL,
418                                         qtu( "directory://" + dir ), NULL,
419                                         0, NULL, -1 );
420
421         /* FIXME: playlist_AddInput() can fail */
422         playlist_AddInput( THEPL, p_input,
423                        go ? ( PLAYLIST_APPEND | PLAYLIST_GO ) : PLAYLIST_APPEND,
424                        PLAYLIST_END, pl, pl_Unlocked );
425         input_Read( THEPL, p_input, false );
426         vlc_gc_decref( p_input );
427     }
428 }
429
430 void DialogsProvider::PLAppendDir()
431 {
432     openDirectory( p_intf, true, false );
433 }
434
435 void DialogsProvider::MLAppendDir()
436 {
437     openDirectory( p_intf, false , false );
438 }
439
440 /****************
441  * Playlist     *
442  ****************/
443 void DialogsProvider::openAPlaylist()
444 {
445     QStringList files = showSimpleOpen( qtr( "Open playlist file" ),
446                                         EXT_FILTER_PLAYLIST );
447     foreach( QString file, files )
448     {
449         playlist_Import( THEPL, qtu(file) );
450     }
451 }
452
453 void DialogsProvider::saveAPlaylist()
454 {
455     QFileDialog *qfd = new QFileDialog( NULL,
456                                    qtr( "Choose a filename to save playlist" ),
457                                    qfu( p_intf->p_sys->psz_filepath ),
458                                    qtr( "XSPF playlist (*.xspf);; " ) +
459                                    qtr( "M3U playlist (*.m3u);; Any (*.*) " ) );
460     qfd->setFileMode( QFileDialog::AnyFile );
461     qfd->setAcceptMode( QFileDialog::AcceptSave );
462     qfd->setConfirmOverwrite( true );
463
464     if( qfd->exec() == QDialog::Accepted )
465     {
466         if( qfd->selectedFiles().count() > 0 )
467         {
468             static const char psz_xspf[] = "export-xspf",
469                               psz_m3u[] = "export-m3u";
470             const char *psz_module;
471
472             QString file = qfd->selectedFiles().first();
473             QString filter = qfd->selectedFilter();
474
475             if( file.contains( ".xsp" ) ||
476                 ( filter.contains( ".xspf" ) && !file.contains( ".m3u" ) ) )
477             {
478                 psz_module = psz_xspf;
479                 if( !file.contains( ".xsp" ) )
480                     file.append( ".xspf" );
481             }
482             else
483             {
484                 psz_module = psz_m3u;
485                 if( !file.contains( ".m3u" ) )
486                     file.append( ".m3u" );
487             }
488
489             playlist_Export( THEPL, qtu( file ), THEPL->p_local_category,
490                              psz_module);
491         }
492     }
493     delete qfd;
494 }
495
496
497 /****************************************************************************
498  * Sout emulation
499  ****************************************************************************/
500
501 void DialogsProvider::streamingDialog( QWidget *parent, QString mrl,
502                                        bool b_transcode_only )
503 {
504     SoutDialog *s = SoutDialog::getInstance( parent, p_intf, b_transcode_only );
505
506     if( s->exec() == QDialog::Accepted )
507     {
508         msg_Dbg( p_intf, "Sout mrl %s", qta( s->getMrl() ) );
509         /* Just do it */
510         int i_len = strlen( qtu( s->getMrl() ) ) + 10;
511         char *psz_option = (char*)malloc( i_len );
512         snprintf( psz_option, i_len - 1, "%s", qtu( s->getMrl() ) );
513
514         playlist_AddExt( THEPL, qtu( mrl ), "Streaming",
515                          PLAYLIST_APPEND | PLAYLIST_GO, PLAYLIST_END,
516                         -1, &psz_option, 1, true, pl_Unlocked );
517     }
518 }
519
520 void DialogsProvider::openThenStreamingDialogs()
521 {
522     OpenDialog::getInstance( p_intf->p_sys->p_mi, p_intf, false, OPEN_AND_STREAM )
523                                 ->showTab( OPEN_FILE_TAB );
524 }
525
526 void DialogsProvider::openThenTranscodingDialogs()
527 {
528     OpenDialog::getInstance( p_intf->p_sys->p_mi , p_intf, false, OPEN_AND_SAVE )
529                                 ->showTab( OPEN_FILE_TAB );
530 }
531
532 /****************************************************************************
533  * Menus / Interaction
534  ****************************************************************************/
535
536 void DialogsProvider::menuAction( QObject *data )
537 {
538     QVLCMenu::DoAction( p_intf, data );
539 }
540
541 void DialogsProvider::menuUpdateAction( QObject *data )
542 {
543     MenuFunc * f = qobject_cast<MenuFunc *>(data);
544     f->doFunc( p_intf );
545 }
546
547 void DialogsProvider::SDMenuAction( QString data )
548 {
549     char *psz_sd = strdup( qtu( data ) );
550     if( !playlist_IsServicesDiscoveryLoaded( THEPL, psz_sd ) )
551         playlist_ServicesDiscoveryAdd( THEPL, psz_sd );
552     else
553         playlist_ServicesDiscoveryRemove( THEPL, psz_sd );
554     free( psz_sd );
555 }
556
557 void DialogsProvider::doInteraction( intf_dialog_args_t *p_arg )
558 {
559     InteractionDialog *qdialog;
560     interaction_dialog_t *p_dialog = p_arg->p_dialog;
561     switch( p_dialog->i_action )
562     {
563     case INTERACT_NEW:
564         qdialog = new InteractionDialog( p_intf, p_dialog );
565         p_dialog->p_private = (void*)qdialog;
566         if( !(p_dialog->i_status == ANSWERED_DIALOG) )
567             qdialog->show();
568         break;
569     case INTERACT_UPDATE:
570         qdialog = (InteractionDialog*)(p_dialog->p_private);
571         if( qdialog )
572             qdialog->update();
573         else
574         {
575             /* The INTERACT_NEW message was forgotten
576                so we must create the dialog and update it*/
577             qdialog = new InteractionDialog( p_intf, p_dialog );
578             p_dialog->p_private = (void*)qdialog;
579             if( !(p_dialog->i_status == ANSWERED_DIALOG) )
580                 qdialog->show();
581             if( qdialog )
582                 qdialog->update();
583         }
584         break;
585     case INTERACT_HIDE:
586         msg_Dbg( p_intf, "Hide the Interaction Dialog" );
587         qdialog = (InteractionDialog*)(p_dialog->p_private);
588         if( qdialog )
589             qdialog->hide();
590         p_dialog->i_status = HIDDEN_DIALOG;
591         break;
592     case INTERACT_DESTROY:
593         msg_Dbg( p_intf, "Destroy the Interaction Dialog" );
594         qdialog = (InteractionDialog*)(p_dialog->p_private);
595         if( !p_dialog->i_flags & DIALOG_NONBLOCKING_ERROR )
596             delete qdialog;
597         p_dialog->i_status = DESTROYED_DIALOG;
598         break;
599     }
600 }
601
602 void DialogsProvider::loadSubtitlesFile()
603 {
604     input_thread_t *p_input = THEMIM->getInput();
605     if( !p_input )
606         return;
607     input_item_t *p_item = input_GetItem( p_input );
608     if( !p_item )
609         return;
610     char *path = input_item_GetURI( p_item );
611     if( !path )
612         path = strdup( "" );
613     char *sep = strrchr( path, DIR_SEP_CHAR );
614     if( sep )
615         *sep = '\0';
616     QStringList qsl = showSimpleOpen( qtr( "Open subtitles file" ),
617                                       EXT_FILTER_SUBTITLE,
618                                       path );
619     free( path );
620     QString qsFile;
621     foreach( qsFile, qsl )
622     {
623         if( !input_AddSubtitles( p_input, qtu( qsFile ), true ) )
624             msg_Warn( p_intf, "unable to load subtitles from '%s'",
625                       qtu( qsFile ) );
626     }
627 }