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