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, UpdateDialog( 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::CreateDialog( extension_dialog_t *p_dialog )
70 ExtensionDialog *dialog = new ExtensionDialog( p_intf,
73 p_dialog->p_sys_intf = (void*) dialog;
74 CONNECT( dialog, destroyDialog( extension_dialog_t* ),
75 this, DestroyDialog( extension_dialog_t* ) );
80 * Note: Lock on p_dialog->lock must be held. */
81 int ExtensionsDialogProvider::DestroyDialog( extension_dialog_t *p_dialog )
84 ExtensionDialog *dialog = ( ExtensionDialog* ) p_dialog->p_sys_intf;
88 p_dialog->p_sys_intf = NULL;
93 * Update/Create/Destroy a dialog
95 ExtensionDialog* ExtensionsDialogProvider::UpdateDialog( extension_dialog_t *p_dialog )
99 ExtensionDialog *dialog = ( ExtensionDialog* ) p_dialog->p_sys_intf;
100 if( p_dialog->b_kill && !dialog )
102 /* This extension could not be activated properly but tried
103 to create a dialog. We must ignore it. */
107 vlc_mutex_lock( &p_dialog->lock );
108 if( !p_dialog->b_kill && !dialog )
110 dialog = CreateDialog( p_dialog );
111 dialog->setVisible( !p_dialog->b_hide );
113 else if( !p_dialog->b_kill && dialog )
115 dialog->has_lock = true;
116 dialog->UpdateWidgets();
117 dialog->has_lock = false;
118 dialog->setVisible( !p_dialog->b_hide );
120 else if( p_dialog->b_kill )
122 DestroyDialog( p_dialog );
124 vlc_cond_signal( &p_dialog->cond );
125 vlc_mutex_unlock( &p_dialog->lock );
130 * Ask the dialog manager to create/update/kill the dialog. Thread-safe.
132 void ExtensionsDialogProvider::ManageDialog( extension_dialog_t *p_dialog )
135 ExtensionsManager *extMgr = ExtensionsManager::getInstance( p_intf );
136 assert( extMgr != NULL );
137 if( !extMgr->isUnloading() )
138 emit SignalDialog( p_dialog ); // Safe because we signal Qt thread
140 UpdateDialog( p_dialog ); // This is safe, we're already in Qt thread
144 * Ask the dialogs provider to create a new dialog
146 static int DialogCallback( vlc_object_t *p_this, const char *psz_variable,
147 vlc_value_t old_val, vlc_value_t new_val,
155 ExtensionsDialogProvider *p_edp = ExtensionsDialogProvider::getInstance();
158 if( !new_val.p_address )
161 extension_dialog_t *p_dialog = ( extension_dialog_t* ) new_val.p_address;
162 p_edp->ManageDialog( p_dialog );
167 ExtensionDialog::ExtensionDialog( intf_thread_t *_p_intf,
168 extensions_manager_t *p_mgr,
169 extension_dialog_t *_p_dialog )
170 : QDialog( NULL ), p_intf( _p_intf ), p_extensions_manager( p_mgr )
171 , p_dialog( _p_dialog ), has_lock(false)
175 msg_Dbg( p_intf, "Creating a new dialog: '%s'", p_dialog->psz_title );
176 this->setWindowFlags( Qt::WindowMinMaxButtonsHint
177 | Qt::WindowCloseButtonHint );
178 this->setWindowTitle( qfu( p_dialog->psz_title ) );
180 layout = new QGridLayout( this );
181 clickMapper = new QSignalMapper( this );
182 CONNECT( clickMapper, mapped( QObject* ), this, TriggerClick( QObject* ) );
183 inputMapper = new QSignalMapper( this );
184 CONNECT( inputMapper, mapped( QObject* ), this, SyncInput( QObject* ) );
185 selectMapper = new QSignalMapper( this );
186 CONNECT( selectMapper, mapped( QObject* ), this, SyncSelection(QObject*) );
191 ExtensionDialog::~ExtensionDialog()
193 msg_Dbg( p_intf, "Deleting extension dialog '%s'", qtu(windowTitle()) );
194 p_dialog->p_sys_intf = NULL;
197 QWidget* ExtensionDialog::CreateWidget( extension_widget_t *p_widget )
199 QLabel *label = NULL;
200 QPushButton *button = NULL;
201 QTextEdit *textArea = NULL;
202 QLineEdit *textInput = NULL;
203 QCheckBox *checkBox = NULL;
204 QComboBox *comboBox = NULL;
205 QListWidget *list = NULL;
206 struct extension_widget_t::extension_widget_value_t *p_value = NULL;
208 assert( p_widget->p_sys_intf == NULL );
210 switch( p_widget->type )
212 case EXTENSION_WIDGET_LABEL:
213 label = new QLabel( qfu( p_widget->psz_text ), this );
214 p_widget->p_sys_intf = label;
215 label->setTextFormat( Qt::RichText );
216 //label->setFixedHeight( label->sizeHint().height );
219 case EXTENSION_WIDGET_BUTTON:
220 button = new QPushButton( qfu( p_widget->psz_text ), this );
221 clickMapper->setMapping( button, new WidgetMapper( p_widget ) );
222 CONNECT( button, clicked(), clickMapper, map() );
223 p_widget->p_sys_intf = button;
226 case EXTENSION_WIDGET_IMAGE:
227 label = new QLabel( this );
228 label->setPixmap( QPixmap( qfu( p_widget->psz_text ) ) );
229 if( p_widget->i_width > 0 )
230 label->setMaximumWidth( p_widget->i_width );
231 if( p_widget->i_height > 0 )
232 label->setMaximumHeight( p_widget->i_height );
233 label->setScaledContents( true );
234 p_widget->p_sys_intf = label;
237 case EXTENSION_WIDGET_HTML:
238 textArea = new QTextEdit( this );
239 textArea->setAcceptRichText( true );
240 textArea->setReadOnly( true );
241 textArea->setHtml( qfu( p_widget->psz_text ) );
242 p_widget->p_sys_intf = textArea;
245 case EXTENSION_WIDGET_TEXT_FIELD:
246 textInput = new QLineEdit( this );
247 textInput->setText( qfu( p_widget->psz_text ) );
248 textInput->setReadOnly( false );
249 textInput->setEchoMode( QLineEdit::Normal );
250 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
251 /// @note: maybe it would be wiser to use textEdited here?
252 CONNECT( textInput, textChanged(const QString &),
253 inputMapper, map() );
254 p_widget->p_sys_intf = textInput;
257 case EXTENSION_WIDGET_PASSWORD:
258 textInput = new QLineEdit( this );
259 textInput->setText( qfu( p_widget->psz_text ) );
260 textInput->setReadOnly( false );
261 textInput->setEchoMode( QLineEdit::Password );
262 inputMapper->setMapping( textInput, new WidgetMapper( p_widget ) );
263 /// @note: maybe it would be wiser to use textEdited here?
264 CONNECT( textInput, textChanged(const QString &),
265 inputMapper, map() );
266 p_widget->p_sys_intf = textInput;
269 case EXTENSION_WIDGET_CHECK_BOX:
270 checkBox = new QCheckBox( this );
271 checkBox->setText( qfu( p_widget->psz_text ) );
272 checkBox->setChecked( p_widget->b_checked );
273 clickMapper->setMapping( checkBox, new WidgetMapper( p_widget ) );
274 CONNECT( checkBox, stateChanged( int ), clickMapper, map() );
275 p_widget->p_sys_intf = checkBox;
278 case EXTENSION_WIDGET_DROPDOWN:
279 comboBox = new QComboBox( this );
280 comboBox->setEditable( false );
281 for( p_value = p_widget->p_values;
283 p_value = p_value->p_next )
285 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
287 /* Set current item */
288 if( p_widget->psz_text )
290 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
292 comboBox->setCurrentIndex( idx );
294 selectMapper->setMapping( comboBox, new WidgetMapper( p_widget ) );
295 CONNECT( comboBox, currentIndexChanged( const QString& ),
296 selectMapper, map() );
299 case EXTENSION_WIDGET_LIST:
300 list = new QListWidget( this );
301 list->setSelectionMode( QAbstractItemView::MultiSelection );
302 for( p_value = p_widget->p_values;
304 p_value = p_value->p_next )
306 QListWidgetItem *item =
307 new QListWidgetItem( qfu( p_value->psz_text ) );
308 item->setData( Qt::UserRole, p_value->i_id );
309 list->addItem( item );
311 selectMapper->setMapping( list, new WidgetMapper( p_widget ) );
312 CONNECT( list, itemSelectionChanged(),
313 selectMapper, map() );
317 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
323 * Forward click event to the extension
324 * @param object A WidgetMapper, whose data() is the p_widget
326 int ExtensionDialog::TriggerClick( QObject *object )
328 assert( object != NULL );
329 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
330 extension_widget_t *p_widget = mapping->getWidget();
332 QCheckBox *checkBox = NULL;
333 int i_ret = VLC_EGENERIC;
335 bool lockedHere = false;
338 vlc_mutex_lock( &p_dialog->lock );
343 switch( p_widget->type )
345 case EXTENSION_WIDGET_BUTTON:
346 i_ret = extension_WidgetClicked( p_dialog, p_widget );
349 case EXTENSION_WIDGET_CHECK_BOX:
350 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
351 p_widget->b_checked = checkBox->isChecked();
356 msg_Dbg( p_intf, "A click event was triggered by a wrong widget" );
362 vlc_mutex_unlock( &p_dialog->lock );
370 * Synchronize psz_text with the widget's text() value on update
371 * @param object A WidgetMapper
373 void ExtensionDialog::SyncInput( QObject *object )
375 assert( object != NULL );
377 bool lockedHere = false;
380 vlc_mutex_lock( &p_dialog->lock );
385 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
386 extension_widget_t *p_widget = mapping->getWidget();
387 assert( p_widget->type == EXTENSION_WIDGET_TEXT_FIELD
388 || p_widget->type == EXTENSION_WIDGET_PASSWORD );
389 /* Synchronize psz_text with the new value */
390 QLineEdit *widget = static_cast< QLineEdit* >( p_widget->p_sys_intf );
391 char *psz_text = widget->text().isNull() ? NULL : strdup( qtu( widget->text() ) );
392 free( p_widget->psz_text );
393 p_widget->psz_text = psz_text;
397 vlc_mutex_unlock( &p_dialog->lock );
403 * Synchronize parameter b_selected in the values list
404 * @param object A WidgetMapper
406 void ExtensionDialog::SyncSelection( QObject *object )
408 assert( object != NULL );
409 struct extension_widget_t::extension_widget_value_t *p_value;
411 bool lockedHere = false;
414 vlc_mutex_lock( &p_dialog->lock );
419 WidgetMapper *mapping = static_cast< WidgetMapper* >( object );
420 extension_widget_t *p_widget = mapping->getWidget();
421 assert( p_widget->type == EXTENSION_WIDGET_DROPDOWN
422 || p_widget->type == EXTENSION_WIDGET_LIST );
424 if( p_widget->type == EXTENSION_WIDGET_DROPDOWN )
426 QComboBox *combo = static_cast< QComboBox* >( p_widget->p_sys_intf );
427 for( p_value = p_widget->p_values;
429 p_value = p_value->p_next )
431 // if( !qstrcmp( p_value->psz_text, qtu( combo->currentText() ) ) )
432 if( combo->itemData( combo->currentIndex(), Qt::UserRole ).toInt()
435 p_value->b_selected = true;
439 p_value->b_selected = false;
442 free( p_widget->psz_text );
443 p_widget->psz_text = strdup( qtu( combo->currentText() ) );
445 else if( p_widget->type == EXTENSION_WIDGET_LIST )
447 QListWidget *list = static_cast<QListWidget*>( p_widget->p_sys_intf );
448 QList<QListWidgetItem *> selection = list->selectedItems();
449 for( p_value = p_widget->p_values;
451 p_value = p_value->p_next )
453 bool b_selected = false;
454 foreach( const QListWidgetItem *item, selection )
456 // if( !qstrcmp( qtu( item->text() ), p_value->psz_text ) )
457 if( item->data( Qt::UserRole ).toInt() == p_value->i_id )
463 p_value->b_selected = b_selected;
469 vlc_mutex_unlock( &p_dialog->lock );
474 void ExtensionDialog::UpdateWidgets()
477 extension_widget_t *p_widget;
478 FOREACH_ARRAY( p_widget, p_dialog->widgets )
480 if( !p_widget ) continue; /* Some widgets may be NULL at this point */
482 int row = p_widget->i_row - 1;
483 int col = p_widget->i_column - 1;
486 row = layout->rowCount();
490 col = layout->columnCount();
491 int hsp = __MAX( 1, p_widget->i_horiz_span );
492 int vsp = __MAX( 1, p_widget->i_vert_span );
493 if( !p_widget->p_sys_intf && !p_widget->b_kill )
495 widget = CreateWidget( p_widget );
498 msg_Warn( p_intf, "Could not create a widget for dialog %s",
499 p_dialog->psz_title );
502 widget->setVisible( !p_widget->b_hide );
503 layout->addWidget( widget, row, col, vsp, hsp );
504 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
505 widget->resize( p_widget->i_width, p_widget->i_height );
506 p_widget->p_sys_intf = widget;
507 this->resize( sizeHint() );
509 else if( p_widget->p_sys_intf && !p_widget->b_kill
510 && p_widget->b_update )
512 widget = UpdateWidget( p_widget );
515 msg_Warn( p_intf, "Could not update a widget for dialog %s",
516 p_dialog->psz_title );
519 widget->setVisible( !p_widget->b_hide );
520 layout->addWidget( widget, row, col, vsp, hsp );
521 if( ( p_widget->i_width > 0 ) && ( p_widget->i_height > 0 ) )
522 widget->resize( p_widget->i_width, p_widget->i_height );
523 p_widget->p_sys_intf = widget;
524 this->resize( sizeHint() );
526 /* Do not update again */
527 p_widget->b_update = false;
529 else if( p_widget->p_sys_intf && p_widget->b_kill )
531 DestroyWidget( p_widget );
532 p_widget->p_sys_intf = NULL;
533 this->resize( sizeHint() );
539 QWidget* ExtensionDialog::UpdateWidget( extension_widget_t *p_widget )
541 QLabel *label = NULL;
542 QPushButton *button = NULL;
543 QTextEdit *textArea = NULL;
544 QLineEdit *textInput = NULL;
545 QCheckBox *checkBox = NULL;
546 QComboBox *comboBox = NULL;
547 QListWidget *list = NULL;
548 struct extension_widget_t::extension_widget_value_t *p_value = NULL;
550 assert( p_widget->p_sys_intf != NULL );
552 switch( p_widget->type )
554 case EXTENSION_WIDGET_LABEL:
555 label = static_cast< QLabel* >( p_widget->p_sys_intf );
556 label->setText( qfu( p_widget->psz_text ) );
559 case EXTENSION_WIDGET_BUTTON:
560 // FIXME: looks like removeMappings does not work
561 button = static_cast< QPushButton* >( p_widget->p_sys_intf );
562 button->setText( qfu( p_widget->psz_text ) );
563 clickMapper->removeMappings( button );
564 clickMapper->setMapping( button, new WidgetMapper( p_widget ) );
565 CONNECT( button, clicked(), clickMapper, map() );
568 case EXTENSION_WIDGET_IMAGE:
569 label = static_cast< QLabel* >( p_widget->p_sys_intf );
570 label->setPixmap( QPixmap( qfu( p_widget->psz_text ) ) );
573 case EXTENSION_WIDGET_HTML:
574 textArea = static_cast< QTextEdit* >( p_widget->p_sys_intf );
575 textArea->setHtml( qfu( p_widget->psz_text ) );
578 case EXTENSION_WIDGET_TEXT_FIELD:
579 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
580 textInput->setText( qfu( p_widget->psz_text ) );
583 case EXTENSION_WIDGET_PASSWORD:
584 textInput = static_cast< QLineEdit* >( p_widget->p_sys_intf );
585 textInput->setText( qfu( p_widget->psz_text ) );
588 case EXTENSION_WIDGET_CHECK_BOX:
589 checkBox = static_cast< QCheckBox* >( p_widget->p_sys_intf );
590 checkBox->setText( qfu( p_widget->psz_text ) );
591 checkBox->setChecked( p_widget->b_checked );
594 case EXTENSION_WIDGET_DROPDOWN:
595 comboBox = static_cast< QComboBox* >( p_widget->p_sys_intf );
597 for( p_value = p_widget->p_values;
599 p_value = p_value->p_next )
601 comboBox->addItem( qfu( p_value->psz_text ), p_value->i_id );
603 /* Set current item */
604 if( p_widget->psz_text )
606 int idx = comboBox->findText( qfu( p_widget->psz_text ) );
608 comboBox->setCurrentIndex( idx );
612 case EXTENSION_WIDGET_LIST:
613 list = static_cast< QListWidget* >( p_widget->p_sys_intf );
615 for( p_value = p_widget->p_values;
617 p_value = p_value->p_next )
619 QListWidgetItem *item =
620 new QListWidgetItem( qfu( p_value->psz_text ) );
621 item->setData( Qt::UserRole, p_value->i_id );
622 list->addItem( item );
627 msg_Err( p_intf, "Widget type %d unknown", p_widget->type );
632 void ExtensionDialog::DestroyWidget( extension_widget_t *p_widget )
634 assert( p_widget && p_widget->b_kill );
635 QWidget *widget = static_cast< QWidget* >( p_widget->p_sys_intf );
637 p_widget->p_sys_intf = NULL;
640 /** Implement closeEvent() in order to intercept the event */
641 void ExtensionDialog::closeEvent( QCloseEvent *event )
643 assert( p_dialog != NULL );
644 extension_DialogClosed( p_dialog );
646 emit destroyDialog( p_dialog );