]> git.sesse.net Git - vlc/blob - modules/gui/skins2/parser/builder.cpp
* skins2: The Image control now supports the "action2" attribute (feel free to
[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     CmdGeneric *pCommand = parseAction( rData.m_action2Id );
465     if( pCommand == NULL )
466     {
467         msg_Err( getIntf(), "Invalid action: %s", rData.m_action2Id.c_str() );
468         return;
469     }
470
471     // Get the visibility variable
472     // XXX check when it is null
473     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
474     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
475
476     CtrlImage::resize_t resizeMethod =
477         (rData.m_resize == "scale" ? CtrlImage::kScale : CtrlImage::kMosaic);
478     CtrlImage *pImage = new CtrlImage( getIntf(), *pBmp, *pCommand,
479         resizeMethod, UString( getIntf(), rData.m_help.c_str() ), pVisible );
480
481     // Compute the position of the control
482     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
483                                        rData.m_xPos,
484                                        rData.m_yPos, pBmp->getWidth(),
485                                        pBmp->getHeight(), *pLayout );
486
487     // XXX: test to be changed! XXX
488     if( rData.m_actionId == "move" )
489     {
490         CtrlMove *pMove = new CtrlMove( getIntf(), m_pTheme->getWindowManager(),
491              *pImage, *pWindow, UString( getIntf(), rData.m_help.c_str() ),
492              NULL);
493         pLayout->addControl( pMove, pos, rData.m_layer );
494     }
495     else if( rData.m_actionId == "resizeS" )
496     {
497         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
498                 UString( getIntf(), rData.m_help.c_str() ), NULL,
499                 CtrlResize::kResizeS );
500         pLayout->addControl( pResize, pos, rData.m_layer );
501     }
502     else if( rData.m_actionId == "resizeE" )
503     {
504         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
505                 UString( getIntf(), rData.m_help.c_str() ), NULL,
506                 CtrlResize::kResizeE );
507         pLayout->addControl( pResize, pos, rData.m_layer );
508     }
509     else if( rData.m_actionId == "resizeSE" )
510     {
511         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
512                 UString( getIntf(), rData.m_help.c_str() ), NULL,
513                 CtrlResize::kResizeSE );
514         pLayout->addControl( pResize, pos, rData.m_layer );
515     }
516     else
517     {
518         pLayout->addControl( pImage, pos, rData.m_layer );
519     }
520
521     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pImage );
522 }
523
524
525 void Builder::addText( const BuilderData::Text &rData )
526 {
527     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
528     if( pLayout == NULL )
529     {
530         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
531         return;
532     }
533
534     GenericFont *pFont = getFont( rData.m_fontId );
535     if( pFont == NULL )
536     {
537         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
538         return;
539     }
540
541     // Convert the scrolling mode
542     CtrlText::Scrolling_t scrolling;
543     if( rData.m_scrolling == "auto" )
544         scrolling = CtrlText::kAutomatic;
545     else if( rData.m_scrolling == "manual" )
546         scrolling = CtrlText::kManual;
547     else if( rData.m_scrolling == "none" )
548         scrolling = CtrlText::kNone;
549     else
550     {
551         msg_Err( getIntf(), "Invalid scrolling mode: %s",
552                  rData.m_scrolling.c_str() );
553         return;
554     }
555
556     // Convert the alignment
557     CtrlText::Align_t alignment;
558     if( rData.m_alignment == "left" )
559         alignment = CtrlText::kLeft;
560     else if( rData.m_alignment == "center" || rData.m_alignment == "centre" )
561         alignment = CtrlText::kCenter;
562     else if( rData.m_alignment == "right" )
563         alignment = CtrlText::kRight;
564     else
565     {
566         msg_Err( getIntf(), "Invalid alignment: %s",
567                  rData.m_alignment.c_str() );
568         return;
569     }
570
571     // Create a text variable
572     VarText *pVar = new VarText( getIntf() );
573     m_pTheme->m_vars.push_back( VariablePtr( pVar ) );
574
575     // Get the visibility variable
576     // XXX check when it is null
577     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
578     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
579
580     CtrlText *pText = new CtrlText( getIntf(), *pVar, *pFont,
581         UString( getIntf(), rData.m_help.c_str() ), rData.m_color, pVisible,
582         scrolling, alignment );
583
584     int height = pFont->getSize();
585
586     // Compute the position of the control
587     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
588                                        rData.m_xPos, rData.m_yPos,
589                                        rData.m_width, height,
590                                        *pLayout );
591
592     pLayout->addControl( pText, pos, rData.m_layer );
593
594     // Set the text of the control
595     UString msg( getIntf(), rData.m_text.c_str() );
596     pVar->set( msg );
597
598     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pText );
599 }
600
601
602 void Builder::addRadialSlider( const BuilderData::RadialSlider &rData )
603 {
604     // Get the bitmaps of the slider
605     GenericBitmap *pSeq = NULL;
606     GET_BMP( pSeq, rData.m_sequence );
607
608     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
609     if( pLayout == NULL )
610     {
611         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
612         return;
613     }
614
615     // Get the variable associated to the slider
616     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
617     VarPercent *pVar = pInterpreter->getVarPercent( rData.m_value, m_pTheme );
618     if( pVar == NULL )
619     {
620         msg_Err( getIntf(), "Unknown slider value: %s", rData.m_value.c_str() );
621         return;
622     }
623
624     // Get the visibility variable
625     // XXX check when it is null
626     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
627
628     // Create the control
629     CtrlRadialSlider *pRadial =
630         new CtrlRadialSlider( getIntf(), *pSeq, rData.m_nbImages, *pVar,
631                               rData.m_minAngle, rData.m_maxAngle,
632                               UString( getIntf(), rData.m_help.c_str() ),
633                               pVisible );
634
635     // XXX: resizing is not supported
636     // Compute the position of the control
637     const Position pos =
638         makePosition( rData.m_leftTop, rData.m_rightBottom, rData.m_xPos,
639                       rData.m_yPos, pSeq->getWidth(),
640                       pSeq->getHeight() / rData.m_nbImages, *pLayout );
641
642     pLayout->addControl( pRadial, pos, rData.m_layer );
643
644     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pRadial );
645 }
646
647
648 void Builder::addSlider( const BuilderData::Slider &rData )
649 {
650     // Add the background first, so that we will still have something almost
651     // functional if the cursor cannot be created properly (this happens for
652     // some winamp2 skins, where the images of the cursor are not always
653     // present)
654
655     // Get the bitmaps of the background
656     GenericBitmap *pBgImage = NULL;
657     if( rData.m_imageId != "none" )
658     {
659         GET_BMP( pBgImage, rData.m_imageId );
660     }
661
662     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
663     if( pLayout == NULL )
664     {
665         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
666         return;
667     }
668
669     Bezier *pCurve = getPoints( rData.m_points.c_str() );
670     if( pCurve == NULL )
671     {
672         msg_Err( getIntf(), "Invalid format in tag points=\"%s\"",
673                  rData.m_points.c_str() );
674         return;
675     }
676     m_pTheme->m_curves.push_back( BezierPtr( pCurve ) );
677
678     // Get the visibility variable
679     // XXX check when it is null
680     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
681     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
682
683     // Get the variable associated to the slider
684     VarPercent *pVar = pInterpreter->getVarPercent( rData.m_value, m_pTheme );
685     if( pVar == NULL )
686     {
687         msg_Err( getIntf(), "Unknown slider value: %s", rData.m_value.c_str() );
688         return;
689     }
690
691     // Create the background control
692     CtrlSliderBg *pBackground = new CtrlSliderBg( getIntf(),
693         *pCurve, *pVar, rData.m_thickness, pBgImage, rData.m_nbHoriz,
694         rData.m_nbVert, rData.m_padHoriz, rData.m_padVert,
695         pVisible, UString( getIntf(), rData.m_help.c_str() ) );
696
697     // Compute the position of the control
698     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
699                                        rData.m_xPos, rData.m_yPos,
700                                        pCurve->getWidth(), pCurve->getHeight(),
701                                        *pLayout );
702
703     pLayout->addControl( pBackground, pos, rData.m_layer );
704
705     m_pTheme->m_controls[rData.m_id + "_bg"] = CtrlGenericPtr( pBackground );
706
707     // Get the bitmaps of the cursor
708     GenericBitmap *pBmpUp = NULL;
709     GET_BMP( pBmpUp, rData.m_upId );
710
711     GenericBitmap *pBmpDown = pBmpUp;
712     GET_BMP( pBmpDown, rData.m_downId );
713
714     GenericBitmap *pBmpOver = pBmpUp;
715     GET_BMP( pBmpOver, rData.m_overId );
716
717     // Create the cursor control
718     CtrlSliderCursor *pCursor = new CtrlSliderCursor( getIntf(), *pBmpUp,
719         *pBmpOver, *pBmpDown, *pCurve, *pVar, pVisible,
720         UString( getIntf(), rData.m_tooltip.c_str() ),
721         UString( getIntf(), rData.m_help.c_str() ) );
722
723     pLayout->addControl( pCursor, pos, rData.m_layer );
724
725     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pCursor );
726
727     // Associate the cursor to the background
728     pBackground->associateCursor( *pCursor );
729 }
730
731
732 void Builder::addList( const BuilderData::List &rData )
733 {
734     // Get the background bitmap, if any
735     GenericBitmap *pBgBmp = NULL;
736     GET_BMP( pBgBmp, rData.m_bgImageId );
737
738     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
739     if( pLayout == NULL )
740     {
741         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
742         return;
743     }
744
745     GenericFont *pFont = getFont( rData.m_fontId );
746     if( pFont == NULL )
747     {
748         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
749         return;
750     }
751
752     // Get the list variable
753     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
754     VarList *pVar = pInterpreter->getVarList( rData.m_var, m_pTheme );
755     if( pVar == NULL )
756     {
757         msg_Err( getIntf(), "No such list variable: %s", rData.m_var.c_str() );
758         return;
759     }
760
761     // Get the visibility variable
762     // XXX check when it is null
763     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
764
765     // Create the list control
766     CtrlList *pList = new CtrlList( getIntf(), *pVar, *pFont, pBgBmp,
767        rData.m_fgColor, rData.m_playColor, rData.m_bgColor1,
768        rData.m_bgColor2, rData.m_selColor,
769        UString( getIntf(), rData.m_help.c_str() ), pVisible );
770
771     // Compute the position of the control
772     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
773                                        rData.m_xPos, rData.m_yPos,
774                                        rData.m_width, rData.m_height,
775                                        *pLayout );
776
777     pLayout->addControl( pList, pos, rData.m_layer );
778
779     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pList );
780 }
781
782 void Builder::addTree( const BuilderData::Tree &rData )
783 {
784     // Get the bitmaps, if any
785     GenericBitmap *pBgBmp = NULL;
786     GenericBitmap *pItemBmp = NULL;
787     GenericBitmap *pOpenBmp = NULL;
788     GenericBitmap *pClosedBmp = NULL;
789     GET_BMP( pBgBmp, rData.m_bgImageId );
790     GET_BMP( pItemBmp, rData.m_itemImageId );
791     GET_BMP( pOpenBmp, rData.m_openImageId );
792     GET_BMP( pClosedBmp, rData.m_closedImageId );
793
794     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
795     if( pLayout == NULL )
796     {
797         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
798         return;
799     }
800
801     GenericFont *pFont = getFont( rData.m_fontId );
802     if( pFont == NULL )
803     {
804         msg_Err( getIntf(), "Unknown font id: %s", rData.m_fontId.c_str() );
805         return;
806     }
807
808     // Get the list variable
809     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
810     VarTree *pVar = pInterpreter->getVarTree( rData.m_var, m_pTheme );
811     if( pVar == NULL )
812     {
813         msg_Err( getIntf(), "No such list variable: %s", rData.m_var.c_str() );
814         return;
815     }
816
817     // Get the visibility variable
818     // XXX check when it is null
819     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
820
821     // Create the list control
822     CtrlTree *pTree = new CtrlTree( getIntf(), *pVar, *pFont, pBgBmp,
823        pItemBmp, pOpenBmp, pClosedBmp,
824        rData.m_fgColor, rData.m_playColor, rData.m_bgColor1,
825        rData.m_bgColor2, rData.m_selColor,
826        UString( getIntf(), rData.m_help.c_str() ), pVisible );
827
828     // Compute the position of the control
829     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
830                                        rData.m_xPos, rData.m_yPos,
831                                        rData.m_width, rData.m_height,
832                                        *pLayout );
833
834     pLayout->addControl( pTree, pos, rData.m_layer );
835
836     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pTree );
837 }
838
839 void Builder::addVideo( const BuilderData::Video &rData )
840 {
841     GenericLayout *pLayout = m_pTheme->getLayoutById(rData.m_layoutId);
842     if( pLayout == NULL )
843     {
844         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
845         return;
846     }
847
848     // Get the visibility variable
849     // XXX check when it is null
850     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
851     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
852
853     CtrlVideo *pVideo = new CtrlVideo( getIntf(), *pLayout,
854         rData.m_autoResize, UString( getIntf(), rData.m_help.c_str() ),
855         pVisible );
856
857     // Compute the position of the control
858     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
859                                        rData.m_xPos, rData.m_yPos,
860                                        rData.m_width, rData.m_height,
861                                        *pLayout );
862
863     pLayout->addControl( pVideo, pos, rData.m_layer );
864
865     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pVideo );
866 }
867
868
869 const Position Builder::makePosition( const string &rLeftTop,
870                                       const string &rRightBottom,
871                                       int xPos, int yPos, int width,
872                                       int height, const Box &rBox ) const
873 {
874     int left = 0, top = 0, right = 0, bottom = 0;
875     Position::Ref_t refLeftTop = Position::kLeftTop;
876     Position::Ref_t refRightBottom = Position::kLeftTop;
877
878     int boxWidth = rBox.getWidth();
879     int boxHeight = rBox.getHeight();
880
881     // Position of the left top corner
882     if( rLeftTop == "lefttop" )
883     {
884         left = xPos;
885         top = yPos;
886         refLeftTop = Position::kLeftTop;
887     }
888     else if( rLeftTop == "righttop" )
889     {
890         left = xPos - boxWidth + 1;
891         top = yPos;
892         refLeftTop = Position::kRightTop;
893     }
894     else if( rLeftTop == "leftbottom" )
895     {
896         left = xPos;
897         top = yPos - boxHeight + 1;
898         refLeftTop = Position::kLeftBottom;
899     }
900     else if( rLeftTop == "rightbottom" )
901     {
902         left = xPos - boxWidth + 1;
903         top = yPos - boxHeight + 1;
904         refLeftTop = Position::kRightBottom;
905     }
906
907     // Position of the right bottom corner
908     if( rRightBottom == "lefttop" )
909     {
910         right = xPos + width - 1;
911         bottom = yPos + height - 1;
912         refRightBottom = Position::kLeftTop;
913     }
914     else if( rRightBottom == "righttop" )
915     {
916         right = xPos + width - boxWidth;
917         bottom = yPos + height - 1;
918         refRightBottom = Position::kRightTop;
919     }
920     else if( rRightBottom == "leftbottom" )
921     {
922         right = xPos + width - 1;
923         bottom = yPos + height - boxHeight;
924         refRightBottom = Position::kLeftBottom;
925     }
926     else if( rRightBottom == "rightbottom" )
927     {
928         right = xPos + width - boxWidth;
929         bottom = yPos + height - boxHeight;
930         refRightBottom = Position::kRightBottom;
931     }
932
933     return Position( left, top, right, bottom, rBox, refLeftTop,
934                      refRightBottom );
935 }
936
937
938 GenericFont *Builder::getFont( const string &fontId )
939 {
940     GenericFont *pFont = m_pTheme->getFontById(fontId);
941     if( !pFont && fontId == "defaultfont" )
942     {
943         // Get the resource path and try to load the default font
944         OSFactory *pOSFactory = OSFactory::instance( getIntf() );
945         const list<string> &resPath = pOSFactory->getResourcePath();
946         const string &sep = pOSFactory->getDirSeparator();
947
948         list<string>::const_iterator it;
949         for( it = resPath.begin(); it != resPath.end(); it++ )
950         {
951             string path = (*it) + sep + "fonts" + sep + "FreeSans.ttf";
952             pFont = new FT2Font( getIntf(), path, 12 );
953             if( pFont->init() )
954             {
955                 // Font loaded successfully
956                 m_pTheme->m_fonts["defaultfont"] = GenericFontPtr( pFont );
957                 break;
958             }
959             else
960             {
961                 delete pFont;
962                 pFont = NULL;
963             }
964         }
965         if( !pFont )
966         {
967             msg_Err( getIntf(), "Failed to open the default font" );
968         }
969     }
970     return pFont;
971 }
972
973
974 string Builder::getFilePath( const string &rFileName ) const
975 {
976     OSFactory *pFactory = OSFactory::instance( getIntf() );
977     return m_path + pFactory->getDirSeparator() + rFileName;
978 }
979
980
981
982 Bezier *Builder::getPoints( const char *pTag ) const
983 {
984     vector<float> xBez, yBez;
985     int x, y, n;
986     while( 1 )
987     {
988         if( sscanf( pTag, "(%d,%d)%n", &x, &y, &n ) < 1 )
989         {
990             return NULL;
991         }
992 #if 0
993         if( x < 0 || y < 0 )
994         {
995             msg_Err( getIntf(),
996                      "Slider points cannot have negative coordinates!" );
997             return NULL;
998         }
999 #endif
1000         xBez.push_back( x );
1001         yBez.push_back( y );
1002         pTag += n;
1003         if( *pTag == '\0' )
1004         {
1005             break;
1006         }
1007         if( *(pTag++) != ',' )
1008         {
1009             return NULL;
1010         }
1011     }
1012
1013     // Create the Bezier curve
1014     return new Bezier( getIntf(), xBez, yBez );
1015 }
1016