1 /*****************************************************************************
2 * main_interface.cpp : Main interface
3 ****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/
24 #include "main_interface.hpp"
25 #include "input_manager.hpp"
26 #include "util/input_slider.hpp"
27 #include "util/qvlcframe.hpp"
28 #include "util/customwidgets.hpp"
29 #include "dialogs_provider.hpp"
30 #include "components/interface_widgets.hpp"
31 #include "dialogs/playlist.hpp"
35 #include <QCloseEvent>
36 #include <QPushButton>
53 #define VISIBLE(i) (i && i->isVisible())
55 #define SET_WIDTH(i,j) i->widgetSize.setWidth(j)
56 #define SET_HEIGHT(i,j) i->widgetSize.setHeight(j)
57 #define SET_WH( i,j,k) i->widgetSize.setWidth(j); i->widgetSize.setHeight(k);
59 #define DS(i) i.width(),i.height()
61 /* Callback prototypes */
62 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
63 vlc_value_t old_val, vlc_value_t new_val, void *param );
64 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
65 vlc_value_t old_val, vlc_value_t new_val, void *param );
66 static int InteractCallback( vlc_object_t *, const char *, vlc_value_t,
69 static void *DoRequest( intf_thread_t *p_intf, vout_thread_t *p_vout,
70 int *pi1, int *pi2, unsigned int*pi3,unsigned int*pi4)
72 return p_intf->p_sys->p_mi->requestVideo( p_vout, pi1, pi2, pi3, pi4 );
74 static void DoRelease( intf_thread_t *p_intf, void *p_win )
76 return p_intf->p_sys->p_mi->releaseVideo( p_win );
78 static int DoControl( intf_thread_t *p_intf, void *p_win, int i_q, va_list a )
80 return p_intf->p_sys->p_mi->controlVideo( p_win, i_q, a );
83 MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf )
86 settings = new QSettings( "VideoLAN", "VLC" );
87 settings->beginGroup( "MainWindow" );
89 setWindowIcon( QApplication::windowIcon() );
91 need_components_update = false;
92 bgWidget = NULL; videoWidget = NULL; playlistWidget = NULL;
93 embeddedPlaylistWasActive = videoIsActive = false;
95 videoEmbeddedFlag = false;
96 if( config_GetInt( p_intf, "embedded-video" ) ) videoEmbeddedFlag = true;
98 alwaysVideoFlag = false;
99 if( videoEmbeddedFlag && config_GetInt( p_intf, "qt-always-video" ))
100 alwaysVideoFlag = true;
102 playlistEmbeddedFlag = settings->value("playlist-embedded", true).toBool();
103 advControlsEnabled= settings->value( "adv-controls", false ).toBool();
104 visualSelectorEnabled= settings->value( "visual-selector", false ).toBool();
107 setWindowTitle( qtr( "VLC media player" ) );
108 handleMainUi( settings );
109 QVLCMenu::createMenuBar( this, p_intf, playlistEmbeddedFlag,
110 advControlsEnabled, visualSelectorEnabled );
111 timeLabel = new QLabel( 0 );
112 nameLabel = new QLabel( 0 );
113 statusBar()->addWidget( nameLabel, 4 );
114 statusBar()->addPermanentWidget( timeLabel, 1 );
116 setFocusPolicy( Qt::StrongFocus );
117 setAcceptDrops(true);
119 /* Init input manager */
120 MainInputManager::getInstance( p_intf );
121 ON_TIMEOUT( updateOnTimer() );
124 CONNECT( ui.volumeSlider, valueChanged(int), this, updateVolume(int) );
125 /* Connect the input manager to the GUI elements it manages */
126 CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
127 slider, setPosition( float,int, int ) );
128 CONNECT( THEMIM->getIM(), positionUpdated( float, int, int ),
129 this, setDisplay( float, int, int ) );
130 CONNECT( THEMIM->getIM(), nameChanged( QString ), this,setName( QString ) );
131 CONNECT( THEMIM->getIM(), statusChanged( int ), this, setStatus( int ) );
132 CONNECT( THEMIM->getIM(), navigationChanged( int ), this, setNavigation(int) );
133 CONNECT( slider, sliderDragged( float ),
134 THEMIM->getIM(), sliderUpdate( float ) );
136 CONNECT( ui.prevSectionButton, clicked(), THEMIM->getIM(),
138 CONNECT( ui.nextSectionButton, clicked(), THEMIM->getIM(),
140 CONNECT( ui.menuButton, clicked(), THEMIM->getIM(),
143 var_Create( p_intf, "interaction", VLC_VAR_ADDRESS );
144 var_AddCallback( p_intf, "interaction", InteractCallback, this );
145 p_intf->b_interaction = VLC_TRUE;
147 /* Register callback for the intf-popupmenu variable */
148 playlist_t *p_playlist =
149 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
150 if( p_playlist != NULL )
152 var_AddCallback( p_playlist, "intf-popupmenu", PopupMenuCB, p_intf );
153 var_AddCallback( p_playlist, "intf-show", IntfShowCB, p_intf );
154 vlc_object_release( p_playlist );
158 MainInterface::~MainInterface()
160 /* Unregister callback for the intf-popupmenu variable */
161 playlist_t *p_playlist =
162 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
163 if( p_playlist != NULL )
165 var_DelCallback( p_playlist, "intf-popupmenu", PopupMenuCB, p_intf );
166 var_DelCallback( p_playlist, "intf-show", IntfShowCB, p_intf );
167 vlc_object_release( p_playlist );
170 settings->setValue( "playlist-embedded", playlistEmbeddedFlag );
171 settings->setValue( "adv-controls", advControlsEnabled );
172 settings->setValue( "pos", pos() );
173 settings->endGroup();
175 p_intf->b_interaction = VLC_FALSE;
176 var_DelCallback( p_intf, "interaction", InteractCallback, this );
178 p_intf->pf_request_window = NULL;
179 p_intf->pf_release_window = NULL;
180 p_intf->pf_control_window = NULL;
183 void MainInterface::handleMainUi( QSettings *settings )
185 QWidget *main = new QWidget( this );
186 setCentralWidget( main );
187 ui.setupUi( centralWidget() );
189 slider = new InputSlider( Qt::Horizontal, NULL );
190 ui.vboxLayout->insertWidget( 0, slider );
191 ui.discFrame->hide();
192 BUTTON_SET_IMG( ui.prevSectionButton, "", previous.png, "" );
193 BUTTON_SET_IMG( ui.nextSectionButton, "", next.png, "" );
194 BUTTON_SET_IMG( ui.menuButton, "", previous.png, "" );
196 BUTTON_SET_ACT_I( ui.prevButton, "" , previous.png,
197 qtr("Previous"), prev() );
198 BUTTON_SET_ACT_I( ui.nextButton, "", next.png, qtr("Next"), next() );
199 BUTTON_SET_ACT_I( ui.playButton, "", play.png, qtr("Play"), play() );
200 BUTTON_SET_ACT_I( ui.stopButton, "", stop.png, qtr("Stop"), stop() );
203 ui.volMuteLabel->setPixmap( QPixmap( ":/pixmaps/volume-low.png" ) );
204 ui.volumeSlider->setMaximum( 100 );
205 ui.volMuteLabel->setToolTip( qtr( "Mute" ) );
206 VolumeClickHandler *h = new VolumeClickHandler( p_intf, this );
207 ui.volMuteLabel->installEventFilter(h);
208 ui.volumeSlider->setFocusPolicy( Qt::NoFocus );
210 BUTTON_SET_IMG( ui.playlistButton, "" , playlist_icon.png,
211 playlistEmbeddedFlag ? qtr( "Show playlist" ) :
212 qtr( "Open playlist" ) );
213 BUTTONACT( ui.playlistButton, playlist() );
215 /* Set initial size */
216 resize ( PREF_W, PREF_H );
218 addSize = QSize( ui.vboxLayout->margin() * 2, PREF_H );
220 advControls = new ControlsWidget( p_intf );
221 ui.vboxLayout->insertWidget( 0, advControls );
222 advControls->updateGeometry();
223 if( !advControlsEnabled ) advControls->hide();
224 need_components_update = true;
226 visualSelector = new VisualSelector( p_intf );
227 ui.vboxLayout->insertWidget( 0, visualSelector );
228 visualSelector->hide();
230 if( alwaysVideoFlag )
232 bgWidget = new BackgroundWidget( p_intf );
233 bgWidget->widgetSize = settings->value( "backgroundSize",
234 QSize( 200, 200 ) ).toSize();
235 bgWidget->resize( bgWidget->widgetSize );
236 bgWidget->updateGeometry();
237 ui.vboxLayout->insertWidget( 0, bgWidget );
240 if( videoEmbeddedFlag )
242 videoWidget = new VideoWidget( p_intf );
243 videoWidget->widgetSize = QSize( 1, 1 );
244 videoWidget->resize( videoWidget->widgetSize );
245 ui.vboxLayout->insertWidget( 0, videoWidget );
247 p_intf->pf_request_window = ::DoRequest;
248 p_intf->pf_release_window = ::DoRelease;
249 p_intf->pf_control_window = ::DoControl;
251 setMinimumSize( PREF_W, addSize.height() );
254 /**********************************************************************
255 * Handling of the components
256 **********************************************************************/
257 void MainInterface::calculateInterfaceSize()
259 int width = 0, height = 0;
260 if( VISIBLE( bgWidget ) )
262 width = bgWidget->widgetSize.width();
263 height = bgWidget->widgetSize.height();
264 assert( !(playlistWidget && playlistWidget->isVisible() ) );
266 else if( VISIBLE( playlistWidget ) )
268 width = playlistWidget->widgetSize.width();
269 height = playlistWidget->widgetSize.height();
271 else if( videoIsActive )
273 width = videoWidget->widgetSize.width() ;
274 height = videoWidget->widgetSize.height();
278 width = PREF_W - addSize.width();
279 height = PREF_H - addSize.height();
281 if( VISIBLE( visualSelector ) )
282 height += visualSelector->height();
283 if( VISIBLE( advControls) )
285 height += advControls->sizeHint().height();
287 mainSize = QSize( width + addSize.width(), height + addSize.height() );
290 void MainInterface::resizeEvent( QResizeEvent *e )
292 videoWidget->widgetSize.setWidth( e->size().width() - addSize.width() );
293 if( videoWidget && videoIsActive && videoWidget->widgetSize.height() > 1 )
295 SET_WH( videoWidget, e->size().width() - addSize.width(),
296 e->size().height() - addSize.height() );
297 videoWidget->updateGeometry();
299 if( VISIBLE( playlistWidget ) )
301 SET_WH( playlistWidget , e->size().width() - addSize.width(),
302 e->size().height() - addSize.height() );
303 playlistWidget->updateGeometry();
307 void *MainInterface::requestVideo( vout_thread_t *p_nvout, int *pi_x,
308 int *pi_y, unsigned int *pi_width,
309 unsigned int *pi_height )
311 void *ret = videoWidget->request( p_nvout,pi_x, pi_y, pi_width, pi_height );
314 videoIsActive = true;
315 if( VISIBLE( playlistWidget ) )
317 embeddedPlaylistWasActive = true;
318 // playlistWidget->hide();
320 bool bgWasVisible = false;
321 if( VISIBLE( bgWidget) )
326 if( THEMIM->getIM()->hasVideo() || !bgWasVisible )
328 videoWidget->widgetSize = QSize( *pi_width, *pi_height );
330 else /* Background widget available, use its size */
332 /* Ok, our visualizations are bad, so don't do this for the moment
333 * use the requested size anyway */
334 // videoWidget->widgetSize = bgWidget->widgeTSize;
335 videoWidget->widgetSize = QSize( *pi_width, *pi_height );
337 // videoWidget->updateGeometry(); /// FIXME: Needed ?
338 need_components_update = true;
343 void MainInterface::releaseVideo( void *p_win )
345 videoWidget->release( p_win );
346 videoWidget->widgetSize = QSize( 0, 0 );
347 videoWidget->resize( videoWidget->widgetSize );
349 if( embeddedPlaylistWasActive )
350 ;//playlistWidget->show();
354 videoIsActive = false;
355 need_components_update = true;
358 class SetVideoOnTopQtEvent : public QEvent
361 SetVideoOnTopQtEvent( bool _onTop ) :
362 QEvent( (QEvent::Type)SetVideoOnTopEvent_Type ), onTop( _onTop)
375 int MainInterface::controlVideo( void *p_window, int i_query, va_list args )
377 int i_ret = VLC_EGENERIC;
382 unsigned int *pi_width = va_arg( args, unsigned int * );
383 unsigned int *pi_height = va_arg( args, unsigned int * );
384 *pi_width = videoWidget->widgetSize.width();
385 *pi_height = videoWidget->widgetSize.height();
391 unsigned int i_width = va_arg( args, unsigned int );
392 unsigned int i_height = va_arg( args, unsigned int );
393 videoWidget->widgetSize = QSize( i_width, i_height );
394 // videoWidget->updateGeometry();
395 need_components_update = true;
399 case VOUT_SET_STAY_ON_TOP:
401 int i_arg = va_arg( args, int );
402 QApplication::postEvent( this, new SetVideoOnTopQtEvent( i_arg ) );
407 msg_Warn( p_intf, "unsupported control query" );
413 void MainInterface::advanced()
415 if( !VISIBLE( advControls ) )
418 advControlsEnabled = true;
423 advControlsEnabled = false;
425 doComponentsUpdate();
428 void MainInterface::visual()
430 if( !VISIBLE( visualSelector) )
432 visualSelector->show();
433 if( !THEMIM->getIM()->hasVideo() )
435 /* Show the background widget */
437 visualSelectorEnabled = true;
441 /* Stop any currently running visualization */
442 visualSelector->hide();
443 visualSelectorEnabled = false;
445 doComponentsUpdate();
448 void MainInterface::playlist()
450 // Toggle the playlist dialog
451 if( !playlistEmbeddedFlag )
457 THEDP->playlistDialog();
461 if( !playlistWidget )
463 PlaylistDialog::killInstance();
464 playlistWidget = new PlaylistWidget( p_intf );
465 ui.vboxLayout->insertWidget( 0, playlistWidget );
466 playlistWidget->widgetSize = settings->value( "playlistSize",
467 QSize( 650, 310 ) ).toSize();
468 playlistWidget->hide();
470 if( VISIBLE( playlistWidget ) )
472 playlistWidget->hide();
475 videoWidget->widgetSize = savedVideoSize;
476 videoWidget->resize( videoWidget->widgetSize );
477 videoWidget->updateGeometry();
482 playlistWidget->show();
485 savedVideoSize = videoWidget->widgetSize;
486 videoWidget->widgetSize.setHeight( 0 );
487 videoWidget->resize( videoWidget->widgetSize );
488 videoWidget->updateGeometry();
490 if( VISIBLE( bgWidget ) ) bgWidget->hide();
492 doComponentsUpdate();
495 /* Video widget cannot do this synchronously as it runs in another thread */
496 /* Well, could it, actually ? Probably dangerous ... */
497 void MainInterface::doComponentsUpdate()
499 calculateInterfaceSize();
503 void MainInterface::undockPlaylist()
507 playlistWidget->hide();
508 playlistWidget->deleteLater();
509 ui.vboxLayout->removeWidget( playlistWidget );
510 playlistWidget = NULL;
511 playlistEmbeddedFlag = false;
514 QVLCMenu::createMenuBar( this, p_intf, false, advControlsEnabled,
515 visualSelectorEnabled);
519 videoWidget->widgetSize = savedVideoSize;
520 videoWidget->resize( videoWidget->widgetSize );
521 videoWidget->updateGeometry();
524 doComponentsUpdate();
525 THEDP->playlistDialog();
529 void MainInterface::customEvent( QEvent *event )
531 if( event->type() == PLDockEvent_Type )
533 PlaylistDialog::killInstance();
534 playlistEmbeddedFlag = true;
536 QVLCMenu::createMenuBar(this, p_intf, true, advControlsEnabled,
537 visualSelectorEnabled);
540 else if ( event->type() == SetVideoOnTopEvent_Type )
542 SetVideoOnTopQtEvent* p_event = (SetVideoOnTopQtEvent*)event;
543 if( p_event->OnTop() )
544 setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
546 setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
547 show(); /* necessary to apply window flags?? */
552 /************************************************************************
554 ************************************************************************/
555 void MainInterface::dropEvent(QDropEvent *event)
557 const QMimeData *mimeData = event->mimeData();
559 /* D&D of a subtitles file, add it on the fly */
560 if( mimeData->urls().size() == 1 )
562 if( THEMIM->getIM()->hasInput() )
564 if( input_AddSubtitles( THEMIM->getInput(),
565 qtu( mimeData->urls()[0].toString() ),
568 event->acceptProposedAction();
574 foreach( QUrl url, mimeData->urls() ) {
575 QString s = url.toString();
576 if( s.length() > 0 ) {
577 playlist_Add( THEPL, qtu(s), NULL,
578 PLAYLIST_APPEND | (first ? PLAYLIST_GO:0),
579 PLAYLIST_END, VLC_TRUE, VLC_FALSE );
583 event->acceptProposedAction();
585 void MainInterface::dragEnterEvent(QDragEnterEvent *event)
587 event->acceptProposedAction();
589 void MainInterface::dragMoveEvent(QDragMoveEvent *event)
591 event->acceptProposedAction();
593 void MainInterface::dragLeaveEvent(QDragLeaveEvent *event)
598 /************************************************************************
600 ************************************************************************/
601 void MainInterface::keyPressEvent( QKeyEvent *e )
603 int i_vlck = qtEventToVLCKey( e );
606 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlck );
613 void MainInterface::wheelEvent( QWheelEvent *e )
615 int i_vlckey = qtWheelEventToVLCKey( e );
616 var_SetInteger( p_intf->p_libvlc, "key-pressed", i_vlckey );
620 void MainInterface::stop()
625 void MainInterface::play()
627 if( playlist_IsEmpty(THEPL) )
629 /* The playlist is empty, open a file requester */
630 THEDP->openFileDialog();
634 THEMIM->togglePlayPause();
637 void MainInterface::prev()
642 void MainInterface::next()
647 void MainInterface::setDisplay( float pos, int time, int length )
649 char psz_length[MSTRTIME_MAX_SIZE], psz_time[MSTRTIME_MAX_SIZE];
650 secstotimestr( psz_length, length );
651 secstotimestr( psz_time, time );
653 title.sprintf( "%s/%s", psz_time, psz_length );
654 timeLabel->setText( " "+title+" " );
657 void MainInterface::setName( QString name )
659 nameLabel->setText( " " + name+" " );
662 void MainInterface::setStatus( int status )
664 if( status == 1 ) // Playing
665 ui.playButton->setIcon( QIcon( ":/pixmaps/pause.png" ) );
667 ui.playButton->setIcon( QIcon( ":/pixmaps/play.png" ) );
670 #define HELP_MENU N_("Menu")
671 #define HELP_PCH N_("Previous chapter")
672 #define HELP_NCH N_("Next chapter")
673 #define HELP_PTR N_("Previous track")
674 #define HELP_NTR N_("Next track")
676 void MainInterface::setNavigation( int navigation )
678 // 1 = chapter, 2 = title, 0 = no
679 if( navigation == 0 )
681 ui.discFrame->hide();
682 } else if( navigation == 1 ) {
683 ui.prevSectionButton->show();
684 ui.prevSectionButton->setToolTip( qfu(HELP_PCH) );
685 ui.nextSectionButton->show();
686 ui.nextSectionButton->setToolTip( qfu(HELP_NCH) );
687 ui.menuButton->show();
688 ui.discFrame->show();
690 ui.prevSectionButton->show();
691 ui.prevSectionButton->setToolTip( qfu(HELP_PCH) );
692 ui.nextSectionButton->show();
693 ui.nextSectionButton->setToolTip( qfu(HELP_NCH) );
694 ui.menuButton->hide();
695 ui.discFrame->show();
699 static bool b_my_volume;
701 void MainInterface::updateOnTimer()
703 /* \todo Make this event-driven */
704 advControls->enableInput( THEMIM->getIM()->hasInput() );
705 advControls->enableVideo( THEMIM->getIM()->hasVideo() );
707 if( intf_ShouldDie( p_intf ) )
709 QApplication::closeAllWindows();
710 QApplication::quit();
712 if( need_components_update )
714 doComponentsUpdate();
715 need_components_update = false;
718 audio_volume_t i_volume;
719 aout_VolumeGet( p_intf, &i_volume );
720 i_volume = (i_volume * 200 )/ AOUT_VOLUME_MAX ;
721 int i_gauge = ui.volumeSlider->value();
723 if( i_volume - i_gauge > 1 || i_gauge - i_volume > 1 )
726 ui.volumeSlider->setValue( i_volume );
731 void MainInterface::closeEvent( QCloseEvent *e )
734 p_intf->b_die = VLC_TRUE;
737 void MainInterface::updateVolume( int sliderVolume )
741 int i_res = sliderVolume * AOUT_VOLUME_MAX /
742 (2*ui.volumeSlider->maximum() );
743 aout_VolumeSet( p_intf, i_res );
747 static int InteractCallback( vlc_object_t *p_this,
748 const char *psz_var, vlc_value_t old_val,
749 vlc_value_t new_val, void *param )
751 intf_dialog_args_t *p_arg = new intf_dialog_args_t;
752 p_arg->p_dialog = (interaction_dialog_t *)(new_val.p_address);
753 DialogEvent *event = new DialogEvent( INTF_DIALOG_INTERACTION, 0, p_arg );
754 QApplication::postEvent( THEDP, static_cast<QEvent*>(event) );
758 /*****************************************************************************
759 * PopupMenuCB: callback triggered by the intf-popupmenu playlist variable.
760 * We don't show the menu directly here because we don't want the
761 * caller to block for a too long time.
762 *****************************************************************************/
763 static int PopupMenuCB( vlc_object_t *p_this, const char *psz_variable,
764 vlc_value_t old_val, vlc_value_t new_val, void *param )
766 intf_thread_t *p_intf = (intf_thread_t *)param;
768 if( p_intf->pf_show_dialog )
770 p_intf->pf_show_dialog( p_intf, INTF_DIALOG_POPUPMENU,
777 /*****************************************************************************
778 * IntfShowCB: callback triggered by the intf-show playlist variable.
779 *****************************************************************************/
780 static int IntfShowCB( vlc_object_t *p_this, const char *psz_variable,
781 vlc_value_t old_val, vlc_value_t new_val, void *param )
783 intf_thread_t *p_intf = (intf_thread_t *)param;
784 //p_intf->p_sys->b_intf_show = VLC_TRUE;