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