Add support for modules help strings (Closes:#413)
[vlc.git] / modules / gui / qt4 / components / preferences.cpp
1 /*****************************************************************************
2  * preferences_tree.cpp : Tree of modules for preferences
3  ****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #include "components/preferences.hpp"
25 #include "components/preferences_widgets.hpp"
26 #include "qt4.hpp"
27 #include <vlc_config_cat.h>
28 #include <assert.h>
29
30 #include "pixmaps/audio.xpm"
31 #include "pixmaps/video.xpm"
32 #include "pixmaps/type_net.xpm"
33 #include "pixmaps/type_playlist.xpm"
34 #include "pixmaps/advanced.xpm"
35 #include "pixmaps/codec.xpm"
36 #include "pixmaps/intf.xpm"
37
38 #include <QLabel>
39 #include <QTreeWidget>
40 #include <QTreeWidgetItem>
41 #include <QVariant>
42 #include <QString>
43 #include <QFont>
44 #include <QGroupBox>
45 #include <QScrollArea>
46 #include <QVBoxLayout>
47 #include <QHBoxLayout>
48
49 #define ITEM_HEIGHT 25
50
51 /*********************************************************************
52  * The Tree
53  *********************************************************************/
54 PrefsTree::PrefsTree( intf_thread_t *_p_intf, QWidget *_parent ) :
55                             QTreeWidget( _parent ), p_intf( _p_intf )
56 {
57     module_t *p_module;
58     vlc_list_t *p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE,
59                                         FIND_ANYWHERE );
60     if( !p_list ) return;
61
62     setColumnCount( 1 );
63     setIconSize( QSize( ITEM_HEIGHT,ITEM_HEIGHT ) );
64     setAlternatingRowColors( true );
65
66     QFont f = font();
67     f.setPointSize( f.pointSize() + 1 );
68     setFont( f );
69
70 #define BI( a,b) QIcon a##_icon = QIcon( QPixmap( b##_xpm ))
71     BI( audio, audio );
72     BI( video, video );
73     BI( input, codec );
74     BI( sout, type_net );
75     BI( advanced, advanced );
76     BI( playlist, type_playlist );
77     BI( interface, intf );
78 #undef BI
79
80     /* Build the tree for the main module */
81     int i_index;
82     for( i_index = 0; i_index < p_list->i_count; i_index++ )
83     {
84         p_module = (module_t *)p_list->p_values[i_index].p_object;
85         if( !strcmp( p_module->psz_object_name, "main" ) )
86              break;
87     }
88     if( i_index < p_list->i_count )
89     {
90         module_config_t *p_item = p_module->p_config;
91         PrefsItemData *data = NULL;
92         QTreeWidgetItem *current_item = NULL;
93         if( p_item ) do
94         {
95             char *psz_help;
96             QIcon icon;
97             switch( p_item->i_type )
98             {
99             case CONFIG_CATEGORY:
100                 if( p_item->i_value == -1 ) break;
101                 data = new PrefsItemData();
102                 data->name = QString( config_CategoryNameGet
103                                                ( p_item->i_value ) );
104                 psz_help = config_CategoryHelpGet( p_item->i_value );
105                 if( psz_help )
106                     data->help = QString( psz_help );
107                 else
108                     data->help.clear();
109                 data->i_type = TYPE_CATEGORY;
110                 data->i_object_id = p_item->i_value;
111
112                 switch( p_item->i_value )
113                 {
114 #define CI(a,b) case a: icon = b##_icon;break
115                 CI( CAT_AUDIO, audio );
116                 CI( CAT_VIDEO, video );
117                 CI( CAT_INPUT, input );
118                 CI( CAT_SOUT, sout );
119                 CI( CAT_ADVANCED, advanced );
120                 CI( CAT_PLAYLIST, playlist );
121                 CI( CAT_INTERFACE, interface );
122 #undef CI
123                 }
124
125                 current_item = new QTreeWidgetItem();
126                 current_item->setText( 0, data->name );
127                 current_item->setIcon( 0 , icon );
128                 current_item->setData( 0, Qt::UserRole,
129                                        qVariantFromValue( data ) );
130                 addTopLevelItem( current_item );
131                 break;
132             case CONFIG_SUBCATEGORY:
133                 if( p_item->i_value == -1 ) break;
134                 if( data &&
135                     ( p_item->i_value == SUBCAT_VIDEO_GENERAL ||
136                       p_item->i_value == SUBCAT_ADVANCED_MISC ||
137                       p_item->i_value == SUBCAT_INPUT_GENERAL ||
138                       p_item->i_value == SUBCAT_INTERFACE_GENERAL ||
139                       p_item->i_value == SUBCAT_SOUT_GENERAL||
140                       p_item->i_value == SUBCAT_PLAYLIST_GENERAL||
141                       p_item->i_value == SUBCAT_AUDIO_GENERAL ) )
142                 {
143                     // Data still contains the correct thing
144                     data->i_type = TYPE_CATSUBCAT;
145                     data->i_subcat_id = p_item->i_value;
146                     data->name = QString( config_CategoryNameGet(
147                                                 p_item->i_value ) );
148                     psz_help = config_CategoryHelpGet( p_item->i_value );
149                     if( psz_help )
150                         data->help = QString( psz_help );
151                     else
152                         data->help.clear();
153                     current_item->setData( 0, Qt::UserRole,
154                                            QVariant::fromValue( data ) );
155                     continue;
156                 }
157                 data = new PrefsItemData();
158                 data->name = QString( config_CategoryNameGet( p_item->i_value));
159                 psz_help = config_CategoryHelpGet( p_item->i_value );
160                 if( psz_help )
161                     data->help = QString( psz_help );
162                 else
163                     data->help.clear();
164                 data->i_type = TYPE_SUBCATEGORY;
165                 data->i_object_id = p_item->i_value;
166
167                 assert( current_item );
168
169                 /* TODO : Choose the image */
170                 QTreeWidgetItem *subcat_item = new QTreeWidgetItem();
171                 subcat_item->setText( 0, data->name );
172                 //item->setIcon( 0 , XXX );
173                 subcat_item->setData( 0, Qt::UserRole,
174                                       qVariantFromValue(data) );
175                 subcat_item->setSizeHint( 0, QSize( -1, ITEM_HEIGHT ) );
176                 current_item->addChild( subcat_item );
177                 break;
178             }
179         } while( p_item->i_type != CONFIG_HINT_END && p_item++ );
180     }
181
182     /* Build the tree of plugins */
183     for( int i_index = 0; i_index < p_list->i_count; i_index++ )
184     {
185         module_config_t *p_item;
186         int i_subcategory = -1, i_category = -1, i_options = 0;
187         p_module = (module_t *)p_list->p_values[i_index].p_object;
188
189         // Main module excluded
190         if( !strcmp( p_module->psz_object_name, "main" ) ) continue;
191
192         /* Exclude empty plugins (submodules don't have config options, they
193          * are stored in the parent module) */
194         if( p_module->b_submodule ) continue;
195
196         p_item = p_module->p_config;
197         if( !p_item ) continue;
198
199         do {
200             if( p_item->i_type == CONFIG_CATEGORY )
201                 i_category = p_item->i_value;
202             else if( p_item->i_type == CONFIG_SUBCATEGORY )
203                 i_subcategory = p_item->i_value;
204             if( p_item->i_type & CONFIG_ITEM )
205                 i_options++;
206
207             if( i_options > 0 && i_category >= 0 && i_subcategory >= 0 )
208                 break;
209         } while( p_item->i_type != CONFIG_HINT_END && p_item++ );
210
211         if( !i_options ) continue; // Nothing to display
212
213         // Locate the category item;
214         QTreeWidgetItem *subcat_item = NULL;
215         bool b_found = false;
216         for( int i_cat_index = 0 ; i_cat_index < topLevelItemCount();
217                                    i_cat_index++ )
218         {
219             QTreeWidgetItem *cat_item = topLevelItem( i_cat_index );
220             PrefsItemData *data = cat_item->data( 0, Qt::UserRole ).
221                                              value<PrefsItemData *>();
222             if( data->i_object_id == i_category )
223             {
224                 for( int i_sc_index = 0; i_sc_index < cat_item->childCount();
225                          i_sc_index++ )
226                 {
227                     subcat_item = cat_item->child( i_sc_index );
228                     PrefsItemData *sc_data = subcat_item->data(0, Qt::UserRole).
229                                                 value<PrefsItemData *>();
230                     if( sc_data && sc_data->i_object_id == i_subcategory )
231                     {
232                         b_found = true;
233                         break;
234                     }
235                 }
236                 if( !b_found )
237                 {
238                     subcat_item = cat_item;
239                     b_found = true;
240                 }
241                 break;
242             }
243         }
244         if( !b_found ) continue;
245
246         PrefsItemData *module_data = new PrefsItemData();
247         module_data->b_submodule = p_module->b_submodule;
248         module_data->i_type = TYPE_MODULE;
249         module_data->i_object_id = p_module->b_submodule ?
250                          ((module_t *)p_module->p_parent)->i_object_id :
251                          p_module->i_object_id;
252         module_data->help.clear();
253         // TODO image
254         QTreeWidgetItem *module_item = new QTreeWidgetItem();
255         module_item->setText( 0, p_module->psz_shortname ?
256                       p_module->psz_shortname : p_module->psz_object_name );
257         //item->setIcon( 0 , XXX );
258         module_item->setData( 0, Qt::UserRole,
259                               QVariant::fromValue( module_data) );
260         module_item->setSizeHint( 0, QSize( -1, ITEM_HEIGHT ) );
261         subcat_item->addChild( module_item );
262     }
263
264     /* We got everything, just sort a bit */
265     sortItems( 0, Qt::AscendingOrder );
266
267     vlc_list_release( p_list );
268 }
269
270 PrefsTree::~PrefsTree() {}
271
272 void PrefsTree::ApplyAll()
273 {
274     DoAll( false );
275 }
276
277 void PrefsTree::CleanAll()
278 {
279     DoAll( true );
280 }
281
282 /// \todo When cleaning, we should remove the panel ?
283 void PrefsTree::DoAll( bool doclean )
284 {
285     for( int i_cat_index = 0 ; i_cat_index < topLevelItemCount();
286              i_cat_index++ )
287     {
288         QTreeWidgetItem *cat_item = topLevelItem( i_cat_index );
289         for( int i_sc_index = 0; i_sc_index <= cat_item->childCount();
290                  i_sc_index++ )
291         {
292             QTreeWidgetItem *sc_item = cat_item->child( i_sc_index );
293             for( int i_module = 0 ; i_module <= sc_item->childCount();
294                      i_module++ )
295             {
296                 PrefsItemData *data = sc_item->child( i_sc_index )->
297                                                  data( 0, Qt::UserRole ).
298                                                  value<PrefsItemData *>();
299                 if( data->panel && doclean )
300                     data->panel->Clean();
301                 else if( data->panel )
302                     data->panel->Apply();
303             }
304         }
305     }
306 }
307
308
309 /*********************************************************************
310  * The Panel
311  *********************************************************************/
312 PrefsPanel::PrefsPanel( QWidget *_parent ) : QWidget( _parent )
313 {}
314
315 PrefsPanel::PrefsPanel( intf_thread_t *_p_intf, QWidget *_parent,
316                         PrefsItemData * data ) :
317                         QWidget( _parent ), p_intf( _p_intf )
318 {
319     module_config_t *p_item;
320     module_t *p_module;
321     vlc_list_t *p_list = NULL;
322     global_layout = new QVBoxLayout();
323
324     if( data->i_type == TYPE_CATEGORY )
325     {
326         /* TODO */
327             return;
328     }
329     else if( data->i_type == TYPE_MODULE )
330     {
331         p_module = (module_t *) vlc_object_get( p_intf, data->i_object_id );
332     }
333     else
334     {
335         /* List the plugins */
336         int i_index;
337         vlc_bool_t b_found = VLC_FALSE;
338         p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
339         if( !p_list ) return;
340
341         for( i_index = 0; i_index < p_list->i_count; i_index++ )
342         {
343             p_module = (module_t *)p_list->p_values[i_index].p_object;
344             if( !strcmp( p_module->psz_object_name, "main" ) )
345             {
346                 b_found = VLC_TRUE;
347                 break;
348             }
349         }
350         if( !p_module && !b_found )
351         {
352             msg_Warn( p_intf, "unable to create preferences (main not found)");
353             return;
354         }
355         if( p_module ) vlc_object_yield( p_module );
356         vlc_list_release( p_list );
357     }
358
359     if( p_module->b_submodule )
360         p_item = ((module_t *)p_module->p_parent)->p_config;
361     else
362         p_item = p_module->p_config;
363
364     if( data->i_type == TYPE_SUBCATEGORY || data->i_type ==  TYPE_CATSUBCAT )
365     {
366         do
367         {
368             if( p_item->i_type == CONFIG_SUBCATEGORY &&
369                             ( data->i_type == TYPE_SUBCATEGORY &&
370                               p_item->i_value == data->i_object_id ) ||
371                             ( data->i_type == TYPE_CATSUBCAT &&
372                               p_item->i_value == data->i_subcat_id ) )
373             {
374                 break;
375             }
376             if( p_item->i_type == CONFIG_HINT_END ) break;
377         } while( p_item++ );
378     }
379
380     QString head;
381     if( data->i_type == TYPE_SUBCATEGORY || data->i_type ==  TYPE_CATSUBCAT )
382     {
383         head = QString( data->name );
384         p_item++; // Why that ?
385     }
386     else
387     {
388         head = QString( qfu(p_module->psz_longname) );
389         if( p_module->psz_help ) 
390         {
391             head.append( "\n" );
392             head.append( qfu( p_module->psz_help ) );
393         }
394     }
395
396     QLabel *label = new QLabel( head, this );
397     QFont font = label->font();
398     font.setPointSize( font.pointSize() + 2 ); font.setBold( true );
399     label->setFont( font );
400     QLabel *help = new QLabel( data->help, this );
401     help->setWordWrap( true );
402
403     global_layout->addWidget( label );
404     global_layout->addWidget( help );
405
406     QGroupBox *box = NULL;
407     QVBoxLayout *boxlayout = NULL;
408
409     QScrollArea *scroller= new QScrollArea;
410     scroller->setFrameStyle( QFrame::NoFrame );
411     QWidget *scrolled_area = new QWidget;
412
413     QVBoxLayout *layout = new QVBoxLayout();
414
415     if( p_item ) do
416     {
417         if( ( ( data->i_type == TYPE_SUBCATEGORY &&
418                 p_item->i_value != data->i_object_id ) ||
419               ( data->i_type == TYPE_CATSUBCAT  &&
420                 p_item->i_value != data->i_subcat_id ) ) &&
421             ( p_item->i_type == CONFIG_CATEGORY ||
422               p_item->i_type == CONFIG_SUBCATEGORY ) )
423             break;
424         if( p_item->b_internal == VLC_TRUE ) continue;
425
426         if( p_item->i_type == CONFIG_SECTION )
427         {
428             if( box )
429             {
430                 box->setLayout( boxlayout );
431                 layout->addWidget( box, 1 );
432             }
433             box = new QGroupBox( qfu(p_item->psz_text) );
434             boxlayout = new QVBoxLayout();
435         }
436
437         ConfigControl *control = ConfigControl::createControl(
438                                     VLC_OBJECT( p_intf ), p_item,
439                                     NULL );
440         if( !control )
441         {
442             continue;
443         }
444         if( !box )
445             layout->addWidget( control );
446         else
447             boxlayout->addWidget( control );
448
449         controls.append( control );
450     }
451     while( !(p_item->i_type == CONFIG_HINT_END ||
452            ( ( data->i_type == TYPE_SUBCATEGORY ||
453                data->i_type == TYPE_CATSUBCAT ) &&
454              ( p_item->i_type == CONFIG_CATEGORY ||
455                p_item->i_type == CONFIG_SUBCATEGORY ) ) ) && p_item++ );
456
457     if( box )
458     {
459         box->setLayout( boxlayout );
460         layout->addWidget( box, 1 );
461     }
462
463     vlc_object_release( p_module );
464
465     scrolled_area->setSizePolicy( QSizePolicy::Preferred,QSizePolicy::Fixed );
466     scrolled_area->setLayout( layout );
467     scroller->setWidget( scrolled_area );
468     scroller->setWidgetResizable( true );
469     global_layout->addWidget( scroller );
470
471     some_hidden_text = new QLabel( "Some options are available but hidden. "\
472                                   "Check \"Advanced options\" to see them." );
473     some_hidden_text->setWordWrap( true );
474
475     setLayout( global_layout );
476     setAdvanced( false );
477 }
478
479 void PrefsPanel::Apply()
480 {
481     /* todo */
482     QList<ConfigControl *>::Iterator i;
483     for( i = controls.begin() ; i != controls.end() ; i++ )
484     {
485         VIntConfigControl *vicc = qobject_cast<VIntConfigControl *>(*i);
486         if( !vicc )
487         {
488             VFloatConfigControl *vfcc = qobject_cast<VFloatConfigControl *>(*i);
489             if( !vfcc)
490             {
491                 VStringConfigControl *vscc =
492                                qobject_cast<VStringConfigControl *>(*i);
493                 assert( vscc );
494                 config_PutPsz( p_intf, vscc->getName().toAscii().data(),
495                                        vscc->getValue().toAscii().data() );
496                 continue;
497             }
498             config_PutFloat( p_intf, vfcc->getName().toAscii().data(),
499                                      vfcc->getValue() );
500             continue;
501         }
502         config_PutInt( p_intf, vicc->getName().toAscii().data(),
503                                vicc->getValue() );
504     }
505 }
506
507 void PrefsPanel::Clean()
508 {}
509
510 void PrefsPanel::setAdvanced( bool adv )
511 {
512     bool some_hidden = false;
513     if( adv == advanced ) return;
514
515     advanced = adv;
516     QList<ConfigControl *>::Iterator i;
517     for( i = controls.begin() ; i != controls.end() ; i++ )
518     {
519         if( (*i)->isAdvanced() )
520         {
521             if( !advanced ) some_hidden = true;
522             (*i)->setVisible( advanced );
523         }
524     }
525     if( some_hidden_text )
526     {
527         global_layout->removeWidget( some_hidden_text );
528         some_hidden_text->hide();
529     }
530     if( some_hidden )
531     {
532         global_layout->addWidget( some_hidden_text );
533         some_hidden_text->show();
534     }
535 }