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