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