skin_parser.cpp
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:26k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * skin_parser.cpp
  3.  *****************************************************************************
  4.  * Copyright (C) 2004 the VideoLAN team
  5.  * $Id: aa222263f6071a1b46dffaa96e5c9b7b92f1196f $
  6.  *
  7.  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. #include "skin_parser.hpp"
  24. #include "../src/os_factory.hpp"
  25. #include "interpreter.hpp"
  26. #include <math.h>
  27. SkinParser::SkinParser( intf_thread_t *pIntf, const string &rFileName,
  28.                         const string &rPath, bool useDTD, BuilderData *pData ):
  29.     XMLParser( pIntf, rFileName, useDTD ), m_path( rPath), m_pData(pData),
  30.     m_ownData(pData == NULL), m_xOffset( 0 ), m_yOffset( 0 )
  31. {
  32.     // Make sure the data is allocated
  33.     if( m_pData == NULL )
  34.     {
  35.         m_pData = new BuilderData();
  36.     }
  37.     // Special id, we don't want any control to have the same one
  38.     m_idSet.insert( "none" );
  39.     // At the beginning, there is no Panel
  40.     m_panelStack.push_back( "none" );
  41. }
  42. SkinParser::~SkinParser()
  43. {
  44.     if( m_ownData )
  45.     {
  46.         delete m_pData;
  47.     }
  48. }
  49. void SkinParser::handleBeginElement( const string &rName, AttrList_t &attr )
  50. {
  51. #define CheckDefault( a, b ) 
  52.     if( attr.find(a) == attr.end() ) attr[strdup(a)] = strdup(b);
  53. #define RequireDefault( a ) 
  54.     if( attr.find(a) == attr.end() ) 
  55.     { 
  56.         msg_Err( getIntf(), "bad theme (element: %s, missing attribute: %s)", 
  57.                  rName.c_str(), a ); 
  58.         m_errors = true; return; 
  59.     }
  60.     if( rName == "Include" )
  61.     {
  62.         RequireDefault( "file" );
  63.         OSFactory *pFactory = OSFactory::instance( getIntf() );
  64.         string fullPath = m_path + pFactory->getDirSeparator() + attr["file"];
  65.         msg_Dbg( getIntf(), "opening included XML file: %s", fullPath.c_str() );
  66.         // FIXME: We do not use the DTD to validate the included XML file,
  67.         // as the parser seems to dislike it otherwise...
  68.         SkinParser subParser( getIntf(), fullPath.c_str(), false, m_pData );
  69.         subParser.parse();
  70.     }
  71.     else if( rName == "IniFile" )
  72.     {
  73.         RequireDefault( "id" );
  74.         RequireDefault( "file" );
  75.         const BuilderData::IniFile iniFile( uniqueId( attr["id"] ),
  76.                 attr["file"] );
  77.         m_pData->m_listIniFile.push_back( iniFile );
  78.     }
  79.     else if( rName == "Anchor" )
  80.     {
  81.         RequireDefault( "priority" );
  82.         CheckDefault( "x", "0" );
  83.         CheckDefault( "y", "0" );
  84.         CheckDefault( "lefttop", "lefttop" );
  85.         CheckDefault( "points", "(0,0)" );
  86.         CheckDefault( "range", "10" );
  87.         const BuilderData::Anchor anchor( atoi( attr["x"] ) + m_xOffset,
  88.                 atoi( attr["y"] ) + m_yOffset, attr["lefttop"],
  89.                 atoi( attr["range"] ), atoi( attr["priority"] ),
  90.                 attr["points"], m_curLayoutId );
  91.         m_pData->m_listAnchor.push_back( anchor );
  92.     }
  93.     else if( rName == "Bitmap" )
  94.     {
  95.         RequireDefault( "id" );
  96.         RequireDefault( "file" );
  97.         RequireDefault( "alphacolor" );
  98.         CheckDefault( "nbframes", "1" );
  99.         CheckDefault( "fps", "4" );
  100.         m_curBitmapId = uniqueId( attr["id"] );
  101.         const BuilderData::Bitmap bitmap( m_curBitmapId,
  102.                 attr["file"], convertColor( attr["alphacolor"] ),
  103.                 atoi( attr["nbframes"] ), atoi( attr["fps"] ) );
  104.         m_pData->m_listBitmap.push_back( bitmap );
  105.     }
  106.     else if( rName == "SubBitmap" )
  107.     {
  108.         RequireDefault( "id" );
  109.         RequireDefault( "x" );
  110.         RequireDefault( "y" );
  111.         RequireDefault( "width" );
  112.         RequireDefault( "height" );
  113.         CheckDefault( "nbframes", "1" );
  114.         CheckDefault( "fps", "4" );
  115.         const BuilderData::SubBitmap bitmap( uniqueId( attr["id"] ),
  116.                 m_curBitmapId, atoi( attr["x"] ), atoi( attr["y"] ),
  117.                 atoi( attr["width"] ), atoi( attr["height"] ),
  118.                 atoi( attr["nbframes"] ), atoi( attr["fps"] ) );
  119.         m_pData->m_listSubBitmap.push_back( bitmap );
  120.     }
  121.     else if( rName == "BitmapFont" )
  122.     {
  123.         RequireDefault( "id" );
  124.         RequireDefault( "file" );
  125.         CheckDefault( "type", "digits" );
  126.         const BuilderData::BitmapFont font( uniqueId( attr["id"] ),
  127.                 attr["file"], attr["type"] );
  128.         m_pData->m_listBitmapFont.push_back( font );
  129.     }
  130.     else if( rName == "PopupMenu" )
  131.     {
  132.         RequireDefault( "id" );
  133.         m_popupPosList.push_back(0);
  134.         m_curPopupId = uniqueId( attr["id"] );
  135.         const BuilderData::PopupMenu popup( m_curPopupId );
  136.         m_pData->m_listPopupMenu.push_back( popup );
  137.     }
  138.     else if( rName == "MenuItem" )
  139.     {
  140.         RequireDefault( "label" );
  141.         CheckDefault( "action", "none" );
  142.         const BuilderData::MenuItem item( attr["label"], attr["action"],
  143.                                           m_popupPosList.back(),
  144.                                           m_curPopupId );
  145.         m_pData->m_listMenuItem.push_back( item );
  146.         m_popupPosList.back()++;
  147.     }
  148.     else if( rName == "MenuSeparator" )
  149.     {
  150.         const BuilderData::MenuSeparator sep( m_popupPosList.back(),
  151.                                               m_curPopupId );
  152.         m_pData->m_listMenuSeparator.push_back( sep );
  153.         m_popupPosList.back()++;
  154.     }
  155.     else if( rName == "Button" )
  156.     {
  157.         RequireDefault( "up" );
  158.         CheckDefault( "id", "none" );
  159.         CheckDefault( "visible", "true" );
  160.         CheckDefault( "x", "0" );
  161.         CheckDefault( "y", "0" );
  162.         CheckDefault( "lefttop", "lefttop" );
  163.         CheckDefault( "rightbottom", "lefttop" );
  164.         CheckDefault( "xkeepratio", "false" );
  165.         CheckDefault( "ykeepratio", "false" );
  166.         CheckDefault( "down", "none" );
  167.         CheckDefault( "over", "none" );
  168.         CheckDefault( "action", "none" );
  169.         CheckDefault( "tooltiptext", "" );
  170.         CheckDefault( "help", "" );
  171.         const BuilderData::Button button( uniqueId( attr["id"] ),
  172.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  173.                 attr["lefttop"], attr["rightbottom"],
  174.                 convertBoolean( attr["xkeepratio"] ),
  175.                 convertBoolean( attr["ykeepratio"] ), attr["visible"],
  176.                 attr["up"], attr["down"], attr["over"], attr["action"],
  177.                 attr["tooltiptext"], attr["help"],
  178.                 m_curLayer, m_curWindowId, m_curLayoutId, m_panelStack.back() );
  179.         m_curLayer++;
  180.         m_pData->m_listButton.push_back( button );
  181.     }
  182.     else if( rName == "Checkbox" )
  183.     {
  184.         RequireDefault( "up1" );
  185.         RequireDefault( "up2" );
  186.         RequireDefault( "state" );
  187.         CheckDefault( "id", "none" );
  188.         CheckDefault( "visible", "true" );
  189.         CheckDefault( "x", "0" );
  190.         CheckDefault( "y", "0" );
  191.         CheckDefault( "lefttop", "lefttop" );
  192.         CheckDefault( "rightbottom", "lefttop" );
  193.         CheckDefault( "xkeepratio", "false" );
  194.         CheckDefault( "ykeepratio", "false" );
  195.         CheckDefault( "down1", "none" );
  196.         CheckDefault( "over1", "none" );
  197.         CheckDefault( "down2", "none" );
  198.         CheckDefault( "over2", "none" );
  199.         CheckDefault( "action1", "none" );
  200.         CheckDefault( "action2", "none" );
  201.         CheckDefault( "tooltiptext1", "" );
  202.         CheckDefault( "tooltiptext2", "" );
  203.         CheckDefault( "help", "" );
  204.         const BuilderData::Checkbox checkbox( uniqueId( attr["id"] ),
  205.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  206.                 attr["lefttop"], attr["rightbottom"],
  207.                 convertBoolean( attr["xkeepratio"] ),
  208.                 convertBoolean( attr["ykeepratio"] ), attr["visible"],
  209.                 attr["up1"], attr["down1"], attr["over1"],
  210.                 attr["up2"], attr["down2"], attr["over2"], attr["state"],
  211.                 attr["action1"], attr["action2"], attr["tooltiptext1"],
  212.                 attr["tooltiptext2"], attr["help"], m_curLayer, m_curWindowId,
  213.                 m_curLayoutId, m_panelStack.back() );
  214.         m_curLayer++;
  215.         m_pData->m_listCheckbox.push_back( checkbox );
  216.     }
  217.     else if( rName == "Font" )
  218.     {
  219.         RequireDefault( "id" );
  220.         RequireDefault( "file" );
  221.         CheckDefault( "size", "12" );
  222.         const BuilderData::Font fontData( uniqueId( attr["id"] ),
  223.                 attr["file"], atoi( attr["size"] ) );
  224.         m_pData->m_listFont.push_back( fontData );
  225.     }
  226.     else if( rName == "Group" )
  227.     {
  228.         CheckDefault( "x", "0" );
  229.         CheckDefault( "y", "0" );
  230.         m_xOffset += atoi( attr["x"] );
  231.         m_yOffset += atoi( attr["y"] );
  232.         m_xOffsetList.push_back( atoi( attr["x"] ) );
  233.         m_yOffsetList.push_back( atoi( attr["y"] ) );
  234.     }
  235.     else if( rName == "Image" )
  236.     {
  237.         RequireDefault( "image" );
  238.         CheckDefault( "id", "none" );
  239.         CheckDefault( "visible", "true" );
  240.         CheckDefault( "x", "0" );
  241.         CheckDefault( "y", "0" );
  242.         CheckDefault( "lefttop", "lefttop" );
  243.         CheckDefault( "rightbottom", "lefttop" );
  244.         CheckDefault( "xkeepratio", "false" );
  245.         CheckDefault( "ykeepratio", "false" );
  246.         CheckDefault( "action", "none" );
  247.         CheckDefault( "action2", "none" );
  248.         CheckDefault( "resize", "mosaic" );
  249.         CheckDefault( "help", "" );
  250.         const BuilderData::Image imageData( uniqueId( attr["id"] ),
  251.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  252.                 attr["lefttop"], attr["rightbottom"],
  253.                 convertBoolean( attr["xkeepratio"] ),
  254.                 convertBoolean( attr["ykeepratio"] ), attr["visible"],
  255.                 attr["image"], attr["action"], attr["action2"], attr["resize"],
  256.                 attr["help"], m_curLayer, m_curWindowId, m_curLayoutId,
  257.                 m_panelStack.back() );
  258.         m_curLayer++;
  259.         m_pData->m_listImage.push_back( imageData );
  260.     }
  261.     else if( rName == "Layout" )
  262.     {
  263.         RequireDefault( "width" );
  264.         RequireDefault( "height" );
  265.         CheckDefault( "id", "none" );
  266.         CheckDefault( "minwidth", "-1" );
  267.         CheckDefault( "maxwidth", "-1" );
  268.         CheckDefault( "minheight", "-1" );
  269.         CheckDefault( "maxheight", "-1" );
  270.         m_curLayoutId = uniqueId( attr["id"] );
  271.         const BuilderData::Layout layout( m_curLayoutId, atoi( attr["width"] ),
  272.                 atoi( attr["height"] ), atoi( attr["minwidth"] ),
  273.                 atoi( attr["maxwidth"] ), atoi( attr["minheight"] ),
  274.                 atoi( attr["maxheight"] ), m_curWindowId );
  275.         m_pData->m_listLayout.push_back( layout );
  276.         m_curLayer = 0;
  277.     }
  278.     else if( rName == "Panel" )
  279.     {
  280.         CheckDefault( "x", "0" );
  281.         CheckDefault( "y", "0" );
  282.         CheckDefault( "lefttop", "lefttop" );
  283.         CheckDefault( "rightbottom", "lefttop" );
  284.         CheckDefault( "xkeepratio", "false" );
  285.         CheckDefault( "ykeepratio", "false" );
  286.         RequireDefault( "width" );
  287.         RequireDefault( "height" );
  288.         string panelId = uniqueId( "none" );
  289.         const BuilderData::Panel panel( panelId,
  290.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  291.                 attr["lefttop"], attr["rightbottom"],
  292.                 convertBoolean( attr["xkeepratio"] ),
  293.                 convertBoolean( attr["ykeepratio"] ),
  294.                 atoi( attr["width"] ), atoi( attr["height" ] ),
  295.                 m_curLayer, m_curWindowId, m_curLayoutId, m_panelStack.back() );
  296.         m_curLayer++;
  297.         m_pData->m_listPanel.push_back( panel );
  298.         // Add the panel to the stack
  299.         m_panelStack.push_back( panelId );
  300.     }
  301.     else if( rName == "Playlist" )
  302.     {
  303.         RequireDefault( "id" );
  304.         RequireDefault( "font" );
  305.         CheckDefault( "visible", "true" );
  306.         CheckDefault( "flat", "true" ); // Only difference here
  307.         CheckDefault( "x", "0" );
  308.         CheckDefault( "y", "0" );
  309.         CheckDefault( "width", "0" );
  310.         CheckDefault( "height", "0" );
  311.         CheckDefault( "lefttop", "lefttop" );
  312.         CheckDefault( "rightbottom", "lefttop" );
  313.         CheckDefault( "xkeepratio", "false" );
  314.         CheckDefault( "ykeepratio", "false" );
  315.         CheckDefault( "bgimage", "none" );
  316.         CheckDefault( "itemimage", "none" );
  317.         CheckDefault( "openimage", "none" );
  318.         CheckDefault( "closedimage", "none" );
  319.         CheckDefault( "fgcolor", "#000000" );
  320.         CheckDefault( "playcolor", "#FF0000" );
  321.         CheckDefault( "bgcolor1", "#FFFFFF" );
  322.         CheckDefault( "bgcolor2", "#FFFFFF" );
  323.         CheckDefault( "selcolor", "#0000FF" );
  324.         CheckDefault( "help", "" );
  325.         m_curTreeId = uniqueId( attr["id"] );
  326.         const BuilderData::Tree treeData( m_curTreeId, atoi( attr["x"] ) +
  327.                 m_xOffset, atoi( attr["y"] ) + m_yOffset, attr["visible"],
  328.                 attr["flat"],
  329.                 atoi( attr["width"]), atoi( attr["height"] ),
  330.                 attr["lefttop"], attr["rightbottom"],
  331.                 convertBoolean( attr["xkeepratio"] ),
  332.                 convertBoolean( attr["ykeepratio"] ),
  333.                 attr["font"], "playtree",
  334.                 attr["bgimage"], attr["itemimage"],
  335.                 attr["openimage"], attr["closedimage"],
  336.                 attr["fgcolor"],
  337.                 attr["playcolor"],
  338.                 attr["bgcolor1"],
  339.                 attr["bgcolor2"],
  340.                 attr["selcolor"], attr["help"],
  341.                 m_curLayer, m_curWindowId, m_curLayoutId, m_panelStack.back() );
  342.         m_curLayer++;
  343.         m_pData->m_listTree.push_back( treeData );
  344.     }
  345.     else if( rName == "Playtree" )
  346.     {
  347.         RequireDefault( "id" );
  348.         RequireDefault( "font" );
  349.         CheckDefault( "visible", "true" );
  350.         CheckDefault( "flat", "false" );
  351.         CheckDefault( "x", "0" );
  352.         CheckDefault( "y", "0" );
  353.         CheckDefault( "width", "0" );
  354.         CheckDefault( "height", "0" );
  355.         CheckDefault( "lefttop", "lefttop" );
  356.         CheckDefault( "rightbottom", "lefttop" );
  357.         CheckDefault( "xkeepratio", "false" );
  358.         CheckDefault( "ykeepratio", "false" );
  359.         CheckDefault( "bgimage", "none" );
  360.         CheckDefault( "itemimage", "none" );
  361.         CheckDefault( "openimage", "none" );
  362.         CheckDefault( "closedimage", "none" );
  363.         CheckDefault( "fgcolor", "#000000" );
  364.         CheckDefault( "playcolor", "#FF0000" );
  365.         CheckDefault( "bgcolor1", "#FFFFFF" );
  366.         CheckDefault( "bgcolor2", "#FFFFFF" );
  367.         CheckDefault( "selcolor", "#0000FF" );
  368.         CheckDefault( "help", "" );
  369.         m_curTreeId = uniqueId( attr["id"] );
  370.         const BuilderData::Tree treeData( m_curTreeId, atoi( attr["x"] ) +
  371.                 m_xOffset, atoi( attr["y"] ) + m_yOffset, attr["visible"],
  372.                 attr["flat"],
  373.                 atoi( attr["width"]), atoi( attr["height"] ),
  374.                 attr["lefttop"], attr["rightbottom"],
  375.                 convertBoolean( attr["xkeepratio"] ),
  376.                 convertBoolean( attr["ykeepratio"] ),
  377.                 attr["font"], "playtree",
  378.                 attr["bgimage"], attr["itemimage"],
  379.                 attr["openimage"], attr["closedimage"],
  380.                 attr["fgcolor"], attr["playcolor"],
  381.                 attr["bgcolor1"], attr["bgcolor2"],
  382.                 attr["selcolor"], attr["help"],
  383.                 m_curLayer, m_curWindowId, m_curLayoutId, m_panelStack.back() );
  384.         m_curLayer++;
  385.         m_pData->m_listTree.push_back( treeData );
  386.     }
  387.     else if( rName == "RadialSlider" )
  388.     {
  389.         RequireDefault( "sequence" );
  390.         RequireDefault( "nbimages" );
  391.         CheckDefault( "id", "none" );
  392.         CheckDefault( "visible", "true" );
  393.         CheckDefault( "x", "0" );
  394.         CheckDefault( "y", "0" );
  395.         CheckDefault( "lefttop", "lefttop" );
  396.         CheckDefault( "rightbottom", "lefttop" );
  397.         CheckDefault( "xkeepratio", "false" );
  398.         CheckDefault( "ykeepratio", "false" );
  399.         CheckDefault( "minangle", "0" );
  400.         CheckDefault( "maxangle", "360" );
  401.         CheckDefault( "value", "none" );
  402.         CheckDefault( "tooltiptext", "" );
  403.         CheckDefault( "help", "" );
  404.         const BuilderData::RadialSlider radial( uniqueId( attr["id"] ),
  405.                 attr["visible"],
  406.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  407.                 attr["lefttop"], attr["rightbottom"],
  408.                 convertBoolean( attr["xkeepratio"] ),
  409.                 convertBoolean( attr["ykeepratio"] ), attr["sequence"],
  410.                 atoi( attr["nbImages"] ), atof( attr["minAngle"] ) * M_PI /180,
  411.                 atof( attr["maxAngle"] ) * M_PI / 180, attr["value"],
  412.                 attr["tooltiptext"], attr["help"], m_curLayer, m_curWindowId,
  413.                 m_curLayoutId, m_panelStack.back() );
  414.         m_curLayer++;
  415.         m_pData->m_listRadialSlider.push_back( radial );
  416.     }
  417.     else if( rName == "Slider" )
  418.     {
  419.         RequireDefault( "up" );
  420.         RequireDefault( "points" );
  421.         CheckDefault( "id", "none" );
  422.         CheckDefault( "visible", "true" );
  423.         CheckDefault( "x", "0" );
  424.         CheckDefault( "y", "0" );
  425.         CheckDefault( "lefttop", "lefttop" );
  426.         CheckDefault( "rightbottom", "lefttop" );
  427.         CheckDefault( "xkeepratio", "false" );
  428.         CheckDefault( "ykeepratio", "false" );
  429.         CheckDefault( "down", "none" );
  430.         CheckDefault( "over", "none" );
  431.         CheckDefault( "thickness", "10" );
  432.         CheckDefault( "value", "none" );
  433.         CheckDefault( "tooltiptext", "" );
  434.         CheckDefault( "help", "" );
  435.         string newValue = attr["value"];
  436.         if( m_curTreeId != "" )
  437.         {
  438.             // Slider associated to a tree
  439.             newValue = "playtree.slider";
  440.         }
  441.         const BuilderData::Slider slider( uniqueId( attr["id"] ),
  442.                 attr["visible"], atoi( attr["x"] ) + m_xOffset,
  443.                 atoi( attr["y"] ) + m_yOffset, attr["lefttop"],
  444.                 attr["rightbottom"], convertBoolean( attr["xkeepratio"] ),
  445.                 convertBoolean( attr["ykeepratio"] ), attr["up"], attr["down"],
  446.                 attr["over"], attr["points"], atoi( attr["thickness"] ),
  447.                 newValue, "none", 0, 0, 0, 0, attr["tooltiptext"],
  448.                 attr["help"], m_curLayer, m_curWindowId, m_curLayoutId,
  449.                 m_panelStack.back() );
  450.         m_curLayer++;
  451.         m_pData->m_listSlider.push_back( slider );
  452.     }
  453.     else if( rName == "SliderBackground" )
  454.     {
  455.         RequireDefault( "image" );
  456.         CheckDefault( "nbhoriz", "1" );
  457.         CheckDefault( "nbvert", "1" );
  458.         CheckDefault( "padhoriz", "0" );
  459.         CheckDefault( "padvert", "0" );
  460.         // Retrieve the current slider data
  461.         BuilderData::Slider &slider = m_pData->m_listSlider.back();
  462.         slider.m_imageId = attr["image"];
  463.         slider.m_nbHoriz = atoi( attr["nbhoriz"] );
  464.         slider.m_nbVert = atoi( attr["nbvert"] );
  465.         slider.m_padHoriz = atoi( attr["padhoriz"] );
  466.         slider.m_padVert = atoi( attr["padvert"] );
  467.     }
  468.     else if( rName == "Text" )
  469.     {
  470.         RequireDefault( "font" );
  471.         CheckDefault( "id", "none" );
  472.         CheckDefault( "visible", "true" );
  473.         CheckDefault( "x", "0" );
  474.         CheckDefault( "y", "0" );
  475.         CheckDefault( "text", "" );
  476.         CheckDefault( "color", "#000000" );
  477.         CheckDefault( "scrolling", "auto" );
  478.         CheckDefault( "alignment", "left" );
  479.         CheckDefault( "width", "0" );
  480.         CheckDefault( "lefttop", "lefttop" );
  481.         CheckDefault( "rightbottom", "lefttop" );
  482.         CheckDefault( "xkeepratio", "false" );
  483.         CheckDefault( "ykeepratio", "false" );
  484.         CheckDefault( "help", "" );
  485.         const BuilderData::Text textData( uniqueId( attr["id"] ),
  486.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  487.                 attr["visible"], attr["font"],
  488.                 attr["text"], atoi( attr["width"] ),
  489.                 attr["lefttop"], attr["rightbottom"],
  490.                 convertBoolean( attr["xkeepratio"] ),
  491.                 convertBoolean( attr["ykeepratio"] ),
  492.                 convertColor( attr["color"] ),
  493.                 attr["scrolling"], attr["alignment"],
  494.                 attr["help"], m_curLayer, m_curWindowId, m_curLayoutId,
  495.                 m_panelStack.back() );
  496.         m_curLayer++;
  497.         m_pData->m_listText.push_back( textData );
  498.     }
  499.     else if( rName == "Theme" )
  500.     {
  501.         RequireDefault( "version" );
  502.         CheckDefault( "tooltipfont", "defaultfont" );
  503.         CheckDefault( "magnet", "15" );
  504.         CheckDefault( "alpha", "255" );
  505.         CheckDefault( "movealpha", "255" );
  506.         // Check the version
  507.         if( strcmp( attr["version"], SKINS_DTD_VERSION ) )
  508.         {
  509.             msg_Err( getIntf(), "bad theme version : %s (you need version %s)",
  510.                      attr["version"], SKINS_DTD_VERSION );
  511.             m_errors = true;
  512.             return;
  513.         }
  514.         const BuilderData::Theme theme( attr["tooltipfont"],
  515.                 atoi( attr["magnet"] ),
  516.                 convertInRange( attr["alpha"], 1, 255, "alpha" ),
  517.                 convertInRange( attr["movealpha"], 1, 255, "movealpha" ) );
  518.         m_pData->m_listTheme.push_back( theme );
  519.     }
  520.     else if( rName == "ThemeInfo" )
  521.     {
  522.         CheckDefault( "name", "" );
  523.         CheckDefault( "author", "" );
  524.         CheckDefault( "email", "" );
  525.         CheckDefault( "website", "" );
  526.         msg_Info( getIntf(), "skin: %s  author: %s", attr["name"],
  527.                   attr["author"] );
  528.     }
  529.     else if( rName == "Video" )
  530.     {
  531.         CheckDefault( "id", "none" );
  532.         CheckDefault( "visible", "true" );
  533.         CheckDefault( "x", "0" );
  534.         CheckDefault( "y", "0" );
  535.         CheckDefault( "width", "0" );
  536.         CheckDefault( "height", "0" );
  537.         CheckDefault( "lefttop", "lefttop" );
  538.         CheckDefault( "rightbottom", "lefttop" );
  539.         CheckDefault( "xkeepratio", "false" );
  540.         CheckDefault( "ykeepratio", "false" );
  541.         CheckDefault( "autoresize", "false" );
  542.         CheckDefault( "help", "" );
  543.         const BuilderData::Video videoData( uniqueId( attr["id"] ),
  544.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  545.                 atoi( attr["width"] ), atoi( attr["height" ]),
  546.                 attr["lefttop"], attr["rightbottom"],
  547.                 convertBoolean( attr["xkeepratio"] ),
  548.                 convertBoolean( attr["ykeepratio"] ),
  549.                 attr["visible"], convertBoolean( attr["autoresize"] ),
  550.                 attr["help"], m_curLayer, m_curWindowId, m_curLayoutId,
  551.                 m_panelStack.back() );
  552.         m_curLayer++;
  553.         m_pData->m_listVideo.push_back( videoData );
  554.     }
  555.     else if( rName == "Window" )
  556.     {
  557.         CheckDefault( "id", "none" );
  558.         CheckDefault( "visible", "true" );
  559.         CheckDefault( "x", "0" );
  560.         CheckDefault( "y", "0" );
  561.         CheckDefault( "dragdrop", "true" );
  562.         CheckDefault( "playondrop", "true" );
  563.         m_curWindowId = uniqueId( attr["id"] );
  564.         const BuilderData::Window window( m_curWindowId,
  565.                 atoi( attr["x"] ) + m_xOffset, atoi( attr["y"] ) + m_yOffset,
  566.                 convertBoolean( attr["visible"] ),
  567.                 convertBoolean( attr["dragdrop"] ),
  568.                 convertBoolean( attr["playondrop"] ) );
  569.         m_pData->m_listWindow.push_back( window );
  570.     }
  571. }
  572. void SkinParser::handleEndElement( const string &rName )
  573. {
  574.     if( rName == "Group" )
  575.     {
  576.         m_xOffset -= m_xOffsetList.back();
  577.         m_yOffset -= m_yOffsetList.back();
  578.         m_xOffsetList.pop_back();
  579.         m_yOffsetList.pop_back();
  580.     }
  581.     else if( rName == "Playtree" || rName == "Playlist" )
  582.     {
  583.         m_curTreeId = "";
  584.     }
  585.     else if( rName == "Popup" )
  586.     {
  587.         m_curPopupId = "";
  588.         m_popupPosList.pop_back();
  589.     }
  590.     else if( rName == "Panel" )
  591.     {
  592.         m_panelStack.pop_back();
  593.     }
  594. }
  595. bool SkinParser::convertBoolean( const char *value ) const
  596. {
  597.     return strcmp( value, "true" ) == 0;
  598. }
  599. int SkinParser::convertColor( const char *transcolor )
  600. {
  601.     // TODO: move to the builder
  602.     unsigned long iRed, iGreen, iBlue;
  603.     iRed = iGreen = iBlue = 0;
  604.     sscanf( transcolor, "#%2lX%2lX%2lX", &iRed, &iGreen, &iBlue );
  605.     return ( iRed << 16 | iGreen << 8 | iBlue );
  606. }
  607. int SkinParser::convertInRange( const char *value, int minValue, int maxValue,
  608.                                 const string &rAttribute ) const
  609. {
  610.     int intValue = atoi( value );
  611.     if( intValue < minValue )
  612.     {
  613.         msg_Warn( getIntf(), "value of "%s" attribute (%i) is out of the "
  614.                   "expected range [%i, %i], using %i instead",
  615.                   rAttribute.c_str(), intValue, minValue, maxValue, minValue );
  616.         return minValue;
  617.     }
  618.     else if( intValue > maxValue )
  619.     {
  620.         msg_Warn( getIntf(), "value of "%s" attribute (%i) is out of the "
  621.                   "expected range [%i, %i], using %i instead",
  622.                   rAttribute.c_str(), intValue, minValue, maxValue, maxValue );
  623.         return maxValue;
  624.     }
  625.     else
  626.     {
  627.         return intValue;
  628.     }
  629. }
  630. const string SkinParser::generateId() const
  631. {
  632.     static int i = 1;
  633.     char genId[5];
  634.     snprintf( genId, 4, "%i", i++ );
  635.     string base = "_ReservedId_" + (string)genId;
  636.     return base;
  637. }
  638. const string SkinParser::uniqueId( const string &id )
  639. {
  640.     string newId;
  641.     if( m_idSet.find( id ) != m_idSet.end() )
  642.     {
  643.         // The id was already used
  644.         if( id != "none" )
  645.         {
  646.             msg_Warn( getIntf(), "non-unique id: %s", id.c_str() );
  647.         }
  648.         newId = generateId();
  649.     }
  650.     else
  651.     {
  652.         // OK, this is a new id
  653.         newId = id;
  654.     }
  655.     // Add the id to the set
  656.     m_idSet.insert( newId );
  657.     return newId;
  658. }