MythTV  master
mythtvmenu.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QFile>
3 #include <QCoreApplication>
4 
5 // MythTV
6 #include "mythlogging.h"
7 #include "mythdirs.h"
8 #include "mythuihelper.h"
9 #include "osd.h"
10 #include "mythtvmenu.h"
11 
12 #define LOC QString("TVMenu: ")
13 
14 bool MythTVMenuItemContext::AddButton(MythOSDDialogData *Menu, bool Active, const QString& Action,
15  const QString& DefaultTextActive,
16  const QString& DefaultTextInactive,
17  bool IsMenu, const QString& TextArg) const
18 {
19  bool result = false;
21  {
22  if ((m_showContext != kMenuShowInactive && Active) || (m_showContext != kMenuShowActive && !Active))
23  {
24  result = true;
25  if (m_doDisplay)
26  {
27  QString text = m_actionText;
28  if (text.isEmpty())
29  text = (Active || DefaultTextInactive.isEmpty()) ? DefaultTextActive : DefaultTextInactive;
30 
31  if (!TextArg.isEmpty())
32  text = text.arg(TextArg);
33 
34  bool current = false;
36  current = Active;
38  current = true;
39 
40  Menu->m_buttons.push_back( { text, Action, IsMenu, current });
41  }
42  }
43  }
44  return result;
45 }
46 
47 // Constructor for a menu element.
48 MythTVMenuItemContext::MythTVMenuItemContext(const MythTVMenu& Menu, const QDomNode& Node,
49  QString Name, MenuCurrentContext Current, bool Display)
50  : m_menu(Menu),
51  m_node(Node),
52  m_category(kMenuCategoryMenu),
53  m_menuName(std::move(Name)),
54  m_showContext(kMenuShowAlways),
55  m_currentContext(Current),
56  m_doDisplay(Display)
57 {
58 }
59 
60 // Constructor for an item element.
61 MythTVMenuItemContext::MythTVMenuItemContext(const MythTVMenu& Menu, const QDomNode& Node,
62  MenuShowContext Context, MenuCurrentContext Current,
63  QString Action, QString ActionText, bool Display)
64  : m_menu(Menu),
65  m_node(Node),
66  m_showContext(Context),
67  m_currentContext(Current),
68  m_action(std::move(Action)),
69  m_actionText(std::move(ActionText)),
70  m_doDisplay(Display)
71 {
72 }
73 
74 // Constructor for an itemlist element.
75 MythTVMenuItemContext::MythTVMenuItemContext(const MythTVMenu& Menu, const QDomNode& Node,
76  MenuShowContext Context, MenuCurrentContext Current,
77  QString Action, bool Display)
78  : m_menu(Menu),
79  m_node(Node),
80  m_category(kMenuCategoryItemlist),
81  m_showContext(Context),
82  m_currentContext(Current),
83  m_action(std::move(Action)),
84  m_doDisplay(Display)
85 {
86 }
87 
89 {
90  delete m_document;
91 }
92 
93 QString MythTVMenu::GetName() const
94 {
95  return m_menuName;
96 }
97 
99 {
100  return m_document != nullptr;
101 }
102 
103 QDomElement MythTVMenu::GetRoot() const
104 {
105  return m_document->documentElement();
106 }
107 
109 {
110  return m_translationContext;
111 }
112 
113 const QString& MythTVMenu::GetKeyBindingContext() const
114 {
115  return m_keyBindingContext;
116 }
117 
118 QString MythTVMenu::Translate(const QString& Text) const
119 {
120  return QCoreApplication::translate(m_translationContext, Text.toUtf8(), nullptr);
121 }
122 
123 bool MythTVMenu::MatchesGroup(const QString &Name, const QString &Prefix,
124  MenuCategory Category, QString &OutPrefix)
125 {
126  OutPrefix = Name;
127  return ((Category == kMenuCategoryItem && Name.startsWith(Prefix)) ||
128  (Category == kMenuCategoryItemlist && Name == Prefix));
129 }
130 
131 QString MythTVMenu::GetPathFromNode(QDomNode Node)
132 {
133  QStringList path;
134 
135  while (Node.isElement())
136  {
137  QDomElement el = Node.toElement();
138  if (el.tagName() != "menu")
139  {
140  path.prepend("NotMenu");
141  break;
142  }
143  path.prepend(el.attribute("text"));
144  Node = Node.parentNode();
145  }
146  return path.join('/');
147 }
148 
149 QDomNode MythTVMenu::GetNodeFromPath(const QString& path) const
150 {
151  QStringList pathList = path.split('/');
152  if (pathList.isEmpty())
153  return QDomNode();
154 
155  // Root node is special
156  QDomElement result = GetRoot();
157  QString name = pathList.takeFirst();
158  if ((result.tagName() != "menu") || (result.attribute("text") != name))
159  return QDomNode();
160 
161  // Start walking children
162  while (!pathList.isEmpty())
163  {
164  bool found = false;
165  name = pathList.takeFirst();
166  auto children = result.childNodes();
167  for (int i = 0 ; i < children.count(); i++)
168  {
169  auto child = children.at(i).toElement();
170  if (child.isNull() ||
171  (name == child.attribute("text")) ||
172  (name == child.attribute("XXXtext")))
173  {
174  result = child;
175  found = true;
176  break;
177  }
178  }
179 
180  if (!found)
181  {
182  // Oops. Have name but no matching child.
183  return QDomNode();
184  }
185  }
186  return result;
187 }
188 
189 bool MythTVMenu::LoadFromFile(MenuTypeId id, const QString& Filename, const QString& Menuname,
190  const char * TranslationContext, const QString& KeyBindingContext,
191  int IncludeLevel)
192 {
193  bool result = false;
194 
195  m_id = id;
196  m_translationContext = TranslationContext;
197  m_keyBindingContext = KeyBindingContext;
198  QStringList searchpath = GetMythUI()->GetThemeSearchPath();
199  searchpath.prepend(GetConfDir() + '/');
200  for (auto it = searchpath.cbegin(); !result && it != searchpath.cend(); ++it)
201  {
202  QString themefile = *it + Filename;
203  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Loading menu %1").arg(themefile));
204  QFile file(themefile);
205  if (file.open(QIODevice::ReadOnly))
206  {
207  m_document = new QDomDocument();
208  if (m_document->setContent(&file))
209  {
210  result = true;
211  QDomElement root = GetRoot();
212  m_menuName = Translate(root.attribute("text", Menuname));
213  ProcessIncludes(root, IncludeLevel);
214  }
215  else
216  {
217  delete m_document;
218  m_document = nullptr;
219  }
220  file.close();
221  }
222 
223  if (!result)
224  LOG(VB_FILE, LOG_ERR, LOC + "No theme file " + themefile);
225  }
226 
227  return result;
228 }
229 
230 bool MythTVMenu::LoadFromString(MenuTypeId id, const QString& Text, const QString& Menuname,
231  const char * TranslationContext, const QString& KeyBindingContext,
232  int IncludeLevel)
233 {
234  bool result = false;
235 
236  m_id = id;
237  m_translationContext = TranslationContext;
238  m_keyBindingContext = KeyBindingContext;
239  m_document = new QDomDocument();
240  if (m_document->setContent(Text))
241  {
242  result = true;
243  QDomElement root = GetRoot();
244  m_menuName = Translate(root.attribute("text", Menuname));
245  ProcessIncludes(root, IncludeLevel);
246  }
247  else
248  {
249  delete m_document;
250  m_document = nullptr;
251  }
252  return result;
253 }
254 
255 void MythTVMenu::ProcessIncludes(QDomElement& Root, int IncludeLevel)
256 {
257  const int maxInclude = 10;
258  for (QDomNode node = Root.firstChild(); !node.isNull(); node = node.nextSibling())
259  {
260  if (node.isElement())
261  {
262  QDomElement element = node.toElement();
263  if (element.tagName() == "include")
264  {
265  QString include = element.attribute("file", "");
266  if (include.isEmpty())
267  continue;
268 
269  if (IncludeLevel >= maxInclude)
270  {
271  LOG(VB_GENERAL, LOG_ERR, QString("Maximum include depth (%1) exceeded for %2")
272  .arg(maxInclude).arg(include));
273  return;
274  }
275 
277  if (menu.LoadFromFile(m_id, include, include, m_translationContext,
278  m_keyBindingContext, IncludeLevel + 1))
279  {
280  QDomNode newChild = menu.GetRoot();
281  newChild = m_document->importNode(newChild, true);
282  Root.replaceChild(newChild, node);
283  node = newChild;
284  }
285  }
286  else if (element.tagName() == "menu")
287  {
288  ProcessIncludes(element, IncludeLevel + 1);
289  }
290  }
291  }
292 }
293 
294 bool MythTVMenu::Show(const QDomNode& Node, const QDomNode& Selected,
295  MythTVMenuItemDisplayer& Displayer, MythOSDDialogData* Menu,
296  bool Display) const
297 {
298  bool hasSelected = false;
299  bool displayed = false;
300  for (QDomNode node = Node.firstChild(); !node.isNull(); node = node.nextSibling())
301  {
302  if (node == Selected)
303  hasSelected = true;
304  }
305 
306  for (QDomNode node = Node.firstChild(); !node.isNull(); node = node.nextSibling())
307  {
308  if (node.isElement())
309  {
310  QDomElement element = node.toElement();
311  QString text = Translate(element.attribute("text", ""));
312  QString show = element.attribute("show", "");
313  MenuShowContext showContext =
314  (show == "active" ? kMenuShowActive :
315  show == "inactive" ? kMenuShowInactive : kMenuShowAlways);
316  QString current = element.attribute("current", "");
317  MenuCurrentContext currentContext = kMenuCurrentDefault;
318  if ((current == "active") && !hasSelected)
319  currentContext = kMenuCurrentActive;
320  else if (((current.startsWith("y") || current.startsWith("t") || current == "1")) && !hasSelected)
321  currentContext = kMenuCurrentAlways;
322 
323  if (element.tagName() == "menu")
324  {
325  if (hasSelected && node == Selected)
326  currentContext = kMenuCurrentAlways;
327  MythTVMenuItemContext context(*this, node, text, currentContext, Display);
328  displayed |= Displayer.MenuItemDisplay(context, Menu);
329  }
330  else if (element.tagName() == "item")
331  {
332  QString action = element.attribute("action", "");
333  MythTVMenuItemContext context(*this, node, showContext, currentContext, action, text, Display);
334  displayed |= Displayer.MenuItemDisplay(context, Menu);
335  }
336  else if (element.tagName() == "itemlist")
337  {
338  QString actiongroup = element.attribute("actiongroup", "");
339  MythTVMenuItemContext context(*this, node, showContext, currentContext, actiongroup, Display);
340  displayed |= Displayer.MenuItemDisplay(context, Menu);
341  }
342  }
343 
344  // early exit optimization
345  if (!Display && displayed)
346  break;
347  }
348  return displayed;
349 }
350 
352  : m_id(Id),
353  m_path(std::move(Path))
354 {
355 }
356 
357 
kMenuShowInactive
@ kMenuShowInactive
Definition: mythtvmenu.h:21
MythTVMenu::GetTranslationContext
const char * GetTranslationContext() const
Definition: mythtvmenu.cpp:108
MythTVMenu::ProcessIncludes
void ProcessIncludes(QDomElement &Root, int IncludeLevel)
Definition: mythtvmenu.cpp:255
MythTVMenuItemDisplayer
Definition: mythtvmenu.h:76
MythTVMenu::Show
bool Show(const QDomNode &Node, const QDomNode &Selected, MythTVMenuItemDisplayer &Displayer, MythOSDDialogData *Menu, bool Display=true) const
Definition: mythtvmenu.cpp:294
kMenuCategoryItemlist
@ kMenuCategoryItemlist
Definition: mythtvmenu.h:14
MythTVMenuItemContext::m_doDisplay
bool m_doDisplay
Definition: mythtvmenu.h:73
MythTVMenuItemContext
Definition: mythtvmenu.h:49
kMenuCategoryItem
@ kMenuCategoryItem
Definition: mythtvmenu.h:13
osd.h
MythTVMenuItemContext::m_currentContext
MenuCurrentContext m_currentContext
Definition: mythtvmenu.h:70
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
show
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:339
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
MythTVMenu::GetKeyBindingContext
const QString & GetKeyBindingContext() const
Definition: mythtvmenu.cpp:113
menu
static MythThemedMenu * menu
Definition: mythtv/programs/mythtv-setup/main.cpp:53
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
Action
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:40
MythTVMenuItemContext::MythTVMenuItemContext
MythTVMenuItemContext(const MythTVMenu &Menu, const QDomNode &Node, QString Name, MenuCurrentContext Current, bool Display)
Definition: mythtvmenu.cpp:48
MythTVMenu::m_keyBindingContext
QString m_keyBindingContext
Definition: mythtvmenu.h:116
MythTVMenuItemContext::m_actionText
const QString m_actionText
Definition: mythtvmenu.h:72
MythTVMenu::GetPathFromNode
static QString GetPathFromNode(QDomNode Node)
Definition: mythtvmenu.cpp:131
MythTVMenu::LoadFromString
bool LoadFromString(MenuTypeId id, const QString &Text, const QString &Menuname, const char *TranslationContext, const QString &KeyBindingContext, int IncludeLevel=0)
Definition: mythtvmenu.cpp:230
MythTVMenu::GetName
QString GetName() const
Definition: mythtvmenu.cpp:93
mythlogging.h
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
MythOSDDialogData
Definition: osd.h:65
MythTVMenu::IsLoaded
bool IsLoaded() const
Definition: mythtvmenu.cpp:98
Root
static FRACTAL * Root
Definition: ifs.cpp:145
MythTVMenu::MatchesGroup
static bool MatchesGroup(const QString &Name, const QString &Prefix, MenuCategory Category, QString &OutPrefix)
Definition: mythtvmenu.cpp:123
MenuShowContext
MenuShowContext
Definition: mythtvmenu.h:18
kMenuCategoryMenu
@ kMenuCategoryMenu
Definition: mythtvmenu.h:15
LOC
#define LOC
Definition: mythtvmenu.cpp:12
MythTVMenu::GetRoot
QDomElement GetRoot() const
Definition: mythtvmenu.cpp:103
MythUIThemeHelper::GetThemeSearchPath
QStringList GetThemeSearchPath()
Definition: mythuithemehelper.cpp:150
MythTVMenu::GetNodeFromPath
QDomNode GetNodeFromPath(const QString &path) const
Definition: mythtvmenu.cpp:149
MenuTypeId
MenuTypeId
Definition: mythtvmenu.h:36
MenuCurrentContext
MenuCurrentContext
Definition: mythtvmenu.h:25
kMenuShowAlways
@ kMenuShowAlways
Definition: mythtvmenu.h:22
MythTVMenu::Translate
QString Translate(const QString &Text) const
Definition: mythtvmenu.cpp:118
kMenuShowActive
@ kMenuShowActive
Definition: mythtvmenu.h:20
MenuCategory
MenuCategory
Definition: mythtvmenu.h:11
mythuihelper.h
MythOSDDialogData::m_buttons
std::vector< MythOSDDialogButton > m_buttons
Definition: osd.h:88
MythTVMenuItemContext::AddButton
bool AddButton(MythOSDDialogData *Menu, bool Active, const QString &Action, const QString &DefaultTextActive, const QString &DefaultTextInactive, bool IsMenu, const QString &TextArg) const
Definition: mythtvmenu.cpp:14
MythTVMenu::m_id
MenuTypeId m_id
Definition: mythtvmenu.h:112
MythTVMenuItemDisplayer::MenuItemDisplay
virtual bool MenuItemDisplay(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)=0
MythTVMenu::m_document
QDomDocument * m_document
Definition: mythtvmenu.h:113
mythtvmenu.h
Name
Definition: channelsettings.cpp:46
std
Definition: mythchrono.h:23
MythTVMenuNodeTuple::MythTVMenuNodeTuple
MythTVMenuNodeTuple()=default
MythTVMenu
Definition: mythtvmenu.h:83
MythTVMenu::m_translationContext
const char * m_translationContext
Definition: mythtvmenu.h:114
kMenuCurrentDefault
@ kMenuCurrentDefault
Definition: mythtvmenu.h:27
MythTVMenuItemContext::m_category
MenuCategory m_category
Definition: mythtvmenu.h:67
build_compdb.action
action
Definition: build_compdb.py:9
kMenuCurrentAlways
@ kMenuCurrentAlways
Definition: mythtvmenu.h:29
MythTVMenu::LoadFromFile
bool LoadFromFile(MenuTypeId id, const QString &Filename, const QString &Menuname, const char *TranslationContext, const QString &KeyBindingContext, int IncludeLevel=0)
Definition: mythtvmenu.cpp:189
MythTVMenu::~MythTVMenu
~MythTVMenu()
Definition: mythtvmenu.cpp:88
GetMythUI
MythUIHelper * GetMythUI()
Definition: mythuihelper.cpp:66
MythTVMenu::m_menuName
QString m_menuName
Definition: mythtvmenu.h:115
MythTVMenuItemContext::m_showContext
MenuShowContext m_showContext
Definition: mythtvmenu.h:69
kMenuCurrentActive
@ kMenuCurrentActive
Definition: mythtvmenu.h:28
MythTVMenuItemContext::m_action
const QString m_action
Definition: mythtvmenu.h:71