• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDED

vfolder_menu.cpp

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

KDED

Skip menu "KDED"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal