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