MythTV master
mythtvmenu.cpp
Go to the documentation of this file.
1// Qt
2#include <QFile>
3#include <QCoreApplication>
4
5// MythTV
9#include "osd.h"
10#include "mythtvmenu.h"
11
12#define LOC QString("TVMenu: ")
13
14bool 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_visible)
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.
49 QString Name, MenuCurrentContext Current, bool Visible)
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_visible(Visible)
57{
58}
59
60// Constructor for an item element.
62 MenuShowContext Context, MenuCurrentContext Current,
63 QString Action, QString ActionText, bool Visible)
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_visible(Visible)
71{
72}
73
74// Constructor for an itemlist element.
76 MenuShowContext Context, MenuCurrentContext Current,
77 QString Action, bool Visible)
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_visible(Visible)
85{
86}
87
89{
90 delete m_document;
91}
92
93QString MythTVMenu::GetName() const
94{
95 return m_menuName;
96}
97
99{
100 return m_document != nullptr;
101}
102
103QDomElement MythTVMenu::GetRoot() const
104{
105 return m_document->documentElement();
106}
107
109{
111}
112
114{
115 return m_keyBindingContext;
116}
117
118QString MythTVMenu::Translate(const QString& Text) const
119{
120 return QCoreApplication::translate(m_translationContext, Text.toUtf8(), nullptr);
121}
122
123bool 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
131QString 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
149QDomNode MythTVMenu::GetNodeFromPath(const QString& path) const
150{
151 QStringList pathList = path.split('/');
152 if (pathList.isEmpty())
153 return {};
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 {};
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 {};
184 }
185 }
186 return result;
187}
188
189bool 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
230bool 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
255void 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
294bool MythTVMenu::Show(const QDomNode& Node, const QDomNode& Selected,
296 bool Visible) 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 {kMenuShowAlways};
314 if (show == "active")
315 showContext = kMenuShowActive;
316 else if (show == "inactive")
317 showContext = kMenuShowInactive;
318 QString current = element.attribute("current", "");
320 if ((current == "active") && !hasSelected)
321 currentContext = kMenuCurrentActive;
322 else if (((current.startsWith("y") || current.startsWith("t") || current == "1")) && !hasSelected)
323 currentContext = kMenuCurrentAlways;
324
325 if (element.tagName() == "menu")
326 {
327 if (hasSelected && node == Selected)
328 currentContext = kMenuCurrentAlways;
329 MythTVMenuItemContext context(*this, node, text, currentContext, Visible);
330 displayed |= Displayer.MenuItemDisplay(context, Menu);
331 }
332 else if (element.tagName() == "item")
333 {
334 QString action = element.attribute("action", "");
335 MythTVMenuItemContext context(*this, node, showContext, currentContext, action, text, Visible);
336 displayed |= Displayer.MenuItemDisplay(context, Menu);
337 }
338 else if (element.tagName() == "itemlist")
339 {
340 QString actiongroup = element.attribute("actiongroup", "");
341 MythTVMenuItemContext context(*this, node, showContext, currentContext, actiongroup, Visible);
342 displayed |= Displayer.MenuItemDisplay(context, Menu);
343 }
344 }
345
346 // early exit optimization
347 if (!Visible && displayed)
348 break;
349 }
350 return displayed;
351}
352
354 : m_id(Id),
355 m_path(std::move(Path))
356{
357}
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:41
std::vector< MythOSDDialogButton > m_buttons
Definition: osd.h:87
MenuCurrentContext m_currentContext
Definition: mythtvmenu.h:70
MenuCategory m_category
Definition: mythtvmenu.h:67
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
MythTVMenuItemContext(const MythTVMenu &Menu, const QDomNode &Node, QString Name, MenuCurrentContext Current, bool Visible)
Definition: mythtvmenu.cpp:48
MenuShowContext m_showContext
Definition: mythtvmenu.h:69
const QString m_action
Definition: mythtvmenu.h:71
const QString m_actionText
Definition: mythtvmenu.h:72
virtual bool MenuItemDisplay(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)=0
MythTVMenuNodeTuple()=default
QString m_menuName
Definition: mythtvmenu.h:115
MenuTypeId m_id
Definition: mythtvmenu.h:112
const QString & GetKeyBindingContext() const
Definition: mythtvmenu.cpp:113
static bool MatchesGroup(const QString &Name, const QString &Prefix, MenuCategory Category, QString &OutPrefix)
Definition: mythtvmenu.cpp:123
bool Show(const QDomNode &Node, const QDomNode &Selected, MythTVMenuItemDisplayer &Displayer, MythOSDDialogData *Menu, bool Visible=true) const
Definition: mythtvmenu.cpp:294
QDomDocument * m_document
Definition: mythtvmenu.h:113
static QString GetPathFromNode(QDomNode Node)
Definition: mythtvmenu.cpp:131
QString GetName() const
Definition: mythtvmenu.cpp:93
const char * m_translationContext
Definition: mythtvmenu.h:114
const char * GetTranslationContext() const
Definition: mythtvmenu.cpp:108
bool LoadFromFile(MenuTypeId id, const QString &Filename, const QString &Menuname, const char *TranslationContext, const QString &KeyBindingContext, int IncludeLevel=0)
Definition: mythtvmenu.cpp:189
bool IsLoaded() const
Definition: mythtvmenu.cpp:98
void ProcessIncludes(QDomElement &Root, int IncludeLevel)
Definition: mythtvmenu.cpp:255
QString m_keyBindingContext
Definition: mythtvmenu.h:116
bool LoadFromString(MenuTypeId id, const QString &Text, const QString &Menuname, const char *TranslationContext, const QString &KeyBindingContext, int IncludeLevel=0)
Definition: mythtvmenu.cpp:230
QDomNode GetNodeFromPath(const QString &path) const
Definition: mythtvmenu.cpp:149
QString Translate(const QString &Text) const
Definition: mythtvmenu.cpp:118
QDomElement GetRoot() const
Definition: mythtvmenu.cpp:103
QStringList GetThemeSearchPath()
static FRACTAL * Root
Definition: ifs.cpp:102
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static MythThemedMenu * menu
#define LOC
Definition: mythtvmenu.cpp:12
MenuCategory
Definition: mythtvmenu.h:12
@ kMenuCategoryMenu
Definition: mythtvmenu.h:15
@ kMenuCategoryItem
Definition: mythtvmenu.h:13
@ kMenuCategoryItemlist
Definition: mythtvmenu.h:14
MenuCurrentContext
Definition: mythtvmenu.h:26
@ kMenuCurrentAlways
Definition: mythtvmenu.h:29
@ kMenuCurrentActive
Definition: mythtvmenu.h:28
@ kMenuCurrentDefault
Definition: mythtvmenu.h:27
MenuShowContext
Definition: mythtvmenu.h:19
@ kMenuShowAlways
Definition: mythtvmenu.h:22
@ kMenuShowInactive
Definition: mythtvmenu.h:21
@ kMenuShowActive
Definition: mythtvmenu.h:20
MenuTypeId
Definition: mythtvmenu.h:37
MythUIHelper * GetMythUI()
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
STL namespace.
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:341