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>
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 QTextEdit *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 QTextEdit( this );
246 textArea->setAcceptRichText( true );
247 textArea->setReadOnly( true );
248 textArea->setHtml( qfu( p_widget->psz_text ) );
249 p_widget->p_sys_intf = textArea;
252 case EXTENSION_WIDGET_TEXT_FIELD:
253 textInput = new QLineEdit( this );
254 textInput->setText( qfu( p_widget->psz_text ) );
255 textInput->setReadOnly( false );
256 textInput->setEchoMode( QLineEdit::Normal );
257 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
258 /// @note: maybe it would be wiser to use textEdited here?
259 CONNECT( textInput, textChanged(const QString &),
260 inputMapper, map() );
261 p_widget->p_sys_intf = textInput;
264 case EXTENSION_WIDGET_PASSWORD:
265 textInput = new QLineEdit( this );
266 textInput->setText( qfu( p_widget->psz_text ) );
267 textInput->setReadOnly( false );
268 textInput->setEchoMode( QLineEdit::Password );
269 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
270 /// @note: maybe it would be wiser to use textEdited here?
271 CONNECT( textInput, textChanged(const QString &),
272 inputMapper, map() );
273 p_widget->p_sys_intf = textInput;
276 case EXTENSION_WIDGET_CHECK_BOX:
277 checkBox = new QCheckBox( this );
278 checkBox->setText( qfu( p_widget->psz_text ) );
279 checkBox->setChecked( p_widget->b_checked );
280 clickMapper->setMapping( checkBox, new WidgetMapper( p_widget ) );
281 CONNECT( checkBox, stateChanged( int ), clickMapper, map() );
282 p_widget->p_sys_intf = checkBox;
285 case EXTENSION_WIDGET_DROPDOWN:
286 comboBox = new QComboBox( this );
287 comboBox->setEditable( false );
288 for( p_value = p_widget->p_values;
290 p_value = p_value->p_next )
292 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
294 /* Set current item */
295 if( p_widget->psz_text )
297 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
299 comboBox->setCurrentIndex( idx );
301 selectMapper->setMapping( comboBox, new WidgetMapper( p_widget ) );
302 CONNECT( comboBox, currentIndexChanged( const QString& ),
303 selectMapper, map() );
306 case EXTENSION_WIDGET_LIST:
307 list = new QListWidget( this );
308 list->setSelectionMode( QAbstractItemView::MultiSelection );
309 for( p_value = p_widget->p_values;
311 p_value = p_value->p_next )
313 QListWidgetItem *item =
314 new QListWidgetItem( qfu( p_value->psz_text ) );
315 item->setData( Qt::UserRole, p_value->i_id );
316 list->addItem( item );
318 selectMapper->setMapping( list, new WidgetMapper( p_widget ) );
319 CONNECT( list, itemSelectionChanged(),
320 selectMapper, map() );
324 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
330 * Forward click event to the extension
331 * @param object A WidgetMapper, whose data() is the p_widget
333 int ExtensionDialog::TriggerClick( QObject *object )
335 assert( object != NULL );
336 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
337 extension_widget_t *p_widget = mapping->getWidget();
339 QCheckBox *checkBox = NULL;
340 int i_ret = VLC_EGENERIC;
342 bool lockedHere = false;
345 vlc_mutex_lock( &p_dialog->lock );
350 switch( p_widget->type )
352 case EXTENSION_WIDGET_BUTTON:
353 i_ret = extension_WidgetClicked( p_dialog, p_widget );
356 case EXTENSION_WIDGET_CHECK_BOX:
357 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
358 p_widget->b_checked = checkBox->isChecked();
363 msg_Dbg( p_intf, "A click event was triggered by a wrong widget" );
369 vlc_mutex_unlock( &p_dialog->lock );
377 * Synchronize psz_text with the widget's text() value on update
378 * @param object A WidgetMapper
380 void ExtensionDialog::SyncInput( QObject *object )
382 assert( object != NULL );
384 bool lockedHere = false;
387 vlc_mutex_lock( &p_dialog->lock );
392 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
393 extension_widget_t *p_widget = mapping->getWidget();
394 assert( p_widget->type == EXTENSION_WIDGET_TEXT_FIELD
395 || p_widget->type == EXTENSION_WIDGET_PASSWORD );
396 /* Synchronize psz_text with the new value */
397 QLineEdit *widget = static_cast< QLineEdit* >( p_widget->p_sys_intf );
398 char *psz_text = widget->text().isNull() ? NULL : strdup( qtu( widget->text() ) );
399 free( p_widget->psz_text );
400 p_widget->psz_text = psz_text;
404 vlc_mutex_unlock( &p_dialog->lock );
410 * Synchronize parameter b_selected in the values list
411 * @param object A WidgetMapper
413 void ExtensionDialog::SyncSelection( QObject *object )
415 assert( object != NULL );
416 struct extension_widget_t::extension_widget_value_t *p_value;
418 bool lockedHere = false;
421 vlc_mutex_lock( &p_dialog->lock );
426 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
427 extension_widget_t *p_widget = mapping->getWidget();
428 assert( p_widget->type == EXTENSION_WIDGET_DROPDOWN
429 || p_widget->type == EXTENSION_WIDGET_LIST );
431 if( p_widget->type == EXTENSION_WIDGET_DROPDOWN )
433 QComboBox *combo = static_cast< QComboBox* >( p_widget->p_sys_intf );
434 for( p_value = p_widget->p_values;
436 p_value = p_value->p_next )
438 // if( !qstrcmp( p_value->psz_text, qtu( combo->currentText() ) ) )
439 if( combo->itemData( combo->currentIndex(), Qt::UserRole ).toInt()
442 p_value->b_selected = true;
446 p_value->b_selected = false;
449 free( p_widget->psz_text );
450 p_widget->psz_text = strdup( qtu( combo->currentText() ) );
452 else if( p_widget->type == EXTENSION_WIDGET_LIST )
454 QListWidget *list = static_cast<QListWidget*>( p_widget->p_sys_intf );
455 QList<QListWidgetItem *> selection = list->selectedItems();
456 for( p_value = p_widget->p_values;
458 p_value = p_value->p_next )
460 bool b_selected = false;
461 foreach( const QListWidgetItem *item, selection )
463 // if( !qstrcmp( qtu( item->text() ), p_value->psz_text ) )
464 if( item->data( Qt::UserRole ).toInt() == p_value->i_id )
470 p_value->b_selected = b_selected;
476 vlc_mutex_unlock( &p_dialog->lock );
481 void ExtensionDialog::UpdateWidgets()
484 extension_widget_t *p_widget;
485 FOREACH_ARRAY( p_widget, p_dialog->widgets )
487 if( !p_widget ) continue; /* Some widgets may be NULL at this point */
489 int row = p_widget->i_row - 1;
490 int col = p_widget->i_column - 1;
493 row = layout->rowCount();
497 col = layout->columnCount();
498 int hsp = __MAX( 1, p_widget->i_horiz_span );
499 int vsp = __MAX( 1, p_widget->i_vert_span );
500 if( !p_widget->p_sys_intf && !p_widget->b_kill )
502 widget = CreateWidget( p_widget );
505 msg_Warn( p_intf, "Could not create a widget for dialog %s",
506 p_dialog->psz_title );
509 widget->setVisible( !p_widget->b_hide );
510 layout->addWidget( widget, row, col, vsp, hsp );
511 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
512 widget->resize( p_widget->i_width, p_widget->i_height );
513 p_widget->p_sys_intf = widget;
514 this->resize( sizeHint() );
516 else if( p_widget->p_sys_intf && !p_widget->b_kill
517 && p_widget->b_update )
519 widget = UpdateWidget( p_widget );
522 msg_Warn( p_intf, "Could not update a widget for dialog %s",
523 p_dialog->psz_title );
526 widget->setVisible( !p_widget->b_hide );
527 layout->addWidget( widget, row, col, vsp, hsp );
528 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
529 widget->resize( p_widget->i_width, p_widget->i_height );
530 p_widget->p_sys_intf = widget;
531 this->resize( sizeHint() );
533 /* Do not update again */
534 p_widget->b_update = false;
536 else if( p_widget->p_sys_intf && p_widget->b_kill )
538 DestroyWidget( p_widget );
539 p_widget->p_sys_intf = NULL;
540 this->resize( sizeHint() );
546 QWidget* ExtensionDialog::UpdateWidget( extension_widget_t *p_widget )
548 QLabel *label = NULL;
549 QPushButton *button = NULL;
550 QTextEdit *textArea = NULL;
551 QLineEdit *textInput = NULL;
552 QCheckBox *checkBox = NULL;
553 QComboBox *comboBox = NULL;
554 QListWidget *list = NULL;
555 struct extension_widget_t::extension_widget_value_t *p_value = NULL;
557 assert( p_widget->p_sys_intf != NULL );
559 switch( p_widget->type )
561 case EXTENSION_WIDGET_LABEL:
562 label = static_cast< QLabel* >( p_widget->p_sys_intf );
563 label->setText( qfu( p_widget->psz_text ) );
566 case EXTENSION_WIDGET_BUTTON:
567 // FIXME: looks like removeMappings does not work
568 button = static_cast< QPushButton* >( p_widget->p_sys_intf );
569 button->setText( qfu( p_widget->psz_text ) );
570 clickMapper->removeMappings( button );
571 clickMapper->setMapping( button, new WidgetMapper( p_widget ) );
572 CONNECT( button, clicked(), clickMapper, map() );
575 case EXTENSION_WIDGET_IMAGE:
576 label = static_cast< QLabel* >( p_widget->p_sys_intf );
577 label->setPixmap( QPixmap( qfu( p_widget->psz_text ) ) );
580 case EXTENSION_WIDGET_HTML:
581 textArea = static_cast< QTextEdit* >( p_widget->p_sys_intf );
582 textArea->setHtml( qfu( p_widget->psz_text ) );
585 case EXTENSION_WIDGET_TEXT_FIELD:
586 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
587 textInput->setText( qfu( p_widget->psz_text ) );
590 case EXTENSION_WIDGET_PASSWORD:
591 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
592 textInput->setText( qfu( p_widget->psz_text ) );
595 case EXTENSION_WIDGET_CHECK_BOX:
596 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
597 checkBox->setText( qfu( p_widget->psz_text ) );
598 checkBox->setChecked( p_widget->b_checked );
601 case EXTENSION_WIDGET_DROPDOWN:
602 comboBox = static_cast< QComboBox* >( p_widget->p_sys_intf );
604 for( p_value = p_widget->p_values;
606 p_value = p_value->p_next )
608 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
610 /* Set current item */
611 if( p_widget->psz_text )
613 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
615 comboBox->setCurrentIndex( idx );
619 case EXTENSION_WIDGET_LIST:
620 list = static_cast< QListWidget* >( p_widget->p_sys_intf );
622 for( p_value = p_widget->p_values;
624 p_value = p_value->p_next )
626 QListWidgetItem *item =
627 new QListWidgetItem( qfu( p_value->psz_text ) );
628 item->setData( Qt::UserRole, p_value->i_id );
629 list->addItem( item );
634 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
639 void ExtensionDialog::DestroyWidget( extension_widget_t *p_widget )
641 assert( p_widget && p_widget->b_kill );
642 QWidget *widget = static_cast< QWidget* >( p_widget->p_sys_intf );
644 p_widget->p_sys_intf = NULL;
647 /** Implement closeEvent() in order to intercept the event */
648 void ExtensionDialog::closeEvent( QCloseEvent *event )
650 assert( p_dialog != NULL );
651 extension_DialogClosed( p_dialog );
653 emit destroyDialog( p_dialog );