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.

vfolder_menu.cpp 46KB

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