From d0966aff3e7d582938559e16aa4d3be328fb0b92 Mon Sep 17 00:00:00 2001 From: Jim Stichnoth Date: Thu, 16 May 2013 15:52:50 -0700 Subject: [PATCH] Themed menus: Implement the cutlist editor menus. Refs #11533 Provides an implementation of the main cutlist editor menu (menu_cutlist.xml), and the exit dialog menu (menu_cutlist_exit.xml). There is a slight change in behavior - pressing SELECT within a cut region brings up the entire cutlist menu, rather than a subset. The original code suppresses the submenu entry at the end on the SELECT action. The original behavior could be restored by adding a third cutlist menu, or by designing a way to pass extra state to the menu builder. To make this work seamlessly with existing action handlers, clumsy action names like DIALOG_CUTPOINT_NEWCUT_0 are preserved. This will need some cleanup. There is one remaining cutlist-related menu that could be implemented - the "already editing" dialog. --- mythtv/libs/libmythtv/tv_play.cpp | 366 ++++++++++++-------- mythtv/libs/libmythtv/tv_play.h | 10 +- mythtv/themes/default/menu_cutlist.xml | 35 ++ mythtv/themes/default/menu_cutlist_exit.xml | 8 + 4 files changed, 281 insertions(+), 138 deletions(-) create mode 100644 mythtv/themes/default/menu_cutlist.xml create mode 100644 mythtv/themes/default/menu_cutlist_exit.xml diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp index d1a345b1caa..8f88caf204b 100644 --- a/mythtv/libs/libmythtv/tv_play.cpp +++ b/mythtv/libs/libmythtv/tv_play.cpp @@ -3790,7 +3790,7 @@ bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) (actx->player->IsInDelete(current_frame)) && (!(actx->player->HasTemporaryMark()))) { - ShowOSDCutpoint(actx, "EDIT_CUT_REGION"); + ShowOSDCutpoint(actx, "EDIT_CUT_POINTS"); handled = true; } else @@ -10001,121 +10001,34 @@ void TV::SetActive(PlayerContext *lctx, int index, bool osd_msg) void TV::ShowOSDCutpoint(PlayerContext *ctx, const QString &type) { - OSD *osd = GetOSDLock(ctx); - if (!osd) + if (type == "EDIT_CUT_POINTS") { - ReturnOSDLock(ctx, osd); - return; + if (!m_cutlistMenu.IsLoaded()) + { + m_cutlistMenu.Load("menu_cutlist.xml", + tr("Edit Cut Points"), + // XXX which translation context to use? + metaObject()->className(), + "TV Editing"); + } + if (m_cutlistMenu.IsLoaded()) + PlaybackMenuShow(m_cutlistMenu, + m_cutlistMenu.GetRoot(), QDomNode()); } - - - if (("EDIT_CUT_POINTS" == type) || ("EDIT_CUT_REGION" == type)) + else if (type == "EXIT_EDIT_MODE") { - uint64_t frame = ctx->player->GetFramesPlayed(); - uint64_t previous_cut = ctx->player->GetNearestMark(frame, false); - uint64_t next_cut = ctx->player->GetNearestMark(frame, true); - uint64_t total_frames = ctx->player->GetTotalFrameCount(); - - osd->DialogShow(OSD_DLG_CUTPOINT, - QObject::tr("Edit Cut Points")); - if (ctx->player->IsInDelete(frame)) + if (!m_cutlistExitMenu.IsLoaded()) { - if (ctx->player->IsTemporaryMark(frame)) - { - if (previous_cut > 0) - osd->DialogAddButton(QObject::tr("Move Previous Cut End " - "Here"), - QString("DIALOG_CUTPOINT_MOVEPREV_0")); - else - osd->DialogAddButton(QObject::tr("Cut to Beginning"), - QString("DIALOG_CUTPOINT_CUTTOBEGINNING_0")); - - if (next_cut == total_frames) - osd->DialogAddButton(QObject::tr("Cut to End"), - QString("DIALOG_CUTPOINT_CUTTOEND_0")); - else - osd->DialogAddButton(QObject::tr("Move Next Cut Start " - "Here"), - QString("DIALOG_CUTPOINT_MOVENEXT_0")); - } - else - { - osd->DialogAddButton(QObject::tr("Move Start of Cut Here"), - QString("DIALOG_CUTPOINT_MOVEPREV_0")); - osd->DialogAddButton(QObject::tr("Move End of Cut Here"), - QString("DIALOG_CUTPOINT_MOVENEXT_0")); - } - osd->DialogAddButton(QObject::tr("Delete This Cut"), - QString("DIALOG_CUTPOINT_DELETE_0")); + m_cutlistExitMenu.Load("menu_cutlist_exit.xml", + tr("Exit Recording Editor"), + // XXX which translation context to use? + metaObject()->className(), + "TV Editing"); } - else - { - if (previous_cut > 0) - osd->DialogAddButton(QObject::tr("Move Previous Cut End Here"), - QString("DIALOG_CUTPOINT_MOVEPREV_0")); - else - osd->DialogAddButton(QObject::tr("Cut to Beginning"), - QString("DIALOG_CUTPOINT_CUTTOBEGINNING_0")); - if (next_cut == total_frames) - osd->DialogAddButton(QObject::tr("Cut to End"), - QString("DIALOG_CUTPOINT_CUTTOEND_0")); - else - osd->DialogAddButton(QObject::tr("Move Next Cut Start Here"), - QString("DIALOG_CUTPOINT_MOVENEXT_0")); - osd->DialogAddButton(QObject::tr("Add New Cut"), - QString("DIALOG_CUTPOINT_NEWCUT_0")); - osd->DialogAddButton(QObject::tr("Join Surrounding Cuts"), - QString("DIALOG_CUTPOINT_DELETE_0")); - } - if (ctx->player->DeleteMapHasUndo()) - osd->DialogAddButton(QObject::tr("Undo") + " - " + - ctx->player->DeleteMapGetUndoMessage(), - QString("DIALOG_CUTPOINT_UNDO_0")); - if (ctx->player->DeleteMapHasRedo()) - osd->DialogAddButton(QObject::tr("Redo") + " - " + - ctx->player->DeleteMapGetRedoMessage(), - QString("DIALOG_CUTPOINT_REDO_0")); - if ("EDIT_CUT_POINTS" == type) - osd->DialogAddButton(QObject::tr("Cut List Options"), - "DIALOG_CUTPOINT_CUTLISTOPTIONS_0", true); - } - else if ("CUT_LIST_OPTIONS" == type) - { - osd->DialogShow(OSD_DLG_CUTPOINT, - QObject::tr("Cut List Options")); - osd->DialogAddButton(QObject::tr("Clear Cuts"), - "DIALOG_CUTPOINT_CLEARMAP_0"); - osd->DialogAddButton(QObject::tr("Reverse Cuts"), - "DIALOG_CUTPOINT_INVERTMAP_0"); - osd->DialogAddButton(QObject::tr("Load Detected Commercials"), - "DIALOG_CUTPOINT_LOADCOMMSKIP_0"); - osd->DialogAddButton(QObject::tr("Undo Changes"), - "DIALOG_CUTPOINT_REVERT_0"); - osd->DialogAddButton(QObject::tr("Exit Without Saving"), - "DIALOG_CUTPOINT_REVERTEXIT_0"); - osd->DialogAddButton(QObject::tr("Save Cuts"), - "DIALOG_CUTPOINT_SAVEMAP_0"); - osd->DialogAddButton(QObject::tr("Save Cuts and Exit"), - "DIALOG_CUTPOINT_SAVEEXIT_0"); - } - else if ("EXIT_EDIT_MODE" == type) - { - osd->DialogShow(OSD_DLG_CUTPOINT, - QObject::tr("Exit Recording Editor")); - osd->DialogAddButton(QObject::tr("Save Cuts and Exit"), - "DIALOG_CUTPOINT_SAVEEXIT_0"); - osd->DialogAddButton(QObject::tr("Exit Without Saving"), - "DIALOG_CUTPOINT_REVERTEXIT_0"); - osd->DialogAddButton(QObject::tr("Save Cuts"), - "DIALOG_CUTPOINT_SAVEMAP_0"); - osd->DialogAddButton(QObject::tr("Undo Changes"), - "DIALOG_CUTPOINT_REVERT_0"); - } - osd->DialogBack("", "DIALOG_CUTPOINT_DONOTHING_0", true); - QHash map; - map.insert("title", tr("Edit")); - osd->SetText("osd_program_editor", map, kOSDTimeout_None); - ReturnOSDLock(ctx, osd); + if (m_cutlistExitMenu.IsLoaded()) + PlaybackMenuShow(m_cutlistExitMenu, + m_cutlistExitMenu.GetRoot(), QDomNode()); + } } bool TV::HandleOSDCutpoint(PlayerContext *ctx, QString action) @@ -10125,15 +10038,7 @@ bool TV::HandleOSDCutpoint(PlayerContext *ctx, QString action) return res; OSD *osd = GetOSDLock(ctx); - if (action == "CUTLISTOPTIONS" && osd) - { - ShowOSDCutpoint(ctx, "CUT_LIST_OPTIONS"); - res = false; - } - else if (action == "DONOTHING" && osd) - { - } - else if (osd) + if (osd) { QStringList actions(action); if (!ctx->player->HandleProgramEditorActions(actions)) @@ -11140,8 +11045,9 @@ static bool matchesGroup(const QString &name, const QString &inPrefix, static void addButton(const MenuItemContext &c, OSD *osd, bool active, bool &result, const QString &action, const QString &defaultTextActive, - const QString &defaultTextInactive = "", - bool isMenu = false) + const QString &defaultTextInactive, + bool isMenu, + const QString &textArg) { if (c.m_category == kMenuCategoryItemlist || action == c.m_action) { @@ -11155,6 +11061,8 @@ static void addButton(const MenuItemContext &c, OSD *osd, bool active, if (text.isEmpty()) text = (active || defaultTextInactive.isEmpty()) ? defaultTextActive : defaultTextInactive; + if (!textArg.isEmpty()) + text = text.arg(textArg); osd->DialogAddButton(text, action, isMenu, active && c.m_setCurrentActive); } @@ -11163,21 +11071,185 @@ static void addButton(const MenuItemContext &c, OSD *osd, bool active, } #define BUTTON(action, text) \ - addButton(c, osd, active, result, (action), (text)) + addButton(c, osd, active, result, (action), (text), "", false, "") #define BUTTON2(action, textActive, textInactive) \ - addButton(c, osd, active, result, (action), (textActive), (textInactive)) + addButton(c, osd, active, result, (action), (textActive), \ + (textInactive), false, "") #define BUTTON3(action, textActive, textInactive, isMenu) \ addButton(c, osd, active, result, (action), (textActive), \ - (textInactive), (isMenu)) + (textInactive), (isMenu), "") -// Returns true if at least one item should be displayed. bool TV::MenuItemDisplay(const MenuItemContext &c) { - // Test &c.m_menu to determine which menu is being displayed and - // therefore which actions to test for. For the regular and - // compact playback menus, we do the same thing so no test is - // needed, but as other playback-context menus are added, we'll - // want to test. + if (&c.m_menu == &m_playbackMenu || + &c.m_menu == &m_compactMenu) + { + return MenuItemDisplayPlayback(c); + } + else if (&c.m_menu == &m_cutlistMenu || + &c.m_menu == &m_cutlistExitMenu) + { + return MenuItemDisplayCutlist(c); + } + else + return false; +} + +bool TV::MenuItemDisplayCutlist(const MenuItemContext &c) +{ + MenuCategory category = c.m_category; + const QString &actionName = c.m_action; + + bool result = false; + bool active = true; + PlayerContext *ctx = m_tvmCtx; + OSD *osd = m_tvmOsd; + if (!osd) + return result; + if (category == kMenuCategoryMenu) + { + result = c.m_menu.Show(c.m_node, QDomNode(), *this, false); + if (result && c.m_doDisplay) + { + QVariant v; + v.setValue(MenuNodeTuple(c.m_menu, c.m_node)); + osd->DialogAddButton(c.m_menuName, v, true, c.m_setCurrentActive); + } + return result; + } + ctx->LockDeletePlayer(__FILE__, __LINE__); + uint64_t frame = ctx->player->GetFramesPlayed(); + uint64_t previous_cut = ctx->player->GetNearestMark(frame, false); + uint64_t next_cut = ctx->player->GetNearestMark(frame, true); + uint64_t total_frames = ctx->player->GetTotalFrameCount(); + bool is_in_delete = ctx->player->IsInDelete(frame); + bool is_temporary_mark = ctx->player->IsTemporaryMark(frame); + if (category == kMenuCategoryItem) + { + if (actionName == "DIALOG_CUTPOINT_MOVEPREV_0") + { + if ((is_in_delete && is_temporary_mark && + previous_cut > 0) || + (is_in_delete && !is_temporary_mark) || + (!is_temporary_mark && previous_cut > 0)) + { + active = !(is_in_delete && !is_temporary_mark); + BUTTON2(actionName, tr("Move Previous Cut End Here"), + tr("Move Start of Cut Here")); + } + } + else if (actionName == "DIALOG_CUTPOINT_MOVENEXT_0") + { + if ((is_in_delete && is_temporary_mark && + next_cut != total_frames) || + (is_in_delete && !is_temporary_mark) || + (!is_temporary_mark && next_cut != total_frames)) + { + active = !(is_in_delete && !is_temporary_mark); + BUTTON2(actionName, tr("Move Next Cut Start Here"), + tr("Move End of Cut Here")); + } + } + else if (actionName == "DIALOG_CUTPOINT_CUTTOBEGINNING_0") + { + if (previous_cut <= 0 && + (is_temporary_mark || !is_in_delete)) + { + BUTTON(actionName, tr("Cut to Beginning")); + } + } + else if (actionName == "DIALOG_CUTPOINT_CUTTOEND_0") + { + if (next_cut == total_frames && + (is_temporary_mark || !is_in_delete)) + { + BUTTON(actionName, tr("Cut to End")); + } + } + else if (actionName == "DIALOG_CUTPOINT_DELETE_0") + { + active = is_in_delete; + BUTTON2(actionName, tr("Delete This Cut"), + tr("Join Surrounding Cuts")); + } + else if (actionName == "DIALOG_CUTPOINT_NEWCUT_0") + { + if (!is_in_delete) + BUTTON(actionName, tr("Add New Cut")); + } + else if (actionName == "DIALOG_CUTPOINT_UNDO_0") + { + if (ctx->player->DeleteMapHasUndo()) + { + //: %1 is the undo message + QString text = tr("Undo - %1"); + addButton(c, osd, active, result, actionName, text, "", false, + ctx->player->DeleteMapGetUndoMessage()); + } + } + else if (actionName == "DIALOG_CUTPOINT_REDO_0") + { + if (ctx->player->DeleteMapHasRedo()) + { + //: %1 is the redo message + QString text = tr("Redo - %1"); + addButton(c, osd, active, result, actionName, text, "", false, + ctx->player->DeleteMapGetRedoMessage()); + } + } + else if (actionName == "DIALOG_CUTPOINT_CLEARMAP_0") + { + BUTTON(actionName, tr("Clear Cuts")); + } + else if (actionName == "DIALOG_CUTPOINT_INVERTMAP_0") + { + BUTTON(actionName, tr("Reverse Cuts")); + } + else if (actionName == "DIALOG_CUTPOINT_LOADCOMMSKIP_0") + { + BUTTON(actionName, tr("Load Detected Commercials")); + } + else if (actionName == "DIALOG_CUTPOINT_REVERT_0") + { + BUTTON(actionName, tr("Undo Changes")); + } + else if (actionName == "DIALOG_CUTPOINT_REVERTEXIT_0") + { + BUTTON(actionName, tr("Exit Without Saving")); + } + else if (actionName == "DIALOG_CUTPOINT_SAVEMAP_0") + { + BUTTON(actionName, tr("Save Cuts")); + } + else if (actionName == "DIALOG_CUTPOINT_SAVEEXIT_0") + { + BUTTON(actionName, tr("Save Cuts and Exit")); + } + else + { + // Allow an arbitrary action if it has a translated + // description available to be used as the button text. + // Look in the specified keybinding context as well as the + // Global context. + // XXX This doesn't work well (yet) because a keybinding + // action named "foo" is actually a menu action named + // "DIALOG_CUTPOINT_foo_0". + QString text = GetMythMainWindow()-> + GetActionText(c.m_menu.GetKeyBindingContext(), actionName); + if (text.isEmpty()) + text = GetMythMainWindow()-> + GetActionText("Global", actionName); + if (!text.isEmpty()) + BUTTON(actionName, text); + } + } + ctx->UnlockDeletePlayer(__FILE__, __LINE__); + return result; +} + +// Returns true if at least one item should be displayed. +bool TV::MenuItemDisplayPlayback(const MenuItemContext &c) +{ MenuCategory category = c.m_category; const QString &actionName = c.m_action; @@ -11918,10 +11990,13 @@ void TV::MenuLazyInit(void *field) } } -void TV::PlaybackMenuInit(void) +void TV::PlaybackMenuInit(const MenuBase &menu) { m_tvmCtx = GetPlayerReadLock(-1, __FILE__, __LINE__); m_tvmOsd = GetOSDLock(m_tvmCtx); + if (&menu != &m_playbackMenu && &menu != &m_compactMenu) + return; + PlayerContext *ctx = m_tvmCtx; m_tvm_avsync = true; @@ -12064,7 +12139,7 @@ void TV::PlaybackMenuInit(void) ctx->UnlockDeletePlayer(__FILE__, __LINE__); } -void TV::PlaybackMenuDeinit(void) +void TV::PlaybackMenuDeinit(const MenuBase &menu) { ReturnOSDLock(m_tvmCtx, m_tvmOsd); ReturnPlayerLock(m_tvmCtx); @@ -12075,10 +12150,17 @@ void TV::PlaybackMenuDeinit(void) void TV::PlaybackMenuShow(const MenuBase &menu, const QDomNode &node, const QDomNode &selected) { - PlaybackMenuInit(); + PlaybackMenuInit(menu); if (m_tvmOsd) { - m_tvmOsd->DialogShow(OSD_DLG_MENU, menu.GetName()); + bool isPlayback = (&menu == &m_playbackMenu || + &menu == &m_compactMenu); + bool isCutlist = (&menu == &m_cutlistMenu || + &menu == &m_cutlistExitMenu); + m_tvmOsd->DialogShow(isPlayback ? OSD_DLG_MENU : + isCutlist ? OSD_DLG_CUTPOINT : + "???", + menu.GetName()); menu.Show(node, selected, *this); QString text = menu.Translate(node.toElement().attribute("text", menu.GetName())); @@ -12090,12 +12172,20 @@ void TV::PlaybackMenuShow(const MenuBase &menu, v.setValue(MenuNodeTuple(menu, node)); m_tvmOsd->DialogBack("", v); } + if (isCutlist) + { + // hack to unhide the editbar + QHash map; + map.insert("title", tr("Edit")); + m_tvmOsd->SetText("osd_program_editor", map, kOSDTimeout_None); + } } - PlaybackMenuDeinit(); + PlaybackMenuDeinit(menu); } void TV::MenuStrings(void) const { + // Playback menu tr("Playback Menu"); tr("Playback Compact Menu"); tr("Audio"); @@ -12132,6 +12222,10 @@ void TV::MenuStrings(void) const tr("Switch Source"); tr("Jobs"); tr("Begin Transcoding"); + + // Cutlist editor menu + tr("Edit Cut Points"); + tr("Cut List Options"); } void TV::ShowOSDMenu(const PlayerContext *ctx, bool isCompact) diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h index 5eaf276c510..4871629854c 100644 --- a/mythtv/libs/libmythtv/tv_play.h +++ b/mythtv/libs/libmythtv/tv_play.h @@ -729,9 +729,13 @@ class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer void PlaybackMenuShow(const MenuBase &menu, const QDomNode &node, const QDomNode &selected); + void CutlistMenuShow(const MenuBase &menu, + const QDomNode &node, const QDomNode &selected); virtual bool MenuItemDisplay(const MenuItemContext &c); - void PlaybackMenuInit(void); - void PlaybackMenuDeinit(void); + bool MenuItemDisplayPlayback(const MenuItemContext &c); + bool MenuItemDisplayCutlist(const MenuItemContext &c); + void PlaybackMenuInit(const MenuBase &menu); + void PlaybackMenuDeinit(const MenuBase &menu); void MenuStrings(void) const; void MenuLazyInit(void *field); @@ -1034,6 +1038,8 @@ class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer MenuBase m_playbackMenu; MenuBase m_compactMenu; + MenuBase m_cutlistMenu; + MenuBase m_cutlistExitMenu; public: // Constants diff --git a/mythtv/themes/default/menu_cutlist.xml b/mythtv/themes/default/menu_cutlist.xml new file mode 100644 index 00000000000..25b194d0345 --- /dev/null +++ b/mythtv/themes/default/menu_cutlist.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mythtv/themes/default/menu_cutlist_exit.xml b/mythtv/themes/default/menu_cutlist_exit.xml new file mode 100644 index 00000000000..faa34cceb97 --- /dev/null +++ b/mythtv/themes/default/menu_cutlist_exit.xml @@ -0,0 +1,8 @@ + + + + + + + +