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