1 /***************************************************************************
2 MenuNode.cpp - generic menu node type
3 -------------------
4 begin : Mon Jan 10 2000
5 copyright : (C) 2000 by Thomas Eschenbacher
6 email : Thomas.Eschenbacher@gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "config.h"
19
20 #include <new>
21
22 #include <QLatin1Char>
23 #include <QPixmap>
24
25 #include "libkwave/Parser.h"
26 #include "libkwave/String.h"
27
28 #include "libgui/MenuGroup.h"
29 #include "libgui/MenuList.h"
30 #include "libgui/MenuNode.h"
31 #include "libgui/MenuRoot.h"
32 #include "libgui/MenuSub.h"
33
34 //*****************************************************************************
MenuNode(Kwave::MenuNode * parent,const QString & name,const QString & command,const QKeySequence & shortcut,const QString & uid)35 Kwave::MenuNode::MenuNode(Kwave::MenuNode *parent,
36 const QString &name,
37 const QString &command,
38 const QKeySequence &shortcut,
39 const QString &uid)
40 :QObject(), m_children(), m_groups(), m_uid(uid), m_shortcut(shortcut),
41 m_name(name), m_command(command), m_parentNode(parent)
42 {
43 }
44
45 //*****************************************************************************
~MenuNode()46 Kwave::MenuNode::~MenuNode()
47 {
48 // leave all groups
49 QStringList::iterator group = m_groups.begin();
50 while (group != m_groups.end()) {
51 leaveGroup(*group);
52 group = m_groups.begin();
53 }
54
55 // remove all childs
56 clear();
57
58 // de-register from our parent
59 if (m_parentNode) m_parentNode->removeChild(this);
60 }
61
62 //*****************************************************************************
path() const63 const QString Kwave::MenuNode::path() const
64 {
65 return (m_parentNode) ?
66 (m_parentNode->path() + _("/") + m_name) : QString();
67 }
68
69 //*****************************************************************************
emitCommand(const QString & command)70 void Kwave::MenuNode::emitCommand(const QString &command)
71 {
72 Q_ASSERT(command.length());
73 if (!command.length()) return ;
74
75 if (!parentNode()) {
76 // no parent -> we are the root node -> we have to emit
77 emit sigCommand(command);
78 } else {
79 // tell the root node to emit
80 Kwave::MenuNode *root = rootNode();
81 Q_ASSERT(root);
82 if (root) root->emitCommand(command);
83 }
84 }
85
86 //*****************************************************************************
actionSelected()87 void Kwave::MenuNode::actionSelected()
88 {
89 if (m_command.length()) emitCommand(m_command);
90 }
91
92 //*****************************************************************************
clear()93 void Kwave::MenuNode::clear()
94 {
95 // remove all children
96 while (!m_children.isEmpty()) {
97 Kwave::MenuNode *child = m_children.takeLast();
98 delete child;
99 }
100 }
101
102 //*****************************************************************************
parentNode() const103 Kwave::MenuNode *Kwave::MenuNode::parentNode() const
104 {
105 return m_parentNode;
106 }
107
108 //*****************************************************************************
rootNode()109 Kwave::MenuNode *Kwave::MenuNode::rootNode()
110 {
111 return (m_parentNode) ? m_parentNode->rootNode() : this;
112 }
113
114 //*****************************************************************************
icon()115 const QIcon Kwave::MenuNode::icon()
116 {
117 static QIcon dummy;
118 Q_ASSERT(dummy.isNull());
119 return dummy;
120 }
121
122 //*****************************************************************************
setIcon(const QIcon & icon)123 void Kwave::MenuNode::setIcon(const QIcon &icon)
124 {
125 qWarning("MenuNode(%s)::setIcon(%p)",
126 DBG(name()), reinterpret_cast<const void *>(&icon));
127 }
128
129 //*****************************************************************************
isEnabled()130 bool Kwave::MenuNode::isEnabled()
131 {
132 // evaluate our own (individual) enable and our parent's enable state
133 if (m_parentNode && !m_parentNode->isEnabled())
134 return false;
135
136 // find out if all our groups are enabled
137 QHash<QString, Kwave::MenuGroup *> &groups = groupList();
138 Kwave::MenuNode *root = rootNode();
139 if (root) {
140 foreach (const QString &group_name, m_groups) {
141 if (groups.contains(group_name)) {
142 Kwave::MenuGroup *group = groups[group_name];
143 if (group && !group->isEnabled()) {
144 qDebug("MenuNode(%s).isEnabled(): group %s is disabled",
145 DBG(name()), DBG(group_name));
146 return false;
147 }
148 }
149 }
150 }
151
152 // if we get here, everything is enabled
153 return true;
154 }
155
156 //*****************************************************************************
setVisible(bool visible)157 void Kwave::MenuNode::setVisible(bool visible)
158 {
159 Q_UNUSED(visible)
160 }
161
162 //*****************************************************************************
setEnabled(bool enable)163 void Kwave::MenuNode::setEnabled(bool enable)
164 {
165 Q_UNUSED(enable)
166 }
167
168 //*****************************************************************************
setChecked(bool check)169 void Kwave::MenuNode::setChecked(bool check)
170 {
171 Q_UNUSED(check)
172 }
173
174 //*****************************************************************************
setText(const QString & text)175 void Kwave::MenuNode::setText(const QString &text)
176 {
177 Q_UNUSED(text)
178 }
179
180 //*****************************************************************************
insertChild(Kwave::MenuNode * node,Kwave::MenuNode * before)181 void Kwave::MenuNode::insertChild(Kwave::MenuNode *node,
182 Kwave::MenuNode *before)
183 {
184 if (!node) return;
185 if (before && m_children.contains(before))
186 m_children.insert(m_children.indexOf(before), node);
187 else
188 m_children.append(node);
189 }
190
191 //*****************************************************************************
setUID(const QString & uid)192 void Kwave::MenuNode::setUID(const QString &uid)
193 {
194 m_uid = uid;
195 }
196
197 //*****************************************************************************
findUID(const QString & uid)198 Kwave::MenuNode *Kwave::MenuNode::findUID(const QString &uid)
199 {
200 if (m_uid == uid) return this; // found ourself
201
202 foreach (Kwave::MenuNode *child, m_children) {
203 Kwave::MenuNode *node = (child) ? child->findUID(uid) : Q_NULLPTR;
204 if (node) return node; // found in child
205 }
206
207 return Q_NULLPTR; // nothing found :-(
208 }
209
210 //*****************************************************************************
findChild(const QString & name)211 Kwave::MenuNode *Kwave::MenuNode::findChild(const QString &name)
212 {
213 Q_ASSERT(name.length());
214
215 foreach (Kwave::MenuNode *child, m_children) {
216 if (child && (name == child->name()))
217 return child;
218 }
219 return Q_NULLPTR;
220 }
221
222 //*****************************************************************************
removeChild(Kwave::MenuNode * child)223 void Kwave::MenuNode::removeChild(Kwave::MenuNode *child)
224 {
225 if (child && !m_children.isEmpty())
226 m_children.removeAll(child);
227 }
228
229 //*****************************************************************************
insertBranch(const QString & name,const QString & command,const QKeySequence & shortcut,const QString & uid)230 Kwave::MenuSub *Kwave::MenuNode::insertBranch(const QString &name,
231 const QString &command,
232 const QKeySequence &shortcut,
233 const QString &uid)
234 {
235 Q_UNUSED(name)
236 Q_UNUSED(command)
237 Q_UNUSED(shortcut)
238 Q_UNUSED(uid)
239 return Q_NULLPTR;
240 }
241
242 //*****************************************************************************
insertLeaf(const QString & name,const QString & command,const QKeySequence & shortcut,const QString & uid)243 Kwave::MenuNode *Kwave::MenuNode::insertLeaf(const QString &name,
244 const QString &command,
245 const QKeySequence &shortcut,
246 const QString &uid)
247 {
248 Q_UNUSED(name)
249 Q_UNUSED(command)
250 Q_UNUSED(shortcut)
251 Q_UNUSED(uid)
252 return Q_NULLPTR;
253 }
254
255 //*****************************************************************************
insertNode(const QString & name,const QString & position,const QString & command,const QKeySequence & shortcut,const QString & uid)256 void Kwave::MenuNode::insertNode(const QString &name,
257 const QString &position,
258 const QString &command,
259 const QKeySequence &shortcut,
260 const QString &uid)
261 {
262 int pos = 0;
263
264 if (!position.length()) {
265 qWarning("MenuNode::insertNode: no position!");
266 return;
267 }
268
269 // at start of the parsing process ?
270 if (!name.length()) {
271 // split off the first token, separated by a slash
272 pos = position.indexOf(QLatin1Char('/'));
273 if (pos < 0) pos = position.length();
274 }
275
276 QString n = position.left(pos);
277 QString p = position;
278 p.remove(0, pos + 1);
279
280 if ((n.length()) && (specialCommand(n))) {
281 // no new branch, only a special command
282 return;
283 }
284
285 if ((!p.length()) || (p[0] == QLatin1Char('#'))) {
286 // end of the tree
287 Kwave::MenuNode *sub = findChild(n);
288 if (sub) {
289 // a leaf with this name already exists
290 // -> maybe we want to set new properties
291 if (!shortcut.isEmpty()) sub->setShortcut(shortcut);
292
293 if (uid.length()) sub->setUID(uid);
294
295 } else {
296 // insert a new leaf
297 sub = insertLeaf(n, command, shortcut, uid);
298 if (!sub) return;
299 }
300
301 if (p.length() && (p[0] == QLatin1Char('#')))
302 sub->specialCommand(p);
303
304 } else {
305 // somewhere in the tree
306 Kwave::MenuNode *sub = findChild(n);
307 if (!sub) {
308 sub = insertBranch(n, command, shortcut, uid);
309 } else if ( !sub->isBranch() && (p[0] != QLatin1Char('#'))) {
310 // remove the "leaf" and insert a branch with
311 // the same properties
312 sub = leafToBranch(sub);
313 } else if (!p.length() || (p[0] == QLatin1Char('#')) ) {
314 // branch already exists and we are at the end of parsing
315 // -> maybe we want to set new properties
316 if (!shortcut.isEmpty()) sub->setShortcut(shortcut);
317 if (uid.length()) sub->setUID(uid);
318 }
319
320 if (sub) {
321 sub->insertNode(QString(), p, command, shortcut, uid);
322 } else {
323 qDebug("MenuNode::insertNode: branch failed!");
324 }
325 }
326 }
327
328 //*****************************************************************************
leafToBranch(Kwave::MenuNode * node)329 Kwave::MenuNode *Kwave::MenuNode::leafToBranch(Kwave::MenuNode *node)
330 {
331 Q_ASSERT(node);
332 Q_ASSERT(node != this);
333
334 if (!node || (node == this)) return Q_NULLPTR;
335
336 // get the old properties
337 bool old_enable = node->isEnabled();
338 QKeySequence old_shortcut = node->shortcut();
339 QString old_uid = node->uid();
340 QIcon old_icon = node->icon();
341 QString name = node->name();
342 QString command = node->command();
343 QStringList old_groups = node->m_groups;
344
345 // remove the old child node
346 removeChild(node);
347
348 // insert the new branch
349 Kwave::MenuSub *sub = insertBranch(name, command, old_shortcut, old_uid);
350 if (sub) {
351 // join it to the same groups
352 foreach (const QString &group, old_groups)
353 sub->joinGroup(group, Kwave::MenuGroup::NORMAL);
354
355 // set the old icon
356 if (!old_icon.isNull()) sub->setIcon(old_icon);
357
358 // set the "enable"
359 sub->setEnabled(old_enable);
360 }
361
362 // free the old node later.
363 // IMPORTANT: we must not call "delete node" now, because we get called
364 // through leafToBranch(this) !
365 Kwave::MenuRoot::deleteLater(node);
366
367 return sub;
368 }
369
370 //*****************************************************************************
groupList()371 QHash<QString, Kwave::MenuGroup *> &Kwave::MenuNode::groupList()
372 {
373 static QHash<QString, Kwave::MenuGroup *> _empty_list;
374 Q_ASSERT(m_parentNode);
375 return (m_parentNode) ? m_parentNode->groupList() : _empty_list;
376 }
377
378 //*****************************************************************************
joinGroup(const QString & group,Kwave::MenuGroup::Mode mode)379 void Kwave::MenuNode::joinGroup(const QString &group,
380 Kwave::MenuGroup::Mode mode)
381 {
382 if (m_groups.contains(group))
383 return; // already joined
384
385 QHash<QString, Kwave::MenuGroup *> &group_list = groupList();
386 Kwave::MenuGroup *grp = Q_NULLPTR;
387 if (group_list.contains(group)) {
388 grp = group_list[group];
389 } else {
390 // group does not already exist, create a new one
391 grp = new(std::nothrow) Kwave::MenuGroup(rootNode(), group, mode);
392 if (grp) group_list.insert(group, grp);
393 }
394
395 // remember that we now belong to the given group
396 m_groups.append(group);
397
398 // register this node as a child of the group
399 if (grp) grp->join(this);
400 }
401
402 //*****************************************************************************
leaveGroup(const QString & group)403 void Kwave::MenuNode::leaveGroup(const QString &group)
404 {
405 QHash<QString, Kwave::MenuGroup *> &group_list = groupList();
406 Kwave::MenuGroup *grp = (group_list.contains(group)) ?
407 group_list.value(group) : Q_NULLPTR;
408
409 // remove the group from our list
410 m_groups.removeAll(group);
411
412 // remove ourself from the group
413 if (grp) {
414 grp->leave(this);
415
416 // clean up the group if nobody uses it any more
417 if (grp->isEmpty()) delete grp;
418 }
419 }
420
421 //*****************************************************************************
specialCommand(const QString & command)422 bool Kwave::MenuNode::specialCommand(const QString &command)
423 {
424 Kwave::Parser parser(command);
425
426 if (parser.command() == _("#icon")) {
427 // --- give the item an icon ---
428 const QString &icon_name = parser.firstParam();
429 if ( icon_name.length()) {
430 // try to load from standard dirs
431 QIcon icon = QIcon::fromTheme( icon_name );
432 if (!icon.isNull()) {
433 setIcon(icon);
434 } else {
435 qWarning("MenuNode '%s': icon '%s' not found !",
436 DBG(name()), DBG( icon_name ));
437 }
438 }
439 return true;
440 }
441
442 if (parser.command() == _("#listmenu")) {
443 // make sure that the current node is a sub menu
444 Kwave::MenuNode *parent = parentNode();
445 Kwave::MenuNode *sub = (parent && !isBranch()) ?
446 parent->leafToBranch(this) : this;
447 if (!sub) return false;
448
449 // append a placeholder for inserting the list
450 // (if it does not already exist)
451 const QString uid = parser.firstParam();
452 const QString cmd = parser.nextParam();
453 if (!sub->findUID(uid)) {
454 Kwave::MenuList *placeholder =
455 new(std::nothrow) Kwave::MenuList(sub, cmd, uid);
456 Q_ASSERT(placeholder);
457 if (!placeholder) return false;
458
459 sub->insertChild(placeholder, Q_NULLPTR);
460 }
461 return true;
462 }
463
464 if (parser.command() == _("#group")) {
465 QString group = parser.firstParam();
466 while (group.length()) {
467 joinGroup(group, Kwave::MenuGroup::NORMAL);
468 group = parser.nextParam();
469 }
470 return true;
471 }
472
473 if (command == _("#disabled")) {
474 // disable the node
475 setEnabled(false);
476 return true;
477 }
478
479 if (command == _("#enabled")) {
480 // enable the node
481 setEnabled(true);
482 return true;
483 }
484
485 return false;
486 }
487
488 //***************************************************************************
489 //***************************************************************************
490