Ticket #12: mythui_themedmenu.diff

File mythui_themedmenu.diff, 83.8 KB (added by stuartm, 16 years ago)

Re-written myththemedmenu, incomplete but functional

  • mythtv/libs/libmythui/myththemedmenu.cpp

     
    1111
    1212#include "myththemedmenu.h"
    1313#include "mythmainwindow.h"
    14 #include "mythfontproperties.h"
    15 #include "mythimage.h"
    1614#include "mythdialogbox.h"
    1715
    1816#include "mythgesture.h"
     
    2826#include "mythdirs.h"
    2927#include "lcddevice.h"
    3028
    31 struct TextAttributes
     29MythThemedMenuState::MythThemedMenuState(MythScreenStack *parent,
     30                                         const QString &name)
     31                    : MythScreenType(parent, name)
    3232{
    33     QRect textRect;
    34     MythFontProperties font;
    35     int textflags;
    36 };
     33    m_loaded = false;
    3734
    38 struct ButtonIcon
    39 {
    40     QString name;
    41     MythImage *icon;
    42     MythImage *activeicon;
    43     MythImage *watermark;
    44     QPoint offset;
    45 };
     35    m_titleState = m_watermarkState = NULL;
     36    m_buttonList = NULL;
    4637
    47 class ThemedButton : public MythUIType
    48 {
    49   public:
    50     ThemedButton(MythUIType *parent, const QString &name)
    51         : MythUIType(parent, name),
    52           background(NULL), icon(NULL), text(NULL),
    53           row(0), col(0)
    54     {
    55     }
     38    m_callback = NULL;
     39    m_callbackdata = NULL;
    5640
    57     void SetActive(bool active)
    58     {
    59         MythUIStateType::StateType state = MythUIStateType::None;
    60         if (active)
    61             state = MythUIStateType::Full;
    62 
    63         if (background)
    64             background->DisplayState(state);
    65         if (icon)
    66             icon->DisplayState(state);
    67         if (text)
    68             text->DisplayState(state);
    69     }
    70 
    71     MythUIStateType *background;
    72     MythUIStateType *icon;
    73     MythUIStateType *text;
    74 
    75     QStringList action;
    76     QString message;
    77     QString type;
    78 
    79     int row;
    80     int col;
    81 };
    82 
    83 struct MenuRow
    84 {
    85     int numitems;
    86     bool visible;
    87     vector<ThemedButton *> buttons;
    88 };
    89 
    90 class MythThemedMenuPrivate;
    91 
    92 /** \class MyththemedMenuState
    93  *  \brief Private class that controls the settings of buttons, logos,
    94  *         backgrounds, texts, and more, for the MythThemedMenu class.
    95  */
    96 class MythThemedMenuState: public XMLParseBase
    97 {
    98   public:
    99     MythThemedMenuState();
    100    ~MythThemedMenuState();
    101 
    102     bool parseSettings(const QString &dir, const QString &menuname);
    103 
    104     void parseBackground(const QString &dir, QDomElement &element);
    105     void parseLogo(const QString &dir, QDomElement &element);
    106     void parseArrow(const QString &dir, QDomElement &element, bool up);
    107     void parseTitle(const QString &dir, QDomElement &element);
    108     void parseButtonDefinition(const QString &dir, QDomElement &element);
    109     void parseButton(const QString &dir, QDomElement &element);
    110 
    111     void parseText(TextAttributes &attributes, QDomElement &element);
    112     void parseOutline(TextAttributes &attributes, QDomElement &element);
    113     void parseShadow(TextAttributes &attributes, QDomElement &element);
    114 
    115     void Reset(void);
    116     void setDefaults(void);
    117 
    118     ButtonIcon *getButtonIcon(const QString &type);
    119 
    120     QRect buttonArea;
    121 
    122     QRect logoRect;
    123     MythImage *logo;
    124 
    125     MythImage *buttonnormal;
    126     MythImage *buttonactive;
    127 
    128     QMap<QString, ButtonIcon> allButtonIcons;
    129 
    130     TextAttributes normalAttributes;
    131     TextAttributes activeAttributes;
    132 
    133     void (*callback)(void *, QString &);
    134     void *callbackdata;
    135 
    136     bool killable;
    137 
    138     bool balancerows;
    139     bool spreadbuttons;
    140     bool buttoncenter;
    141 
    142     QMap<QString, MythImage *> titleIcons;
    143     QMap<QString, MythImage *> m_loadedImages;
    144     QString titleText;
    145     QPoint titlePos;
    146 
    147     MythImage *buttonBackground;
    148 
    149     MythImage *uparrow;
    150     QRect uparrowRect;
    151     MythImage *downarrow;
    152     QRect downarrowRect;
    153 
    154     QPoint watermarkPos;
    155     QRect watermarkRect;
    156 
    157     bool allowreorder;
    158     int maxColumns;
    159 
    160     int visiblerowlimit;
    161 
    162     bool loaded;
    163 
    164     QString themeDir;
    165 
    166     /* FIXME, remove */
    167     static bool parseFonts;
    168 };
    169 
    170 bool MythThemedMenuState::parseFonts = true;
    171 
    172 class MythThemedMenuPrivate: public XMLParseBase
    173 {
    174   public:
    175     MythThemedMenuPrivate(MythThemedMenu *lparent, const QString &cdir,
    176                           MythThemedMenuState *lstate);
    177    ~MythThemedMenuPrivate();
    178 
    179     bool keyPressHandler(QKeyEvent *e);
    180     bool keyHandler(QStringList &actions, bool fullexit);
    181 
    182     bool ReloadTheme(void);
    183 
    184     bool parseMenu(const QString &menuname);
    185 
    186     void parseThemeButton(QDomElement &element);
    187 
    188     void addButton(const QString &type, const QString &text,
    189                    const QString &alttext, const QStringList &action);
    190     bool layoutButtons(void);
    191     void positionButtons(bool resetpos);
    192     bool makeRowVisible(int newrow, int oldrow);
    193 
    194     bool handleAction(const QString &action);
    195     bool findDepends(const QString &fileList);
    196     QString findMenuFile(const QString &menuname);
    197 
    198     void checkScrollArrows(void);
    199 
    200     bool checkPinCode(const QString &timestamp_setting,
    201                       const QString &password_setting,
    202                       const QString &text);
    203  
    204     void SetupBackground();
    205     void SetupUITypes();
    206 
    207     bool gestureEvent(MythUIType *origtype, MythGestureEvent *ge);
    208 
    209     void updateLCD(void);
    210 
    211     MythThemedMenu *parent;
    212 
    213     MythThemedMenuState *m_state;
    214     bool allocedstate;
    215 
    216     vector<ThemedButton *> buttonList;
    217     ThemedButton *activebutton;
    218     int currentrow;
    219     int currentcolumn;
    220 
    221     vector<MenuRow> buttonRows;
    222 
    223     QString selection;
    224     bool foundtheme;
    225 
    226     int exitModifier;
    227 
    228     bool ignorekeys;
    229 
    230     int maxrows;
    231     int visiblerows;
    232     int columns;
    233 
    234     bool wantpop;
    235 
    236     QString titleText;
    237     QString menumode;
    238 
    239     MythUIStateType *watermark;
    240     MythUIImage *uparrow;
    241     MythUIImage *downarrow;
    242 };
    243 
    244 /////////////////////////////////////////////////////////////////////////////
    245 
    246 MythThemedMenuState::MythThemedMenuState() :
    247     logo(NULL),          buttonnormal(NULL),
    248     buttonactive(NULL),  callback(NULL),
    249     callbackdata(NULL),  killable(false),
    250     balancerows(true),   spreadbuttons(false),
    251     buttoncenter(false), buttonBackground(NULL),
    252     uparrow(NULL),       downarrow(NULL),
    253     allowreorder(true),  maxColumns(20),
    254     visiblerowlimit(6),  loaded(false)
    255 {
     41    m_killable = false;
    25642}
    25743
    25844MythThemedMenuState::~MythThemedMenuState()
    25945{
    260     Reset();
    261 }
    262 
    263 void MythThemedMenuState::Reset(void)
    264 {
    265     if (logo)
    266         logo->DownRef();
    267     if (buttonnormal)
    268         buttonnormal->DownRef();
    269     if (buttonactive)
    270         buttonactive->DownRef();
    271     if (uparrow)
    272         uparrow->DownRef();
    273     if (downarrow)
    274         downarrow->DownRef();
    275     if (buttonBackground)
    276         buttonBackground->DownRef();
    277 
    278     logo = NULL;
    279     buttonnormal = NULL;
    280     buttonactive = NULL;
    281     uparrow = NULL;
    282     downarrow = NULL;
    283     buttonBackground = NULL;
    284 
    28546    QMap<QString, ButtonIcon>::Iterator it;
    286     for (it = allButtonIcons.begin(); it != allButtonIcons.end(); ++it)
     47    for (it = m_allButtonIcons.begin(); it != m_allButtonIcons.end(); ++it)
    28748    {
    28849        if (it.value().icon)
    28950            it.value().icon->DownRef();
    29051        if (it.value().activeicon)
    29152            it.value().activeicon->DownRef();
    292         if (it.value().watermark)
    293             it.value().watermark->DownRef();
    29453    }
    295     allButtonIcons.clear();
     54    m_allButtonIcons.clear();
    29655
    29756    QMap<QString, MythImage *>::Iterator jt;
    298     for (jt = titleIcons.begin(); jt != titleIcons.end(); ++jt)
     57    for (jt = m_titleIcons.begin(); jt != m_titleIcons.end(); ++jt)
    29958    {
    30059        jt.value()->DownRef();
    30160    }
    302     titleIcons.clear();
    303     m_loadedImages.clear();
    304 
    305     normalAttributes = activeAttributes = TextAttributes();
    306     setDefaults();
    307 
    308     loaded = false;
     61    m_titleIcons.clear();
    30962}
    31063
    311 ButtonIcon *MythThemedMenuState::getButtonIcon(const QString &type)
     64bool MythThemedMenuState::Create(void)
    31265{
    313     if (allButtonIcons.find(type) != allButtonIcons.end())
    314         return &(allButtonIcons[type]);
    315     return NULL;
    316 }
     66    bool foundtheme = false;
    31767
    318 /** \brief Parse through the element's tags and set the button's
    319  *         area, spread, center, rows, columns and visible lower limit.
    320  *  \param dir     the directory path of background
    321  *  \param element QDomElement with information about the background
    322  */
    323 void MythThemedMenuState::parseBackground(
    324     const QString &dir, QDomElement &element)
    325 {
    326     QString path;
     68    foundtheme = LoadWindowFromXML("menu-ui.xml", "mainmenu", this);
    32769
    328     bool hasarea = false;
     70    if (!foundtheme)
     71        return false;
    32972
    330     buttoncenter = true;
    331     spreadbuttons = true;
    332     maxColumns = 20;        // Arbitrary number
    333     visiblerowlimit = 6;    // the old default
     73    m_titleState = dynamic_cast<MythUIStateType *> (GetChild("titles"));
     74    m_watermarkState = dynamic_cast<MythUIStateType *> (GetChild("watermarks"));
     75    m_buttonList = dynamic_cast<MythUIButtonList *> (GetChild("menu"));
    33476
    335     for (QDomNode child = element.firstChild(); !child.isNull();
    336          child = child.nextSibling())
     77    if (!m_buttonList)
    33778    {
    338         QDomElement info = child.toElement();
    339         if (!info.isNull())
    340         {
    341             if (info.tagName() == "buttonarea")
    342             {
    343                 buttonArea = parseRect(info);
    344                 hasarea = true;
    345 
    346                 if (info.hasAttribute("background"))
    347                 {
    348                     QString bPath = dir + info.attribute("background");
    349                     QImage *image = GetMythUI()->LoadScaleImage(bPath);
    350                     buttonBackground = MythImage::FromQImage(&image);
    351                 }
    352             }
    353             else if (info.tagName() == "buttonspread")
    354             {
    355                 QString val = getFirstText(info);
    356                 if (val == "no")
    357                     spreadbuttons = false;
    358             }
    359             else if (info.tagName() == "buttoncenter")
    360             {
    361                 QString val = getFirstText(info);
    362                 if (val == "no")
    363                     buttoncenter = false;
    364             }
    365             else if (info.tagName() == "balancerows")
    366             {
    367                 QString val = getFirstText(info);
    368                 if (val == "no")
    369                     balancerows = false;
    370             }
    371             else if (info.tagName() == "columns")
    372             {
    373                 QString val = getFirstText(info);
    374                 maxColumns = val.toInt();
    375             }
    376             else if (info.tagName() == "visiblerowlimit")
    377             {
    378                 visiblerowlimit = getFirstText(info).toInt();
    379             }
    380             else
    381             {
    382                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 in "
    383                                             "background").arg(info.tagName()));
    384             }
    385         }
     79        VERBOSE(VB_IMPORTANT, "Missing 'menu' buttonlist.");
     80        return false;
    38681    }
    38782
    388     if (!hasarea)
    389     {
    390         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing button area in background");
    391         return;
    392     }
    393 }
     83    m_buttonList->SetActive(true);
    39484
    395 /** \brief Parse through the element's tags and set the shadow's color,
    396  *         offset, and alpha.
    397  *  \param attributes text attributes whose font shadow will be set
    398  *  \param element    DOM dealing with shadow
    399  */
    400 void MythThemedMenuState::parseShadow(TextAttributes &attributes,
    401                                       QDomElement &element)
    402 {
    403     QPoint offset;
    404     QColor color;
    405     int alpha = 255;
     85    m_loaded = true;
    40686
    407     bool hascolor = false;
    408     bool hasoffset = false;
    409     bool hasalpha = false;
    410 
    411     for (QDomNode child = element.firstChild(); !child.isNull();
    412          child = child.nextSibling())
    413     {
    414         QDomElement info = child.toElement();
    415         if (!info.isNull())
    416         {
    417             if (info.tagName() == "color")
    418             {
    419                 // workaround alpha bug with named colors
    420                 QColor temp(getFirstText(info));
    421                 color = QColor(temp.name());
    422                 hascolor = true;
    423             }
    424             else if (info.tagName() == "offset")
    425             {
    426                 offset = parsePoint(info);
    427                 hasoffset = true;
    428             }
    429             else if (info.tagName() == "alpha")
    430             {
    431                 alpha = getFirstText(info).toInt();
    432                 hasalpha = true;
    433             }
    434             else
    435             {
    436                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 in "
    437                                             "text/shadow").arg(info.tagName()));
    438             }
    439         }
    440     }
    441 
    442     if (!hascolor)
    443     {
    444         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing color tag in shadow");
    445         return;
    446     }
    447 
    448     if (!hasalpha)
    449     {
    450         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing alpha tag in shadow");
    451         return;
    452     }
    453 
    454     if (!hasoffset)
    455     {
    456         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing offset tag in shadow");
    457         return;
    458     }
    459 
    460     attributes.font.SetShadow(true, offset, color, alpha);
     87    return foundtheme;
    46188}
    46289
    463 /** \brief Parse through the element's tags and set the outline's
    464  *         color and size.
    465  *
    466  *  \param attributes text attributes whose font outline will be set
    467  *  \param element    DOM element dealing with outline
    468  */
    469 void MythThemedMenuState::parseOutline(TextAttributes &attributes,
    470                                        QDomElement &element)
     90void MythThemedMenu::setButtonActive(MythUIButtonListItem* item)
    47191{
    472     QColor color;
    473     int size = 0, alpha = 255;
    474 
    475     bool hascolor = false;
    476     bool hassize = false;
    477 
    478     for (QDomNode child = element.firstChild(); !child.isNull();
    479          child = child.nextSibling())
     92    ThemedButton *button = item->GetData().value<ThemedButton *>();
     93    if (m_watermarkState && button)
    48094    {
    481         QDomElement info = child.toElement();
    482         if (!info.isNull())
    483         {
    484             if (info.tagName() == "color")
    485             {
    486                 // workaround alpha bug with named colors
    487                 QColor temp(getFirstText(info));
    488                 color = QColor(temp.name());
    489                 hascolor = true;
    490             }
    491             else if (info.tagName() == "size")
    492             {
    493                 int lsize = getFirstText(info).toInt();
    494                 size = GetMythMainWindow()->NormY(lsize);
    495                 hassize = true;
    496             }
    497             else
    498             {
    499                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 in "
    500                                             "text/shadow").arg(info.tagName()));
    501             }
    502         }
     95        if (m_watermarkState->DisplayState(button->type))
     96            return;
    50397    }
    504 
    505     if (!hassize)
    506     {
    507         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing size in outline");
    508         return;
    509     }
    510 
    511     if (!hascolor)
    512     {
    513         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing color in outline");
    514         return;
    515     }
    516 
    517     attributes.font.SetOutline(true, color, size, alpha);
     98    m_watermarkState->DisplayState("DEFAULT");
    51899}
    519100
    520 /** \brief Parse through the element's tags and set the text's area,
    521  *          fontsize, fontname, positioning, and decorations.
    522  *
    523  *  \param attributes text attributes whose font face will be set
    524  *  \param element    DOM element dealing with text
    525  */
    526 void MythThemedMenuState::parseText(TextAttributes &attributes,
    527                                     QDomElement &element)
     101ButtonIcon *MythThemedMenuState::getButtonIcon(const QString &type)
    528102{
    529     bool hasarea = false;
    530 
    531     int weight = QFont::Normal;
    532     int fontsize = 14;
    533     QString fontname = "Arial";
    534     bool italic = false;
    535 
    536     attributes.textflags = Qt::TextWordWrap;
    537     for (QDomNode child = element.firstChild(); !child.isNull();
    538          child = child.nextSibling())
    539     {
    540         QDomElement info = child.toElement();
    541         if (!info.isNull())
    542         {
    543             if (info.tagName() == "area")
    544             {
    545                 hasarea = true;
    546                 attributes.textRect = parseRect(info);
    547                 attributes.textRect = QRect(attributes.textRect.x(),
    548                                             attributes.textRect.y(),
    549                                             buttonnormal->width() -
    550                                             attributes.textRect.width() -
    551                                             attributes.textRect.x(),
    552                                             buttonnormal->height() -
    553                                             attributes.textRect.height() -
    554                                             attributes.textRect.y());
    555             }
    556             else if (info.tagName() == "fontsize")
    557             {
    558                 fontsize = getFirstText(info).toInt();
    559             }
    560             else if (info.tagName() == "fontname")
    561             {
    562                 fontname = getFirstText(info);
    563             }
    564             else if (info.tagName() == "bold")
    565             {
    566                 if (getFirstText(info) == "yes")
    567                     weight = QFont::Bold;
    568             }
    569             else if (info.tagName() == "italics")
    570             {
    571                 if (getFirstText(info) == "yes")
    572                     italic = true;
    573             }
    574             else if (info.tagName() == "color")
    575             {
    576                 // workaround alpha bug with named colors
    577                 QColor temp(getFirstText(info));
    578                 attributes.font.SetColor(QColor(temp.name()));
    579             }
    580             else if (info.tagName() == "centered")
    581             {
    582                 if (getFirstText(info) == "yes")
    583                 {
    584                     if (GetMythUI()->GetLanguage() == "ja")
    585                     {
    586                         attributes.textflags = Qt::AlignVCenter |
    587                                                Qt::AlignHCenter |
    588                                                Qt::TextWordWrap;
    589                     }
    590                     else
    591                     {
    592                         attributes.textflags = Qt::AlignTop |
    593                                                Qt::AlignHCenter |
    594                                                Qt::TextWordWrap;
    595                     }
    596                 }
    597             }
    598             // halign has three possible values: center, left, and right. //
    599             else if (info.tagName() == "halign")
    600             {
    601                 if (getFirstText(info) == "center")
    602                 {
    603                     // Apparently Japanese is drawn along a _center_ line //
    604                     if (GetMythUI()->GetLanguage() == "ja")
    605                     {
    606                         attributes.textflags =
    607                             (attributes.textflags & ~Qt::AlignHorizontal_Mask) |
    608                             Qt::AlignCenter;
    609                     }
    610                     else
    611                     {
    612                         attributes.textflags =
    613                             (attributes.textflags & ~Qt::AlignHorizontal_Mask) |
    614                             Qt::AlignHCenter;
    615                     }
    616                 }
    617                 else if (getFirstText(info) == "left")
    618                 {
    619                     attributes.textflags =
    620                         (attributes.textflags & ~Qt::AlignHorizontal_Mask) |
    621                         Qt::AlignLeft | Qt::TextWordWrap;
    622                 }
    623                 else if (getFirstText(info) == "right")
    624                 {
    625                     attributes.textflags =
    626                         (attributes.textflags & ~Qt::AlignHorizontal_Mask) |
    627                         Qt::AlignRight | Qt::TextWordWrap;
    628                 }
    629                 else
    630                 {
    631                             VERBOSE(VB_GENERAL,
    632                     QString("MythThemedMenuPrivate: Unknown value %1 "
    633                                         "for halign").arg(getFirstText(info)));
    634                 }
    635             }
    636             // valign has three possible values: center, top, and bottom. //
    637             else if (info.tagName() == "valign")
    638             {
    639                 if (getFirstText(info) == "center")
    640                 {
    641                     attributes.textflags =
    642                         (attributes.textflags & ~Qt::AlignVertical_Mask) |
    643                         Qt::AlignVCenter;
    644                 }
    645                 else if (getFirstText(info) == "top")
    646                 {
    647                     attributes.textflags =
    648                         (attributes.textflags & ~Qt::AlignVertical_Mask) |
    649                         Qt::AlignTop;
    650                 }
    651                 else if (getFirstText(info) == "bottom")
    652                 {
    653                     attributes.textflags =
    654                         (attributes.textflags & ~Qt::AlignVertical_Mask) |
    655                         Qt::AlignBottom;
    656                 }
    657                 else
    658                 {
    659                             VERBOSE(VB_GENERAL,
    660                     QString("MythThemedMenuPrivate: Unknown value %1 "
    661                                         "for valign").arg(getFirstText(info)));
    662                 }
    663             }
    664             else if (info.tagName() == "outline")
    665             {
    666                 parseOutline(attributes, info);
    667             }
    668             else if (info.tagName() == "shadow")
    669             {
    670                 parseShadow(attributes, info);
    671             }
    672             else
    673             {
    674                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown "
    675                                         "tag %1 in text").arg(info.tagName()));
    676                 return;
    677             }
    678         }
    679     }
    680 
    681     QFont font = GetMythMainWindow()->CreateQFont(
    682         fontname, fontsize, weight, italic);
    683 
    684     attributes.font.SetFace(font);
    685 
    686     if (!hasarea)
    687     {
    688         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing 'area' "
    689                 "tag in 'text' element of 'genericbutton'");
    690         return;
    691     }
     103    if (m_allButtonIcons.find(type) != m_allButtonIcons.end())
     104        return &(m_allButtonIcons[type]);
     105    return NULL;
    692106}
    693107
    694 void MythThemedMenuState::parseButtonDefinition(const QString &dir,
    695                                                 QDomElement &element)
     108void MythThemedMenuState::CopyFrom(MythUIType *base)
    696109{
    697     bool hasnormal = false;
    698     bool hasactive = false;
    699     bool hasactivetext = false;
    700 
    701     QString setting;
    702 
    703     QImage *tmp;
    704 
    705     for (QDomNode child = element.firstChild(); !child.isNull();
    706          child = child.nextSibling())
     110    MythThemedMenuState *st = dynamic_cast<MythThemedMenuState *>(base);
     111    if (!st)
    707112    {
    708         QDomElement info = child.toElement();
    709         if (!info.isNull())
    710         {
    711             if (info.tagName() == "normal")
    712             {
    713                 setting = dir + getFirstText(info);
    714                 tmp = GetMythUI()->LoadScaleImage(setting);
    715                 if (tmp)
    716                 {
    717                     buttonnormal = MythImage::FromQImage(&tmp);
    718                     hasnormal = true;
    719                 }
    720             }
    721             else if (info.tagName() == "active")
    722             {
    723                 setting = dir + getFirstText(info);
    724                 tmp = GetMythUI()->LoadScaleImage(setting);
    725                 if (tmp)
    726                 {
    727                     buttonactive = MythImage::FromQImage(&tmp);
    728                     hasactive = true;
    729                 }
    730             }
    731             else if (info.tagName() == "text")
    732             {
    733                 if (!hasnormal)
    734                 {
    735                     VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: The 'normal' "
    736                             "tag needs to come before the 'text' tag");
    737                     return;
    738                 }
    739                 parseText(normalAttributes, info);
    740             }
    741             else if (info.tagName() == "activetext")
    742             {
    743                 if (!hasactive)
    744                 {
    745                     VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: The 'active' "
    746                             "tag needs to come before the 'activetext' tag");
    747                     return;
    748                 }
    749                 parseText(activeAttributes, info);
    750                 hasactivetext = true;
    751             }
    752             else if (info.tagName() == "watermarkposition")
    753             {
    754                 watermarkPos = parsePoint(info);
    755             }
    756             else
    757             {
    758                 VERBOSE(VB_GENERAL,
    759                         QString("MythThemedMenuPrivate: Unknown tag %1 in "
    760                                 "genericbutton").arg(info.tagName()));
    761             }
    762         }
    763     }
    764 
    765     if (!hasnormal)
    766     {
    767         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: No normal button image defined");
     113        VERBOSE(VB_IMPORTANT, "ERROR, bad parsing");
    768114        return;
    769115    }
    770116
    771     if (!hasactive)
    772     {
    773         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: No active button image defined");
    774         return;
    775     }
     117    m_loaded = st->m_loaded;
    776118
    777     if (!hasactivetext)
    778     {
    779         activeAttributes = normalAttributes;
    780     }
     119    MythScreenType::CopyFrom(base);
    781120
    782     watermarkRect = QRect(watermarkPos, QSize(0, 0));
     121    m_titleState = dynamic_cast<MythUIStateType *> (GetChild("titles"));
     122    m_watermarkState = dynamic_cast<MythUIStateType *> (GetChild("watermarks"));
     123    m_buttonList = dynamic_cast<MythUIButtonList *> (GetChild("menu"));
    783124}
    784125
    785 /** \brief Parse through the element's tags and set the logo's
    786  *         image and position.
     126////////////////////////////////////////////////////////////////////////////
     127
     128/** \brief Creates a themed menu.
    787129 *
    788  *  \param dir     directory where logo images may be found
    789  *  \param element DOM element whose nodes describe the logo
     130 *  \param menufile     file name of menu definition file
     131 *  \param parent       the screen stack that owns this UI type
     132 *  \param name         the name of this UI type
     133 *  \param state        theme state associated with this menu
    790134 */
    791 void MythThemedMenuState::parseLogo(const QString &dir, QDomElement &element)
     135MythThemedMenu::MythThemedMenu(const QString &cdir, const QString &menufile,
     136                               MythScreenStack *parent, const QString &name,
     137                               bool allowreorder, MythThemedMenuState *state)
     138              : MythThemedMenuState(parent, name)
    792139{
    793     bool hasimage = false;
    794     bool hasposition = false;
     140    m_state = state;
     141    m_allocedstate = m_foundtheme = m_ignorekeys = m_wantpop = false;
     142    m_exitModifier = -1;
     143    m_menumode = "";
    795144
    796     QPoint logopos;
    797 
    798     for (QDomNode child = element.firstChild(); !child.isNull();
    799          child = child.nextSibling())
     145    if (!m_state)
    800146    {
    801         QDomElement info = child.toElement();
    802         if (!info.isNull())
    803         {
    804             if (info.tagName() == "image")
    805             {
    806                 QString logopath = dir + getFirstText(info);
    807                 QImage *tmp = GetMythUI()->LoadScaleImage(logopath);
    808                 if (tmp)
    809                 {
    810                     logo = MythImage::FromQImage(&tmp);
    811                     hasimage = true;
    812                 }
    813             }
    814             else if (info.tagName() == "position")
    815             {
    816                 logopos = parsePoint(info);
    817                 hasposition = true;
    818             }
    819             else
    820             {
    821                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 "
    822                                             "in logo").arg(info.tagName()));
    823             }
    824         }
     147        m_state = new MythThemedMenuState(parent, "themedmenustate");
     148        m_allocedstate = true;
    825149    }
    826150
    827     if (!hasimage)
    828     {
    829         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing image tag in logo");
    830         return;
    831     }
    832 
    833     if (!hasposition)
    834     {
    835         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing position tag in logo");
    836         return;
    837     }
    838 
    839     logoRect = QRect(logopos.x(), logopos.y(), logo->width(),
    840                      logo->height());
     151    Init(menufile);
    841152}
    842153
    843 /** \brief Parse through the element's tags and set the title's image
    844  *         (and subsequent mode) and position.
     154/** \brief Loads the main UI theme, and a menu theme.
    845155 *
    846  *  \param dir     directory where title images may be found
    847  *  \param element DOM element dealing with the title
    848  */
    849 void MythThemedMenuState::parseTitle(const QString &dir, QDomElement &element)
    850 {
    851     bool hasimage = false;
    852     bool hasposition = false;
    853 
    854     for (QDomNode child = element.firstChild(); !child.isNull();
    855          child = child.nextSibling())
    856     {
    857         QDomElement info = child.toElement();
    858         if (!info.isNull())
    859         {
    860             if (info.tagName() == "image")
    861             {
    862 
    863                 QString titlepath = dir + getFirstText(info);
    864 
    865                 QString name = info.attribute("mode", "");
    866                 if (name != "")
    867                 {
    868                     MythImage *icon;
    869                     QImage *tmppix;
    870 
    871                     if (m_loadedImages[titlepath])
    872                     {
    873                         icon = m_loadedImages[titlepath];
    874                         icon->UpRef();
    875                     }
    876                     else
    877                     {
    878                         tmppix = GetMythUI()->LoadScaleImage(titlepath);
    879 
    880                         if (!tmppix)
    881                             continue;
    882 
    883                         icon = MythImage::FromQImage(&tmppix);
    884                         m_loadedImages.insert(titlepath, icon);
    885                     }
    886 
    887                     titleIcons[name] = icon;
    888                 }
    889                 else
    890                 {
    891                     VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: "
    892                             "Missing mode in titles/image");
    893                     return;
    894                 }
    895 
    896                 hasimage = true;
    897             }
    898             else if (info.tagName() == "position")
    899             {
    900                 titlePos = parsePoint(info);
    901                 hasposition = true;
    902             }
    903             else
    904             {
    905                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 "
    906                                             "in logo").arg(info.tagName()));
    907             }
    908         }
    909     }
    910 
    911     if (!hasimage)
    912     {
    913         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing image tag in titles");
    914         return;
    915     }
    916 
    917     if (!hasposition)
    918     {
    919         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing position tag in titles");
    920         return;
    921     }
    922 }
    923 
    924 /** \brief Parse through the element's tags to set the arrows
    925  *         image and position.
     156 *  See also foundtheme(void), it will return true when called after
     157 *  this method if this method was successful.
    926158 *
    927  *  \param dir     directory where arrow images may be found
    928  *  \param element DOM element dealing with arrow image and position
     159 *  See also ReloadTheme(void) which you can use to load a generic theme,
     160 *  if foundtheme(void) returns false after calling this.
     161 *
     162 *  \param menufile name of menu item xml file
    929163 */
    930 void MythThemedMenuState::parseArrow(const QString &dir, QDomElement &element,
    931                                      bool up)
     164void MythThemedMenu::Init(const QString &menufile)
    932165{
    933     QRect arrowrect;
    934     QPoint arrowpos;
    935     QImage *pix = NULL;   
     166    ReloadExitKey();
    936167
    937     bool hasimage = false;
    938     bool hasposition = false;
    939 
    940     for (QDomNode child = element.firstChild(); !child.isNull();
    941          child = child.nextSibling())
     168    if (!m_state->m_loaded)
    942169    {
    943         QDomElement info = child.toElement();
    944         if (!info.isNull())
    945         {
    946             if (info.tagName() == "image")
    947             {
    948                 QString arrowpath = dir + getFirstText(info);
    949                 pix = GetMythUI()->LoadScaleImage(arrowpath);
    950                 if (pix)
    951                     hasimage = true;
    952             }
    953             else if (info.tagName() == "position")
    954             {
    955                 arrowpos = parsePoint(info);
    956                 hasposition = true;
    957             }
    958             else
    959             {
    960                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 "
    961                                             "in arrow").arg(info.tagName()));
    962             }
    963         }
     170        if (m_state->Create())
     171            m_foundtheme = true;
    964172    }
     173    else
     174        m_foundtheme = true;
    965175
    966     if (!hasimage)
    967     {
    968         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing image tag in arrow");
     176    if (!m_foundtheme)
    969177        return;
    970     }
    971178
    972     if (!hasposition)
    973     {
    974         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing position tag in arrow");
    975         return;
    976     }
     179    CopyFrom(m_state);
    977180
    978     arrowrect = QRect(arrowpos.x(), arrowpos.y(), pix->width(),
    979                       pix->height());
     181    parseMenu(menufile);
    980182
    981     if (up)
    982     {
    983         uparrow = MythImage::FromQImage(&pix);
    984         uparrowRect = arrowrect;
    985     }
    986     else
    987     {
    988         downarrow = MythImage::FromQImage(&pix);
    989         downarrowRect = arrowrect;
    990     }
     183    connect(m_buttonList, SIGNAL(itemSelected(MythUIButtonListItem*)),
     184            SLOT(setButtonActive(MythUIButtonListItem*)));
     185    connect(m_buttonList, SIGNAL(itemClicked(MythUIButtonListItem*)),
     186            SLOT(buttonAction(MythUIButtonListItem*)));
     187
     188    m_buttonList->SetActive(true);
    991189}
    992190
    993 /** \brief Parse through the element's tags and set the button's
    994  *         definition as normal, active, text, or activetext.
    995  *
    996  *  \param dir     directory where the button images may be found
    997  *  \param element DOM element whose nodes define Buttons
    998  */
    999 void MythThemedMenuState::parseButton(const QString &dir, QDomElement &element)
     191MythThemedMenu::~MythThemedMenu(void)
    1000192{
    1001     bool hasname = false;
    1002     bool hasoffset = false;
    1003     bool hasicon = false;
     193    if (m_allocedstate)
     194        delete m_state;
     195}
    1004196
    1005     QString name = "";
    1006     QImage *tmpimg = NULL;
    1007     MythImage *image = NULL;
    1008     MythImage *activeimage = NULL;
    1009     MythImage *watermark = NULL;
    1010     QPoint offset;
     197/// \brief Returns true iff a theme has been found by a previous call to
     198///        Init(const char*,const char*) or ReloadTheme().
     199bool MythThemedMenu::foundTheme(void)
     200{
     201    return m_foundtheme;
     202}
    1011203
    1012     name = element.attribute("name", "");
    1013     if (name != "")
    1014         hasname = true;
     204/// \brief Set the themed menus callback function and data for that function
     205void MythThemedMenu::setCallback(void (*lcallback)(void *, QString &), void *data)
     206{
     207    m_state->m_callback = lcallback;
     208    m_state->m_callbackdata = data;
     209}
    1015210
    1016     for (QDomNode child = element.firstChild(); !child.isNull();
    1017          child = child.nextSibling())
    1018     {
    1019         QDomElement info = child.toElement();
    1020         if (!info.isNull())
    1021         {
    1022             if (info.tagName() == "image")
    1023             {
    1024                 QString imagepath = dir + getFirstText(info);
     211void MythThemedMenu::setKillable(void)
     212{
     213    m_state->m_killable = true;
     214}
    1025215
    1026                 if (m_loadedImages[imagepath])
    1027                 {
    1028                     image = m_loadedImages[imagepath];
    1029                     image->UpRef();
    1030                 }
    1031                 else
    1032                 {
    1033                     tmpimg = GetMythUI()->LoadScaleImage(imagepath);
    1034                     image = MythImage::FromQImage(&tmpimg);
    1035                     m_loadedImages.insert(imagepath, image);
    1036                 }
    1037 
    1038                 if (image)
    1039                     hasicon = true;
    1040             }
    1041             else if (info.tagName() == "activeimage")
    1042             {
    1043                 QString imagepath = dir + getFirstText(info);
    1044 
    1045                 if (m_loadedImages[imagepath])
    1046                 {
    1047                     activeimage = m_loadedImages[imagepath];
    1048                     activeimage->UpRef();
    1049                 }
    1050                 else
    1051                 {
    1052                     tmpimg = GetMythUI()->LoadScaleImage(imagepath);
    1053                     activeimage = MythImage::FromQImage(&tmpimg);
    1054                     m_loadedImages.insert(imagepath, activeimage);
    1055                 }
    1056             }
    1057             else if (info.tagName() == "offset")
    1058             {
    1059                 offset = parsePoint(info);
    1060                 hasoffset = true;
    1061             }
    1062             else if (info.tagName() == "watermarkimage")
    1063             {
    1064                 QString imagepath = dir + getFirstText(info);
    1065 
    1066                 if (m_loadedImages[imagepath])
    1067                 {
    1068                     watermark = m_loadedImages[imagepath];
    1069                     watermark->UpRef();
    1070                 }
    1071                 else
    1072                 {
    1073                     tmpimg = GetMythUI()->LoadScaleImage(imagepath);
    1074                     watermark = MythImage::FromQImage(&tmpimg);
    1075                     m_loadedImages.insert(imagepath, watermark);
    1076                 }
    1077             }
    1078             else
    1079             {
    1080                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 "
    1081                                             "in buttondef").arg(info.tagName()));
    1082             }
    1083         }
    1084     }
    1085 
    1086     if (!hasname)
    1087     {
    1088         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing name in button");
    1089         return;
    1090     }
    1091 
    1092     if (!hasoffset)
    1093     {
    1094         VERBOSE(VB_IMPORTANT, QString("MythThemedMenuPrivate: Missing offset "
    1095                                       "in buttondef %1").arg(name));
    1096         return;
    1097     }
    1098 
    1099     if (!hasicon)
    1100     {
    1101         VERBOSE(VB_IMPORTANT, QString("MythThemedMenuPrivate: Missing image "
    1102                                       "in buttondef %1").arg(name));
    1103         return;
    1104     }
    1105 
    1106     ButtonIcon newbutton;
    1107 
    1108     newbutton.name = name;
    1109     newbutton.icon = image;
    1110     newbutton.offset = offset;
    1111     newbutton.activeicon = activeimage;
    1112 
    1113     if (watermark)
    1114     {
    1115         if (watermark->width() > watermarkRect.width())
    1116             watermarkRect.setWidth(watermark->width());
    1117 
    1118         if (watermark->height() > watermarkRect.height())
    1119             watermarkRect.setHeight(watermark->height());
    1120     }
    1121 
    1122     newbutton.watermark = watermark;
    1123 
    1124     allButtonIcons[name] = newbutton;
    1125 
    1126     if (tmpimg)
    1127         delete tmpimg;
     216QString MythThemedMenu::getSelection(void)
     217{
     218    return m_selection;
    1128219}
    1129220
    1130 /** \brief Set buttons, logo, title icons and text, arrows and
    1131  *         watermarks back to the defaults.
     221/** \brief Looks at "AllowQuitShutdown" setting in DB, in order to
     222 *         determine what to show to user on exit from the frontend.
    1132223 */
    1133 void MythThemedMenuState::setDefaults(void)
     224void MythThemedMenu::ReloadExitKey(void)
    1134225{
    1135     logo = NULL;
    1136     buttonnormal = buttonactive = NULL;
    1137     balancerows = true;
     226    int allowsd = GetMythDB()->GetNumSetting("AllowQuitShutdown");
    1138227
    1139     normalAttributes.textflags = Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap;
    1140     activeAttributes.textflags = Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap;
    1141 
    1142     titleIcons.clear();
    1143     titleText = "";
    1144     uparrow = NULL;
    1145     downarrow = NULL;
    1146     watermarkPos = QPoint(0, 0);
    1147     watermarkRect = QRect(0, 0, 0, 0);
     228    if (allowsd == 1)
     229        m_exitModifier = Qt::ControlModifier;
     230    else if (allowsd == 2)
     231        m_exitModifier = Qt::MetaModifier;
     232    else if (allowsd == 3)
     233        m_exitModifier = Qt::AltModifier;
     234    else if (allowsd == 4)
     235        m_exitModifier = 0;
     236    else
     237        m_exitModifier = -1;
    1148238}
    1149239
    1150 /** \brief Parse the menu from the given dir for background, button,
    1151  *         logo, arrow, and font settings.
    1152  *  \param dir      directory where setting may be found
    1153  *  \param menuname file name of menu file from which settings are parsed
    1154  *  \return true iff file exists, opens, and parses correctly
     240/** \brief keyboard/LIRC event handler.
     241 *
     242 *  This translates key presses through the "menu" context into MythTV
     243 *  actions and then handles them as appropriate.
    1155244 */
    1156 bool MythThemedMenuState::parseSettings(
    1157     const QString &dir, const QString &menuname)
     245bool MythThemedMenu::keyPressEvent(QKeyEvent *e)
    1158246{
    1159     QString filename = dir + menuname;
    1160 
    1161     QDomDocument doc;
    1162     QFile f(filename);
    1163 
    1164     if (!f.open(QIODevice::ReadOnly))
    1165     {
    1166         VERBOSE(VB_IMPORTANT, QString("MythThemedMenuPrivate::parseSettings(): "
    1167                                       "Can't open: %1").arg(filename));
     247    if (m_ignorekeys)
    1168248        return false;
    1169     }
    1170249
    1171     QString errorMsg;
    1172     int errorLine = 0;
    1173     int errorColumn = 0;
     250    m_ignorekeys = true;
    1174251
    1175     if (!doc.setContent(&f, false, &errorMsg, &errorLine, &errorColumn))
    1176     {
    1177         VERBOSE(VB_IMPORTANT, QString("MythThemedMenuPrivate: Error, parsing %1\n"
    1178                                       "at line: %2  column: %3 msg: %4").
    1179                 arg(filename).arg(errorLine).arg(errorColumn).arg(errorMsg));
    1180         f.close();
    1181         return false;
    1182     }
     252    bool ret = true;
     253    if (!keyPressHandler(e))
     254        ret = MythScreenType::keyPressEvent(e);
    1183255
    1184     f.close();
     256    m_ignorekeys = false;
    1185257
    1186     bool setbackground = false;
    1187     bool setbuttondef = false;
     258    if (m_wantpop)
     259        m_ScreenStack->PopScreen();
    1188260
    1189     setDefaults();
    1190 
    1191     QDomElement docElem = doc.documentElement();
    1192     QDomNode n = docElem.firstChild();
    1193     while (!n.isNull())
    1194     {
    1195         QDomElement e = n.toElement();
    1196         if (!e.isNull())
    1197         {
    1198             if (e.tagName() == "background")
    1199             {
    1200                 parseBackground(dir, e);
    1201                 setbackground = true;
    1202             }
    1203             else if (e.tagName() == "genericbutton")
    1204             {
    1205                 parseButtonDefinition(dir, e);
    1206                 setbuttondef = true;
    1207             }
    1208             else if (e.tagName() == "logo")
    1209             {
    1210                 parseLogo(dir, e);
    1211             }
    1212             else if (e.tagName() == "buttondef")
    1213             {
    1214                 parseButton(dir, e);
    1215             }
    1216             else if (e.tagName() == "titles")
    1217             {
    1218                 parseTitle(dir, e);
    1219             }
    1220             else if (e.tagName() == "uparrow")
    1221             {
    1222                 parseArrow(dir, e, true);
    1223             }
    1224             else if (e.tagName() == "downarrow")
    1225             {
    1226                 parseArrow(dir, e, false);
    1227             }
    1228             else if (e.tagName() == "font")
    1229             {
    1230                 if (parseFonts)
    1231                 {
    1232                     MythFontProperties *font;
    1233                     font = MythFontProperties::ParseFromXml(e, NULL, true);
    1234                     delete font;
    1235                 }
    1236             }
    1237             else
    1238             {
    1239                 VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown "
    1240                                             "element %1").arg(e.tagName()));
    1241             }
    1242         }
    1243         n = n.nextSibling();
    1244     }
    1245 
    1246     if (!setbackground)
    1247     {
    1248         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing background element");
    1249         return false;
    1250     }
    1251 
    1252     if (!setbuttondef)
    1253     {
    1254         VERBOSE(VB_IMPORTANT,
    1255                 "MythThemedMenuPrivate: Missing genericbutton definition");
    1256         return false;
    1257     }
    1258 
    1259     parseFonts = false;
    1260     loaded = true;
    1261     return true;
     261    return ret;
    1262262}
    1263263
    1264 /////////////////////////////////////////////////////////////////////////////
    1265 
    1266 /** \brief Constructor, Init() must be called before class can be used.
    1267  *
    1268  *  \param lparent menu that owns this instance
    1269  *  \param cdir    directory where theme is stored
    1270  *  \param lstate  corresponding settings of the theme
    1271  */
    1272 MythThemedMenuPrivate::MythThemedMenuPrivate(MythThemedMenu *lparent,
    1273                                              const QString &cdir,
    1274                                              MythThemedMenuState *lstate) :
    1275     parent(lparent),     m_state(lstate),
    1276     allocedstate(false), activebutton(NULL),
    1277     currentrow(0),       currentcolumn(0),
    1278     foundtheme(false),   exitModifier(-1),
    1279     ignorekeys(false),   maxrows(0),
    1280     visiblerows(0),      columns(0),
    1281     wantpop(false),      watermark(NULL),
    1282     uparrow(NULL),       downarrow(NULL)
     264void MythThemedMenu::aboutToShow()
    1283265{
    1284     if (!m_state)
    1285     {
    1286         m_state = new MythThemedMenuState();
    1287         allocedstate = true;
    1288     }
    1289 
    1290     m_state->themeDir = cdir;
     266    MythScreenType::aboutToShow();
     267//     updateLCD();
    1291268}
    1292269
    1293 MythThemedMenuPrivate::~MythThemedMenuPrivate()
    1294 {
    1295     if (allocedstate)
    1296         delete m_state;
    1297 }
    1298 
    1299270/** \brief Parses the element's tags and set the ThemeButton's type,
    1300271 *         text, depends, and action, then adds the button.
    1301272 *
    1302273 *  \param element DOM element describing features of the themeButton
    1303274 */
    1304 void MythThemedMenuPrivate::parseThemeButton(QDomElement &element)
     275void MythThemedMenu::parseThemeButton(QDomElement &element)
    1305276{
    1306277    QString type = "";
    1307278    QString text = "";
     
    1322293            }
    1323294            else if (info.tagName() == "text")
    1324295            {
    1325                 if ((text.isNull() || text.isEmpty()) && 
    1326                     info.attribute("lang","") == "")
     296                if ((text.isNull() || text.isEmpty()) &&
     297                    info.attribute("lang","").isEmpty())
    1327298                {
    1328                     text = getFirstText(info);
     299                    text = qApp->translate("ThemeUI",
     300                                            qPrintable(getFirstText(info)));
    1329301                }
    1330                 else if (info.attribute("lang","").toLower() == 
     302                else if (info.attribute("lang","").toLower() ==
    1331303                         GetMythUI()->GetLanguageAndVariant())
    1332304                {
    1333305                    text = getFirstText(info);
    1334306                }
    1335                 else if (info.attribute("lang","").toLower() == 
     307                else if (info.attribute("lang","").toLower() ==
    1336308                         GetMythUI()->GetLanguage())
    1337309                {
    1338310                    text = getFirstText(info);
     
    1341313            else if (info.tagName() == "alttext")
    1342314            {
    1343315                if ((alttext.isNull() || alttext.isEmpty()) &&
    1344                     info.attribute("lang","") == "")
     316                    info.attribute("lang","").isEmpty())
    1345317                {
    1346                     alttext = getFirstText(info);
     318                    alttext = qApp->translate("ThemeUI",
     319                                            qPrintable(getFirstText(info)));
    1347320                }
    1348                 else if (info.attribute("lang","").toLower() == 
     321                else if (info.attribute("lang","").toLower() ==
    1349322                         GetMythUI()->GetLanguageAndVariant())
    1350323                {
    1351324                    alttext = getFirstText(info);
     
    1380353        }
    1381354    }
    1382355
    1383     if (text == "")
     356    if (text.isEmpty())
    1384357    {
    1385358        VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing 'text' in button");
    1386359        return;
    1387360    }
    1388    
     361
    1389362    if (action.empty())
    1390363    {
    1391364        VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Missing 'action' in button");
     
    1409382 *  non-essential portion of MythTV which the theme does not support.
    1410383 *
    1411384 */
    1412 bool MythThemedMenuPrivate::parseMenu(const QString &menuname)
     385bool MythThemedMenu::parseMenu(const QString &menuname)
    1413386{
    1414387    QString filename = findMenuFile(menuname);
    1415388
     
    1426399        }
    1427400        else
    1428401        {
    1429             parent->GetScreenStack()->PopScreen();
    1430 #if 0
    1431             MythPopupBox::showOkPopup(gContext->GetMainWindow(), QObject::tr("No Menu File"),
    1432                                       QObject::tr(QString("Myth could not locate the menu file %1.\n\n"
    1433                                       "We will now return to the main menu.").arg(menuname)));
    1434 #endif
     402            GetScreenStack()->PopScreen();
    1435403            return false;
    1436404        }
    1437        
    1438        
     405
    1439406    }
    1440407
    1441408    QString errorMsg;
     
    1454421            return false;
    1455422        }
    1456423
    1457         parent->GetScreenStack()->PopScreen();
    1458 #if 0
    1459         MythPopupBox::showOkPopup(gContext->GetMainWindow(),
    1460                                   QObject::tr("Bad Menu File"),
    1461                                   QObject::tr(QString("The menu file %1 is "
    1462                                               "incomplete.\n\nWe will now "
    1463                                               "return to the main menu.")
    1464                                               .arg(menuname)));
    1465 #endif
     424        GetScreenStack()->PopScreen();
    1466425        return false;
    1467426    }
    1468427
    1469428    f.close();
    1470429
    1471     buttonList.clear();
    1472     buttonRows.clear();
    1473 
    1474     SetupBackground();
    1475 
    1476430    QDomElement docElem = doc.documentElement();
    1477431
    1478     menumode = docElem.attribute("name", "");
     432    m_menumode = docElem.attribute("name", "MAIN");
    1479433
    1480434    QDomNode n = docElem.firstChild();
    1481435    while (!n.isNull())
     
    1497451        n = n.nextSibling();
    1498452    }
    1499453
    1500     if (buttonList.size() == 0)
     454    if (m_buttonList->GetCount() == 0)
    1501455    {
    1502456        VERBOSE(VB_IMPORTANT, QString("MythThemedMenuPrivate: No buttons "
    1503457                                      "for menu %1").arg(menuname));
    1504458        return false;
    1505459    }
    1506460
    1507     if (!layoutButtons())
    1508         return false;
    1509     activebutton = NULL;
    1510     positionButtons(true);
    1511 
    1512     SetupUITypes();
    1513 
    1514461    if (LCD::Get())
    1515462    {
    1516         titleText = "MYTH-";
    1517         titleText += menumode;
     463        m_titleText = "MYTH-";
     464        m_titleText += m_menumode;
    1518465    }
    1519466
    1520     selection = "";
     467    if (m_titleState)
     468        m_titleState->DisplayState(m_menumode);
     469
     470    m_selection = "";
    1521471    return true;
    1522472}
    1523473
    1524 /// \brief Sets up menu background, must be done before the buttons are parsed
    1525 void MythThemedMenuPrivate::SetupBackground(void)
     474void MythThemedMenu::updateLCD(void)
    1526475{
    1527 
    1528     if (m_state->buttonBackground)
    1529     {
    1530         MythUIImage *buttonBackground;
    1531         buttonBackground = new MythUIImage(parent, "menu button background");
    1532         buttonBackground->SetImage(m_state->buttonBackground);
    1533         buttonBackground->SetPosition(m_state->buttonArea.topLeft());
    1534     }
    1535 
    1536 }
    1537 
    1538 /// \brief Sets up UI according to the corresponding mythThemedMenuState.
    1539 void MythThemedMenuPrivate::SetupUITypes(void)
    1540 {
    1541     if (m_state->titleIcons.contains(menumode))
    1542     {
    1543         MythUIImage *curTitle;
    1544         curTitle = new MythUIImage(parent, "menu title image");
    1545         curTitle->SetImage(m_state->titleIcons[menumode]);
    1546         curTitle->SetPosition(m_state->titlePos);
    1547     }
    1548 
    1549     if (m_state->logo)
    1550     {
    1551         MythUIImage *logo;
    1552         logo = new MythUIImage(parent, "menu logo");
    1553         logo->SetImage(m_state->logo);
    1554         logo->SetPosition(m_state->logoRect.topLeft());
    1555     }
    1556 
    1557     watermark = new MythUIStateType(parent, "menu watermarks");
    1558     watermark->SetArea(m_state->watermarkRect);
    1559     watermark->SetShowEmpty(true);
    1560     QMap<QString, ButtonIcon>::Iterator it = m_state->allButtonIcons.begin();
    1561     for (; it != m_state->allButtonIcons.end(); ++it)
    1562     {
    1563         ButtonIcon *icon = &(it.value());
    1564         if (icon->watermark)
    1565             watermark->AddImage(icon->name, icon->watermark);
    1566     }
    1567 
    1568     watermark->DisplayState(activebutton->type);
    1569 
    1570     uparrow = new MythUIImage(parent, "menu up arrow");
    1571     if (m_state->uparrow)
    1572     {
    1573         uparrow->SetArea(m_state->uparrowRect);
    1574         uparrow->SetImage(m_state->uparrow);
    1575     }
    1576     uparrow->SetVisible(false);
    1577     uparrow->SetCanTakeFocus(true);
    1578 
    1579     downarrow = new MythUIImage(parent, "menu down arrow");
    1580     if (m_state->downarrow)
    1581     {
    1582         downarrow->SetArea(m_state->downarrowRect);
    1583         downarrow->SetImage(m_state->downarrow);
    1584     }
    1585     downarrow->SetVisible(false);
    1586     downarrow->SetCanTakeFocus(true);
    1587 
    1588     checkScrollArrows();
    1589 }
    1590 
    1591 void MythThemedMenuPrivate::updateLCD(void)
    1592 {
    1593476    LCD *lcddev = LCD::Get();
    1594477    if (lcddev == NULL)
    1595478        return;
     
    1598481    QList<LCDMenuItem> menuItems;
    1599482    bool selected;
    1600483
    1601     for (int r = 0; r < (int)buttonRows.size(); r++)
    1602     {
    1603         if (r == currentrow)
    1604             selected = true;
    1605         else
    1606             selected = false;
    1607 
    1608         if (currentcolumn < buttonRows[r].numitems)
    1609             menuItems.append(LCDMenuItem(selected, NOTCHECKABLE,
    1610                              buttonRows[r].buttons[currentcolumn]->message));
    1611     }
    1612 
    1613     if (!menuItems.isEmpty())
    1614         lcddev->switchToMenu(menuItems, titleText);
     484//     for (int r = 0; r < (int)buttonRows.size(); r++)
     485//     {
     486//         if (r == currentrow)
     487//             selected = true;
     488//         else
     489//             selected = false;
     490//
     491//         if (currentcolumn < buttonRows[r].numitems)
     492//             menuItems.append(LCDMenuItem(selected, NOTCHECKABLE,
     493//                              buttonRows[r].buttons[currentcolumn]->message));
     494//     }
     495//
     496//     if (!menuItems.isEmpty())
     497//         lcddev->switchToMenu(menuItems, m_titleText);
    1615498}
    1616499
    1617500/** \brief Create a new MythThemedButton based on the MythThemedMenuState
     
    1623506 *  \param alttext alternate text to appear when required
    1624507 *  \param action  actions to be associated with button
    1625508 */
    1626 void MythThemedMenuPrivate::addButton(const QString &type, const QString &text,
    1627                                       const QString &alttext, 
     509void MythThemedMenu::addButton(const QString &type, const QString &text,
     510                                      const QString &alttext,
    1628511                                      const QStringList &action)
    1629512{
    1630     ThemedButton *newbutton = new ThemedButton(parent, type);
     513    MythUIButtonListItem *listbuttonitem =
     514                                new MythUIButtonListItem(m_buttonList, text);
    1631515
     516    ThemedButton *newbutton = new ThemedButton;
    1632517    newbutton->type = type;
    1633518    newbutton->action = action;
    1634     newbutton->message = text;
     519    newbutton->text = text;
     520    newbutton->active = false;
    1635521
    1636     newbutton->SetCanTakeFocus(true);
    1637     newbutton->background = new MythUIStateType(newbutton, "button background");
    1638     if (m_state->buttonnormal)
    1639         newbutton->background->AddImage(MythUIStateType::None,
    1640                                         m_state->buttonnormal);
    1641     if (m_state->buttonactive)
    1642         newbutton->background->AddImage(MythUIStateType::Full,
    1643                                         m_state->buttonactive);
    1644     newbutton->background->DisplayState(MythUIStateType::None);
    1645 
    1646     newbutton->SetArea(QRect(0, 0, m_state->buttonnormal->width(),
    1647                              m_state->buttonnormal->height()));
    1648 
    1649     newbutton->icon = NULL;
    1650     ButtonIcon *buttonicon = m_state->getButtonIcon(type);
    1651     if (buttonicon)
    1652     {
    1653         newbutton->icon = new MythUIStateType(newbutton, "button icon");
    1654         newbutton->icon->SetPosition(buttonicon->offset);
    1655         if (buttonicon->icon)
    1656             newbutton->icon->AddImage(MythUIStateType::None,
    1657                                       buttonicon->icon);
    1658         if (buttonicon->activeicon)
    1659             newbutton->icon->AddImage(MythUIStateType::Full,
    1660                                       buttonicon->activeicon);
    1661         newbutton->icon->DisplayState(MythUIStateType::None);
    1662     }
    1663 
    1664     newbutton->text = new MythUIStateType(newbutton, "button text state");
    1665     {
    1666         QString msg = text;
    1667         QRect buttonTextRect = m_state->normalAttributes.textRect;
    1668         QRect testBoundRect = buttonTextRect;
    1669         testBoundRect.setWidth(testBoundRect.width() + 40);
    1670 
    1671         QFontMetrics tmp(m_state->normalAttributes.font.face());
    1672         QRect testBound = tmp.boundingRect(testBoundRect.x(), testBoundRect.y(),
    1673                                            testBoundRect.width(),
    1674                                            testBoundRect.height(),
    1675                                            m_state->normalAttributes.textflags,
    1676                                            text);
    1677         if (testBound.height() > buttonTextRect.height() && alttext != "")
    1678             msg = alttext;
    1679 
    1680         MythUIText *txt = new MythUIText(msg, m_state->normalAttributes.font,
    1681                                          buttonTextRect, buttonTextRect,
    1682                                          newbutton->text, "button normal text");
    1683         txt->SetJustification(m_state->normalAttributes.textflags);
    1684         newbutton->text->AddObject(MythUIStateType::None, txt);
    1685     }
    1686     {
    1687         QString msg = text;
    1688         QRect buttonTextRect = m_state->activeAttributes.textRect;
    1689         QRect testBoundRect = buttonTextRect;
    1690         testBoundRect.setWidth(testBoundRect.width() + 40);
    1691 
    1692         QFontMetrics tmp(m_state->activeAttributes.font.face());
    1693         QRect testBound = tmp.boundingRect(testBoundRect.x(), testBoundRect.y(),
    1694                                            testBoundRect.width(),
    1695                                            testBoundRect.height(),
    1696                                            m_state->activeAttributes.textflags,
    1697                                            text);
    1698         if (testBound.height() > buttonTextRect.height() && alttext != "")
    1699             msg = alttext;
    1700 
    1701         MythUIText *txt = new MythUIText(msg, m_state->activeAttributes.font,
    1702                                          buttonTextRect, buttonTextRect,
    1703                                          newbutton->text, "button normal text");
    1704         txt->SetJustification(m_state->activeAttributes.textflags);
    1705         newbutton->text->AddObject(MythUIStateType::Full, txt);
    1706     }
    1707     newbutton->text->DisplayState(MythUIStateType::None);
    1708 
    1709     newbutton->SetVisible(false);
    1710 
    1711     buttonList.push_back(newbutton);
     522    listbuttonitem->SetData(qVariantFromValue(newbutton));
    1712523}
    1713524
    1714 /** \brief Properly lay out all of the buttons that were added with addButton
    1715  *  \return true iff there are more than 0 rows or columns
    1716  */
    1717 bool MythThemedMenuPrivate::layoutButtons(void)
    1718 {
    1719     int numbuttons = buttonList.size();
    1720  
    1721     columns = m_state->buttonArea.width() / m_state->buttonnormal->width();
    1722     columns = columns > m_state->maxColumns ? m_state->maxColumns : columns;
    1723 
    1724     maxrows = m_state->buttonArea.height() / m_state->buttonnormal->height();
    1725 
    1726     if (maxrows < 1)
    1727     {
    1728         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Must have "
    1729                 "room for at least 1 row of buttons");
    1730         return false;
    1731     }
    1732    
    1733     if (columns < 1)
    1734     {
    1735         VERBOSE(VB_IMPORTANT, "MythThemedMenuPrivate: Must have "
    1736                 "room for at least 1 column of buttons");
    1737         return false;
    1738     }
    1739 
    1740     if (m_state->balancerows)
    1741     {
    1742         // keep the rows balanced
    1743         if (numbuttons <= 4)
    1744         {
    1745             if (columns > 2)
    1746                 columns = 2;
    1747         }
    1748         else
    1749         {
    1750             if (columns > 3)
    1751                 columns = 3;
    1752         }
    1753     }   
    1754 
    1755     // limit it to 6 items displayed at one time
    1756     if (columns * maxrows > m_state->visiblerowlimit)
    1757     {
    1758         maxrows = m_state->visiblerowlimit / columns;
    1759     }
    1760                              
    1761     vector<ThemedButton *>::iterator iter = buttonList.begin();
    1762 
    1763     int rows = numbuttons / columns;
    1764     rows++;
    1765 
    1766     visiblerows = 0;
    1767 
    1768     for (int i = 0; i < rows; i++)
    1769     {
    1770         MenuRow newrow;
    1771         newrow.numitems = 0;
    1772 
    1773         for (int j = 0; j < columns && iter != buttonList.end();
    1774              j++, iter++)
    1775         {
    1776             if (columns == 3 && j == 1 && m_state->allowreorder)
    1777                 newrow.buttons.insert(newrow.buttons.begin(), *iter);
    1778             else
    1779                 newrow.buttons.push_back(*iter);
    1780             newrow.numitems++;
    1781         }
    1782 
    1783         if (i < maxrows && newrow.numitems > 0)
    1784         {
    1785             newrow.visible = true;
    1786             visiblerows++;
    1787         }
    1788         else
    1789             newrow.visible = false;
    1790  
    1791         if (newrow.numitems > 0)
    1792             buttonRows.push_back(newrow);
    1793     }           
    1794 
    1795     return true;
    1796 }
    1797 
    1798 /** \brief Place buttons in position and set them visible and active.
    1799  *
    1800  *  \param resetpos whether or not to reset the active button to the
    1801  *                  first one on the buttonlist.
    1802  */
    1803 void MythThemedMenuPrivate::positionButtons(bool resetpos)
    1804 {
    1805     QRect buttonArea = m_state->buttonArea;
    1806     int buttonHeight = m_state->buttonnormal->height();
    1807     int buttonWidth = m_state->buttonnormal->width();
    1808 
    1809     int rows = visiblerows;
    1810     int yspacing = (buttonArea.height() - buttonHeight * rows) /
    1811                    (rows + 1);
    1812     int ystart = 0;
    1813    
    1814     if (!m_state->spreadbuttons)
    1815     {
    1816         yspacing = 0;
    1817         if (m_state->buttoncenter)
    1818             ystart = (buttonArea.height() - buttonHeight * rows) / 2;
    1819     }
    1820 
    1821     int row = 1;
    1822 
    1823     vector<MenuRow>::iterator menuiter = buttonRows.begin();
    1824     for (; menuiter != buttonRows.end(); menuiter++)
    1825     {
    1826         if (!(*menuiter).visible)
    1827         {
    1828             vector<ThemedButton *>::iterator biter;
    1829             biter = (*menuiter).buttons.begin();
    1830             for (; biter != (*menuiter).buttons.end(); biter++)
    1831             {
    1832                 ThemedButton *tbutton = (*biter);
    1833                 tbutton->SetVisible(false);
    1834                 tbutton->SetActive(false);
    1835             }
    1836             continue;
    1837         }
    1838 
    1839         int ypos = yspacing * row + (buttonHeight * (row - 1));
    1840         ypos += buttonArea.y() + ystart;
    1841 
    1842         int xspacing = (buttonArea.width() - buttonWidth *
    1843                        (*menuiter).numitems) / ((*menuiter).numitems + 1);
    1844         int col = 1;
    1845         vector<ThemedButton *>::iterator biter = (*menuiter).buttons.begin();
    1846         for (; biter != (*menuiter).buttons.end(); biter++)
    1847         {
    1848             int xpos = xspacing * col + (buttonWidth * (col - 1));
    1849             xpos = (m_state->maxColumns == 1) ? 0 : xpos;
    1850             xpos += buttonArea.x();
    1851 
    1852             ThemedButton *tbutton = (*biter);
    1853 
    1854             tbutton->SetVisible(true);
    1855             tbutton->SetActive(false);
    1856             tbutton->row = row;
    1857             tbutton->col = col;
    1858             tbutton->SetPosition(xpos, ypos);
    1859 
    1860             col++;
    1861         }
    1862 
    1863         row++;
    1864     }
    1865 
    1866     if (resetpos)
    1867     {
    1868         ThemedButton *old = activebutton;
    1869         activebutton = (*(buttonList.begin()));
    1870 
    1871         if (activebutton != old && old)
    1872             old->SetActive(false);
    1873 
    1874         activebutton->SetActive(true);
    1875 
    1876         currentrow = activebutton->row - 1;
    1877         currentcolumn = activebutton->col - 1;
    1878     }
    1879 }
    1880 
    1881 bool MythThemedMenuPrivate::makeRowVisible(int newrow, int oldrow)
    1882 {
    1883     if (buttonRows[newrow].visible)
    1884         return true;
    1885 
    1886     if (newrow > oldrow)
    1887     {
    1888         int row;
    1889         for (row = newrow; row >= 0; row--)
    1890         {
    1891             if (row > newrow - visiblerows)
    1892                 buttonRows[row].visible = true;
    1893             else
    1894                 buttonRows[row].visible = false;
    1895         }
    1896     }
    1897     else
    1898     {
    1899         int row;
    1900         for (row = newrow; row < (int)buttonRows.size(); row++)
    1901         {
    1902             if (row < newrow + visiblerows)
    1903                 buttonRows[row].visible = true;
    1904             else
    1905                 buttonRows[row].visible = false;
    1906         }
    1907     }
    1908 
    1909     positionButtons(false);
    1910 
    1911     checkScrollArrows();
    1912 
    1913     return true;
    1914 }
    1915 
    1916 /// \brief Add or remove scroll arrows as needed.
    1917 void MythThemedMenuPrivate::checkScrollArrows(void)
    1918 {
    1919     bool needup = false;
    1920     bool needdown = false;
    1921 
    1922     if (!buttonRows.front().visible)
    1923         needup = true;
    1924     if (!buttonRows.back().visible)
    1925         needdown = true;
    1926 
    1927     uparrow->SetVisible(needup);
    1928     downarrow->SetVisible(needdown);
    1929 }
    1930 
    1931525/** \brief Reset and reparse everything.
    1932526 *
    1933527 *  Note: this does not use the theme or menu file chosen in Init(), but
    1934528 *  instead uses defaults which should work if MythTV was properly installed.
    1935529 */
    1936 bool MythThemedMenuPrivate::ReloadTheme(void)
     530void MythThemedMenu::ReloadTheme(void)
    1937531{
     532    m_foundtheme = false;
     533
    1938534    GetGlobalFontMap()->Clear();
    1939     m_state->parseFonts = true;
    1940535
    1941     buttonList.clear();
    1942     buttonRows.clear();
     536    m_buttonList->Reset();
    1943537
    1944     parent->ReloadExitKey();
    1945  
    1946     m_state->Reset();
     538    ReloadExitKey();
    1947539
    1948     parent->DeleteAllChildren();
    1949  
    1950     QString themedir = GetMythUI()->GetThemeDir();
    1951     bool ok = m_state->parseSettings(themedir, "theme.xml");
    1952     if (!ok)
    1953         return ok;
     540    DeleteAllChildren();
    1954541
    1955     return parseMenu("mainmenu.xml");
     542    if (parseMenu("mainmenu.xml"))
     543        m_foundtheme = true;
    1956544}
    1957545
    1958546/** \brief Delegate key event to appropriate action for keyHandler()
    1959547 *
    1960548 *  \return true iff key event was properly handled
    1961549 */
    1962 bool MythThemedMenuPrivate::keyPressHandler(QKeyEvent *e)
     550bool MythThemedMenu::keyPressHandler(QKeyEvent *e)
    1963551{
    1964552    QStringList actions;
    1965553    GetMythMainWindow()->TranslateKeyPress("menu", e, actions);
    1966554
    1967     return keyHandler(actions, e->modifiers() == exitModifier);
     555    return keyHandler(actions, e->modifiers() == m_exitModifier);
    1968556}
    1969557
    1970558/** \brief Interpret key presses on the menu into the appropriate actions
    1971559 *
    1972560 *  \param actions list of MythTV actions to be handled.
    1973561 */
    1974 bool MythThemedMenuPrivate::keyHandler(QStringList &actions,
     562bool MythThemedMenu::keyHandler(QStringList &actions,
    1975563                                       bool fullexit)
    1976564{
    1977     ThemedButton *lastbutton = activebutton;
    1978     int oldrow = currentrow;
    1979     int oldcolumn = currentcolumn;
    1980565    bool handled = false;
    1981566
    1982567    for (int i = 0; i < actions.size() && !handled; i++)
     
    1984569        QString action = actions[i];
    1985570        handled = true;
    1986571
    1987         if (columns == 1)
     572//         if (columns == 1)
     573//         {
     574//             if (action == "LEFT")
     575//                 action = "ESCAPE";
     576//             else if (action == "RIGHT")
     577//                 action = "SELECT";
     578//         }
     579        if (action == "ESCAPE")
    1988580        {
    1989             if (action == "LEFT")
    1990                 action = "ESCAPE";
    1991             else if (action == "RIGHT")
    1992                 action = "SELECT";
    1993         }
    1994 
    1995         if (action == "UP")
    1996         {
    1997             if (currentrow > 0)
    1998                 currentrow--;
    1999             else if (columns == 1)
    2000                 currentrow = buttonRows.size() - 1;
    2001 
    2002             if (currentcolumn >= buttonRows[currentrow].numitems)
    2003                 currentcolumn = buttonRows[currentrow].numitems - 1;
    2004            
    2005             if (currentrow == oldrow && currentcolumn == oldcolumn)
    2006             {
    2007                 handled = false;
    2008             }
    2009         }
    2010         else if (action == "PAGEUP")
    2011         {
    2012             currentrow = max(currentrow - m_state->visiblerowlimit, 0);
    2013 
    2014             if (currentcolumn >= buttonRows[currentrow].numitems)
    2015                 currentcolumn = buttonRows[currentrow].numitems - 1;
    2016         }
    2017         else if (action == "LEFT")
    2018         {
    2019             if (currentcolumn > 0)
    2020                 currentcolumn--;
    2021             else
    2022                 currentcolumn = buttonRows[currentrow].numitems - 1;
    2023         }
    2024         else if (action == "DOWN")
    2025         {
    2026             if (currentrow < (int)buttonRows.size() - 1)
    2027                 currentrow++;
    2028             else if (columns == 1)
    2029                 currentrow = 0;
    2030 
    2031             if (currentcolumn >= buttonRows[currentrow].numitems)
    2032                 currentcolumn = buttonRows[currentrow].numitems - 1;
    2033 
    2034             if (currentrow == oldrow && currentcolumn == oldcolumn)
    2035             {
    2036                 handled = false;
    2037             }
    2038         }
    2039         else if (action == "PAGEDOWN")
    2040         {
    2041             currentrow = min(currentrow + m_state->visiblerowlimit,
    2042                              (int)buttonRows.size() - 1);
    2043 
    2044             if (currentcolumn >= buttonRows[currentrow].numitems)
    2045                 currentcolumn = buttonRows[currentrow].numitems - 1;
    2046         }
    2047         else if (action == "RIGHT")
    2048         {
    2049             if (currentcolumn < buttonRows[currentrow].numitems - 1)
    2050                 currentcolumn++;
    2051             else
    2052                 currentcolumn = 0;
    2053         }
    2054         else if (action == "SELECT")
    2055         {
    2056             lastbutton = activebutton;
    2057             activebutton = NULL;
    2058 
    2059             QStringList::Iterator it = lastbutton->action.begin();
    2060             for (; it != lastbutton->action.end(); it++)
    2061             {
    2062                 if (handleAction(*it))
    2063                     break;
    2064             }
    2065 
    2066             lastbutton = NULL;
    2067         }
    2068         else if (action == "ESCAPE")
    2069         {
    2070581            QString action = "UPMENU";
    2071             if (!allocedstate)
     582            if (!m_allocedstate)
    2072583                handleAction(action);
    2073             else if (m_state->killable)
     584            else if (m_state->m_killable)
    2074585            {
    2075                 wantpop = true;
    2076                 if (m_state->callback != NULL)
     586                m_wantpop = true;
     587                if (m_state->m_callback)
    2077588                {
    2078589                    QString sel = "EXITING_MENU";
    2079                     m_state->callback(m_state->callbackdata, sel);
     590                    m_state->m_callback(m_state->m_callbackdata, sel);
    2080591                }
    2081592
    2082593                if (GetMythMainWindow()->GetMainStack()->TotalScreens() == 1)
    2083594                    QApplication::exit();
    2084595            }
    2085             else if (exitModifier >= 0 && fullexit &&
     596            else if (m_exitModifier >= 0 && fullexit &&
    2086597                     GetMythMainWindow()->GetMainStack()->TotalScreens() == 1)
    2087598            {
    2088599                QApplication::exit();
    2089                 wantpop = true;
     600                m_wantpop = true;
    2090601            }
    2091             lastbutton = NULL;
    2092602        }
    2093603        else
    2094604            handled = false;
     
    2097607    if (!handled)
    2098608        return false;
    2099609
    2100     if (!buttonRows[currentrow].visible)
    2101     {
    2102         makeRowVisible(currentrow, oldrow);
    2103     }
    2104 
    2105     activebutton = buttonRows[currentrow].buttons[currentcolumn];
    2106     watermark->DisplayState(activebutton->type);
    2107 
    2108     if (lastbutton != activebutton && lastbutton && activebutton)
    2109     {
    2110         lastbutton->SetActive(false);
    2111         activebutton->SetActive(true);
    2112     }
    2113 
    2114610    // only update the LCD if we are still on the top of the stack
    2115     if (parent->GetScreenStack()->GetTopScreen() == (MythScreenType*) parent)
    2116         updateLCD();
     611//     if (GetScreenStack()->GetTopScreen() == this)
     612//         updateLCD();
    2117613
    2118614    return true;
    2119 } 
     615}
    2120616
    2121 /** \brief Interprets mouse gestures as MythTV action events
    2122  *
    2123  *  \param origtype originating element type for the gesture
    2124  *  \param ge       mouse gesture event
    2125  *  \return true iff a gesture was handled here.
    2126  */
    2127 bool MythThemedMenuPrivate::gestureEvent(MythUIType *origtype,
    2128                                          MythGestureEvent *ge)
     617void MythThemedMenu::buttonAction(MythUIButtonListItem *item)
    2129618{
    2130     if (ge->gesture() == MythGestureEvent::Click)
    2131     {
    2132         if (origtype == uparrow)
    2133         {
    2134             QStringList action("PAGEUP");
    2135             keyHandler(action, false);
    2136         }
    2137         else if (origtype == downarrow)
    2138         {
    2139             QStringList action("PAGEDOWN");
    2140             keyHandler(action, false);
    2141         }
    2142         else
    2143         {
    2144             if (ThemedButton *button = reinterpret_cast<ThemedButton*>(origtype))
    2145             {
    2146                 ThemedButton *lastbutton = activebutton;
    2147                 activebutton = button;
     619    ThemedButton *button = item->GetData().value<ThemedButton *>();
    2148620
    2149                 if (LCD *lcddev = LCD::Get())
    2150                     lcddev->switchToTime();
    2151 
    2152                 QStringList::Iterator it = button->action.begin();
    2153                 for (; it != button->action.end(); it++)
    2154                 {
    2155                     if (handleAction(*it))
    2156                         break;
    2157                 }
    2158 
    2159                 watermark->DisplayState(activebutton->type);
    2160 
    2161                 if (lastbutton != activebutton && lastbutton && activebutton)
    2162                 {
    2163                     lastbutton->SetActive(false);
    2164                     activebutton->SetActive(true);
    2165                 }
    2166             }
    2167         }
    2168        
    2169         return true;
    2170     }
    2171 
    2172     if (ge->gesture() == MythGestureEvent::Left)
     621    QStringList::Iterator it = button->action.begin();
     622    for (; it != button->action.end(); it++)
    2173623    {
    2174         QStringList action("ESCAPE");
    2175         keyHandler(action, true);
    2176         return true;
     624        if (handleAction(*it))
     625            break;
    2177626    }
    2178627
    2179     return false;
     628    button = NULL;
    2180629}
    2181630
    2182631/** \brief Locates the appropriate menu file from which to parse the menu
     
    2184633 *  \param menuname file name of the menu you want to find
    2185634 *  \return the directory in which the menu file is found
    2186635 */
    2187 QString MythThemedMenuPrivate::findMenuFile(const QString &menuname)
     636QString MythThemedMenu::findMenuFile(const QString &menuname)
    2188637{
    2189638    QString testdir = GetConfDir() + "/" + menuname;
    2190639    QFile file(testdir);
     
    2196645    if (file.exists())
    2197646        return testdir;
    2198647
    2199        
    2200648    testdir = GetMythUI()->GetThemeDir() + "/" + menuname;
    2201649    file.setFileName(testdir);
    2202650    if (file.exists())
    2203651        return testdir;
    2204        
     652
    2205653    testdir = GetShareDir() + menuname;
    2206654    file.setFileName(testdir);
    2207655    if (file.exists())
    2208656        return testdir;
    2209        
     657
    2210658    testdir = "../mythfrontend/" + menuname;
    2211659    file.setFileName(testdir);
    2212660    if (file.exists())
    2213661        return testdir;
    2214        
     662
    2215663    return "";
    2216664}
    2217665
     
    2220668 *  \param action single action to be handled
    2221669 *  \return true if the action is not to EXEC another program
    2222670 */
    2223 bool MythThemedMenuPrivate::handleAction(const QString &action)
     671bool MythThemedMenu::handleAction(const QString &action)
    2224672{
    2225673    MythUIMenuCallbacks *cbs = GetMythUI()->GetMenuCBs();
    2226674
     
    2229677        QString rest = action.right(action.length() - 5);
    2230678        if (cbs && cbs->exec_program)
    2231679            cbs->exec_program(rest);
    2232        
     680
    2233681        return false;
    2234682    }
    2235683    else if (action.left(7) == "EXECTV ")
     
    2240688    }
    2241689    else if (action.left(5) == "MENU ")
    2242690    {
    2243         QString rest = action.right(action.length() - 5);
     691        QString menu = action.right(action.length() - 5);
    2244692
    2245         if (rest == "main_settings.xml" &&
     693        if (menu == "main_settings.xml" &&
    2246694            GetMythDB()->GetNumSetting("SetupPinCodeRequired", 0) &&
    2247695            !checkPinCode("SetupPinCodeTime", "SetupPinCode", "Setup Pin:"))
    2248696        {
    2249697            return true;
    2250698        }
    2251699
    2252         MythScreenStack *stack = parent->GetScreenStack();
     700        MythScreenStack *stack = GetScreenStack();
    2253701
    2254         MythThemedMenu *newmenu = new MythThemedMenu(m_state->themeDir,
    2255                                                      rest, stack, rest,
    2256                                                      m_state->allowreorder,
    2257                                                      m_state);
     702        MythThemedMenu *newmenu = new MythThemedMenu("", menu, stack, menu,
     703                                                     false, m_state);
    2258704        stack->AddScreen(newmenu);
    2259705    }
    2260706    else if (action.left(6) == "UPMENU")
    2261707    {
    2262         wantpop = true;
     708        m_wantpop = true;
    2263709    }
    2264710    else if (action.left(12) == "CONFIGPLUGIN")
    2265711    {
     
    2275721    }
    2276722    else if (action.left(8) == "SHUTDOWN")
    2277723    {
    2278         if (allocedstate)
     724        if (m_allocedstate)
    2279725        {
    2280             wantpop = true;
     726            m_wantpop = true;
    2281727        }
    2282728    }
    2283729    else if (action.left(5) == "EJECT")
     
    2292738    }
    2293739    else
    2294740    {
    2295         selection = action;
    2296         if (m_state->callback != NULL)
    2297             m_state->callback(m_state->callbackdata, selection);
     741        m_selection = action;
     742        if (m_state->m_callback)
     743            m_state->m_callback(m_state->m_callbackdata, m_selection);
    2298744    }
    2299745
    2300     return true;   
     746    return true;
    2301747}
    2302748
    2303 
    2304 bool MythThemedMenuPrivate::findDepends(const QString &fileList)
     749bool MythThemedMenu::findDepends(const QString &fileList)
    2305750{
    2306751    QStringList files = fileList.split(" ");
    2307752    QString filename;
    2308753
    2309     for (QStringList::Iterator it = files.begin(); it != files.end(); ++it ) 
     754    for (QStringList::Iterator it = files.begin(); it != files.end(); ++it )
    2310755    {
    2311756        QString filename = findMenuFile(*it);
    2312757        if (filename != "" && filename.endsWith(".xml"))
     
    2330775 *  \param text              the message text to be displayed
    2331776 *  \return true if password checks out or is not needed.
    2332777 */
    2333 bool MythThemedMenuPrivate::checkPinCode(const QString &timestamp_setting,
     778bool MythThemedMenu::checkPinCode(const QString &timestamp_setting,
    2334779                                         const QString &password_setting,
    2335780                                         const QString &text)
    2336781{
     
    2349794    }
    2350795    else
    2351796    {
    2352         QDateTime last_time = QDateTime::fromString(last_time_stamp, 
     797        QDateTime last_time = QDateTime::fromString(last_time_stamp,
    2353798                                                    Qt::TextDate);
    2354799        if (last_time.secsTo(curr_time) < 120)
    2355800        {
     
    2383828
    2384829    return false;
    2385830}
    2386 
    2387 ////////////////////////////////////////////////////////////////////////////
    2388 
    2389 /** \brief Creates a themed menu.
    2390  *
    2391  *  \param cdir         directory where theme is stored
    2392  *  \param menufile     file name of menu definition file
    2393  *  \param parent       the screen stack that owns this UI type
    2394  *  \param name         the name of this UI type
    2395  *  \param allowreorder will buttons be inserted into new rows or pushed back
    2396  *  \param state        theme state associated with this menu
    2397  */
    2398 MythThemedMenu::MythThemedMenu(const QString &cdir, const QString &menufile,
    2399                                MythScreenStack *parent, const QString &name,
    2400                                bool allowreorder, MythThemedMenuState *state)
    2401               : MythScreenType(parent, name)
    2402 {
    2403     d = new MythThemedMenuPrivate(this, cdir, state);
    2404     d->m_state->allowreorder = allowreorder;
    2405 
    2406     Init(cdir, menufile);
    2407 }
    2408 
    2409 /** \brief Loads the main UI theme, and a menu theme.
    2410  *
    2411  *  See also foundTheme(void), it will return true when called after
    2412  *  this method if this method was successful.
    2413  *
    2414  *  See also ReloadTheme(void) which you can use to load a generic theme,
    2415  *  if foundTheme(void) returns false after calling this.
    2416  *
    2417  *  \param cdir directory where theme.xml is stored
    2418  *  \param menufile name of menu item xml file
    2419  */
    2420 void MythThemedMenu::Init(const QString &cdir, const QString &menufile)
    2421 {
    2422     QString dir = QString(cdir) + "/";
    2423     QString filename = dir + "theme.xml";
    2424 
    2425     d->foundtheme = true;
    2426     QFile filetest(filename);
    2427     if (!filetest.exists())
    2428     {
    2429         d->foundtheme = false;
    2430         return;
    2431     }
    2432 
    2433     ReloadExitKey();
    2434 
    2435     if (!d->m_state->loaded)
    2436         d->foundtheme = d->m_state->parseSettings(dir, "theme.xml");
    2437 
    2438     if (d->foundtheme)
    2439         d->parseMenu(menufile);
    2440 }
    2441 
    2442 MythThemedMenu::~MythThemedMenu(void)
    2443 {
    2444     if (d)
    2445         delete d;
    2446 }
    2447 
    2448 /// \brief Returns true iff a theme has been found by a previous call to
    2449 ///        Init(const char*,const char*) or ReloadTheme().
    2450 bool MythThemedMenu::foundTheme(void)
    2451 {
    2452     return d->foundtheme;
    2453 }
    2454 
    2455 /// \brief Set the themed menus callback function and data for that function
    2456 void MythThemedMenu::setCallback(void (*lcallback)(void *, QString &), void *data)
    2457 {
    2458     d->m_state->callback = lcallback;
    2459     d->m_state->callbackdata = data;
    2460 }
    2461 
    2462 void MythThemedMenu::setKillable(void)
    2463 {
    2464     d->m_state->killable = true;
    2465 }
    2466 
    2467 QString MythThemedMenu::getSelection(void)
    2468 {
    2469     return d->selection;
    2470 }
    2471 
    2472 /** \brief Looks at "AllowQuitShutdown" setting in DB, in order to
    2473  *         determine what to show to user on exit from the frontend.
    2474  */
    2475 void MythThemedMenu::ReloadExitKey(void)
    2476 {
    2477     int allowsd = GetMythDB()->GetNumSetting("AllowQuitShutdown");
    2478 
    2479     if (allowsd == 1)
    2480         d->exitModifier = Qt::ControlModifier;
    2481     else if (allowsd == 2)
    2482         d->exitModifier = Qt::MetaModifier;
    2483     else if (allowsd == 3)
    2484         d->exitModifier = Qt::AltModifier;
    2485     else if (allowsd == 4)
    2486         d->exitModifier = 0;
    2487     else
    2488         d->exitModifier = -1;
    2489 }
    2490 
    2491 /** \brief Reset and reparse everything, check foundTheme(void) for success.
    2492  *
    2493  *  Note: this does not use the theme or menu file chosen in Init(), but
    2494  *  instead uses defaults which should work if MythTV was properly installed.
    2495  */
    2496 void MythThemedMenu::ReloadTheme(void)
    2497 {
    2498     if (!d->ReloadTheme())
    2499         d->foundtheme = false;
    2500 }
    2501 
    2502 /** \brief keyboard/LIRC event handler.
    2503  *
    2504  *  This translates key presses through the "menu" context into MythTV
    2505  *  actions and then handles them as appropriate.
    2506  */
    2507 bool MythThemedMenu::keyPressEvent(QKeyEvent *e)
    2508 {
    2509     if (d->ignorekeys)
    2510         return false;
    2511 
    2512     d->ignorekeys = true;
    2513 
    2514     bool ret = true;
    2515     if (!d->keyPressHandler(e))
    2516     {
    2517         ret = MythScreenType::keyPressEvent(e);
    2518     }
    2519 
    2520     d->ignorekeys = false;
    2521 
    2522 
    2523     if (d->wantpop)
    2524         m_ScreenStack->PopScreen();
    2525 
    2526     return ret;
    2527 }
    2528 
    2529 /** \brief Interprets mouse gestures as MythTV action events
    2530  *
    2531  *  \param origtype originating element type from the screen
    2532  *  \param ge       mouse gesture event
    2533  */
    2534 void MythThemedMenu::gestureEvent(MythUIType *origtype, MythGestureEvent *ge)
    2535 {
    2536     if (d->gestureEvent(origtype, ge))
    2537     {
    2538         if (d->wantpop)
    2539             m_ScreenStack->PopScreen();
    2540     }
    2541     else
    2542         MythScreenType::gestureEvent(origtype, ge);
    2543 }
    2544 
    2545 void MythThemedMenu::aboutToShow()
    2546 {
    2547     MythScreenType::aboutToShow();
    2548     d->updateLCD();
    2549 }
  • mythtv/libs/libmythui/myththemedmenu.h

     
    22#define MYTHTHEMEDMENU_H_
    33
    44#include "mythscreentype.h"
     5#include "mythuistatetype.h"
     6#include "mythuibuttonlist.h"
     7#include "xmlparsebase.h"
    58
    69#include <QKeyEvent>
    710
    811class MythMainWindow;
    9 class MythThemedMenuPrivate;
    1012class MythThemedMenuState;
    1113
     14struct ThemedButton
     15{
     16    QString type;
     17    QStringList action;
     18    QString text;
     19    MythImage *icon;
     20    bool active;
     21};
     22
     23Q_DECLARE_METATYPE(ThemedButton*);
     24
     25struct ButtonIcon
     26{
     27    QString name;
     28    MythImage *icon;
     29    MythImage *activeicon;
     30    QPoint offset;
     31};
     32
     33
     34/** \class MyththemedMenuState
     35 *  \brief Private class that controls the settings of buttons, logos,
     36 *         backgrounds, texts, and more, for the MythThemedMenu class.
     37 */
     38class MythThemedMenuState : public MythScreenType
     39{
     40  public:
     41    MythThemedMenuState(MythScreenStack *parent, const QString &name);
     42   ~MythThemedMenuState();
     43
     44    bool Create(void);
     45
     46    ButtonIcon *getButtonIcon(const QString &type);
     47
     48    QMap<QString, ButtonIcon> m_allButtonIcons;
     49    QMap<QString, MythImage *> m_titleIcons;
     50    QString m_titleText;
     51
     52    void (*m_callback)(void *, QString &);
     53    void *m_callbackdata;
     54
     55    bool m_killable;
     56
     57    bool m_loaded;
     58    MythUIStateType *m_titleState;
     59    MythUIStateType *m_watermarkState;
     60    MythUIButtonList *m_buttonList;
     61
     62  protected:
     63    void CopyFrom(MythUIType*);
     64};
     65
    1266/// \brief Themed menu class, used for main menus in %MythTV frontend
    13 class MythThemedMenu : public MythScreenType
     67class MythThemedMenu : public MythThemedMenuState
    1468{
    1569    Q_OBJECT
    1670  public:
    1771    MythThemedMenu(const QString &cdir, const QString &menufile,
    18                    MythScreenStack *parent, const QString &name,
    19                    bool allowreorder = true, MythThemedMenuState *state = NULL);
     72                    MythScreenStack *parent, const QString &name,
     73                    bool allowreorder = false, MythThemedMenuState *state = NULL);
    2074   ~MythThemedMenu();
    2175
    2276    bool foundTheme(void);
     
    3286
    3387  protected:
    3488    virtual bool keyPressEvent(QKeyEvent *e);
    35     virtual void gestureEvent(MythUIType *origtype, MythGestureEvent *ge);
    3689
     90  private slots:
     91    void setButtonActive(MythUIButtonListItem* item);
     92    void buttonAction(MythUIButtonListItem* item);
     93
    3794  private:
    38     void Init(const QString &cdir, const QString &menufile);
     95    void Init(const QString &menufile);
    3996
    40     MythThemedMenuPrivate *d;
     97    bool keyPressHandler(QKeyEvent *e);
     98    bool keyHandler(QStringList &actions, bool fullexit);
     99
     100    bool parseMenu(const QString &menuname);
     101    void parseThemeButton(QDomElement &element);
     102
     103    void addButton(const QString &type, const QString &text,
     104                   const QString &alttext, const QStringList &action);
     105
     106    bool handleAction(const QString &action);
     107    bool findDepends(const QString &fileList);
     108    QString findMenuFile(const QString &menuname);
     109
     110    bool checkPinCode(const QString &timestamp_setting,
     111                      const QString &password_setting,
     112                      const QString &text);
     113
     114    void updateLCD(void);
     115
     116    MythThemedMenu *m_parent;
     117
     118    MythThemedMenuState *m_state;
     119    bool m_allocedstate;
     120
     121    ThemedButton *m_activebutton;
     122
     123    QString m_selection;
     124    bool m_foundtheme;
     125
     126    int m_exitModifier;
     127
     128    bool m_ignorekeys;
     129
     130    bool m_wantpop;
     131
     132    QString m_titleText;
     133    QString m_menumode;
    41134};
    42135
    43136#endif