Ticket #11533: themedmenu.patch

File themedmenu.patch, 110.5 KB (added by Jim Stichnoth, 6 years ago)
  • mythtv/libs/libmythtv/tv_actions.h

    commit df6f98bb109e584c37da6818472d12234f15636f
    Author: Jim Stichnoth <stichnot@localhost>
    Date:   Thu May 9 09:47:00 2013 -0700
    
        Initial implementation of Themed Menus.
        
        Themed Menus allows customization of menus through a hierarchical xml
        description, consisting of actions, action groups, and submenus.  An
        action group is essentially a macro that expands into a possibly
        context-dependent list of actions.
        
        This is demonstrated by replacing the deeply complex and hierarchical
        Playback OSD menu with a themed version, defined in menu_playback.xml.
        In addition, a "compact" version of the Playback OSD menu is
        supported, with the MENUCOMPACT action and an associated key binding
        to display it.
        
        When more menus are converted to themed versions, the MenuBase class
        should be moved out of tv_play.cpp and tv_play.h into a new file.
    
    diff --git a/mythtv/libs/libmythtv/tv_actions.h b/mythtv/libs/libmythtv/tv_actions.h
    index b2ffe61..34edf37 100644
    a b  
    33
    44#define ACTION_EXITSHOWNOPROMPTS "EXITSHOWNOPROMPTS"
    55
     6#define ACTION_MENUCOMPACT   "MENUCOMPACT"
    67#define ACTION_PLAYBACK      "PLAYBACK"
    78#define ACTION_STOP          "STOPPLAYBACK"
    89#define ACTION_DAYLEFT       "DAYLEFT"
  • mythtv/libs/libmythtv/tv_play.cpp

    diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
    index 1575a6c..aad11f0 100644
    a b using namespace std; 
    1616#include <QEvent>
    1717#include <QFile>
    1818#include <QDir>
     19#include <QDomDocument>
     20#include <QDomElement>
     21#include <QDomNode>
     22
     23Q_DECLARE_METATYPE(QDomNode)
    1924
    2025#include "signalhandling.h"
    2126#include "mythdb.h"
    void TV::InitKeys(void) 
    569574
    570575    REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls",
    571576            "Exit or return to DVD menu"), "Esc");
     577    REG_KEY("TV Playback", ACTION_MENUCOMPACT, QT_TRANSLATE_NOOP("MythControls",
     578            "Playback Compact Menu"), "Alt+M");
    572579    REG_KEY("TV Playback", ACTION_CLEAROSD, QT_TRANSLATE_NOOP("MythControls",
    573580            "Clear OSD"), "Backspace");
    574581    REG_KEY("TV Playback", ACTION_PAUSE, QT_TRANSLATE_NOOP("MythControls",
    bool TV::ActiveHandleAction(PlayerContext *ctx, 
    44814488        ChangeTimeStretch(ctx, -1);
    44824489    else if (has_action("MENU", actions))
    44834490        ShowOSDMenu(ctx);
     4491    else if (has_action(ACTION_MENUCOMPACT, actions))
     4492        ShowOSDMenu(ctx, true);
    44844493    else if (has_action("INFO", actions) ||
    44854494             has_action("INFOWITHCUTLIST", actions))
    44864495    {
    void TV::customEvent(QEvent *e) 
    91779186    {
    91789187        DialogCompletionEvent *dce =
    91799188            reinterpret_cast<DialogCompletionEvent*>(e);
    9180         OSDDialogEvent(dce->GetResult(), dce->GetResultText(),
    9181                        dce->GetData().toString());
     9189        if (dce->GetData().userType() == qMetaTypeId<QDomNode>())
     9190        {
     9191            QDomNode data = qVariantValue<QDomNode>(dce->GetData());
     9192            if (dce->GetResult() == -1) // menu exit/back
     9193                MenuShow(data.parentNode(), data);
     9194            else
     9195                MenuShow(data, QDomNode());
     9196        }
     9197        else
     9198            OSDDialogEvent(dce->GetResult(), dce->GetResultText(),
     9199                           dce->GetData().toString());
    91829200        return;
    91839201    }
    91849202
    void TV::OSDDialogEvent(int result, QString text, QString action) 
    1059310611        action.remove("DIALOG_");
    1059410612        QStringList desc = action.split("_");
    1059510613        bool valid = desc.size() == 3;
    10596         if (valid && desc[0] == "MENU")
    10597         {
    10598             ShowOSDMenu(actx, desc[1], text);
    10599             hide = false;
    10600         }
    10601         else if (valid && desc[0] == ACTION_JUMPREC)
     10614        if (valid && desc[0] == ACTION_JUMPREC)
    1060210615        {
    1060310616            FillOSDMenuJumpRec(actx, desc[1], desc[2].toInt(), text);
    1060410617            hide = false;
    void TV::HandleOSDInfo(PlayerContext *ctx, QString action) 
    1095910972    }
    1096010973}
    1096110974
    10962 void TV::ShowOSDMenu(const PlayerContext *ctx, const QString category,
    10963                      const QString selected)
     10975bool MenuBase::MenuLoadFromFile(const QString &filename,
     10976                                const QString &menuname,
     10977                                const char *translationContext,
     10978                                const QString &keyBindingContext)
    1096410979{
    10965     QString cat = category.isEmpty() ? "MAIN" : category;
     10980    bool result = false;
    1096610981
    10967     OSD *osd = GetOSDLock(ctx);
    10968     if (osd)
    10969     {
    10970         osd->DialogShow(OSD_DLG_MENU, tr("Playback Menu"));
    10971         QString currenttext = QString();
    10972         QString back = QString();
    10973 
    10974         FillOSDMenuAudio    (ctx, osd, cat, selected, currenttext, back);
    10975         FillOSDMenuVideo    (ctx, osd, cat, selected, currenttext, back);
    10976         FillOSDMenuSubtitles(ctx, osd, cat, selected, currenttext, back);
    10977         FillOSDMenuPlayback (ctx, osd, cat, selected, currenttext, back);
    10978         FillOSDMenuNavigate (ctx, osd, cat, selected, currenttext, back);
    10979         FillOSDMenuSchedule (ctx, osd, cat, selected, currenttext, back);
    10980         FillOSDMenuSource   (ctx, osd, cat, selected, currenttext, back);
    10981         FillOSDMenuJobs     (ctx, osd, cat, selected, currenttext, back);
    10982 
    10983         if (!currenttext.isEmpty())
    10984             osd->DialogSetText(currenttext);
    10985         if (!back.isEmpty() && !category.isEmpty())
    10986             osd->DialogBack(cat, QString("DIALOG_MENU_%1_0").arg(back));
    10987     }
    10988     ReturnOSDLock(ctx, osd);
    10989 }
    10990 
    10991 void TV::FillOSDMenuAudio(const PlayerContext *ctx, OSD *osd,
    10992                           QString category, const QString selected,
    10993                           QString &currenttext, QString &backaction)
    10994 {
    10995     QStringList tracks;
    10996     uint curtrack = ~0;
    10997     bool avsync = true;
    10998     bool visual = false;
    10999     QString active = QString("");
    11000     bool upmixing = false;
    11001     bool canupmix = false;
    11002     ctx->LockDeletePlayer(__FILE__, __LINE__);
    11003     if (ctx->player)
    11004     {
    11005         visual = ctx->player->CanVisualise();
    11006         active = ctx->player->GetVisualiserName();
    11007         tracks = ctx->player->GetTracks(kTrackTypeAudio);
    11008         if (!tracks.empty())
    11009             curtrack = (uint) ctx->player->GetTrack(kTrackTypeAudio);
    11010         avsync = (ctx->player->GetTrackCount(kTrackTypeVideo) > 0) &&
    11011                   !tracks.empty();
    11012         upmixing = ctx->player->GetAudio()->IsUpmixing();
    11013         canupmix = ctx->player->GetAudio()->CanUpmix();
    11014     }
    11015     ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    11016 
    11017     if (tracks.empty()) // No audio
    11018         return;
    11019 
    11020     if (category == "MAIN")
    11021     {
    11022         osd->DialogAddButton(tr("Audio"), "DIALOG_MENU_AUDIO_0",
    11023                              true, selected == "AUDIO");
    11024     }
    11025     else if (category == "AUDIO")
     10982    m_translationContext = translationContext;
     10983    m_keyBindingContext = keyBindingContext;
     10984    const QStringList searchpath = GetMythUI()->GetThemeSearchPath();
     10985    QStringList::const_iterator it = searchpath.begin();
     10986    for (; !result && it != searchpath.end(); ++it)
    1102610987    {
    11027         // TODO Add mute and volume
    11028         backaction = "MAIN";
    11029         currenttext = tr("Audio");
    11030         if (tracks.size() > 1)
    11031         {
    11032             osd->DialogAddButton(tr("Select Audio Track"),
    11033                                  "DIALOG_MENU_AUDIOTRACKS_0", true,
    11034                                  selected == "AUDIOTRACKS");
    11035         }
    11036         if (avsync)
    11037             osd->DialogAddButton(tr("Adjust Audio Sync"), ACTION_TOGGELAUDIOSYNC);
    11038         if (visual)
     10988        QString themefile = *it + filename;
     10989        LOG(VB_PLAYBACK, LOG_INFO,
     10990            LOC + QString("Loading menu %1").arg(themefile));
     10991        QFile file(themefile);
     10992        if (file.open(QIODevice::ReadOnly))
    1103910993        {
    11040             osd->DialogAddButton(tr("Visualisation"),
    11041                                  "DIALOG_MENU_VISUALISATIONS_0", true,
    11042                                  selected == "VISUALISATIONS");
    11043         }
    11044 
    11045         if (canupmix)
    11046         {
    11047             if (upmixing)
     10994            m_menuDocument = new QDomDocument();
     10995            if (m_menuDocument->setContent(&file))
    1104810996            {
    11049                 osd->DialogAddButton(tr("Disable Audio Upmixer"),
    11050                                      ACTION_DISABLEUPMIX);
     10997                result = true;
     10998                m_menuName =
     10999                    MenuTranslate(MenuGetRoot().attribute("text", menuname));
     11000                QDomElement root = MenuGetRoot();
     11001                MenuProcessIncludes(root);
    1105111002            }
    1105211003            else
    1105311004            {
    11054                 osd->DialogAddButton(tr("Enable Audio Upmixer"),
    11055                                      ACTION_ENABLEUPMIX);
     11005                delete m_menuDocument;
     11006                m_menuDocument = NULL;
    1105611007            }
     11008            file.close();
    1105711009        }
    11058 
    11059     }
    11060     else if (category == "AUDIOTRACKS")
    11061     {
    11062         backaction  = "AUDIO";
    11063         currenttext = tr("Select Audio Track");
    11064         for (uint i = 0; i < (uint)tracks.size(); i++)
     11010        if (!result)
    1106511011        {
    11066             osd->DialogAddButton(tracks[i],
    11067                                  "SELECTAUDIO_" + QString::number(i),
    11068                                  false, i == curtrack);
     11012            LOG(VB_FILE, LOG_ERR, LOC + "No theme file " + themefile);
    1106911013        }
    1107011014    }
    11071     else if (category == "VISUALISATIONS")
     11015
     11016    return result;
     11017}
     11018
     11019void MenuBase::MenuProcessIncludes(QDomElement &root)
     11020{
     11021    const int maxRecursion = 10;
     11022    for (QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling())
    1107211023    {
    11073         backaction = "AUDIO";
    11074         currenttext = tr("Visualisation");
    11075         osd->DialogAddButton(tr("None"),
    11076                              ACTION_DISABLEVISUALISATION, false,
    11077                              active.isEmpty());
    11078         QStringList visualisers;
    11079         ctx->LockDeletePlayer(__FILE__, __LINE__);
    11080         if (ctx->player)
    11081             visualisers = ctx->player->GetVisualiserList();
    11082         ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    11083         for (int i = 0; i < visualisers.size(); i++)
     11024        if (n.isElement())
    1108411025        {
    11085             osd->DialogAddButton(visualisers[i],
    11086                 "VISUALISER_" + visualisers[i], false,
    11087                 active == visualisers[i]);
     11026            QDomElement e = n.toElement();
     11027            if (e.tagName() == "include")
     11028            {
     11029                QString include = e.attribute("file", "");
     11030                int level = m_recursionLevel + 1;
     11031                if (level > maxRecursion)
     11032                {
     11033                    LOG(VB_GENERAL, LOG_ERR,
     11034                        QString("Maximum include depth (%1) "
     11035                                "exceeded for %2")
     11036                        .arg(maxRecursion).arg(include));
     11037                    return;
     11038                }
     11039                MenuBase menu;
     11040                menu.m_recursionLevel = level;
     11041                if (menu.MenuLoadFromFile(include, include,
     11042                                          m_translationContext,
     11043                                          m_keyBindingContext))
     11044                {
     11045                    QDomNode newChild = menu.MenuGetRoot();
     11046                    newChild = m_menuDocument->importNode(newChild, true);
     11047                    root.replaceChild(newChild, n);
     11048                    n = newChild;
     11049                }
     11050            }
     11051            else if (e.tagName() == "menu")
     11052            {
     11053                MenuProcessIncludes(e);
     11054            }
    1108811055        }
    1108911056    }
    1109011057}
    1109111058
    11092 void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
    11093                           QString category, const QString selected,
    11094                           QString &currenttext, QString &backaction)
     11059MenuBase::~MenuBase()
    1109511060{
    11096     QStringList tracks;
    11097     //uint curtrack                     = ~0;
    11098     uint sup                          = kPictureAttributeSupported_None;
    11099     bool studio_levels                = false;
    11100     bool autodetect                   = false;
    11101     AdjustFillMode adjustfill         = kAdjustFill_Off;
    11102     AspectOverrideMode aspectoverride = kAspect_Off;
    11103     FrameScanType scan_type           = kScan_Ignore;
    11104     bool scan_type_locked             = false;
    11105     bool stereoallowed                = false;
    11106     StereoscopicMode stereomode       = kStereoscopicModeNone;
    11107 
    11108     ctx->LockDeletePlayer(__FILE__, __LINE__);
    11109     if (ctx->player)
     11061    if (m_menuDocument)
    1111011062    {
    11111         tracks           = ctx->player->GetTracks(kTrackTypeVideo);
    11112         aspectoverride   = ctx->player->GetAspectOverride();
    11113         adjustfill       = ctx->player->GetAdjustFill();
    11114         scan_type        = ctx->player->GetScanType();
    11115         scan_type_locked = ctx->player->IsScanTypeLocked();
    11116         //if (!tracks.empty())
    11117         //    curtrack = (uint) ctx->player->GetTrack(kTrackTypeVideo);
    11118         VideoOutput *vo = ctx->player->GetVideoOutput();
    11119         if (vo)
    11120         {
    11121             sup = vo->GetSupportedPictureAttributes();
    11122             studio_levels = vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0;
    11123             autodetect = !vo->hasHWAcceleration();
    11124             stereoallowed = vo->StereoscopicModesAllowed();
    11125             stereomode = vo->GetStereoscopicMode();
    11126         }
     11063        delete m_menuDocument;
     11064        m_menuDocument = NULL;
    1112711065    }
    11128     ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     11066}
    1112911067
    11130     if (tracks.empty()) // No video
    11131         return;
     11068QDomElement MenuBase::MenuGetRoot(void) const
     11069{
     11070    return m_menuDocument->documentElement();
     11071}
    1113211072
    11133     if (category == "MAIN")
     11073QString MenuBase::MenuTranslate(const QString &text) const
     11074{
     11075    return qApp->translate(m_translationContext, text.toUtf8(), NULL,
     11076                           QCoreApplication::UnicodeUTF8);
     11077}
     11078
     11079bool MenuBase::MenuShowHelper(const QDomNode &node,
     11080                              const QDomNode &selected,
     11081                              bool doDisplay)
     11082{
     11083    bool hasSelected = false;
     11084    bool displayed = false;
     11085    for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling())
    1113411086    {
    11135         osd->DialogAddButton(tr("Video"), "DIALOG_MENU_VIDEO_0",
    11136                              true, selected == "VIDEO");
     11087        if (n == selected)
     11088            hasSelected = true;
    1113711089    }
    11138     else if (category == "VIDEO")
     11090    for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling())
    1113911091    {
    11140         // TODO Add deinterlacer, filters, video track
    11141         backaction = "MAIN";
    11142         currenttext = tr("Video");
    11143         if (ctx == GetPlayer(ctx, 0))
     11092        if (n.isElement())
    1114411093        {
    11145             osd->DialogAddButton(tr("Change Aspect Ratio"),
    11146                                  "DIALOG_MENU_VIDEOASPECT_0", true,
    11147                                  selected == "VIDEOASPECT");
    11148             osd->DialogAddButton(tr("Adjust Fill"),
    11149                                  "DIALOG_MENU_ADJUSTFILL_0", true,
    11150                                  selected == "ADJUSTFILL");
    11151             osd->DialogAddButton(tr("Manual Zoom Mode"), "TOGGLEMANUALZOOM");
    11152             if (sup != kPictureAttributeSupported_None)
     11094            QDomElement e = n.toElement();
     11095            QString text  = e.attribute("text", "");
     11096            text          = MenuTranslate(text);
     11097            QString show = e.attribute("show", "");
     11098            MenuShowContext showContext =
     11099                (show == "active" ? kMenuShowActive :
     11100                 show == "inactive" ? kMenuShowInactive : kMenuShowAlways);
     11101            QString current = e.attribute("current", "");
     11102            bool currentActive = (current == "active") && !hasSelected;
     11103            if (e.tagName() == "menu")
    1115311104            {
    11154                 osd->DialogAddButton(tr("Adjust Picture"),
    11155                                      "DIALOG_MENU_ADJUSTPICTURE_0", true,
    11156                                      selected == "ADJUSTPICTURE");
     11105                MenuItemContext c(n, text, (hasSelected && n == selected),
     11106                                  doDisplay);
     11107                displayed |= MenuDisplayItem(c);
     11108            }
     11109            else if (e.tagName() == "item")
     11110            {
     11111                QString action = e.attribute("action", "");
     11112                MenuItemContext c(n, showContext, currentActive, action,
     11113                                  text, doDisplay);
     11114                displayed |= MenuDisplayItem(c);
     11115            }
     11116            else if (e.tagName() == "itemlist")
     11117            {
     11118                QString actiongroup = e.attribute("actiongroup", "");
     11119                MenuItemContext c(n, showContext, currentActive,
     11120                                  actiongroup, doDisplay);
     11121                displayed |= MenuDisplayItem(c);
    1115711122            }
    1115811123        }
    11159         if (stereoallowed)
    11160         {
    11161             osd->DialogAddButton(tr("3D"), "DIALOG_MENU_3D_0",
    11162                                  true, selected == "3D");
    11163         }
    11164         osd->DialogAddButton(tr("Advanced"), "DIALOG_MENU_ADVANCEDVIDEO_0",
    11165                              true, selected == "ADVANCEDVIDEO");
     11124        if (!doDisplay && displayed)
     11125            break; // early exit optimization
    1116611126    }
    11167     else if (category == "VIDEOASPECT")
    11168     {
    11169         backaction = "VIDEO";
    11170         currenttext = tr("Change Aspect Ratio");
     11127    return displayed;
     11128}
    1117111129
    11172         for (int j = kAspect_Off; j < kAspect_END; j++)
    11173         {
    11174             // swap 14:9 and 16:9
    11175             int i = ((kAspect_14_9 == j) ? kAspect_16_9 :
    11176                      ((kAspect_16_9 == j) ? kAspect_14_9 : j));
    11177             osd->DialogAddButton(toString((AspectOverrideMode) i),
    11178                                  QString("TOGGLEASPECT%1").arg(i), false,
    11179                                  aspectoverride == i);
    11180         }
    11181     }
    11182     else if (category == "ADJUSTFILL")
    11183     {
    11184         backaction = "VIDEO";
    11185         currenttext = tr("Adjust Fill");
     11130static bool matchesGroup(const QString &name, const QString &inPrefix,
     11131                         MenuCategory category, QString &outPrefix)
     11132{
     11133    outPrefix = name;
     11134    return ((category == kMenuCategoryItem && name.startsWith(inPrefix)) ||
     11135            (category == kMenuCategoryItemlist && name == inPrefix));
     11136}
    1118611137
    11187         if (autodetect)
    11188         {
    11189             osd->DialogAddButton(tr("Auto Detect"), "AUTODETECT_FILL",
    11190                  false, (adjustfill == kAdjustFill_AutoDetect_DefaultHalf) ||
    11191                         (adjustfill == kAdjustFill_AutoDetect_DefaultOff));
    11192         }
    11193         for (int i = kAdjustFill_Off; i < kAdjustFill_END; i++)
    11194         {
    11195             osd->DialogAddButton(toString((AdjustFillMode) i),
    11196                                  QString("TOGGLEFILL%1").arg(i), false,
    11197                                  adjustfill == i);
    11198         }
    11199     }
    11200     else if (category == "ADJUSTPICTURE")
     11138static void addButton(const MenuItemContext &c, OSD *osd, bool active,
     11139                      bool &result, const QString &action,
     11140                      const QString &defaultTextActive,
     11141                      const QString &defaultTextInactive = "",
     11142                      bool isMenu = false)
     11143{
     11144    if (c.m_category == kMenuCategoryItemlist || action == c.m_action)
    1120111145    {
    11202         backaction = "VIDEO";
    11203         currenttext = tr("Adjust Picture");
    11204         for (int i = kPictureAttribute_MIN; i < kPictureAttribute_MAX; i++)
     11146        if ((c.m_showContext != kMenuShowInactive && active) ||
     11147            (c.m_showContext != kMenuShowActive && !active))
    1120511148        {
    11206             if (toMask((PictureAttribute)i) & sup)
     11149            result = true;
     11150            if (c.m_doDisplay)
    1120711151            {
    11208                 if ((PictureAttribute)i == kPictureAttribute_StudioLevels)
    11209                 {
    11210                     QString msg = studio_levels ? tr("Disable studio levels") :
    11211                                                   tr("Enable studio levels");
    11212                     osd->DialogAddButton(msg, ACTION_TOGGLESTUDIOLEVELS);
    11213                 }
    11214                 else
    11215                 {
    11216                     osd->DialogAddButton(toString((PictureAttribute) i),
    11217                                    QString("TOGGLEPICCONTROLS%1").arg(i));
    11218                 }
     11152                QString text = c.m_actionText;
     11153                if (text.isEmpty())
     11154                    text = (active || defaultTextInactive.isEmpty()) ?
     11155                        defaultTextActive : defaultTextInactive;
     11156                osd->DialogAddButton(text, action, isMenu,
     11157                                     active && c.m_setCurrentActive);
    1121911158            }
    1122011159        }
    11221         osd->DialogAddButton(
    11222             gCoreContext->GetNumSetting("NightModeEnabled", 0) ?
    11223             tr("Disable Night Mode") : tr("Enable Night Mode"),
    11224             ACTION_TOGGLENIGHTMODE);
    11225     }
    11226     else if (category == "3D")
    11227     {
    11228         backaction = "VIDEO";
    11229         currenttext = tr("3D");
    11230         osd->DialogAddButton(tr("None"),
    11231                              ACTION_3DNONE, false,
    11232                              stereomode == kStereoscopicModeNone);
    11233         osd->DialogAddButton(tr("Side by Side"),
    11234                              ACTION_3DSIDEBYSIDE, false,
    11235                              stereomode == kStereoscopicModeSideBySide);
    11236         osd->DialogAddButton(tr("Discard Side by Side"),
    11237                              ACTION_3DSIDEBYSIDEDISCARD, false,
    11238                              stereomode == kStereoscopicModeSideBySideDiscard);
    11239         osd->DialogAddButton(tr("Top and Bottom"),
    11240                              ACTION_3DTOPANDBOTTOM, false,
    11241                              stereomode == kStereoscopicModeTopAndBottom);
    11242         osd->DialogAddButton(tr("Discard Top and Bottom"),
    11243                              ACTION_3DTOPANDBOTTOMDISCARD, false,
    11244                              stereomode == kStereoscopicModeTopAndBottomDiscard);
    11245     }
    11246     else if (category == "ADVANCEDVIDEO")
    11247     {
    11248         osd->DialogAddButton(tr("Video Scan"),
    11249                              "DIALOG_MENU_VIDEOSCAN_0", true,
    11250                              selected == "VIDEOSCAN");
    11251         if (kScan_Progressive != scan_type)
    11252         {
    11253             osd->DialogAddButton(tr("Deinterlacer"),
    11254                                  "DIALOG_MENU_DEINTERLACER_0", true,
    11255                                  selected == "DEINTERLACER");
    11256         }
    11257         backaction = "VIDEO";
    11258         currenttext = tr("Advanced");
    11259     }
    11260     else if (category == "DEINTERLACER")
    11261     {
    11262         backaction = "ADVANCEDVIDEO";
    11263         currenttext = tr("Deinterlacer");
    11264 
    11265         QStringList deinterlacers;
    11266         QString     currentdeinterlacer;
    11267         bool        doublerate = false;
    11268         ctx->LockDeletePlayer(__FILE__, __LINE__);
    11269         if (ctx->player && ctx->player->GetVideoOutput())
    11270         {
    11271             ctx->player->GetVideoOutput()->GetDeinterlacers(deinterlacers);
    11272             currentdeinterlacer = ctx->player->GetVideoOutput()->GetDeinterlacer();
    11273             doublerate = ctx->player->CanSupportDoubleRate();
    11274         }
    11275         ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    11276 
    11277         foreach (QString deint, deinterlacers)
    11278         {
    11279             if ((deint.contains("doublerate") ||
    11280                 deint.contains("doubleprocess") ||
    11281                 deint.contains("bobdeint")) && !doublerate)
    11282             {
    11283                 continue;
    11284             }
    11285             QString trans = VideoDisplayProfile::GetDeinterlacerName(deint);
    11286             osd->DialogAddButton(trans, "DEINTERLACER_" + deint, false,
    11287                                  deint == currentdeinterlacer);
    11288         }
    11289     }
    11290     else if (category == "VIDEOSCAN")
    11291     {
    11292         backaction = "ADVANCEDVIDEO";
    11293         currenttext = tr("Video Scan");
    11294 
    11295         QString cur_mode = "";
    11296         if (!scan_type_locked)
    11297         {
    11298             if (kScan_Interlaced == scan_type)
    11299                 cur_mode = tr("(I)", "Interlaced (Normal)");
    11300             else if (kScan_Intr2ndField == scan_type)
    11301                 cur_mode = tr("(i)", "Interlaced (Reversed)");
    11302             else if (kScan_Progressive == scan_type)
    11303                 cur_mode = tr("(P)", "Progressive");
    11304             cur_mode = " " + cur_mode;
    11305             scan_type = kScan_Detect;
    11306         }
    11307 
    11308         osd->DialogAddButton(tr("Detect") + cur_mode, "SELECTSCAN_0", false,
    11309                              scan_type == kScan_Detect);
    11310         osd->DialogAddButton(tr("Progressive"), "SELECTSCAN_3", false,
    11311                              scan_type == kScan_Progressive);
    11312         osd->DialogAddButton(tr("Interlaced (Normal)"), "SELECTSCAN_1", false,
    11313                              scan_type == kScan_Interlaced);
    11314         osd->DialogAddButton(tr("Interlaced (Reversed)"), "SELECTSCAN_2", false,
    11315                              scan_type == kScan_Intr2ndField);
    11316     }
    11317 }
    11318 
    11319 void TV::FillOSDMenuSubtitles(const PlayerContext *ctx, OSD *osd,
    11320                               QString category, const QString selected,
    11321                               QString &currenttext, QString &backaction)
    11322 {
    11323     uint capmode  = 0;
    11324     QStringList av_tracks;
    11325     QStringList cc708_tracks;
    11326     QStringList cc608_tracks;
    11327     QStringList ttx_tracks;
    11328     QStringList ttm_tracks;
    11329     QStringList text_tracks;
    11330     uint av_curtrack    = ~0;
    11331     uint cc708_curtrack = ~0;
    11332     uint cc608_curtrack = ~0;
    11333     uint ttx_curtrack   = ~0;
    11334     uint text_curtrack  = ~0;
    11335     bool havetext = false;
    11336     bool forcedon = true;
    11337     bool enabled  = false;
    11338     ctx->LockDeletePlayer(__FILE__, __LINE__);
    11339     if (ctx->player)
    11340     {
    11341         capmode      = ctx->player->GetCaptionMode();
    11342         enabled      = ctx->player->GetCaptionsEnabled();
    11343         havetext     = ctx->player->HasTextSubtitles();
    11344         forcedon     = ctx->player->GetAllowForcedSubtitles();
    11345         av_tracks    = ctx->player->GetTracks(kTrackTypeSubtitle);
    11346         cc708_tracks = ctx->player->GetTracks(kTrackTypeCC708);
    11347         cc608_tracks = ctx->player->GetTracks(kTrackTypeCC608);
    11348         ttx_tracks   = ctx->player->GetTracks(kTrackTypeTeletextCaptions);
    11349         ttm_tracks   = ctx->player->GetTracks(kTrackTypeTeletextMenu);
    11350         text_tracks  = ctx->player->GetTracks(kTrackTypeRawText);
    11351         if (!av_tracks.empty())
    11352             av_curtrack = (uint) ctx->player->GetTrack(kTrackTypeSubtitle);
    11353         if (!cc708_tracks.empty())
    11354             cc708_curtrack = (uint) ctx->player->GetTrack(kTrackTypeCC708);
    11355         if (!cc608_tracks.empty())
    11356             cc608_curtrack = (uint) ctx->player->GetTrack(kTrackTypeCC608);
    11357         if (!ttx_tracks.empty())
    11358             ttx_curtrack = (uint) ctx->player->GetTrack(kTrackTypeTeletextCaptions);
    11359         if (!text_tracks.empty())
    11360             text_curtrack = (uint) ctx->player->GetTrack(kTrackTypeRawText);
    1136111160    }
    11362     ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     11161}
    1136311162
    11364     bool have_subs = !av_tracks.empty() || havetext || !cc708_tracks.empty() ||
    11365                      !cc608_tracks.empty() || !ttx_tracks.empty() ||
    11366                      !text_tracks.empty();
     11163#define BUTTON(action, text) \
     11164    addButton(c, osd, active, result, (action), (text))
     11165#define BUTTON2(action, textActive, textInactive) \
     11166    addButton(c, osd, active, result, (action), (textActive), (textInactive))
     11167#define BUTTON3(action, textActive, textInactive, isMenu)     \
     11168    addButton(c, osd, active, result, (action), (textActive), \
     11169              (textInactive), (isMenu))
    1136711170
    11368     if (category == "MAIN")
     11171// Returns true if at least one item should be displayed.
     11172bool TV::MenuDisplayItem(const MenuItemContext &c)
     11173{
     11174    MenuCategory category = c.m_category;
     11175    const QString &actionName = c.m_action;
     11176
     11177    bool result = false;
     11178    bool active = true;
     11179    PlayerContext *ctx = m_tvmCtx;
     11180    OSD *osd = m_tvmOsd;
     11181    if (!osd)
     11182        return result;
     11183    if (category == kMenuCategoryMenu)
    1136911184    {
    11370         if (have_subs || !ttm_tracks.empty())
     11185        result = MenuShowHelper(c.m_node, QDomNode(), false);
     11186        if (result && c.m_doDisplay)
    1137111187        {
    11372             osd->DialogAddButton(tr("Subtitles"),
    11373                                  "DIALOG_MENU_SUBTITLES_0",
    11374                                  true, selected == "SUBTITLES");
     11188            QVariant v;
     11189            v.setValue(c.m_node);
     11190            osd->DialogAddButton(c.m_menuName, v, true, c.m_setCurrentActive);
    1137511191        }
     11192        return result;
    1137611193    }
    11377     else if (category == "SUBTITLES")
     11194    ctx->LockDeletePlayer(__FILE__, __LINE__);
     11195    QString prefix;
     11196    if (matchesGroup(actionName, "VISUALISER_", category, prefix))
    1137811197    {
    11379         backaction = "MAIN";
    11380         currenttext = tr("Subtitles");
    11381 
    11382         uint capmode = ctx->player->GetCaptionMode();
    11383         if (have_subs && enabled)
    11384             osd->DialogAddButton(tr("Disable Subtitles"), ACTION_DISABLESUBS);
    11385         else if (have_subs && !enabled)
    11386             osd->DialogAddButton(tr("Enable Subtitles"), ACTION_ENABLESUBS);
    11387         if (!av_tracks.empty() || !text_tracks.empty())
    11388         {
    11389             if (forcedon)
    11390             {
    11391                 osd->DialogAddButton(tr("Disable Forced Subtitles"),
    11392                                      ACTION_DISABLEFORCEDSUBS);
    11393             }
    11394             else
    11395             {
    11396                 osd->DialogAddButton(tr("Enable Forced Subtitles"),
    11397                                      ACTION_ENABLEFORCEDSUBS);
    11398             }
    11399             if (!av_tracks.empty())
    11400                 osd->DialogAddButton(tr("Select Subtitle"),
    11401                                      "DIALOG_MENU_AVSUBTITLES_0",
    11402                                      true, selected == "AVSUBTITLES");
    11403         }
    11404         if (havetext || !text_tracks.empty())
     11198        for (int i = 0; i < m_tvm_visualisers.size(); i++)
    1140511199        {
    11406             osd->DialogAddButton(tr("Text Subtitles"),
    11407                                  "DIALOG_MENU_TEXTSUBTITLES_0",
    11408                                  true, selected == "TEXTSUBTITLES");
     11200            QString action = prefix + m_tvm_visualisers[i];
     11201            active = (m_tvm_active == m_tvm_visualisers[i]);
     11202            BUTTON(action, m_tvm_visualisers[i]);
    1140911203        }
    11410         if (!cc708_tracks.empty())
    11411         {
    11412             osd->DialogAddButton(tr("Select ATSC CC"),
    11413                                  "DIALOG_MENU_708SUBTITLES_0",
    11414                                  true, selected == "708SUBTITLES");
    11415         }
    11416         if (!cc608_tracks.empty())
    11417         {
    11418             osd->DialogAddButton(tr("Select VBI CC"),
    11419                                  "DIALOG_MENU_608SUBTITLES_0",
    11420                                  true, selected == "608SUBTITLES");
    11421         }
    11422         if (!ttx_tracks.empty())
    11423         {
    11424             osd->DialogAddButton(tr("Select Teletext CC"),
    11425                                  "DIALOG_MENU_TTXSUBTITLES_0",
    11426                                  true, selected == "TTXSUBTITLES");
    11427         }
    11428         if (!ttm_tracks.empty())
    11429             osd->DialogAddButton(tr("Toggle Teletext Menu"), "TOGGLETTM");
    11430         if (enabled)
    11431             osd->DialogAddButton(tr("Adjust Subtitle Zoom"),
    11432                                  ACTION_TOGGLESUBTITLEZOOM);
    11433         if (enabled &&
    11434             (capmode == kDisplayRawTextSubtitle ||
    11435              capmode == kDisplayTextSubtitle))
    11436             osd->DialogAddButton(tr("Adjust Subtitle Delay"),
    11437                                  ACTION_TOGGLESUBTITLEDELAY);
    1143811204    }
    11439     else if (category == "AVSUBTITLES")
     11205    else if (matchesGroup(actionName, "TOGGLEASPECT", category, prefix))
    1144011206    {
    11441         backaction = "SUBTITLES";
    11442         currenttext = tr("Select Subtitle");
    11443         for (uint i = 0; i < (uint)av_tracks.size(); i++)
     11207        if (ctx == GetPlayer(ctx, 0))
    1144411208        {
    11445             osd->DialogAddButton(av_tracks[i],
    11446                                  "SELECTSUBTITLE_" + QString::number(i),
    11447                                  false, i == av_curtrack);
     11209            for (int j = kAspect_Off; j < kAspect_END; j++)
     11210            {
     11211                // swap 14:9 and 16:9
     11212                int i = ((kAspect_14_9 == j) ? kAspect_16_9 :
     11213                         ((kAspect_16_9 == j) ? kAspect_14_9 : j));
     11214                QString action = prefix + QString::number(i);
     11215                active = (m_tvm_aspectoverride == i);
     11216                BUTTON(action, toString((AspectOverrideMode) i));
     11217            }
    1144811218        }
    1144911219    }
    11450     else if (category == "TEXTSUBTITLES")
     11220    else if (matchesGroup(actionName, "TOGGLEFILL", category, prefix))
    1145111221    {
    11452         backaction = "SUBTITLES";
    11453         currenttext = tr("Text Subtitles");
    11454         if (havetext)
     11222        if (ctx == GetPlayer(ctx, 0))
    1145511223        {
    11456             if (capmode == kDisplayTextSubtitle)
    11457             {
    11458                 osd->DialogAddButton(tr("Disable External Subtitles"),
    11459                                      ACTION_DISABLEEXTTEXT);
    11460             }
    11461             else
     11224            for (int i = kAdjustFill_Off; i < kAdjustFill_END; i++)
    1146211225            {
    11463                 osd->DialogAddButton(tr("Enable External Subtitles"),
    11464                                      ACTION_ENABLEEXTTEXT);
     11226                QString action = prefix + QString::number(i);
     11227                active = (m_tvm_adjustfill == i);
     11228                BUTTON(action, toString((AdjustFillMode) i));
    1146511229            }
    1146611230        }
    11467         if (!text_tracks.empty())
     11231    }
     11232    else if (matchesGroup(actionName, "TOGGLEPICCONTROLS", category, prefix))
     11233    {
     11234        if (ctx == GetPlayer(ctx, 0))
    1146811235        {
    11469             for (uint i = 0; i < (uint)text_tracks.size(); i++)
     11236            for (int i = kPictureAttribute_MIN; i < kPictureAttribute_MAX; i++)
    1147011237            {
    11471                 osd->DialogAddButton(text_tracks[i],
    11472                                      "SELECTRAWTEXT_" + QString::number(i),
    11473                                      false, i == text_curtrack);
     11238                if (toMask((PictureAttribute)i) & m_tvm_sup)
     11239                {
     11240                    QString action = prefix +
     11241                        QString::number(i - kPictureAttribute_MIN);
     11242                    active = m_tvm_studio_levels;
     11243                    if ((PictureAttribute)i == kPictureAttribute_StudioLevels)
     11244                        BUTTON(ACTION_TOGGLESTUDIOLEVELS,
     11245                               toString((AdjustFillMode) i));
     11246                    else
     11247                        BUTTON(action, toString((PictureAttribute) i));
     11248                }
    1147411249            }
    1147511250        }
    1147611251    }
    11477     else if (category == "708SUBTITLES")
     11252    else if (matchesGroup(actionName, "3D", category, prefix))
    1147811253    {
    11479         backaction = "SUBTITLES";
    11480         currenttext = tr("Select ATSC CC");
    11481         for (uint i = 0; i < (uint)cc708_tracks.size(); i++)
     11254        if (m_tvm_stereoallowed)
    1148211255        {
    11483             osd->DialogAddButton(cc708_tracks[i],
    11484                                  "SELECTCC708_" + QString::number(i),
    11485                                  false, i == cc708_curtrack);
     11256            active = (m_tvm_stereomode == kStereoscopicModeNone);
     11257            BUTTON(ACTION_3DNONE, tr("None"));
     11258            active = (m_tvm_stereomode == kStereoscopicModeSideBySide);
     11259            BUTTON(ACTION_3DSIDEBYSIDE, tr("Side by Side"));
     11260            active = (m_tvm_stereomode == kStereoscopicModeSideBySideDiscard);
     11261            BUTTON(ACTION_3DSIDEBYSIDEDISCARD, tr("Discard Side by Side"));
     11262            active = (m_tvm_stereomode == kStereoscopicModeTopAndBottom);
     11263            BUTTON(ACTION_3DTOPANDBOTTOM, tr("Top and Bottom"));
     11264            active = (m_tvm_stereomode == kStereoscopicModeTopAndBottomDiscard);
     11265            BUTTON(ACTION_3DTOPANDBOTTOMDISCARD, tr("Discard Top and Bottom"));
    1148611266        }
    1148711267    }
    11488     else if (category == "608SUBTITLES")
     11268    else if (matchesGroup(actionName, "SELECTSCAN_", category, prefix))
    1148911269    {
    11490         backaction = "SUBTITLES";
    11491         currenttext = tr("Select VBI CC");
    11492         for (uint i = 0; i < (uint)cc608_tracks.size(); i++)
    11493         {
    11494             osd->DialogAddButton(cc608_tracks[i],
    11495                                  "SELECTCC608_" + QString::number(i),
    11496                                  false, i == cc608_curtrack);
    11497         }
     11270        active = (m_tvm_scan_type_unlocked == kScan_Detect);
     11271        BUTTON("SELECTSCAN_0", tr("Detect") + m_tvm_cur_mode);
     11272        active = (m_tvm_scan_type_unlocked == kScan_Progressive);
     11273        BUTTON("SELECTSCAN_3", tr("Progressive"));
     11274        active = (m_tvm_scan_type_unlocked == kScan_Interlaced);
     11275        BUTTON("SELECTSCAN_1", tr("Interlaced (Normal)"));
     11276        active = (m_tvm_scan_type_unlocked == kScan_Intr2ndField);
     11277        BUTTON("SELECTSCAN_2", tr("Interlaced (Reversed)"));
    1149811278    }
    11499     else if (category == "TTXSUBTITLES")
     11279    else if (matchesGroup(actionName, "DEINTERLACER_", category, prefix))
    1150011280    {
    11501         backaction = "SUBTITLES";
    11502         currenttext = tr("Select Teletext CC");
    11503         for (uint i = 0; i < (uint)ttx_tracks.size(); i++)
     11281        if (m_tvm_scan_type != kScan_Progressive)
    1150411282        {
    11505             osd->DialogAddButton(ttx_tracks[i],
    11506                                  "SELECTTTC_" + QString::number(i),
    11507                                  false, i == ttx_curtrack);
    11508         }
    11509     }
    11510 }
    11511 
    11512 void TV::FillOSDMenuNavigate(const PlayerContext *ctx, OSD *osd,
    11513                              QString category, const QString selected,
    11514                              QString &currenttext, QString &backaction)
    11515 {
    11516     int num_chapters  = GetNumChapters(ctx);
    11517     int num_titles    = GetNumTitles(ctx);
    11518     int num_angles    = GetNumAngles(ctx);
    11519     TVState state     = ctx->GetState();
    11520     bool isdvd        = state == kState_WatchingDVD;
    11521     bool isbd         = ctx->buffer && ctx->buffer->IsBD() &&
    11522                         ctx->buffer->BD()->IsHDMVNavigation();
    11523     bool islivetv     = StateIsLiveTV(state);
    11524     bool isrecording  = state == kState_WatchingPreRecorded ||
    11525                         state == kState_WatchingRecording;
    11526     bool previouschan = false;
    11527     if (islivetv)
    11528     {
    11529         QString prev_channum = ctx->GetPreviousChannel();
    11530         QString cur_channum  = QString();
    11531         if (ctx->tvchain)
    11532             cur_channum = ctx->tvchain->GetChannelName(-1);
    11533         if (!prev_channum.isEmpty() && prev_channum != cur_channum)
    11534             previouschan = true;
     11283            foreach (QString deint, m_tvm_deinterlacers)
     11284            {
     11285                if ((deint.contains("doublerate") ||
     11286                     deint.contains("doubleprocess") ||
     11287                     deint.contains("bobdeint")) && !m_tvm_doublerate)
     11288                {
     11289                    continue;
     11290                }
     11291                QString action = prefix + deint;
     11292                active = (deint == m_tvm_currentdeinterlacer);
     11293                QString trans = VideoDisplayProfile::GetDeinterlacerName(deint);
     11294                BUTTON(action, trans);
     11295            }
     11296        }
     11297    }
     11298    else if (matchesGroup(actionName, "SELECTSUBTITLE_", category, prefix) ||
     11299             matchesGroup(actionName, "SELECTRAWTEXT_",  category, prefix) ||
     11300             matchesGroup(actionName, "SELECTCC708_",    category, prefix) ||
     11301             matchesGroup(actionName, "SELECTCC608_",    category, prefix) ||
     11302             matchesGroup(actionName, "SELECTTTC_",      category, prefix) ||
     11303             matchesGroup(actionName, "SELECTAUDIO_",    category, prefix))
     11304    {
     11305        int i = 0;
     11306        TrackType type = kTrackTypeUnknown;
     11307        if (prefix == "SELECTSUBTITLE_")
     11308            type = kTrackTypeSubtitle;
     11309        else if (prefix == "SELECTRAWTEXT_")
     11310            type = kTrackTypeRawText;
     11311        else if (prefix == "SELECTCC708_")
     11312            type = kTrackTypeCC708;
     11313        else if (prefix == "SELECTCC608_")
     11314            type = kTrackTypeCC608;
     11315        else if (prefix == "SELECTTTC_")
     11316            type = kTrackTypeTeletextCaptions;
     11317        else if (prefix == "SELECTAUDIO_")
     11318        {
     11319            type = kTrackTypeAudio;
     11320            if (m_tvm_tracks[type].size() <= 1)
     11321                i = 1; // don't show choices if only 1 audio track
     11322        }
     11323
     11324        for (; i < m_tvm_tracks[type].size(); i++)
     11325        {
     11326            QString action = prefix + QString::number(i);
     11327            active = (i == m_tvm_curtrack[type]);
     11328            BUTTON(action, m_tvm_tracks[type][i]);
     11329        }
     11330    }
     11331    else if (matchesGroup(actionName, "ADJUSTSTRETCH", category, prefix))
     11332    {
     11333        static struct {
     11334            int speedX100;
     11335            QString suffix;
     11336            QString trans;
     11337        } speeds[] = {
     11338            {  0, "",    tr("Adjust")},
     11339            { 50, "0.5", tr("0.5x")},
     11340            { 90, "0.9", tr("0.9x")},
     11341            {100, "1.0", tr("1.0x")},
     11342            {110, "1.1", tr("1.1x")},
     11343            {120, "1.2", tr("1.2x")},
     11344            {130, "1.3", tr("1.3x")},
     11345            {140, "1.4", tr("1.4x")},
     11346            {150, "1.5", tr("1.5x")},
     11347        };
     11348        for (uint i = 0; i < sizeof(speeds) / sizeof(*speeds); ++i)
     11349        {
     11350            QString action = prefix + speeds[i].suffix;
     11351            active = (m_tvm_speedX100 == speeds[i].speedX100);
     11352            BUTTON(action, speeds[i].trans);
     11353        }
     11354    }
     11355    else if (matchesGroup(actionName, "TOGGLESLEEP", category, prefix))
     11356    {
     11357        active = false;
     11358        if (sleepTimerId)
     11359            BUTTON(ACTION_TOGGLESLEEP + "ON", tr("Sleep Off"));
     11360        BUTTON(ACTION_TOGGLESLEEP + "30", tr("%n minute(s)", "", 30));
     11361        BUTTON(ACTION_TOGGLESLEEP + "60", tr("%n minute(s)", "", 60));
     11362        BUTTON(ACTION_TOGGLESLEEP + "90", tr("%n minute(s)", "", 90));
     11363        BUTTON(ACTION_TOGGLESLEEP + "120", tr("%n minute(s)", "", 120));
    1153511364    }
    11536 
    11537     bool jump = !num_chapters && !isdvd && !isbd &&
    11538                 ctx->buffer->IsSeekingAllowed();
    11539     bool show = isdvd || num_chapters || num_titles || previouschan ||
    11540                 isrecording || num_angles || jump;
    11541 
    11542     if (category == "MAIN")
     11365    else if (matchesGroup(actionName, "CHANGROUP_", category, prefix))
    1154311366    {
    11544         if (show)
     11367        if (db_use_channel_groups)
    1154511368        {
    11546             osd->DialogAddButton(tr("Navigate"), "DIALOG_MENU_NAVIGATE_0",
    11547                                  true, selected == "NAVIGATE");
     11369            active = false;
     11370            BUTTON("CHANGROUP_ALL_CHANNELS", tr("All Channels"));
     11371            ChannelGroupList::const_iterator it;
     11372            for (it = db_channel_groups.begin();
     11373                 it != db_channel_groups.end(); ++it)
     11374            {
     11375                QString action = prefix + QString::number(it->grpid);
     11376                active = ((int)(it->grpid) == channelGroupId);
     11377                BUTTON(action, it->name);
     11378            }
    1154811379        }
    1154911380    }
    11550     else if (category == "NAVIGATE")
     11381    else if (matchesGroup(actionName, "TOGGLECOMMSKIP", category, prefix))
    1155111382    {
    11552         backaction = "MAIN";
    11553         currenttext = tr("Navigate");
    11554         if (jump)
    11555         {
    11556             osd->DialogAddButton(tr("Jump Ahead"), ACTION_JUMPFFWD, false, false);
    11557             osd->DialogAddButton(tr("Jump Back"), ACTION_JUMPRWND, false, false);
    11558         }
    11559         if (isrecording)
    11560         {
    11561             osd->DialogAddButton(tr("Commercial Auto-Skip"),
    11562                                  "DIALOG_MENU_COMMSKIP_0",
    11563                                  true, selected == "COMMSKIP");
    11564         }
    11565         if (isbd)
    11566         {
    11567             osd->DialogAddButton(tr("Top menu"), ACTION_JUMPTODVDROOTMENU);
    11568             osd->DialogAddButton(tr("Popup menu"), ACTION_JUMPTOPOPUPMENU);
    11569         }
    11570         if (isdvd)
     11383        static uint cas_ord[] = { 0, 2, 1 };
     11384        if (m_tvm_isrecording || m_tvm_isrecorded)
    1157111385        {
    11572             osd->DialogAddButton(tr("DVD Root Menu"),    ACTION_JUMPTODVDROOTMENU);
    11573             osd->DialogAddButton(tr("DVD Title Menu"),   ACTION_JUMPTODVDTITLEMENU);
    11574             osd->DialogAddButton(tr("DVD Chapter Menu"), ACTION_JUMPTODVDCHAPTERMENU);
    11575         }
    11576         if (previouschan)
    11577         {
    11578             osd->DialogAddButton(tr("Previous Channel"), "PREVCHAN");
    11579         }
    11580         if (num_chapters)
    11581         {
    11582             osd->DialogAddButton(tr("Chapter"), "DIALOG_MENU_AVCHAPTER_0",
    11583                                  true, selected == "AVCHAPTER");
    11584         }
    11585         if (num_angles > 1)
    11586         {
    11587             osd->DialogAddButton(tr("Angle"), "DIALOG_MENU_AVANGLE_0",
    11588                                  true, selected == "AVANGLE");
    11589         }
    11590         if (num_titles)
    11591         {
    11592             osd->DialogAddButton(tr("Title"), "DIALOG_MENU_AVTITLE_0",
    11593                                  true, selected == "AVTITLE");
     11386            for (uint i = 0; i < sizeof(cas_ord)/sizeof(cas_ord[0]); i++)
     11387            {
     11388                const CommSkipMode mode = (CommSkipMode) cas_ord[i];
     11389                QString action = prefix + QString::number(cas_ord[i]);
     11390                active = (mode == m_tvm_curskip);
     11391                BUTTON(action, toString((CommSkipMode) cas_ord[i]));
     11392            }
    1159411393        }
    1159511394    }
    11596     else if (category == "AVCHAPTER")
     11395    else if (matchesGroup(actionName, "JUMPTOCHAPTER", category, prefix))
    1159711396    {
    11598         backaction = "NAVIGATE";
    11599         currenttext = tr("Chapter");
    11600         int current_chapter =  GetCurrentChapter(ctx);
    11601         QList<long long> times;
    11602         GetChapterTimes(ctx, times);
    11603         if (num_chapters == times.size())
     11397        if (m_tvm_num_chapters &&
     11398            m_tvm_num_chapters == m_tvm_chapter_times.size())
    1160411399        {
    11605             int size = QString::number(num_chapters).size();
    11606             for (int i = 0; i < num_chapters; i++)
     11400            int size = QString::number(m_tvm_num_chapters).size();
     11401            for (int i = 0; i < m_tvm_num_chapters; i++)
    1160711402            {
    11608                 int hours   = times[i] / 60 / 60;
    11609                 int minutes = (times[i] / 60) - (hours * 60);
    11610                 int secs    = times[i] % 60;
     11403                int hours   = m_tvm_chapter_times[i] / 60 / 60;
     11404                int minutes = (m_tvm_chapter_times[i] / 60) - (hours * 60);
     11405                int secs    = m_tvm_chapter_times[i] % 60;
    1161111406                QString chapter1 = QString("%1").arg(i+1, size, 10, QChar(48));
    1161211407                QString chapter2 = QString("%1").arg(i+1, 3   , 10, QChar(48));
    1161311408                QString desc = chapter1 + QString(" (%1:%2:%3)")
    11614                     .arg(hours, 2, 10, QChar(48)).arg(minutes, 2, 10, QChar(48))
    11615                     .arg(secs, 2, 10, QChar(48));
    11616                 osd->DialogAddButton(desc, ACTION_JUMPCHAPTER + chapter2,
    11617                                      false, current_chapter == (i + 1));
     11409                    .arg(hours,   2, 10, QChar(48))
     11410                    .arg(minutes, 2, 10, QChar(48))
     11411                    .arg(secs,    2, 10, QChar(48));
     11412                QString action = prefix + chapter2;
     11413                active = (m_tvm_current_chapter == (i + 1));
     11414                BUTTON(action, desc);
    1161811415            }
    1161911416        }
    1162011417    }
    11621     else if (category == "AVTITLE")
     11418    else if (matchesGroup(actionName, "SWITCHTOANGLE", category, prefix))
    1162211419    {
    11623         backaction = "NAVIGATE";
    11624         currenttext = tr("Title");
    11625         int current_title = GetCurrentTitle(ctx);
    11626 
    11627         for (int i = 0; i < num_titles; i++)
     11420        if (m_tvm_num_angles > 1)
     11421        {
     11422            for (int i = 1; i <= m_tvm_num_angles; i++)
     11423            {
     11424                QString angleIdx = QString("%1").arg(i, 3, 10, QChar(48));
     11425                QString desc = GetAngleName(ctx, i);
     11426                QString action = prefix + angleIdx;
     11427                active = (m_tvm_current_angle == i);
     11428                BUTTON(action, desc);
     11429            }
     11430        }
     11431    }
     11432    else if (matchesGroup(actionName, "JUMPTOTITLE", category, prefix))
     11433    {
     11434        for (int i = 0; i < m_tvm_num_titles; i++)
    1162811435        {
    1162911436            if (GetTitleDuration(ctx, i) < 120) // Ignore < 2 minutes long
    1163011437                continue;
    1163111438
    1163211439            QString titleIdx = QString("%1").arg(i, 3, 10, QChar(48));
    1163311440            QString desc = GetTitleName(ctx, i);
    11634             osd->DialogAddButton(desc, ACTION_SWITCHTITLE + titleIdx,
    11635                                  false, current_title == i);
     11441            QString action = prefix + titleIdx;
     11442            active = (m_tvm_current_title == i);
     11443            BUTTON(action, desc);
    1163611444        }
    1163711445    }
    11638     else if (category == "AVANGLE")
     11446    else if (matchesGroup(actionName, "SWITCHTOINPUT_", category, prefix))
    1163911447    {
    11640         backaction = "NAVIGATE";
    11641         currenttext = tr("Angle");
    11642         int current_angle = GetCurrentAngle(ctx);
    11643 
    11644         for (int i = 1; i <= num_angles; i++)
     11448        if (ctx->recorder)
    1164511449        {
    11646             QString angleIdx = QString("%1").arg(i, 3, 10, QChar(48));
    11647             QString desc = GetAngleName(ctx, i);
    11648             osd->DialogAddButton(desc, ACTION_SWITCHANGLE + angleIdx,
    11649                                  false, current_angle == i);
    11650         }
    11651     }
    11652     else if (category == "COMMSKIP")
    11653     {
    11654         backaction = "NAVIGATE";
    11655         currenttext = tr("Commercial Auto-Skip");
    11656         uint cas_ord[] = { 0, 2, 1 };
    11657         ctx->LockDeletePlayer(__FILE__, __LINE__);
    11658         CommSkipMode cur = kCommSkipOff;
    11659         if (ctx->player)
    11660             cur = ctx->player->GetAutoCommercialSkip();
    11661         ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     11450            vector<uint> cardids = CardUtil::GetCardList();
     11451            uint cardid  = ctx->GetCardID();
     11452            vector<uint> excluded_cardids;
     11453            stable_sort(cardids.begin(), cardids.end());
     11454            excluded_cardids.push_back(cardid);
     11455            vector<uint>::const_iterator it = cardids.begin();
     11456            InfoMap info;
     11457            ctx->recorder->GetChannelInfo(info);
     11458            uint sourceid = info["sourceid"].toUInt();
     11459            for (; it != cardids.end(); ++it)
     11460            {
     11461                vector<InputInfo> inputs =
     11462                    RemoteRequestFreeInputList(*it, excluded_cardids);
    1166211463
    11663         for (uint i = 0; i < sizeof(cas_ord)/sizeof(uint); i++)
    11664         {
    11665             const CommSkipMode mode = (CommSkipMode) cas_ord[i];
    11666             osd->DialogAddButton(toString((CommSkipMode) cas_ord[i]),
    11667                                  QString("TOGGLECOMMSKIP%1").arg(cas_ord[i]),
    11668                                  false, mode == cur);
    11669         }
    11670     }
    11671 }
     11464                for (uint i = 0; i < inputs.size(); i++)
     11465                {
     11466                    // don't add current input to list
     11467                    if ((inputs[i].cardid   == cardid) &&
     11468                        (inputs[i].sourceid == sourceid))
     11469                    {
     11470                        continue;
     11471                    }
    1167211472
    11673 void TV::FillOSDMenuSource(const PlayerContext *ctx, OSD *osd,
    11674                            QString category, const QString selected,
    11675                            QString &currenttext, QString &backaction)
    11676 {
    11677     QMap<uint,InputInfo> sources;
    11678     vector<uint> cardids;
    11679     uint cardid = 0;
    11680     vector<uint> excluded_cardids;
    11681     uint sourceid = 0;
     11473                    QString name = CardUtil::GetDisplayName(inputs[i].inputid);
     11474                    if (name.isEmpty())
     11475                    {
     11476                        name = tr("C", "Card") + ":" +
     11477                            QString::number(*it) + " " +
     11478                            tr("I", "Input") + ":" + inputs[i].name;
     11479                    }
    1168211480
    11683     if ((category == "SOURCE" || category == "INPUTSWITCHING"||
    11684          category == "SOURCESWITCHING") && ctx->recorder)
     11481                    QString action = prefix +
     11482                        QString::number(inputs[i].inputid);
     11483                    active = false;
     11484                    BUTTON(action, name);
     11485                }
     11486            }
     11487        }
     11488    }
     11489    else if (matchesGroup(actionName, "SWITCHTOSOURCE_", category, prefix))
    1168511490    {
    11686         cardids = CardUtil::GetCardList();
    11687         cardid  = ctx->GetCardID();
    11688         // The cardids are already in the preferred order.  Don't
    11689         // alter it if switching sources.
    11690         if (category != "SOURCESWITCHING")
    11691             stable_sort(cardids.begin(), cardids.end());
    11692         excluded_cardids.push_back(cardid);
    11693         InfoMap info;
    11694         ctx->recorder->GetChannelInfo(info);
    11695         sourceid = info["sourceid"].toUInt();
    11696 
    11697         if (category != "INPUTSWITCHING")
     11491        if (ctx->recorder)
    1169811492        {
     11493            QMap<uint,InputInfo> sources;
     11494            vector<uint> cardids;
     11495            uint cardid = 0;
     11496            vector<uint> excluded_cardids;
     11497            uint sourceid = 0;
     11498            cardids = CardUtil::GetCardList();
     11499            cardid  = ctx->GetCardID();
     11500            excluded_cardids.push_back(cardid);
     11501            InfoMap info;
     11502            ctx->recorder->GetChannelInfo(info);
     11503            sourceid = info["sourceid"].toUInt();
    1169911504            // Get sources available on other cards
    1170011505            vector<uint>::const_iterator it = cardids.begin();
    1170111506            for (; it != cardids.end(); ++it)
    1170211507            {
    1170311508                vector<InputInfo> inputs =
    11704                         RemoteRequestFreeInputList(*it, excluded_cardids);
     11509                    RemoteRequestFreeInputList(*it, excluded_cardids);
    1170511510                if (inputs.empty())
    1170611511                    continue;
    1170711512
    1170811513                for (uint i = 0; i < inputs.size(); i++)
    1170911514                    if (!sources.contains(inputs[i].sourceid))
    11710                        sources[inputs[i].sourceid] = inputs[i];
     11515                        sources[inputs[i].sourceid] = inputs[i];
    1171111516            }
    1171211517            // Get other sources available on this card
    1171311518            vector<uint> currentinputs = CardUtil::GetInputIDs(cardid);
    void TV::FillOSDMenuSource(const PlayerContext *ctx, OSD *osd, 
    1172511530            }
    1172611531            // delete current source from list
    1172711532            sources.remove(sourceid);
     11533            QMap<uint,InputInfo>::const_iterator sit = sources.begin();
     11534            for (; sit != sources.end(); ++sit)
     11535            {
     11536                QString action = QString("SWITCHTOINPUT_%1").arg((*sit).inputid);
     11537                BUTTON(action, SourceUtil::GetSourceName((*sit).sourceid));
     11538            }
    1172811539        }
    1172911540    }
    11730 
    11731     if (category == "MAIN")
    11732     {
    11733         osd->DialogAddButton(tr("Source"), "DIALOG_MENU_SOURCE_0",
    11734                              true, selected == "SOURCE");
    11735     }
    11736     else if (category == "SOURCE")
     11541    else if (category == kMenuCategoryItem)
    1173711542    {
    11738         backaction = "MAIN";
    11739         currenttext = tr("Source");
    11740         osd->DialogAddButton(tr("Jump to Program"),
    11741                              "DIALOG_MENU_" + ACTION_JUMPREC + "_0",
    11742                              true, selected == ACTION_JUMPREC);
    11743 
    11744         vector<uint>::const_iterator it = cardids.begin();
    11745         for (; it != cardids.end(); ++it)
     11543        if (actionName == "TOGGLEAUDIOSYNC")
     11544        {
     11545            BUTTON(actionName, tr("Adjust Audio Sync"));
     11546        }
     11547        else if (actionName == "DISABLEVISUALISATION")
    1174611548        {
    11747             vector<InputInfo> inputs = RemoteRequestFreeInputList(
    11748                 *it, excluded_cardids);
    11749             uint testsize = 0;
    11750             for (uint i = 0; i < inputs.size(); i++)
     11549            if (m_tvm_visual)
     11550                BUTTON(actionName, tr("None"));
     11551        }
     11552        else if (actionName == "DISABLEUPMIX")
     11553        {
     11554            if (m_tvm_canupmix)
    1175111555            {
    11752                 if ((inputs[i].cardid   == cardid) &&
    11753                     (inputs[i].sourceid == sourceid))
    11754                 {
    11755                     testsize = 1;
    11756                     break;
    11757                 }
     11556                active = !m_tvm_upmixing;
     11557                BUTTON(actionName, tr("Disable Audio Upmixer"));
    1175811558            }
    11759             if (inputs.size() <= testsize)
    11760                 continue;
    11761             osd->DialogAddButton(tr("Switch Input"),
    11762                                  "DIALOG_MENU_INPUTSWITCHING_0",
    11763                                  true, selected == "INPUTSWITCHING");
    11764             break;
    1176511559        }
    11766         if (!sources.empty())
     11560        else if (actionName == "ENABLEUPMIX")
    1176711561        {
    11768             osd->DialogAddButton(tr("Switch Source"),
    11769                                  "DIALOG_MENU_SOURCESWITCHING_0",
    11770                                  true, selected == "SOURCESWITCHING");
     11562            if (m_tvm_canupmix)
     11563            {
     11564                active = m_tvm_upmixing;
     11565                BUTTON(actionName, tr("Auto Detect"));
     11566            }
    1177111567        }
    11772     }
    11773     else if (category == ACTION_JUMPREC)
    11774     {
    11775         backaction = "SOURCE";
    11776         currenttext = tr("Jump to Program");
    11777         osd->DialogAddButton(tr("Recorded Program"),
    11778                              "DIALOG_" + ACTION_JUMPREC + "_X_0",
    11779                              true, selected == ACTION_JUMPREC + "2");
    11780         if (lastProgram != NULL)
     11568        else if (actionName == "AUTODETECT_FILL")
    1178111569        {
    11782             if (lastProgram->GetSubtitle().isEmpty())
    11783                 osd->DialogAddButton(lastProgram->GetTitle(), ACTION_JUMPPREV);
    11784             else
    11785                 osd->DialogAddButton(QString("%1: %2")
    11786                         .arg(lastProgram->GetTitle())
    11787                         .arg(lastProgram->GetSubtitle()), ACTION_JUMPPREV);
     11570            if (m_tvm_fill_autodetect)
     11571            {
     11572                active =
     11573                    (m_tvm_adjustfill == kAdjustFill_AutoDetect_DefaultHalf) ||
     11574                    (m_tvm_adjustfill == kAdjustFill_AutoDetect_DefaultOff);
     11575                BUTTON(actionName, tr("Auto Detect"));
     11576            }
    1178811577        }
    11789     }
    11790     else if (category == "INPUTSWITCHING")
    11791     {
    11792         backaction = "SOURCE";
    11793         currenttext = tr("Switch Input");
    11794         vector<uint>::const_iterator it = cardids.begin();
    11795         for (; it != cardids.end(); ++it)
     11578        else if (actionName == "TOGGLEMANUALZOOM")
    1179611579        {
    11797             vector<InputInfo> inputs = RemoteRequestFreeInputList(
    11798                 *it, excluded_cardids);;
    11799 
    11800             for (uint i = 0; i < inputs.size(); i++)
     11580            BUTTON(actionName, tr("Manual Zoom Mode"));
     11581        }
     11582        else if (actionName == "TOGGLENIGHTMODE")
     11583        {
     11584            if (m_tvm_sup != kPictureAttributeSupported_None)
    1180111585            {
    11802                 // don't add current input to list
    11803                 if ((inputs[i].cardid   == cardid) &&
    11804                     (inputs[i].sourceid == sourceid))
    11805                 {
    11806                     continue;
    11807                 }
    11808 
    11809                 QString name = CardUtil::GetDisplayName(inputs[i].inputid);
    11810                 if (name.isEmpty())
    11811                 {
    11812                     name = tr("C", "Card") + ":" + QString::number(*it) + " " +
    11813                         tr("I", "Input") + ":" + inputs[i].name;
    11814                 }
    11815 
    11816                 osd->DialogAddButton(name,
    11817                     QString("SWITCHTOINPUT_%1").arg(inputs[i].inputid));
     11586                active = gCoreContext->GetNumSetting("NightModeEnabled", 0);
     11587                BUTTON2(actionName,
     11588                        tr("Disable Night Mode"), tr("Enable Night Mode"));
    1181811589            }
    1181911590        }
    11820     }
    11821     else if (category == "SOURCESWITCHING")
    11822     {
    11823         backaction = "SOURCE";
    11824         currenttext = tr("Switch Source");
    11825         QMap<uint,InputInfo>::const_iterator sit = sources.begin();
    11826         for (; sit != sources.end(); ++sit)
     11591        else if (actionName == "DISABLESUBS")
    1182711592        {
    11828             osd->DialogAddButton(SourceUtil::GetSourceName((*sit).sourceid),
    11829                                  QString("SWITCHTOINPUT_%1").arg((*sit).inputid));
     11593            active = !m_tvm_subs_enabled;
     11594            if (m_tvm_subs_have_subs)
     11595                BUTTON(actionName, tr("Disable Subtitles"));
    1183011596        }
    11831     }
    11832 }
    11833 
    11834 void TV::FillOSDMenuJobs(const PlayerContext *ctx, OSD *osd,
    11835                          QString category, const QString selected,
    11836                          QString &currenttext, QString &backaction)
    11837 {
    11838     TVState state    = ctx->GetState();
    11839     bool islivetv    = StateIsLiveTV(state);
    11840     bool isrecorded  = state == kState_WatchingPreRecorded;
    11841     bool isrecording = state == kState_WatchingRecording;
    11842     bool isvideo     = state == kState_WatchingVideo;
    11843 
    11844     if (category == "MAIN")
    11845     {
    11846         if (islivetv || isrecording || isrecorded || isvideo)
     11597        else if (actionName == "ENABLESUBS")
    1184711598        {
    11848             osd->DialogAddButton(tr("Jobs"), "DIALOG_MENU_JOBS_0",
    11849                                  true, selected == "JOBS");
     11599            active = m_tvm_subs_enabled;
     11600            if (m_tvm_subs_have_subs)
     11601                BUTTON(actionName, tr("Enable Subtitles"));
    1185011602        }
    11851     }
    11852     else if (category == "JOBS")
    11853     {
    11854         backaction = "MAIN";
    11855         currenttext = tr("Jobs");
    11856 
    11857         ctx->LockPlayingInfo(__FILE__, __LINE__);
    11858         bool is_on = ctx->playingInfo->QueryAutoExpire() != kDisableAutoExpire;
    11859         bool transcoding = JobQueue::IsJobQueuedOrRunning(
    11860                                 JOB_TRANSCODE,
    11861                                 ctx->playingInfo->GetChanID(),
    11862                                 ctx->playingInfo->GetScheduledStartTime());
    11863         ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    11864 
    11865         if (islivetv)
     11603        else if (actionName == "DISABLEFORCEDSUBS")
    1186611604        {
    11867             osd->DialogAddButton(tr("Edit Channel"),   "EDIT");
     11605            active = !m_tvm_subs_forcedon;
     11606            if (!m_tvm_tracks[kTrackTypeSubtitle].empty() ||
     11607                !m_tvm_tracks[kTrackTypeRawText].empty())
     11608            {
     11609                BUTTON(actionName, tr("Disable Forced Subtitles"));
     11610            }
    1186811611        }
    11869 
    11870         if (isrecorded || isrecording || isvideo)
     11612        else if (actionName == "ENABLEFORCEDSUBS")
    1187111613        {
    11872             osd->DialogAddButton(tr("Edit Recording"), "EDIT");
     11614            active = m_tvm_subs_forcedon;
     11615            if (!m_tvm_tracks[kTrackTypeSubtitle].empty() ||
     11616                !m_tvm_tracks[kTrackTypeRawText].empty())
     11617            {
     11618                BUTTON(actionName, tr("Enable Forced Subtitles"));
     11619            }
    1187311620        }
    11874 
    11875         if (isrecorded || isrecording)
     11621        else if (actionName == "DISABLEEXTTEXT")
    1187611622        {
    11877             osd->DialogAddButton(is_on ? tr("Turn Auto-Expire OFF") :
    11878                                  tr("Turn Auto-Expire ON"), "TOGGLEAUTOEXPIRE");
     11623            active = m_tvm_subs_capmode != kDisplayTextSubtitle;
     11624            if (m_tvm_subs_havetext)
     11625                BUTTON(actionName, tr("Disable External Subtitles"));
    1187911626        }
    11880 
    11881         if (isrecorded)
     11627        else if (actionName == "ENABLEEXTTEXT")
     11628        {
     11629            active = m_tvm_subs_capmode == kDisplayTextSubtitle;
     11630            if (m_tvm_subs_havetext)
     11631                BUTTON(actionName, tr("Enable External Subtitles"));
     11632        }
     11633        else if (actionName == "TOGGLETTM")
     11634        {
     11635            if (!m_tvm_tracks[kTrackTypeTeletextMenu].empty())
     11636                BUTTON(actionName, tr("Toggle Teletext Menu"));
     11637        }
     11638        else if (actionName == "TOGGLESUBZOOM")
     11639        {
     11640            if (m_tvm_subs_enabled)
     11641                BUTTON(actionName, tr("Adjust Subtitle Zoom"));
     11642        }
     11643        else if (actionName == "TOGGLESUBDELAY")
    1188211644        {
    11883             if (transcoding)
     11645            if (m_tvm_subs_enabled &&
     11646                (m_tvm_subs_capmode == kDisplayRawTextSubtitle ||
     11647                 m_tvm_subs_capmode == kDisplayTextSubtitle))
    1188411648            {
    11885                 osd->DialogAddButton(tr("Stop Transcoding"), "QUEUETRANSCODE");
     11649                BUTTON(actionName, tr("Adjust Subtitle Delay"));
    1188611650            }
    11887             else
     11651        }
     11652        else if (actionName == "PAUSE")
     11653        {
     11654            active = m_tvm_ispaused;
     11655            BUTTON2(actionName, tr("Play"), tr("Pause"));
     11656        }
     11657        else if (actionName == "TOGGLESTRETCH")
     11658        {
     11659            BUTTON(actionName, tr("Toggle"));
     11660        }
     11661        else if (actionName == "CREATEPIPVIEW")
     11662        {
     11663            MenuLazyInit(&m_tvm_freerecordercount);
     11664            if (m_tvm_freerecordercount &&
     11665                player.size() <= kMaxPIPCount && !m_tvm_hasPBP && m_tvm_allowPIP)
    1188811666            {
    11889                 osd->DialogAddButton(tr("Begin Transcoding"),
    11890                                      "DIALOG_MENU_TRANSCODE_0",
    11891                                      true, selected == "TRANSCODE");
     11667                BUTTON(actionName, tr("Open Live TV PIP"));
    1189211668            }
    1189311669        }
    11894     }
    11895     else if (category == "TRANSCODE")
    11896     {
    11897         backaction = "JOBS";
    11898         currenttext = tr("Begin Transcoding");
    11899         osd->DialogAddButton(tr("Default"),       "QUEUETRANSCODE");
    11900         osd->DialogAddButton(tr("Autodetect"),    "QUEUETRANSCODE_AUTO");
    11901         osd->DialogAddButton(tr("High Quality"),  "QUEUETRANSCODE_HIGH");
    11902         osd->DialogAddButton(tr("Medium Quality"),"QUEUETRANSCODE_MEDIUM");
    11903         osd->DialogAddButton(tr("Low Quality"),   "QUEUETRANSCODE_LOW");
    11904     }
    11905 }
    11906 
    11907 void TV::FillOSDMenuPlayback(const PlayerContext *ctx, OSD *osd,
    11908                              QString category, const QString selected,
    11909                              QString &currenttext, QString &backaction)
    11910 {
    11911     bool allowPIP = IsPIPSupported(ctx);
    11912     bool allowPBP = IsPBPSupported(ctx);
    11913     bool ispaused = false;
    11914     ctx->LockDeletePlayer(__FILE__, __LINE__);
    11915     if (ctx->player)
    11916         ispaused = ctx->player->IsPaused();
    11917     ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    11918 
    11919     if (category == "MAIN")
    11920     {
    11921         osd->DialogAddButton(tr("Playback"), "DIALOG_MENU_PLAYBACK_0",
    11922                              true, selected == "PLAYBACK");
    11923     }
    11924     else if (category == "PLAYBACK")
    11925     {
    11926         backaction = "MAIN";
    11927         currenttext = tr("Playback");
    11928 
    11929         osd->DialogAddButton(ispaused ? tr("Play") : tr("Pause"),
    11930                              ACTION_PAUSE, false, false);
    11931         osd->DialogAddButton(tr("Adjust Time Stretch"),
    11932                              "DIALOG_MENU_TIMESTRETCH_0", true,
    11933                               selected == "TIMESTRETCH");
    11934         if (allowPIP || allowPBP)
     11670        else if (actionName == "CREATEPBPVIEW")
    1193511671        {
    11936             osd->DialogAddButton(tr("Picture-in-Picture"),
    11937                                  "DIALOG_MENU_PIP_1", true,
    11938                                  selected == "PIP");
     11672            MenuLazyInit(&m_tvm_freerecordercount);
     11673            if (m_tvm_freerecordercount &&
     11674                player.size() < kMaxPBPCount && !m_tvm_hasPIP && m_tvm_allowPBP)
     11675            {
     11676                BUTTON(actionName, tr("Open Live TV PBP"));
     11677            }
    1193911678        }
    11940         osd->DialogAddButton(tr("Sleep"),
    11941                              "DIALOG_MENU_SLEEP_0", true,
    11942                               selected == "SLEEP");
    11943         if (db_use_channel_groups)
     11679        else if (actionName == "JUMPRECPIP")
    1194411680        {
    11945             osd->DialogAddButton(tr("Channel Groups"),
    11946                                  "DIALOG_MENU_CHANNELGROUP_0",
    11947                                  true, selected == "CHANNELGROUP");
    11948         }
    11949         if (!db_browse_always)
    11950             osd->DialogAddButton(tr("Toggle Browse Mode"), "TOGGLEBROWSE");
    11951         if (inPlaylist)
    11952             osd->DialogAddButton(tr("Cancel Playlist"), "CANCELPLAYLIST");
    11953         osd->DialogAddButton(tr("Playback data"),
    11954                              ACTION_TOGGLEOSDDEBUG, false, false);
    11955     }
    11956     else if (category == "TIMESTRETCH")
    11957     {
    11958         backaction = "PLAYBACK";
    11959         currenttext = tr("Adjust Time Stretch");
    11960         int speedX100 = (int)(round(ctx->ts_normal * 100));
    11961         osd->DialogAddButton(tr("Toggle"), "TOGGLESTRETCH");
    11962         osd->DialogAddButton(tr("Adjust"), "ADJUSTSTRETCH");
    11963         osd->DialogAddButton(tr("0.5X"), "ADJUSTSTRETCH0.5", false, speedX100 == 50);
    11964         osd->DialogAddButton(tr("0.9X"), "ADJUSTSTRETCH0.9", false, speedX100 == 90);
    11965         osd->DialogAddButton(tr("1.0X"), "ADJUSTSTRETCH1.0", false, speedX100 == 100);
    11966         osd->DialogAddButton(tr("1.1X"), "ADJUSTSTRETCH1.1", false, speedX100 == 110);
    11967         osd->DialogAddButton(tr("1.2X"), "ADJUSTSTRETCH1.2", false, speedX100 == 120);
    11968         osd->DialogAddButton(tr("1.3X"), "ADJUSTSTRETCH1.3", false, speedX100 == 130);
    11969         osd->DialogAddButton(tr("1.4X"), "ADJUSTSTRETCH1.4", false, speedX100 == 140);
    11970         osd->DialogAddButton(tr("1.5X"), "ADJUSTSTRETCH1.5", false, speedX100 == 150);
    11971     }
    11972     else if (category == "SLEEP")
    11973     {
    11974         backaction = "PLAYBACK";
    11975         currenttext = tr("Sleep");
    11976         if (sleepTimerId)
    11977             osd->DialogAddButton(tr("Sleep Off"), ACTION_TOGGLESLEEP + "ON");
    11978         osd->DialogAddButton(tr("%n minute(s)", "", 30),
    11979                                 ACTION_TOGGLESLEEP + "30");
    11980         osd->DialogAddButton(tr("%n minute(s)", "", 60),
    11981                                 ACTION_TOGGLESLEEP + "60");
    11982         osd->DialogAddButton(tr("%n minute(s)", "", 90),
    11983                                 ACTION_TOGGLESLEEP + "90");
    11984         osd->DialogAddButton(tr("%n minute(s)", "", 120),
    11985                                 ACTION_TOGGLESLEEP + "120");
    11986     }
    11987     else if (category == "CHANNELGROUP")
    11988     {
    11989         backaction = "PLAYBACK";
    11990         currenttext = tr("Channel Groups");
    11991         osd->DialogAddButton(tr("All Channels"), "CHANGROUP_ALL_CHANNELS");
    11992         ChannelGroupList::const_iterator it;
    11993         for (it = db_channel_groups.begin();
    11994              it != db_channel_groups.end(); ++it)
     11681            if (player.size() <= kMaxPIPCount &&
     11682                !m_tvm_hasPBP && m_tvm_allowPIP)
     11683            {
     11684                BUTTON(actionName, tr("Open Recording PIP"));
     11685            }
     11686        }
     11687        else if (actionName == "JUMPRECPBP")
    1199511688        {
    11996             osd->DialogAddButton(it->name,
    11997                                  QString("CHANGROUP_%1").arg(it->grpid),
    11998                                  false, (int)(it->grpid) == channelGroupId);
     11689            if (player.size() < kMaxPBPCount &&
     11690                !m_tvm_hasPIP && m_tvm_allowPBP)
     11691            {
     11692                BUTTON(actionName, tr("Open Recording PBP"));
     11693            }
    1199911694        }
    12000     }
    12001     else if (category == "PIP" && (allowPIP || allowPBP))
    12002     {
    12003         backaction = "PLAYBACK";
    12004         currenttext = tr("Picture-in-Picture");
    12005         bool hasPBP = (player.size()>1) && GetPlayer(ctx,1)->IsPBP();
    12006         bool hasPIP = (player.size()>1) && GetPlayer(ctx,1)->IsPIP();
    12007 
    12008         if (RemoteGetFreeRecorderCount())
     11695        else if (actionName == "NEXTPIPWINDOW")
    1200911696        {
    12010             if (player.size() <= kMaxPIPCount && !hasPBP && allowPIP)
    12011                 osd->DialogAddButton(tr("Open Live TV PIP"), "CREATEPIPVIEW");
    12012             if (player.size() < kMaxPBPCount && !hasPIP && allowPBP)
    12013                 osd->DialogAddButton(tr("Open Live TV PBP"), "CREATEPBPVIEW");
     11697            if (player.size() > 1)
     11698                BUTTON(actionName, tr("Change Active Window"));
    1201411699        }
    12015 
    12016         if (player.size() <= kMaxPIPCount && !hasPBP && allowPIP)
    12017             osd->DialogAddButton(tr("Open Recording PIP"), "JUMPRECPIP");
    12018         if (player.size() < kMaxPBPCount && !hasPIP && allowPBP)
    12019             osd->DialogAddButton(tr("Open Recording PBP"), "JUMPRECPBP");
    12020 
    12021         if (player.size() > 1)
     11700        else if (actionName == "TOGGLEPIPMODE")
    1202211701        {
    12023             osd->DialogAddButton(tr("Change Active Window"), "NEXTPIPWINDOW");
    12024 
    12025             QString pipType  = (ctx->IsPBP()) ? "PBP" : "PIP";
    12026             QString toggleMode = QString("TOGGLE%1MODE").arg(pipType);
    12027 
    12028             bool isPBP = ctx->IsPBP();
    12029             const PlayerContext *mctx = GetPlayer(ctx, 0);
    12030             QString pipClose = (isPBP) ? tr("Close PBP") : tr("Close PIP");
    12031             if (mctx == ctx)
     11702            if (player.size() > 1)
    1203211703            {
    12033                 if (player.size() > 2)
    12034                     pipClose = (isPBP) ? tr("Close PBPs") : tr("Close PIPs");
    12035                 osd->DialogAddButton(pipClose, toggleMode);
    12036 
    12037                 if (player.size() == 2)
    12038                     osd->DialogAddButton(tr("Swap Windows"), "SWAPPIP");
     11704                const PlayerContext *mctx = GetPlayer(ctx, 0);
     11705                const PlayerContext *octx = GetPlayer(ctx, 1);
     11706                if (mctx == ctx && octx->IsPIP())
     11707                    BUTTON(actionName, tr("Close PIP(s)", 0, player.size() - 1));
    1203911708            }
    12040             else
     11709        }
     11710        else if (actionName == "TOGGLEPBPMODE")
     11711        {
     11712            if (player.size() > 1)
    1204111713            {
    12042                 osd->DialogAddButton(pipClose, toggleMode);
    12043                 osd->DialogAddButton(tr("Swap Windows"), "SWAPPIP");
     11714                const PlayerContext *mctx = GetPlayer(ctx, 0);
     11715                const PlayerContext *octx = GetPlayer(ctx, 1);
     11716                if (mctx == ctx && octx->IsPBP())
     11717                    BUTTON(actionName, tr("Close PBP(s)", 0, player.size() - 1));
    1204411718            }
    12045 
     11719        }
     11720        else if (actionName == "SWAPPIP")
     11721        {
     11722            const PlayerContext *mctx = GetPlayer(ctx, 0);
     11723            if (mctx != ctx || player.size() == 2)
     11724                BUTTON(actionName, tr("Swap Windows"));
     11725        }
     11726        else if (actionName == "TOGGLEPIPSTATE")
     11727        {
    1204611728            uint max_cnt = min(kMaxPBPCount, kMaxPIPCount+1);
    1204711729            if (player.size() <= max_cnt &&
    12048                 !(hasPIP && !allowPBP) &&
    12049                 !(hasPBP && !allowPIP))
     11730                !(m_tvm_hasPIP && !m_tvm_allowPBP) &&
     11731                !(m_tvm_hasPBP && !m_tvm_allowPIP))
     11732            {
     11733                active = !m_tvm_hasPBP;
     11734                BUTTON2(actionName, tr("Switch to PBP"), tr("Switch to PIP"));
     11735            }
     11736        }
     11737        else if (actionName == "TOGGLEBROWSE")
     11738        {
     11739            if (db_use_channel_groups)
     11740                BUTTON(actionName, tr("Toggle Browse Mode"));
     11741        }
     11742        else if (actionName == "CANCELPLAYLIST")
     11743        {
     11744            if (inPlaylist)
     11745                BUTTON(actionName, tr("Cancel Playlist"));
     11746        }
     11747        else if (actionName == "DEBUGOSD")
     11748        {
     11749            BUTTON(actionName, tr("Playback Data"));
     11750        }
     11751        else if (actionName == "JUMPFFWD")
     11752        {
     11753            if (m_tvm_jump)
     11754                BUTTON(actionName, tr("Jump Ahead"));
     11755        }
     11756        else if (actionName == "JUMPRWND")
     11757        {
     11758            if (m_tvm_jump)
     11759                BUTTON(actionName, tr("Jump Back"));
     11760        }
     11761        else if (actionName == "JUMPTODVDROOTMENU")
     11762        {
     11763            if (m_tvm_isbd || m_tvm_isdvd)
     11764            {
     11765                active = m_tvm_isdvd;
     11766                BUTTON2(actionName, tr("DVD Root Menu"), tr("Top menu"));
     11767            }
     11768        }
     11769        else if (actionName == "JUMPTOPOPUPMENU")
     11770        {
     11771            if (m_tvm_isbd)
     11772                BUTTON(actionName, tr("Popup menu"));
     11773        }
     11774        else if (actionName == "JUMPTODVDTITLEMENU")
     11775        {
     11776            if (m_tvm_isdvd)
     11777                BUTTON(actionName, tr("DVD Title Menu"));
     11778        }
     11779        else if (actionName == "JUMPTODVDCHAPTERMENU")
     11780        {
     11781            if (m_tvm_isdvd)
     11782                BUTTON(actionName, tr("DVD Chapter Menu"));
     11783        }
     11784        else if (actionName == "PREVCHAN")
     11785        {
     11786            if (m_tvm_previouschan)
     11787                BUTTON(actionName, tr("Previous Channel"));
     11788        }
     11789        else if (actionName == "GUIDE")
     11790        {
     11791            BUTTON(actionName, tr("Program Guide"));
     11792        }
     11793        else if (actionName == "FINDER")
     11794        {
     11795            BUTTON(actionName, tr("Program Finder"));
     11796        }
     11797        else if (actionName == "VIEWSCHEDULED")
     11798        {
     11799            BUTTON(actionName, tr("Upcoming Recordings"));
     11800        }
     11801        else if (actionName == "SCHEDULE")
     11802        {
     11803            BUTTON(actionName, tr("Edit Recording Schedule"));
     11804        }
     11805        else if (actionName == "DIALOG_JUMPREC_X_0")
     11806        {
     11807            BUTTON3(actionName, tr("Recorded Program"), "", true);
     11808            QVariant v;
     11809            v.setValue(c.m_node);
     11810            m_tvm_jumprec_back_hack = v;
     11811        }
     11812        else if (actionName == "JUMPPREV")
     11813        {
     11814            if (lastProgram != NULL)
    1205011815            {
    12051                 QString switchTo = (isPBP) ? tr("Switch to PIP") : tr("Switch to PBP");
    12052                 osd->DialogAddButton(switchTo, "TOGGLEPIPSTATE");
     11816                if (lastProgram->GetSubtitle().isEmpty())
     11817                    BUTTON(actionName, lastProgram->GetTitle());
     11818                else
     11819                    BUTTON(actionName,
     11820                           QString("%1: %2")
     11821                           .arg(lastProgram->GetTitle())
     11822                           .arg(lastProgram->GetSubtitle()));
     11823            }
     11824        }
     11825        else if (actionName == "EDIT")
     11826        {
     11827            if (m_tvm_islivetv || m_tvm_isrecorded ||
     11828                m_tvm_isrecording || m_tvm_isvideo)
     11829            {
     11830                active = m_tvm_islivetv;
     11831                BUTTON2(actionName, tr("Edit Channel"), tr("Edit Recording"));
    1205311832            }
    1205411833        }
     11834        else if (actionName == "TOGGLEAUTOEXPIRE")
     11835        {
     11836            if (m_tvm_isrecorded || m_tvm_isrecording)
     11837            {
     11838                active = m_tvm_is_on;
     11839                BUTTON2(actionName,
     11840                        tr("Turn Auto-Expire OFF"), tr("Turn Auto-Expire ON"));
     11841            }
     11842        }
     11843        else if (actionName == "QUEUETRANSCODE")
     11844        {
     11845            if (m_tvm_isrecorded)
     11846            {
     11847                active = m_tvm_transcoding;
     11848                BUTTON2(actionName, tr("Stop Transcoding"), tr("Default"));
     11849            }
     11850        }
     11851        else if (actionName == "QUEUETRANSCODE_AUTO")
     11852        {
     11853            if (m_tvm_isrecorded)
     11854            {
     11855                active = m_tvm_transcoding;
     11856                BUTTON(actionName, tr("Autodetect"));
     11857            }
     11858        }
     11859        else if (actionName == "QUEUETRANSCODE_HIGH")
     11860        {
     11861            if (m_tvm_isrecorded)
     11862            {
     11863                active = m_tvm_transcoding;
     11864                BUTTON(actionName, tr("High Quality"));
     11865            }
     11866        }
     11867        else if (actionName == "QUEUETRANSCODE_MEDIUM")
     11868        {
     11869            if (m_tvm_isrecorded)
     11870            {
     11871                active = m_tvm_transcoding;
     11872                BUTTON(actionName, tr("Medium Quality"));
     11873            }
     11874        }
     11875        else if (actionName == "QUEUETRANSCODE_LOW")
     11876        {
     11877            if (m_tvm_isrecorded)
     11878            {
     11879                active = m_tvm_transcoding;
     11880                BUTTON(actionName, tr("Low Quality"));
     11881            }
     11882        }
     11883        else
     11884        {
     11885            // Allow an arbitrary action if it has a translated
     11886            // description available to be used as the button text.
     11887            // Look in the specified keybinding context as well as the
     11888            // Global context.
     11889            QString text = GetMythMainWindow()->
     11890                GetActionText(m_keyBindingContext, actionName);
     11891            if (text.isEmpty())
     11892                text = GetMythMainWindow()
     11893                    ->GetActionText("Global", actionName);
     11894            if (!text.isEmpty())
     11895                BUTTON(actionName, text);
     11896        }
    1205511897    }
    12056 }
    1205711898
    12058 void TV::FillOSDMenuSchedule(const PlayerContext *ctx, OSD *osd,
    12059                              QString category, const QString selected,
    12060                              QString &currenttext, QString &backaction)
    12061 {
    12062     if (category == "MAIN")
     11899    ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     11900    return result;
     11901}
     11902
     11903void TV::MenuLazyInit(void *field)
     11904{
     11905    if (field == &m_tvm_freerecordercount)
     11906    {
     11907        if (m_tvm_freerecordercount < 0)
     11908            m_tvm_freerecordercount = RemoteGetFreeRecorderCount();
     11909    }
     11910}
     11911
     11912void TV::MenuInit(void)
     11913{
     11914    m_tvmCtx = GetPlayerReadLock(-1, __FILE__, __LINE__);
     11915    m_tvmOsd = GetOSDLock(m_tvmCtx);
     11916    PlayerContext *ctx = m_tvmCtx;
     11917
     11918    m_tvm_avsync   = true;
     11919    m_tvm_visual   = false;
     11920    m_tvm_active   = "";
     11921    m_tvm_upmixing = false;
     11922    m_tvm_canupmix = false;
     11923
     11924    m_tvm_aspectoverride     = kAspect_Off;
     11925    m_tvm_adjustfill         = kAdjustFill_Off;
     11926    m_tvm_fill_autodetect    = false;
     11927    m_tvm_sup                = kPictureAttributeSupported_None;
     11928    m_tvm_studio_levels      = false;
     11929    m_tvm_stereoallowed      = false;
     11930    m_tvm_stereomode         = kStereoscopicModeNone;
     11931    m_tvm_scan_type          = kScan_Ignore;
     11932    m_tvm_scan_type_unlocked = kScan_Ignore;
     11933    m_tvm_scan_type_locked   = false;
     11934    m_tvm_cur_mode           = "";
     11935    m_tvm_doublerate         = false;
     11936
     11937    m_tvm_speedX100         = (int)(round(ctx->ts_normal * 100));
     11938    m_tvm_state             = ctx->GetState();
     11939    m_tvm_isrecording       = (m_tvm_state == kState_WatchingRecording);
     11940    m_tvm_isrecorded        = (m_tvm_state == kState_WatchingPreRecorded);
     11941    m_tvm_isrecorded        = (m_tvm_state == kState_WatchingPreRecorded);
     11942    m_tvm_isvideo           = (m_tvm_state == kState_WatchingVideo);
     11943    m_tvm_curskip           = kCommSkipOff;
     11944    m_tvm_ispaused          = false;
     11945    m_tvm_allowPIP          = IsPIPSupported(ctx);
     11946    m_tvm_allowPBP          = IsPBPSupported(ctx);
     11947    m_tvm_hasPBP            = (player.size() > 1) && GetPlayer(ctx,1)->IsPBP();
     11948    m_tvm_hasPIP            = (player.size() > 1) && GetPlayer(ctx,1)->IsPIP();
     11949    m_tvm_freerecordercount = -1;
     11950    m_tvm_isdvd             = (m_tvm_state == kState_WatchingDVD);
     11951    m_tvm_isbd              = (ctx->buffer && ctx->buffer->IsBD() &&
     11952                               ctx->buffer->BD()->IsHDMVNavigation());
     11953    m_tvm_jump              = (!m_tvm_num_chapters && !m_tvm_isdvd &&
     11954                               !m_tvm_isbd && ctx->buffer->IsSeekingAllowed());
     11955    m_tvm_islivetv          = StateIsLiveTV(m_tvm_state);
     11956    m_tvm_previouschan      = false;
     11957
     11958    m_tvm_num_chapters    = GetNumChapters(ctx);
     11959    m_tvm_current_chapter = GetCurrentChapter(ctx);
     11960    m_tvm_num_angles      = GetNumAngles(ctx);
     11961    m_tvm_current_angle   = GetCurrentAngle(ctx);
     11962    m_tvm_num_titles      = GetNumTitles(ctx);
     11963    m_tvm_current_title   = GetCurrentTitle(ctx);
     11964    m_tvm_chapter_times.clear();
     11965    GetChapterTimes(ctx, m_tvm_chapter_times);
     11966
     11967    m_tvm_subs_capmode   = 0;
     11968    m_tvm_subs_havetext  = false;
     11969    m_tvm_subs_forcedon  = true;
     11970    m_tvm_subs_enabled   = false;
     11971    m_tvm_subs_have_subs = false;
     11972
     11973    for (int i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
     11974        m_tvm_curtrack[i] = -1;
     11975
     11976    if (m_tvm_islivetv)
    1206311977    {
    12064         osd->DialogAddButton(tr("Schedule"),
    12065                              "DIALOG_MENU_SCHEDULE_0",
    12066                              true, selected == "SCHEDULE");
     11978        QString prev_channum = ctx->GetPreviousChannel();
     11979        QString cur_channum  = QString();
     11980        if (ctx->tvchain)
     11981            cur_channum = ctx->tvchain->GetChannelName(-1);
     11982        if (!prev_channum.isEmpty() && prev_channum != cur_channum)
     11983            m_tvm_previouschan = true;
    1206711984    }
    12068     else if (category == "SCHEDULE")
     11985
     11986    ctx->LockDeletePlayer(__FILE__, __LINE__);
     11987
     11988    if (ctx->player)
    1206911989    {
    12070         backaction = "MAIN";
    12071         currenttext = tr("Schedule");
    12072         osd->DialogAddButton(tr("Program Guide"),           ACTION_GUIDE);
    12073         osd->DialogAddButton(tr("Program Finder"),          ACTION_FINDER);
    12074         osd->DialogAddButton(tr("Upcoming Recordings"),     ACTION_VIEWSCHEDULED);
    12075         osd->DialogAddButton(tr("Edit Recording Schedule"), "SCHEDULE");
     11990        for (int i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
     11991        {
     11992            m_tvm_tracks[i] = ctx->player->GetTracks(i);
     11993            if (!m_tvm_tracks[i].empty())
     11994                m_tvm_curtrack[i] = ctx->player->GetTrack(i);
     11995        }
     11996        m_tvm_subs_have_subs =
     11997            !m_tvm_tracks[kTrackTypeSubtitle].empty() ||
     11998            m_tvm_subs_havetext ||
     11999            !m_tvm_tracks[kTrackTypeCC708].empty() ||
     12000            !m_tvm_tracks[kTrackTypeCC608].empty() ||
     12001            !m_tvm_tracks[kTrackTypeTeletextCaptions].empty() ||
     12002            !m_tvm_tracks[kTrackTypeRawText].empty();
     12003        m_tvm_avsync = (ctx->player->GetTrackCount(kTrackTypeVideo) > 0) &&
     12004            !m_tvm_tracks[kTrackTypeAudio].empty();
     12005        m_tvm_visual             = ctx->player->CanVisualise();
     12006        m_tvm_active             = ctx->player->GetVisualiserName();
     12007        m_tvm_upmixing           = ctx->player->GetAudio()->IsUpmixing();
     12008        m_tvm_canupmix           = ctx->player->GetAudio()->CanUpmix();
     12009        m_tvm_aspectoverride     = ctx->player->GetAspectOverride();
     12010        m_tvm_adjustfill         = ctx->player->GetAdjustFill();
     12011        m_tvm_scan_type          = ctx->player->GetScanType();
     12012        m_tvm_scan_type_unlocked = m_tvm_scan_type;
     12013        m_tvm_scan_type_locked   = ctx->player->IsScanTypeLocked();
     12014        m_tvm_doublerate         = ctx->player->CanSupportDoubleRate();
     12015        m_tvm_curskip            = ctx->player->GetAutoCommercialSkip();
     12016        m_tvm_ispaused           = ctx->player->IsPaused();
     12017        m_tvm_subs_capmode       = ctx->player->GetCaptionMode();
     12018        m_tvm_subs_enabled       = ctx->player->GetCaptionsEnabled();
     12019        m_tvm_subs_havetext      = ctx->player->HasTextSubtitles();
     12020        m_tvm_subs_forcedon      = ctx->player->GetAllowForcedSubtitles();
     12021        ctx->player->GetVideoOutput()->GetDeinterlacers(m_tvm_deinterlacers);
     12022        m_tvm_currentdeinterlacer =
     12023            ctx->player->GetVideoOutput()->GetDeinterlacer();
     12024        if (m_tvm_visual)
     12025            m_tvm_visualisers = ctx->player->GetVisualiserList();
     12026        VideoOutput *vo = ctx->player->GetVideoOutput();
     12027        if (vo)
     12028        {
     12029            m_tvm_sup             = vo->GetSupportedPictureAttributes();
     12030            m_tvm_stereoallowed   = vo->StereoscopicModesAllowed();
     12031            m_tvm_stereomode      = vo->GetStereoscopicMode();
     12032            m_tvm_fill_autodetect = !vo->hasHWAcceleration();
     12033            m_tvm_studio_levels   =
     12034                vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0;
     12035        }
     12036        if (!m_tvm_scan_type_locked)
     12037        {
     12038            if (kScan_Interlaced == m_tvm_scan_type)
     12039                m_tvm_cur_mode = tr("(I)", "Interlaced (Normal)");
     12040            else if (kScan_Intr2ndField == m_tvm_scan_type)
     12041                m_tvm_cur_mode = tr("(i)", "Interlaced (Reversed)");
     12042            else if (kScan_Progressive == m_tvm_scan_type)
     12043                m_tvm_cur_mode = tr("(P)", "Progressive");
     12044            m_tvm_cur_mode = " " + m_tvm_cur_mode;
     12045            m_tvm_scan_type_unlocked = kScan_Detect;
     12046        }
    1207612047    }
     12048    ctx->LockPlayingInfo(__FILE__, __LINE__);
     12049    m_tvm_is_on = ctx->playingInfo->QueryAutoExpire() != kDisableAutoExpire;
     12050    m_tvm_transcoding = JobQueue::IsJobQueuedOrRunning
     12051        (JOB_TRANSCODE, ctx->playingInfo->GetChanID(),
     12052         ctx->playingInfo->GetScheduledStartTime());
     12053    ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     12054
     12055    ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     12056}
     12057
     12058void TV::MenuDeinit(void)
     12059{
     12060    ReturnOSDLock(m_tvmCtx, m_tvmOsd);
     12061    ReturnPlayerLock(m_tvmCtx);
     12062    m_tvmOsd = NULL;
     12063    m_tvmOsd = NULL;
     12064}
     12065
     12066void TV::MenuShow(const QDomNode &node, const QDomNode &selected)
     12067{
     12068    MenuInit();
     12069    if (m_tvmOsd)
     12070    {
     12071        m_tvmOsd->DialogShow(OSD_DLG_MENU, m_menuName);
     12072        MenuBase::MenuShow(node, selected);
     12073        QString text =
     12074            MenuTranslate(node.toElement().attribute("text", m_menuName));
     12075        m_tvmOsd->DialogSetText(text);
     12076        QDomNode parent = node.parentNode();
     12077        if (!parent.parentNode().isNull())
     12078        {
     12079            QVariant v;
     12080            v.setValue(node);
     12081            m_tvmOsd->DialogBack("", v);
     12082        }
     12083    }
     12084    MenuDeinit();
     12085}
     12086
     12087void TV::MenuStrings(void) const
     12088{
     12089    tr("Playback Menu");
     12090    tr("Playback Compact Menu");
     12091    tr("Audio");
     12092    tr("Select Audio Track");
     12093    tr("Visualisation");
     12094    tr("Video");
     12095    tr("Change Aspect Ratio");
     12096    tr("Adjust Fill");
     12097    tr("Adjust Picture");
     12098    tr("3D");
     12099    tr("Advanced");
     12100    tr("Video Scan");
     12101    tr("Deinterlacer");
     12102    tr("Subtitles");
     12103    tr("Select Subtitle");
     12104    tr("Text Subtitles");
     12105    tr("Select ATSC CC");
     12106    tr("Select VBI CC");
     12107    tr("Select Teletext CC");
     12108    tr("Playback");
     12109    tr("Adjust Time Stretch");
     12110    tr("Picture-in-Picture");
     12111    tr("Sleep");
     12112    tr("Channel Groups");
     12113    tr("Navigate");
     12114    tr("Commercial Auto-Skip");
     12115    tr("Chapter");
     12116    tr("Angle");
     12117    tr("Title");
     12118    tr("Schedule");
     12119    tr("Source");
     12120    tr("Jump to Program");
     12121    tr("Switch Input");
     12122    tr("Switch Source");
     12123    tr("Jobs");
     12124    tr("Begin Transcoding");
     12125}
     12126
     12127void TV::ShowOSDMenu(const PlayerContext *ctx, bool isCompact)
     12128{
     12129    if (!MenuIsLoaded())
     12130    {
     12131        MenuLoadFromFile("menu_playback.xml",
     12132                         tr("Playback Menu"),
     12133                         metaObject()->className(),
     12134                         "TV Playback");
     12135        m_compactMenu.MenuLoadFromFile("menu_playback_compact.xml",
     12136                                       tr("Playback Compact Menu"),
     12137                                       metaObject()->className(),
     12138                                       "TV Playback");
     12139    }
     12140    if (isCompact && m_compactMenu.MenuIsLoaded())
     12141        MenuShow(m_compactMenu.MenuGetRoot(), QDomNode());
     12142    else if (MenuIsLoaded())
     12143        MenuShow(MenuGetRoot(), QDomNode());
    1207712144}
    1207812145
    1207912146void TV::FillOSDMenuJumpRec(PlayerContext* ctx, const QString category,
    void TV::FillOSDMenuJumpRec(PlayerContext* ctx, const QString category, 
    1218512252            if (level == 1)
    1218612253                osd->DialogBack(category, "DIALOG_" + ACTION_JUMPREC + "_X_0");
    1218712254            else if (level == 0)
    12188                 osd->DialogBack(ACTION_JUMPREC,
    12189                                 "DIALOG_MENU_" + ACTION_JUMPREC +"_0");
     12255            {
     12256                if (m_tvm_jumprec_back_hack.isValid())
     12257                    osd->DialogBack("", m_tvm_jumprec_back_hack);
     12258                else
     12259                    osd->DialogBack(ACTION_JUMPREC,
     12260                                    "DIALOG_MENU_" + ACTION_JUMPREC +"_0");
     12261            }
    1219012262        }
    1219112263    }
    1219212264    ReturnOSDLock(ctx, osd);
  • mythtv/libs/libmythtv/tv_play.h

    diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h
    index 56556dc..43e7599 100644
    a b using namespace std; 
    3939#include "channelgroup.h"
    4040#include "mythtimer.h"
    4141#include "osd.h"
     42#include "decoderbase.h"
    4243
    4344class QDateTime;
     45class QDomDocument;
     46class QDomNode;
    4447class OSD;
    4548class RemoteEncoder;
    4649class MythPlayer;
    class AskProgramInfo 
    138141    ProgramInfo *info;
    139142};
    140143
     144enum MenuCategory {
     145    kMenuCategoryItem,
     146    kMenuCategoryItemlist,
     147    kMenuCategoryMenu
     148};
     149enum MenuShowContext {
     150    kMenuShowActive,
     151    kMenuShowInactive,
     152    kMenuShowAlways
     153};
     154
     155class MenuItemContext
     156{
     157public:
     158    // Constructor for a menu element.
     159    MenuItemContext(const QDomNode &node,
     160                    const QString &menuName,
     161                    bool setCurrentActive,
     162                    bool doDisplay) :
     163        m_node(node),
     164        m_category(kMenuCategoryMenu),
     165        m_menuName(menuName),
     166        m_showContext(kMenuShowAlways),
     167        m_setCurrentActive(setCurrentActive),
     168        m_action(""),
     169        m_actionText(""),
     170        m_doDisplay(doDisplay) {}
     171    // Constructor for an item element.
     172    MenuItemContext(const QDomNode &node,
     173                    MenuShowContext showContext,
     174                    bool setCurrentActive,
     175                    const QString &action,
     176                    const QString &actionText,
     177                    bool doDisplay) :
     178        m_node(node),
     179        m_category(kMenuCategoryItem),
     180        m_menuName(""),
     181        m_showContext(showContext),
     182        m_setCurrentActive(setCurrentActive),
     183        m_action(action),
     184        m_actionText(actionText),
     185        m_doDisplay(doDisplay) {}
     186    // Constructor for an itemlist element.
     187    MenuItemContext(const QDomNode &node,
     188                    MenuShowContext showContext,
     189                    bool setCurrentActive,
     190                    const QString &action,
     191                    bool doDisplay) :
     192        m_node(node),
     193        m_category(kMenuCategoryItemlist),
     194        m_menuName(""),
     195        m_showContext(showContext),
     196        m_setCurrentActive(setCurrentActive),
     197        m_action(action),
     198        m_actionText(""),
     199        m_doDisplay(doDisplay) {}
     200    const QDomNode &m_node;
     201    MenuCategory m_category;
     202    const QString m_menuName;
     203    MenuShowContext m_showContext;
     204    bool m_setCurrentActive;
     205    const QString m_action;
     206    const QString m_actionText;
     207    bool m_doDisplay;
     208};
     209
     210class MenuBase
     211{
     212public:
     213    MenuBase() : m_menuDocument(NULL), m_translationContext(""),
     214                 m_recursionLevel(0) {}
     215    ~MenuBase();
     216    bool MenuLoadFromFile(const QString &filename, const QString &menuname,
     217                          const char *translationContext,
     218                          const QString &keyBindingContext);
     219    bool MenuIsLoaded(void) const { return (m_menuDocument != NULL); }
     220    QDomElement MenuGetRoot(void) const;
     221    QString MenuTranslate(const QString &text) const;
     222    virtual void MenuShow(const QDomNode &node, const QDomNode &selected) {
     223        MenuShowHelper(node, selected, true);
     224    }
     225protected:
     226    virtual bool MenuShowHelper(const QDomNode &node,
     227                                const QDomNode &selected,
     228                                bool doDisplay);
     229    virtual bool MenuDisplayItem(const MenuItemContext &c) { return false; }
     230    virtual void MenuStrings(void) const {}
     231    QDomDocument *m_menuDocument;
     232    QString m_menuName;
     233    const char *m_translationContext;
     234    QString m_keyBindingContext;
     235private:
     236    void MenuProcessIncludes(QDomElement &root);
     237    int m_recursionLevel; // guard against infinite recursion
     238};
     239
    141240/**
    142241 * \class TV
    143242 *
    class AskProgramInfo 
    156255 * \qmlsignal TVPlaybackSought(qint position_seconds)
    157256 * Absolute seek has completed to position_seconds
    158257 */
    159 class MTV_PUBLIC TV : public QObject
     258class MTV_PUBLIC TV : public QObject, public MenuBase
    160259{
    161260    friend class PlaybackBox;
    162261    friend class GuideGrid;
    class MTV_PUBLIC TV : public QObject 
    606705    bool HandleOSDVideoExit(PlayerContext *ctx, QString action);
    607706
    608707    // Menu dialog
    609     void ShowOSDMenu(const PlayerContext*, const QString category = "",
    610                      const QString selected = "");
    611 
    612     void FillOSDMenuAudio    (const PlayerContext *ctx, OSD *osd,
    613                               QString category, const QString selected,
    614                               QString &currenttext, QString &backaction);
    615     void FillOSDMenuVideo    (const PlayerContext *ctx, OSD *osd,
    616                               QString category, const QString selected,
    617                               QString &currenttext, QString &backaction);
    618     void FillOSDMenuSubtitles(const PlayerContext *ctx, OSD *osd,
    619                               QString category, const QString selected,
    620                               QString &currenttext, QString &backaction);
    621     void FillOSDMenuNavigate (const PlayerContext *ctx, OSD *osd,
    622                               QString category, const QString selected,
    623                               QString &currenttext, QString &backaction);
    624     void FillOSDMenuJobs     (const PlayerContext *ctx, OSD *osd,
    625                               QString category, const QString selected,
    626                               QString &currenttext, QString &backaction);
    627     void FillOSDMenuPlayback (const PlayerContext *ctx, OSD *osd,
    628                               QString category, const QString selected,
    629                               QString &currenttext, QString &backaction);
    630     void FillOSDMenuSchedule (const PlayerContext *ctx, OSD *osd,
    631                               QString category, const QString selected,
    632                               QString &currenttext, QString &backaction);
    633     void FillOSDMenuSource   (const PlayerContext *ctx, OSD *osd,
    634                               QString category, const QString selected,
    635                               QString &currenttext, QString &backaction);
     708    void ShowOSDMenu(const PlayerContext*, bool isCompact = false);
     709
    636710    void FillOSDMenuJumpRec  (PlayerContext* ctx, const QString category = "",
    637711                              int level = 0, const QString selected = "");
    638712
     713    virtual void MenuShow(const QDomNode &node, const QDomNode &selected);
     714    virtual bool MenuDisplayItem(const MenuItemContext &c);
     715    virtual void MenuInit(void);
     716    virtual void MenuDeinit(void);
     717    virtual void MenuStrings(void) const;
     718    void MenuLazyInit(void *field);
     719
    639720    // LCD
    640721    void UpdateLCD(void);
    641722    void ShowLCDChannelInfo(const PlayerContext*);
    class MTV_PUBLIC TV : public QObject 
    857938    TimerContextMap      signalMonitorTimerId;
    858939    TimerContextMap      tvchainUpdateTimerId;
    859940
     941    // Playback menu state caching
     942    PlayerContext *m_tvmCtx;
     943    OSD *m_tvmOsd;
     944
     945    // Various tracks
     946    // XXX This ignores kTrackTypeTextSubtitle which is greater than
     947    // kTrackTypeCount, and it unnecessarily includes
     948    // kTrackTypeUnknown.
     949    QStringList m_tvm_tracks[kTrackTypeCount];
     950    int m_tvm_curtrack[kTrackTypeCount];
     951
     952    // Audio
     953    bool m_tvm_avsync;
     954    bool m_tvm_visual;
     955    QString m_tvm_active;
     956    bool m_tvm_upmixing;
     957    bool m_tvm_canupmix;
     958    QStringList m_tvm_visualisers;
     959
     960    // Video
     961    AspectOverrideMode m_tvm_aspectoverride;
     962    AdjustFillMode m_tvm_adjustfill;
     963    bool m_tvm_fill_autodetect;
     964    uint m_tvm_sup;
     965    bool m_tvm_studio_levels;
     966    bool m_tvm_stereoallowed;
     967    StereoscopicMode m_tvm_stereomode;
     968    FrameScanType m_tvm_scan_type;
     969    FrameScanType m_tvm_scan_type_unlocked;
     970    bool m_tvm_scan_type_locked;
     971    QString m_tvm_cur_mode;
     972    QStringList m_tvm_deinterlacers;
     973    QString     m_tvm_currentdeinterlacer;
     974    bool        m_tvm_doublerate;
     975
     976    // Playback
     977    int m_tvm_speedX100;
     978    TVState m_tvm_state;
     979    bool m_tvm_isrecording;
     980    bool m_tvm_isrecorded;
     981    bool m_tvm_isvideo;
     982    CommSkipMode m_tvm_curskip;
     983    bool m_tvm_ispaused;
     984    bool m_tvm_allowPIP;
     985    bool m_tvm_allowPBP;
     986    bool m_tvm_hasPIP;
     987    bool m_tvm_hasPBP;
     988    int m_tvm_freerecordercount;
     989    bool m_tvm_isdvd;
     990    bool m_tvm_isbd;
     991    bool m_tvm_jump;
     992    bool m_tvm_islivetv;
     993    bool m_tvm_previouschan;
     994
     995    // Navigate
     996    int m_tvm_num_chapters;
     997    int m_tvm_current_chapter;
     998    QList<long long> m_tvm_chapter_times;
     999    int m_tvm_num_angles;
     1000    int m_tvm_current_angle;
     1001    int m_tvm_num_titles;
     1002    int m_tvm_current_title;
     1003
     1004    // Subtitles
     1005    uint m_tvm_subs_capmode;
     1006    bool m_tvm_subs_havetext;
     1007    bool m_tvm_subs_forcedon;
     1008    bool m_tvm_subs_enabled;
     1009    bool m_tvm_subs_have_subs;
     1010
     1011    bool m_tvm_is_on;
     1012    bool m_tvm_transcoding;
     1013
     1014    QVariant m_tvm_jumprec_back_hack;
     1015    // End of playback menu state caching
     1016
     1017    MenuBase m_compactMenu;
     1018
    8601019  public:
    8611020    // Constants
    8621021    static const int kInitFFRWSpeed; ///< 1x, default to normal speed
  • mythtv/libs/libmythui/mythmainwindow.cpp

    diff --git a/mythtv/libs/libmythui/mythmainwindow.cpp b/mythtv/libs/libmythui/mythmainwindow.cpp
    index cfa0ebc..7abc1f7 100644
    a b class MythMainWindowPrivate 
    233233    QMap<int, JumpData*> jumpMap;
    234234    QMap<QString, JumpData> destinationMap;
    235235    QMap<QString, MPData> mediaPluginMap;
     236    QHash<QString, QHash<QString, QString> > actionText;
    236237
    237238    void (*exitmenucallback)(void);
    238239
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    17011702    }
    17021703
    17031704    BindKey(context, action, keybind);
     1705    d->actionText[context][action] = description;
    17041706}
    17051707
    17061708QString MythMainWindow::GetKey(const QString &context,
    QString MythMainWindow::GetKey(const QString &context, 
    17251727    return query.value(0).toString();
    17261728}
    17271729
     1730QString MythMainWindow::GetActionText(const QString &context,
     1731                                      const QString &action) const
     1732{
     1733    if (d->actionText.contains(context))
     1734    {
     1735        QHash<QString, QString> entry = d->actionText.value(context);
     1736        if (entry.contains(action))
     1737            return entry.value(action);
     1738    }
     1739    return "";
     1740}
     1741
    17281742void MythMainWindow::ClearJump(const QString &destination)
    17291743{
    17301744    /* make sure that the jump point exists (using [] would add it)*/
  • mythtv/libs/libmythui/mythmainwindow.h

    diff --git a/mythtv/libs/libmythui/mythmainwindow.h b/mythtv/libs/libmythui/mythmainwindow.h
    index 7643af8..f56d12f 100644
    a b class MUI_PUBLIC MythMainWindow : public QWidget 
    6262    void RegisterKey(const QString &context, const QString &action,
    6363                     const QString &description, const QString &key);
    6464    QString GetKey(const QString &context, const QString &action) const;
     65    QString GetActionText(const QString &context, const QString &action) const;
    6566
    6667    void ClearJump(const QString &destination);
    6768    void BindJump(const QString &destination, const QString &key);
  • new file mythtv/themes/default/menu_playback.xml

    diff --git a/mythtv/themes/default/menu_playback.xml b/mythtv/themes/default/menu_playback.xml
    new file mode 100644
    index 0000000..7dc85ad
    - +  
     1<?xml version="1.0"?>
     2
     3<!-- All "text" attribute values need translations.  The "XXXtext"
     4     attributes illustrate the default value if the "text" attribute
     5     is omitted. -->
     6
     7<menu text="Playback Menu">
     8  <menu text="Audio">
     9    <menu text="Select Audio Track">
     10      <itemlist actiongroup="SELECTAUDIO_" current="active" />
     11    </menu>
     12    <item action="TOGGLEAUDIOSYNC" XXXtext="Adjust Audio Sync" />
     13    <menu text="Visualisation">
     14      <item action="DISABLEVISUALISATION" XXXtext="None" />
     15      <itemlist actiongroup="VISUALISER_" current="active" />
     16    </menu>
     17    <item action="DISABLEUPMIX" XXXtext="Disable Audio Upmixer"
     18          show="inactive" />
     19    <item action="ENABLEUPMIX" XXXtext="Enable Audio Upmixer"
     20          show="inactive" />
     21  </menu>
     22  <menu text="Video">
     23    <menu text="Change Aspect Ratio">
     24      <itemlist actiongroup="TOGGLEASPECT" current="active" />
     25      <!-- Alternatively:
     26      <item action="TOGGLEASPECT0" XXXtext="Off" current="active" />
     27      <item action="TOGGLEASPECT1" XXXtext="4:3" current="active" />
     28      <item action="TOGGLEASPECT3" XXXtext="14:9" current="active" />
     29      <item action="TOGGLEASPECT2" XXXtext="16:9" current="active" />
     30      <item action="TOGGLEASPECT4" XXXtext="2.35:1" current="active" />
     31      -->
     32    </menu>
     33    <menu text="Adjust Fill">
     34      <item action="AUTODETECT_FILL" XXXtext="Auto Detect" current="active" />
     35      <!-- Alternatively:
     36      <item action="TOGGLEFILL0" XXXtext="Off" current="active" />
     37      <item action="TOGGLEFILL1" XXXtext="Half" current="active" />
     38      <item action="TOGGLEFILL2" XXXtext="Full" current="active" />
     39      <item action="TOGGLEFILL3" XXXtext="H.Stretch" current="active" />
     40      <item action="TOGGLEFILL4" XXXtext="V.Stretch" current="active" />
     41      <item action="TOGGLEFILL5" XXXtext="H.Fill" current="active" />
     42      <item action="TOGGLEFILL6" XXXtext="V.Fill" current="active" />
     43      -->
     44      <itemlist actiongroup="TOGGLEFILL" current="active" />
     45    </menu>
     46    <item action="TOGGLEMANUALZOOM" XXXtext="Manual Zoom Mode" />
     47    <menu text="Adjust Picture">
     48      <itemlist actiongroup="TOGGLEPICCONTROLS" />
     49      <!-- Alternatively:
     50      <item action="TOGGLEPICCONTROLS0" XXXtext="None" />
     51      <item action="TOGGLEPICCONTROLS1" XXXtext="Brightness" />
     52      <item action="TOGGLEPICCONTROLS2" XXXtext="Contrast" />
     53      <item action="TOGGLEPICCONTROLS3" XXXtext="Color" />
     54      <item action="TOGGLEPICCONTROLS4" XXXtext="Hue" />
     55      <item action="TOGGLESTUDIOLEVELS"
     56            XXXtext="Disable Studio Levels" show="active" />
     57      <item action="TOGGLESTUDIOLEVELS"
     58            XXXtext="Enable Studio Levels" show="active" />
     59      <item action="TOGGLEPICCONTROLS6" XXXtext="Volume" />
     60      -->
     61      <item action="TOGGLENIGHTMODE"
     62            XXXtext="Disable Night Mode" show="active" />
     63      <item action="TOGGLENIGHTMODE"
     64            XXXtext="Enable Night Mode" show="inactive" />
     65    </menu>
     66    <menu text="3D">
     67      <itemlist actiongroup="3D" current="active" />
     68      <!-- Alternatively:
     69      <item action="3DNONE" XXXtext="None" current="active" />
     70      <item action="3DSIDEBYSIDE" XXXtext="Side by Side" current="active" />
     71      <item action="3DSIDEBYSIDEDISCARD" XXXtext="Discard Side by Side"
     72            current="active" />
     73      <item action="3DTOPANDBOTTOM" XXXtext="Top and Bottom"
     74            current="active" />
     75      <item action="3DTOPANDBOTTOMDISCARD" XXXtext="Discard Top and Bottom"
     76            current="active" />
     77      -->
     78    </menu>
     79    <menu text="Advanced">
     80      <menu text="Video Scan">
     81        <itemlist actiongroup="SELECTSCAN_" current="active" />
     82        <!-- Alternatively:
     83        <item action="SELECTSCAN_0"
     84              XXXtext="Detect" current="active" />
     85        <item action="SELECTSCAN_3"
     86              XXXtext="Progressive" current="active" />
     87        <item action="SELECTSCAN_1"
     88              XXXtext="Interlaced (Normal)" current="active" />
     89        <item action="SELECTSCAN_2"
     90              XXXtext="Interlaced (Reversed)" current="active" />
     91        -->
     92      </menu>
     93      <menu text="Deinterlacer">
     94        <itemlist actiongroup="DEINTERLACER_" current="active" />
     95      </menu>
     96    </menu>
     97  </menu>
     98  <menu text="Subtitles">
     99    <item action="DISABLESUBS" XXXtext="Disable Subtitles" show="inactive" />
     100    <item action="ENABLESUBS" XXXtext="Enable Subtitles" show="inactive" />
     101    <item action="DISABLEFORCEDSUBS" XXXtext="Disable Forced Subtitles"
     102          show="inactive" />
     103    <item action="ENABLEFORCEDSUBS" XXXtext="Enable Forced Subtitles"
     104          show="inactive" />
     105    <menu text="Select Subtitle">
     106      <itemlist actiongroup="SELECTSUBTITLE_" current="active" />
     107    </menu>
     108    <menu text="Text Subtitles">
     109      <item action="DISABLEEXTTEXT" XXXtext="Disable External Subtitles"
     110            show="inactive" />
     111      <item action="ENABLEEXTTEXT" XXXtext="Enable External Subtitles"
     112            show="inactive" />
     113      <itemlist actiongroup="SELECTRAWTEXT_" current="active" />
     114    </menu>
     115    <menu text="Select ATSC CC">
     116      <itemlist actiongroup="SELECTCC708_" current="active" />
     117    </menu>
     118    <menu text="Select VBI CC">
     119      <itemlist actiongroup="SELECTCC608_" current="active" />
     120    </menu>
     121    <menu text="Select Teletext CC">
     122      <itemlist actiongroup="SELECTTTC_" current="active" />
     123    </menu>
     124    <item action="TOGGLETTM" XXXtext="Toggle Teletext Menu" />
     125    <item action="TOGGLESUBZOOM" XXXtext="Adjust Subtitle Zoom" />
     126    <item action="TOGGLESUBDELAY" XXXtext="Adjust Subtitle Delay" />
     127  </menu>
     128  <menu text="Playback">
     129    <item action="PAUSE" XXXtext="Play" show="active" />
     130    <item action="PAUSE" XXXtext="Pause" show="inactive" />
     131    <menu text="Adjust Time Stretch">
     132      <item action="TOGGLESTRETCH" XXXtext="Toggle" />
     133      <itemlist actiongroup="ADJUSTSTRETCH" current="active" />
     134      <!-- Alternatively:
     135      <item action="ADJUSTSTRETCH"    XXXtext="Adjust" />
     136      <item action="ADJUSTSTRETCH0.5" XXXtext="0.5X" current="active" />
     137      <item action="ADJUSTSTRETCH0.9" XXXtext="0.9X" current="active" />
     138      <item action="ADJUSTSTRETCH1.0" XXXtext="1.0X" current="active" />
     139      <item action="ADJUSTSTRETCH1.1" XXXtext="1.1X" current="active" />
     140      <item action="ADJUSTSTRETCH1.2" XXXtext="1.2X" current="active" />
     141      <item action="ADJUSTSTRETCH1.3" XXXtext="1.3X" current="active" />
     142      <item action="ADJUSTSTRETCH1.4" XXXtext="1.4X" current="active" />
     143      <item action="ADJUSTSTRETCH1.5" XXXtext="1.5X" current="active" />
     144      -->
     145    </menu>
     146    <menu text="Picture-in-Picture">
     147      <item action="JUMPRECPIP" XXXtext="Open Recording PIP" />
     148      <item action="JUMPRECPBP" XXXtext="Open Recording PBP" />
     149      <item action="CREATEPIPVIEW" XXXtext="Open Live TV PIP" />
     150      <item action="CREATEPBPVIEW" XXXtext="Open Live TV PBP" />
     151      <item action="NEXTPIPWINDOW" XXXtext="Change Active Window" />
     152      <item action="TOGGLEPIPMODE" XXXtext="Close PIP(s)" />
     153      <item action="TOGGLEPBPMODE" XXXtext="Close PBP(s)" />
     154      <item action="SWAPPIP" XXXtext="Swap Windows" />
     155      <item action="TOGGLEPIPSTATE" XXXtext="Switch to PBP" show="active" />
     156      <item action="TOGGLEPIPSTATE" XXXtext="Switch to PIP" show="inactive" />
     157    </menu>
     158    <menu text="Sleep">
     159      <itemlist actiongroup="TOGGLESLEEP" />
     160      <!-- Alternatively:
     161      <item action="TOGGLESLEEPON" XXXtext="Sleep Off" />
     162      <item action="TOGGLESLEEP30" XXXtext="Sleep 30 minutes" />
     163      <item action="TOGGLESLEEP60" XXXtext="Sleep 60 minutes" />
     164      <item action="TOGGLESLEEP90" XXXtext="Sleep 90 minutes" />
     165      <item action="TOGGLESLEEP120" XXXtext="Sleep 120 minutes" />
     166      -->
     167    </menu>
     168    <menu text="Channel Groups">
     169      <itemlist actiongroup="CHANGROUP_" current="active" />
     170    </menu>
     171    <item action="TOGGLEBROWSE" XXXtext="Toggle Browse Mode" />
     172    <item action="CANCELPLAYLIST" XXXtext="Cancel Playlist" />
     173    <item action="DEBUGOSD" XXXtext="Playback Data" />
     174  </menu>
     175  <menu text="Navigate">
     176    <item action="JUMPFFWD" XXXtext="Jump Ahead" />
     177    <item action="JUMPRWND" XXXtext="Jump Back" />
     178    <menu text="Commercial Auto-Skip">
     179      <itemlist actiongroup="TOGGLECOMMSKIP" current="active" />
     180      <!-- Alternatively:
     181      <item action="TOGGLECOMMSKIP0" XXXtext="Auto-Skip OFF"
     182            current="active" />
     183      <item action="TOGGLECOMMSKIP2" XXXtext="Auto-Skip Notify"
     184            current="active" />
     185      <item action="TOGGLECOMMSKIP1" XXXtext="Auto-Skip ON"
     186            current="active" />
     187      -->
     188    </menu>
     189    <!-- JUMPTODVDROOTMENU has different text/meanings for BD & DVD -->
     190    <item action="JUMPTODVDROOTMENU" XXXtext="DVD Root Menu" show="active" />
     191    <item action="JUMPTODVDROOTMENU" XXXtext="Top menu" show="inactive" />
     192    <item action="JUMPTOPOPUPMENU" XXXtext="Popup menu" />
     193    <item action="JUMPTODVDTITLEMENU" XXXtext="DVD Title Menu" />
     194    <item action="JUMPTODVDCHAPTERMENU" XXXtext="DVD Chapter Menu" />
     195    <item action="PREVCHAN" XXXtext="Previous Channel" />
     196    <menu text="Chapter">
     197      <itemlist actiongroup="JUMPTOCHAPTER" current="active" />
     198    </menu>
     199    <menu text="Angle">
     200      <itemlist actiongroup="SWITCHTOANGLE" current="active" />
     201    </menu>
     202    <menu text="Title">
     203      <itemlist actiongroup="JUMPTOTITLE" current="active" />
     204    </menu>
     205  </menu>
     206  <menu text="Schedule">
     207    <item action="GUIDE" XXXtext="Program Guide" />
     208    <item action="FINDER" XXXtext="Program Finder" />
     209    <item action="VIEWSCHEDULED" XXXtext="Upcoming Recordings" />
     210    <item action="SCHEDULE" XXXtext="Edit Recording Schedule" />
     211  </menu>
     212  <menu text="Source">
     213    <menu text="Jump to Program">
     214      <item action="DIALOG_JUMPREC_X_0" XXXtext="Recorded Program" />
     215      <item action="JUMPPREV" />
     216    </menu>
     217    <menu text="Switch Input">
     218      <itemlist actiongroup="SWITCHTOINPUT_" show="inactive" />
     219    </menu>
     220    <menu text="Switch Source">
     221      <itemlist actiongroup="SWITCHTOSOURCE_" />
     222    </menu>
     223  </menu>
     224  <menu text="Jobs">
     225    <item action="EDIT" XXXtext="Edit Channel" show="active" />
     226    <item action="EDIT" XXXtext="Edit Recording" show="inactive" />
     227    <item action="TOGGLEAUTOEXPIRE" XXXtext="Turn Auto-Expire OFF" show="active" />
     228    <item action="TOGGLEAUTOEXPIRE" XXXtext="Turn Auto-Expire ON" show="inactive" />
     229    <item action="QUEUETRANSCODE" XXXtext="Stop Transcoding" show="active" />
     230    <menu text="Begin Transcoding">
     231      <item action="QUEUETRANSCODE" XXXtext="Default"
     232            show="inactive" />
     233      <item action="QUEUETRANSCODE_AUTO" XXXtext="Autodetect"
     234            show="inactive" />
     235      <item action="QUEUETRANSCODE_HIGH" XXXtext="High Quality"
     236            show="inactive" />
     237      <item action="QUEUETRANSCODE_MEDIUM" XXXtext="Medium Quality"
     238            show="inactive" />
     239      <item action="QUEUETRANSCODE_LOW" XXXtext="Low Quality"
     240            show="inactive" />
     241    </menu>
     242  </menu>
     243</menu>
  • new file mythtv/themes/default/menu_playback_compact.xml

    diff --git a/mythtv/themes/default/menu_playback_compact.xml b/mythtv/themes/default/menu_playback_compact.xml
    new file mode 100644
    index 0000000..59ecad4
    - +  
     1<?xml version="1.0"?>
     2
     3<menu text="Playback Compact Menu">
     4  <!-- Insert desired actions here.  End by including the main menu
     5       so that all actions are ultimately available.  The actions
     6       may need more descriptive text outside the context of the
     7       original menu; if so, it's best to find a string from tv_play.cpp
     8       that is already being translated. -->
     9  <include file="menu_playback.xml" />
     10</menu>