eee860fd520bd8ea96136d70957bcbfbda4be4e0
[vlc.git] / modules / gui / beos / PreferencesWindow.cpp
1 /*****************************************************************************
2  * PreferencesWindow.cpp: beos interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001 VideoLAN
5  * $Id: PreferencesWindow.cpp,v 1.23 2003/05/20 11:44:18 titer Exp $
6  *
7  * Authors: Eric Petit <titer@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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #include <stdlib.h> /* atoi(), strtod() */
25
26 #include <vlc/vlc.h>
27 #include <vlc/intf.h>
28
29 #include "PreferencesWindow.h"
30
31 /* TODO:
32     - handle CONFIG_HINT_SUBCATEGORY
33     - use BSliders for integer_with_range and float_with_range
34     - add the needed LockLooper()s
35     - fix horizontal window resizing */
36
37 /* We use this function to order the items of the BOutlineView */
38 int compare_func( const BListItem * _first, const BListItem * _second )
39 {
40     StringItemWithView * first = (StringItemWithView*) _first;
41     StringItemWithView * second = (StringItemWithView*) _second;
42
43     /* The Modules tree at last */
44     if( !strcmp( first->Text(), _( "Modules" ) ) )
45         return 1;
46     if( !strcmp( second->Text(), _( "Modules" ) ) )
47         return -1;
48
49     /* alphabetic order */
50     return( strcmp( first->Text(), second->Text() ) );
51 }
52
53 /*****************************************************************************
54  * PreferencesWindow::PreferencesWindow
55  *****************************************************************************/
56 PreferencesWindow::PreferencesWindow( intf_thread_t * p_interface,
57                                       BRect frame, const char * name )
58     : BWindow( frame, name, B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
59                B_NOT_ZOOMABLE | B_NOT_H_RESIZABLE ),
60       fConfigScroll( NULL ),
61       p_intf( p_interface )
62 {
63     SetSizeLimits( PREFS_WINDOW_WIDTH, PREFS_WINDOW_WIDTH,
64                    200, 2000 );
65
66     BRect rect;
67
68     /* The "background" view */
69     fPrefsView = new BView( Bounds(), NULL, B_FOLLOW_ALL, B_WILL_DRAW );
70     fPrefsView->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
71     AddChild( fPrefsView );
72
73     /* Create the preferences tree */
74     rect = Bounds();
75     rect.InsetBy( 10, 10 );
76     rect.right = rect.left + 150;
77     fOutline = new BOutlineListView( rect, "preferences tree",
78                                      B_SINGLE_SELECTION_LIST,
79                                      B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM );
80     BScrollView * scrollview = new BScrollView( "scrollview", fOutline,
81                                                 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM,
82                                                 0, false, true );
83     fPrefsView->AddChild( scrollview );
84
85     /* We need to be informed if the user selects an item */
86     fOutline->SetSelectionMessage( new BMessage( PREFS_ITEM_SELECTED ) );
87
88     /* Create a dummy view so we can correctly place the real config views later */
89     rect.bottom -= 40;
90     rect.left = rect.right + 15 + B_V_SCROLL_BAR_WIDTH;
91     rect.right = Bounds().right - 15;
92     fDummyView = new BView( rect, "", B_FOLLOW_ALL_SIDES, B_WILL_DRAW );
93     fDummyView->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
94     fPrefsView->AddChild( fDummyView );
95
96     /* Add a category for modules configuration */
97     StringItemWithView * modulesItem;
98     modulesItem = new StringItemWithView( _("Modules") );
99     fOutline->AddItem( modulesItem );
100
101     /* Fill the tree */
102     vlc_list_t * p_list;
103     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
104     if( !p_list )
105     {
106         msg_Warn( p_intf, "couldn't find any module !" );
107         return;
108     }
109
110
111     /* First, handle the main module */
112     module_t * p_module = NULL;
113     module_config_t * p_item;
114     for( int i = 0; i < p_list->i_count; i++ )
115     {
116         p_module = (module_t*) p_list->p_values[i].p_object;
117
118         if( !strcmp( p_module->psz_object_name, "main" ) &&
119             ( p_item = p_module->p_config ) )
120             break;
121         else
122             p_module = NULL;
123     }
124
125     if( p_module )
126     {
127         /* We found the main module */
128         while( p_item->i_type == CONFIG_HINT_CATEGORY )
129         {
130             StringItemWithView * stringItem;
131             stringItem = new StringItemWithView( p_item->psz_text );
132             p_item++;
133             stringItem->fConfigView = BuildConfigView( &p_item, true );
134             fOutline->AddItem( stringItem );
135         }
136     }
137
138     for( int i = 0; i < p_list->i_count; i++ )
139     {
140         p_module = (module_t*) p_list->p_values[i].p_object;
141
142         if( !strcmp( p_module->psz_object_name, "main" ) )
143             continue;
144
145         /* If the module has no config option, ignore it */
146         p_item = p_module->p_config;
147         if( !p_item )
148             continue;
149         do {
150             if( p_item->i_type & CONFIG_ITEM )
151                 break;
152         } while( p_item->i_type != CONFIG_HINT_END && p_item++ );
153         if( p_item->i_type == CONFIG_HINT_END )
154             continue;
155
156         /* Create the capability tree if it doesn't already exist */
157         char * psz_capability;
158         psz_capability = p_module->psz_capability;
159         if( !psz_capability || !*psz_capability )
160         {
161             /* Empty capability ? Let's look at the submodules */
162             module_t * p_submodule;
163             for( int j = 0; j < p_module->i_children; j++ )
164             {
165                 p_submodule = (module_t*)p_module->pp_children[ j ];
166                 if( p_submodule->psz_capability && *p_submodule->psz_capability )
167                 {
168                     psz_capability = p_submodule->psz_capability;
169                     break;
170                 }
171             }
172         }
173
174         StringItemWithView * capabilityItem;
175         capabilityItem = NULL;
176         for( int j = 0; j < fOutline->CountItemsUnder( modulesItem, true ); j++ )
177         {
178             if( !strcmp( ((StringItemWithView*)
179                              fOutline->ItemUnderAt( modulesItem, true, j ))->Text(),
180                          psz_capability ) )
181             {
182                 capabilityItem = (StringItemWithView*)
183                     fOutline->ItemUnderAt( modulesItem, true, j );
184                 break;
185             }
186         }
187         if( !capabilityItem )
188         {
189              capabilityItem = new StringItemWithView( psz_capability );
190              fOutline->AddUnder( capabilityItem, modulesItem );
191         }
192
193         /* Now add the item ! */
194         StringItemWithView * stringItem;
195         stringItem = new StringItemWithView( p_module->psz_object_name );
196         stringItem->fConfigView = BuildConfigView( &p_item, false );
197         fOutline->AddUnder( stringItem, capabilityItem );
198     }
199
200     vlc_list_release( p_list );
201
202     /* Set the correct values */
203     ApplyChanges( false );
204
205     /* Sort items, collapse the tree */
206     fOutline->FullListSortItems( compare_func );
207     fOutline->Collapse( modulesItem );
208     for( int i = 0; i < fOutline->CountItemsUnder( modulesItem, true ); i++ )
209         fOutline->Collapse( fOutline->ItemUnderAt( modulesItem, true, i ) );
210
211     /* Select the first item */
212     fOutline->Select( 0 );
213
214     /* Add the buttons */
215     BButton * button;
216     rect = Bounds();
217     rect.InsetBy( 10, 10 );
218     rect.left = rect.right - 80;
219     rect.top = rect.bottom - 25;
220     button = new BButton( rect, "", _("Apply"), new BMessage( PREFS_APPLY ),
221                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
222     button->MakeDefault( true );
223     fPrefsView->AddChild( button );
224     rect.OffsetBy( -90, 0 );
225     button = new BButton( rect, "", _("Save"), new BMessage( PREFS_SAVE ),
226                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
227     fPrefsView->AddChild( button );
228     rect.OffsetBy( -90, 0 );
229     button = new BButton( rect, "", _("Defaults"), new BMessage( PREFS_DEFAULTS ),
230                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
231     fPrefsView->AddChild( button );
232
233     Hide();
234     Show();
235 }
236
237 /*****************************************************************************
238  * PreferencesWindow::~PreferencesWindow
239  *****************************************************************************/
240 PreferencesWindow::~PreferencesWindow()
241 {
242 }
243
244 /*****************************************************************************
245  * PreferencesWindow::QuitRequested
246  *****************************************************************************/
247 bool PreferencesWindow::QuitRequested()
248 {
249     if( !IsHidden() )
250         Hide();
251         return false;
252 }
253
254 /*****************************************************************************
255  * PreferencesWindow::MessageReceived
256  *****************************************************************************/
257 void PreferencesWindow::MessageReceived( BMessage * message )
258 {
259     switch( message->what )
260     {
261         case PREFS_ITEM_SELECTED:
262             Update();
263             break;
264
265         case PREFS_DEFAULTS:
266             config_ResetAll( p_intf );
267             ApplyChanges( false );
268             break;
269
270         case PREFS_APPLY:
271             ApplyChanges( true );
272             break;
273
274         case PREFS_SAVE:
275             SaveChanges();
276             break;
277
278         default:
279             BWindow::MessageReceived( message );
280     }
281 }
282
283 /*****************************************************************************
284  * PreferencesWindow::FrameResized
285  *****************************************************************************/
286 void PreferencesWindow::FrameResized( float width, float height )
287 {
288     BWindow::FrameResized( width, height );
289
290     /* Get the current config BView */
291     BView * view;
292     view = fConfigScroll->ChildAt( 0 );
293
294     UpdateScrollBar();
295 }
296
297 /*****************************************************************************
298  * PreferencesWindow::Update
299  *****************************************************************************/
300 void PreferencesWindow::Update()
301 {
302     /* Get the selected item, if any */
303     if( fOutline->CurrentSelection() < 0 )
304         return;
305     StringItemWithView * selectedItem =
306         (StringItemWithView*) fOutline->ItemAt( fOutline->CurrentSelection() );
307
308     if( !selectedItem->fConfigView )
309         /* This is a category */
310         return;
311
312     if( fConfigScroll )
313     {
314         /* If we don't do this, the config BView will remember a wrong position */
315         BScrollBar * scrollBar = fConfigScroll->ScrollBar( B_VERTICAL );
316         scrollBar->SetValue( 0 );
317
318         /* Detach the current config BView, remove the BScrollView */
319         BView * view;
320         while( ( view = fConfigScroll->ChildAt( 0 ) ) )
321             fConfigScroll->RemoveChild( view );
322         fDummyView->RemoveChild( fConfigScroll );
323         delete fConfigScroll;
324     }
325
326     /* Create a BScrollView with the new config BView in it */
327     BRect oldBounds = selectedItem->fConfigView->Bounds();
328     selectedItem->fConfigView->ResizeTo( fDummyView->Bounds().Width() -
329                                              B_V_SCROLL_BAR_WIDTH,
330                                          fDummyView->Bounds().Height() );
331     fConfigScroll = new BScrollView( "", selectedItem->fConfigView, B_FOLLOW_ALL_SIDES,
332                                      0, false, true, B_NO_BORDER );
333     fConfigScroll->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
334     fDummyView->AddChild( fConfigScroll );
335     selectedItem->fConfigView->ResizeTo( oldBounds.Width(),
336                                          oldBounds.Height() );
337     UpdateScrollBar();
338 }
339
340
341 /*****************************************************************************
342  * PreferencesWindow::UpdateScrollBar
343  *****************************************************************************/
344 void PreferencesWindow::UpdateScrollBar()
345 {
346     /* We have to fix the scrollbar manually because it doesn't handle
347        correctly simple BViews */
348
349     /* Get the current config view */
350     BView * view;
351     view = fConfigScroll->ChildAt( 0 );
352
353     /* Get the available BRect for display */
354     BRect display = fConfigScroll->Bounds();
355     display.right -= B_V_SCROLL_BAR_WIDTH;
356
357     /* Fix the scrollbar */
358     BScrollBar * scrollBar;
359     long max;
360         BRect visible = display & view->Bounds();
361         BRect total = display | view->Bounds();
362     scrollBar = fConfigScroll->ScrollBar( B_VERTICAL );
363     max = (long)( view->Bounds().Height() - visible.Height() );
364     if( max < 0 ) max = 0;
365     scrollBar->SetRange( 0, max );
366     scrollBar->SetProportion( visible.Height() / total.Height() );
367     scrollBar->SetSteps( 10, 100 );
368 }
369
370 /*****************************************************************************
371  * PreferencesWindow::ApplyChanges
372  * Apply changes if doIt is true, revert them otherwise
373  *****************************************************************************/
374 void PreferencesWindow::ApplyChanges( bool doIt )
375 {
376     StringItemWithView * item;
377     BView * view;
378     BView * child;
379     const char * name;
380     BString string;
381     for( int i = 0; i < fOutline->CountItems(); i++ )
382     {
383         item = (StringItemWithView*) fOutline->ItemAt( i );
384         view = item->fConfigView;
385
386         if( !view )
387             /* This is a category */
388             continue;
389
390         for( int j = 0; j < view->CountChildren(); j++ )
391         {
392             child = view->ChildAt( j );
393             name = child->Name();
394             if( !strcmp( name, "ConfigTextControl" ) )
395             {
396                 ConfigTextControl * textControl;
397                 textControl = (ConfigTextControl*) child;
398                 switch( textControl->fConfigType )
399                 {
400                     case CONFIG_ITEM_STRING:
401                         if( doIt )
402                             config_PutPsz( p_intf, textControl->fConfigName, textControl->Text() );
403                         else
404                             textControl->SetText( config_GetPsz( p_intf, textControl->fConfigName ) );
405                         break;
406                     case CONFIG_ITEM_INTEGER:
407                         if( doIt )
408                             config_PutInt( p_intf, textControl->fConfigName, atoi( textControl->Text() ) );
409                         else
410                         {
411                             string = "";
412                             string << config_GetInt( p_intf, textControl->fConfigName );
413                             textControl->SetText( string.String() );
414                         }
415                         break;
416                     case CONFIG_ITEM_FLOAT:
417                         if( doIt )
418                             config_PutFloat( p_intf, textControl->fConfigName,
419                                              strtod( textControl->Text(), NULL ) );
420                         else
421                         {
422                             string = "";
423                             string << config_GetFloat( p_intf, textControl->fConfigName );
424                             textControl->SetText( string.String() );
425                         }
426                         break;
427                 }
428             }
429             else if( !strcmp( name, "ConfigCheckBox" ) )
430             {
431                 ConfigCheckBox * checkBox;
432                 checkBox = (ConfigCheckBox*) child;
433                 if( doIt )
434                     config_PutInt( p_intf, checkBox->fConfigName, checkBox->Value() );
435                 else
436                     checkBox->SetValue( config_GetInt( p_intf, checkBox->fConfigName ) );
437             }
438             else if( !strcmp( name, "ConfigMenuField" ) )
439             {
440                 ConfigMenuField * menuField;
441                 menuField = (ConfigMenuField*) child;
442                 BMenu * menu;
443                 BMenuItem * menuItem;
444                 menu = menuField->Menu();
445                 if( doIt )
446                 {
447                     menuItem = menu->FindMarked();
448                     if( menuItem )
449                         config_PutPsz( p_intf, menuField->fConfigName, menuItem->Label() );
450                 }
451                 else
452                 {
453                     char * value;
454                     value = config_GetPsz( p_intf, menuField->fConfigName );
455                     if( !value ) value = "";
456                     for( int k = 0; k < menu->CountItems(); k++ )
457                     {
458                         menuItem = menu->ItemAt( k );
459                         if( !strcmp( value, menuItem->Label() ) )
460                         {
461                             menuItem->SetMarked( true );
462                             break;
463                         }
464                     }
465                 }
466             }
467         }
468     }
469 }
470
471 /*****************************************************************************
472  * PreferencesWindow::SaveChanges
473  *****************************************************************************/
474 void PreferencesWindow::SaveChanges()
475 {
476     ApplyChanges( true );
477     config_SaveConfigFile( p_intf, NULL );
478 }
479
480 /*****************************************************************************
481  * PreferencesWindow::ReallyQuit
482  *****************************************************************************/
483 void PreferencesWindow::ReallyQuit()
484 {
485     Lock();
486     Hide();
487     Quit();
488 }
489
490 /*****************************************************************************
491  * PreferencesWindow::BuildConfigView
492  *****************************************************************************/
493 BView * PreferencesWindow::BuildConfigView( module_config_t ** pp_item,
494                                             bool stop_after_category )
495 {
496     /* Build the config view for this module */
497     BRect rect = fDummyView->Bounds();
498     rect.right -= B_V_SCROLL_BAR_WIDTH;
499     BView * configView;
500     configView = new BView( rect, "config view",
501                             B_FOLLOW_NONE, B_WILL_DRAW );
502     configView->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
503
504     rect = configView->Bounds();
505     rect.InsetBy( 10, 10 );
506     ConfigTextControl * textControl;
507     ConfigCheckBox * checkBox;
508     ConfigMenuField * menuField;
509     BPopUpMenu * popUp;
510
511     bool categoryHit = false;
512     do
513     {
514         switch( (*pp_item)->i_type )
515         {
516             case CONFIG_ITEM_STRING:
517             case CONFIG_ITEM_FILE:
518             case CONFIG_ITEM_MODULE:
519             case CONFIG_ITEM_DIRECTORY:
520                 if( (*pp_item)->ppsz_list && (*pp_item)->ppsz_list[0] )
521                 {
522                     rect.bottom = rect.top + 20;
523                     popUp = new BPopUpMenu( "" );
524                     menuField = new ConfigMenuField( rect, (*pp_item)->psz_text,
525                                                      popUp, (*pp_item)->psz_name );
526                     BMenuItem * menuItem;
527                     for( int i = 0; (*pp_item)->ppsz_list[i]; i++ )
528                     {
529                         menuItem = new BMenuItem( (*pp_item)->ppsz_list[i], new BMessage() );
530                         popUp->AddItem( menuItem );
531                     }
532                     configView->AddChild( menuField );
533                     rect.top = rect.bottom + 10;
534                 }
535                 else
536                 {
537                     rect.bottom = rect.top + 20;
538                     textControl = new ConfigTextControl( rect, (*pp_item)->psz_text,
539                                                          CONFIG_ITEM_STRING, (*pp_item)->psz_name );
540                     configView->AddChild( textControl );
541                     rect.top = rect.bottom + 10;
542                 }
543                 break;
544
545             case CONFIG_ITEM_INTEGER:
546                 rect.bottom = rect.top + 20;
547                 textControl = new ConfigTextControl( rect, (*pp_item)->psz_text,
548                                                      CONFIG_ITEM_INTEGER, (*pp_item)->psz_name );
549                 configView->AddChild( textControl );
550                 rect.top = rect.bottom + 10;
551                 break;
552
553             case CONFIG_ITEM_FLOAT:
554                 rect.bottom = rect.top + 20;
555                 textControl = new ConfigTextControl( rect, (*pp_item)->psz_text,
556                                                      CONFIG_ITEM_FLOAT, (*pp_item)->psz_name );
557                 configView->AddChild( textControl );
558                 rect.top = rect.bottom + 10;
559                 break;
560
561             case CONFIG_ITEM_BOOL:
562                 if( !strcmp( (*pp_item)->psz_name, "advanced" ) )
563                     /* Don't show this one, the interface doesn't handle it anyway */
564                     break;
565
566                 rect.bottom = rect.top + 20;
567                 checkBox = new ConfigCheckBox( rect, (*pp_item)->psz_text,
568                                                (*pp_item)->psz_name );
569                 configView->AddChild( checkBox );
570                 rect.top = rect.bottom + 10;
571                 break;
572
573             case CONFIG_HINT_CATEGORY:
574                 if( stop_after_category )
575                     categoryHit = true;
576         }
577
578     } while( !categoryHit &&
579              (*pp_item)->i_type != CONFIG_HINT_END &&
580              (*pp_item)++ );
581
582     /* Adjust the configView size */
583     configView->ResizeTo( configView->Bounds().Width(), rect.top );
584
585     return configView;
586 }
587