• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.5 API Reference
  • KDE Home
  • Contact Us
 

KDED

  • kded
vfolder_menu.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License version 2 as published by the Free Software Foundation;
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  * Library General Public License for more details.
12  *
13  * You should have received a copy of the GNU Library General Public License
14  * along with this library; see the file COPYING.LIB. If not, write to
15  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301, USA.
17  **/
18 
19 #include "vfolder_menu.h"
20 #include "kbuildservicefactory.h"
21 #include "kbuildsycocainterface.h"
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <config.h>
28 
29 #include <kdebug.h>
30 #include <kglobal.h>
31 #include <kstandarddirs.h>
32 #include <kservice.h>
33 #include <kde_file.h>
34 
35 #include <QtCore/QMap>
36 #include <QtCore/QFile>
37 #include <QtCore/QDir>
38 #include <QtCore/QRegExp>
39 #include <QtCore/QDirIterator>
40 
41 static void foldNode(QDomElement &docElem, QDomElement &e, QMap<QString,QDomElement> &dupeList, QString s=QString()) //krazy:exclude=passbyvalue
42 {
43  if (s.isEmpty())
44  s = e.text();
45  QMap<QString,QDomElement>::iterator it = dupeList.find(s);
46  if (it != dupeList.end())
47  {
48  kDebug(7021) << e.tagName() << "and" << s << "requires combining!";
49 
50  docElem.removeChild(*it);
51  dupeList.erase(it);
52  }
53  dupeList.insert(s, e);
54 }
55 
56 static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag)
57 {
58  for(QStringList::ConstIterator it = list.begin();
59  it != list.end(); ++it)
60  {
61  QDomElement e = docElem.ownerDocument().createElement(tag);
62  QDomText txt = docElem.ownerDocument().createTextNode(*it);
63  e.appendChild(txt);
64  docElem.insertAfter(e, n);
65  }
66 
67  QDomNode next = n.nextSibling();
68  docElem.removeChild(n);
69  n = next;
70 // kDebug(7021) << "Next tag = " << n.toElement().tagName();
71 }
72 
73 void VFolderMenu::registerFile(const QString &file)
74 {
75  int i = file.lastIndexOf('/');
76  if (i < 0)
77  return;
78 
79  QString dir = file.left(i+1); // Include trailing '/'
80  registerDirectory(dir);
81 }
82 
83 void VFolderMenu::registerDirectory(const QString &directory)
84 {
85  m_allDirectories.append(directory);
86 }
87 
88 QStringList VFolderMenu::allDirectories()
89 {
90  if (m_allDirectories.isEmpty())
91  return m_allDirectories;
92  m_allDirectories.sort();
93 
94  QStringList::Iterator it = m_allDirectories.begin();
95  QString previous = *it++;
96  for(;it != m_allDirectories.end();)
97  {
98  if ((*it).startsWith(previous))
99  {
100  it = m_allDirectories.erase(it);
101  }
102  else
103  {
104  previous = *it;
105  ++it;
106  }
107  }
108  return m_allDirectories;
109 }
110 
111 static void
112 track(const QString &menuId, const QString &menuName, const QHash<QString,KService::Ptr>& includeList, const QHash<QString,KService::Ptr>& excludeList, const QHash<QString,KService::Ptr>& itemList, const QString &comment)
113 {
114  if (itemList.contains(menuId))
115  printf("%s: %s INCL %d EXCL %d\n", qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0);
116 }
117 
118 void
119 VFolderMenu::includeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
120 {
121  foreach (const KService::Ptr &p, items2) {
122  items1.insert(p->menuId(), p);
123  }
124 }
125 
126 void
127 VFolderMenu::matchItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
128 {
129  foreach (const KService::Ptr &p, items1)
130  {
131  QString id = p->menuId();
132  if (!items2.contains(id))
133  items1.remove(id);
134  }
135 }
136 
137 void
138 VFolderMenu::excludeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
139 {
140  foreach (const KService::Ptr &p, items2)
141  items1.remove(p->menuId());
142 }
143 
144 VFolderMenu::SubMenu*
145 VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName)
146 {
147  const int i = menuName.indexOf('/');
148  const QString s1 = i > 0 ? menuName.left(i) : menuName;
149  const QString s2 = menuName.mid(i+1);
150 
151  // Look up menu
152  for (QList<SubMenu*>::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it)
153  {
154  SubMenu* menu = *it;
155  if (menu->name == s1)
156  {
157  if (i == -1)
158  {
159  // Take it out
160  parentMenu->subMenus.erase(it);
161  return menu;
162  }
163  else
164  {
165  return takeSubMenu(menu, s2);
166  }
167  }
168  }
169  return 0; // Not found
170 }
171 
172 void
173 VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority)
174 {
175  if (m_track)
176  {
177  track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("Before MenuMerge w. %1 (incl)").arg(menu2->name));
178  track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("Before MenuMerge w. %1 (excl)").arg(menu2->name));
179  }
180  if (reversePriority)
181  {
182  // Merge menu1 with menu2, menu1 takes precedent
183  excludeItems(menu2->items, menu1->excludeItems);
184  includeItems(menu1->items, menu2->items);
185  excludeItems(menu2->excludeItems, menu1->items);
186  includeItems(menu1->excludeItems, menu2->excludeItems);
187  }
188  else
189  {
190  // Merge menu1 with menu2, menu2 takes precedent
191  excludeItems(menu1->items, menu2->excludeItems);
192  includeItems(menu1->items, menu2->items);
193  includeItems(menu1->excludeItems, menu2->excludeItems);
194  menu1->isDeleted = menu2->isDeleted;
195  }
196  while (!menu2->subMenus.isEmpty())
197  {
198  SubMenu *subMenu = menu2->subMenus.takeFirst();
199  insertSubMenu(menu1, subMenu->name, subMenu, reversePriority);
200  }
201 
202  if (reversePriority)
203  {
204  // Merge menu1 with menu2, menu1 takes precedent
205  if (menu1->directoryFile.isEmpty())
206  menu1->directoryFile = menu2->directoryFile;
207  if (menu1->defaultLayoutNode.isNull())
208  menu1->defaultLayoutNode = menu2->defaultLayoutNode;
209  if (menu1->layoutNode.isNull())
210  menu1->layoutNode = menu2->layoutNode;
211  }
212  else
213  {
214  // Merge menu1 with menu2, menu2 takes precedent
215  if (!menu2->directoryFile.isEmpty())
216  menu1->directoryFile = menu2->directoryFile;
217  if (!menu2->defaultLayoutNode.isNull())
218  menu1->defaultLayoutNode = menu2->defaultLayoutNode;
219  if (!menu2->layoutNode.isNull())
220  menu1->layoutNode = menu2->layoutNode;
221  }
222 
223  if (m_track)
224  {
225  track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("After MenuMerge w. %1 (incl)").arg(menu2->name));
226  track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("After MenuMerge w. %1 (excl)").arg(menu2->name));
227  }
228 
229  delete menu2;
230 }
231 
232 void
233 VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority)
234 {
235  const int i = menuName.indexOf('/');
236  const QString s1 = menuName.left(i);
237  const QString s2 = menuName.mid(i+1);
238 
239  // Look up menu
240  foreach (SubMenu *menu, parentMenu->subMenus)
241  {
242  if (menu->name == s1)
243  {
244  if (i == -1)
245  {
246  mergeMenu(menu, newMenu, reversePriority);
247  return;
248  }
249  else
250  {
251  insertSubMenu(menu, s2, newMenu, reversePriority);
252  return;
253  }
254  }
255  }
256  if (i == -1)
257  {
258  // Add it here
259  newMenu->name = menuName;
260  parentMenu->subMenus.append(newMenu);
261  }
262  else
263  {
264  SubMenu *menu = new SubMenu;
265  menu->name = s1;
266  parentMenu->subMenus.append(menu);
267  insertSubMenu(menu, s2, newMenu);
268  }
269 }
270 
271 void
272 VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService)
273 {
274  const int i = name.indexOf('/');
275 
276  if (i == -1)
277  {
278  // Add it here
279  parentMenu->items.insert(newService->menuId(), newService);
280  return;
281  }
282 
283  QString s1 = name.left(i);
284  QString s2 = name.mid(i+1);
285 
286  // Look up menu
287  foreach (SubMenu *menu, parentMenu->subMenus)
288  {
289  if (menu->name == s1)
290  {
291  insertService(menu, s2, newService);
292  return;
293  }
294  }
295 
296  SubMenu *menu = new SubMenu;
297  menu->name = s1;
298  parentMenu->subMenus.append(menu);
299  insertService(menu, s2, newService);
300 }
301 
302 
303 VFolderMenu::VFolderMenu(KBuildServiceFactory* serviceFactory, KBuildSycocaInterface* kbuildsycocaInterface)
304  : m_track(false),
305  m_serviceFactory(serviceFactory),
306  m_kbuildsycocaInterface(kbuildsycocaInterface)
307 {
308  m_usedAppsDict.reserve(797);
309  m_rootMenu = 0;
310  initDirs();
311 }
312 
313 VFolderMenu::~VFolderMenu()
314 {
315  delete m_rootMenu;
316  delete m_appsInfo;
317 }
318 
319 #define FOR_ALL_APPLICATIONS(it) \
320  foreach (AppsInfo *info, m_appsInfoStack) \
321  { \
322  QHashIterator<QString,KService::Ptr> it = info->applications; \
323  while (it.hasNext()) \
324  { \
325  it.next();
326 #define FOR_ALL_APPLICATIONS_END } }
327 
328 #define FOR_CATEGORY(category, it) \
329  foreach (AppsInfo *info, m_appsInfoStack) \
330  { \
331  const KService::List list = info->dictCategories.value(category); \
332  for(KService::List::ConstIterator it = list.constBegin(); \
333  it != list.constEnd(); ++it) \
334  {
335 #define FOR_CATEGORY_END } }
336 
337 KService::Ptr
338 VFolderMenu::findApplication(const QString &relPath)
339 {
340  foreach(AppsInfo *info, m_appsInfoStack)
341  {
342  if (info->applications.contains(relPath)) {
343  KService::Ptr s = info->applications[relPath];
344  if (s)
345  return s;
346  }
347  }
348  return KService::Ptr();
349 }
350 
351 void
352 VFolderMenu::addApplication(const QString &id, KService::Ptr service)
353 {
354  service->setMenuId(id);
355  m_appsInfo->applications.insert(id, service); // replaces, if already there
356  m_serviceFactory->addEntry(KSycocaEntry::Ptr::staticCast(service));
357 }
358 
359 void
360 VFolderMenu::buildApplicationIndex(bool unusedOnly)
361 {
362  foreach (AppsInfo *info, m_appsInfoList)
363  {
364  info->dictCategories.clear();
365  QMutableHashIterator<QString,KService::Ptr> it = info->applications;
366  while (it.hasNext())
367  {
368  KService::Ptr s = it.next().value();
369  if (unusedOnly && m_usedAppsDict.contains(s->menuId()))
370  {
371  // Remove and skip this one
372  it.remove();
373  continue;
374  }
375 
376  Q_FOREACH(const QString& cat, s->categories()) {
377  info->dictCategories[cat].append(s); // find or insert entry in hash
378  }
379  }
380  }
381 }
382 
383 void
384 VFolderMenu::createAppsInfo()
385 {
386  if (m_appsInfo) return;
387 
388  m_appsInfo = new AppsInfo;
389  m_appsInfoStack.prepend(m_appsInfo);
390  m_appsInfoList.append(m_appsInfo);
391  m_currentMenu->apps_info = m_appsInfo;
392 }
393 
394 void
395 VFolderMenu::loadAppsInfo()
396 {
397  m_appsInfo = m_currentMenu->apps_info;
398  if (!m_appsInfo)
399  return; // No appsInfo for this menu
400 
401  if (m_appsInfoStack.count() && m_appsInfoStack.first() == m_appsInfo)
402  return; // Already added (By createAppsInfo?)
403 
404  m_appsInfoStack.prepend(m_appsInfo); // Add
405 }
406 
407 void
408 VFolderMenu::unloadAppsInfo()
409 {
410  m_appsInfo = m_currentMenu->apps_info;
411  if (!m_appsInfo)
412  return; // No appsInfo for this menu
413 
414  if (m_appsInfoStack.first() != m_appsInfo)
415  {
416  return; // Already removed (huh?)
417  }
418 
419  m_appsInfoStack.removeAll(m_appsInfo); // Remove
420  m_appsInfo = 0;
421 }
422 
423 QString
424 VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg)
425 {
426  QString dir = _dir;
427  if (QDir::isRelativePath(dir))
428  {
429  dir = baseDir + dir;
430  }
431  if (!dir.endsWith('/'))
432  dir += '/';
433 
434  bool relative = QDir::isRelativePath(dir);
435  if (relative && !keepRelativeToCfg) {
436  relative = false;
437  dir = KGlobal::dirs()->findResource("xdgconf-menu", dir);
438  }
439 
440  if (!relative)
441  dir = KGlobal::dirs()->realPath(dir);
442 
443  return dir;
444 }
445 
446 static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir)
447 {
448  QDomNodeList mergeFileList = doc.elementsByTagName(tag);
449  for(int i = 0; i < (int)mergeFileList.count(); i++)
450  {
451  QDomAttr attr = doc.createAttribute("__BaseDir");
452  attr.setValue(dir);
453  mergeFileList.item(i).toElement().setAttributeNode(attr);
454  }
455 }
456 
457 static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path)
458 {
459  QDomNodeList mergeFileList = doc.elementsByTagName(tag);
460  for(int i = 0; i < (int)mergeFileList.count(); i++)
461  {
462  QDomAttr attr = doc.createAttribute("__BasePath");
463  attr.setValue(path);
464  mergeFileList.item(i).toElement().setAttributeNode(attr);
465  }
466 }
467 
468 QDomDocument
469 VFolderMenu::loadDoc()
470 {
471  QDomDocument doc;
472  if ( m_docInfo.path.isEmpty() )
473  {
474  return doc;
475  }
476  QFile file( m_docInfo.path );
477  if ( !file.open( QIODevice::ReadOnly ) )
478  {
479  kWarning(7021) << "Could not open " << m_docInfo.path;
480  return doc;
481  }
482  QString errorMsg;
483  int errorRow;
484  int errorCol;
485  if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) {
486  kWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg;
487  file.close();
488  return doc;
489  }
490  file.close();
491 
492  tagBaseDir(doc, "MergeFile", m_docInfo.baseDir);
493  tagBasePath(doc, "MergeFile", m_docInfo.path);
494  tagBaseDir(doc, "MergeDir", m_docInfo.baseDir);
495  tagBaseDir(doc, "DirectoryDir", m_docInfo.baseDir);
496  tagBaseDir(doc, "AppDir", m_docInfo.baseDir);
497  tagBaseDir(doc, "LegacyDir", m_docInfo.baseDir);
498 
499  return doc;
500 }
501 
502 
503 void
504 VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere)
505 {
506 kDebug(7021) << "VFolderMenu::mergeFile:" << m_docInfo.path;
507  QDomDocument doc = loadDoc();
508 
509  QDomElement docElem = doc.documentElement();
510  QDomNode n = docElem.firstChild();
511  QDomNode last = mergeHere;
512  while( !n.isNull() )
513  {
514  QDomElement e = n.toElement(); // try to convert the node to an element.
515  QDomNode next = n.nextSibling();
516 
517  if (e.isNull())
518  {
519  // Skip
520  }
521  // The spec says we must ignore any Name nodes
522  else if (e.tagName() != "Name")
523  {
524  parent.insertAfter(n, last);
525  last = n;
526  }
527 
528  docElem.removeChild(n);
529  n = next;
530  }
531 }
532 
533 
534 void
535 VFolderMenu::mergeMenus(QDomElement &docElem, QString &name)
536 {
537  QMap<QString,QDomElement> menuNodes;
538  QMap<QString,QDomElement> directoryNodes;
539  QMap<QString,QDomElement> appDirNodes;
540  QMap<QString,QDomElement> directoryDirNodes;
541  QMap<QString,QDomElement> legacyDirNodes;
542  QDomElement defaultLayoutNode;
543  QDomElement layoutNode;
544 
545  QDomNode n = docElem.firstChild();
546  while( !n.isNull() ) {
547  QDomElement e = n.toElement(); // try to convert the node to an element.
548  if( e.isNull() ) {
549 // kDebug(7021) << "Empty node";
550  }
551  else if( e.tagName() == "DefaultAppDirs") {
552  // Replace with m_defaultAppDirs
553  replaceNode(docElem, n, m_defaultAppDirs, "AppDir");
554  continue;
555  }
556  else if( e.tagName() == "DefaultDirectoryDirs") {
557  // Replace with m_defaultDirectoryDirs
558  replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir");
559  continue;
560  }
561  else if( e.tagName() == "DefaultMergeDirs") {
562  // Replace with m_defaultMergeDirs
563  replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir");
564  continue;
565  }
566  else if( e.tagName() == "AppDir") {
567  // Filter out dupes
568  foldNode(docElem, e, appDirNodes);
569  }
570  else if( e.tagName() == "DirectoryDir") {
571  // Filter out dupes
572  foldNode(docElem, e, directoryDirNodes);
573  }
574  else if( e.tagName() == "LegacyDir") {
575  // Filter out dupes
576  foldNode(docElem, e, legacyDirNodes);
577  }
578  else if( e.tagName() == "Directory") {
579  // Filter out dupes
580  foldNode(docElem, e, directoryNodes);
581  }
582  else if( e.tagName() == "Move") {
583  // Filter out dupes
584  QString orig;
585  QDomNode n2 = e.firstChild();
586  while( !n2.isNull() ) {
587  QDomElement e2 = n2.toElement(); // try to convert the node to an element.
588  if( e2.tagName() == "Old")
589  {
590  orig = e2.text();
591  break;
592  }
593  n2 = n2.nextSibling();
594  }
595  foldNode(docElem, e, appDirNodes, orig);
596  }
597  else if( e.tagName() == "Menu") {
598  QString name;
599  mergeMenus(e, name);
600  QMap<QString,QDomElement>::iterator it = menuNodes.find(name);
601  if (it != menuNodes.end())
602  {
603  QDomElement docElem2 = *it;
604  QDomNode n2 = docElem2.firstChild();
605  QDomNode first = e.firstChild();
606  while( !n2.isNull() ) {
607  QDomElement e2 = n2.toElement(); // try to convert the node to an element.
608  QDomNode n3 = n2.nextSibling();
609  e.insertBefore(n2, first);
610  docElem2.removeChild(n2);
611  n2 = n3;
612  }
613  // We still have duplicated Name entries
614  // but we don't care about that
615 
616  docElem.removeChild(docElem2);
617  menuNodes.erase(it);
618  }
619  menuNodes.insert(name, e);
620  }
621  else if( e.tagName() == "MergeFile") {
622  if ((e.attribute("type") == "parent"))
623  pushDocInfoParent(e.attribute("__BasePath"), e.attribute("__BaseDir"));
624  else
625  pushDocInfo(e.text(), e.attribute("__BaseDir"));
626 
627  if (!m_docInfo.path.isEmpty())
628  mergeFile(docElem, n);
629  popDocInfo();
630 
631  QDomNode last = n;
632  n = n.nextSibling();
633  docElem.removeChild(last); // Remove the MergeFile node
634  continue;
635  }
636  else if( e.tagName() == "MergeDir") {
637  QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"), true);
638 
639  const QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu", dir);
640  for(QStringList::ConstIterator it=dirs.begin();
641  it != dirs.end(); ++it)
642  {
643  registerDirectory(*it);
644  }
645 
646  QStringList fileList;
647  if (!QDir::isRelativePath(dir))
648  {
649  // Absolute
650  fileList = KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu");
651  }
652  else
653  {
654  // Relative
655  (void) KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu",
656  KStandardDirs::NoDuplicates, fileList);
657  }
658 
659  for(QStringList::ConstIterator it=fileList.constBegin();
660  it != fileList.constEnd(); ++it)
661  {
662  pushDocInfo(*it);
663  mergeFile(docElem, n);
664  popDocInfo();
665  }
666 
667  QDomNode last = n;
668  n = n.nextSibling();
669  docElem.removeChild(last); // Remove the MergeDir node
670 
671  continue;
672  }
673  else if( e.tagName() == "Name") {
674  name = e.text();
675  }
676  else if( e.tagName() == "DefaultLayout") {
677  if (!defaultLayoutNode.isNull())
678  docElem.removeChild(defaultLayoutNode);
679  defaultLayoutNode = e;
680  }
681  else if( e.tagName() == "Layout") {
682  if (!layoutNode.isNull())
683  docElem.removeChild(layoutNode);
684  layoutNode = e;
685  }
686  n = n.nextSibling();
687  }
688 }
689 
690 void
691 VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir)
692 {
693  m_docInfoStack.push(m_docInfo);
694  if (!baseDir.isEmpty())
695  {
696  if (!QDir::isRelativePath(baseDir))
697  m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu", baseDir);
698  else
699  m_docInfo.baseDir = baseDir;
700  }
701 
702  QString baseName = fileName;
703  if (!QDir::isRelativePath(baseName))
704  registerFile(baseName);
705  else
706  baseName = m_docInfo.baseDir + baseName;
707 
708  m_docInfo.path = locateMenuFile(fileName);
709  if (m_docInfo.path.isEmpty())
710  {
711  m_docInfo.baseDir.clear();
712  m_docInfo.baseName.clear();
713  kDebug(7021) << "Menu" << fileName << "not found.";
714  return;
715  }
716  int i;
717  i = baseName.lastIndexOf('/');
718  if (i > 0)
719  {
720  m_docInfo.baseDir = baseName.left(i+1);
721  m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6);
722  }
723  else
724  {
725  m_docInfo.baseDir.clear();
726  m_docInfo.baseName = baseName.left( baseName.length() - 5 );
727  }
728 }
729 
730 void
731 VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir)
732 {
733  m_docInfoStack.push(m_docInfo);
734 
735  m_docInfo.baseDir = baseDir;
736 
737  QString fileName = basePath.mid(basePath.lastIndexOf('/')+1);
738  m_docInfo.baseName = fileName.left( fileName.length() - 5 );
739  QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
740 
741  QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu", baseName);
742 
743  while( !result.isEmpty() && (result[0] != basePath))
744  result.erase(result.begin());
745 
746  if (result.count() <= 1)
747  {
748  m_docInfo.path.clear(); // No parent found
749  return;
750  }
751  m_docInfo.path = result[1];
752 }
753 
754 void
755 VFolderMenu::popDocInfo()
756 {
757  m_docInfo = m_docInfoStack.pop();
758 }
759 
760 QString
761 VFolderMenu::locateMenuFile(const QString &fileName)
762 {
763  if (!QDir::isRelativePath(fileName))
764  {
765  if (KStandardDirs::exists(fileName))
766  return fileName;
767  return QString();
768  }
769 
770  QString result;
771 
772  QString xdgMenuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX"));
773  if (!xdgMenuPrefix.isEmpty())
774  {
775  QFileInfo fileInfo(fileName);
776 
777  QString fileNameOnly = fileInfo.fileName();
778  if (!fileNameOnly.startsWith(xdgMenuPrefix))
779  fileNameOnly = xdgMenuPrefix + fileNameOnly;
780 
781  QString baseName = QDir::cleanPath(m_docInfo.baseDir +
782  fileInfo.path() + '/' + fileNameOnly);
783  result = KStandardDirs::locate("xdgconf-menu", baseName);
784  }
785 
786  if (result.isEmpty())
787  {
788  QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
789  result = KStandardDirs::locate("xdgconf-menu", baseName);
790  }
791 
792  return result;
793 }
794 
795 QString
796 VFolderMenu::locateDirectoryFile(const QString &fileName)
797 {
798  if (fileName.isEmpty())
799  return QString();
800 
801  if (!QDir::isRelativePath(fileName))
802  {
803  if (KStandardDirs::exists(fileName))
804  return fileName;
805  return QString();
806  }
807 
808  // First location in the list wins
809  for(QStringList::ConstIterator it = m_directoryDirs.constBegin();
810  it != m_directoryDirs.constEnd();
811  ++it)
812  {
813  QString tmp = (*it)+fileName;
814  if (KStandardDirs::exists(tmp))
815  return tmp;
816  }
817 
818  return QString();
819 }
820 
821 void
822 VFolderMenu::initDirs()
823 {
824  m_defaultDataDirs = KGlobal::dirs()->kfsstnd_prefixes().split(':', QString::SkipEmptyParts);
825  const QString localDir = m_defaultDataDirs.first();
826  m_defaultDataDirs.removeAll(localDir); // Remove local dir
827 
828  m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString());
829  m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString());
830  m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps");
831 }
832 
833 void
834 VFolderMenu::loadMenu(const QString &fileName)
835 {
836  m_defaultMergeDirs.clear();
837 
838  if (!fileName.endsWith(QLatin1String(".menu")))
839  return;
840 
841  pushDocInfo(fileName);
842  m_defaultMergeDirs << m_docInfo.baseName+"-merged/";
843  m_doc = loadDoc();
844  popDocInfo();
845 
846  if (m_doc.isNull())
847  {
848  if (m_docInfo.path.isEmpty())
849  kError(7021) << fileName << " not found in " << m_allDirectories << endl;
850  else
851  kWarning(7021) << "Load error (" << m_docInfo.path << ")";
852  return;
853  }
854 
855  QDomElement e = m_doc.documentElement();
856  QString name;
857  mergeMenus(e, name);
858 }
859 
860 void
861 VFolderMenu::processCondition(QDomElement &domElem, QHash<QString,KService::Ptr>& items)
862 {
863  if (domElem.tagName() == "And")
864  {
865  QDomNode n = domElem.firstChild();
866  // Look for the first child element
867  while (!n.isNull()) // loop in case of comments
868  {
869  QDomElement e = n.toElement();
870  n = n.nextSibling();
871  if ( !e.isNull() ) {
872  processCondition(e, items);
873  break; // we only want the first one
874  }
875  }
876 
877  QHash<QString,KService::Ptr> andItems;
878  while( !n.isNull() ) {
879  QDomElement e = n.toElement();
880  if (e.tagName() == "Not")
881  {
882  // Special handling for "and not"
883  QDomNode n2 = e.firstChild();
884  while( !n2.isNull() ) {
885  QDomElement e2 = n2.toElement();
886  andItems.clear();
887  processCondition(e2, andItems);
888  excludeItems(items, andItems);
889  n2 = n2.nextSibling();
890  }
891  }
892  else
893  {
894  andItems.clear();
895  processCondition(e, andItems);
896  matchItems(items, andItems);
897  }
898  n = n.nextSibling();
899  }
900  }
901  else if (domElem.tagName() == "Or")
902  {
903  QDomNode n = domElem.firstChild();
904  // Look for the first child element
905  while (!n.isNull()) // loop in case of comments
906  {
907  QDomElement e = n.toElement();
908  n = n.nextSibling();
909  if ( !e.isNull() ) {
910  processCondition(e, items);
911  break; // we only want the first one
912  }
913  }
914 
915  QHash<QString,KService::Ptr> orItems;
916  while( !n.isNull() ) {
917  QDomElement e = n.toElement();
918  if ( !e.isNull() ) {
919  orItems.clear();
920  processCondition(e, orItems);
921  includeItems(items, orItems);
922  }
923  n = n.nextSibling();
924  }
925  }
926  else if (domElem.tagName() == "Not")
927  {
928  FOR_ALL_APPLICATIONS(it)
929  {
930  KService::Ptr s = it.value();
931  items.insert(s->menuId(), s);
932  }
933  FOR_ALL_APPLICATIONS_END
934 
935  QHash<QString,KService::Ptr> notItems;
936  QDomNode n = domElem.firstChild();
937  while( !n.isNull() ) {
938  QDomElement e = n.toElement();
939  if ( !e.isNull() ) {
940  notItems.clear();
941  processCondition(e, notItems);
942  excludeItems(items, notItems);
943  }
944  n = n.nextSibling();
945  }
946  }
947  else if (domElem.tagName() == "Category")
948  {
949  FOR_CATEGORY(domElem.text(), it)
950  {
951  KService::Ptr s = *it;
952  items.insert(s->menuId(), s);
953  }
954  FOR_CATEGORY_END
955  }
956  else if (domElem.tagName() == "All")
957  {
958  FOR_ALL_APPLICATIONS(it)
959  {
960  KService::Ptr s = it.value();
961  items.insert(s->menuId(), s);
962  }
963  FOR_ALL_APPLICATIONS_END
964  }
965  else if (domElem.tagName() == "Filename")
966  {
967  const QString filename = domElem.text();
968  //kDebug(7021) << "Adding file" << filename;
969  KService::Ptr s = findApplication(filename);
970  if (s)
971  items.insert(filename, s);
972  }
973 }
974 
975 void
976 VFolderMenu::loadApplications(const QString &dir, const QString &prefix)
977 {
978  kDebug(7021) << "Looking up applications under" << dir;
979 
980  QDirIterator it(dir);
981  while (it.hasNext()) {
982  it.next();
983  const QFileInfo fi = it.fileInfo();
984  const QString fn = fi.fileName();
985  if (fi.isDir()) {
986  if(fn == QLatin1String(".") || fn == QLatin1String(".."))
987  continue;
988  loadApplications(fi.filePath(), prefix + fn + '-');
989  continue;
990  }
991  if (fi.isFile()) {
992  if (!fn.endsWith(QLatin1String(".desktop")))
993  continue;
994  KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath());
995  if (service)
996  addApplication(prefix + fn, service);
997  }
998  }
999 }
1000 
1001 void
1002 VFolderMenu::processKDELegacyDirs()
1003 {
1004  kDebug(7021);
1005 
1006  QHash<QString,KService::Ptr> items;
1007  QString prefix = "kde4-";
1008 
1009  QStringList relFiles;
1010 
1011  (void) KGlobal::dirs()->findAllResources( "apps",
1012  QString(),
1013  KStandardDirs::Recursive |
1014  KStandardDirs::NoDuplicates,
1015  relFiles);
1016  for(QStringList::ConstIterator it = relFiles.constBegin();
1017  it != relFiles.constEnd(); ++it)
1018  {
1019  if (!m_forcedLegacyLoad && (*it).endsWith(QLatin1String(".directory")))
1020  {
1021  QString name = *it;
1022  if (!name.endsWith(QLatin1String("/.directory")))
1023  continue; // Probably ".directory", skip it.
1024 
1025  name = name.left(name.length()-11);
1026 
1027  SubMenu *newMenu = new SubMenu;
1028  newMenu->directoryFile = KStandardDirs::locate("apps", *it);
1029 
1030  insertSubMenu(m_currentMenu, name, newMenu);
1031  continue;
1032  }
1033 
1034  if ((*it).endsWith(QLatin1String(".desktop")))
1035  {
1036  QString name = *it;
1037  KService::Ptr service = m_kbuildsycocaInterface->createService(name);
1038 
1039  if (service && !m_forcedLegacyLoad)
1040  {
1041  QString id = name;
1042  // Strip path from id
1043  int i = id.lastIndexOf('/');
1044  if (i >= 0)
1045  id = id.mid(i+1);
1046 
1047  id.prepend(prefix);
1048 
1049  // TODO: add Legacy category
1050  addApplication(id, service);
1051  items.insert(service->menuId(), service);
1052  if (service->categories().isEmpty())
1053  insertService(m_currentMenu, name, service);
1054 
1055  }
1056  }
1057  }
1058  markUsedApplications(items);
1059  m_legacyLoaded = true;
1060 }
1061 
1062 void
1063 VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix)
1064 {
1065  kDebug(7021).nospace() << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")";
1066 
1067  QHash<QString,KService::Ptr> items;
1068  QDirIterator it(dir);
1069  while (it.hasNext()) {
1070  it.next();
1071  const QFileInfo fi = it.fileInfo();
1072  const QString fn = fi.fileName();
1073  if (fi.isDir()) {
1074  if(fn == QLatin1String(".") || fn == QLatin1String(".."))
1075  continue;
1076  SubMenu *parentMenu = m_currentMenu;
1077 
1078  m_currentMenu = new SubMenu;
1079  m_currentMenu->name = fn;
1080  m_currentMenu->directoryFile = fi.absoluteFilePath() + "/.directory";
1081 
1082  parentMenu->subMenus.append(m_currentMenu);
1083 
1084  processLegacyDir(fi.filePath(), relDir + fn + '/', prefix);
1085  m_currentMenu = parentMenu;
1086  continue;
1087  }
1088  if (fi.isFile() /*&& !fi.isSymLink() ?? */) {
1089  if (!fn.endsWith(QLatin1String(".desktop")))
1090  continue;
1091  KService::Ptr service = m_kbuildsycocaInterface->createService(fi.absoluteFilePath());
1092  if (service)
1093  {
1094  const QString id = prefix + fn;
1095 
1096  // TODO: Add legacy category
1097  addApplication(id, service);
1098  items.insert(service->menuId(), service);
1099 
1100  if (service->categories().isEmpty())
1101  m_currentMenu->items.insert(id, service);
1102  }
1103  }
1104  }
1105  markUsedApplications(items);
1106 }
1107 
1108 
1109 
1110 void
1111 VFolderMenu::processMenu(QDomElement &docElem, int pass)
1112 {
1113  SubMenu *parentMenu = m_currentMenu;
1114  int oldDirectoryDirsCount = m_directoryDirs.count();
1115 
1116  QString name;
1117  QString directoryFile;
1118  bool onlyUnallocated = false;
1119  bool isDeleted = false;
1120  bool kdeLegacyDirsDone = false;
1121  QDomElement defaultLayoutNode;
1122  QDomElement layoutNode;
1123 
1124  QDomElement query;
1125  QDomNode n = docElem.firstChild();
1126  while( !n.isNull() ) {
1127  QDomElement e = n.toElement(); // try to convert the node to an element.
1128  if (e.tagName() == "Name")
1129  {
1130  name = e.text();
1131  }
1132  else if (e.tagName() == "Directory")
1133  {
1134  directoryFile = e.text();
1135  }
1136  else if (e.tagName() == "DirectoryDir")
1137  {
1138  QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
1139 
1140  m_directoryDirs.prepend(dir);
1141  }
1142  else if (e.tagName() == "OnlyUnallocated")
1143  {
1144  onlyUnallocated = true;
1145  }
1146  else if (e.tagName() == "NotOnlyUnallocated")
1147  {
1148  onlyUnallocated = false;
1149  }
1150  else if (e.tagName() == "Deleted")
1151  {
1152  isDeleted = true;
1153  }
1154  else if (e.tagName() == "NotDeleted")
1155  {
1156  isDeleted = false;
1157  }
1158  else if (e.tagName() == "DefaultLayout")
1159  {
1160  defaultLayoutNode = e;
1161  }
1162  else if (e.tagName() == "Layout")
1163  {
1164  layoutNode = e;
1165  }
1166  n = n.nextSibling();
1167  }
1168 
1169  // Setup current menu entry
1170  if (pass == 0)
1171  {
1172  m_currentMenu = 0;
1173  // Look up menu
1174  if (parentMenu)
1175  {
1176  foreach (SubMenu *menu, parentMenu->subMenus)
1177  {
1178  if (menu->name == name)
1179  {
1180  m_currentMenu = menu;
1181  break;
1182  }
1183  }
1184  }
1185 
1186  if (!m_currentMenu) // Not found?
1187  {
1188  // Create menu
1189  m_currentMenu = new SubMenu;
1190  m_currentMenu->name = name;
1191 
1192  if (parentMenu)
1193  parentMenu->subMenus.append(m_currentMenu);
1194  else
1195  m_rootMenu = m_currentMenu;
1196  }
1197  if (directoryFile.isEmpty())
1198  {
1199  kDebug(7021) << "Menu" << name << "does not specify a directory file.";
1200  }
1201 
1202  // Override previous directoryFile iff available
1203  QString tmp = locateDirectoryFile(directoryFile);
1204  if (! tmp.isEmpty())
1205  m_currentMenu->directoryFile = tmp;
1206  m_currentMenu->isDeleted = isDeleted;
1207 
1208  m_currentMenu->defaultLayoutNode = defaultLayoutNode;
1209  m_currentMenu->layoutNode = layoutNode;
1210  }
1211  else
1212  {
1213  // Look up menu
1214  if (parentMenu)
1215  {
1216  foreach (SubMenu *menu, parentMenu->subMenus)
1217  {
1218  if (menu->name == name)
1219  {
1220  m_currentMenu = menu;
1221  break;
1222  }
1223  }
1224  }
1225  else
1226  {
1227  m_currentMenu = m_rootMenu;
1228  }
1229  }
1230 
1231  // Process AppDir and LegacyDir
1232  if (pass == 0)
1233  {
1234  QDomElement query;
1235  QDomNode n = docElem.firstChild();
1236  while( !n.isNull() ) {
1237  QDomElement e = n.toElement(); // try to convert the node to an element.
1238  if (e.tagName() == "AppDir")
1239  {
1240  createAppsInfo();
1241  QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
1242 
1243  registerDirectory(dir);
1244 
1245  loadApplications(dir, QString());
1246  }
1247  else if (e.tagName() == "KDELegacyDirs")
1248  {
1249  createAppsInfo();
1250  if (!kdeLegacyDirsDone)
1251  {
1252 kDebug(7021) << "Processing KDE Legacy dirs for <KDE>";
1253  SubMenu *oldMenu = m_currentMenu;
1254  m_currentMenu = new SubMenu;
1255 
1256  processKDELegacyDirs();
1257 
1258  m_legacyNodes.insert("<KDE>", m_currentMenu);
1259  m_currentMenu = oldMenu;
1260 
1261  kdeLegacyDirsDone = true;
1262  }
1263  }
1264  else if (e.tagName() == "LegacyDir")
1265  {
1266  createAppsInfo();
1267  QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
1268 
1269  QString prefix = e.attributes().namedItem("prefix").toAttr().value();
1270 
1271  if (m_defaultLegacyDirs.contains(dir))
1272  {
1273  if (!kdeLegacyDirsDone)
1274  {
1275 kDebug(7021) << "Processing KDE Legacy dirs for" << dir;
1276  SubMenu *oldMenu = m_currentMenu;
1277  m_currentMenu = new SubMenu;
1278 
1279  processKDELegacyDirs();
1280 
1281  m_legacyNodes.insert("<KDE>", m_currentMenu);
1282  m_currentMenu = oldMenu;
1283 
1284  kdeLegacyDirsDone = true;
1285  }
1286  }
1287  else
1288  {
1289  SubMenu *oldMenu = m_currentMenu;
1290  m_currentMenu = new SubMenu;
1291 
1292  registerDirectory(dir);
1293 
1294  processLegacyDir(dir, QString(), prefix);
1295 
1296  m_legacyNodes.insert(dir, m_currentMenu);
1297  m_currentMenu = oldMenu;
1298  }
1299  }
1300  n = n.nextSibling();
1301  }
1302  }
1303 
1304  loadAppsInfo(); // Update the scope wrt the list of applications
1305 
1306  if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
1307  {
1308  n = docElem.firstChild();
1309 
1310  while( !n.isNull() ) {
1311  QDomElement e = n.toElement(); // try to convert the node to an element.
1312  if (e.tagName() == "Include")
1313  {
1314  QHash<QString,KService::Ptr> items;
1315 
1316  QDomNode n2 = e.firstChild();
1317  while( !n2.isNull() ) {
1318  QDomElement e2 = n2.toElement();
1319  items.clear();
1320  processCondition(e2, items);
1321  if (m_track)
1322  track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Include>");
1323  includeItems(m_currentMenu->items, items);
1324  excludeItems(m_currentMenu->excludeItems, items);
1325  markUsedApplications(items);
1326 
1327  if (m_track)
1328  track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Include>");
1329 
1330  n2 = n2.nextSibling();
1331  }
1332  }
1333 
1334  else if (e.tagName() == "Exclude")
1335  {
1336  QHash<QString,KService::Ptr> items;
1337 
1338  QDomNode n2 = e.firstChild();
1339  while( !n2.isNull() ) {
1340  QDomElement e2 = n2.toElement();
1341  items.clear();
1342  processCondition(e2, items);
1343  if (m_track)
1344  track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Exclude>");
1345  excludeItems(m_currentMenu->items, items);
1346  includeItems(m_currentMenu->excludeItems, items);
1347  if (m_track)
1348  track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Exclude>");
1349  n2 = n2.nextSibling();
1350  }
1351  }
1352 
1353  n = n.nextSibling();
1354  }
1355  }
1356 
1357  n = docElem.firstChild();
1358  while( !n.isNull() ) {
1359  QDomElement e = n.toElement(); // try to convert the node to an element.
1360  if (e.tagName() == "Menu")
1361  {
1362  processMenu(e, pass);
1363  }
1364 // We insert legacy dir in pass 0, this way the order in the .menu-file determines
1365 // which .directory file gets used, but the menu-entries of legacy-menus will always
1366 // have the lowest priority.
1367 // else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
1368  else if (pass == 0)
1369  {
1370  if (e.tagName() == "LegacyDir")
1371  {
1372  // Add legacy nodes to Menu structure
1373  QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
1374  SubMenu *legacyMenu = m_legacyNodes[dir];
1375  if (legacyMenu)
1376  {
1377  mergeMenu(m_currentMenu, legacyMenu);
1378  }
1379  }
1380 
1381  else if (e.tagName() == "KDELegacyDirs")
1382  {
1383  // Add legacy nodes to Menu structure
1384  QString dir = "<KDE>";
1385  SubMenu *legacyMenu = m_legacyNodes[dir];
1386  if (legacyMenu)
1387  {
1388  mergeMenu(m_currentMenu, legacyMenu);
1389  }
1390  }
1391  }
1392  n = n.nextSibling();
1393  }
1394 
1395  if (pass == 2)
1396  {
1397  n = docElem.firstChild();
1398  while( !n.isNull() ) {
1399  QDomElement e = n.toElement(); // try to convert the node to an element.
1400  if (e.tagName() == "Move")
1401  {
1402  QString orig;
1403  QString dest;
1404  QDomNode n2 = e.firstChild();
1405  while( !n2.isNull() ) {
1406  QDomElement e2 = n2.toElement(); // try to convert the node to an element.
1407  if( e2.tagName() == "Old")
1408  orig = e2.text();
1409  if( e2.tagName() == "New")
1410  dest = e2.text();
1411  n2 = n2.nextSibling();
1412  }
1413  kDebug(7021) << "Moving" << orig << "to" << dest;
1414  if (!orig.isEmpty() && !dest.isEmpty())
1415  {
1416  SubMenu *menu = takeSubMenu(m_currentMenu, orig);
1417  if (menu)
1418  {
1419  insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority
1420  }
1421  }
1422  }
1423  n = n.nextSibling();
1424  }
1425 
1426  }
1427 
1428  unloadAppsInfo(); // Update the scope wrt the list of applications
1429 
1430  while (m_directoryDirs.count() > oldDirectoryDirsCount)
1431  m_directoryDirs.pop_front();
1432 
1433  m_currentMenu = parentMenu;
1434 }
1435 
1436 
1437 
1438 static QString parseAttribute( const QDomElement &e)
1439 {
1440  QString option;
1441  if ( e.hasAttribute( "show_empty" ) )
1442  {
1443  QString str = e.attribute( "show_empty" );
1444  if ( str=="true" )
1445  option= "ME ";
1446  else if ( str=="false" )
1447  option= "NME ";
1448  else
1449  kDebug()<<" Error in parsing show_empty attribute :"<<str;
1450  }
1451  if ( e.hasAttribute( "inline" ) )
1452  {
1453  QString str = e.attribute( "inline" );
1454  if ( str=="true" )
1455  option+="I ";
1456  else if ( str=="false" )
1457  option+="NI ";
1458  else
1459  kDebug()<<" Error in parsing inline attribute :"<<str;
1460  }
1461  if ( e.hasAttribute( "inline_limit" ) )
1462  {
1463  bool ok;
1464  int value = e.attribute( "inline_limit" ).toInt(&ok);
1465  if ( ok )
1466  option+=QString( "IL[%1] " ).arg( value );
1467  }
1468  if ( e.hasAttribute( "inline_header" ) )
1469  {
1470  QString str = e.attribute( "inline_header" );
1471  if ( str=="true")
1472  option+="IH ";
1473  else if ( str == "false" )
1474  option+="NIH ";
1475  else
1476  kDebug()<<" Error in parsing of inline_header attribute :"<<str;
1477 
1478  }
1479  if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true")
1480  {
1481  QString str = e.attribute( "inline_alias" );
1482  if ( str=="true" )
1483  option+="IA";
1484  else if ( str=="false" )
1485  option+="NIA";
1486  else
1487  kDebug()<<" Error in parsing inline_alias attribute :"<<str;
1488  }
1489  if( !option.isEmpty())
1490  {
1491  option = option.prepend(":O");
1492  }
1493  return option;
1494 
1495 }
1496 
1497 static QStringList parseLayoutNode(const QDomElement &docElem)
1498 {
1499  QStringList layout;
1500 
1501  QString optionDefaultLayout;
1502  if( docElem.tagName()=="DefaultLayout")
1503  optionDefaultLayout = parseAttribute( docElem);
1504  if ( !optionDefaultLayout.isEmpty() )
1505  layout.append( optionDefaultLayout );
1506 
1507  bool mergeTagExists = false;
1508  QDomNode n = docElem.firstChild();
1509  while( !n.isNull() ) {
1510  QDomElement e = n.toElement(); // try to convert the node to an element.
1511  if (e.tagName() == "Separator")
1512  {
1513  layout.append(":S");
1514  }
1515  else if (e.tagName() == "Filename")
1516  {
1517  layout.append(e.text());
1518  }
1519  else if (e.tagName() == "Menuname")
1520  {
1521  layout.append('/'+e.text());
1522  QString option = parseAttribute( e );
1523  if( !option.isEmpty())
1524  layout.append( option );
1525  }
1526  else if (e.tagName() == "Merge")
1527  {
1528  QString type = e.attributeNode("type").value();
1529  if (type == "files")
1530  layout.append(":F");
1531  else if (type == "menus")
1532  layout.append(":M");
1533  else if (type == "all")
1534  layout.append(":A");
1535  mergeTagExists = true;
1536  }
1537 
1538  n = n.nextSibling();
1539  }
1540 
1541  if ( !mergeTagExists ) {
1542  layout.append(":M");
1543  layout.append(":F");
1544  kWarning() << "The menu spec file contains a Layout or DefaultLayout tag without the mandatory Merge tag inside. Please fix your file.";
1545  }
1546  return layout;
1547 }
1548 
1549 void
1550 VFolderMenu::layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout) //krazy:exclude=passbyvalue
1551 {
1552  if (!menu->defaultLayoutNode.isNull())
1553  {
1554  defaultLayout = parseLayoutNode(menu->defaultLayoutNode);
1555  }
1556 
1557  if (menu->layoutNode.isNull())
1558  {
1559  menu->layoutList = defaultLayout;
1560  }
1561  else
1562  {
1563  menu->layoutList = parseLayoutNode(menu->layoutNode);
1564  if (menu->layoutList.isEmpty())
1565  menu->layoutList = defaultLayout;
1566  }
1567 
1568  foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
1569  {
1570  layoutMenu(subMenu, defaultLayout);
1571  }
1572 }
1573 
1574 void
1575 VFolderMenu::markUsedApplications(const QHash<QString,KService::Ptr>& items)
1576 {
1577  foreach(const KService::Ptr &p, items)
1578  m_usedAppsDict.insert(p->menuId());
1579 }
1580 
1581 VFolderMenu::SubMenu *
1582 VFolderMenu::parseMenu(const QString &file, bool forceLegacyLoad)
1583 {
1584  m_forcedLegacyLoad = false;
1585  m_legacyLoaded = false;
1586  m_appsInfo = 0;
1587 
1588  const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu");
1589  for(QStringList::ConstIterator it=dirs.begin();
1590  it != dirs.end(); ++it)
1591  {
1592  registerDirectory(*it);
1593  }
1594 
1595  loadMenu(file);
1596 
1597  delete m_rootMenu;
1598  m_rootMenu = m_currentMenu = 0;
1599 
1600  QDomElement docElem = m_doc.documentElement();
1601 
1602  for (int pass = 0; pass <= 2; pass++)
1603  {
1604  // pass 0: load application desktop files
1605  // pass 1: the normal processing
1606  // pass 2: process <OnlyUnallocated> to put unused files into "Lost & Found".
1607  processMenu(docElem, pass);
1608 
1609  switch (pass) {
1610  case 0:
1611  // Fill the dictCategories for each AppsInfo in m_appsInfoList,
1612  // in preparation for processMenu pass 1.
1613  buildApplicationIndex(false);
1614  break;
1615  case 1:
1616  // Fill the dictCategories for each AppsInfo in m_appsInfoList,
1617  // with only the unused apps, in preparation for processMenu pass 2.
1618  buildApplicationIndex(true /* unusedOnly */);
1619  break;
1620  case 2:
1621  {
1622  QStringList defaultLayout;
1623  defaultLayout << ":M"; // Sub-Menus
1624  defaultLayout << ":F"; // Individual entries
1625  layoutMenu(m_rootMenu, defaultLayout);
1626  break;
1627  }
1628  default:
1629  break;
1630  }
1631  }
1632 
1633  if (!m_legacyLoaded && forceLegacyLoad)
1634  {
1635  m_forcedLegacyLoad = true;
1636  processKDELegacyDirs();
1637  }
1638 
1639  return m_rootMenu;
1640 }
1641 
1642 void
1643 VFolderMenu::setTrackId(const QString &id)
1644 {
1645  m_track = !id.isEmpty();
1646  m_trackId = id;
1647 }
1648 
1649 #include "vfolder_menu.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Tue Jul 16 2013 11:50:39 by doxygen 1.8.1.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDED

Skip menu "KDED"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.10.5 API Reference

Skip menu "kdelibs-4.10.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal