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