TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1674 lines
46 KiB

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