1 /*****************************************************************************
2 * extensions.cpp: Extensions manager for Qt: dialogs manager
3 ****************************************************************************
4 * Copyright (C) 2009-2010 VideoLAN and authors
7 * Authors: Jean-Philippe André < jpeg # 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.
22 *****************************************************************************/
24 #include "extensions.hpp"
25 #include "../extensions_manager.hpp" // for isUnloading()
27 #include <vlc_dialog.h>
29 #include <QGridLayout>
30 #include <QPushButton>
31 #include <QSignalMapper>
35 #include <QTextBrowser>
37 #include <QListWidget>
39 #include <QCloseEvent>
41 ExtensionsDialogProvider *ExtensionsDialogProvider::instance = NULL;
43 static int DialogCallback( vlc_object_t *p_this, const char *psz_variable,
44 vlc_value_t old_val, vlc_value_t new_val,
48 ExtensionsDialogProvider::ExtensionsDialogProvider( intf_thread_t *_p_intf,
49 extensions_manager_t *p_mgr )
50 : QObject( NULL ), p_intf( _p_intf ), p_extensions_manager( p_mgr )
52 // At this point, we consider that the Qt interface already called
53 // dialog_Register() in order to be the extension dialog provider
54 var_Create( p_intf, "dialog-extension", VLC_VAR_ADDRESS );
55 var_AddCallback( p_intf, "dialog-extension", DialogCallback, NULL );
57 CONNECT( this, SignalDialog( extension_dialog_t* ),
58 this, UpdateExtDialog( extension_dialog_t* ) );
61 ExtensionsDialogProvider::~ExtensionsDialogProvider()
63 var_DelCallback( p_intf, "dialog-extension", DialogCallback, NULL );
67 * Note: Lock on p_dialog->lock must be held. */
68 ExtensionDialog* ExtensionsDialogProvider::CreateExtDialog(
69 extension_dialog_t *p_dialog )
71 ExtensionDialog *dialog = new ExtensionDialog( p_intf,
74 p_dialog->p_sys_intf = (void*) dialog;
75 CONNECT( dialog, destroyDialog( extension_dialog_t* ),
76 this, DestroyExtDialog( extension_dialog_t* ) );
81 * Note: Lock on p_dialog->lock must be held. */
82 int ExtensionsDialogProvider::DestroyExtDialog( extension_dialog_t *p_dialog )
85 ExtensionDialog *dialog = ( ExtensionDialog* ) p_dialog->p_sys_intf;
89 p_dialog->p_sys_intf = NULL;
94 * Update/Create/Destroy a dialog
96 ExtensionDialog* ExtensionsDialogProvider::UpdateExtDialog(
97 extension_dialog_t *p_dialog )
101 ExtensionDialog *dialog = ( ExtensionDialog* ) p_dialog->p_sys_intf;
102 if( p_dialog->b_kill && !dialog )
104 /* This extension could not be activated properly but tried
105 to create a dialog. We must ignore it. */
109 vlc_mutex_lock( &p_dialog->lock );
110 if( !p_dialog->b_kill && !dialog )
112 dialog = CreateExtDialog( p_dialog );
113 dialog->setVisible( !p_dialog->b_hide );
115 else if( !p_dialog->b_kill && dialog )
117 dialog->has_lock = true;
118 dialog->UpdateWidgets();
119 dialog->has_lock = false;
120 dialog->setVisible( !p_dialog->b_hide );
122 else if( p_dialog->b_kill )
124 DestroyExtDialog( p_dialog );
126 vlc_cond_signal( &p_dialog->cond );
127 vlc_mutex_unlock( &p_dialog->lock );
132 * Ask the dialog manager to create/update/kill the dialog. Thread-safe.
134 void ExtensionsDialogProvider::ManageDialog( extension_dialog_t *p_dialog )
137 ExtensionsManager *extMgr = ExtensionsManager::getInstance( p_intf );
138 assert( extMgr != NULL );
139 if( !extMgr->isUnloading() )
140 emit SignalDialog( p_dialog ); // Safe because we signal Qt thread
142 UpdateExtDialog( p_dialog ); // This is safe, we're already in Qt thread
146 * Ask the dialogs provider to create a new dialog
148 static int DialogCallback( vlc_object_t *p_this, const char *psz_variable,
149 vlc_value_t old_val, vlc_value_t new_val,
157 ExtensionsDialogProvider *p_edp = ExtensionsDialogProvider::getInstance();
160 if( !new_val.p_address )
163 extension_dialog_t *p_dialog = ( extension_dialog_t* ) new_val.p_address;
164 p_edp->ManageDialog( p_dialog );
169 ExtensionDialog::ExtensionDialog( intf_thread_t *_p_intf,
170 extensions_manager_t *p_mgr,
171 extension_dialog_t *_p_dialog )
172 : QDialog( NULL ), p_intf( _p_intf ), p_extensions_manager( p_mgr )
173 , p_dialog( _p_dialog ), has_lock(false)
177 msg_Dbg( p_intf, "Creating a new dialog: '%s'", p_dialog->psz_title );
179 this->setWindowFlags( Qt::WindowMinMaxButtonsHint
180 | Qt::WindowCloseButtonHint );
182 this->setWindowFlags( Qt::WindowMinMaxButtonsHint );
185 this->setWindowTitle( qfu( p_dialog->psz_title ) );
187 layout = new QGridLayout( this );
188 clickMapper = new QSignalMapper( this );
189 CONNECT( clickMapper, mapped( QObject* ), this, TriggerClick( QObject* ) );
190 inputMapper = new QSignalMapper( this );
191 CONNECT( inputMapper, mapped( QObject* ), this, SyncInput( QObject* ) );
192 selectMapper = new QSignalMapper( this );
193 CONNECT( selectMapper, mapped( QObject* ), this, SyncSelection(QObject*) );
198 ExtensionDialog::~ExtensionDialog()
200 msg_Dbg( p_intf, "Deleting extension dialog '%s'", qtu(windowTitle()) );
201 p_dialog->p_sys_intf = NULL;
204 QWidget* ExtensionDialog::CreateWidget( extension_widget_t *p_widget )
206 QLabel *label = NULL;
207 QPushButton *button = NULL;
208 QTextBrowser *textArea = NULL;
209 QLineEdit *textInput = NULL;
210 QCheckBox *checkBox = NULL;
211 QComboBox *comboBox = NULL;
212 QListWidget *list = NULL;
213 struct extension_widget_t::extension_widget_value_t *p_value = NULL;
215 assert( p_widget->p_sys_intf == NULL );
217 switch( p_widget->type )
219 case EXTENSION_WIDGET_LABEL:
220 label = new QLabel( qfu( p_widget->psz_text ), this );
221 p_widget->p_sys_intf = label;
222 label->setTextFormat( Qt::RichText );
223 //label->setFixedHeight( label->sizeHint().height );
226 case EXTENSION_WIDGET_BUTTON:
227 button = new QPushButton( qfu( p_widget->psz_text ), this );
228 clickMapper->setMapping( button, new WidgetMapper( p_widget ) );
229 CONNECT( button, clicked(), clickMapper, map() );
230 p_widget->p_sys_intf = button;
233 case EXTENSION_WIDGET_IMAGE:
234 label = new QLabel( this );
235 label->setPixmap( QPixmap( qfu( p_widget->psz_text ) ) );
236 if( p_widget->i_width > 0 )
237 label->setMaximumWidth( p_widget->i_width );
238 if( p_widget->i_height > 0 )
239 label->setMaximumHeight( p_widget->i_height );
240 label->setScaledContents( true );
241 p_widget->p_sys_intf = label;
244 case EXTENSION_WIDGET_HTML:
245 textArea = new QTextBrowser( this );
246 textArea->setOpenExternalLinks( true );
247 textArea->setHtml( qfu( p_widget->psz_text ) );
248 p_widget->p_sys_intf = textArea;
251 case EXTENSION_WIDGET_TEXT_FIELD:
252 textInput = new QLineEdit( this );
253 textInput->setText( qfu( p_widget->psz_text ) );
254 textInput->setReadOnly( false );
255 textInput->setEchoMode( QLineEdit::Normal );
256 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
257 /// @note: maybe it would be wiser to use textEdited here?
258 CONNECT( textInput, textChanged(const QString &),
259 inputMapper, map() );
260 p_widget->p_sys_intf = textInput;
263 case EXTENSION_WIDGET_PASSWORD:
264 textInput = new QLineEdit( this );
265 textInput->setText( qfu( p_widget->psz_text ) );
266 textInput->setReadOnly( false );
267 textInput->setEchoMode( QLineEdit::Password );
268 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
269 /// @note: maybe it would be wiser to use textEdited here?
270 CONNECT( textInput, textChanged(const QString &),
271 inputMapper, map() );
272 p_widget->p_sys_intf = textInput;
275 case EXTENSION_WIDGET_CHECK_BOX:
276 checkBox = new QCheckBox( this );
277 checkBox->setText( qfu( p_widget->psz_text ) );
278 checkBox->setChecked( p_widget->b_checked );
279 clickMapper->setMapping( checkBox, new WidgetMapper( p_widget ) );
280 CONNECT( checkBox, stateChanged( int ), clickMapper, map() );
281 p_widget->p_sys_intf = checkBox;
284 case EXTENSION_WIDGET_DROPDOWN:
285 comboBox = new QComboBox( this );
286 comboBox->setEditable( false );
287 for( p_value = p_widget->p_values;
289 p_value = p_value->p_next )
291 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
293 /* Set current item */
294 if( p_widget->psz_text )
296 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
298 comboBox->setCurrentIndex( idx );
300 selectMapper->setMapping( comboBox, new WidgetMapper( p_widget ) );
301 CONNECT( comboBox, currentIndexChanged( const QString& ),
302 selectMapper, map() );
305 case EXTENSION_WIDGET_LIST:
306 list = new QListWidget( this );
307 list->setSelectionMode( QAbstractItemView::ExtendedSelection );
308 for( p_value = p_widget->p_values;
310 p_value = p_value->p_next )
312 QListWidgetItem *item =
313 new QListWidgetItem( qfu( p_value->psz_text ) );
314 item->setData( Qt::UserRole, p_value->i_id );
315 list->addItem( item );
317 selectMapper->setMapping( list, new WidgetMapper( p_widget ) );
318 CONNECT( list, itemSelectionChanged(),
319 selectMapper, map() );
323 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
329 * Forward click event to the extension
330 * @param object A WidgetMapper, whose data() is the p_widget
332 int ExtensionDialog::TriggerClick( QObject *object )
334 assert( object != NULL );
335 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
336 extension_widget_t *p_widget = mapping->getWidget();
338 QCheckBox *checkBox = NULL;
339 int i_ret = VLC_EGENERIC;
341 bool lockedHere = false;
344 vlc_mutex_lock( &p_dialog->lock );
349 switch( p_widget->type )
351 case EXTENSION_WIDGET_BUTTON:
352 i_ret = extension_WidgetClicked( p_dialog, p_widget );
355 case EXTENSION_WIDGET_CHECK_BOX:
356 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
357 p_widget->b_checked = checkBox->isChecked();
362 msg_Dbg( p_intf, "A click event was triggered by a wrong widget" );
368 vlc_mutex_unlock( &p_dialog->lock );
376 * Synchronize psz_text with the widget's text() value on update
377 * @param object A WidgetMapper
379 void ExtensionDialog::SyncInput( QObject *object )
381 assert( object != NULL );
383 bool lockedHere = false;
386 vlc_mutex_lock( &p_dialog->lock );
391 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
392 extension_widget_t *p_widget = mapping->getWidget();
393 assert( p_widget->type == EXTENSION_WIDGET_TEXT_FIELD
394 || p_widget->type == EXTENSION_WIDGET_PASSWORD );
395 /* Synchronize psz_text with the new value */
396 QLineEdit *widget = static_cast< QLineEdit* >( p_widget->p_sys_intf );
397 char *psz_text = widget->text().isNull() ? NULL : strdup( qtu( widget->text() ) );
398 free( p_widget->psz_text );
399 p_widget->psz_text = psz_text;
403 vlc_mutex_unlock( &p_dialog->lock );
409 * Synchronize parameter b_selected in the values list
410 * @param object A WidgetMapper
412 void ExtensionDialog::SyncSelection( QObject *object )
414 assert( object != NULL );
415 struct extension_widget_t::extension_widget_value_t *p_value;
417 bool lockedHere = false;
420 vlc_mutex_lock( &p_dialog->lock );
425 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
426 extension_widget_t *p_widget = mapping->getWidget();
427 assert( p_widget->type == EXTENSION_WIDGET_DROPDOWN
428 || p_widget->type == EXTENSION_WIDGET_LIST );
430 if( p_widget->type == EXTENSION_WIDGET_DROPDOWN )
432 QComboBox *combo = static_cast< QComboBox* >( p_widget->p_sys_intf );
433 for( p_value = p_widget->p_values;
435 p_value = p_value->p_next )
437 // if( !qstrcmp( p_value->psz_text, qtu( combo->currentText() ) ) )
438 if( combo->itemData( combo->currentIndex(), Qt::UserRole ).toInt()
441 p_value->b_selected = true;
445 p_value->b_selected = false;
448 free( p_widget->psz_text );
449 p_widget->psz_text = strdup( qtu( combo->currentText() ) );
451 else if( p_widget->type == EXTENSION_WIDGET_LIST )
453 QListWidget *list = static_cast<QListWidget*>( p_widget->p_sys_intf );
454 QList<QListWidgetItem *> selection = list->selectedItems();
455 for( p_value = p_widget->p_values;
457 p_value = p_value->p_next )
459 bool b_selected = false;
460 foreach( const QListWidgetItem *item, selection )
462 // if( !qstrcmp( qtu( item->text() ), p_value->psz_text ) )
463 if( item->data( Qt::UserRole ).toInt() == p_value->i_id )
469 p_value->b_selected = b_selected;
475 vlc_mutex_unlock( &p_dialog->lock );
480 void ExtensionDialog::UpdateWidgets()
483 extension_widget_t *p_widget;
484 FOREACH_ARRAY( p_widget, p_dialog->widgets )
486 if( !p_widget ) continue; /* Some widgets may be NULL at this point */
488 int row = p_widget->i_row - 1;
489 int col = p_widget->i_column - 1;
492 row = layout->rowCount();
496 col = layout->columnCount();
497 int hsp = __MAX( 1, p_widget->i_horiz_span );
498 int vsp = __MAX( 1, p_widget->i_vert_span );
499 if( !p_widget->p_sys_intf && !p_widget->b_kill )
501 widget = CreateWidget( p_widget );
504 msg_Warn( p_intf, "Could not create a widget for dialog %s",
505 p_dialog->psz_title );
508 widget->setVisible( !p_widget->b_hide );
509 layout->addWidget( widget, row, col, vsp, hsp );
510 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
511 widget->resize( p_widget->i_width, p_widget->i_height );
512 p_widget->p_sys_intf = widget;
513 this->resize( sizeHint() );
515 else if( p_widget->p_sys_intf && !p_widget->b_kill
516 && p_widget->b_update )
518 widget = UpdateWidget( p_widget );
521 msg_Warn( p_intf, "Could not update a widget for dialog %s",
522 p_dialog->psz_title );
525 widget->setVisible( !p_widget->b_hide );
526 layout->addWidget( widget, row, col, vsp, hsp );
527 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
528 widget->resize( p_widget->i_width, p_widget->i_height );
529 p_widget->p_sys_intf = widget;
530 this->resize( sizeHint() );
532 /* Do not update again */
533 p_widget->b_update = false;
535 else if( p_widget->p_sys_intf && p_widget->b_kill )
537 DestroyWidget( p_widget );
538 p_widget->p_sys_intf = NULL;
539 this->resize( sizeHint() );
545 QWidget* ExtensionDialog::UpdateWidget( extension_widget_t *p_widget )
547 QLabel *label = NULL;
548 QPushButton *button = NULL;
549 QTextBrowser *textArea = NULL;
550 QLineEdit *textInput = NULL;
551 QCheckBox *checkBox = NULL;
552 QComboBox *comboBox = NULL;
553 QListWidget *list = NULL;
554 struct extension_widget_t::extension_widget_value_t *p_value = NULL;
556 assert( p_widget->p_sys_intf != NULL );
558 switch( p_widget->type )
560 case EXTENSION_WIDGET_LABEL:
561 label = static_cast< QLabel* >( p_widget->p_sys_intf );
562 label->setText( qfu( p_widget->psz_text ) );
565 case EXTENSION_WIDGET_BUTTON:
566 // FIXME: looks like removeMappings does not work
567 button = static_cast< QPushButton* >( p_widget->p_sys_intf );
568 button->setText( qfu( p_widget->psz_text ) );
569 clickMapper->removeMappings( button );
570 clickMapper->setMapping( button, new WidgetMapper( p_widget ) );
571 CONNECT( button, clicked(), clickMapper, map() );
574 case EXTENSION_WIDGET_IMAGE:
575 label = static_cast< QLabel* >( p_widget->p_sys_intf );
576 label->setPixmap( QPixmap( qfu( p_widget->psz_text ) ) );
579 case EXTENSION_WIDGET_HTML:
580 textArea = static_cast< QTextBrowser* >( p_widget->p_sys_intf );
581 textArea->setHtml( qfu( p_widget->psz_text ) );
584 case EXTENSION_WIDGET_TEXT_FIELD:
585 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
586 textInput->setText( qfu( p_widget->psz_text ) );
589 case EXTENSION_WIDGET_PASSWORD:
590 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
591 textInput->setText( qfu( p_widget->psz_text ) );
594 case EXTENSION_WIDGET_CHECK_BOX:
595 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
596 checkBox->setText( qfu( p_widget->psz_text ) );
597 checkBox->setChecked( p_widget->b_checked );
600 case EXTENSION_WIDGET_DROPDOWN:
601 comboBox = static_cast< QComboBox* >( p_widget->p_sys_intf );
603 for( p_value = p_widget->p_values;
605 p_value = p_value->p_next )
607 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
609 /* Set current item */
610 if( p_widget->psz_text )
612 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
614 comboBox->setCurrentIndex( idx );
618 case EXTENSION_WIDGET_LIST:
619 list = static_cast< QListWidget* >( p_widget->p_sys_intf );
621 for( p_value = p_widget->p_values;
623 p_value = p_value->p_next )
625 QListWidgetItem *item =
626 new QListWidgetItem( qfu( p_value->psz_text ) );
627 item->setData( Qt::UserRole, p_value->i_id );
628 list->addItem( item );
633 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
638 void ExtensionDialog::DestroyWidget( extension_widget_t *p_widget )
640 assert( p_widget && p_widget->b_kill );
641 QWidget *widget = static_cast< QWidget* >( p_widget->p_sys_intf );
643 p_widget->p_sys_intf = NULL;
646 /** Implement closeEvent() in order to intercept the event */
647 void ExtensionDialog::closeEvent( QCloseEvent *event )
649 assert( p_dialog != NULL );
650 extension_DialogClosed( p_dialog );
652 emit destroyDialog( p_dialog );