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