vfolder_menu.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
00003  *
00004  *  This library is free software; you can redistribute it and/or
00005  *  modify it under the terms of the GNU Library General Public
00006  *  License version 2 as published by the Free Software Foundation;
00007  *
00008  *  This library is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  *  Library General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU Library General Public License
00014  *  along with this library; see the file COPYING.LIB.  If not, write to
00015  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  *  Boston, MA 02110-1301, USA.
00017  **/
00018 
00019 #include <sys/types.h>
00020 #include <sys/stat.h>
00021 #include <unistd.h>
00022 #include <dirent.h>
00023 #include <stdlib.h> // getenv
00024 
00025 #include <kdebug.h>
00026 #include <kglobal.h>
00027 #include <kstandarddirs.h>
00028 #include <kservice.h>
00029 #include <kde_file.h>
00030 
00031 #include <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 baseName = QDir::cleanDirPath(m_docInfo.baseDir + fileName);
00776    QString result = locate("xdgconf-menu", baseName);
00777 
00778    return result;
00779 }
00780 
00781 QString
00782 VFolderMenu::locateDirectoryFile(const QString &fileName)
00783 {
00784    if (fileName.isEmpty())
00785       return QString::null;
00786 
00787    if (!QDir::isRelativePath(fileName))
00788    {
00789       if (KStandardDirs::exists(fileName))
00790          return fileName;
00791       return QString::null;
00792    }
00793 
00794    // First location in the list wins
00795    QString tmp;
00796    for(QStringList::ConstIterator it = m_directoryDirs.begin();
00797        it != m_directoryDirs.end();
00798        ++it)
00799    {
00800       tmp = (*it)+fileName;
00801       if (KStandardDirs::exists(tmp))
00802          return tmp;
00803    }
00804 
00805    return QString::null;
00806 }
00807 
00808 void
00809 VFolderMenu::initDirs()
00810 {
00811    m_defaultDataDirs = QStringList::split(':', KGlobal::dirs()->kfsstnd_prefixes());
00812    QString localDir = m_defaultDataDirs.first();
00813    m_defaultDataDirs.remove(localDir); // Remove local dir
00814 
00815    m_defaultAppDirs = KGlobal::dirs()->findDirs("xdgdata-apps", QString::null);
00816    m_defaultDirectoryDirs = KGlobal::dirs()->findDirs("xdgdata-dirs", QString::null);
00817    m_defaultLegacyDirs = KGlobal::dirs()->resourceDirs("apps");
00818 }
00819 
00820 void
00821 VFolderMenu::loadMenu(const QString &fileName)
00822 {
00823    m_defaultMergeDirs.clear();
00824 
00825    if (!fileName.endsWith(".menu"))
00826       return;
00827 
00828    pushDocInfo(fileName);
00829    m_defaultMergeDirs << m_docInfo.baseName+"-merged/";
00830    m_doc = loadDoc();
00831    popDocInfo();
00832 
00833    if (m_doc.isNull())
00834    {
00835       kdWarning(7021) << "Load error (" << m_docInfo.path << ")" << endl;
00836       return;
00837    }
00838 
00839    QDomElement e = m_doc.documentElement();
00840    QString name;
00841    mergeMenus(e, name);
00842 }
00843 
00844 void
00845 VFolderMenu::processCondition(QDomElement &domElem, QDict<KService> *items)
00846 {
00847    if (domElem.tagName() == "And")
00848    {
00849       QDomNode n = domElem.firstChild();
00850       // Look for the first child element
00851       while (!n.isNull()) // loop in case of comments
00852       {
00853          QDomElement e = n.toElement();
00854          n = n.nextSibling();
00855          if ( !e.isNull() ) {
00856              processCondition(e, items);
00857              break; // we only want the first one
00858          }
00859       }
00860 
00861       QDict<KService> andItems;
00862       while( !n.isNull() ) {
00863          QDomElement e = n.toElement();
00864          if (e.tagName() == "Not")
00865          {
00866             // Special handling for "and not"
00867             QDomNode n2 = e.firstChild();
00868             while( !n2.isNull() ) {
00869                QDomElement e2 = n2.toElement();
00870                andItems.clear();
00871                processCondition(e2, &andItems);
00872                excludeItems(items, &andItems);
00873                n2 = n2.nextSibling();
00874             }
00875          }
00876          else
00877          {
00878             andItems.clear();
00879             processCondition(e, &andItems);
00880             matchItems(items, &andItems);
00881          }
00882          n = n.nextSibling();
00883       }
00884    }
00885    else if (domElem.tagName() == "Or")
00886    {
00887       QDomNode n = domElem.firstChild();
00888       // Look for the first child element
00889       while (!n.isNull()) // loop in case of comments
00890       {
00891          QDomElement e = n.toElement();
00892          n = n.nextSibling();
00893          if ( !e.isNull() ) {
00894              processCondition(e, items);
00895              break; // we only want the first one
00896          }
00897       }
00898 
00899       QDict<KService> orItems;
00900       while( !n.isNull() ) {
00901          QDomElement e = n.toElement();
00902          if ( !e.isNull() ) {
00903              orItems.clear();
00904              processCondition(e, &orItems);
00905              includeItems(items, &orItems);
00906          }
00907          n = n.nextSibling();
00908       }
00909    }
00910    else if (domElem.tagName() == "Not")
00911    {
00912       FOR_ALL_APPLICATIONS(it)
00913       {
00914          KService *s = it.current();
00915          items->replace(s->menuId(), s);
00916       }
00917       FOR_ALL_APPLICATIONS_END
00918 
00919       QDict<KService> notItems;
00920       QDomNode n = domElem.firstChild();
00921       while( !n.isNull() ) {
00922          QDomElement e = n.toElement();
00923          if ( !e.isNull() ) {
00924              notItems.clear();
00925              processCondition(e, &notItems);
00926              excludeItems(items, &notItems);
00927          }
00928          n = n.nextSibling();
00929       }
00930    }
00931    else if (domElem.tagName() == "Category")
00932    {
00933       FOR_CATEGORY(domElem.text(), it)
00934       {
00935          KService *s = *it;
00936          items->replace(s->menuId(), s);
00937       }
00938       FOR_CATEGORY_END
00939    }
00940    else if (domElem.tagName() == "All")
00941    {
00942       FOR_ALL_APPLICATIONS(it)
00943       {
00944          KService *s = it.current();
00945          items->replace(s->menuId(), s);
00946       }
00947       FOR_ALL_APPLICATIONS_END
00948    }
00949    else if (domElem.tagName() == "Filename")
00950    {
00951       QString filename = domElem.text();
00952 kdDebug(7021) << "Adding file " << filename << endl;
00953       KService *s = findApplication(filename);
00954       if (s)
00955          items->replace(filename, s);
00956    }
00957 }
00958 
00959 void
00960 VFolderMenu::loadApplications(const QString &dir, const QString &prefix)
00961 {
00962    kdDebug(7021) << "Looking up applications under " << dir << endl;
00963 
00964    // We look for a set of files.
00965    DIR *dp = opendir( QFile::encodeName(dir));
00966    if (!dp)
00967       return;
00968 
00969    struct dirent *ep;
00970    KDE_struct_stat buff;
00971 
00972    QString _dot(".");
00973    QString _dotdot("..");
00974 
00975    while( ( ep = readdir( dp ) ) != 0L )
00976    {
00977       QString fn( QFile::decodeName(ep->d_name));
00978       if (fn == _dot || fn == _dotdot || fn.at(fn.length() - 1).latin1() == '~')
00979          continue;
00980 
00981       QString pathfn = dir + fn;
00982       if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
00983          continue; // Couldn't stat (e.g. no read permissions)
00984       }
00985       if ( S_ISDIR( buff.st_mode )) {
00986          loadApplications(pathfn + '/', prefix + fn + '-');
00987          continue;
00988       }
00989 
00990       if ( S_ISREG( buff.st_mode))
00991       {
00992          if (!fn.endsWith(".desktop"))
00993             continue;
00994 
00995          KService *service = 0;
00996          emit newService(pathfn, &service);
00997          if (service)
00998             addApplication(prefix+fn, service);
00999       }
01000     }
01001     closedir( dp );
01002 }
01003 
01004 void
01005 VFolderMenu::processKDELegacyDirs()
01006 {
01007 kdDebug(7021) << "processKDELegacyDirs()" << endl;
01008 
01009    QDict<KService> items;
01010    QString prefix = "kde-";
01011 
01012    QStringList relFiles;
01013    QRegExp files("\\.(desktop|kdelnk)$");
01014    QRegExp dirs("\\.directory$");
01015 
01016    (void) KGlobal::dirs()->findAllResources( "apps",
01017                                              QString::null,
01018                                              true, // Recursive!
01019                                              true, // uniq
01020                                              relFiles);
01021    for(QStringList::ConstIterator it = relFiles.begin();
01022        it != relFiles.end(); ++it)
01023    {
01024       if (!m_forcedLegacyLoad && (dirs.search(*it) != -1))
01025       {
01026          QString name = *it;
01027          if (!name.endsWith("/.directory"))
01028             continue; // Probably ".directory", skip it.
01029 
01030          name = name.left(name.length()-11);
01031 
01032          SubMenu *newMenu = new SubMenu;
01033          newMenu->directoryFile = locate("apps", *it);
01034 
01035          insertSubMenu(m_currentMenu, name, newMenu);
01036          continue;
01037       }
01038 
01039       if (files.search(*it) != -1)
01040       {
01041          QString name = *it;
01042          KService *service = 0;
01043          emit newService(name, &service);
01044 
01045          if (service && !m_forcedLegacyLoad)
01046          {
01047             QString id = name;
01048             // Strip path from id
01049             int i = id.findRev('/');
01050             if (i >= 0)
01051                id = id.mid(i+1);
01052 
01053             id.prepend(prefix);
01054 
01055             // TODO: add Legacy category
01056             addApplication(id, service);
01057             items.replace(service->menuId(), service);
01058             if (service->categories().isEmpty())
01059                insertService(m_currentMenu, name, service);
01060 
01061          }
01062       }
01063    }
01064    markUsedApplications(&items);
01065    m_legacyLoaded = true;
01066 }
01067 
01068 void
01069 VFolderMenu::processLegacyDir(const QString &dir, const QString &relDir, const QString &prefix)
01070 {
01071 kdDebug(7021) << "processLegacyDir(" << dir << ", " << relDir << ", " << prefix << ")" << endl;
01072 
01073    QDict<KService> items;
01074    // We look for a set of files.
01075    DIR *dp = opendir( QFile::encodeName(dir));
01076    if (!dp)
01077       return;
01078 
01079    struct dirent *ep;
01080    KDE_struct_stat buff;
01081 
01082    QString _dot(".");
01083    QString _dotdot("..");
01084 
01085    while( ( ep = readdir( dp ) ) != 0L )
01086    {
01087       QString fn( QFile::decodeName(ep->d_name));
01088       if (fn == _dot || fn == _dotdot || fn.at(fn.length() - 1).latin1() == '~')
01089          continue;
01090 
01091       QString pathfn = dir + fn;
01092       if ( KDE_stat( QFile::encodeName(pathfn), &buff ) != 0 ) {
01093          continue; // Couldn't stat (e.g. no read permissions)
01094       }
01095       if ( S_ISDIR( buff.st_mode )) {
01096          SubMenu *parentMenu = m_currentMenu;
01097 
01098          m_currentMenu = new SubMenu;
01099          m_currentMenu->name = fn;
01100          m_currentMenu->directoryFile = dir + fn + "/.directory";
01101 
01102          parentMenu->subMenus.append(m_currentMenu);
01103 
01104          processLegacyDir(pathfn + '/', relDir+fn+'/', prefix);
01105          m_currentMenu = parentMenu;
01106          continue;
01107       }
01108 
01109       if ( S_ISREG( buff.st_mode))
01110       {
01111          if (!fn.endsWith(".desktop"))
01112             continue;
01113 
01114          KService *service = 0;
01115          emit newService(pathfn, &service);
01116          if (service)
01117          {
01118             QString id = prefix+fn;
01119 
01120             // TODO: Add legacy category
01121             addApplication(id, service);
01122             items.replace(service->menuId(), service);
01123 
01124             if (service->categories().isEmpty())
01125                m_currentMenu->items.replace(id, service);
01126          }
01127       }
01128     }
01129     closedir( dp );
01130     markUsedApplications(&items);
01131 }
01132 
01133 
01134 
01135 void
01136 VFolderMenu::processMenu(QDomElement &docElem, int pass)
01137 {
01138    SubMenu *parentMenu = m_currentMenu;
01139    unsigned int oldDirectoryDirsCount = m_directoryDirs.count();
01140 
01141    QString name;
01142    QString directoryFile;
01143    bool onlyUnallocated = false;
01144    bool isDeleted = false;
01145    bool kdeLegacyDirsDone = false;
01146    QDomElement defaultLayoutNode;
01147    QDomElement layoutNode;
01148 
01149    QDomElement query;
01150    QDomNode n = docElem.firstChild();
01151    while( !n.isNull() ) {
01152       QDomElement e = n.toElement(); // try to convert the node to an element.
01153       if (e.tagName() == "Name")
01154       {
01155          name = e.text();
01156       }
01157       else if (e.tagName() == "Directory")
01158       {
01159          directoryFile = e.text();
01160       }
01161       else if (e.tagName() == "DirectoryDir")
01162       {
01163          QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01164 
01165          m_directoryDirs.prepend(dir);
01166       }
01167       else if (e.tagName() == "OnlyUnallocated")
01168       {
01169          onlyUnallocated = true;
01170       }
01171       else if (e.tagName() == "NotOnlyUnallocated")
01172       {
01173          onlyUnallocated = false;
01174       }
01175       else if (e.tagName() == "Deleted")
01176       {
01177          isDeleted = true;
01178       }
01179       else if (e.tagName() == "NotDeleted")
01180       {
01181          isDeleted = false;
01182       }
01183       else if (e.tagName() == "DefaultLayout")
01184       {
01185          defaultLayoutNode = e;
01186       }
01187       else if (e.tagName() == "Layout")
01188       {
01189          layoutNode = e;
01190       }
01191       n = n.nextSibling();
01192    }
01193 
01194    // Setup current menu entry
01195    if (pass == 0)
01196    {
01197       m_currentMenu = 0;
01198       // Look up menu
01199       if (parentMenu)
01200       {
01201          for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
01202          {
01203             if (menu->name == name)
01204             {
01205                m_currentMenu = menu;
01206                break;
01207             }
01208          }
01209       }
01210 
01211       if (!m_currentMenu) // Not found?
01212       {
01213          // Create menu
01214          m_currentMenu = new SubMenu;
01215          m_currentMenu->name = name;
01216 
01217          if (parentMenu)
01218             parentMenu->subMenus.append(m_currentMenu);
01219          else
01220             m_rootMenu = m_currentMenu;
01221       }
01222       if (directoryFile.isEmpty())
01223       {
01224          kdDebug(7021) << "Menu " << name << " does not specify a directory file." << endl;
01225       }
01226 
01227       // Override previous directoryFile iff available
01228       QString tmp = locateDirectoryFile(directoryFile);
01229       if (! tmp.isEmpty())
01230          m_currentMenu->directoryFile = tmp;
01231       m_currentMenu->isDeleted = isDeleted;
01232 
01233       m_currentMenu->defaultLayoutNode = defaultLayoutNode;
01234       m_currentMenu->layoutNode = layoutNode;
01235    }
01236    else
01237    {
01238       // Look up menu
01239       if (parentMenu)
01240       {
01241          for(SubMenu *menu = parentMenu->subMenus.first(); menu; menu = parentMenu->subMenus.next())
01242          {
01243             if (menu->name == name)
01244             {
01245                m_currentMenu = menu;
01246                break;
01247             }
01248          }
01249       }
01250       else
01251       {
01252          m_currentMenu = m_rootMenu;
01253       }
01254    }
01255 
01256    // Process AppDir and LegacyDir
01257    if (pass == 0)
01258    {
01259       QDomElement query;
01260       QDomNode n = docElem.firstChild();
01261       while( !n.isNull() ) {
01262          QDomElement e = n.toElement(); // try to convert the node to an element.
01263          if (e.tagName() == "AppDir")
01264          {
01265             createAppsInfo();
01266             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01267 
01268             registerDirectory(dir);
01269 
01270             loadApplications(dir, QString::null);
01271          }
01272          else if (e.tagName() == "KDELegacyDirs")
01273          {
01274             createAppsInfo();
01275             if (!kdeLegacyDirsDone)
01276             {
01277 kdDebug(7021) << "Processing KDE Legacy dirs for <KDE>" << endl;
01278                SubMenu *oldMenu = m_currentMenu;
01279                m_currentMenu = new SubMenu;
01280 
01281                processKDELegacyDirs();
01282 
01283                m_legacyNodes.replace("<KDE>", m_currentMenu);
01284                m_currentMenu = oldMenu;
01285 
01286                kdeLegacyDirsDone = true;
01287             }
01288          }
01289          else if (e.tagName() == "LegacyDir")
01290          {
01291             createAppsInfo();
01292             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01293 
01294             QString prefix = e.attributes().namedItem("prefix").toAttr().value();
01295 
01296             if (m_defaultLegacyDirs.contains(dir))
01297             {
01298                if (!kdeLegacyDirsDone)
01299                {
01300 kdDebug(7021) << "Processing KDE Legacy dirs for " << dir << 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
01313             {
01314                SubMenu *oldMenu = m_currentMenu;
01315                m_currentMenu = new SubMenu;
01316 
01317                registerDirectory(dir);
01318 
01319                processLegacyDir(dir, QString::null, prefix);
01320 
01321                m_legacyNodes.replace(dir, m_currentMenu);
01322                m_currentMenu = oldMenu;
01323             }
01324          }
01325          n = n.nextSibling();
01326       }
01327    }
01328 
01329    loadAppsInfo(); // Update the scope wrt the list of applications
01330 
01331    if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
01332    {
01333       n = docElem.firstChild();
01334 
01335       while( !n.isNull() ) {
01336          QDomElement e = n.toElement(); // try to convert the node to an element.
01337          if (e.tagName() == "Include")
01338          {
01339             QDict<KService> items;
01340 
01341             QDomNode n2 = e.firstChild();
01342             while( !n2.isNull() ) {
01343                QDomElement e2 = n2.toElement();
01344                items.clear();
01345                processCondition(e2, &items);
01346                if (m_track)
01347                   track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "Before <Include>");
01348                includeItems(&(m_currentMenu->items), &items);
01349                excludeItems(&(m_currentMenu->excludeItems), &items);
01350                markUsedApplications(&items);
01351 
01352                if (m_track)
01353                   track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "After <Include>");
01354 
01355                n2 = n2.nextSibling();
01356             }
01357          }
01358 
01359          else if (e.tagName() == "Exclude")
01360          {
01361             QDict<KService> items;
01362 
01363             QDomNode n2 = e.firstChild();
01364             while( !n2.isNull() ) {
01365                QDomElement e2 = n2.toElement();
01366                items.clear();
01367                processCondition(e2, &items);
01368                if (m_track)
01369                   track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "Before <Exclude>");
01370                excludeItems(&(m_currentMenu->items), &items);
01371                includeItems(&(m_currentMenu->excludeItems), &items);
01372                if (m_track)
01373                   track(m_trackId, m_currentMenu->name, &(m_currentMenu->items), &(m_currentMenu->excludeItems), &items, "After <Exclude>");
01374                n2 = n2.nextSibling();
01375             }
01376          }
01377 
01378          n = n.nextSibling();
01379       }
01380    }
01381 
01382    n = docElem.firstChild();
01383    while( !n.isNull() ) {
01384       QDomElement e = n.toElement(); // try to convert the node to an element.
01385       if (e.tagName() == "Menu")
01386       {
01387          processMenu(e, pass);
01388       }
01389 // We insert legacy dir in pass 0, this way the order in the .menu-file determines
01390 // which .directory file gets used, but the menu-entries of legacy-menus will always
01391 // have the lowest priority.
01392 //      else if (((pass == 1) && !onlyUnallocated) || ((pass == 2) && onlyUnallocated))
01393       else if (pass == 0)
01394       {
01395          if (e.tagName() == "LegacyDir")
01396          {
01397             // Add legacy nodes to Menu structure
01398             QString dir = absoluteDir(e.text(), e.attribute("__BaseDir"));
01399             SubMenu *legacyMenu = m_legacyNodes.find(dir);
01400             if (legacyMenu)
01401             {
01402                mergeMenu(m_currentMenu, legacyMenu);
01403             }
01404          }
01405 
01406          else if (e.tagName() == "KDELegacyDirs")
01407          {
01408             // Add legacy nodes to Menu structure
01409             QString dir = "<KDE>";
01410             SubMenu *legacyMenu = m_legacyNodes.find(dir);
01411             if (legacyMenu)
01412             {
01413                mergeMenu(m_currentMenu, legacyMenu);
01414             }
01415          }
01416       }
01417       n = n.nextSibling();
01418    }
01419 
01420    if (pass == 2)
01421    {
01422       n = docElem.firstChild();
01423       while( !n.isNull() ) {
01424          QDomElement e = n.toElement(); // try to convert the node to an element.
01425          if (e.tagName() == "Move")
01426          {
01427             QString orig;
01428             QString dest;
01429             QDomNode n2 = e.firstChild();
01430             while( !n2.isNull() ) {
01431                QDomElement e2 = n2.toElement(); // try to convert the node to an element.
01432                if( e2.tagName() == "Old")
01433                   orig = e2.text();
01434                if( e2.tagName() == "New")
01435                   dest = e2.text();
01436                n2 = n2.nextSibling();
01437             }
01438             kdDebug(7021) << "Moving " << orig << " to " << dest << endl;
01439             if (!orig.isEmpty() && !dest.isEmpty())
01440             {
01441               SubMenu *menu = takeSubMenu(m_currentMenu, orig);
01442               if (menu)
01443               {
01444                 insertSubMenu(m_currentMenu, dest, menu, true); // Destination has priority
01445               }
01446             }
01447          }
01448          n = n.nextSibling();
01449       }
01450 
01451    }
01452 
01453    unloadAppsInfo(); // Update the scope wrt the list of applications
01454 
01455    while (m_directoryDirs.count() > oldDirectoryDirsCount)
01456       m_directoryDirs.pop_front();
01457 
01458    m_currentMenu = parentMenu;
01459 }
01460 
01461 
01462 
01463 static QString parseAttribute( const QDomElement &e)
01464 {
01465     QString option;
01466     if ( e.hasAttribute( "show_empty" ) )
01467     {
01468         QString str = e.attribute( "show_empty" );
01469         if ( str=="true" )
01470             option= "ME ";
01471         else if ( str=="false" )
01472             option= "NME ";
01473         else
01474             kdDebug()<<" Error in parsing show_empty attribute :"<<str<<endl;
01475     }
01476     if ( e.hasAttribute( "inline" ) )
01477     {
01478         QString str = e.attribute( "inline" );
01479         if (  str=="true" )
01480             option+="I ";
01481         else if ( str=="false" )
01482             option+="NI ";
01483         else
01484             kdDebug()<<" Error in parsing inlibe attribute :"<<str<<endl;
01485     }
01486     if ( e.hasAttribute( "inline_limit" ) )
01487     {
01488         bool ok;
01489         int value = e.attribute( "inline_limit" ).toInt(&ok);
01490         if ( ok )
01491             option+=QString( "IL[%1] " ).arg( value );
01492     }
01493     if ( e.hasAttribute( "inline_header" ) )
01494     {
01495         QString str = e.attribute( "inline_header" );
01496         if ( str=="true")
01497             option+="IH ";
01498         else if ( str == "false" )
01499             option+="NIH ";
01500         else
01501             kdDebug()<<" Error in parsing of inline_header attribute :"<<str<<endl;
01502 
01503     }
01504     if ( e.hasAttribute( "inline_alias" ) && e.attribute( "inline_alias" )=="true")
01505     {
01506         QString str = e.attribute( "inline_alias" );
01507         if ( str=="true" )
01508             option+="IA";
01509         else if ( str=="false" )
01510             option+="NIA";
01511         else
01512             kdDebug()<<" Error in parsing inline_alias attribute :"<<str<<endl;
01513     }
01514     if( !option.isEmpty())
01515     {
01516         option = option.prepend(":O");
01517     }
01518     return option;
01519 
01520 }
01521 
01522 static QStringList parseLayoutNode(const QDomElement &docElem)
01523 {
01524    QStringList layout;
01525 
01526    QString optionDefaultLayout;
01527    if( docElem.tagName()=="DefaultLayout")
01528        optionDefaultLayout =  parseAttribute( docElem);
01529    if ( !optionDefaultLayout.isEmpty() )
01530        layout.append( optionDefaultLayout );
01531 
01532    QDomNode n = docElem.firstChild();
01533    while( !n.isNull() ) {
01534       QDomElement e = n.toElement(); // try to convert the node to an element.
01535       if (e.tagName() == "Separator")
01536       {
01537          layout.append(":S");
01538       }
01539       else if (e.tagName() == "Filename")
01540       {
01541          layout.append(e.text());
01542       }
01543       else if (e.tagName() == "Menuname")
01544       {
01545          layout.append("/"+e.text());
01546          QString option = parseAttribute( e );
01547          if( !option.isEmpty())
01548              layout.append( option );
01549       }
01550       else if (e.tagName() == "Merge")
01551       {
01552          QString type = e.attributeNode("type").value();
01553          if (type == "files")
01554             layout.append(":F");
01555          else if (type == "menus")
01556             layout.append(":M");
01557          else if (type == "all")
01558             layout.append(":A");
01559       }
01560 
01561       n = n.nextSibling();
01562    }
01563    return layout;
01564 }
01565 
01566 void
01567 VFolderMenu::layoutMenu(VFolderMenu::SubMenu *menu, QStringList defaultLayout)
01568 {
01569    if (!menu->defaultLayoutNode.isNull())
01570    {
01571       defaultLayout = parseLayoutNode(menu->defaultLayoutNode);
01572    }
01573 
01574    if (menu->layoutNode.isNull())
01575    {
01576      menu->layoutList = defaultLayout;
01577    }
01578    else
01579    {
01580      menu->layoutList = parseLayoutNode(menu->layoutNode);
01581      if (menu->layoutList.isEmpty())
01582         menu->layoutList = defaultLayout;
01583    }
01584 
01585    for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next())
01586    {
01587       layoutMenu(subMenu, defaultLayout);
01588    }
01589 }
01590 
01591 void
01592 VFolderMenu::markUsedApplications(QDict<KService> *items)
01593 {
01594    for(QDictIterator<KService> it(*items); it.current(); ++it)
01595    {
01596       m_usedAppsDict.replace(it.current()->menuId(), it.current());
01597    }
01598 }
01599 
01600 VFolderMenu::SubMenu *
01601 VFolderMenu::parseMenu(const QString &file, bool forceLegacyLoad)
01602 {
01603    m_forcedLegacyLoad = false;
01604    m_legacyLoaded = false;
01605    m_appsInfo = 0;
01606 
01607    QStringList dirs = KGlobal::dirs()->resourceDirs("xdgconf-menu");
01608    for(QStringList::ConstIterator it=dirs.begin();
01609        it != dirs.end(); ++it)
01610    {
01611       registerDirectory(*it);
01612    }
01613 
01614    loadMenu(file);
01615 
01616    delete m_rootMenu;
01617    m_rootMenu = m_currentMenu = 0;
01618 
01619    QDomElement docElem = m_doc.documentElement();
01620 
01621    for (int pass = 0; pass <= 2; pass++)
01622    {
01623       processMenu(docElem, pass);
01624 
01625       if (pass == 0)
01626       {
01627          buildApplicationIndex(false);
01628       }
01629       if (pass == 1)
01630       {
01631          buildApplicationIndex(true);
01632       }
01633       if (pass == 2)
01634       {
01635          QStringList defaultLayout;
01636          defaultLayout << ":M"; // Sub-Menus
01637          defaultLayout << ":F"; // Individual entries
01638          layoutMenu(m_rootMenu, defaultLayout);
01639       }
01640    }
01641 
01642    if (!m_legacyLoaded && forceLegacyLoad)
01643    {
01644       m_forcedLegacyLoad = true;
01645       processKDELegacyDirs();
01646    }
01647 
01648    return m_rootMenu;
01649 }
01650 
01651 void
01652 VFolderMenu::setTrackId(const QString &id)
01653 {
01654    m_track = !id.isEmpty();
01655    m_trackId = id;
01656 }
01657 
01658 #include "vfolder_menu.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys