]> git.sesse.net Git - vlc/blob - modules/gui/skins2/parser/builder.cpp
ceba8c0c5ddc48856b6b1e2b50c6d6466e4098b8
[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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include <string.h>
26 #include "builder.hpp"
27 #include "builder_data.hpp"
28 #include "interpreter.hpp"
29 #include "skin_parser.hpp"
30 #include "../src/file_bitmap.hpp"
31 #include "../src/os_factory.hpp"
32 #include "../src/generic_bitmap.hpp"
33 #include "../src/top_window.hpp"
34 #include "../src/anchor.hpp"
35 #include "../src/bitmap_font.hpp"
36 #include "../src/ft2_font.hpp"
37 #include "../src/ini_file.hpp"
38 #include "../src/generic_layout.hpp"
39 #include "../src/popup.hpp"
40 #include "../src/theme.hpp"
41 #include "../commands/cmd_generic.hpp"
42 #include "../controls/ctrl_button.hpp"
43 #include "../controls/ctrl_checkbox.hpp"
44 #include "../controls/ctrl_image.hpp"
45 #include "../controls/ctrl_list.hpp"
46 #include "../controls/ctrl_move.hpp"
47 #include "../controls/ctrl_resize.hpp"
48 #include "../controls/ctrl_slider.hpp"
49 #include "../controls/ctrl_radialslider.hpp"
50 #include "../controls/ctrl_text.hpp"
51 #include "../controls/ctrl_tree.hpp"
52 #include "../controls/ctrl_video.hpp"
53 #include "../utils/bezier.hpp"
54 #include "../utils/position.hpp"
55 #include "../utils/var_bool.hpp"
56 #include "../utils/var_text.hpp"
57
58 #include "vlc_image.h"
59
60
61 Builder::Builder( intf_thread_t *pIntf, const BuilderData &rData,
62                   const string &rPath ):
63     SkinObject( pIntf ), m_rData( rData ), m_path( rPath ), m_pTheme( NULL )
64 {
65     m_pImageHandler = image_HandlerCreate( pIntf );
66 }
67
68 Builder::~Builder()
69 {
70     if( m_pImageHandler ) image_HandlerDelete( m_pImageHandler );
71 }
72
73 CmdGeneric *Builder::parseAction( const string &rAction )
74 {
75     return Interpreter::instance( getIntf() )->parseAction( rAction, m_pTheme );
76 }
77
78
79 // Useful macro
80 #define ADD_OBJECTS( type ) \
81     list<BuilderData::type>::const_iterator it##type; \
82     for( it##type = m_rData.m_list##type.begin(); \
83          it##type != m_rData.m_list##type.end(); it##type++ ) \
84     { \
85         add##type( *it##type ); \
86     }
87
88
89 Theme *Builder::build()
90 {
91     m_pTheme = new Theme( getIntf() );
92     if( m_pTheme == NULL )
93     {
94         return NULL;
95     }
96
97     // Create everything from the data in the XML
98     ADD_OBJECTS( Theme );
99     ADD_OBJECTS( IniFile );
100     ADD_OBJECTS( Bitmap );
101     ADD_OBJECTS( SubBitmap );
102     ADD_OBJECTS( BitmapFont );
103     ADD_OBJECTS( Font );
104     ADD_OBJECTS( Window );
105     // XXX: PopupMenus are created after the windows, so that the Win32Factory
106     // (at least) can give a valid window handle to the OSPopup objects
107     ADD_OBJECTS( PopupMenu );
108     ADD_OBJECTS( Layout );
109     ADD_OBJECTS( Anchor );
110     ADD_OBJECTS( Button );
111     ADD_OBJECTS( Checkbox );
112     ADD_OBJECTS( Image );
113     ADD_OBJECTS( Text );
114     ADD_OBJECTS( RadialSlider );
115     ADD_OBJECTS( Slider );
116     ADD_OBJECTS( List );
117     ADD_OBJECTS( Tree );
118     ADD_OBJECTS( Video );
119     // MenuItems must be created after all the rest, so that the IDs of the
120     // other elements exist and can be parsed in the actions
121     ADD_OBJECTS( MenuItem );
122     ADD_OBJECTS( MenuSeparator );
123
124     return m_pTheme;
125 }
126
127
128 // Macro to get a bitmap by its ID in the builder
129 #define GET_BMP( pBmp, id ) \
130     if( id != "none" ) \
131     { \
132         pBmp = m_pTheme->getBitmapById(id); \
133         if( pBmp == NULL ) \
134         { \
135             msg_Err( getIntf(), "unknown bitmap id: %s", id.c_str() ); \
136             return; \
137         } \
138     }
139
140 void Builder::addTheme( const BuilderData::Theme &rData )
141 {
142     WindowManager &rManager = m_pTheme->getWindowManager();
143     rManager.setMagnetValue( rData.m_magnet );
144     rManager.setAlphaValue( rData.m_alpha );
145     rManager.setMoveAlphaValue( rData.m_moveAlpha );
146     GenericFont *pFont = getFont( rData.m_tooltipfont );
147     if( pFont )
148     {
149         rManager.createTooltip( *pFont );
150     }
151     else
152     {
153         msg_Warn( getIntf(), "invalid tooltip font: %s",
154                   rData.m_tooltipfont.c_str() );
155     }
156 }
157
158
159 void Builder::addIniFile( const BuilderData::IniFile &rData )
160 {
161     // Parse the INI file
162     IniFile iniFile( getIntf(), rData.m_id, getFilePath( rData.m_file ) );
163     iniFile.parseFile();
164 }
165
166
167 void Builder::addBitmap( const BuilderData::Bitmap &rData )
168 {
169     GenericBitmap *pBmp =
170         new FileBitmap( getIntf(), m_pImageHandler,
171                         getFilePath( rData.m_fileName ), rData.m_alphaColor,
172                         rData.m_nbFrames, rData.m_fps );
173     if( !pBmp->getData() )
174     {
175         // Invalid bitmap
176         delete pBmp;
177         return;
178     }
179     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
180 }
181
182
183 void Builder::addSubBitmap( const BuilderData::SubBitmap &rData )
184 {
185     if( m_pTheme->m_bitmaps.find( rData.m_id ) != m_pTheme->m_bitmaps.end() )
186     {
187         msg_Dbg( getIntf(), "bitmap %s already exists", rData.m_id.c_str() );
188         return;
189     }
190
191     // Get the parent bitmap
192     GenericBitmap *pParentBmp = NULL;
193     GET_BMP( pParentBmp, rData.m_parent );
194
195     // Copy a region of the parent bitmap to the new one
196     BitmapImpl *pBmp =
197         new BitmapImpl( getIntf(), rData.m_width, rData.m_height,
198                         rData.m_nbFrames, rData.m_fps );
199     bool res = pBmp->drawBitmap( *pParentBmp, rData.m_x, rData.m_y, 0, 0,
200                                  rData.m_width, rData.m_height );
201     if( !res )
202     {
203         // Invalid sub-bitmap
204         delete pBmp;
205         msg_Warn( getIntf(), "sub-bitmap %s ignored", rData.m_id.c_str() );
206         return;
207     }
208     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
209 }
210
211
212 void Builder::addBitmapFont( const BuilderData::BitmapFont &rData )
213 {
214     if( m_pTheme->m_fonts.find( rData.m_id ) != m_pTheme->m_fonts.end() )
215     {
216         msg_Dbg( getIntf(), "font %s already exists", rData.m_id.c_str() );
217         return;
218     }
219
220     GenericBitmap *pBmp =
221         new FileBitmap( getIntf(), m_pImageHandler,
222                         getFilePath( rData.m_file ), 0 );
223     if( !pBmp->getData() )
224     {
225         // Invalid bitmap
226         delete pBmp;
227         return;
228     }
229
230     m_pTheme->m_bitmaps[rData.m_id] = GenericBitmapPtr( pBmp );
231
232     GenericFont *pFont = new BitmapFont( getIntf(), *pBmp, rData.m_type );
233     if( pFont->init() )
234     {
235         m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
236     }
237     else
238     {
239         delete pFont;
240     }
241 }
242
243
244 void Builder::addFont( const BuilderData::Font &rData )
245 {
246     // Try to load the font from the theme directory
247     GenericFont *pFont = new FT2Font( getIntf(),
248                                       getFilePath( rData.m_fontFile ),
249                                       rData.m_size );
250     if( pFont->init() )
251     {
252         m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
253     }
254     else
255     {
256         delete pFont;
257
258         // Font not found; try in the resource path
259         OSFactory *pOSFactory = OSFactory::instance( getIntf() );
260         const list<string> &resPath = pOSFactory->getResourcePath();
261         const string &sep = pOSFactory->getDirSeparator();
262
263         list<string>::const_iterator it;
264         for( it = resPath.begin(); it != resPath.end(); it++ )
265         {
266             string path = (*it) + sep + "fonts" + sep + rData.m_fontFile;
267             pFont = new FT2Font( getIntf(), path, rData.m_size );
268             if( pFont->init() )
269             {
270                 // Font loaded successfully
271                 m_pTheme->m_fonts[rData.m_id] = GenericFontPtr( pFont );
272                 break;
273             }
274             else
275             {
276                 delete pFont;
277             }
278         }
279     }
280 }
281
282
283 void Builder::addPopupMenu( const BuilderData::PopupMenu &rData )
284 {
285     Popup *pPopup = new Popup( getIntf(), m_pTheme->getWindowManager() );
286
287     m_pTheme->m_popups[rData.m_id] = PopupPtr( pPopup );
288 }
289
290
291 void Builder::addMenuItem( const BuilderData::MenuItem &rData )
292 {
293     Popup *pPopup = m_pTheme->getPopupById( rData.m_popupId );
294     if( pPopup == NULL )
295     {
296         msg_Err( getIntf(), "unknown popup id: %s", rData.m_popupId.c_str() );
297         return;
298     }
299
300     CmdGeneric *pCommand = parseAction( rData.m_action );
301     if( pCommand == NULL )
302     {
303         msg_Err( getIntf(), "invalid action: %s", rData.m_action.c_str() );
304         return;
305     }
306
307     pPopup->addItem( rData.m_label, *pCommand, rData.m_pos );
308 }
309
310
311 void Builder::addMenuSeparator( const BuilderData::MenuSeparator &rData )
312 {
313     Popup *pPopup = m_pTheme->getPopupById( rData.m_popupId );
314     if( pPopup == NULL )
315     {
316         msg_Err( getIntf(), "unknown popup id: %s", rData.m_popupId.c_str() );
317         return;
318     }
319
320     pPopup->addSeparator( rData.m_pos );
321 }
322
323
324 void Builder::addWindow( const BuilderData::Window &rData )
325 {
326     TopWindow *pWin =
327         new TopWindow( getIntf(), rData.m_xPos, rData.m_yPos,
328                        m_pTheme->getWindowManager(),
329                        rData.m_dragDrop, rData.m_playOnDrop,
330                        rData.m_visible );
331
332     m_pTheme->m_windows[rData.m_id] = TopWindowPtr( pWin );
333 }
334
335
336 void Builder::addLayout( const BuilderData::Layout &rData )
337 {
338     TopWindow *pWin = m_pTheme->getWindowById( rData.m_windowId );
339     if( pWin == NULL )
340     {
341         msg_Err( getIntf(), "unknown window id: %s", rData.m_windowId.c_str() );
342         return;
343     }
344
345     int minWidth = rData.m_minWidth != -1 ? rData.m_minWidth : rData.m_width;
346     int maxWidth = rData.m_maxWidth != -1 ? rData.m_maxWidth : rData.m_width;
347     int minHeight = rData.m_minHeight != -1 ? rData.m_minHeight :
348                     rData.m_height;
349     int maxHeight = rData.m_maxHeight != -1 ? rData.m_maxHeight :
350                     rData.m_height;
351     GenericLayout *pLayout = new GenericLayout( getIntf(), rData.m_width,
352                                                 rData.m_height,
353                                                 minWidth, maxWidth, minHeight,
354                                                 maxHeight );
355     m_pTheme->m_layouts[rData.m_id] = GenericLayoutPtr( pLayout );
356
357     // Attach the layout to its window
358     m_pTheme->getWindowManager().addLayout( *pWin, *pLayout );
359 }
360
361
362 void Builder::addAnchor( const BuilderData::Anchor &rData )
363 {
364     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
365     if( pLayout == NULL )
366     {
367         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
368         return;
369     }
370
371     Bezier *pCurve = getPoints( rData.m_points.c_str() );
372     if( pCurve == NULL )
373     {
374         msg_Err( getIntf(), "invalid format in tag points=\"%s\"",
375                  rData.m_points.c_str() );
376         return;
377     }
378     m_pTheme->m_curves.push_back( BezierPtr( pCurve ) );
379
380     Anchor *pAnc = new Anchor( getIntf(), rData.m_xPos, rData.m_yPos,
381                                rData.m_range, rData.m_priority,
382                                *pCurve, *pLayout );
383     pLayout->addAnchor( pAnc );
384 }
385
386
387 void Builder::addButton( const BuilderData::Button &rData )
388 {
389     // Get the bitmaps of the button
390     GenericBitmap *pBmpUp = NULL;
391     GET_BMP( pBmpUp, rData.m_upId );
392
393     GenericBitmap *pBmpDown = pBmpUp;
394     GET_BMP( pBmpDown, rData.m_downId );
395
396     GenericBitmap *pBmpOver = pBmpUp;
397     GET_BMP( pBmpOver, rData.m_overId );
398
399     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
400     if( pLayout == NULL )
401     {
402         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
403         return;
404     }
405
406     CmdGeneric *pCommand = parseAction( rData.m_actionId );
407     if( pCommand == NULL )
408     {
409         msg_Err( getIntf(), "invalid action: %s", rData.m_actionId.c_str() );
410         return;
411     }
412
413     // Get the visibility variable
414     // XXX check when it is null
415     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
416     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
417
418     CtrlButton *pButton = new CtrlButton( getIntf(), *pBmpUp, *pBmpOver,
419         *pBmpDown, *pCommand, UString( getIntf(), rData.m_tooltip.c_str() ),
420         UString( getIntf(), rData.m_help.c_str() ), pVisible );
421
422     // Compute the position of the control
423     // XXX (we suppose all the images have the same size...)
424     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
425                                        rData.m_xPos, rData.m_yPos,
426                                        pBmpUp->getWidth(),
427                                        pBmpUp->getHeight(), *pLayout );
428
429     pLayout->addControl( pButton, pos, rData.m_layer );
430
431     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pButton );
432 }
433
434
435 void Builder::addCheckbox( const BuilderData::Checkbox &rData )
436 {
437     // Get the bitmaps of the checkbox
438     GenericBitmap *pBmpUp1 = NULL;
439     GET_BMP( pBmpUp1, rData.m_up1Id );
440
441     GenericBitmap *pBmpDown1 = pBmpUp1;
442     GET_BMP( pBmpDown1, rData.m_down1Id );
443
444     GenericBitmap *pBmpOver1 = pBmpUp1;
445     GET_BMP( pBmpOver1, rData.m_over1Id );
446
447     GenericBitmap *pBmpUp2 = NULL;
448     GET_BMP( pBmpUp2, rData.m_up2Id );
449
450     GenericBitmap *pBmpDown2 = pBmpUp2;
451     GET_BMP( pBmpDown2, rData.m_down2Id );
452
453     GenericBitmap *pBmpOver2 = pBmpUp2;
454     GET_BMP( pBmpOver2, rData.m_over2Id );
455
456     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
457     if( pLayout == NULL )
458     {
459         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
460         return;
461     }
462
463     CmdGeneric *pCommand1 = parseAction( rData.m_action1 );
464     if( pCommand1 == NULL )
465     {
466         msg_Err( getIntf(), "invalid action: %s", rData.m_action1.c_str() );
467         return;
468     }
469
470     CmdGeneric *pCommand2 = parseAction( rData.m_action2 );
471     if( pCommand2 == NULL )
472     {
473         msg_Err( getIntf(), "invalid action: %s", rData.m_action2.c_str() );
474         return;
475     }
476
477     // Get the state variable
478     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
479     VarBool *pVar = pInterpreter->getVarBool( rData.m_state, m_pTheme );
480     if( pVar == NULL )
481     {
482         // TODO: default state
483         return;
484     }
485
486     // Get the visibility variable
487     // XXX check when it is null
488     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
489
490     // Create the control
491     CtrlCheckbox *pCheckbox = new CtrlCheckbox( getIntf(), *pBmpUp1,
492         *pBmpOver1, *pBmpDown1, *pBmpUp2, *pBmpOver2, *pBmpDown2, *pCommand1,
493         *pCommand2, UString( getIntf(), rData.m_tooltip1.c_str() ),
494         UString( getIntf(), rData.m_tooltip2.c_str() ), *pVar,
495         UString( getIntf(), rData.m_help.c_str() ), pVisible );
496
497     // Compute the position of the control
498     // XXX (we suppose all the images have the same size...)
499     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
500                                        rData.m_xPos, rData.m_yPos,
501                                        pBmpUp1->getWidth(),
502                                        pBmpUp1->getHeight(), *pLayout );
503
504     pLayout->addControl( pCheckbox, pos, rData.m_layer );
505
506     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pCheckbox );
507 }
508
509
510 void Builder::addImage( const BuilderData::Image &rData )
511 {
512     GenericBitmap *pBmp = NULL;
513     GET_BMP( pBmp, rData.m_bmpId );
514
515     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
516     if( pLayout == NULL )
517     {
518         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
519         return;
520     }
521
522     TopWindow *pWindow = m_pTheme->getWindowById( rData.m_windowId );
523     if( pWindow == NULL )
524     {
525         msg_Err( getIntf(), "unknown window id: %s", rData.m_windowId.c_str() );
526         return;
527     }
528
529     CmdGeneric *pCommand = parseAction( rData.m_action2Id );
530     if( pCommand == NULL )
531     {
532         msg_Err( getIntf(), "invalid action: %s", rData.m_action2Id.c_str() );
533         return;
534     }
535
536     // Get the visibility variable
537     // XXX check when it is null
538     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
539     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
540
541     CtrlImage::resize_t resizeMethod =
542         (rData.m_resize == "scale" ? CtrlImage::kScale : CtrlImage::kMosaic);
543     CtrlImage *pImage = new CtrlImage( getIntf(), *pBmp, *pCommand,
544         resizeMethod, UString( getIntf(), rData.m_help.c_str() ), pVisible );
545
546     // Compute the position of the control
547     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
548                                        rData.m_xPos,
549                                        rData.m_yPos, pBmp->getWidth(),
550                                        pBmp->getHeight(), *pLayout );
551
552     // XXX: test to be changed! XXX
553     if( rData.m_actionId == "move" )
554     {
555         CtrlMove *pMove = new CtrlMove( getIntf(), m_pTheme->getWindowManager(),
556              *pImage, *pWindow, UString( getIntf(), rData.m_help.c_str() ),
557              pVisible );
558         pLayout->addControl( pMove, pos, rData.m_layer );
559     }
560     else if( rData.m_actionId == "resizeS" )
561     {
562         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
563                 UString( getIntf(), rData.m_help.c_str() ), pVisible,
564                 CtrlResize::kResizeS );
565         pLayout->addControl( pResize, pos, rData.m_layer );
566     }
567     else if( rData.m_actionId == "resizeE" )
568     {
569         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
570                 UString( getIntf(), rData.m_help.c_str() ), pVisible,
571                 CtrlResize::kResizeE );
572         pLayout->addControl( pResize, pos, rData.m_layer );
573     }
574     else if( rData.m_actionId == "resizeSE" )
575     {
576         CtrlResize *pResize = new CtrlResize( getIntf(), *pImage, *pLayout,
577                 UString( getIntf(), rData.m_help.c_str() ), pVisible,
578                 CtrlResize::kResizeSE );
579         pLayout->addControl( pResize, pos, rData.m_layer );
580     }
581     else
582     {
583         pLayout->addControl( pImage, pos, rData.m_layer );
584     }
585
586     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pImage );
587 }
588
589
590 void Builder::addText( const BuilderData::Text &rData )
591 {
592     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
593     if( pLayout == NULL )
594     {
595         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
596         return;
597     }
598
599     GenericFont *pFont = getFont( rData.m_fontId );
600     if( pFont == NULL )
601     {
602         msg_Err( getIntf(), "unknown font id: %s", rData.m_fontId.c_str() );
603         return;
604     }
605
606     // Convert the scrolling mode
607     CtrlText::Scrolling_t scrolling;
608     if( rData.m_scrolling == "auto" )
609         scrolling = CtrlText::kAutomatic;
610     else if( rData.m_scrolling == "autooff" )
611         scrolling = CtrlText::kAutomaticOff;
612     else if( rData.m_scrolling == "manual" )
613         scrolling = CtrlText::kManual;
614     else if( rData.m_scrolling == "none" )
615         scrolling = CtrlText::kNone;
616     else
617     {
618         msg_Err( getIntf(), "invalid scrolling mode: %s",
619                  rData.m_scrolling.c_str() );
620         return;
621     }
622
623     // Convert the alignment
624     CtrlText::Align_t alignment;
625     if( rData.m_alignment == "left" )
626         alignment = CtrlText::kLeft;
627     else if( rData.m_alignment == "center" || rData.m_alignment == "centre" )
628         alignment = CtrlText::kCenter;
629     else if( rData.m_alignment == "right" )
630         alignment = CtrlText::kRight;
631     else
632     {
633         msg_Err( getIntf(), "invalid alignment: %s",
634                  rData.m_alignment.c_str() );
635         return;
636     }
637
638     // Create a text variable
639     VarText *pVar = new VarText( getIntf() );
640     m_pTheme->m_vars.push_back( VariablePtr( pVar ) );
641
642     // Get the visibility variable
643     // XXX check when it is null
644     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
645     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
646
647     CtrlText *pText = new CtrlText( getIntf(), *pVar, *pFont,
648         UString( getIntf(), rData.m_help.c_str() ), rData.m_color, pVisible,
649         scrolling, alignment );
650
651     int height = pFont->getSize();
652
653     // Compute the position of the control
654     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
655                                        rData.m_xPos, rData.m_yPos,
656                                        rData.m_width, height,
657                                        *pLayout );
658
659     pLayout->addControl( pText, pos, rData.m_layer );
660
661     // Set the text of the control
662     UString msg( getIntf(), rData.m_text.c_str() );
663     pVar->set( msg );
664
665     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pText );
666 }
667
668
669 void Builder::addRadialSlider( const BuilderData::RadialSlider &rData )
670 {
671     // Get the bitmaps of the slider
672     GenericBitmap *pSeq = NULL;
673     GET_BMP( pSeq, rData.m_sequence );
674
675     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
676     if( pLayout == NULL )
677     {
678         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
679         return;
680     }
681
682     // Get the variable associated to the slider
683     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
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     // Get the visibility variable
692     // XXX check when it is null
693     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
694
695     // Create the control
696     CtrlRadialSlider *pRadial =
697         new CtrlRadialSlider( getIntf(), *pSeq, rData.m_nbImages, *pVar,
698                               rData.m_minAngle, rData.m_maxAngle,
699                               UString( getIntf(), rData.m_help.c_str() ),
700                               pVisible );
701
702     // XXX: resizing is not supported
703     // Compute the position of the control
704     const Position pos =
705         makePosition( rData.m_leftTop, rData.m_rightBottom, rData.m_xPos,
706                       rData.m_yPos, pSeq->getWidth(),
707                       pSeq->getHeight() / rData.m_nbImages, *pLayout );
708
709     pLayout->addControl( pRadial, pos, rData.m_layer );
710
711     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pRadial );
712 }
713
714
715 void Builder::addSlider( const BuilderData::Slider &rData )
716 {
717     // Add the background first, so that we will still have something almost
718     // functional if the cursor cannot be created properly (this happens for
719     // some winamp2 skins, where the images of the cursor are not always
720     // present)
721
722     // Get the bitmaps of the background
723     GenericBitmap *pBgImage = NULL;
724     if( rData.m_imageId != "none" )
725     {
726         GET_BMP( pBgImage, rData.m_imageId );
727     }
728
729     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
730     if( pLayout == NULL )
731     {
732         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
733         return;
734     }
735
736     Bezier *pCurve = getPoints( rData.m_points.c_str() );
737     if( pCurve == NULL )
738     {
739         msg_Err( getIntf(), "invalid format in tag points=\"%s\"",
740                  rData.m_points.c_str() );
741         return;
742     }
743     m_pTheme->m_curves.push_back( BezierPtr( pCurve ) );
744
745     // Get the visibility variable
746     // XXX check when it is null
747     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
748     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
749
750     // Get the variable associated to the slider
751     VarPercent *pVar = pInterpreter->getVarPercent( rData.m_value, m_pTheme );
752     if( pVar == NULL )
753     {
754         msg_Err( getIntf(), "unknown slider value: %s", rData.m_value.c_str() );
755         return;
756     }
757
758     // Create the background control
759     CtrlSliderBg *pBackground = new CtrlSliderBg( getIntf(),
760         *pCurve, *pVar, rData.m_thickness, pBgImage, rData.m_nbHoriz,
761         rData.m_nbVert, rData.m_padHoriz, rData.m_padVert,
762         pVisible, UString( getIntf(), rData.m_help.c_str() ) );
763
764     // Compute the position of the control
765     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
766                                        rData.m_xPos, rData.m_yPos,
767                                        pCurve->getWidth(), pCurve->getHeight(),
768                                        *pLayout );
769
770     pLayout->addControl( pBackground, pos, rData.m_layer );
771
772     m_pTheme->m_controls[rData.m_id + "_bg"] = CtrlGenericPtr( pBackground );
773
774     // Get the bitmaps of the cursor
775     GenericBitmap *pBmpUp = NULL;
776     GET_BMP( pBmpUp, rData.m_upId );
777
778     GenericBitmap *pBmpDown = pBmpUp;
779     GET_BMP( pBmpDown, rData.m_downId );
780
781     GenericBitmap *pBmpOver = pBmpUp;
782     GET_BMP( pBmpOver, rData.m_overId );
783
784     // Create the cursor control
785     CtrlSliderCursor *pCursor = new CtrlSliderCursor( getIntf(), *pBmpUp,
786         *pBmpOver, *pBmpDown, *pCurve, *pVar, pVisible,
787         UString( getIntf(), rData.m_tooltip.c_str() ),
788         UString( getIntf(), rData.m_help.c_str() ) );
789
790     pLayout->addControl( pCursor, pos, rData.m_layer );
791
792     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pCursor );
793
794     // Associate the cursor to the background
795     pBackground->associateCursor( *pCursor );
796 }
797
798
799 void Builder::addList( const BuilderData::List &rData )
800 {
801     // Get the background bitmap, if any
802     GenericBitmap *pBgBmp = NULL;
803     GET_BMP( pBgBmp, rData.m_bgImageId );
804
805     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
806     if( pLayout == NULL )
807     {
808         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
809         return;
810     }
811
812     GenericFont *pFont = getFont( rData.m_fontId );
813     if( pFont == NULL )
814     {
815         msg_Err( getIntf(), "unknown font id: %s", rData.m_fontId.c_str() );
816         return;
817     }
818
819     // Get the list variable
820     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
821     VarList *pVar = pInterpreter->getVarList( rData.m_var, m_pTheme );
822     if( pVar == NULL )
823     {
824         msg_Err( getIntf(), "no such list variable: %s", rData.m_var.c_str() );
825         return;
826     }
827
828     // Get the visibility variable
829     // XXX check when it is null
830     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
831
832     // Get the color values
833     uint32_t fgColor = getColor( rData.m_fgColor );
834     uint32_t playColor = getColor( rData.m_playColor );
835     uint32_t bgColor1 = getColor( rData.m_bgColor1 );
836     uint32_t bgColor2 = getColor( rData.m_bgColor2 );
837     uint32_t selColor = getColor( rData.m_selColor );
838
839     // Create the list control
840     CtrlList *pList = new CtrlList( getIntf(), *pVar, *pFont, pBgBmp,
841        fgColor, playColor, bgColor1, bgColor2, selColor,
842        UString( getIntf(), rData.m_help.c_str() ), pVisible );
843
844     // Compute the position of the control
845     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
846                                        rData.m_xPos, rData.m_yPos,
847                                        rData.m_width, rData.m_height,
848                                        *pLayout );
849
850     pLayout->addControl( pList, pos, rData.m_layer );
851
852     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pList );
853 }
854
855 void Builder::addTree( const BuilderData::Tree &rData )
856 {
857     // Get the bitmaps, if any
858     GenericBitmap *pBgBmp = NULL;
859     GenericBitmap *pItemBmp = NULL;
860     GenericBitmap *pOpenBmp = NULL;
861     GenericBitmap *pClosedBmp = NULL;
862     GET_BMP( pBgBmp, rData.m_bgImageId );
863     GET_BMP( pItemBmp, rData.m_itemImageId );
864     GET_BMP( pOpenBmp, rData.m_openImageId );
865     GET_BMP( pClosedBmp, rData.m_closedImageId );
866
867     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
868     if( pLayout == NULL )
869     {
870         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
871         return;
872     }
873
874     GenericFont *pFont = getFont( rData.m_fontId );
875     if( pFont == NULL )
876     {
877         msg_Err( getIntf(), "unknown font id: %s", rData.m_fontId.c_str() );
878         return;
879     }
880
881     // Get the list variable
882     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
883     VarTree *pVar = pInterpreter->getVarTree( rData.m_var, m_pTheme );
884     if( pVar == NULL )
885     {
886         msg_Err( getIntf(), "no such list variable: %s", rData.m_var.c_str() );
887         return;
888     }
889
890     // Get the visibility variable
891     // XXX check when it is null
892     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
893     VarBool *pFlat = pInterpreter->getVarBool( rData.m_flat, m_pTheme );
894
895     // Get the color values
896     uint32_t fgColor = getColor( rData.m_fgColor );
897     uint32_t playColor = getColor( rData.m_playColor );
898     uint32_t bgColor1 = getColor( rData.m_bgColor1 );
899     uint32_t bgColor2 = getColor( rData.m_bgColor2 );
900     uint32_t selColor = getColor( rData.m_selColor );
901
902     // Create the list control
903     CtrlTree *pTree = new CtrlTree( getIntf(), *pVar, *pFont, pBgBmp,
904        pItemBmp, pOpenBmp, pClosedBmp,
905        fgColor, playColor, bgColor1, bgColor2, selColor,
906        UString( getIntf(), rData.m_help.c_str() ), pVisible, pFlat );
907
908     // Compute the position of the control
909     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
910                                        rData.m_xPos, rData.m_yPos,
911                                        rData.m_width, rData.m_height,
912                                        *pLayout );
913
914     pLayout->addControl( pTree, pos, rData.m_layer );
915
916     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pTree );
917 }
918
919 void Builder::addVideo( const BuilderData::Video &rData )
920 {
921     GenericLayout *pLayout = m_pTheme->getLayoutById( rData.m_layoutId );
922     if( pLayout == NULL )
923     {
924         msg_Err( getIntf(), "unknown layout id: %s", rData.m_layoutId.c_str() );
925         return;
926     }
927
928     // Get the visibility variable
929     // XXX check when it is null
930     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
931     VarBool *pVisible = pInterpreter->getVarBool( rData.m_visible, m_pTheme );
932
933     CtrlVideo *pVideo = new CtrlVideo( getIntf(), *pLayout,
934         rData.m_autoResize, UString( getIntf(), rData.m_help.c_str() ),
935         pVisible );
936
937     // Compute the position of the control
938     const Position pos = makePosition( rData.m_leftTop, rData.m_rightBottom,
939                                        rData.m_xPos, rData.m_yPos,
940                                        rData.m_width, rData.m_height,
941                                        *pLayout );
942
943     pLayout->addControl( pVideo, pos, rData.m_layer );
944
945     m_pTheme->m_controls[rData.m_id] = CtrlGenericPtr( pVideo );
946 }
947
948
949 const Position Builder::makePosition( const string &rLeftTop,
950                                       const string &rRightBottom,
951                                       int xPos, int yPos, int width,
952                                       int height, const Box &rBox ) const
953 {
954     int left = 0, top = 0, right = 0, bottom = 0;
955     Position::Ref_t refLeftTop = Position::kLeftTop;
956     Position::Ref_t refRightBottom = Position::kLeftTop;
957
958     int boxWidth = rBox.getWidth();
959     int boxHeight = rBox.getHeight();
960
961     // Position of the left top corner
962     if( rLeftTop == "lefttop" )
963     {
964         left = xPos;
965         top = yPos;
966         refLeftTop = Position::kLeftTop;
967     }
968     else if( rLeftTop == "righttop" )
969     {
970         left = xPos - boxWidth + 1;
971         top = yPos;
972         refLeftTop = Position::kRightTop;
973     }
974     else if( rLeftTop == "leftbottom" )
975     {
976         left = xPos;
977         top = yPos - boxHeight + 1;
978         refLeftTop = Position::kLeftBottom;
979     }
980     else if( rLeftTop == "rightbottom" )
981     {
982         left = xPos - boxWidth + 1;
983         top = yPos - boxHeight + 1;
984         refLeftTop = Position::kRightBottom;
985     }
986
987     // Position of the right bottom corner
988     if( rRightBottom == "lefttop" )
989     {
990         right = xPos + width - 1;
991         bottom = yPos + height - 1;
992         refRightBottom = Position::kLeftTop;
993     }
994     else if( rRightBottom == "righttop" )
995     {
996         right = xPos + width - boxWidth;
997         bottom = yPos + height - 1;
998         refRightBottom = Position::kRightTop;
999     }
1000     else if( rRightBottom == "leftbottom" )
1001     {
1002         right = xPos + width - 1;
1003         bottom = yPos + height - boxHeight;
1004         refRightBottom = Position::kLeftBottom;
1005     }
1006     else if( rRightBottom == "rightbottom" )
1007     {
1008         right = xPos + width - boxWidth;
1009         bottom = yPos + height - boxHeight;
1010         refRightBottom = Position::kRightBottom;
1011     }
1012
1013     return Position( left, top, right, bottom, rBox, refLeftTop,
1014                      refRightBottom );
1015 }
1016
1017
1018 GenericFont *Builder::getFont( const string &fontId )
1019 {
1020     GenericFont *pFont = m_pTheme->getFontById(fontId);
1021     if( !pFont && fontId == "defaultfont" )
1022     {
1023         // Get the resource path and try to load the default font
1024         OSFactory *pOSFactory = OSFactory::instance( getIntf() );
1025         const list<string> &resPath = pOSFactory->getResourcePath();
1026         const string &sep = pOSFactory->getDirSeparator();
1027
1028         list<string>::const_iterator it;
1029         for( it = resPath.begin(); it != resPath.end(); it++ )
1030         {
1031             string path = (*it) + sep + "fonts" + sep + "FreeSans.ttf";
1032             pFont = new FT2Font( getIntf(), path, 12 );
1033             if( pFont->init() )
1034             {
1035                 // Font loaded successfully
1036                 m_pTheme->m_fonts["defaultfont"] = GenericFontPtr( pFont );
1037                 break;
1038             }
1039             else
1040             {
1041                 delete pFont;
1042                 pFont = NULL;
1043             }
1044         }
1045         if( !pFont )
1046         {
1047             msg_Err( getIntf(), "failed to open the default font" );
1048         }
1049     }
1050     return pFont;
1051 }
1052
1053
1054 string Builder::getFilePath( const string &rFileName ) const
1055 {
1056     OSFactory *pFactory = OSFactory::instance( getIntf() );
1057     return m_path + pFactory->getDirSeparator() + rFileName;
1058 }
1059
1060
1061
1062 Bezier *Builder::getPoints( const char *pTag ) const
1063 {
1064     vector<float> xBez, yBez;
1065     int x, y, n;
1066     while( 1 )
1067     {
1068         if( sscanf( pTag, "(%d,%d)%n", &x, &y, &n ) < 1 )
1069         {
1070             return NULL;
1071         }
1072 #if 0
1073         if( x < 0 || y < 0 )
1074         {
1075             msg_Err( getIntf(),
1076                      "Slider points cannot have negative coordinates!" );
1077             return NULL;
1078         }
1079 #endif
1080         xBez.push_back( x );
1081         yBez.push_back( y );
1082         pTag += n;
1083         if( *pTag == '\0' )
1084         {
1085             break;
1086         }
1087         if( *(pTag++) != ',' )
1088         {
1089             return NULL;
1090         }
1091     }
1092
1093     // Create the Bezier curve
1094     return new Bezier( getIntf(), xBez, yBez );
1095 }
1096
1097
1098 uint32_t Builder::getColor( const string &rVal ) const
1099 {
1100     // Check it the value is a registered constant
1101     Interpreter *pInterpreter = Interpreter::instance( getIntf() );
1102     string val = pInterpreter->getConstant( rVal );
1103
1104     // Convert to an int value
1105     return SkinParser::convertColor( val.c_str() );
1106 }
1107