9b97a39f79f1b7b66a4f277060610be3b511f5e5
[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.20 2003/05/13 11:18:25 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 <InterfaceKit.h>
27 #include <SupportKit.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc/intf.h>
31
32 #include "PreferencesWindow.h"
33
34 /* We use this function to order the items of the BOutlineView */
35 int compare_func( const BListItem * _first, const BListItem * _second )
36 {
37     StringItemWithView * first = (StringItemWithView*) _first;
38     StringItemWithView * second = (StringItemWithView*) _second;
39     
40     /* the beos module first */
41     if( !strcmp( first->Text(), "beos" ) )
42         return -1;
43     if( !strcmp( second->Text(), "beos" ) )
44         return 1;
45
46     /* the main module in second */
47     if( !strcmp( first->Text(), "main" ) )
48         return -1;
49     if( !strcmp( second->Text(), "main" ) )
50         return 1;
51
52     /* alphabetic order */
53     return( strcmp( first->Text(), second->Text() ) );
54 }
55
56 /*****************************************************************************
57  * StringItemWithView::StringItemWithView
58  *****************************************************************************/
59 StringItemWithView::StringItemWithView( const char * text )
60     : BStringItem( text )
61 {
62     /* We use the default constructor */
63 }
64
65 /*****************************************************************************
66  * ConfigView::ConfigView
67  *****************************************************************************/
68 ConfigView::ConfigView( BRect frame, const char * name,
69                         uint32 resizingMode, uint32 flags )
70     : BView( frame, name, resizingMode, flags )
71 {
72     /* We use the default constructor */
73 }
74
75 /*****************************************************************************
76  * PreferencesWindow::PreferencesWindow
77  *****************************************************************************/
78 PreferencesWindow::PreferencesWindow( intf_thread_t * p_interface,
79                                       BRect frame, const char * name )
80     : BWindow( frame, name, B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
81                B_NOT_ZOOMABLE | B_NOT_H_RESIZABLE ),
82       fConfigScroll( NULL ),
83       p_intf( p_interface )
84 {
85     SetSizeLimits( PREFS_WINDOW_WIDTH, PREFS_WINDOW_WIDTH,
86                    200, 2000 );
87
88     BRect rect;
89
90     /* The "background" view */
91     rgb_color background = ui_color( B_PANEL_BACKGROUND_COLOR );
92     fPrefsView = new BView( Bounds(), NULL, B_FOLLOW_ALL, B_WILL_DRAW );
93     fPrefsView->SetViewColor( background );
94     AddChild( fPrefsView );
95     
96     /* Create the preferences tree */
97     rect = Bounds();
98     rect.InsetBy( 10, 10 );
99     rect.right = rect.left + 150;
100     fOutline = new BOutlineListView( rect, "preferences tree",
101                                      B_SINGLE_SELECTION_LIST,
102                                      B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM );
103     BScrollView * scrollview = new BScrollView( "scrollview", fOutline,
104                                                 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM,
105                                                 0, false, true );
106     fPrefsView->AddChild( scrollview );
107     
108     /* We need to be informed if the user selects an item */
109     fOutline->SetSelectionMessage( new BMessage( PREFS_ITEM_SELECTED ) );
110
111     /* Create a dummy view so we can correctly place the real config views later */
112     rect.bottom -= 40;
113     rect.left = rect.right + 15 + B_V_SCROLL_BAR_WIDTH;
114     rect.right = Bounds().right - 15;
115     fDummyView = new BView( rect, "", B_FOLLOW_ALL_SIDES, B_WILL_DRAW );
116     fPrefsView->AddChild( fDummyView );
117
118     /* Add a category for modules configuration */
119     StringItemWithView * modulesItem;
120     modulesItem = new StringItemWithView( _("Modules") );
121     modulesItem->fConfigView = NULL;
122     fOutline->AddItem( modulesItem );
123    
124     /* Fill the tree */
125     /* TODO:
126         - manage CONFIG_HINT_SUBCATEGORY
127         - use a pop-up for CONFIG_HINT_MODULE
128         - use BSliders for integer_with_range and float_with_range
129         - add the needed LockLooper()s
130         - fix horizontal window resizing
131         - make this intuitive ! */
132     vlc_list_t * p_list;
133     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
134     if( !p_list )
135     {
136         msg_Warn( p_intf, "couldn't find any module !" );
137         return;
138     }
139
140     module_t * p_module;
141     for( int i = 0; i < p_list->i_count; i++ )
142     {
143         p_module = (module_t*) p_list->p_values[i].p_object;
144         
145         /* If the module has no config option, ignore it */
146         module_config_t * p_item;
147         p_item = p_module->p_config;
148         if( !p_item )
149             continue;
150         do
151         {
152             if( p_item->i_type & CONFIG_ITEM )
153                 break;
154         } while( p_item->i_type != CONFIG_HINT_END && p_item++ );
155         if( p_item->i_type == CONFIG_HINT_END )
156             continue;
157         
158         /* Build the config view for this module */
159         rect = fDummyView->Bounds();
160         rect.right -= B_V_SCROLL_BAR_WIDTH;
161         ConfigView * configView;
162         configView = new ConfigView( rect, "config view",
163                                      B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW );
164         configView->SetViewColor( background );
165         
166         rect = configView->Bounds();
167         rect.InsetBy( 10, 10 );
168         rect.bottom = rect.top + TEXT_HEIGHT;
169         BTextControl * textControl;
170         BCheckBox * checkBox;
171
172         /* FIXME: we use the BControl name to store the VLC variable name.
173            To know what variable type it is, I add one character at the beginning
174            of the name (see ApplyChanges()); it's not pretty, but it works. To
175            be cleaned later. */    
176         char name[128];
177         p_item = p_module->p_config;
178         bool firstItem = true;
179         do
180         {
181             switch( p_item->i_type )
182             {
183                 case CONFIG_ITEM_STRING:
184                 case CONFIG_ITEM_FILE:
185                 case CONFIG_ITEM_MODULE:
186                 case CONFIG_ITEM_DIRECTORY:
187                     if( !firstItem )
188                         rect.OffsetBy( 0, 25 );
189                     else
190                         firstItem = false;
191                     
192                     memset( name, 0, 128 );
193                     sprintf( name, "s%s", p_item->psz_name );
194                     textControl = new BTextControl( rect, name, p_item->psz_text,
195                                                     "", new BMessage(),
196                                                     B_FOLLOW_NONE );
197                     configView->AddChild( textControl );
198                     break;
199
200                 case CONFIG_ITEM_INTEGER:
201                     if( !firstItem )
202                         rect.OffsetBy( 0, 25 );
203                     else
204                         firstItem = false;
205                         
206                     memset( name, 0, 128 );
207                     sprintf( name, "i%s", p_item->psz_name );
208                     textControl = new BTextControl( rect, name, p_item->psz_text,
209                                                     "", new BMessage(),
210                                                     B_FOLLOW_NONE );
211                     configView->AddChild( textControl );
212                     break;
213
214                 case CONFIG_ITEM_FLOAT:
215                     if( !firstItem )
216                         rect.OffsetBy( 0, 25 );
217                     else
218                         firstItem = false;
219                         
220                     memset( name, 0, 128 );
221                     sprintf( name, "f%s", p_item->psz_name );
222                     textControl = new BTextControl( rect, name, p_item->psz_text,
223                                                     "", new BMessage(),
224                                                     B_FOLLOW_NONE );
225                     configView->AddChild( textControl );
226                     break;
227             
228                 case CONFIG_ITEM_BOOL:
229                     if( !firstItem )
230                         rect.OffsetBy( 0,25 );
231                     else
232                        firstItem = false;
233                        
234                     memset( name, 0, 128 );
235                     sprintf( name, "b%s", p_item->psz_name );
236                     checkBox = new BCheckBox( rect, name, p_item->psz_text,
237                                               new BMessage(), B_FOLLOW_NONE );
238                     configView->AddChild( checkBox );
239                     break;
240             }
241
242         } while( p_item->i_type != CONFIG_HINT_END && p_item++ );
243     
244         /* Adjust the configView size */
245         rect.bottom += 10;
246         configView->fRealBounds = BRect( 0, 0, configView->Bounds().Width(), rect.bottom );
247         configView->ResizeTo( configView->Bounds().Width(), configView->Bounds().Height() );
248
249         /* Add the item to the tree */
250         StringItemWithView * stringItem;
251         stringItem = new StringItemWithView( p_module->psz_object_name );
252         stringItem->fConfigView = configView;
253         if( !strcmp( p_module->psz_object_name, "beos" )
254             || !strcmp( p_module->psz_object_name, "main" ) )
255             fOutline->AddItem( stringItem );
256         else
257             fOutline->AddUnder( stringItem, modulesItem );
258     }
259     
260     vlc_list_release( p_list );
261     
262     /* Set the correct values */
263     ApplyChanges( false );
264     
265     /* Sort items, collapse the Modules one */
266     fOutline->FullListSortItems( compare_func );
267     fOutline->Collapse( modulesItem );
268     
269     /* Select the first item */
270     fOutline->Select( 0 );
271     
272     /* Add the buttons */
273     BButton * button;
274     rect = Bounds();
275     rect.InsetBy( 10, 10 );
276     rect.left = rect.right - 80;
277     rect.top = rect.bottom - 25;
278     button = new BButton( rect, "", _("Apply"), new BMessage( PREFS_APPLY ),
279                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
280     button->MakeDefault( true );
281     fPrefsView->AddChild( button );
282     rect.OffsetBy( -90, 0 );
283     button = new BButton( rect, "", _("Save"), new BMessage( PREFS_SAVE ),
284                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
285     fPrefsView->AddChild( button );
286     rect.OffsetBy( -90, 0 );
287     button = new BButton( rect, "", _("Defaults"), new BMessage( PREFS_DEFAULTS ),
288                           B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM );
289     fPrefsView->AddChild( button );
290     
291     Hide();
292     Show();
293 }
294
295 /*****************************************************************************
296  * PreferencesWindow::~PreferencesWindow
297  *****************************************************************************/
298 PreferencesWindow::~PreferencesWindow()
299 {
300 }
301
302 /*****************************************************************************
303  * PreferencesWindow::QuitRequested
304  *****************************************************************************/
305 bool PreferencesWindow::QuitRequested()
306 {
307     if( !IsHidden() )
308         Hide();
309         return false;
310 }
311
312 /*****************************************************************************
313  * PreferencesWindow::MessageReceived
314  *****************************************************************************/
315 void PreferencesWindow::MessageReceived( BMessage * message )
316 {
317     switch( message->what )
318     {
319         case PREFS_ITEM_SELECTED:
320             Update();
321             break;
322         
323         case PREFS_DEFAULTS:
324             config_ResetAll( p_intf );
325             ApplyChanges( false );
326             break;
327         
328         case PREFS_APPLY:
329             ApplyChanges( true );
330             break;
331         
332         case PREFS_SAVE:
333             SaveChanges();
334             break;
335
336         default:
337             BWindow::MessageReceived( message );
338     }
339 }
340
341 /*****************************************************************************
342  * PreferencesWindow::FrameResized
343  *****************************************************************************/
344 void PreferencesWindow::FrameResized( float width, float height )
345 {
346     BWindow::FrameResized( width, height );
347     
348     /* Get the current ConfigView */
349     ConfigView * view;
350     view = (ConfigView*) fConfigScroll->ChildAt( 0 );
351
352     view->ResizeTo( fDummyView->Bounds().Width() - B_V_SCROLL_BAR_WIDTH,
353                     fDummyView->Bounds().Height() );
354
355     UpdateScrollBar();
356 }
357
358 /*****************************************************************************
359  * PreferencesWindow::Update
360  *****************************************************************************/
361 void PreferencesWindow::Update()
362 {
363     /* Get the selected item, if any */
364     if( fOutline->CurrentSelection() < 0 )
365         return;
366     StringItemWithView * selectedItem =
367         (StringItemWithView*) fOutline->ItemAt( fOutline->CurrentSelection() );
368     
369     if( !selectedItem->fConfigView )
370         /* This must be the "Modules" item */
371         return;
372
373     if( fConfigScroll )
374     {
375         /* If we don't do this, the ConfigView will remember a wrong position */
376         BScrollBar * scrollBar = fConfigScroll->ScrollBar( B_VERTICAL );
377         scrollBar->SetValue( 0 );
378
379         /* Detach the current ConfigView, remove the BScrollView */
380         BView * view;
381         while( ( view = fConfigScroll->ChildAt( 0 ) ) )
382             fConfigScroll->RemoveChild( view );
383         fDummyView->RemoveChild( fConfigScroll );
384         delete fConfigScroll;
385     }
386     
387     selectedItem->fConfigView->ResizeTo( fDummyView->Bounds().Width() -
388                                              B_V_SCROLL_BAR_WIDTH,
389                                          fDummyView->Bounds().Height() );
390     
391     /* Create a BScrollView with the new ConfigView in it */
392     fConfigScroll = new BScrollView( "", selectedItem->fConfigView, B_FOLLOW_ALL_SIDES,
393                                      0, false, true, B_NO_BORDER );
394     fDummyView->AddChild( fConfigScroll );
395     UpdateScrollBar();
396 }
397
398
399 /*****************************************************************************
400  * PreferencesWindow::UpdateScrollBar
401  *****************************************************************************/
402 void PreferencesWindow::UpdateScrollBar()
403 {
404     /* We have to fix the scrollbar manually because it doesn't handle
405        correctly simple BViews */
406        
407     /* Get the current config view */
408     ConfigView * view;
409     view = (ConfigView*) fConfigScroll->ChildAt( 0 );
410     
411     /* Get the available BRect for display */
412     BRect display = fConfigScroll->Bounds();
413     display.right -= B_V_SCROLL_BAR_WIDTH;
414     
415     /* Fix the scrollbar */
416     BScrollBar * scrollBar;
417     long max;
418         BRect visible = display & view->fRealBounds;
419         BRect total = display | view->fRealBounds;
420     scrollBar = fConfigScroll->ScrollBar( B_VERTICAL );
421     max = (long)( view->fRealBounds.Height() - visible.Height() );
422     if( max < 0 ) max = 0;
423     scrollBar->SetRange( 0, max );
424     scrollBar->SetProportion( visible.Height() / total.Height() );
425     scrollBar->SetSteps( 10, 100 );
426 }
427
428 /*****************************************************************************
429  * PreferencesWindow::ApplyChanges
430  * Apply changes if doIt is true, revert them otherwise
431  *****************************************************************************/
432 void PreferencesWindow::ApplyChanges( bool doIt )
433 {
434     StringItemWithView * item;
435     ConfigView * view;
436     BView * child;
437     const char * name;
438     BString string;
439     for( int i = 0; i < fOutline->CountItems(); i++ )
440     {
441         item = (StringItemWithView*) fOutline->ItemAt( i );
442         view = item->fConfigView;
443         
444         if( !view )
445             /* This must be the "Modules" item */
446             continue;
447         
448         for( int j = 0; j < view->CountChildren(); j++ )
449         {
450             child = view->ChildAt( j );
451             name = child->Name();
452             switch( *name )
453             {
454                 case 's': /* BTextControl, string variable */
455                     if( doIt )
456                         config_PutPsz( p_intf, name + 1, ((BTextControl*)child)->Text() );
457                     else
458                         ((BTextControl*)child)->SetText( config_GetPsz( p_intf, name + 1 ) );
459                     break;
460                 case 'i': /* BTextControl, int variable */
461                     if( doIt )
462                         config_PutInt( p_intf, name + 1, atoi( ((BTextControl*)child)->Text() ) );
463                     else
464                     {
465                         string = "";
466                         string << config_GetInt( p_intf, name + 1 );
467                         ((BTextControl*)child)->SetText( string.String() );
468                     }
469                     break;
470                 case 'f': /* BTextControl, float variable */
471                     if( doIt )
472                         config_PutFloat( p_intf, name + 1,
473                                          strtod( ((BTextControl*)child)->Text(), NULL ) );
474                     else
475                     {
476                         string = "";
477                         string << config_GetFloat( p_intf, name + 1 );
478                         ((BTextControl*)child)->SetText( string.String() );
479                     }
480                     break;
481                 case 'b': /* BCheckBox, bool variable */
482                     if( doIt )
483                         config_PutInt( p_intf, name + 1, ((BCheckBox*)child)->Value() );
484                     else
485                         ((BCheckBox*)child)->SetValue( config_GetInt( p_intf, name + 1 ) );
486                     break;
487             }
488         }
489     }
490 }
491
492 /*****************************************************************************
493  * PreferencesWindow::SaveChanges
494  *****************************************************************************/
495 void PreferencesWindow::SaveChanges()
496 {
497     ApplyChanges( true );
498     config_SaveConfigFile( p_intf, NULL );
499 }
500
501 /*****************************************************************************
502  * PreferencesWindow::ReallyQuit
503  *****************************************************************************/
504 void PreferencesWindow::ReallyQuit()
505 {
506     Lock();
507     Hide();
508     Quit();
509 }