File indexing completed on 2024-05-12 16:41:09

0001 /***********************************************************************************
0002   Copyright (C) 2011-2012 by Holger Danielsson (holger.danielsson@versanet.de)
0003  ***********************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or modify  *
0008  *   it under the terms of the GNU General Public License as published by  *
0009  *   the Free Software Foundation; either version 2 of the License, or     *
0010  *   (at your option) any later version.                                   *
0011  *                                                                         *
0012  ***************************************************************************/
0013 
0014 
0015 #include <QIcon>
0016 #include <QInputDialog>
0017 #include <QFile>
0018 #include <QFileInfo>
0019 #include <QHeaderView>
0020 #include <QDomDocument>
0021 #include <QProcessEnvironment>
0022 
0023 #include <QMenu>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 
0027 
0028 #include "dialogs/usermenu/usermenuitem.h"
0029 #include "dialogs/usermenu/usermenutree.h"
0030 
0031 #include "kiledebug.h"
0032 
0033 
0034 // Qt::UserRole+1: menutype
0035 // Qt::UserRole+2: errorcode for menu item
0036 
0037 namespace KileMenu {
0038 
0039 // - Separators are shown as a horizontal line (Qt:UserRole+1)
0040 // - Menu items with errors are displayed in red (Qt:UserRole+2)
0041 void MenuentryDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const
0042 {
0043     QString menutitle = index.data(Qt::DisplayRole).toString();
0044     int error = index.data(Qt::UserRole+2).toInt();
0045 
0046     // any errors?
0047     if (index.column()==0 && error!=UserMenuItem::MODEL_ERROR_NONE ) {
0048         QStyleOptionViewItem optionCustom = option;
0049         optionCustom.palette.setColor(QPalette::Text, Qt::red);
0050         QStyledItemDelegate::paint( painter, optionCustom, index );
0051     }
0052     else {
0053         QStyledItemDelegate::paint( painter, option, index );
0054     }
0055 
0056     // display separators
0057     QString itemtype = index.data(Qt::UserRole+1).toString();
0058     if ( itemtype == "separator" ) {
0059         QRect r = option.rect;
0060         int y =  ( r.bottom() + r.top() ) / 2;
0061 
0062         painter->save();
0063         QPen pen = QPen(Qt::gray);
0064         painter->setPen(pen);
0065         painter->drawLine(r.left()+3,y, r.right()-20,y);
0066         painter->restore();
0067     }
0068 
0069 }
0070 ///////////////////////////// UserMenuTree //////////////////////////////
0071 
0072 UserMenuTree::UserMenuTree(QWidget *parent)
0073     : QTreeWidget(parent)
0074 {
0075     setColumnCount(2);
0076 
0077     header()->setSectionResizeMode(0, QHeaderView::Stretch);
0078     header()->setSectionResizeMode(1, QHeaderView::Fixed);
0079     header()->setSectionsMovable(false);
0080     header()->setStretchLastSection(false);
0081     setColumnWidth(1,140);
0082     setItemDelegateForColumn(0, new MenuentryDelegate(parent));
0083 
0084     // drag and drop
0085     setDragEnabled(true);
0086     setDropIndicatorShown(true);
0087     //setAcceptDrops(true);
0088     setDragDropMode(QAbstractItemView::InternalMove);
0089     setDragDropOverwriteMode(false);
0090 
0091     initEnvPathlist();
0092 }
0093 
0094 bool UserMenuTree::isEmpty()
0095 {
0096     return ( topLevelItemCount() == 0 );
0097 }
0098 
0099 ///////////////////////////// PATH environment variable //////////////////////////////
0100 
0101 // save PATH to search for executable files
0102 void UserMenuTree::initEnvPathlist()
0103 {
0104     QString envpath;
0105 #if QT_VERSION >= 0x040600
0106     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0107     if ( env.contains("PATH") ) {
0108         envpath = env.value("PATH");
0109     }
0110 #else
0111     // Returns the environment of the calling process as a list of key=value pairs.
0112     QStringList environment = QProcess::systemEnvironment();
0113     foreach ( const QString &s, environment ) {
0114         if ( s.startsWith(QLatin1String("PATH=")) ) {
0115             envpath = s.mid(5);
0116             break;
0117         }
0118     }
0119 #endif
0120 
0121 #ifdef Q_WS_WIN
0122     m_envPathlist = envpath.split(';');
0123 #else
0124     m_envPathlist = envpath.split(':');
0125 #endif
0126     m_envPathlist.append(".");
0127 }
0128 
0129 bool UserMenuTree::isItemExecutable(const QString &filename)
0130 {
0131     if ( filename.isEmpty() ) {
0132         return false;
0133     }
0134 
0135     // absolute paths can be checked immediately
0136     QFileInfo fi(filename);
0137     if ( fi.isAbsolute() ) {
0138         return fi.isExecutable();
0139     }
0140 
0141     // search in all paths
0142     for (int i=0; i<m_envPathlist.size(); ++i ) {
0143         bool executable = QFileInfo(m_envPathlist[i]+'/'+filename).isExecutable();
0144         if ( executable ) {
0145             // move to front
0146             if ( i > 0 ) {
0147                 QString temp = m_envPathlist[0];
0148                 m_envPathlist[0] = m_envPathlist[i];
0149                 m_envPathlist[i] = temp;
0150             }
0151             return true;
0152         }
0153     }
0154 
0155     return false;
0156 }
0157 
0158 ///////////////////////////// context menu event //////////////////////////////
0159 
0160 // build a context menu
0161 void UserMenuTree::contextMenuRequested(const QPoint &pos)
0162 {
0163     KILE_DEBUG_MAIN << "context menu requested ..." ;
0164 
0165     m_popupItem = dynamic_cast<UserMenuItem*>(itemAt(pos));
0166     if ( !m_popupItem ) {
0167         KILE_DEBUG_MAIN << "... no item found";
0168         return;
0169     }
0170 
0171     KILE_DEBUG_MAIN << "... popup item found: " << m_popupItem->text(0);
0172     bool submenu = ( m_popupItem->menutype() ==  UserMenuData::Submenu );
0173     bool separator = ( m_popupItem->menutype() ==  UserMenuData::Separator );
0174 
0175     QMenu popup;
0176 
0177     // insert standard menu items
0178     popup.addAction(QIcon::fromTheme("usermenu-insert-above.png"),i18n("Insert above"), this, [this] {insertMenuItem(m_popupItem, false);});
0179     popup.addAction(QIcon::fromTheme("usermenu-insert-below.png"),i18n("Insert below"), this, [this] {insertMenuItem(m_popupItem, true);});
0180     popup.addSeparator();
0181 
0182     // insert separators
0183     if ( !separator ) {
0184         popup.addAction(QIcon::fromTheme("usermenu-separator-above.png"),i18n("Insert a separator above"), this, [this] {insertSeparator(m_popupItem, false);});
0185         popup.addAction(QIcon::fromTheme("usermenu-separator-below.png"),i18n("Insert a separator below"), this, [this] {insertSeparator(m_popupItem, true);});
0186         popup.addSeparator();
0187     }
0188 
0189     // insert submenus
0190     popup.addAction(QIcon::fromTheme("usermenu-submenu-above.png"),i18n("Insert a submenu above"), this, [this] {insertSubmenu(m_popupItem, false);});
0191     popup.addAction(QIcon::fromTheme("usermenu-submenu-below.png"),i18n("Insert a submenu below"), this, [this] {insertSubmenu(m_popupItem, true);});
0192     popup.addSeparator();
0193 
0194     // insert into submenus
0195     if ( submenu ) {
0196         popup.addAction(QIcon::fromTheme("usermenu-into-submenu.png"),i18n("Insert into this submenu"), this, [this] {insertIntoSubmenu(m_popupItem, UserMenuData::Text);});
0197         popup.addAction(i18n("Insert a separator into this submenu"), this, [this] {insertIntoSubmenu(m_popupItem, UserMenuData::Separator);});
0198         popup.addAction(i18n("Insert a submenu into this submenu"), this, [this] {insertIntoSubmenu(m_popupItem, UserMenuData::Submenu);});
0199         popup.addSeparator();
0200     }
0201 
0202     // delete actions
0203     popup.addAction(QIcon::fromTheme("usermenu-delete.png"),i18n("Delete this item"), this, [this] {itemDelete(m_popupItem);});
0204     popup.addSeparator();
0205     popup.addAction(QIcon::fromTheme("usermenu-clear.png"),i18n("Delete the complete tree"), this, [this] {deleteMenuTree();});
0206 
0207     // expand/collapse tree
0208     if ( submenu ) {
0209         popup.addSeparator();
0210         if ( m_popupItem->isExpanded() ) {
0211             popup.addAction(i18n("Collapse submenu"), this, [this] {m_popupItem->setExpanded(false);});
0212         }
0213         else  {
0214             popup.addAction(i18n("Expand submenu"), this, [this] {m_popupItem->setExpanded(true);});
0215         }
0216         popup.addSeparator();
0217         popup.addAction(i18n("Collapse complete tree"), this, [this] {collapseAll();});
0218         popup.addAction(i18n("Expand complete tree"), this, [this] {expandAll();});
0219     }
0220 
0221     // if there are any errors with this item, some info is available
0222     int error = m_popupItem->data(0,Qt::UserRole+2).toInt();
0223     if ( error != UserMenuItem::MODEL_ERROR_NONE ) {
0224         popup.addSeparator();
0225         popup.addAction(QIcon::fromTheme("help-about.png"),i18n("Info"), this, [this] {itemInfo(m_popupItem);});
0226     }
0227 
0228     // const QPoint& pos parameter in the customContextMenuRequested() signal is normally in widget coordinates.
0229     // But classes like QTreeWidget, which inherit from QAbstractScrollArea1 instead use the coordinates of their viewport()
0230     if ( !popup.isEmpty() ) {
0231         popup.exec( viewport()->mapToGlobal(pos) );
0232     }
0233 }
0234 
0235 ///////////////////////////// read XML //////////////////////////////
0236 
0237 // read an xml file and check for errors
0238 bool UserMenuTree::readXml(const QString &filename)
0239 {
0240     KILE_DEBUG_MAIN << "read xml file " << filename;
0241 
0242     QDomDocument doc("UserMenu");
0243     QFile file(filename);
0244     if ( !file.open(QFile::ReadOnly | QFile::Text) ) {
0245         KMessageBox::error(this, i18n("File '%1' does not exist.", filename));
0246         return false;
0247     }
0248     if ( !doc.setContent( &file ) ) {
0249         file.close();
0250         return false;
0251     }
0252     file.close();
0253 
0254     KILE_DEBUG_MAIN << "parse xml ...";
0255     blockSignals(true);
0256 
0257     // clear menutree
0258     clear();
0259 
0260     // read toplevelitems
0261     QDomElement root = doc.documentElement();
0262     QDomElement e = root.firstChildElement();
0263     while ( !e.isNull()) {
0264         QString tag = e.tagName();
0265 
0266         UserMenuItem *item = Q_NULLPTR;
0267         if ( tag == "submenu" ) {
0268             item = readXmlSubmenu(e);
0269         }
0270         else if ( tag == "separator" ) {
0271             item = readXmlSeparator();
0272         }
0273         else { /* if ( tag == "menu" ) */
0274             item = readXmlMenuentry(e);
0275         }
0276 
0277         if ( item ) {
0278             addTopLevelItem(item);
0279         }
0280         e = e.nextSiblingElement();
0281     }
0282 
0283     // the xml menu is built, now check for errors
0284     setErrorCodes();
0285 
0286     // polish menutree
0287     expandAll();
0288     if ( topLevelItemCount() > 0 ) {
0289         setCurrentItem( topLevelItem(0) );
0290     }
0291     blockSignals(false);
0292 
0293     return true;
0294 }
0295 
0296 // a separator tag was found
0297 UserMenuItem *UserMenuTree::readXmlSeparator()
0298 {
0299     return new UserMenuItem(UserMenuData::Separator, QString());
0300 }
0301 
0302 // read tags for a submenu
0303 UserMenuItem *UserMenuTree::readXmlSubmenu(const QDomElement &element)
0304 {
0305     UserMenuItem *submenuitem = new UserMenuItem(UserMenuData::Submenu, QString()) ;
0306 
0307     if ( element.hasChildNodes() ) {
0308         QDomElement e = element.firstChildElement();
0309         while ( !e.isNull()) {
0310             UserMenuItem *item = Q_NULLPTR;
0311 
0312             QString title;
0313             QString tag = e.tagName();
0314             if ( tag == "title" ) {
0315                 title = e.text();
0316             }
0317             else if ( tag == "submenu" ) {
0318                 item = readXmlSubmenu(e);
0319             }
0320             else if ( tag == "separator" ) {
0321                 item = readXmlSeparator();
0322             }
0323             else { /* if ( tag == "menu" ) */
0324                 item = readXmlMenuentry(e);
0325             }
0326 
0327             submenuitem->setMenutitle(title);
0328             submenuitem->setText(0,title);
0329 
0330             if ( item ) {
0331                 submenuitem->addChild(item);
0332             }
0333             e = e.nextSiblingElement();
0334         }
0335     }
0336 
0337     return submenuitem;
0338 }
0339 
0340 // read tags for a standard menu item
0341 UserMenuItem *UserMenuTree::readXmlMenuentry(const QDomElement &element)
0342 {
0343     QString menutypename = element.attribute("type");
0344     UserMenuData::MenuType menutype = UserMenuData::xmlMenuType(menutypename);
0345 
0346     UserMenuItem *menuentryitem = new UserMenuItem(menutype, QString()) ;
0347 
0348     if ( element.hasChildNodes() ) {
0349         // default values
0350         QString title;
0351         QString plaintext;
0352         QString filename;
0353         QString parameter;
0354         QString icon;
0355         QString shortcut;
0356         bool needsSelection = false;
0357         bool useContextMenu = false;
0358         bool replaceSelection = false;
0359         bool selectInsertion = false;
0360         bool insertOutput = false;
0361 
0362         // read values
0363         QDomElement e = element.firstChildElement();
0364         while ( !e.isNull()) {
0365             QString tag = e.tagName();
0366             QString text = e.text();
0367 
0368             int index = UserMenuData::xmlMenuTag(tag);
0369             switch (index) {
0370             case  UserMenuData::XML_TITLE:
0371                 title = text;
0372                 break;
0373             case  UserMenuData::XML_PLAINTEXT:
0374                 plaintext = text;
0375                 break;
0376             case  UserMenuData::XML_FILENAME:
0377                 filename = text;
0378                 break;
0379             case  UserMenuData::XML_PARAMETER:
0380                 parameter = text;
0381                 break;
0382             case  UserMenuData::XML_ICON:
0383                 icon = text;
0384                 break;
0385             case  UserMenuData::XML_SHORTCUT:
0386                 shortcut = text;
0387                 break;
0388             case  UserMenuData::XML_NEEDSSELECTION:
0389                 needsSelection   = str2bool(text);
0390                 break;
0391             case  UserMenuData::XML_USECONTEXTMENU:
0392                 useContextMenu   = str2bool(text);
0393                 break;
0394             case  UserMenuData::XML_REPLACESELECTION:
0395                 replaceSelection = str2bool(text);
0396                 break;
0397             case  UserMenuData::XML_SELECTINSERTION:
0398                 selectInsertion  = str2bool(text);
0399                 break;
0400             case  UserMenuData::XML_INSERTOUTPUT:
0401                 insertOutput     = str2bool(text);
0402                 break;
0403             }
0404 
0405             e = e.nextSiblingElement();
0406         }
0407 
0408         // save values
0409         menuentryitem->setMenutitle(title);
0410 
0411         // add code newline
0412         plaintext = UserMenuData::decodeLineFeed(plaintext);
0413         menuentryitem->setPlaintext(plaintext);
0414 
0415         menuentryitem->setFilename(filename);
0416         menuentryitem->setParameter(parameter);
0417         if ( !icon.isEmpty() ) {
0418             menuentryitem->setMenuicon(icon);
0419             menuentryitem->setIcon(0,QIcon::fromTheme(icon));
0420         }
0421         if ( !shortcut.isEmpty() ) {
0422             QKeySequence seq = QKeySequence::fromString(shortcut,QKeySequence::PortableText);
0423             shortcut = seq.toString(QKeySequence::NativeText);
0424 
0425             menuentryitem->setShortcut(shortcut);
0426             menuentryitem->setText(1,shortcut);
0427         }
0428         menuentryitem->setNeedsSelection(needsSelection);
0429         menuentryitem->setUseContextMenu(useContextMenu);
0430         menuentryitem->setReplaceSelection(replaceSelection);
0431         menuentryitem->setSelectInsertion(selectInsertion);
0432         menuentryitem->setInsertOutput(insertOutput);
0433 
0434         menuentryitem->setText(0,title);
0435     }
0436 
0437     return menuentryitem;
0438 }
0439 
0440 
0441 ///////////////////////////// write XML //////////////////////////////
0442 
0443 bool UserMenuTree::writeXml(const QString &filename)
0444 {
0445     KILE_DEBUG_MAIN << "write xml file " << filename;
0446 
0447     QFile file(filename);
0448     if ( !file.open(QFile::WriteOnly | QFile::Text) ) {
0449         KMessageBox::error(this, i18n("File '%1' could not be opened to save the usermenu file.", filename));
0450         return false;
0451     }
0452 
0453     QXmlStreamWriter xmlWriter(&file);
0454     xmlWriter.setAutoFormatting(true);
0455     xmlWriter.setAutoFormattingIndent(2) ;
0456 
0457     xmlWriter.writeStartDocument();
0458     xmlWriter.writeStartElement("UserMenu");
0459 
0460     for (int i = 0; i < topLevelItemCount(); ++i) {
0461         writeXmlItem(&xmlWriter, dynamic_cast<UserMenuItem *>(topLevelItem(i)) );
0462     }
0463     xmlWriter.writeEndDocument();
0464 
0465     file.close();
0466     return true;
0467 }
0468 
0469 void UserMenuTree::writeXmlItem(QXmlStreamWriter *xml, UserMenuItem *item)
0470 {
0471     switch (item->menutype()) {
0472     case UserMenuData::Separator:
0473         writeXmlSeparator(xml);
0474         break;
0475     case UserMenuData::Submenu:
0476         writeXmlSubmenu(xml,item);
0477         break;
0478     default:
0479         writeXmlMenuentry(xml,item);
0480         break;
0481     }
0482 }
0483 
0484 // write xml tags for a standard menu item
0485 void UserMenuTree::writeXmlMenuentry(QXmlStreamWriter *xml, UserMenuItem *item)
0486 {
0487     int menutype = item->menutype();
0488     QString menutypename = UserMenuData::xmlMenuTypeName(menutype);
0489 
0490     xml->writeStartElement("menu");
0491     xml->writeAttribute("type", menutypename);
0492 
0493     QString menutitle = ( item->text(0) == EMPTY_MENUENTRY ) ? QString() : item->text(0);
0494     xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_TITLE),menutitle);
0495 
0496     if ( menutype == UserMenuData::Text ) {
0497         QString plaintext = item->plaintext();
0498         if ( !plaintext.isEmpty() ) {
0499             // encode newline characters
0500             plaintext = UserMenuData::encodeLineFeed(plaintext);
0501             xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_PLAINTEXT), plaintext);
0502         }
0503     }
0504     else { /* if ( menutype!=UserMenuData::FileContent || menutype==UserMenuData:Program) */
0505         // both types use a filename
0506         QString filename = item->filename();
0507         if ( !filename.isEmpty() ) {
0508             xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_FILENAME), filename);
0509 
0510             // but UserMenuItem::Program may have an additional parameter
0511             if ( menutype == UserMenuData::Program) {
0512                 QString parameter = item->parameter();
0513                 if ( !parameter.isEmpty() ) {
0514                     xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_PARAMETER), parameter);
0515                 }
0516             }
0517         }
0518     }
0519 
0520     QString icon = item->menuicon();
0521     if ( !icon.isEmpty() ) {
0522         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_ICON),icon);
0523     }
0524 
0525     QKeySequence seq = QKeySequence::fromString( item->shortcut(), QKeySequence::NativeText );
0526     if ( !seq.isEmpty() ) {
0527         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_SHORTCUT), seq.toString(QKeySequence::PortableText));
0528     }
0529 
0530     if ( item->needsSelection() ) {
0531         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_NEEDSSELECTION), "true");
0532     }
0533     if ( item->useContextMenu() ) {
0534         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_USECONTEXTMENU), "true");
0535     }
0536     if ( item->replaceSelection() ) {
0537         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_REPLACESELECTION), "true");
0538     }
0539     if ( item->selectInsertion() ) {
0540         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_SELECTINSERTION), "true");
0541     }
0542     if ( item->insertOutput() ) {
0543         xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_INSERTOUTPUT), "true");
0544     }
0545 
0546     xml->writeEndElement();
0547 }
0548 
0549 // write xml tags for a submenu
0550 void UserMenuTree::writeXmlSubmenu(QXmlStreamWriter *xml, UserMenuItem *item)
0551 {
0552     xml->writeStartElement("submenu");
0553 
0554     QString menutitle = item->text(0);
0555     if ( menutitle == EMPTY_MENUENTRY ) {
0556         menutitle.clear();
0557     }
0558     else if ( menutitle.right(LENGTH_SUBSTITUTE) == EMPTY_SUBMENU ) {
0559         menutitle = menutitle.left(menutitle.length()-LENGTH_SUBSTITUTE);
0560     }
0561     xml->writeTextElement(UserMenuData::xmlMenuTagName(UserMenuData::XML_TITLE),menutitle);
0562 
0563     for ( int i=0; i<item->childCount(); ++i ) {
0564         writeXmlItem(xml, dynamic_cast<UserMenuItem *>(item->child(i)) );
0565     }
0566 
0567     xml->writeEndElement();
0568 }
0569 
0570 // write xml tag for a separator
0571 void UserMenuTree::writeXmlSeparator(QXmlStreamWriter *xml)
0572 {
0573     xml->writeStartElement("separator");
0574     xml->writeEndElement();
0575 }
0576 
0577 ///////////////////////////// check menutree and set error codes //////////////////////////////
0578 
0579 // the complete menutree was build, now check for errors
0580 //  - empty menutitles
0581 //  - useless submenus without children
0582 //  - empty filenames, not existing or not executable files
0583 void UserMenuTree::setErrorCodes()
0584 {
0585     KILE_DEBUG_MAIN << "check menutree for errors and set error codes ...";
0586 
0587     for (int i = 0; i < topLevelItemCount(); ++i) {
0588         UserMenuItem *item = dynamic_cast<UserMenuItem *>(topLevelItem(i));
0589         UserMenuData::MenuType type = item->menutype();
0590 
0591         bool executable = ( type==UserMenuData::Program && isItemExecutable(item->filename()) );
0592         item->setModelData(executable);
0593 
0594         if ( type != UserMenuData::Separator ) {
0595             checkMenuTitle(item);
0596         }
0597         if ( type == UserMenuData::Submenu ) {
0598             checkSubmenu(item);
0599         }
0600     }
0601 }
0602 
0603 void UserMenuTree::checkMenuTitle(UserMenuItem *item)
0604 {
0605     if ( item->menutitle().isEmpty() ) {
0606         item->setText(0,EMPTY_MENUENTRY);
0607         KILE_DEBUG_MAIN << "empty menutitle changed to " << EMPTY_MENUENTRY;
0608     }
0609 }
0610 
0611 void UserMenuTree::checkSubmenu(UserMenuItem *item)
0612 {
0613     QString menutitle = item->menutitle();
0614     int children = item->childCount();
0615 
0616     if ( menutitle.isEmpty() ) {
0617         menutitle = EMPTY_MENUENTRY;
0618     }
0619     else if ( children == 0 ) {
0620         menutitle += EMPTY_SUBMENU;
0621     }
0622     item->setText(0,menutitle);
0623 
0624     for ( int i=0; i<children; ++i ) {
0625         UserMenuItem *child = dynamic_cast<UserMenuItem *>(item->child(i));
0626         child->setModelData();
0627         UserMenuData::MenuType type = child->menutype();
0628 
0629         if ( type != UserMenuData::Separator ) {
0630             checkMenuTitle(child);
0631         }
0632         if ( type == UserMenuData::Submenu ) {
0633             checkSubmenu(child);
0634         }
0635     }
0636 }
0637 
0638 ///////////////////////////// check menutree  //////////////////////////////
0639 
0640 // check for errors (true=no errors)
0641 bool UserMenuTree::errorCheck()
0642 {
0643     KILE_DEBUG_MAIN << "check menutree for errors ...";
0644 
0645     for (int i = 0; i < topLevelItemCount(); ++i) {
0646         UserMenuItem *item = dynamic_cast<UserMenuItem *>(topLevelItem(i));
0647         UserMenuData::MenuType type = item->menutype();
0648 
0649         if ( type != UserMenuData::Separator ) {
0650             if ( item->data(0,Qt::UserRole+2).toInt() != UserMenuItem::MODEL_ERROR_NONE ) {
0651                 return false;
0652             }
0653         }
0654 
0655         if ( type == UserMenuData::Submenu ) {
0656             if ( checkSubmenuError(item) == false ) {
0657                 return false;
0658             }
0659         }
0660     }
0661 
0662     return true;
0663 }
0664 
0665 bool UserMenuTree::checkSubmenuError(UserMenuItem *item)
0666 {
0667     int children = item->childCount();
0668     for ( int i=0; i<children; ++i ) {
0669         UserMenuItem *child = dynamic_cast<UserMenuItem *>(item->child(i));
0670         UserMenuData::MenuType type = child->menutype();
0671 
0672         if ( type != UserMenuData::Separator ) {
0673             if ( child->data(0,Qt::UserRole+2).toInt() != UserMenuItem::MODEL_ERROR_NONE ) {
0674                 return false;
0675             }
0676         }
0677 
0678         if ( type == UserMenuData::Submenu ) {
0679             if ( checkSubmenuError(child) == false ) {
0680                 return false;
0681             }
0682         }
0683     }
0684 
0685     return true;
0686 }
0687 
0688 ///////////////////////////// tree ops //////////////////////////////
0689 
0690 // insert a standard menu item
0691 bool UserMenuTree::insertMenuItem(QTreeWidgetItem *current, bool below)
0692 {
0693     QString menulabel = getMenuTitle(i18n("Please enter a label for this menu item:"));
0694     if ( menulabel.isEmpty() )  {
0695         return false;
0696     }
0697 
0698     if ( below ) {
0699         insertMenuItemBelow(current,UserMenuData::Text,menulabel);
0700     }
0701     else {
0702         insertMenuItemAbove(current,UserMenuData::Text,menulabel);
0703     }
0704     return true;
0705 }
0706 
0707 // insert a submenu item
0708 bool UserMenuTree::insertSubmenu(QTreeWidgetItem *current, bool below)
0709 {
0710     QString menulabel = getMenuTitle(i18n("Please enter a label for this submenu:"));
0711     if ( menulabel.isEmpty() )  {
0712         return false;
0713     }
0714 
0715     if ( below ) {
0716         insertMenuItemBelow(current,UserMenuData::Submenu,menulabel);
0717     }
0718     else {
0719         insertMenuItemAbove(current,UserMenuData::Submenu,menulabel);
0720     }
0721     return true;
0722 }
0723 
0724 // insert a separator item
0725 bool UserMenuTree::insertSeparator(QTreeWidgetItem *current, bool below)
0726 {
0727     if(below) {
0728         insertMenuItemBelow(current,UserMenuData::Separator, QString());
0729     }
0730     else {
0731         insertMenuItemAbove(current,UserMenuData::Separator, QString());
0732     }
0733     return true;
0734 }
0735 
0736 void UserMenuTree::insertMenuItemAbove(QTreeWidgetItem *current, UserMenuData::MenuType type, const QString &menulabel)
0737 {
0738     QTreeWidgetItem *parent = ( current ) ? current->parent() : Q_NULLPTR;
0739     int index = itemIndex(parent,current);
0740 
0741     UserMenuItem *item = new UserMenuItem(type,menulabel);
0742     insertItem(parent,index,item);
0743 
0744     item->setText(0,menulabel);
0745     setCurrentItem(item);
0746 }
0747 
0748 void UserMenuTree::insertMenuItemBelow(QTreeWidgetItem *current, UserMenuData::MenuType type, const QString &menulabel)
0749 {
0750     UserMenuItem *item;
0751     QTreeWidgetItem *parent = ( current ) ? current->parent() : Q_NULLPTR;
0752 
0753     if(!parent) {
0754         item = new UserMenuItem(this,current,type,menulabel);
0755     }
0756     else {
0757         item = new UserMenuItem(parent,current,type,menulabel);
0758     }
0759 
0760     item->setText(0,menulabel);
0761     setCurrentItem(item);
0762 }
0763 
0764 void UserMenuTree::insertIntoSubmenu(QTreeWidgetItem *current, UserMenuData::MenuType type)
0765 {
0766     QString menulabel;
0767     if ( type == UserMenuData::Text || type == UserMenuData::Submenu ) {
0768         menulabel = getMenuTitle(i18n("Please enter a label for this entry:"));
0769         if ( menulabel.isEmpty() ) {
0770             return;
0771         }
0772     }
0773 
0774     UserMenuItem *item = new UserMenuItem(type,menulabel);
0775     insertItem(current,0,item);
0776     setCurrentItem(item);
0777 }
0778 
0779 // delete an item
0780 void UserMenuTree::itemDelete(QTreeWidgetItem *current)
0781 {
0782     int children,index;
0783     QTreeWidgetItem *item, *selectitem;
0784     QTreeWidgetItem *parent = current->parent();
0785     if(!parent) {
0786         children = topLevelItemCount();
0787         index = indexOfTopLevelItem(current);
0788         if ( index < children-1 ) {
0789             selectitem = topLevelItem(index+1);
0790         }
0791         else if ( index > 0  ) {
0792             selectitem = topLevelItem(index-1);
0793         }
0794         else {
0795             selectitem = Q_NULLPTR;
0796         }
0797 
0798         item = takeTopLevelItem(index);
0799     }
0800     else {
0801         children = parent->childCount();
0802         index = parent->indexOfChild(current);
0803         if ( index < children-1 ) {
0804             selectitem = parent->child(index+1);
0805         }
0806         else if ( index > 0  ) {
0807             selectitem = parent->child(index-1);
0808         }
0809         else {
0810             selectitem = parent;
0811         }
0812 
0813         item = parent->takeChild(index);
0814     }
0815 
0816     delete item;
0817 
0818     if(selectitem) {
0819         setCurrentItem(selectitem);
0820     }
0821 }
0822 
0823 // move an item one position up
0824 void UserMenuTree::itemUp()
0825 {
0826     QTreeWidgetItem *current = currentItem();
0827     UserMenuItem *aboveitem = dynamic_cast<UserMenuItem *>(itemAbove(current));
0828     if (!aboveitem) {
0829         return;
0830     }
0831 
0832     bool expanded = current->isExpanded();
0833     blockSignals(true);
0834 
0835     QTreeWidgetItem *aboveparent = aboveitem->parent();
0836     int aboveindex = itemIndex(aboveparent,aboveitem);
0837 
0838     UserMenuItem *parent = dynamic_cast<UserMenuItem *>(current->parent());
0839     int index = itemIndex(parent,current);
0840     takeItem(parent,current);
0841 
0842     if ( parent!=aboveparent && index!=0 ) {
0843         aboveindex++;
0844     }
0845 
0846     if ( parent==aboveparent &&  aboveitem->menutype()==UserMenuData::Submenu ) {
0847         insertItem(aboveitem,0,current);
0848     }
0849     else {
0850         insertItem(aboveparent,aboveindex,current);
0851     }
0852 
0853     // update model data of old and new parent, if it has changed
0854     UserMenuItem *newparent = dynamic_cast<UserMenuItem *>(current->parent());
0855     if ( parent != newparent ) {
0856         if ( parent ) {
0857             parent->setModelData();
0858             parent->setText(0, parent->updateMenutitle());
0859         }
0860         if ( newparent ) {
0861             newparent->setModelData();
0862             newparent->setText(0, newparent->updateMenutitle());
0863         }
0864     }
0865 
0866     current->setExpanded(expanded);
0867     setCurrentItem(current);
0868     blockSignals(false);
0869 }
0870 
0871 // move an item one position down
0872 void UserMenuTree::itemDown()
0873 {
0874     QTreeWidgetItem *current = currentItem();
0875     bool expanded = current->isExpanded();
0876     blockSignals(true);
0877 
0878     // get all necessary parameter
0879     UserMenuItem *parent = dynamic_cast<UserMenuItem *>(current->parent());
0880     int index = itemIndex(parent,current);
0881     int children = numChildren(parent);
0882 
0883     // successor exists?
0884     if ( index < children-1 ) {
0885         UserMenuItem *successor = dynamic_cast<UserMenuItem *>( itemAtIndex(parent,index+1) );
0886         takeItem(parent,current);
0887         if ( successor->menutype() == UserMenuData::Submenu ) {
0888             successor->insertChild(0,current);
0889         }
0890         else {
0891             insertItem(parent,index+1,current);
0892         }
0893     }
0894     else if ( parent ) {
0895         QTreeWidgetItem *grandparent = parent->parent();
0896         int parentindex = itemIndex(grandparent,parent);
0897         takeItem(parent,current);
0898         insertItem(grandparent,parentindex+1,current);
0899     }
0900 
0901     // update model data of old and new parent, if it has changed
0902     UserMenuItem *newparent = dynamic_cast<UserMenuItem *>(current->parent());
0903     if ( parent != newparent ) {
0904         if ( parent ) {
0905             parent->setModelData();
0906             parent->setText(0, parent->updateMenutitle());
0907         }
0908         if ( newparent ) {
0909             newparent->setModelData();
0910             newparent->setText(0, newparent->updateMenutitle());
0911         }
0912     }
0913 
0914     current->setExpanded(expanded);
0915     setCurrentItem(current);
0916     blockSignals(false);
0917 }
0918 
0919 ////////////////////////////// delete tree //////////////////////////////
0920 
0921 // delete the whole menutree
0922 void  UserMenuTree::deleteMenuTree()
0923 {
0924     if ( KMessageBox::questionTwoActions(this, i18n("Do you really want to clear the complete menutree?"),
0925                                          i18n("Clear menutree"),
0926                                          KStandardGuiItem::clear(), KStandardGuiItem::cancel()) == KMessageBox::PrimaryAction) {
0927         blockSignals(true);
0928         clear();
0929         blockSignals(false);
0930     }
0931 }
0932 
0933 ////////////////////////////// info //////////////////////////////
0934 
0935 // prepare info for en item with errors
0936 void  UserMenuTree::itemInfo(UserMenuItem *item)
0937 {
0938     int error = item->data(0,Qt::UserRole+2).toInt();
0939 
0940     QStringList list;
0941     if ( error & UserMenuItem::MODEL_ERROR_EMPTY ) {
0942         list << i18n("This menuitem has no title.");
0943     }
0944 
0945     if ( error & UserMenuItem::MODEL_ERROR_SUBMENU ) {
0946         list << i18n("This submenu item is useless without children.");
0947     }
0948 
0949     if ( error & UserMenuItem::MODEL_ERROR_TEXT ) {
0950         list << i18n("This item offers no text to insert.");
0951     }
0952 
0953     if ( error & UserMenuItem::MODEL_ERROR_FILE_EMPTY ) {
0954         list << i18n("No file is given for this task.");
0955     }
0956 
0957     if ( error & UserMenuItem::MODEL_ERROR_FILE_EXIST ) {
0958         list << i18n("The file for this item does not exist.");
0959     }
0960 
0961     if ( error & UserMenuItem::MODEL_ERROR_FILE_EXECUTABLE ) {
0962         list << i18n("The file for this item is not executable.");
0963     }
0964 
0965     QString msg = i18n("<p><strong>Error:</strong>");
0966     if ( list.size() == 1 ) {
0967         msg += "<br/><br/>" + list[0] + "</p>";
0968     }
0969     else {
0970         msg += "<ul>";
0971         foreach ( const QString &s, list ) {
0972             msg += "<li>&nbsp;" + s + "</li>";
0973         }
0974         msg += "</ul></p>";
0975     }
0976 
0977     KMessageBox::information(this, msg, i18n("Menutree Error"));
0978 }
0979 
0980 ////////////////////////////// auxiliary //////////////////////////////
0981 
0982 int UserMenuTree::itemIndex(QTreeWidgetItem *parent, QTreeWidgetItem *item)
0983 {
0984     return ( parent ) ? parent->indexOfChild(item) : indexOfTopLevelItem(item);
0985 }
0986 
0987 QTreeWidgetItem *UserMenuTree::itemAtIndex(QTreeWidgetItem *parent, int index)
0988 {
0989     return ( parent ) ? parent->child(index) : topLevelItem(index);
0990 }
0991 
0992 int UserMenuTree::numChildren(QTreeWidgetItem *parent)
0993 {
0994     return ( parent ) ? parent->childCount() : topLevelItemCount();
0995 }
0996 
0997 void UserMenuTree::insertItem(QTreeWidgetItem *parent, int index, QTreeWidgetItem *item)
0998 {
0999     if ( parent ) {
1000         parent->insertChild(index,item);
1001     }
1002     else {
1003         insertTopLevelItem(index,item);
1004     }
1005 }
1006 
1007 void UserMenuTree::takeItem(QTreeWidgetItem *parent, QTreeWidgetItem *item)
1008 {
1009     if ( parent ) {
1010         int index = parent->indexOfChild(item);
1011         parent->takeChild(index);
1012     }
1013     else {
1014         int index = indexOfTopLevelItem(item);
1015         takeTopLevelItem(index);
1016     }
1017 }
1018 
1019 bool UserMenuTree::str2bool(const QString &value)
1020 {
1021     return ( value == "true" );
1022 }
1023 
1024 
1025 QString UserMenuTree::getMenuTitle(const QString &title)
1026 {
1027     bool ok;
1028     QString value = QInputDialog::getText(this, i18n("Name"), title, QLineEdit::Normal, QString(), &ok);
1029     return ( ok ) ? value : QString();
1030 
1031 }
1032 
1033 
1034 }
1035