00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "vfolder_menu.h"
00020 #include "kbuildservicefactory.h"
00021
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <unistd.h>
00025 #include <dirent.h>
00026 #include <config.h>
00027
00028 #include <kdebug.h>
00029 #include <kglobal.h>
00030 #include <kstandarddirs.h>
00031 #include <kservice.h>
00032 #include <kde_file.h>
00033
00034 #include <QtCore/QMap>
00035 #include <QtCore/QFile>
00036 #include <QtCore/QDir>
00037 #include <QtCore/QRegExp>
00038
00039 static void foldNode(QDomElement &docElem, QDomElement &e, QMap<QString,QDomElement> &dupeList, QString s=QString())
00040 {
00041 if (s.isEmpty())
00042 s = e.text();
00043 QMap<QString,QDomElement>::iterator it = dupeList.find(s);
00044 if (it != dupeList.end())
00045 {
00046 kDebug(7021) << e.tagName() << "and" << s << "requires combining!";
00047
00048 docElem.removeChild(*it);
00049 dupeList.erase(it);
00050 }
00051 dupeList.insert(s, e);
00052 }
00053
00054 static void replaceNode(QDomElement &docElem, QDomNode &n, const QStringList &list, const QString &tag)
00055 {
00056 for(QStringList::ConstIterator it = list.begin();
00057 it != list.end(); ++it)
00058 {
00059 QDomElement e = docElem.ownerDocument().createElement(tag);
00060 QDomText txt = docElem.ownerDocument().createTextNode(*it);
00061 e.appendChild(txt);
00062 docElem.insertAfter(e, n);
00063 }
00064
00065 QDomNode next = n.nextSibling();
00066 docElem.removeChild(n);
00067 n = next;
00068
00069 }
00070
00071 void VFolderMenu::registerFile(const QString &file)
00072 {
00073 int i = file.lastIndexOf('/');
00074 if (i < 0)
00075 return;
00076
00077 QString dir = file.left(i+1);
00078 registerDirectory(dir);
00079 }
00080
00081 void VFolderMenu::registerDirectory(const QString &directory)
00082 {
00083 m_allDirectories.append(directory);
00084 }
00085
00086 QStringList VFolderMenu::allDirectories()
00087 {
00088 if (m_allDirectories.isEmpty())
00089 return m_allDirectories;
00090 m_allDirectories.sort();
00091
00092 QStringList::Iterator it = m_allDirectories.begin();
00093 QString previous = *it++;
00094 for(;it != m_allDirectories.end();)
00095 {
00096 if ((*it).startsWith(previous))
00097 {
00098 it = m_allDirectories.erase(it);
00099 }
00100 else
00101 {
00102 previous = *it;
00103 ++it;
00104 }
00105 }
00106 return m_allDirectories;
00107 }
00108
00109 static void
00110 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)
00111 {
00112 if (itemList.contains(menuId))
00113 printf("%s: %s INCL %d EXCL %d\n", qPrintable(menuName), qPrintable(comment), includeList.contains(menuId) ? 1 : 0, excludeList.contains(menuId) ? 1 : 0);
00114 }
00115
00116 void
00117 VFolderMenu::includeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00118 {
00119 foreach (const KService::Ptr &p, items2) {
00120 items1.insert(p->menuId(), p);
00121 }
00122 }
00123
00124 void
00125 VFolderMenu::matchItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00126 {
00127 foreach (const KService::Ptr &p, items1)
00128 {
00129 QString id = p->menuId();
00130 if (!items2.contains(id))
00131 items1.remove(id);
00132 }
00133 }
00134
00135 void
00136 VFolderMenu::excludeItems(QHash<QString,KService::Ptr>& items1, const QHash<QString,KService::Ptr>& items2)
00137 {
00138 foreach (const KService::Ptr &p, items2)
00139 items1.remove(p->menuId());
00140 }
00141
00142 VFolderMenu::SubMenu*
00143 VFolderMenu::takeSubMenu(SubMenu *parentMenu, const QString &menuName)
00144 {
00145 const int i = menuName.indexOf('/');
00146 const QString s1 = i > 0 ? menuName.left(i) : menuName;
00147 const QString s2 = menuName.mid(i+1);
00148
00149
00150 for (QList<SubMenu*>::Iterator it = parentMenu->subMenus.begin(); it != parentMenu->subMenus.end(); ++it)
00151 {
00152 SubMenu* menu = *it;
00153 if (menu->name == s1)
00154 {
00155 if (i == -1)
00156 {
00157
00158 parentMenu->subMenus.erase(it);
00159 return menu;
00160 }
00161 else
00162 {
00163 return takeSubMenu(menu, s2);
00164 }
00165 }
00166 }
00167 return 0;
00168 }
00169
00170 void
00171 VFolderMenu::mergeMenu(SubMenu *menu1, SubMenu *menu2, bool reversePriority)
00172 {
00173 if (m_track)
00174 {
00175 track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("Before MenuMerge w. %1 (incl)").arg(menu2->name));
00176 track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("Before MenuMerge w. %1 (excl)").arg(menu2->name));
00177 }
00178 if (reversePriority)
00179 {
00180
00181 excludeItems(menu2->items, menu1->excludeItems);
00182 includeItems(menu1->items, menu2->items);
00183 excludeItems(menu2->excludeItems, menu1->items);
00184 includeItems(menu1->excludeItems, menu2->excludeItems);
00185 }
00186 else
00187 {
00188
00189 excludeItems(menu1->items, menu2->excludeItems);
00190 includeItems(menu1->items, menu2->items);
00191 includeItems(menu1->excludeItems, menu2->excludeItems);
00192 menu1->isDeleted = menu2->isDeleted;
00193 }
00194 while (!menu2->subMenus.isEmpty())
00195 {
00196 SubMenu *subMenu = menu2->subMenus.takeFirst();
00197 insertSubMenu(menu1, subMenu->name, subMenu, reversePriority);
00198 }
00199
00200 if (reversePriority)
00201 {
00202
00203 if (menu1->directoryFile.isEmpty())
00204 menu1->directoryFile = menu2->directoryFile;
00205 if (menu1->defaultLayoutNode.isNull())
00206 menu1->defaultLayoutNode = menu2->defaultLayoutNode;
00207 if (menu1->layoutNode.isNull())
00208 menu1->layoutNode = menu2->layoutNode;
00209 }
00210 else
00211 {
00212
00213 if (!menu2->directoryFile.isEmpty())
00214 menu1->directoryFile = menu2->directoryFile;
00215 if (!menu2->defaultLayoutNode.isNull())
00216 menu1->defaultLayoutNode = menu2->defaultLayoutNode;
00217 if (!menu2->layoutNode.isNull())
00218 menu1->layoutNode = menu2->layoutNode;
00219 }
00220
00221 if (m_track)
00222 {
00223 track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->items, QString("After MenuMerge w. %1 (incl)").arg(menu2->name));
00224 track(m_trackId, menu1->name, menu1->items, menu1->excludeItems, menu2->excludeItems, QString("After MenuMerge w. %1 (excl)").arg(menu2->name));
00225 }
00226
00227 delete menu2;
00228 }
00229
00230 void
00231 VFolderMenu::insertSubMenu(SubMenu *parentMenu, const QString &menuName, SubMenu *newMenu, bool reversePriority)
00232 {
00233 const int i = menuName.indexOf('/');
00234 const QString s1 = menuName.left(i);
00235 const QString s2 = menuName.mid(i+1);
00236
00237
00238 foreach (SubMenu *menu, parentMenu->subMenus)
00239 {
00240 if (menu->name == s1)
00241 {
00242 if (i == -1)
00243 {
00244 mergeMenu(menu, newMenu, reversePriority);
00245 return;
00246 }
00247 else
00248 {
00249 insertSubMenu(menu, s2, newMenu, reversePriority);
00250 return;
00251 }
00252 }
00253 }
00254 if (i == -1)
00255 {
00256
00257 newMenu->name = menuName;
00258 parentMenu->subMenus.append(newMenu);
00259 }
00260 else
00261 {
00262 SubMenu *menu = new SubMenu;
00263 menu->name = s1;
00264 parentMenu->subMenus.append(menu);
00265 insertSubMenu(menu, s2, newMenu);
00266 }
00267 }
00268
00269 void
00270 VFolderMenu::insertService(SubMenu *parentMenu, const QString &name, KService::Ptr newService)
00271 {
00272 const int i = name.indexOf('/');
00273
00274 if (i == -1)
00275 {
00276
00277 parentMenu->items.insert(newService->menuId(), newService);
00278 return;
00279 }
00280
00281 QString s1 = name.left(i);
00282 QString s2 = name.mid(i+1);
00283
00284
00285 foreach (SubMenu *menu, parentMenu->subMenus)
00286 {
00287 if (menu->name == s1)
00288 {
00289 insertService(menu, s2, newService);
00290 return;
00291 }
00292 }
00293
00294 SubMenu *menu = new SubMenu;
00295 menu->name = s1;
00296 parentMenu->subMenus.append(menu);
00297 insertService(menu, s2, newService);
00298 }
00299
00300
00301 VFolderMenu::VFolderMenu(KBuildServiceFactory* serviceFactory) :
00302 m_track(false),
00303 m_serviceFactory(serviceFactory)
00304 {
00305 m_usedAppsDict.reserve(797);
00306 m_rootMenu = 0;
00307 initDirs();
00308 }
00309
00310 VFolderMenu::~VFolderMenu()
00311 {
00312 delete m_rootMenu;
00313 delete m_appsInfo;
00314 }
00315
00316 #define FOR_ALL_APPLICATIONS(it) \
00317 foreach (AppsInfo *info, m_appsInfoStack) \
00318 { \
00319 QHashIterator<QString,KService::Ptr> it = info->applications; \
00320 while (it.hasNext()) \
00321 { \
00322 it.next();
00323 #define FOR_ALL_APPLICATIONS_END } }
00324
00325 #define FOR_CATEGORY(category, it) \
00326 foreach (AppsInfo *info, m_appsInfoStack) \
00327 { \
00328 const KService::List list = info->dictCategories.value(category); \
00329 for(KService::List::ConstIterator it = list.constBegin(); \
00330 it != list.constEnd(); ++it) \
00331 {
00332 #define FOR_CATEGORY_END } }
00333
00334 KService::Ptr
00335 VFolderMenu::findApplication(const QString &relPath)
00336 {
00337 foreach(AppsInfo *info, m_appsInfoStack)
00338 {
00339 if (info->applications.contains(relPath)) {
00340 KService::Ptr s = info->applications[relPath];
00341 if (s)
00342 return s;
00343 }
00344 }
00345 return KService::Ptr();
00346 }
00347
00348 void
00349 VFolderMenu::addApplication(const QString &id, KService::Ptr service)
00350 {
00351 service->setMenuId(id);
00352 m_appsInfo->applications.insert(id, service);
00353 m_serviceFactory->addEntry(KSycocaEntry::Ptr::staticCast(service));
00354 }
00355
00356 void
00357 VFolderMenu::buildApplicationIndex(bool unusedOnly)
00358 {
00359 foreach (AppsInfo *info, m_appsInfoList)
00360 {
00361 info->dictCategories.clear();
00362 QMutableHashIterator<QString,KService::Ptr> it = info->applications;
00363 while (it.hasNext())
00364 {
00365 KService::Ptr s = it.next().value();
00366 if (unusedOnly && m_usedAppsDict.contains(s->menuId()))
00367 {
00368
00369 it.remove();
00370 continue;
00371 }
00372
00373 const QStringList cats = s->categories();
00374 for(QStringList::ConstIterator it2 = cats.begin();
00375 it2 != cats.end(); ++it2)
00376 {
00377 const QString &cat = *it2;
00378 info->dictCategories[cat].append(s);
00379 }
00380 }
00381 }
00382 }
00383
00384 void
00385 VFolderMenu::createAppsInfo()
00386 {
00387 if (m_appsInfo) return;
00388
00389 m_appsInfo = new AppsInfo;
00390 m_appsInfoStack.prepend(m_appsInfo);
00391 m_appsInfoList.append(m_appsInfo);
00392 m_currentMenu->apps_info = m_appsInfo;
00393 }
00394
00395 void
00396 VFolderMenu::loadAppsInfo()
00397 {
00398 m_appsInfo = m_currentMenu->apps_info;
00399 if (!m_appsInfo)
00400 return;
00401
00402 if (m_appsInfoStack.count() && m_appsInfoStack.first() == m_appsInfo)
00403 return;
00404
00405 m_appsInfoStack.prepend(m_appsInfo);
00406 }
00407
00408 void
00409 VFolderMenu::unloadAppsInfo()
00410 {
00411 m_appsInfo = m_currentMenu->apps_info;
00412 if (!m_appsInfo)
00413 return;
00414
00415 if (m_appsInfoStack.first() != m_appsInfo)
00416 {
00417 return;
00418 }
00419
00420 m_appsInfoStack.removeAll(m_appsInfo);
00421 m_appsInfo = 0;
00422 }
00423
00424 QString
00425 VFolderMenu::absoluteDir(const QString &_dir, const QString &baseDir, bool keepRelativeToCfg)
00426 {
00427 QString dir = _dir;
00428 if (QDir::isRelativePath(dir))
00429 {
00430 dir = baseDir + dir;
00431 }
00432 if (!dir.endsWith('/'))
00433 dir += '/';
00434
00435 if (QDir::isRelativePath(dir) && !keepRelativeToCfg)
00436 {
00437 dir = KGlobal::dirs()->findResource("xdgconf-menu", dir);
00438 }
00439
00440 dir = KGlobal::dirs()->realPath(dir);
00441
00442 return dir;
00443 }
00444
00445 static void tagBaseDir(QDomDocument &doc, const QString &tag, const QString &dir)
00446 {
00447 QDomNodeList mergeFileList = doc.elementsByTagName(tag);
00448 for(int i = 0; i < (int)mergeFileList.count(); i++)
00449 {
00450 QDomAttr attr = doc.createAttribute("__BaseDir");
00451 attr.setValue(dir);
00452 mergeFileList.item(i).toElement().setAttributeNode(attr);
00453 }
00454 }
00455
00456 static void tagBasePath(QDomDocument &doc, const QString &tag, const QString &path)
00457 {
00458 QDomNodeList mergeFileList = doc.elementsByTagName(tag);
00459 for(int i = 0; i < (int)mergeFileList.count(); i++)
00460 {
00461 QDomAttr attr = doc.createAttribute("__BasePath");
00462 attr.setValue(path);
00463 mergeFileList.item(i).toElement().setAttributeNode(attr);
00464 }
00465 }
00466
00467 QDomDocument
00468 VFolderMenu::loadDoc()
00469 {
00470 QDomDocument doc;
00471 if ( m_docInfo.path.isEmpty() )
00472 {
00473 return doc;
00474 }
00475 QFile file( m_docInfo.path );
00476 if ( !file.open( QIODevice::ReadOnly ) )
00477 {
00478 kWarning(7021) << "Could not open " << m_docInfo.path;
00479 return doc;
00480 }
00481 QString errorMsg;
00482 int errorRow;
00483 int errorCol;
00484 if ( !doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) {
00485 kWarning(7021) << "Parse error in " << m_docInfo.path << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg;
00486 file.close();
00487 return doc;
00488 }
00489 file.close();
00490
00491 tagBaseDir(doc, "MergeFile", m_docInfo.baseDir);
00492 tagBasePath(doc, "MergeFile", m_docInfo.path);
00493 tagBaseDir(doc, "MergeDir", m_docInfo.baseDir);
00494 tagBaseDir(doc, "DirectoryDir", m_docInfo.baseDir);
00495 tagBaseDir(doc, "AppDir", m_docInfo.baseDir);
00496 tagBaseDir(doc, "LegacyDir", m_docInfo.baseDir);
00497
00498 return doc;
00499 }
00500
00501
00502 void
00503 VFolderMenu::mergeFile(QDomElement &parent, const QDomNode &mergeHere)
00504 {
00505 kDebug(7021) << "VFolderMenu::mergeFile:" << m_docInfo.path;
00506 QDomDocument doc = loadDoc();
00507
00508 QDomElement docElem = doc.documentElement();
00509 QDomNode n = docElem.firstChild();
00510 QDomNode last = mergeHere;
00511 while( !n.isNull() )
00512 {
00513 QDomElement e = n.toElement();
00514 QDomNode next = n.nextSibling();
00515
00516 if (e.isNull())
00517 {
00518
00519 }
00520
00521 else if (e.tagName() != "Name")
00522 {
00523 parent.insertAfter(n, last);
00524 last = n;
00525 }
00526
00527 docElem.removeChild(n);
00528 n = next;
00529 }
00530 }
00531
00532
00533 void
00534 VFolderMenu::mergeMenus(QDomElement &docElem, QString &name)
00535 {
00536 QMap<QString,QDomElement> menuNodes;
00537 QMap<QString,QDomElement> directoryNodes;
00538 QMap<QString,QDomElement> appDirNodes;
00539 QMap<QString,QDomElement> directoryDirNodes;
00540 QMap<QString,QDomElement> legacyDirNodes;
00541 QDomElement defaultLayoutNode;
00542 QDomElement layoutNode;
00543
00544 QDomNode n = docElem.firstChild();
00545 while( !n.isNull() ) {
00546 QDomElement e = n.toElement();
00547 if( e.isNull() ) {
00548
00549 }
00550 else if( e.tagName() == "DefaultAppDirs") {
00551
00552 replaceNode(docElem, n, m_defaultAppDirs, "AppDir");
00553 continue;
00554 }
00555 else if( e.tagName() == "DefaultDirectoryDirs") {
00556
00557 replaceNode(docElem, n, m_defaultDirectoryDirs, "DirectoryDir");
00558 continue;
00559 }
00560 else if( e.tagName() == "DefaultMergeDirs") {
00561
00562 replaceNode(docElem, n, m_defaultMergeDirs, "MergeDir");
00563 continue;
00564 }
00565 else if( e.tagName() == "AppDir") {
00566
00567 foldNode(docElem, e, appDirNodes);
00568 }
00569 else if( e.tagName() == "DirectoryDir") {
00570
00571 foldNode(docElem, e, directoryDirNodes);
00572 }
00573 else if( e.tagName() == "LegacyDir") {
00574
00575 foldNode(docElem, e, legacyDirNodes);
00576 }
00577 else if( e.tagName() == "Directory") {
00578
00579 foldNode(docElem, e, directoryNodes);
00580 }
00581 else if( e.tagName() == "Move") {
00582
00583 QString orig;
00584 QDomNode n2 = e.firstChild();
00585 while( !n2.isNull() ) {
00586 QDomElement e2 = n2.toElement();
00587 if( e2.tagName() == "Old")
00588 {
00589 orig = e2.text();
00590 break;
00591 }
00592 n2 = n2.nextSibling();
00593 }
00594 foldNode(docElem, e, appDirNodes, orig);
00595 }
00596 else if( e.tagName() == "Menu") {
00597 QString name;
00598 mergeMenus(e, name);
00599 QMap<QString,QDomElement>::iterator it = menuNodes.find(name);
00600 if (it != menuNodes.end())
00601 {
00602 QDomElement docElem2 = *it;
00603 QDomNode n2 = docElem2.firstChild();
00604 QDomNode first = e.firstChild();
00605 while( !n2.isNull() ) {
00606 QDomElement e2 = n2.toElement();
00607 QDomNode n3 = n2.nextSibling();
00608 e.insertBefore(n2, first);
00609 docElem2.removeChild(n2);
00610 n2 = n3;
00611 }
00612
00613
00614
00615 docElem.removeChild(docElem2);
00616 menuNodes.erase(it);
00617 }
00618 menuNodes.insert(name, e);
00619 }
00620 else if( e.tagName() == "MergeFile") {
00621 if ((e.attribute("type") == "parent"))
00622 pushDocInfoParent(e.attribute("__BasePath"), e.attribute("__BaseDir"));
00623 else
00624 pushDocInfo(e.text(), e.attribute("__BaseDir"));
00625
00626 if (!m_docInfo.path.isEmpty())
00627 mergeFile(docElem, n);
00628 popDocInfo();
00629
00630 QDomNode last = n;
00631 n = n.nextSibling();
00632 docElem.removeChild(last);
00633 continue;
00634 }
00635 else if( e.tagName() == "MergeDir") {
00636 QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"), true);
00637
00638 const QStringList dirs = KGlobal::dirs()->findDirs("xdgconf-menu", dir);
00639 for(QStringList::ConstIterator it=dirs.begin();
00640 it != dirs.end(); ++it)
00641 {
00642 registerDirectory(*it);
00643 }
00644
00645 QStringList fileList;
00646 if (!QDir::isRelativePath(dir))
00647 {
00648
00649 fileList = KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu");
00650 }
00651 else
00652 {
00653
00654 (void) KGlobal::dirs()->findAllResources("xdgconf-menu", dir+"*.menu",
00655 KStandardDirs::NoDuplicates, fileList);
00656 }
00657
00658 for(QStringList::ConstIterator it=fileList.constBegin();
00659 it != fileList.constEnd(); ++it)
00660 {
00661 pushDocInfo(*it);
00662 mergeFile(docElem, n);
00663 popDocInfo();
00664 }
00665
00666 QDomNode last = n;
00667 n = n.nextSibling();
00668 docElem.removeChild(last);
00669
00670 continue;
00671 }
00672 else if( e.tagName() == "Name") {
00673 name = e.text();
00674 }
00675 else if( e.tagName() == "DefaultLayout") {
00676 if (!defaultLayoutNode.isNull())
00677 docElem.removeChild(defaultLayoutNode);
00678 defaultLayoutNode = e;
00679 }
00680 else if( e.tagName() == "Layout") {
00681 if (!layoutNode.isNull())
00682 docElem.removeChild(layoutNode);
00683 layoutNode = e;
00684 }
00685 n = n.nextSibling();
00686 }
00687 }
00688
00689 void
00690 VFolderMenu::pushDocInfo(const QString &fileName, const QString &baseDir)
00691 {
00692 m_docInfoStack.push(m_docInfo);
00693 if (!baseDir.isEmpty())
00694 {
00695 if (!QDir::isRelativePath(baseDir))
00696 m_docInfo.baseDir = KGlobal::dirs()->relativeLocation("xdgconf-menu", baseDir);
00697 else
00698 m_docInfo.baseDir = baseDir;
00699 }
00700
00701 QString baseName = fileName;
00702 if (!QDir::isRelativePath(baseName))
00703 registerFile(baseName);
00704 else
00705 baseName = m_docInfo.baseDir + baseName;
00706
00707 m_docInfo.path = locateMenuFile(fileName);
00708 if (m_docInfo.path.isEmpty())
00709 {
00710 m_docInfo.baseDir.clear();
00711 m_docInfo.baseName.clear();
00712 kDebug(7021) << "Menu" << fileName << "not found.";
00713 return;
00714 }
00715 int i;
00716 i = baseName.lastIndexOf('/');
00717 if (i > 0)
00718 {
00719 m_docInfo.baseDir = baseName.left(i+1);
00720 m_docInfo.baseName = baseName.mid(i+1, baseName.length() - i - 6);
00721 }
00722 else
00723 {
00724 m_docInfo.baseDir.clear();
00725 m_docInfo.baseName = baseName.left( baseName.length() - 5 );
00726 }
00727 }
00728
00729 void
00730 VFolderMenu::pushDocInfoParent(const QString &basePath, const QString &baseDir)
00731 {
00732 m_docInfoStack.push(m_docInfo);
00733
00734 m_docInfo.baseDir = baseDir;
00735
00736 QString fileName = basePath.mid(basePath.lastIndexOf('/')+1);
00737 m_docInfo.baseName = fileName.left( fileName.length() - 5 );
00738 QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
00739
00740 QStringList result = KGlobal::dirs()->findAllResources("xdgconf-menu", baseName);
00741
00742 while( !result.isEmpty() && (result[0] != basePath))
00743 result.erase(result.begin());
00744
00745 if (result.count() <= 1)
00746 {
00747 m_docInfo.path.clear();
00748 return;
00749 }
00750 m_docInfo.path = result[1];
00751 }
00752
00753 void
00754 VFolderMenu::popDocInfo()
00755 {
00756 m_docInfo = m_docInfoStack.pop();
00757 }
00758
00759 QString
00760 VFolderMenu::locateMenuFile(const QString &fileName)
00761 {
00762 if (!QDir::isRelativePath(fileName))
00763 {
00764 if (KStandardDirs::exists(fileName))
00765 return fileName;
00766 return QString();
00767 }
00768
00769 QString result;
00770
00771 QString xdgMenuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX"));
00772 if (!xdgMenuPrefix.isEmpty())
00773 {
00774 QFileInfo fileInfo(fileName);
00775
00776 QString fileNameOnly = fileInfo.fileName();
00777 if (!fileNameOnly.startsWith(xdgMenuPrefix))
00778 fileNameOnly = xdgMenuPrefix + fileNameOnly;
00779
00780 QString baseName = QDir::cleanPath(m_docInfo.baseDir +
00781 fileInfo.path() + '/' + fileNameOnly);
00782 result = KStandardDirs::locate("xdgconf-menu", baseName);
00783 }
00784
00785 if (result.isEmpty())
00786 {
00787 QString baseName = QDir::cleanPath(m_docInfo.baseDir + fileName);
00788 result = KStandardDirs::locate("xdgconf-menu", baseName);
00789 }
00790
00791 return result;
00792 }
00793
00794 QString
00795 VFolderMenu::locateDirectoryFile(const QString &fileName)
00796 {
00797 if (fileName.isEmpty())
00798 return QString();
00799
00800 if (!QDir::isRelativePath(fileName))
00801 {
00802 if (KStandardDirs::exists(fileName))
00803 return fileName;
00804 return QString();
00805 }
00806
00807
00808 for(QStringList::ConstIterator it = m_directoryDirs.constBegin();
00809 it != m_directoryDirs.constEnd();
00810 ++it)
00811 {
00812 QString tmp = (*it)+fileName;
00813 if (KStandardDirs::exists(tmp))
00814 return tmp;
00815 }
00816
00817 return QString();
00818 }
00819
00820 void
00821 VFolderMenu::initDirs()
00822 {
00823 m_defaultDataDirs = KGlobal::dirs()->kfsstnd_prefixes().split(':', QString::SkipEmptyParts);
00824 const QString localDir = m_defaultDataDirs.first();
00825 m_defaultDataDirs.removeAll(localDir);
00826
00827 m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString());
00828 m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString());
00829 m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps");
00830 }
00831
00832 void
00833 VFolderMenu::loadMenu(const QString &fileName)
00834 {
00835 m_defaultMergeDirs.clear();
00836
00837 if (!fileName.endsWith(".menu"))
00838 return;
00839
00840 pushDocInfo(fileName);
00841 m_defaultMergeDirs << m_docInfo.baseName+"-merged/";
00842 m_doc = loadDoc();
00843 popDocInfo();
00844
00845 if (m_doc.isNull())
00846 {
00847 if (m_docInfo.path.isEmpty())
00848 kError(7021) << fileName << " not found in " << m_allDirectories << endl;
00849 else
00850 kWarning(7021) << "Load error (" << m_docInfo.path << ")";
00851 return;
00852 }
00853
00854 QDomElement e = m_doc.documentElement();
00855 QString name;
00856 mergeMenus(e, name);
00857 }
00858
00859 void
00860 VFolderMenu::processCondition(QDomElement &domElem, QHash<QString,KService::Ptr>& items)
00861 {
00862 if (domElem.tagName() == "And")
00863 {
00864 QDomNode n = domElem.firstChild();
00865
00866 while (!n.isNull())
00867 {
00868 QDomElement e = n.toElement();
00869 n = n.nextSibling();
00870 if ( !e.isNull() ) {
00871 processCondition(e, items);
00872 break;
00873 }
00874 }
00875
00876 QHash<QString,KService::Ptr> andItems;
00877 while( !n.isNull() ) {
00878 QDomElement e = n.toElement();
00879 if (e.tagName() == "Not")
00880 {
00881
00882 QDomNode n2 = e.firstChild();
00883 while( !n2.isNull() ) {
00884 QDomElement e2 = n2.toElement();
00885 andItems.clear();
00886 processCondition(e2, andItems);
00887 excludeItems(items, andItems);
00888 n2 = n2.nextSibling();
00889 }
00890 }
00891 else
00892 {
00893 andItems.clear();
00894 processCondition(e, andItems);
00895 matchItems(items, andItems);
00896 }
00897 n = n.nextSibling();
00898 }
00899 }
00900 else if (domElem.tagName() == "Or")
00901 {
00902 QDomNode n = domElem.firstChild();
00903
00904 while (!n.isNull())
00905 {
00906 QDomElement e = n.toElement();
00907 n = n.nextSibling();
00908 if ( !e.isNull() ) {
00909 processCondition(e, items);
00910 break;
00911 }
00912 }
00913
00914 QHash<QString,KService::Ptr> orItems;
00915 while( !n.isNull() ) {
00916 QDomElement e = n.toElement();
00917 if ( !e.isNull() ) {
00918 orItems.clear();
00919 processCondition(e, orItems);
00920 includeItems(items, orItems);
00921 }
00922 n = n.nextSibling();
00923 }
00924 }
00925 else if (domElem.tagName() == "Not")
00926 {
00927 FOR_ALL_APPLICATIONS(it)
00928 {
00929 KService::Ptr s = it.value();
00930 items.insert(s->menuId(), s);
00931 }
00932 FOR_ALL_APPLICATIONS_END
00933
00934 QHash<QString,KService::Ptr> notItems;
00935 QDomNode n = domElem.firstChild();
00936 while( !n.isNull() ) {
00937 QDomElement e = n.toElement();
00938 if ( !e.isNull() ) {
00939 notItems.clear();
00940 processCondition(e, notItems);
00941 excludeItems(items, notItems);
00942 }
00943 n = n.nextSibling();
00944 }
00945 }
00946 else if (domElem.tagName() == "Category")
00947 {
00948 FOR_CATEGORY(domElem.text(), it)
00949 {
00950 KService::Ptr s = *it;
00951 items.insert(s->menuId(), s);
00952 }
00953 FOR_CATEGORY_END
00954 }
00955 else if (domElem.tagName() == "All")
00956 {
00957 FOR_ALL_APPLICATIONS(it)
00958 {
00959 KService::Ptr s = it.value();
00960 items.insert(s->menuId(), s);
00961 }
00962 FOR_ALL_APPLICATIONS_END
00963 }
00964 else if (domElem.tagName() == "Filename")
00965 {
00966 QString filename = domElem.text();
00967 kDebug(7021) << "Adding file" << filename;
00968 KService::Ptr s = findApplication(filename);
00969 if (s)
00970 items.insert(filename, s);
00971 }
00972 }
00973
00974 void
00975 VFolderMenu::loadApplications(const QString &dir, const QString &prefix)
00976 {
00977 kDebug(7021) << "Looking up applications under" << dir;
00978
00979
00980 DIR *dp = opendir( QFile::encodeName(dir));
00981 if (!dp)
00982 return;
00983
00984 KDE_struct_dirent *ep;
00985
00986 while( ( ep = KDE_readdir( dp ) ) != 0L )
00987 {
00988 QString fn( QFile::decodeName(ep->d_name));
00989 if (fn == "." || fn == ".." || fn.at(fn.length() - 1) == '~')
00990 continue;
00991
00992 bool isDir;
00993 bool isReg;
00994 QString pathfn = dir + fn;
00995
00996 #ifdef HAVE_DIRENT_D_TYPE
00997 isDir = ep->d_type == DT_DIR;
00998 isReg = ep->d_type == DT_REG;
00999
01000 if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
01001 #endif
01002 {
01003 KDE_struct_stat buff;
01004 if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
01005 continue;
01006 }
01007 isDir = S_ISDIR ( buff.st_mode );
01008 isReg = S_ISREG ( buff.st_mode );
01009 }
01010 if (isDir) {
01011 loadApplications(pathfn + '/', prefix + fn + '-');
01012 continue;
01013 }
01014
01015 if (isReg)
01016 {
01017 if (!fn.endsWith(".desktop"))
01018 continue;
01019
01020 KService::Ptr service;
01021 emit newService(pathfn, &service);
01022 if (service)
01023 addApplication(prefix+fn, service);
01024 }
01025 }
01026 closedir( dp );
01027 }
01028
01029 void
01030 VFolderMenu::processKDELegacyDirs()
01031 {
01032 kDebug(7021) << "processKDELegacyDirs()";
01033
01034 QHash<QString,KService::Ptr> items;
01035 QString prefix = "kde4-";
01036
01037 QStringList relFiles;
01038
01039 (void) KGlobal::dirs()->findAllResources( "apps",
01040 QString(),
01041 KStandardDirs::Recursive |
01042 KStandardDirs::NoDuplicates,
01043 relFiles);
01044 for(QStringList::ConstIterator it = relFiles.constBegin();
01045 it != relFiles.constEnd(); ++it)
01046 {
01047 if (!m_forcedLegacyLoad && (*it).endsWith(".directory"))
01048 {
01049 QString name = *it;
01050 if (!name.endsWith("/.directory"))
01051 continue;
01052
01053 name = name.left(name.length()-11);
01054
01055 SubMenu *newMenu = new SubMenu;
01056 newMenu->directoryFile = KStandardDirs::locate("apps", *it);
01057
01058 insertSubMenu(m_currentMenu, name, newMenu);
01059 continue;
01060 }
01061
01062 if ((*it).endsWith(".desktop"))
01063 {
01064 QString name = *it;
01065 KService::Ptr service;
01066 emit newService(name, &service);
01067
01068 if (service && !m_forcedLegacyLoad)
01069 {
01070 QString id = name;
01071
01072 int i = id.lastIndexOf('/');
01073 if (i >= 0)
01074 id = id.mid(i+1);
01075
01076 id.prepend(prefix);
01077
01078
01079 addApplication(id, service);
01080 items.insert(service->menuId(), service);
01081 if (service->categories().isEmpty())
01082 insertService(m_currentMenu, name, service);
01083
01084 }
01085 }
01086 }
01087 markUsedApplications(items);
01088 m_legacyLoaded = true;
01089 }
01090
01091 void
01092 VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix)
01093 {
01094 kDebug(7021).nospace() << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")";
01095
01096 QHash<QString,KService::Ptr> items;
01097
01098 DIR *dp = opendir( QFile::encodeName(dir));
01099 if (!dp)
01100 return;
01101
01102 KDE_struct_dirent *ep;
01103
01104 while( ( ep = KDE_readdir( dp ) ) != 0L )
01105 {
01106 QString fn( QFile::decodeName(ep->d_name));
01107 if (fn == "." || fn == ".." || fn.at(fn.length() - 1) == '~')
01108 continue;
01109
01110 bool isDir;
01111 bool isReg;
01112 QString pathfn = dir + fn;
01113
01114 #ifdef HAVE_DIRENT_D_TYPE
01115 isDir = ep->d_type == DT_DIR;
01116 isReg = ep->d_type == DT_REG;
01117
01118 if (ep->d_type == DT_UNKNOWN || ep->d_type == DT_LNK)
01119 #endif
01120 {
01121 KDE_struct_stat buff;
01122 if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
01123 continue;
01124 }
01125 isDir = S_ISDIR ( buff.st_mode );
01126 isReg = S_ISREG ( buff.st_mode );
01127 }
01128 if ( isDir ) {
01129 SubMenu *parentMenu = m_currentMenu;
01130
01131 m_currentMenu = new SubMenu;
01132 m_currentMenu->name = fn;
01133 m_currentMenu->directoryFile = dir + fn + "/.directory";
01134
01135 parentMenu->subMenus.append(m_currentMenu);
01136
01137 processLegacyDir(pathfn + '/', relDir+fn+'/', prefix);
01138 m_currentMenu = parentMenu;
01139 continue;
01140 }
01141
01142 if ( isReg )
01143 {
01144 if (!fn.endsWith(".desktop"))
01145 continue;
01146
01147 KService::Ptr service;
01148 emit newService(pathfn, &service);
01149 if (service)
01150 {
01151 QString id = prefix+fn;
01152
01153
01154 addApplication(id, service);
01155 items.insert(service->menuId(), service);
01156
01157 if (service->categories().isEmpty())
01158 m_currentMenu->items.insert(id, service);
01159 }
01160 }
01161 }
01162 closedir( dp );
01163 markUsedApplications(items);
01164 }
01165
01166
01167
01168 void
01169 VFolderMenu::processMenu(QDomElement &docElem, int pass)
01170 {
01171 SubMenu *parentMenu = m_currentMenu;
01172 int oldDirectoryDirsCount = m_directoryDirs.count();
01173
01174 QString name;
01175 QString directoryFile;
01176 bool onlyUnallocated = false;
01177 bool isDeleted = false;
01178 bool kdeLegacyDirsDone = false;
01179 QDomElement defaultLayoutNode;
01180 QDomElement layoutNode;
01181
01182 QDomElement query;
01183 QDomNode n = docElem.firstChild();
01184 while( !n.isNull() ) {
01185 QDomElement e = n.toElement();
01186 if (e.tagName() == "Name")
01187 {
01188 name = e.text();
01189 }
01190 else if (e.tagName() == "Directory")
01191 {
01192 directoryFile = e.text();
01193 }
01194 else if (e.tagName() == "DirectoryDir")
01195 {
01196 QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01197
01198 m_directoryDirs.prepend(dir);
01199 }
01200 else if (e.tagName() == "OnlyUnallocated")
01201 {
01202 onlyUnallocated = true;
01203 }
01204 else if (e.tagName() == "NotOnlyUnallocated")
01205 {
01206 onlyUnallocated = false;
01207 }
01208 else if (e.tagName() == "Deleted")
01209 {
01210 isDeleted = true;
01211 }
01212 else if (e.tagName() == "NotDeleted")
01213 {
01214 isDeleted = false;
01215 }
01216 else if (e.tagName() == "DefaultLayout")
01217 {
01218 defaultLayoutNode = e;
01219 }
01220 else if (e.tagName() == "Layout")
01221 {
01222 layoutNode = e;
01223 }
01224 n = n.nextSibling();
01225 }
01226
01227
01228 if (pass == 0)
01229 {
01230 m_currentMenu = 0;
01231
01232 if (parentMenu)
01233 {
01234 foreach (SubMenu *menu, parentMenu->subMenus)
01235 {
01236 if (menu->name == name)
01237 {
01238 m_currentMenu = menu;
01239 break;
01240 }
01241 }
01242 }
01243
01244 if (!m_currentMenu)
01245 {
01246
01247 m_currentMenu = new SubMenu;
01248 m_currentMenu->name = name;
01249
01250 if (parentMenu)
01251 parentMenu->subMenus.append(m_currentMenu);
01252 else
01253 m_rootMenu = m_currentMenu;
01254 }
01255 if (directoryFile.isEmpty())
01256 {
01257 kDebug(7021) << "Menu" << name << "does not specify a directory file.";
01258 }
01259
01260
01261 QString tmp = locateDirectoryFile(directoryFile);
01262 if (! tmp.isEmpty())
01263 m_currentMenu->directoryFile = tmp;
01264 m_currentMenu->isDeleted = isDeleted;
01265
01266 m_currentMenu->defaultLayoutNode = defaultLayoutNode;
01267 m_currentMenu->layoutNode = layoutNode;
01268 }
01269 else
01270 {
01271
01272 if (parentMenu)
01273 {
01274 foreach (SubMenu *menu, parentMenu->subMenus)
01275 {
01276 if (menu->name == name)
01277 {
01278 m_currentMenu = menu;
01279 break;
01280 }
01281 }
01282 }
01283 else
01284 {
01285 m_currentMenu = m_rootMenu;
01286 }
01287 }
01288
01289
01290 if (pass == 0)
01291 {
01292 QDomElement query;
01293 QDomNode n = docElem.firstChild();
01294 while( !n.isNull() ) {
01295 QDomElement e = n.toElement();
01296 if (e.tagName() == "AppDir")
01297 {
01298 createAppsInfo();
01299 QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01300
01301 registerDirectory(dir);
01302
01303 loadApplications(dir, QString());
01304 }
01305 else if (e.tagName() == "KDELegacyDirs")
01306 {
01307 createAppsInfo();
01308 if (!kdeLegacyDirsDone)
01309 {
01310 kDebug(7021) << "Processing KDE Legacy dirs for <KDE>";
01311 SubMenu *oldMenu = m_currentMenu;
01312 m_currentMenu = new SubMenu;
01313
01314 processKDELegacyDirs();
01315
01316 m_legacyNodes.insert("<KDE>", m_currentMenu);
01317 m_currentMenu = oldMenu;
01318
01319 kdeLegacyDirsDone = true;
01320 }
01321 }
01322 else if (e.tagName() == "LegacyDir")
01323 {
01324 createAppsInfo();
01325 QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01326
01327 QString prefix = e.attributes().namedItem("prefix").toAttr().value();
01328
01329 if (m_defaultLegacyDirs.contains(dir))
01330 {
01331 if (!kdeLegacyDirsDone)
01332 {
01333 kDebug(7021) << "Processing KDE Legacy dirs for" << dir;
01334 SubMenu *oldMenu = m_currentMenu;
01335 m_currentMenu = new SubMenu;
01336
01337 processKDELegacyDirs();
01338
01339 m_legacyNodes.insert("<KDE>", m_currentMenu);
01340 m_currentMenu = oldMenu;
01341
01342 kdeLegacyDirsDone = true;
01343 }
01344 }
01345 else
01346 {
01347 SubMenu *oldMenu = m_currentMenu;
01348 m_currentMenu = new SubMenu;
01349
01350 registerDirectory(dir);
01351
01352 processLegacyDir(dir, QString(), prefix);
01353
01354 m_legacyNodes.insert(dir, m_currentMenu);
01355 m_currentMenu = oldMenu;
01356 }
01357 }
01358 n = n.nextSibling();
01359 }
01360 }
01361
01362 loadAppsInfo();
01363
01364 if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
01365 {
01366 n = docElem.firstChild();
01367
01368 while( !n.isNull() ) {
01369 QDomElement e = n.toElement();
01370 if (e.tagName() == "Include")
01371 {
01372 QHash<QString,KService::Ptr> items;
01373
01374 QDomNode n2 = e.firstChild();
01375 while( !n2.isNull() ) {
01376 QDomElement e2 = n2.toElement();
01377 items.clear();
01378 processCondition(e2, items);
01379 if (m_track)
01380 track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Include>");
01381 includeItems(m_currentMenu->items, items);
01382 excludeItems(m_currentMenu->excludeItems, items);
01383 markUsedApplications(items);
01384
01385 if (m_track)
01386 track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Include>");
01387
01388 n2 = n2.nextSibling();
01389 }
01390 }
01391
01392 else if (e.tagName() == "Exclude")
01393 {
01394 QHash<QString,KService::Ptr> items;
01395
01396 QDomNode n2 = e.firstChild();
01397 while( !n2.isNull() ) {
01398 QDomElement e2 = n2.toElement();
01399 items.clear();
01400 processCondition(e2, items);
01401 if (m_track)
01402 track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "Before <Exclude>");
01403 excludeItems(m_currentMenu->items, items);
01404 includeItems(m_currentMenu->excludeItems, items);
01405 if (m_track)
01406 track(m_trackId, m_currentMenu->name, m_currentMenu->items, m_currentMenu->excludeItems, items, "After <Exclude>");
01407 n2 = n2.nextSibling();
01408 }
01409 }
01410
01411 n = n.nextSibling();
01412 }
01413 }
01414
01415 n = docElem.firstChild();
01416 while( !n.isNull() ) {
01417 QDomElement e = n.toElement();
01418 if (e.tagName() == "Menu")
01419 {
01420 processMenu(e, pass);
01421 }
01422
01423
01424
01425
01426 else if (pass == 0)
01427 {
01428 if (e.tagName() == "LegacyDir")
01429 {
01430
01431 QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01432 SubMenu *legacyMenu = m_legacyNodes[dir];
01433 if (legacyMenu)
01434 {
01435 mergeMenu(m_currentMenu, legacyMenu);
01436 }
01437 }
01438
01439 else if (e.tagName() == "KDELegacyDirs")
01440 {
01441
01442 QString dir = "<KDE>";
01443 SubMenu *legacyMenu = m_legacyNodes[dir];
01444 if (legacyMenu)
01445 {
01446 mergeMenu(m_currentMenu, legacyMenu);
01447 }
01448 }
01449 }
01450 n = n.nextSibling();
01451 }
01452
01453 if (pass == 2)
01454 {
01455 n = docElem.firstChild();
01456 while( !n.isNull() ) {
01457 QDomElement e = n.toElement();
01458 if (e.tagName() == "Move")
01459 {
01460 QString orig;
01461 QString dest;
01462 QDomNode n2 = e.firstChild();
01463 while( !n2.isNull() ) {
01464 QDomElement e2 = n2.toElement();
01465 if( e2.tagName() == "Old")
01466 orig = e2.text();
01467 if( e2.tagName() == "New")
01468 dest = e2.text();
01469 n2 = n2.nextSibling();
01470 }
01471 kDebug(7021) << "Moving" << orig << "to" << dest;
01472 if (!orig.isEmpty() && !dest.isEmpty())
01473 {
01474 SubMenu *menu = takeSubMenu(m_currentMenu, orig);
01475 if (menu)
01476 {
01477 insertSubMenu(m_currentMenu, dest, menu, true);
01478 }
01479 }
01480 }
01481 n = n.nextSibling();
01482 }
01483
01484 }
01485
01486 unloadAppsInfo();
01487
01488 while (m_directoryDirs.count() > oldDirectoryDirsCount)
01489 m_directoryDirs.pop_front();
01490
01491 m_currentMenu = parentMenu;
01492 }
01493
01494
01495
01496 static QString parseAttribute( const QDomElement &e)
01497 {
01498 QString option;
01499 if ( e.hasAttribute( "show_empty" ) )
01500 {
01501 QString str = e.attribute( "show_empty" );
01502 if ( str=="true" )
01503 option= "ME ";
01504 else if ( str=="false" )
01505 option= "NME ";
01506 else
01507 kDebug()<<" Error in parsing show_empty attribute :"<<str;
01508 }
01509 if ( e.hasAttribute( "inline" ) )
01510 {
01511 QString str = e.attribute( "inline" );
01512 if ( str=="true" )
01513 option+="I ";
01514 else if ( str=="false" )
01515 option+="NI ";
01516 else
01517 kDebug()<<" Error in parsing inlibe attribute :"<<str;
01518 }
01519 if ( e.hasAttribute( "inline_limit" ) )
01520 {
01521 bool ok;
01522 int value = e.attribute( "inline_limit" ).toInt(&ok);
01523 if ( ok )
01524 option+=QString( "IL[%1] " ).arg( value );
01525 }
01526 if ( e.hasAttribute( "inline_header" ) )
01527 {
01528 QString str = e.attribute( "inline_header" );
01529 if ( str=="true")
01530 option+="IH ";
01531 else if ( str == "false" )
01532 option+="NIH ";
01533 else
01534 kDebug()<<" Error in parsing of inline_header attribute :"<<str;
01535
01536 }
01537 if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true")
01538 {
01539 QString str = e.attribute( "inline_alias" );
01540 if ( str=="true" )
01541 option+="IA";
01542 else if ( str=="false" )
01543 option+="NIA";
01544 else
01545 kDebug()<<" Error in parsing inline_alias attribute :"<<str;
01546 }
01547 if( !option.isEmpty())
01548 {
01549 option = option.prepend(":O");
01550 }
01551 return option;
01552
01553 }
01554
01555 static QStringList parseLayoutNode(const QDomElement &docElem)
01556 {
01557 QStringList layout;
01558
01559 QString optionDefaultLayout;
01560 if( docElem.tagName()=="DefaultLayout")
01561 optionDefaultLayout = parseAttribute( docElem);
01562 if ( !optionDefaultLayout.isEmpty() )
01563 layout.append( optionDefaultLayout );
01564
01565 QDomNode n = docElem.firstChild();
01566 while( !n.isNull() ) {
01567 QDomElement e = n.toElement();
01568 if (e.tagName() == "Separator")
01569 {
01570 layout.append(":S");
01571 }
01572 else if (e.tagName() == "Filename")
01573 {
01574 layout.append(e.text());
01575 }
01576 else if (e.tagName() == "Menuname")
01577 {
01578 layout.append('/'+e.text());
01579 QString option = parseAttribute( e );
01580 if( !option.isEmpty())
01581 layout.append( option );
01582 }
01583 else if (e.tagName() == "Merge")
01584 {
01585 QString type = e.attributeNode("type").value();
01586 if (type == "files")
01587 layout.append(":F");
01588 else if (type == "menus")
01589 layout.append(":M");
01590 else if (type == "all")
01591 layout.append(":A");
01592 }
01593
01594 n = n.nextSibling();
01595 }
01596 return layout;
01597 }
01598
01599 void
01600 VFolderMenu::layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout)
01601 {
01602 if (!menu->defaultLayoutNode.isNull())
01603 {
01604 defaultLayout = parseLayoutNode(menu->defaultLayoutNode);
01605 }
01606
01607 if (menu->layoutNode.isNull())
01608 {
01609 menu->layoutList = defaultLayout;
01610 }
01611 else
01612 {
01613 menu->layoutList = parseLayoutNode(menu->layoutNode);
01614 if (menu->layoutList.isEmpty())
01615 menu->layoutList = defaultLayout;
01616 }
01617
01618 foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
01619 {
01620 layoutMenu(subMenu, defaultLayout);
01621 }
01622 }
01623
01624 void
01625 VFolderMenu::markUsedApplications(const QHash<QString,KService::Ptr>& items)
01626 {
01627 foreach(const KService::Ptr &p, items)
01628 m_usedAppsDict.insert(p->menuId());
01629 }
01630
01631 VFolderMenu::SubMenu *
01632 VFolderMenu::parseMenu(const QString &file, bool forceLegacyLoad)
01633 {
01634 m_forcedLegacyLoad = false;
01635 m_legacyLoaded = false;
01636 m_appsInfo = 0;
01637
01638 const QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu");
01639 for(QStringList::ConstIterator it=dirs.begin();
01640 it != dirs.end(); ++it)
01641 {
01642 registerDirectory(*it);
01643 }
01644
01645 loadMenu(file);
01646
01647 delete m_rootMenu;
01648 m_rootMenu = m_currentMenu = 0;
01649
01650 QDomElement docElem = m_doc.documentElement();
01651
01652 for (int pass = 0; pass <= 2; pass++)
01653 {
01654
01655
01656
01657 processMenu(docElem, pass);
01658
01659 switch (pass) {
01660 case 0:
01661
01662
01663 buildApplicationIndex(false);
01664 break;
01665 case 1:
01666
01667
01668 buildApplicationIndex(true );
01669 break;
01670 case 2:
01671 {
01672 QStringList defaultLayout;
01673 defaultLayout << ":M";
01674 defaultLayout << ":F";
01675 layoutMenu(m_rootMenu, defaultLayout);
01676 break;
01677 }
01678 default:
01679 break;
01680 }
01681 }
01682
01683 if (!m_legacyLoaded && forceLegacyLoad)
01684 {
01685 m_forcedLegacyLoad = true;
01686 processKDELegacyDirs();
01687 }
01688
01689 return m_rootMenu;
01690 }
01691
01692 void
01693 VFolderMenu::setTrackId(const QString &id)
01694 {
01695 m_track = !id.isEmpty();
01696 m_trackId = id;
01697 }
01698
01699 #include "vfolder_menu.moc"