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