]> git.sesse.net Git - vlc/blob - modules/gui/qt4/main_interface.cpp
Qt4: fix a minor (but annoying) UI glitch on restart
[vlc] / modules / gui / qt4 / main_interface.cpp
1 /*****************************************************************************
2  * main_interface.cpp : Main interface
3  ****************************************************************************
4  * Copyright (C) 2006-2010 VideoLAN and AUTHORS
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *          Ilkka Ollakka <ileoo@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "qt4.hpp"
31
32 #include "main_interface.hpp"
33 #include "input_manager.hpp"                    // Creation
34 #include "actions_manager.hpp"                  // killInstance
35 #include "extensions_manager.hpp"               // killInstance
36
37 #include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
38 #include "util/qt_dirs.hpp"                     // toNativeSeparators
39
40 #include "components/interface_widgets.hpp"     // bgWidget, videoWidget
41 #include "components/controller.hpp"            // controllers
42 #include "components/playlist/playlist.hpp"     // plWidget
43 #include "dialogs/firstrun.hpp"                 // First Run
44
45 #include "menus.hpp"                            // Menu creation
46 #include "recents.hpp"                          // RecentItems when DnD
47
48 #include <QCloseEvent>
49 #include <QKeyEvent>
50
51 #include <QUrl>
52 #include <QSize>
53 #include <QDate>
54
55 #include <QMenu>
56 #include <QMenuBar>
57 #include <QStatusBar>
58 #include <QLabel>
59 #include <QStackedWidget>
60
61 #include <vlc_keys.h>                       /* Wheel event */
62 #include <vlc_vout_display.h>               /* vout_thread_t and VOUT_ events */
63
64 // #define DEBUG_INTF
65
66 /* Callback prototypes */
67 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
68                         vlc_value_t old_val, vlc_value_t new_val, void *param );
69 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
70                        vlc_value_t old_val, vlc_value_t new_val, void *param );
71
72 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
73 {
74     /* Variables initialisation */
75     bgWidget             = NULL;
76     videoWidget          = NULL;
77     playlistWidget       = NULL;
78     stackCentralOldWidget= NULL;
79 #ifndef HAVE_MAEMO
80     sysTray              = NULL;
81 #endif
82     fullscreenControls   = NULL;
83     cryptedLabel         = NULL;
84     controls             = NULL;
85     inputC               = NULL;
86
87     b_hideAfterCreation  = false; // --qt-start-minimized
88     playlistVisible      = false;
89     input_name           = "";
90
91
92     /* Ask for Privacy */
93     FirstRun::CheckAndRun( this, p_intf );
94
95     /**
96      *  Configuration and settings
97      *  Pre-building of interface
98      **/
99     /* Main settings */
100     setFocusPolicy( Qt::StrongFocus );
101     setAcceptDrops( true );
102     setWindowRole( "vlc-main" );
103     setWindowIcon( QApplication::windowIcon() );
104     setWindowOpacity( var_InheritFloat( p_intf, "qt-opacity" ) );
105 #ifdef Q_WS_MAC
106     setAttribute( Qt::WA_MacBrushedMetal );
107 #endif
108
109     /* Is video in embedded in the UI or not */
110     b_videoEmbedded = var_InheritBool( p_intf, "embedded-video" );
111
112     /* Does the interface resize to video size or the opposite */
113     b_autoresize = var_InheritBool( p_intf, "qt-video-autoresize" );
114
115     /* Are we in the enhanced always-video mode or not ? */
116     b_minimalView = var_InheritBool( p_intf, "qt-minimal-view" );
117
118     /* Do we want anoying popups or not */
119     b_notificationEnabled = var_InheritBool( p_intf, "qt-notification" );
120
121     /* Set the other interface settings */
122     settings = getSettings();
123     settings->beginGroup( "MainWindow" );
124
125     /* */
126     b_plDocked = getSettings()->value( "pl-dock-status", true ).toBool();
127
128     settings->endGroup( );
129
130     /**************
131      * Status Bar *
132      **************/
133     createStatusBar();
134
135     /**************************
136      *  UI and Widgets design
137      **************************/
138     setVLCWindowsTitle();
139
140     /************
141      * Menu Bar *
142      ************/
143     QVLCMenu::createMenuBar( this, p_intf );
144     CONNECT( THEMIM->getIM(), voutListChanged( vout_thread_t **, int ),
145              this, destroyPopupMenu() );
146
147     createMainWidget( settings );
148     /*********************************
149      * Create the Systray Management *
150      *********************************/
151     initSystray();
152
153     /********************
154      * Input Manager    *
155      ********************/
156     MainInputManager::getInstance( p_intf );
157
158 #ifdef WIN32
159     himl = NULL;
160     p_taskbl = NULL;
161     taskbar_wmsg = RegisterWindowMessage("TaskbarButtonCreated");
162 #endif
163
164     /************************************************************
165      * Connect the input manager to the GUI elements it manages *
166      ************************************************************/
167     /**
168      * Connects on nameChanged()
169      * Those connects are different because options can impeach them to trigger.
170      **/
171     /* Main Interface statusbar */
172     CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
173              this, setName( const QString& ) );
174     /* and systray */
175 #ifndef HAVE_MAEMO
176     if( sysTray )
177     {
178         CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
179                  this, updateSystrayTooltipName( const QString& ) );
180     }
181 #endif
182     /* and title of the Main Interface*/
183     if( var_InheritBool( p_intf, "qt-name-in-title" ) )
184     {
185         CONNECT( THEMIM->getIM(), nameChanged( const QString& ),
186                  this, setVLCWindowsTitle( const QString& ) );
187     }
188
189     /**
190      * CONNECTS on PLAY_STATUS
191      **/
192     /* Status on the systray */
193 #ifndef HAVE_MAEMO
194     if( sysTray )
195     {
196         CONNECT( THEMIM->getIM(), statusChanged( int ),
197                  this, updateSystrayTooltipStatus( int ) );
198     }
199 #endif
200
201     /* END CONNECTS ON IM */
202
203     /* VideoWidget connects for asynchronous calls */
204     b_videoFullScreen = false;
205     b_videoOnTop = false;
206     connect( this, SIGNAL(askGetVideo(WId*,int*,int*,unsigned*,unsigned *)),
207              this, SLOT(getVideoSlot(WId*,int*,int*,unsigned*,unsigned*)),
208              Qt::BlockingQueuedConnection );
209     connect( this, SIGNAL(askReleaseVideo( void )),
210              this, SLOT(releaseVideoSlot( void )),
211              Qt::BlockingQueuedConnection );
212     CONNECT( this, askVideoOnTop(bool), this, setVideoOnTop(bool));
213
214     if( videoWidget )
215     {
216         if( b_autoresize )
217         {
218             CONNECT( this, askVideoToResize( unsigned int, unsigned int ),
219                      this, setVideoSize( unsigned int, unsigned int ) );
220             CONNECT( videoWidget, sizeChanged( int, int ),
221                      this, resizeStack( int,  int ) );
222         }
223         CONNECT( this, askVideoSetFullScreen( bool ),
224                  this, setVideoFullScreen( bool ) );
225     }
226
227     CONNECT( THEDP, toolBarConfUpdated(), this, recreateToolbars() );
228
229     /** END of CONNECTS**/
230
231
232     /************
233      * Callbacks
234      ************/
235     var_AddCallback( p_intf->p_libvlc, "intf-show", IntfShowCB, p_intf );
236
237     /* Register callback for the intf-popupmenu variable */
238     var_AddCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
239
240     /* Playlist */
241     int i_plVis = settings->value( "MainWindow/playlist-visible", false ).toBool();
242
243     if( i_plVis ) togglePlaylist();
244
245     /**** FINAL SIZING and placement of interface */
246     settings->beginGroup( "MainWindow" );
247     QVLCTools::restoreWidgetPosition( settings, this, QSize(400, 100) );
248     settings->endGroup();
249
250     b_interfaceFullScreen = isFullScreen();
251
252     /* Final sizing and showing */
253     setVisible( !b_hideAfterCreation );
254
255     setMinimumWidth( __MAX( controls->sizeHint().width(),
256                             menuBar()->sizeHint().width() ) + 30 );
257
258     /* Switch to minimal view if needed, must be called after the show() */
259     if( b_minimalView )
260         toggleMinimalView( true );
261 }
262
263 MainInterface::~MainInterface()
264 {
265     /* Unsure we hide the videoWidget before destroying it */
266     if( stackCentralOldWidget == videoWidget )
267         showTab( bgWidget );
268
269 #ifdef WIN32
270     if( himl )
271         ImageList_Destroy( himl );
272     if(p_taskbl)
273         p_taskbl->vt->Release(p_taskbl);
274     CoUninitialize();
275 #endif
276
277     /* Be sure to kill the actionsManager... Only used in the MI and control */
278     ActionsManager::killInstance();
279
280     /* Idem */
281     ExtensionsManager::killInstance();
282
283     /* Delete the FSC controller */
284     delete fullscreenControls;
285
286     /* Save states */
287     settings->beginGroup( "MainWindow" );
288
289     settings->setValue( "pl-dock-status", b_plDocked );
290     /* Save playlist state */
291     if( playlistWidget )
292         settings->setValue( "playlist-visible", playlistVisible );
293
294     settings->setValue( "adv-controls",
295                         getControlsVisibilityStatus() & CONTROLS_ADVANCED );
296
297     /* Save the stackCentralW sizes */
298     settings->setValue( "bgSize", stackWidgetsSizes[bgWidget] );
299     settings->setValue( "playlistSize", stackWidgetsSizes[playlistWidget] );
300
301     /* Save this size */
302     QVLCTools::saveWidgetPosition(settings, this);
303
304     settings->endGroup();
305
306     /* Save undocked playlist size */
307     if( playlistWidget && !isPlDocked() )
308         QVLCTools::saveWidgetPosition( p_intf, "Playlist", playlistWidget );
309
310     delete playlistWidget;
311
312     delete statusBar();
313
314     /* Unregister callbacks */
315     var_DelCallback( p_intf->p_libvlc, "intf-show", IntfShowCB, p_intf );
316     var_DelCallback( p_intf->p_libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
317
318     p_intf->p_sys->p_mi = NULL;
319 }
320
321 /*****************************
322  *   Main UI handling        *
323  *****************************/
324 void MainInterface::recreateToolbars()
325 {
326     bool b_adv = getControlsVisibilityStatus() & CONTROLS_ADVANCED;
327
328     settings->beginGroup( "MainWindow" );
329     delete controls;
330     delete inputC;
331
332     controls = new ControlsWidget( p_intf, b_adv, this );
333     inputC = new InputControlsWidget( p_intf, this );
334
335     if( fullscreenControls )
336     {
337         delete fullscreenControls;
338         fullscreenControls = new FullscreenControllerWidget( p_intf, this );
339         CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
340                  this, handleKeyPress( QKeyEvent * ) );
341     }
342     mainLayout->insertWidget( 2, inputC );
343     mainLayout->insertWidget( settings->value( "ToolbarPos", 0 ).toInt() ? 0: 3,
344                               controls );
345     settings->endGroup();
346 }
347
348 void MainInterface::createMainWidget( QSettings *settings )
349 {
350     /* Create the main Widget and the mainLayout */
351     QWidget *main = new QWidget;
352     setCentralWidget( main );
353     mainLayout = new QVBoxLayout( main );
354     main->setContentsMargins( 0, 0, 0, 0 );
355     mainLayout->setSpacing( 0 ); mainLayout->setMargin( 0 );
356
357     /* */
358     stackCentralW = new QVLCStackedWidget( main );
359
360     /* Bg Cone */
361     bgWidget = new BackgroundWidget( p_intf );
362     stackCentralW->addWidget( bgWidget );
363
364     /* And video Outputs */
365     if( b_videoEmbedded )
366     {
367         videoWidget = new VideoWidget( p_intf );
368         stackCentralW->addWidget( videoWidget );
369     }
370     mainLayout->insertWidget( 1, stackCentralW );
371
372     settings->beginGroup( "MainWindow" );
373     stackWidgetsSizes[bgWidget] = settings->value( "bgSize", QSize( 400, 0 ) ).toSize();
374     /* Resize even if no-auto-resize, because we are at creation */
375     resizeStack( stackWidgetsSizes[bgWidget].width(), stackWidgetsSizes[bgWidget].height() );
376
377     /* Create the CONTROLS Widget */
378     controls = new ControlsWidget( p_intf,
379                    settings->value( "adv-controls", false ).toBool(), this );
380     inputC = new InputControlsWidget( p_intf, this );
381
382     mainLayout->insertWidget( 2, inputC );
383     mainLayout->insertWidget( settings->value( "ToolbarPos", 0 ).toInt() ? 0: 3,
384                               controls );
385
386     /* Visualisation, disabled for now, they SUCK */
387     #if 0
388     visualSelector = new VisualSelector( p_intf );
389     mainLayout->insertWidget( 0, visualSelector );
390     visualSelector->hide();
391     #endif
392
393     settings->endGroup();
394
395     /* Enable the popup menu in the MI */
396     main->setContextMenuPolicy( Qt::CustomContextMenu );
397     CONNECT( main, customContextMenuRequested( const QPoint& ),
398              this, popupMenu( const QPoint& ) );
399
400     if ( depth() > 8 ) /* 8bit depth has too many issues with opacity */
401         /* Create the FULLSCREEN CONTROLS Widget */
402         if( var_InheritBool( p_intf, "qt-fs-controller" ) )
403         {
404             fullscreenControls = new FullscreenControllerWidget( p_intf, this );
405             CONNECT( fullscreenControls, keyPressed( QKeyEvent * ),
406                      this, handleKeyPress( QKeyEvent * ) );
407         }
408 }
409
410 inline void MainInterface::initSystray()
411 {
412 #ifndef HAVE_MAEMO
413     bool b_systrayAvailable = QSystemTrayIcon::isSystemTrayAvailable();
414     bool b_systrayWanted = var_InheritBool( p_intf, "qt-system-tray" );
415
416     if( var_InheritBool( p_intf, "qt-start-minimized") )
417     {
418         if( b_systrayAvailable )
419         {
420             b_systrayWanted = true;
421             b_hideAfterCreation = true;
422         }
423         else
424             msg_Err( p_intf, "cannot start minimized without system tray bar" );
425     }
426
427     if( b_systrayAvailable && b_systrayWanted )
428         createSystray();
429 #endif
430 }
431
432 inline void MainInterface::createStatusBar()
433 {
434     /****************
435      *  Status Bar  *
436      ****************/
437     /* Widgets Creation*/
438     QStatusBar *statusBarr = statusBar();
439
440     TimeLabel *timeLabel = new TimeLabel( p_intf );
441     nameLabel = new QLabel( this );
442     nameLabel->setTextInteractionFlags( Qt::TextSelectableByMouse
443                                       | Qt::TextSelectableByKeyboard );
444     SpeedLabel *speedLabel = new SpeedLabel( p_intf, this );
445
446     /* Styling those labels */
447     timeLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
448     speedLabel->setFrameStyle( QFrame::Sunken | QFrame::Panel );
449     nameLabel->setFrameStyle( QFrame::Sunken | QFrame::StyledPanel);
450     timeLabel->setStyleSheet(
451             "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
452     speedLabel->setStyleSheet(
453             "QLabel:hover { background-color: rgba(255, 255, 255, 50%) }" );
454
455     /* and adding those */
456     statusBarr->addWidget( nameLabel, 8 );
457     statusBarr->addPermanentWidget( speedLabel, 0 );
458     statusBarr->addPermanentWidget( timeLabel, 0 );
459
460     /* timeLabel behaviour:
461        - double clicking opens the goto time dialog
462        - right-clicking and clicking just toggle between remaining and
463          elapsed time.*/
464     CONNECT( timeLabel, timeLabelDoubleClicked(), THEDP, gotoTimeDialog() );
465
466     CONNECT( THEMIM->getIM(), encryptionChanged( bool ),
467              this, showCryptedLabel( bool ) );
468
469     CONNECT( THEMIM->getIM(), seekRequested( float ),
470              timeLabel, setDisplayPosition( float ) );
471
472     /* This shouldn't be necessary, but for somehow reason, the statusBarr
473        starts at height of 20px and when a text is shown it needs more space.
474        But, as the QMainWindow policy doesn't allow statusBar to change QMW's
475        geometry, we need to force a height. If you have a better idea, please
476        tell me -- jb
477      */
478     statusBarr->setFixedHeight( statusBarr->sizeHint().height() + 2 );
479 }
480
481 /**********************************************************************
482  * Handling of sizing of the components
483  **********************************************************************/
484
485 void MainInterface::debug()
486 {
487 #ifdef DEBUG_INTF
488     msg_Dbg( p_intf, "size: %i - %i", size().height(), size().width() );
489     msg_Dbg( p_intf, "sizeHint: %i - %i", sizeHint().height(), sizeHint().width() );
490     msg_Dbg( p_intf, "minimumsize: %i - %i", minimumSize().height(), minimumSize().width() );
491
492     msg_Dbg( p_intf, "Stack size: %i - %i", stackCentralW->size().height(), stackCentralW->size().width() );
493     msg_Dbg( p_intf, "Stack sizeHint: %i - %i", stackCentralW->sizeHint().height(), stackCentralW->sizeHint().width() );
494     msg_Dbg( p_intf, "Central size: %i - %i", centralWidget()->size().height(), centralWidget()->size().width() );
495 #endif
496 }
497
498 inline void MainInterface::showVideo() { showTab( videoWidget ); }
499 inline void MainInterface::restoreStackOldWidget()
500             { showTab( stackCentralOldWidget ); }
501
502 inline void MainInterface::showTab( QWidget *widget )
503 {
504 #ifdef DEBUG_INTF
505     msg_Warn( p_intf, "Old stackCentralOldWidget %i", stackCentralW->indexOf( stackCentralOldWidget ) );
506 #endif
507
508     stackCentralOldWidget = stackCentralW->currentWidget();
509     stackWidgetsSizes[stackCentralOldWidget] = stackCentralW->size();
510
511     stackCentralW->setCurrentWidget( widget );
512     if( b_autoresize )
513         resizeStack( stackWidgetsSizes[widget].width(), stackWidgetsSizes[widget].height() );
514
515 #ifdef DEBUG_INTF
516     msg_Warn( p_intf, "State change %i",  stackCentralW->currentIndex() );
517     msg_Warn( p_intf, "New stackCentralOldWidget %i", stackCentralW->indexOf( stackCentralOldWidget ) );
518 #endif
519 }
520
521 void MainInterface::destroyPopupMenu()
522 {
523     QVLCMenu::PopupMenu( p_intf, false );
524 }
525
526 void MainInterface::popupMenu( const QPoint &p )
527 {
528     QVLCMenu::PopupMenu( p_intf, true );
529 }
530
531 void MainInterface::toggleFSC()
532 {
533    if( !fullscreenControls ) return;
534
535    IMEvent *eShow = new IMEvent( FullscreenControlToggle_Type, 0 );
536    QApplication::postEvent( fullscreenControls, eShow );
537 }
538
539 /****************************************************************************
540  * Video Handling
541  ****************************************************************************/
542
543 /**
544  * NOTE:
545  * You must not change the state of this object or other Qt4 UI objects,
546  * from the video output thread - only from the Qt4 UI main loop thread.
547  * All window provider queries must be handled through signals or events.
548  * That's why we have all those emit statements...
549  */
550 WId MainInterface::getVideo( int *pi_x, int *pi_y,
551                              unsigned int *pi_width, unsigned int *pi_height )
552 {
553     if( !videoWidget )
554         return 0;
555
556     /* This is a blocking call signal. Results are returned through pointers.
557      * Beware of deadlocks! */
558     WId id;
559     emit askGetVideo( &id, pi_x, pi_y, pi_width, pi_height );
560     return id;
561 }
562
563 void MainInterface::getVideoSlot( WId *p_id, int *pi_x, int *pi_y,
564                                   unsigned *pi_width, unsigned *pi_height )
565 {
566     /* Request the videoWidget */
567     WId ret = videoWidget->request( pi_x, pi_y,
568                                     pi_width, pi_height, !b_autoresize );
569     *p_id = ret;
570     if( ret ) /* The videoWidget is available */
571     {
572         /* Consider the video active now */
573         showVideo();
574
575         /* Ask videoWidget to resize correctly, if we are in normal mode */
576         if( !isFullScreen() && !isMaximized() && b_autoresize )
577             videoWidget->SetSizing( *pi_width, *pi_height );
578     }
579 }
580
581 /* Asynchronous call from the WindowClose function */
582 void MainInterface::releaseVideo( void )
583 {
584     emit askReleaseVideo();
585 }
586
587 /* Function that is CONNECTED to the previous emit */
588 void MainInterface::releaseVideoSlot( void )
589 {
590     videoWidget->release();
591     setVideoOnTop( false );
592     setVideoFullScreen( false );
593
594     if( stackCentralW->currentWidget() == videoWidget )
595         restoreStackOldWidget();
596
597     /* We don't want to have a blank video to popup */
598     stackCentralOldWidget = bgWidget;
599 }
600
601 void MainInterface::setVideoSize( unsigned int w, unsigned int h )
602 {
603     if( !isFullScreen() && !isMaximized() )
604         videoWidget->SetSizing( w, h );
605 }
606
607 void MainInterface::setVideoFullScreen( bool fs )
608 {
609     b_videoFullScreen = fs;
610     if( fs )
611     {
612         int numscreen = var_InheritInteger( p_intf, "qt-fullscreen-screennumber" );
613         /* if user hasn't defined screennumber, or screennumber that is bigger
614          * than current number of screens, take screennumber where current interface
615          * is
616          */
617         if( numscreen == -1 || numscreen > QApplication::desktop()->numScreens() )
618             numscreen = QApplication::desktop()->screenNumber( p_intf->p_sys->p_mi );
619
620         QRect screenres = QApplication::desktop()->screenGeometry( numscreen );
621
622         /* To be sure window is on proper-screen in xinerama */
623         if( !screenres.contains( pos() ) )
624         {
625             msg_Dbg( p_intf, "Moving video to correct screen");
626             move( QPoint( screenres.x(), screenres.y() ) );
627         }
628         setMinimalView( true );
629         setInterfaceFullScreen( true );
630     }
631     else
632     {
633         /* TODO do we want to restore screen and position ? (when
634          * qt-fullscreen-screennumber is forced) */
635         setMinimalView( b_minimalView );
636         setInterfaceFullScreen( b_interfaceFullScreen );
637     }
638     videoWidget->sync();
639 }
640
641 /* Slot to change the video always-on-top flag.
642  * Emit askVideoOnTop() to invoke this from other thread. */
643 void MainInterface::setVideoOnTop( bool on_top )
644 {
645     b_videoOnTop = on_top;
646
647     Qt::WindowFlags oldflags = windowFlags(), newflags;
648
649     if( b_videoOnTop )
650         newflags = oldflags | Qt::WindowStaysOnTopHint;
651     else
652         newflags = oldflags & ~Qt::WindowStaysOnTopHint;
653
654     if( newflags != oldflags )
655     {
656         setWindowFlags( newflags );
657         show(); /* necessary to apply window flags */
658     }
659 }
660
661 /* Asynchronous call from WindowControl function */
662 int MainInterface::controlVideo( int i_query, va_list args )
663 {
664     switch( i_query )
665     {
666     case VOUT_WINDOW_SET_SIZE:
667     {
668         unsigned int i_width  = va_arg( args, unsigned int );
669         unsigned int i_height = va_arg( args, unsigned int );
670
671         emit askVideoToResize( i_width, i_height );
672         return VLC_SUCCESS;
673     }
674     case VOUT_WINDOW_SET_STATE:
675     {
676         unsigned i_arg = va_arg( args, unsigned );
677         unsigned on_top = i_arg & VOUT_WINDOW_STATE_ABOVE;
678
679         emit askVideoOnTop( on_top != 0 );
680         return VLC_SUCCESS;
681     }
682     case VOUT_WINDOW_SET_FULLSCREEN:
683     {
684         bool b_fs = va_arg( args, int );
685
686         emit askVideoSetFullScreen( b_fs );
687         return VLC_SUCCESS;
688     }
689     default:
690         msg_Warn( p_intf, "unsupported control query" );
691         return VLC_EGENERIC;
692     }
693 }
694
695 /*****************************************************************************
696  * Playlist, Visualisation and Menus handling
697  *****************************************************************************/
698 /**
699  * Toggle the playlist widget or dialog
700  **/
701 void MainInterface::createPlaylist()
702 {
703     playlistWidget = new PlaylistWidget( p_intf, this );
704
705     if( b_plDocked )
706     {
707         stackCentralW->addWidget( playlistWidget );
708         stackWidgetsSizes[playlistWidget] = settings->value( "playlistSize", QSize( 500, 250 ) ).toSize();
709     }
710     else
711     {
712 #ifdef WIN32
713         playlistWidget->setParent( NULL );
714 #endif
715         playlistWidget->setWindowFlags( Qt::Window );
716
717         /* This will restore the geometry but will not work for position,
718            because of parenting */
719         QVLCTools::restoreWidgetPosition( p_intf, "Playlist",
720                 playlistWidget, QSize( 600, 300 ) );
721     }
722 }
723
724 void MainInterface::togglePlaylist()
725 {
726     if( !playlistWidget )
727     {
728         createPlaylist();
729     }
730
731     if( b_plDocked )
732     {
733         /* Playlist is not visible, show it */
734         if( stackCentralW->currentWidget() != playlistWidget )
735         {
736             showTab( playlistWidget );
737         }
738         else /* Hide it! */
739         {
740             restoreStackOldWidget();
741         }
742         playlistVisible = ( stackCentralW->currentWidget() == playlistWidget );
743     }
744     else
745     {
746 #ifdef WIN32
747         playlistWidget->setParent( NULL );
748 #endif
749         playlistWidget->setWindowFlags( Qt::Window );
750         playlistVisible = !playlistVisible;
751         playlistWidget->setVisible( playlistVisible );
752     }
753     debug();
754 }
755
756 void MainInterface::dockPlaylist( bool p_docked )
757 {
758     if( b_plDocked == p_docked ) return;
759     b_plDocked = p_docked;
760
761     if( !playlistWidget ) return; /* Playlist wasn't created yet */
762     if( !p_docked )
763     {
764         stackCentralW->removeWidget( playlistWidget );
765 #ifdef WIN32
766         playlistWidget->setParent( NULL );
767 #endif
768         playlistWidget->setWindowFlags( Qt::Window );
769         QVLCTools::restoreWidgetPosition( p_intf, "Playlist",
770                 playlistWidget, QSize( 600, 300 ) );
771         playlistWidget->show();
772         restoreStackOldWidget();
773     }
774     else
775     {
776         QVLCTools::saveWidgetPosition( p_intf, "Playlist", playlistWidget );
777         playlistWidget->setWindowFlags( Qt::Widget ); // Probably a Qt bug here
778         // It would be logical that QStackWidget::addWidget reset the flags...
779         stackCentralW->addWidget( playlistWidget );
780         showTab( playlistWidget );
781     }
782     playlistVisible = true;
783 }
784
785 void MainInterface::setMinimalView( bool b_minimal )
786 {
787     menuBar()->setVisible( !b_minimal );
788     controls->setVisible( !b_minimal );
789     statusBar()->setVisible( !b_minimal );
790     inputC->setVisible( !b_minimal );
791 }
792
793 /*
794   If b_minimal is false, then we are normalView
795  */
796 void MainInterface::toggleMinimalView( bool b_minimal )
797 {
798     if( !b_minimalView && b_autoresize ) /* Normal mode */
799     {
800         if( stackCentralW->currentWidget() == bgWidget )
801         {
802             if( stackCentralW->height() < 16 )
803             {
804                 resizeStack( stackCentralW->width(), 100 );
805             }
806         }
807     }
808     b_minimalView = b_minimal;
809     if( !b_videoFullScreen )
810         setMinimalView( b_minimalView );
811
812     emit minimalViewToggled( b_minimalView );
813 }
814
815 /* toggling advanced controls buttons */
816 void MainInterface::toggleAdvancedButtons()
817 {
818     controls->toggleAdvanced();
819 //    if( fullscreenControls ) fullscreenControls->toggleAdvanced();
820 }
821
822 /* Get the visibility status of the controls (hidden or not, advanced or not) */
823 int MainInterface::getControlsVisibilityStatus()
824 {
825     if( !controls ) return 0;
826     return( (controls->isVisible() ? CONTROLS_VISIBLE : CONTROLS_HIDDEN )
827                 + CONTROLS_ADVANCED * controls->b_advancedVisible );
828 }
829
830 #if 0
831 void MainInterface::visual()
832 {
833     if( !VISIBLE( visualSelector) )
834     {
835         visualSelector->show();
836         if( !THEMIM->getIM()->hasVideo() )
837         {
838             /* Show the background widget */
839         }
840         visualSelectorEnabled = true;
841     }
842     else
843     {
844         /* Stop any currently running visualization */
845         visualSelector->hide();
846         visualSelectorEnabled = false;
847     }
848 }
849 #endif
850
851 /************************************************************************
852  * Other stuff
853  ************************************************************************/
854 void MainInterface::setName( const QString& name )
855 {
856     input_name = name; /* store it for the QSystray use */
857     /* Display it in the status bar, but also as a Tooltip in case it doesn't
858        fit in the label */
859     nameLabel->setText( " " + name + " " );
860     nameLabel->setToolTip( " " + name +" " );
861 }
862
863 /**
864  * Give the decorations of the Main Window a correct Name.
865  * If nothing is given, set it to VLC...
866  **/
867 void MainInterface::setVLCWindowsTitle( const QString& aTitle )
868 {
869     if( aTitle.isEmpty() )
870     {
871         setWindowTitle( qtr( "VLC media player" ) );
872     }
873     else
874     {
875         setWindowTitle( aTitle + " - " + qtr( "VLC media player" ) );
876     }
877 }
878
879 void MainInterface::showCryptedLabel( bool b_show )
880 {
881     if( cryptedLabel == NULL )
882     {
883         cryptedLabel = new QLabel;
884         // The lock icon is not the right one for DRM protection/scrambled.
885         //cryptedLabel->setPixmap( QPixmap( ":/lock" ) );
886         cryptedLabel->setText( "DRM" );
887         statusBar()->addWidget( cryptedLabel );
888     }
889
890     cryptedLabel->setVisible( b_show );
891 }
892
893 void MainInterface::showBuffering( float f_cache )
894 {
895     QString amount = QString("Buffering: %1%").arg( (int)(100*f_cache) );
896     statusBar()->showMessage( amount, 1000 );
897 }
898
899 /*****************************************************************************
900  * Systray Icon and Systray Menu
901  *****************************************************************************/
902 #ifndef HAVE_MAEMO
903 /**
904  * Create a SystemTray icon and a menu that would go with it.
905  * Connects to a click handler on the icon.
906  **/
907 void MainInterface::createSystray()
908 {
909     QIcon iconVLC;
910     if( QDate::currentDate().dayOfYear() >= 354 )
911         iconVLC =  QIcon( ":/logo/vlc128-christmas.png" );
912     else
913         iconVLC =  QIcon( ":/logo/vlc128.png" );
914     sysTray = new QSystemTrayIcon( iconVLC, this );
915     sysTray->setToolTip( qtr( "VLC media player" ));
916
917     systrayMenu = new QMenu( qtr( "VLC media player" ), this );
918     systrayMenu->setIcon( iconVLC );
919
920     QVLCMenu::updateSystrayMenu( this, p_intf, true );
921     sysTray->show();
922
923     CONNECT( sysTray, activated( QSystemTrayIcon::ActivationReason ),
924             this, handleSystrayClick( QSystemTrayIcon::ActivationReason ) );
925 }
926
927 /**
928  * Updates the Systray Icon's menu and toggle the main interface
929  */
930 void MainInterface::toggleUpdateSystrayMenu()
931 {
932     /* If hidden, show it */
933     if( isHidden() )
934     {
935         show();
936         activateWindow();
937     }
938     else if( isMinimized() )
939     {
940         /* Minimized */
941         showNormal();
942         activateWindow();
943     }
944     else
945     {
946         /* Visible (possibly under other windows) */
947 #ifdef WIN32
948         /* check if any visible window is above vlc in the z-order,
949          * but ignore the ones always on top
950          * and the ones which can't be activated */
951         WINDOWINFO wi;
952         HWND hwnd;
953         wi.cbSize = sizeof( WINDOWINFO );
954         for( hwnd = GetNextWindow( internalWinId(), GW_HWNDPREV );
955                 hwnd && ( !IsWindowVisible( hwnd ) ||
956                     ( GetWindowInfo( hwnd, &wi ) &&
957                       (wi.dwExStyle&WS_EX_NOACTIVATE) ) );
958                 hwnd = GetNextWindow( hwnd, GW_HWNDPREV ) );
959             if( !hwnd || !GetWindowInfo( hwnd, &wi ) ||
960                 (wi.dwExStyle&WS_EX_TOPMOST) )
961             {
962                 hide();
963             }
964             else
965             {
966                 activateWindow();
967             }
968 #else
969         hide();
970 #endif
971     }
972     QVLCMenu::updateSystrayMenu( this, p_intf );
973 }
974
975 void MainInterface::handleSystrayClick(
976                                     QSystemTrayIcon::ActivationReason reason )
977 {
978     switch( reason )
979     {
980         case QSystemTrayIcon::Trigger:
981         case QSystemTrayIcon::DoubleClick:
982 #ifdef Q_WS_MAC
983             QVLCMenu::updateSystrayMenu( this, p_intf );
984 #else
985             toggleUpdateSystrayMenu();
986 #endif
987             break;
988         case QSystemTrayIcon::MiddleClick:
989             sysTray->showMessage( qtr( "VLC media player" ),
990                     qtr( "Control menu for the player" ),
991                     QSystemTrayIcon::Information, 3000 );
992             break;
993         default:
994             break;
995     }
996 }
997
998 /**
999  * Updates the name of the systray Icon tooltip.
1000  * Doesn't check if the systray exists, check before you call it.
1001  **/
1002 void MainInterface::updateSystrayTooltipName( const QString& name )
1003 {
1004     if( name.isEmpty() )
1005     {
1006         sysTray->setToolTip( qtr( "VLC media player" ) );
1007     }
1008     else
1009     {
1010         sysTray->setToolTip( name );
1011         if( b_notificationEnabled && ( isHidden() || isMinimized() ) )
1012         {
1013             sysTray->showMessage( qtr( "VLC media player" ), name,
1014                     QSystemTrayIcon::NoIcon, 3000 );
1015         }
1016     }
1017
1018     QVLCMenu::updateSystrayMenu( this, p_intf );
1019 }
1020
1021 /**
1022  * Updates the status of the systray Icon tooltip.
1023  * Doesn't check if the systray exists, check before you call it.
1024  **/
1025 void MainInterface::updateSystrayTooltipStatus( int i_status )
1026 {
1027     switch( i_status )
1028     {
1029     case PLAYING_S:
1030         sysTray->setToolTip( input_name );
1031         break;
1032     case PAUSE_S:
1033         sysTray->setToolTip( input_name + " - " + qtr( "Paused") );
1034         break;
1035     default:
1036         sysTray->setToolTip( qtr( "VLC media player" ) );
1037         break;
1038     }
1039     QVLCMenu::updateSystrayMenu( this, p_intf );
1040 }
1041 #endif
1042
1043 /************************************************************************
1044  * D&D Events
1045  ************************************************************************/
1046 void MainInterface::dropEvent(QDropEvent *event)
1047 {
1048     dropEventPlay( event, true );
1049 }
1050
1051 void MainInterface::dropEventPlay( QDropEvent *event, bool b_play )
1052 {
1053     if( event->possibleActions() & Qt::CopyAction )
1054        event->setDropAction( Qt::CopyAction );
1055     else
1056         return;
1057
1058     const QMimeData *mimeData = event->mimeData();
1059
1060     /* D&D of a subtitles file, add it on the fly */
1061     if( mimeData->urls().size() == 1 && THEMIM->getIM()->hasInput() )
1062     {
1063         if( !input_AddSubtitle( THEMIM->getInput(),
1064                  qtu( toNativeSeparators( mimeData->urls()[0].toLocalFile() ) ),
1065                  true ) )
1066         {
1067             event->accept();
1068             return;
1069         }
1070     }
1071
1072     bool first = b_play;
1073     foreach( const QUrl &url, mimeData->urls() )
1074     {
1075         if( url.isValid() )
1076         {
1077             char* psz_uri = make_URI( qtu( url.toString() ), NULL );
1078             playlist_Add( THEPL, psz_uri, NULL,
1079                           PLAYLIST_APPEND | (first ? PLAYLIST_GO: PLAYLIST_PREPARSE),
1080                           PLAYLIST_END, true, pl_Unlocked );
1081             free( psz_uri );
1082             first = false;
1083             RecentsMRL::getInstance( p_intf )->addRecent( url.toString() );
1084         }
1085     }
1086
1087     /* Browsers give content as text if you dnd the addressbar,
1088        so check if mimedata has valid url in text and use it
1089        if we didn't get any normal Urls()*/
1090     if( !mimeData->hasUrls() && mimeData->hasText() &&
1091         QUrl(mimeData->text()).isValid() )
1092     {
1093         char *psz_uri = make_URI( qtu( mimeData->text() ), NULL );
1094         playlist_Add( THEPL, psz_uri, NULL,
1095                       PLAYLIST_APPEND | (first ? PLAYLIST_GO: PLAYLIST_PREPARSE),
1096                       PLAYLIST_END, true, pl_Unlocked );
1097         free( psz_uri );
1098     }
1099     event->accept();
1100 }
1101 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
1102 {
1103      event->acceptProposedAction();
1104 }
1105 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
1106 {
1107      event->acceptProposedAction();
1108 }
1109 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
1110 {
1111      event->accept();
1112 }
1113
1114 /************************************************************************
1115  * Events stuff
1116  ************************************************************************/
1117 void MainInterface::keyPressEvent( QKeyEvent *e )
1118 {
1119     handleKeyPress( e );
1120 }
1121
1122 void MainInterface::handleKeyPress( QKeyEvent *e )
1123 {
1124     if( ( e->modifiers() &  Qt::ControlModifier ) && ( e->key() == Qt::Key_H ) )
1125     {
1126         toggleMinimalView( !b_minimalView );
1127         e->accept();
1128     }
1129
1130     int i_vlck = qtEventToVLCKey( e );
1131     if( i_vlck > 0 )
1132     {
1133         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
1134         e->accept();
1135     }
1136     else
1137         e->ignore();
1138 }
1139
1140 void MainInterface::wheelEvent( QWheelEvent *e )
1141 {
1142     int i_vlckey = qtWheelEventToVLCKey( e );
1143     var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
1144     e->accept();
1145 }
1146
1147 void MainInterface::closeEvent( QCloseEvent *e )
1148 {
1149     e->accept();
1150     hide();
1151     THEDP->quit();
1152 }
1153
1154 void MainInterface::setInterfaceFullScreen( bool fs )
1155 {
1156     if( fs )
1157         setWindowState( windowState() | Qt::WindowFullScreen );
1158     else
1159         setWindowState( windowState() & ~Qt::WindowFullScreen );
1160 }
1161 void MainInterface::toggleInterfaceFullScreen()
1162 {
1163     b_interfaceFullScreen = !b_interfaceFullScreen;
1164     if( !b_videoFullScreen )
1165         setInterfaceFullScreen( b_interfaceFullScreen );
1166     emit fullscreenInterfaceToggled( b_interfaceFullScreen );
1167 }
1168
1169 /*****************************************************************************
1170  * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
1171  *  We don't show the menu directly here because we don't want the
1172  *  caller to block for a too long time.
1173  *****************************************************************************/
1174 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
1175                         vlc_value_t old_val, vlc_value_t new_val, void *param )
1176 {
1177     intf_thread_t *p_intf = (intf_thread_t *)param;
1178
1179     if( p_intf->pf_show_dialog )
1180     {
1181         p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
1182                                 new_val.b_bool, NULL );
1183     }
1184
1185     return VLC_SUCCESS;
1186 }
1187
1188 /*****************************************************************************
1189  * IntfShowCB: callback triggered by the intf-show libvlc variable.
1190  *****************************************************************************/
1191 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
1192                        vlc_value_t old_val, vlc_value_t new_val, void *param )
1193 {
1194     intf_thread_t *p_intf = (intf_thread_t *)param;
1195     p_intf->p_sys->p_mi->toggleFSC();
1196
1197     /* Show event */
1198      return VLC_SUCCESS;
1199 }