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