]> git.sesse.net Git - vlc/blob - modules/gui/skins2/parser/builder.cpp
* winamp2.xml: all the equalizer sliders are working !!
[vlc] / modules / gui / skins2 / parser / builder.cpp
1 /*****************************************************************************
2  * builder.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include <string.h>
26 #include "builder.hpp"
27 #include "builder_data.hpp"
28 #include "interpreter.hpp"
29 #include "../src/file_bitmap.hpp"
30 #include "../src/os_factory.hpp"
31 #include "../src/generic_bitmap.hpp"
32 #include "../src/top_window.hpp"
33 #include "../src/anchor.hpp"
34 #include "../src/bitmap_font.hpp"
35 #include "../src/ft2_font.hpp"
36 #include "../src/theme.hpp"
37 #include "../controls/ctrl_button.hpp"
38 #include "../controls/ctrl_checkbox.hpp"
39 #include "../controls/ctrl_image.hpp"
40 #include "../controls/ctrl_list.hpp"
41 #include "../controls/ctrl_move.hpp"
42 #include "../controls/ctrl_resize.hpp"
43 #include "../controls/ctrl_slider.hpp"
44 #include "../controls/ctrl_radialslider.hpp"
45 #include "../controls/ctrl_text.hpp"
46 #include "../controls/ctrl_tree.hpp"
47 #include "../controls/ctrl_video.hpp"
48 #include "../utils/position.hpp"
49 #include "../utils/var_bool.hpp"
50 #include "../utils/var_text.hpp"
51
52 #include "vlc_image.h"
53
54
55 Builder::Builder( intf_thread_t *pIntf, const BuilderData &rData,
56                   const string &rPath ):
57     SkinObject( pIntf ), m_rData( rData ), m_path( rPath ), m_pTheme( NULL )
58 {
59     m_pImageHandler = image_HandlerCreate( pIntf );
60 }
61
62 Builder::~Builder()
63 {
64     if( m_pImageHandler ) image_HandlerDelete( m_pImageHandler );
65 }
66
67 CmdGeneric *Builder::parseAction( const string &rAction )
68 {
69     return Interpreter::instance( getIntf() )->parseAction( rAction, m_pTheme );
70 }
71
72
73 // Useful macro
74 #define ADD_OBJECTS( type ) \
75     list<BuilderData::type>::const_iterator it##type; \
76     for( it##type = m_rData.m_list##type.begin(); \
77          it##type != m_rData.m_list##type.end(); it##type++ ) \
78     { \
79         add##type( *it##type ); \
80     }
81
82
83 Theme *Builder::build()
84 {
85     m_pTheme = new Theme( getIntf() );
86     if( m_pTheme == NULL )
87     {
88         return NULL;
89     }
90
91     // Create everything from the data in the XML
92     ADD_OBJECTS( Theme );
93     ADD_OBJECTS( Bitmap );
94     ADD_OBJECTS( SubBitmap );
95     ADD_OBJECTS( BitmapFont );
96     ADD_OBJECTS( Font );
97     ADD_OBJECTS( Window );
98     ADD_OBJECTS( Layout );
99     ADD_OBJECTS( Anchor );
100     ADD_OBJECTS( Button );
101     ADD_OBJECTS( Checkbox );
102     ADD_OBJECTS( Image );
103     ADD_OBJECTS( Text );
104     ADD_OBJECTS( RadialSlider );
105     ADD_OBJECTS( Slider );
106     ADD_OBJECTS( List );
107     ADD_OBJECTS( Tree );
108     ADD_OBJECTS( Video );
109
110     return m_pTheme;
111 }
112
113
114 // Macro to get a bitmap by its ID in the builder
115 #define GET_BMP( pBmp, id ) \
116     if( id != "none" ) \
117     { \
118         pBmp = m_pTheme->getBitmapById(id); \
119         if( pBmp == NULL ) \
120         { \
121             msg_Err( getIntf(), "unknown bitmap id: %s", id.c_str() ); \
122             return; \
123         } \
124     }
125
126 void Builder::addTheme( const BuilderData::Theme &rData )
127 {
128     WindowManager &rManager = m_pTheme->getWindowManager();
129     rManager.setMagnetValue( rData.m_magnet );
130     rManager.setAlphaValue( rData.m_alpha );
131     rManager.setMoveAlphaValue( rData.m_moveAlpha );
132     GenericFont *pFont = getFont( rData.m_tooltipfont );
133     if( pFont )
134     {
135         rManager.createTooltip( *pFont );
136     }
137     else
138     {
139         msg_Warn( getIntf(), "Invalid tooltip font: %s",
140                   rData.m_tooltipfont.c_str() );
141     }
142 }
143
144
145 void Builder::addBitmap( const BuilderData::Bitmap &rData )
146 {
147     GenericBitmap *pBmp =
148         new FileBitmap( getIntf(), m_pImageHandler,
149                         getFilePath( rData.m_fileName ), rData.m_alphaColor );
150     if( !pBmp->getData() )
151     {
152         // Invalid bitmap
153         delete pBmp;
154         return;
155     }
156     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
157 }
158
159
160 void Builder::addSubBitmap( const BuilderData::SubBitmap &rData )
161 {
162     // Get the parent bitmap
163     GenericBitmap *pParentBmp = NULL;
164     GET_BMP( pParentBmp, rData.m_parent );
165
166     // Copy a region of the parent bitmap to the new one
167     BitmapImpl *pBmp =
168         new BitmapImpl( getIntf(), rData.m_width, rData.m_height );
169     bool res = pBmp->drawBitmap( *pParentBmp, rData.m_x, rData.m_y, 0, 0,
170                                  rData.m_width, rData.m_height );
171     if( !res )
172     {
173         // Invalid sub-bitmap
174         delete pBmp;
175         msg_Warn( getIntf(), "SubBitmap %s ignored", rData.m_id.c_str() );
176         return;
177     }
178     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
179 }
180
181
182 void Builder::addBitmapFont( const BuilderData::BitmapFont &rData )
183 {
184     GenericBitmap *pBmp =
185         new FileBitmap( getIntf(), m_pImageHandler,
186                         getFilePath( rData.m_file ), 0 );
187     if( !pBmp->getData() )
188     {
189         // Invalid bitmap
190         delete pBmp;
191         return;
192     }
193
194     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
195
196     GenericFont *pFont = new BitmapFont( getIntf(), *pBmp, rData.m_type );
197     if( pFont->init() )
198     {
199         m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
200     }
201     else
202     {
203         delete pFont;
204     }
205 }
206
207
208 void Builder::addFont( const BuilderData::Font &rData )
209 {
210     // Try to load the font from the theme directory
211     GenericFont *pFont = new FT2Font( getIntf(),
212                                       getFilePath( rData.m_fontFile ),
213                                       rData.m_size );
214     if( pFont->init() )
215     {
216         m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
217     }
218     else
219     {
220         delete pFont;
221
222         // Font not found; try in the resource path
223         OSFactory *pOSFactory = OSFactory::instance( getIntf() );
224         const list<string> &resPath = pOSFactory->getResourcePath();
225         const string &sep = pOSFactory->getDirSeparator();
226
227         list<string>::const_iterator it;
228         for( it = resPath.begin(); it != resPath.end(); it++ )
229         {
230             string path = (*it) + sep + "fonts" + sep + rData.m_fontFile;
231             pFont = new FT2Font( getIntf(), path, rData.m_size );
232             if( pFont->init() )
233             {
234                 // Font loaded successfully
235                 m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
236                 break;
237             }
238             else
239             {
240                 delete pFont;
241             }
242         }
243     }
244 }
245
246
247 void Builder::addWindow( const BuilderData::Window &rData )
248 {
249     TopWindow *pWin =
250         new TopWindow( getIntf(), rData.m_xPos, rData.m_yPos,
251                        m_pTheme->getWindowManager(),
252                        rData.m_dragDrop, rData.m_playOnDrop,
253                        rData.m_visible );
254
255     m_pTheme->m_windows[rData.m_id] = TopWindowPtr( pWin );
256 }
257
258
259 void Builder::addLayout( const BuilderData::Layout &rData )
260 {
261     TopWindow *pWin = m_pTheme->getWindowById(rData.m_windowId);
262     if( pWin == NULL )
263     {
264         msg_Err( getIntf(), "unknown window id: %s", rData.m_windowId.c_str() );
265         return;
266     }
267
268     int minWidth = rData.m_minWidth != -1 ? rData.m_minWidth : rData.m_width;
269     int maxWidth = rData.m_maxWidth != -1 ? rData.m_maxWidth : rData.m_width;
270     int minHeight = rData.m_minHeight != -1 ? rData.m_minHeight :
271                     rData.m_height;
272     int maxHeight = rData.m_maxHeight != -1 ? rData.m_maxHeight :
273                     rData.m_height;
274     GenericLayout *pLayout = new GenericLayout( getIntf(), rData.m_width,
275                                                 rData.m_height,
276                                                 minWidth, maxWidth, minHeight,
277                                                 maxHeight );
278     m_pTheme->m_layouts[rData.m_id] = GenericLayoutPtr( pLayout );
279
280     // Attach the layout to its window
281     m_pTheme->getWindowManager().addLayout( *pWin, *pLayout );
282 }
283
284
285 void Builder::addAnchor( const BuilderData::Anchor &rData )
286 {
287     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
288     if( pLayout == NULL )
289     {
290         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
291         return;
292     }
293
294     Bezier *pCurve = getPoints( rData.m_points.c_str() );
295     if( pCurve == NULL )
296     {
297         msg_Err( getIntf(), "Invalid format in tag points=\"%s\"",
298                  rData.m_points.c_str() );
299         return;
300     }
301     m_pTheme->m_curves.push_back( BezierPtr( pCurve ) );
302
303     Anchor *pAnc = new Anchor( getIntf(), rData.m_xPos, rData.m_yPos,
304                                rData.m_range, rData.m_priority,
305                                *pCurve, *pLayout );
306     pLayout->addAnchor( pAnc );
307 }
308
309
310 void Builder::addButton( const BuilderData::Button &rData )
311 {
312     // Get the bitmaps of the button
313     GenericBitmap *pBmpUp = NULL;
314     GET_BMP( pBmpUp, rData.m_upId );
315
316     GenericBitmap *pBmpDown = pBmpUp;
317     GET_BMP( pBmpDown, rData.m_downId );
318
319     GenericBitmap *pBmpOver = pBmpUp;
320     GET_BMP( pBmpOver, rData.m_overId );
321
322     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
323     if( pLayout == NULL )
324     {
325         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
326         return;
327     }
328
329     CmdGeneric *pCommand = parseAction( rData.m_actionId );
330     if( pCommand == NULL )
331     {
332         msg_Err( getIntf(), "Invalid action: %s", rData.m_actionId.c_str() );
333         return;
334     }
335
336     // Get the visibility variable
337     // XXX check when it is null
338     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
339     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
340
341     CtrlButton *pButton = new CtrlButton( getIntf(), *pBmpUp, *pBmpOver,
342         *pBmpDown, *pCommand, UString( getIntf(), rData.m_tooltip.c_str() ),
343         UString( getIntf(), rData.m_help.c_str() ), pVisible );
344
345     // Compute the position of the control
346     // XXX (we suppose all the images have the same size...)
347     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
348                                        rData.m_xPos, rData.m_yPos,
349                                        pBmpUp->getWidth(),
350                                        pBmpUp->getHeight(), *pLayout );
351
352     pLayout->addControl( pButton, pos, rData.m_layer );
353
354     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pButton );
355 }
356
357
358 void Builder::addCheckbox( const BuilderData::Checkbox &rData )
359 {
360     // Get the bitmaps of the checkbox
361     GenericBitmap *pBmpUp1 = NULL;
362     GET_BMP( pBmpUp1, rData.m_up1Id );
363
364     GenericBitmap *pBmpDown1 = pBmpUp1;
365     GET_BMP( pBmpDown1, rData.m_down1Id );
366
367     GenericBitmap *pBmpOver1 = pBmpUp1;
368     GET_BMP( pBmpOver1, rData.m_over1Id );
369
370     GenericBitmap *pBmpUp2 = NULL;
371     GET_BMP( pBmpUp2, rData.m_up2Id );
372
373     GenericBitmap *pBmpDown2 = pBmpUp2;
374     GET_BMP( pBmpDown2, rData.m_down2Id );
375
376     GenericBitmap *pBmpOver2 = pBmpUp2;
377     GET_BMP( pBmpOver2, rData.m_over2Id );
378
379     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
380     if( pLayout == NULL )
381     {
382         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
383         return;
384     }
385
386     CmdGeneric *pCommand1 = parseAction( rData.m_action1 );
387     if( pCommand1 == NULL )
388     {
389         msg_Err( getIntf(), "Invalid action: %s", rData.m_action1.c_str() );
390         return;
391     }
392
393     CmdGeneric *pCommand2 = parseAction( rData.m_action2 );
394     if( pCommand2 == NULL )
395     {
396         msg_Err( getIntf(), "Invalid action: %s", rData.m_action2.c_str() );
397         return;
398     }
399
400     // Get the state variable
401     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
402     VarBool *pVar = pInterpreter->getVarBool( rData.m_state, m_pTheme );
403     if( pVar == NULL )
404     {
405         // TODO: default state
406         return;
407     }
408
409     // Get the visibility variable
410     // XXX check when it is null
411     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
412
413     // Create the control
414     CtrlCheckbox *pCheckbox = new CtrlCheckbox( getIntf(), *pBmpUp1,
415         *pBmpOver1, *pBmpDown1, *pBmpUp2, *pBmpOver2, *pBmpDown2, *pCommand1,
416         *pCommand2, UString( getIntf(), rData.m_tooltip1.c_str() ),
417         UString( getIntf(), rData.m_tooltip2.c_str() ), *pVar,
418         UString( getIntf(), rData.m_help.c_str() ), pVisible );
419
420     // Compute the position of the control
421     // XXX (we suppose all the images have the same size...)
422     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
423                                        rData.m_xPos, rData.m_yPos,
424                                        pBmpUp1->getWidth(),
425                                        pBmpUp1->getHeight(), *pLayout );
426
427     pLayout->addControl( pCheckbox, pos, rData.m_layer );
428
429     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pCheckbox );
430 }
431
432
433 void Builder::addImage( const BuilderData::Image &rData )
434 {
435     GenericBitmap *pBmp = NULL;
436     GET_BMP( pBmp, rData.m_bmpId );
437
438     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
439     if( pLayout == NULL )
440     {
441         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
442         return;
443     }
444
445     TopWindow *pWindow = m_pTheme->getWindowById(rData.m_windowId);
446     if( pWindow == NULL )
447     {
448         msg_Err( getIntf(), "unknown window id: %s", rData.m_windowId.c_str() );
449         return;
450     }
451
452     // Get the visibility variable
453     // XXX check when it is null
454     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
455     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
456
457     CtrlImage::resize_t resizeMethod =
458         (rData.m_resize == "scale" ? CtrlImage::kScale : CtrlImage::kMosaic);
459     CtrlImage *pImage = new CtrlImage( getIntf(), *pBmp, resizeMethod,
460         UString( getIntf(), rData.m_help.c_str() ), pVisible );
461
462     // Compute the position of the control
463     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
464                                        rData.m_xPos,
465                                        rData.m_yPos, pBmp->getWidth(),
466                                        pBmp->getHeight(), *pLayout );
467
468     // XXX: test to be changed! XXX
469     if( rData.m_actionId == "move" )
470     {
471         CtrlMove *pMove = new CtrlMove( getIntf(), m_pTheme->getWindowManager(),
472              *pImage, *pWindow, UString( getIntf(), rData.m_help.c_str() ),
473              NULL);
474         pLayout->addControl( pMove, pos, rData.m_layer );
475     }
476     else if( rData.m_actionId == "resizeSE" )
477     {
478         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
479                 UString( getIntf(), rData.m_help.c_str() ), NULL );
480         pLayout->addControl( pResize, pos, rData.m_layer );
481     }
482     else
483     {
484         pLayout->addControl( pImage, pos, rData.m_layer );
485     }
486
487     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pImage );
488 }
489
490
491 void Builder::addText( const BuilderData::Text &rData )
492 {
493     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
494     if( pLayout == NULL )
495     {
496         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
497         return;
498     }
499
500     GenericFont *pFont = getFont( rData.m_fontId );
501     if( pFont == NULL )
502     {
503         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
504         return;
505     }
506
507     // Create a text variable
508     VarText *pVar = new VarText( getIntf() );
509     UString msg( getIntf(), rData.m_text.c_str() );
510     pVar->set( msg );
511     m_pTheme->m_vars.push_back( VariablePtr( pVar ) );
512
513     // Get the visibility variable
514     // XXX check when it is null
515     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
516     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
517
518     CtrlText *pText = new CtrlText( getIntf(), *pVar, *pFont,
519         UString( getIntf(), rData.m_help.c_str() ), rData.m_color, pVisible );
520
521     int height = pFont->getSize();
522
523     // Compute the position of the control
524     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
525                                        rData.m_xPos, rData.m_yPos,
526                                        rData.m_width, height,
527                                        *pLayout );
528
529     pLayout->addControl( pText, pos, rData.m_layer );
530
531     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pText );
532 }
533
534
535 void Builder::addRadialSlider( const BuilderData::RadialSlider &rData )
536 {
537     // Get the bitmaps of the slider
538     GenericBitmap *pSeq = NULL;
539     GET_BMP( pSeq, rData.m_sequence );
540
541     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
542     if( pLayout == NULL )
543     {
544         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
545         return;
546     }
547
548     // Get the variable associated to the slider
549     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
550     VarPercent *pVar = pInterpreter->getVarPercent( rData.m_value, m_pTheme );
551     if( pVar == NULL )
552     {
553         msg_Err( getIntf(), "Unknown slider value: %s", rData.m_value.c_str() );
554         return;
555     }
556
557     // Get the visibility variable
558     // XXX check when it is null
559     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
560
561     // Create the control
562     CtrlRadialSlider *pRadial =
563         new CtrlRadialSlider( getIntf(), *pSeq, rData.m_nbImages, *pVar,
564                               rData.m_minAngle, rData.m_maxAngle,
565                               UString( getIntf(), rData.m_help.c_str() ),
566                               pVisible );
567
568     // XXX: resizing is not supported
569     // Compute the position of the control
570     const Position pos =
571         makePosition( rData.m_leftTop, rData.m_rightBottom, rData.m_xPos,
572                       rData.m_yPos, pSeq->getWidth(),
573                       pSeq->getHeight() / rData.m_nbImages, *pLayout );
574
575     pLayout->addControl( pRadial, pos, rData.m_layer );
576
577     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pRadial );
578 }
579
580
581 void Builder::addSlider( const BuilderData::Slider &rData )
582 {
583     // Get the bitmaps of the slider
584     GenericBitmap *pBmpUp = NULL;
585     GET_BMP( pBmpUp, rData.m_upId );
586
587     GenericBitmap *pBmpDown = pBmpUp;
588     GET_BMP( pBmpDown, rData.m_downId );
589
590     GenericBitmap *pBmpOver = pBmpUp;
591     GET_BMP( pBmpOver, rData.m_overId );
592
593     GenericBitmap *pBgImage = NULL;
594     if( rData.m_imageId != "none" )
595     {
596         GET_BMP( pBgImage, rData.m_imageId );
597     }
598
599     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
600     if( pLayout == NULL )
601     {
602         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
603         return;
604     }
605
606     Bezier *pCurve = getPoints( rData.m_points.c_str() );
607     if( pCurve == NULL )
608     {
609         msg_Err( getIntf(), "Invalid format in tag points=\"%s\"",
610                  rData.m_points.c_str() );
611         return;
612     }
613     m_pTheme->m_curves.push_back( BezierPtr( pCurve ) );
614
615     // Get the visibility variable
616     // XXX check when it is null
617     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
618     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
619
620     // Get the variable associated to the slider
621     VarPercent *pVar = pInterpreter->getVarPercent( rData.m_value, m_pTheme );
622     if( pVar == NULL )
623     {
624         msg_Err( getIntf(), "Unknown slider value: %s", rData.m_value.c_str() );
625         return;
626     }
627
628     // Create the cursor and background controls
629     CtrlSliderCursor *pCursor = new CtrlSliderCursor( getIntf(), *pBmpUp,
630         *pBmpOver, *pBmpDown, *pCurve, *pVar, pVisible,
631         UString( getIntf(), rData.m_tooltip.c_str() ),
632         UString( getIntf(), rData.m_help.c_str() ) );
633
634     CtrlSliderBg *pBackground = new CtrlSliderBg( getIntf(), *pCursor,
635         *pCurve, *pVar, rData.m_thickness, pBgImage, rData.m_nbHoriz,
636         rData.m_nbVert, rData.m_padHoriz, rData.m_padVert,
637         pVisible, UString( getIntf(), rData.m_help.c_str() ) );
638
639     // Compute the position of the control
640     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
641                                        rData.m_xPos, rData.m_yPos,
642                                        pCurve->getWidth(), pCurve->getHeight(),
643                                        *pLayout );
644
645     pLayout->addControl( pBackground, pos, rData.m_layer );
646     pLayout->addControl( pCursor, pos, rData.m_layer );
647
648     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pCursor );
649     m_pTheme->m_controls[rData.m_id + "_bg"] = CtrlGenericPtr( pBackground );
650 }
651
652
653 void Builder::addList( const BuilderData::List &rData )
654 {
655     // Get the background bitmap, if any
656     GenericBitmap *pBgBmp = NULL;
657     GET_BMP( pBgBmp, rData.m_bgImageId );
658
659     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
660     if( pLayout == NULL )
661     {
662         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
663         return;
664     }
665
666     GenericFont *pFont = getFont( rData.m_fontId );
667     if( pFont == NULL )
668     {
669         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
670         return;
671     }
672
673     // Get the list variable
674     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
675     VarList *pVar = pInterpreter->getVarList( rData.m_var, m_pTheme );
676     if( pVar == NULL )
677     {
678         msg_Err( getIntf(), "No such list variable: %s", rData.m_var.c_str() );
679         return;
680     }
681
682     // Get the visibility variable
683     // XXX check when it is null
684     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
685
686     // Create the list control
687     CtrlList *pList = new CtrlList( getIntf(), *pVar, *pFont, pBgBmp,
688        rData.m_fgColor, rData.m_playColor, rData.m_bgColor1,
689        rData.m_bgColor2, rData.m_selColor,
690        UString( getIntf(), rData.m_help.c_str() ), pVisible );
691
692     // Compute the position of the control
693     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
694                                        rData.m_xPos, rData.m_yPos,
695                                        rData.m_width, rData.m_height,
696                                        *pLayout );
697
698     pLayout->addControl( pList, pos, rData.m_layer );
699
700     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pList );
701 }
702
703 void Builder::addTree( const BuilderData::Tree &rData )
704 {
705     // Get the bitmaps, if any
706     GenericBitmap *pBgBmp = NULL;
707     GenericBitmap *pItemBmp = NULL;
708     GenericBitmap *pOpenBmp = NULL;
709     GenericBitmap *pClosedBmp = NULL;
710     GET_BMP( pBgBmp, rData.m_bgImageId );
711     GET_BMP( pItemBmp, rData.m_itemImageId );
712     GET_BMP( pOpenBmp, rData.m_openImageId );
713     GET_BMP( pClosedBmp, rData.m_closedImageId );
714
715     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
716     if( pLayout == NULL )
717     {
718         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
719         return;
720     }
721
722     GenericFont *pFont = getFont( rData.m_fontId );
723     if( pFont == NULL )
724     {
725         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
726         return;
727     }
728
729     // Get the list variable
730     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
731     VarTree *pVar = pInterpreter->getVarTree( rData.m_var, m_pTheme );
732     if( pVar == NULL )
733     {
734         msg_Err( getIntf(), "No such list variable: %s", rData.m_var.c_str() );
735         return;
736     }
737
738     // Get the visibility variable
739     // XXX check when it is null
740     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
741
742     // Create the list control
743     CtrlTree *pTree = new CtrlTree( getIntf(), *pVar, *pFont, pBgBmp,
744        pItemBmp, pOpenBmp, pClosedBmp,
745        rData.m_fgColor, rData.m_playColor, rData.m_bgColor1,
746        rData.m_bgColor2, rData.m_selColor,
747        UString( getIntf(), rData.m_help.c_str() ), pVisible );
748
749     // Compute the position of the control
750     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
751                                        rData.m_xPos, rData.m_yPos,
752                                        rData.m_width, rData.m_height,
753                                        *pLayout );
754
755     pLayout->addControl( pTree, pos, rData.m_layer );
756
757     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pTree );
758 }
759
760 void Builder::addVideo( const BuilderData::Video &rData )
761 {
762     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
763     if( pLayout == NULL )
764     {
765         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
766         return;
767     }
768
769     // Get the visibility variable
770     // XXX check when it is null
771     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
772     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
773
774     CtrlVideo *pVideo = new CtrlVideo( getIntf(), *pLayout,
775         rData.m_autoResize, UString( getIntf(), rData.m_help.c_str() ),
776         pVisible );
777
778     // Compute the position of the control
779     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
780                                        rData.m_xPos, rData.m_yPos,
781                                        rData.m_width, rData.m_height,
782                                        *pLayout );
783
784     pLayout->addControl( pVideo, pos, rData.m_layer );
785
786     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pVideo );
787 }
788
789
790 const Position Builder::makePosition( const string &rLeftTop,
791                                       const string &rRightBottom,
792                                       int xPos, int yPos, int width,
793                                       int height, const Box &rBox ) const
794 {
795     int left = 0, top = 0, right = 0, bottom = 0;
796     Position::Ref_t refLeftTop = Position::kLeftTop;
797     Position::Ref_t refRightBottom = Position::kLeftTop;
798
799     int boxWidth = rBox.getWidth();
800     int boxHeight = rBox.getHeight();
801
802     // Position of the left top corner
803     if( rLeftTop == "lefttop" )
804     {
805         left = xPos;
806         top = yPos;
807         refLeftTop = Position::kLeftTop;
808     }
809     else if( rLeftTop == "righttop" )
810     {
811         left = xPos - boxWidth + 1;
812         top = yPos;
813         refLeftTop = Position::kRightTop;
814     }
815     else if( rLeftTop == "leftbottom" )
816     {
817         left = xPos;
818         top = yPos - boxHeight + 1;
819         refLeftTop = Position::kLeftBottom;
820     }
821     else if( rLeftTop == "rightbottom" )
822     {
823         left = xPos - boxWidth + 1;
824         top = yPos - boxHeight + 1;
825         refLeftTop = Position::kRightBottom;
826     }
827
828     // Position of the right bottom corner
829     if( rRightBottom == "lefttop" )
830     {
831         right = xPos + width - 1;
832         bottom = yPos + height - 1;
833         refRightBottom = Position::kLeftTop;
834     }
835     else if( rRightBottom == "righttop" )
836     {
837         right = xPos + width - boxWidth;
838         bottom = yPos + height - 1;
839         refRightBottom = Position::kRightTop;
840     }
841     else if( rRightBottom == "leftbottom" )
842     {
843         right = xPos + width - 1;
844         bottom = yPos + height - boxHeight;
845         refRightBottom = Position::kLeftBottom;
846     }
847     else if( rRightBottom == "rightbottom" )
848     {
849         right = xPos + width - boxWidth;
850         bottom = yPos + height - boxHeight;
851         refRightBottom = Position::kRightBottom;
852     }
853
854     return Position( left, top, right, bottom, rBox, refLeftTop,
855                      refRightBottom );
856 }
857
858
859 GenericFont *Builder::getFont( const string &fontId )
860 {
861     GenericFont *pFont = m_pTheme->getFontById(fontId);
862     if( !pFont && fontId == "defaultfont" )
863     {
864         // Get the resource path and try to load the default font
865         OSFactory *pOSFactory = OSFactory::instance( getIntf() );
866         const list<string> &resPath = pOSFactory->getResourcePath();
867         const string &sep = pOSFactory->getDirSeparator();
868
869         list<string>::const_iterator it;
870         for( it = resPath.begin(); it != resPath.end(); it++ )
871         {
872             string path = (*it) + sep + "fonts" + sep + "FreeSans.ttf";
873             pFont = new FT2Font( getIntf(), path, 12 );
874             if( pFont->init() )
875             {
876                 // Font loaded successfully
877                 m_pTheme->m_fonts["defaultfont"] = GenericFontPtr( pFont );
878                 break;
879             }
880             else
881             {
882                 delete pFont;
883                 pFont = NULL;
884             }
885         }
886         if( !pFont )
887         {
888             msg_Err( getIntf(), "Failed to open the default font" );
889         }
890     }
891     return pFont;
892 }
893
894
895 string Builder::getFilePath( const string &rFileName ) const
896 {
897     OSFactory *pFactory = OSFactory::instance( getIntf() );
898     return m_path + pFactory->getDirSeparator() + rFileName;
899 }
900
901
902
903 Bezier *Builder::getPoints( const char *pTag ) const
904 {
905     vector<float> xBez, yBez;
906     int x, y, n;
907     while( 1 )
908     {
909         if( sscanf( pTag, "(%d,%d)%n", &x, &y, &n ) < 1 )
910         {
911             return NULL;
912         }
913 #if 0
914         if( x < 0 || y < 0 )
915         {
916             msg_Err( getIntf(),
917                      "Slider points cannot have negative coordinates!" );
918             return NULL;
919         }
920 #endif
921         xBez.push_back( x );
922         yBez.push_back( y );
923         pTag += n;
924         if( *pTag == '\0' )
925         {
926             break;
927         }
928         if( *(pTag++) != ',' )
929         {
930             return NULL;
931         }
932     }
933
934     // Create the Bezier curve
935     return new Bezier( getIntf(), xBez, yBez );
936 }
937