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