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

KDED

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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