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

KDED

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

KDE's Doxygen guidelines are available online.

KDED

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

kdelibs API Reference

Skip menu "kdelibs 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

Search



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