File indexing completed on 2024-04-28 16:26:30
0001 /*********************************************************************************** 0002 Copyright (C) 2011-2012 by Holger Danielsson (holger.danielsson@versanet.de) 0003 (C) 2017-2019 by Michel Ludwig (michel.ludwig@kdemail.net) 0004 ***********************************************************************************/ 0005 0006 /*************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 #include "usermenu/usermenu.h" 0016 0017 #include <QFile> 0018 #include <QRegExp> 0019 0020 #include <QTemporaryFile> 0021 #include <KXMLGUIFactory> 0022 #include <QMenuBar> 0023 #include <QAction> 0024 #include <KMessageBox> 0025 #include <QFileDialog> 0026 0027 #include "kileactions.h" 0028 #include "editorextension.h" 0029 #include "kileviewmanager.h" 0030 0031 #include "kileconfig.h" 0032 #include "kiledebug.h" 0033 #include "utilities.h" 0034 0035 0036 namespace KileMenu { 0037 0038 // The UserMenu uses six values/data structures: 0039 // 0040 // - getUserMenu(): the menu with its entries/actions itself (QMenu *) 0041 // (actions for menu items are named 'useraction-n', where n is a 0042 // number starting at 0. It is also used as index for the m_menudata list.) 0043 // 0044 // - m_menudata: a list, containing all info for menu item (QList<UserMenuData>) 0045 // 0046 // - m_actioncollection: KActionCollection of KileMainWindow (KActionCollection *) 0047 // 0048 // - m_actionlist: a list with all actions of the menu (QList<QAction *>) 0049 // 0050 // - m_actionlistContextMenu: a list with all actions of the context menu for selected text (QList<QAction *>) 0051 // 0052 // - a menu is defined in an xml file, which is placed in KileUtilities::locate(QStandardPaths::AppDataLocation, "usermenu", QStandardPaths::LocateDirectory) 0053 0054 UserMenu::UserMenu(KileInfo *ki, QObject *receiver) 0055 : m_ki(ki), m_receiver(receiver), m_proc(Q_NULLPTR) 0056 { 0057 KXmlGuiWindow *mainwindow = m_ki->mainWindow(); 0058 m_actioncollection = mainwindow->actionCollection(); 0059 0060 // add actions and menu entries 0061 m_wizardAction1 = new QAction(this); 0062 m_wizardAction1->setSeparator(true); 0063 m_wizardAction2 = createAction("wizard_usermenu"); 0064 0065 m_latexAction1 = new QAction(this); 0066 m_latexAction1->setSeparator(true); 0067 m_latexAction2 = createAction("wizard_usermenu2"); 0068 0069 m_latexMenuEntry = new QMenu(i18n("User Menu")); 0070 m_latexMenuEntry->setObjectName("usermenu-submenu"); 0071 0072 addSpecialActionsToMenus(); 0073 0074 // look for an existing menufile: 0075 // if filename matches 'basename.ext' then the file is placed in 'KILE-LOCAL-DIR/usermenu' directory 0076 m_currentXmlFile = KileConfig::userMenuFile(); 0077 if ( !m_currentXmlFile.isEmpty() ) { 0078 if ( !m_currentXmlFile.contains("/") ) { 0079 m_currentXmlFile = KileUtilities::locate(QStandardPaths::AppDataLocation, "usermenu", QStandardPaths::LocateDirectory) + m_currentXmlFile; 0080 } 0081 0082 if ( QFile(m_currentXmlFile).exists() ) { 0083 KILE_DEBUG_MAIN << "install menufile: " << m_currentXmlFile; 0084 installXml(m_currentXmlFile); 0085 } 0086 else { 0087 m_currentXmlFile.clear(); 0088 } 0089 } 0090 0091 updateUsermenuPosition(); 0092 } 0093 0094 UserMenu::~UserMenu() 0095 { 0096 delete m_proc; 0097 } 0098 0099 bool UserMenu::isEmpty() 0100 { 0101 return (getMenuItem()->actions().size() == 0); 0102 } 0103 /////////////////////// install usermenu////////////////////////////// 0104 0105 QAction *UserMenu::createAction(const QString &name) 0106 { 0107 QAction *action = m_actioncollection->addAction(name, m_receiver, SLOT(quickUserMenuDialog())); 0108 action->setText(i18n("Edit User Menu")); 0109 action->setIcon(QIcon::fromTheme("wizard_usermenu")); 0110 return action; 0111 } 0112 0113 void UserMenu::addSpecialActionsToMenus() 0114 { 0115 KXmlGuiWindow *mainwindow = m_ki->mainWindow(); 0116 0117 // update wizard menu 0118 QMenu *wizard_menu = dynamic_cast<QMenu*>(mainwindow->guiFactory()->container("wizard", mainwindow)); 0119 wizard_menu->addAction(m_wizardAction1); 0120 wizard_menu->addAction(m_wizardAction2); 0121 0122 // update latex menu 0123 QMenu *latex_menu = dynamic_cast<QMenu*>(mainwindow->guiFactory()->container("menu_latex", mainwindow)); 0124 latex_menu->addAction(m_latexAction1); 0125 latex_menu->addAction(m_latexAction2); 0126 latex_menu->addMenu(m_latexMenuEntry); 0127 } 0128 0129 void UserMenu::updateUsermenuPosition() 0130 { 0131 // and set the new one 0132 const bool show = !isEmpty() && m_ki->viewManager()->currentTextView(); 0133 if(getUserMenuLocation() == StandAloneLocation) { 0134 setStandAloneMenuVisible(true, show); 0135 } 0136 else { 0137 setStandAloneMenuVisible(false, show); 0138 } 0139 } 0140 0141 void UserMenu::setStandAloneMenuVisible(bool state, bool show) 0142 { 0143 m_wizardAction1->setVisible(state); 0144 m_wizardAction2->setVisible(state); 0145 0146 m_latexAction1->setVisible(!state); 0147 m_latexAction2->setVisible(!state); 0148 0149 m_latexMenuEntry->menuAction()->setVisible(!state && show); 0150 0151 KXmlGuiWindow *mainwindow = m_ki->mainWindow(); 0152 QMenu *standAloneMenu = dynamic_cast<QMenu*>(mainwindow->guiFactory()->container("menu_usermenu", mainwindow)); 0153 if(standAloneMenu) { 0154 standAloneMenu->menuAction()->setVisible(state && show); 0155 } 0156 } 0157 0158 ///////////////////////////// clear all data ////////////////////////////// 0159 0160 // clear all lists and data for an existing usermenu 0161 void UserMenu::clear() 0162 { 0163 // clear usermenu and menudata 0164 if(getMenuItem()) { 0165 getMenuItem()->clear(); 0166 } 0167 m_menudata.clear(); 0168 0169 // remove all actions from actioncollection 0170 for(QAction *action : m_actionlist) { 0171 m_actioncollection->removeAction(action); 0172 } 0173 0174 // clear actionlists 0175 m_actionlist.clear(); 0176 m_actionlistContextMenu.clear(); 0177 } 0178 0179 ///////////////////////////// update GUI ////////////////////////////// 0180 0181 // repopulate the user menu and show it at the desired location 0182 void UserMenu::updateGUI() 0183 { 0184 KILE_DEBUG_MAIN << "updating usermenu ..."; 0185 0186 addSpecialActionsToMenus(); // adding actions twice has no effect 0187 0188 // like installXmlFile(), but without updating KileConfig::userMenuFile 0189 // first clear old usermenu, menudata, actions and actionlists 0190 clear(); 0191 0192 // then install 0193 if(!m_currentXmlFile.isEmpty() && installXml(m_currentXmlFile)) { 0194 // add changed context menu to all existing views 0195 KileView::Manager* viewManager = m_ki->viewManager(); 0196 int views = viewManager->textViewCount(); 0197 for ( int i=0; i<views; ++i ) { 0198 viewManager->installContextMenu( viewManager->textView(i) ); 0199 } 0200 } 0201 0202 updateUsermenuPosition(); 0203 } 0204 0205 ///////////////////////////// update key bindings ////////////////////////////// 0206 0207 // shortcut dialog was called, so key bindings may have been changed 0208 void UserMenu::updateKeyBindings() 0209 { 0210 if ( m_currentXmlFile.isEmpty() && !QFile(m_currentXmlFile).exists() ) { 0211 return; 0212 } 0213 0214 // new key bindings are found in kileui.rc (ActionProperties) 0215 // remove them, as they will be written into usermenu xml file 0216 removeActionProperties(); 0217 0218 // update xml file of current usermenu 0219 updateXmlFile(m_currentXmlFile); 0220 } 0221 0222 QMenu* UserMenu::getMenuItem() 0223 { 0224 0225 if(getUserMenuLocation() == StandAloneLocation) { 0226 KParts::MainWindow *mainWindow = m_ki->mainWindow(); 0227 return dynamic_cast<QMenu*>(mainWindow->guiFactory()->container("menu_usermenu", mainWindow)); 0228 } 0229 else { 0230 return m_latexMenuEntry; 0231 } 0232 } 0233 0234 void UserMenu::removeActionProperties() 0235 { 0236 QString xmlfile = "kileui.rc"; 0237 QString xml(KXMLGUIFactory::readConfigFile(xmlfile)); 0238 if ( xml.isEmpty() ) { 0239 KILE_DEBUG_MAIN << "STOP: xmlfile not found: " << xmlfile; 0240 return; 0241 } 0242 0243 QDomDocument doc; 0244 doc.setContent( xml ); 0245 0246 // process XML data in section 'ActionProperties' 0247 QDomElement actionPropElement = KXMLGUIFactory::actionPropertiesElement( doc ); 0248 if ( actionPropElement.isNull() ) { 0249 KILE_DEBUG_MAIN << "QDomElement actionPropertiesElement not found "; 0250 return; 0251 } 0252 0253 // search for all actions of the user-defined UserMenu 0254 KILE_DEBUG_MAIN << "QDomElement actionPropertiesElement found "; 0255 bool changed = false; 0256 QRegExp re("useraction-(\\d+)$"); 0257 QDomElement e = actionPropElement.firstChildElement(); 0258 while(!e.isNull()) { 0259 QString tag = e.tagName(); 0260 if(tag != "Action") { 0261 continue; 0262 } 0263 0264 QString shortcut = e.attribute("shortcut"); 0265 QString name = e.attribute("name"); 0266 0267 QDomElement removeElement; 0268 if ( re.indexIn(name) == 0) { 0269 int index = re.cap(1).toInt(); 0270 KILE_DEBUG_MAIN << "action property was changed: old=" << m_menudata[index].shortcut << " new=" << name << " actionIndex=" << index; 0271 removeElement = e; 0272 changed = true; 0273 } 0274 0275 e = e.nextSiblingElement(); 0276 0277 // finally delete element 0278 if ( !removeElement.isNull() ) { 0279 KILE_DEBUG_MAIN << "remove ActionProperty: shortcut=" << shortcut << " name=" << name; 0280 actionPropElement.removeChild(removeElement); 0281 } 0282 } 0283 0284 // Write back to XML file 0285 if ( changed ) { 0286 KXMLGUIFactory::saveConfigFile(doc,xmlfile); 0287 } 0288 } 0289 0290 ///////////////////////////// update action properties (shortcuts) ////////////////////////////// 0291 0292 // Calling m_mainWindow->guiFactory()->refreshActionProperties() in kile.cpp removes all 0293 // user-defined action shortcuts and icons. Here they will be refreshed again. 0294 void UserMenu::refreshActionProperties() 0295 { 0296 KILE_DEBUG_MAIN << "refresh action properties"; 0297 0298 QRegExp re("useraction-(\\d+)$"); 0299 foreach ( QAction *action, m_actionlist ) { 0300 if ( re.indexIn(action->objectName()) == 0 ) { 0301 int actionIndex = re.cap(1).toInt(); 0302 if ( !m_menudata[actionIndex].icon.isEmpty() ) { 0303 action->setIcon( QIcon::fromTheme(m_menudata[actionIndex].icon) ); 0304 } 0305 if ( !m_menudata[actionIndex].shortcut.isEmpty() ) { 0306 action->setShortcut( QKeySequence(m_menudata[actionIndex].shortcut,QKeySequence::NativeText) ); 0307 } 0308 } 0309 } 0310 } 0311 0312 // Before calling usermenu dialog, all user-defined action shortcuts must be removed, 0313 // or the dialog will give a lot of warnings. All shortcuts (even if changed) in the usermenu 0314 // will be refreshed again, when the dialog is finished 0315 void UserMenu::removeShortcuts() 0316 { 0317 foreach ( QAction *action, m_actionlist ) { 0318 action->setShortcut( QKeySequence() ); 0319 } 0320 } 0321 0322 ///////////////////////////// install/remove xml ////////////////////////////// 0323 0324 // call from the menu: no xml file given 0325 void UserMenu::installXmlMenufile() 0326 { 0327 KILE_DEBUG_MAIN << "install xml file with QFileDialog::getOpenFileName"; 0328 0329 QString directory = selectUserMenuDir(); 0330 QString filter = i18n("User Menu Files (*.xml)"); 0331 0332 QString filename = QFileDialog::getOpenFileName(m_ki->mainWindow(), i18n("Select Menu File"), directory, filter); 0333 if(filename.isEmpty()) { 0334 return; 0335 } 0336 0337 if( !QFile::exists(filename) ) { 0338 KMessageBox::error(m_ki->mainWindow(), i18n("File '%1' does not exist.", filename)); 0339 } 0340 else { 0341 installXmlFile(filename); 0342 } 0343 } 0344 0345 // SIGNAL from usermenu dialog: install new usermenu (xml file given) 0346 // 0347 // use 'basename.ext' if the file is placed in 'KILE-LOCAL-DIR/usermenu' directory and full filepath else 0348 void UserMenu::installXmlFile(const QString &filename) 0349 { 0350 KILE_DEBUG_MAIN << "install xml file" << filename; 0351 0352 // clear old usermenu, menudata, actions and actionlists 0353 clear(); 0354 0355 if ( installXml(filename) ) { 0356 // update current xml filename (with path) 0357 m_currentXmlFile = filename; 0358 0359 // save xml file in config (with or without path) 0360 QString xmlfile = filename; 0361 QString dir = KileUtilities::locate(QStandardPaths::AppDataLocation, "usermenu", QStandardPaths::LocateDirectory); 0362 if ( filename.startsWith(dir) ) { 0363 QString basename = filename.right( filename.length()-dir.length() ); 0364 if ( !basename.isEmpty() && !basename.contains("/") ) { 0365 xmlfile = basename; 0366 } 0367 } 0368 KileConfig::setUserMenuFile(xmlfile); 0369 emit (updateStatus()); 0370 0371 // add changed context menu to all existing views 0372 KileView::Manager* viewManager = m_ki->viewManager(); 0373 int views = viewManager->textViewCount(); 0374 for ( int i=0; i<views; ++i ) { 0375 viewManager->installContextMenu( viewManager->textView(i) ); 0376 } 0377 } 0378 } 0379 0380 void UserMenu::removeXmlFile() 0381 { 0382 KILE_DEBUG_MAIN << "remove xml file"; 0383 0384 clear(); 0385 m_currentXmlFile.clear(); 0386 0387 KileConfig::setUserMenuFile(m_currentXmlFile); 0388 emit (updateStatus()); 0389 } 0390 0391 ///////////////////////////// install usermenu from XML ////////////////////////////// 0392 0393 // pre: usermenu is already cleared 0394 bool UserMenu::installXml(const QString &filename) 0395 { 0396 KILE_DEBUG_MAIN << "install: start"; 0397 0398 QMenu *userMenu = getMenuItem(); 0399 0400 if(!userMenu) { 0401 KILE_DEBUG_MAIN << "Hmmmm: found no usermenu"; 0402 return false; 0403 } 0404 0405 // read content of xml file 0406 QDomDocument doc("UserMenu"); 0407 QFile file(filename); 0408 if ( !file.open(QFile::ReadOnly | QFile::Text) ) { 0409 // TODO KMessageBox 0410 KILE_DEBUG_MAIN << "STOP: can't open xml file " << filename; 0411 return false; 0412 } 0413 0414 if( !doc.setContent( &file ) ) { 0415 file.close(); 0416 return false; 0417 } 0418 file.close(); 0419 0420 KILE_DEBUG_MAIN << "parse xml ..."; 0421 m_actionsContextMenu = 0; 0422 0423 // parse toplevelitems 0424 int actionnumber = 0; 0425 QDomElement root = doc.documentElement(); 0426 QDomElement e = root.firstChildElement(); 0427 while ( !e.isNull()) { 0428 QString tag = e.tagName(); 0429 0430 if ( tag=="submenu" || tag=="separator") { 0431 if ( tag == "submenu" ) { 0432 installXmlSubmenu(e, userMenu, actionnumber); 0433 } 0434 else { /* tag=="separator" */ 0435 userMenu->addSeparator(); 0436 } 0437 0438 // try to get some structure into to the context menu 0439 if ( m_actionsContextMenu > 0 ) { 0440 m_actionlistContextMenu.append(Q_NULLPTR); 0441 m_actionsContextMenu = 0; 0442 } 0443 } 0444 else { /* if ( tag == "menu" ) */ 0445 installXmlMenuentry(e, userMenu, actionnumber); 0446 } 0447 0448 e = e.nextSiblingElement(); 0449 } 0450 KILE_DEBUG_MAIN << "install: finished "; 0451 0452 return true; 0453 } 0454 0455 // install a submenu item 0456 void UserMenu::installXmlSubmenu(const QDomElement &element, QMenu *parentmenu, int &actionnumber) 0457 { 0458 QMenu *submenu = parentmenu->addMenu(QString()); 0459 0460 if ( element.hasChildNodes() ) { 0461 QDomElement e = element.firstChildElement(); 0462 while ( !e.isNull()) { 0463 0464 QString tag = e.tagName(); 0465 if ( tag == "title" ) { 0466 QString title = e.text(); 0467 submenu->setTitle(title); 0468 } 0469 else if ( tag == "submenu" ) { 0470 installXmlSubmenu(e,submenu,actionnumber); 0471 } 0472 else if ( tag == "separator" ) { 0473 submenu->addSeparator(); 0474 } 0475 else { /* if ( tag == "menu" ) */ 0476 installXmlMenuentry(e,submenu,actionnumber); 0477 } 0478 0479 e = e.nextSiblingElement(); 0480 } 0481 } 0482 } 0483 0484 // install a standard menu item 0485 void UserMenu::installXmlMenuentry(const QDomElement &element, QMenu *parentmenu, int &actionnumber) 0486 { 0487 UserMenuData menudata; 0488 0489 menudata.menutype = UserMenuData::xmlMenuType( element.attribute("type") ); 0490 0491 // read values 0492 if ( element.hasChildNodes() ) { 0493 QDomElement e = element.firstChildElement(); 0494 while ( !e.isNull()) { 0495 QString tag = e.tagName(); 0496 QString text = e.text(); 0497 0498 int index = UserMenuData::xmlMenuTag(tag); 0499 switch (index) { 0500 case UserMenuData::XML_TITLE: 0501 menudata.menutitle = text; 0502 break; 0503 case UserMenuData::XML_PLAINTEXT: 0504 menudata.text = UserMenuData::decodeLineFeed(text); 0505 break; 0506 case UserMenuData::XML_FILENAME: 0507 menudata.filename = text; 0508 break; 0509 case UserMenuData::XML_PARAMETER: 0510 menudata.parameter = text; 0511 break; 0512 case UserMenuData::XML_ICON: 0513 menudata.icon = text; 0514 break; 0515 case UserMenuData::XML_SHORTCUT: 0516 menudata.shortcut = text; 0517 break; 0518 case UserMenuData::XML_NEEDSSELECTION: 0519 menudata.needsSelection = str2bool(text); 0520 break; 0521 case UserMenuData::XML_USECONTEXTMENU: 0522 menudata.useContextMenu = str2bool(text); 0523 break; 0524 case UserMenuData::XML_REPLACESELECTION: 0525 menudata.replaceSelection = str2bool(text); 0526 break; 0527 case UserMenuData::XML_SELECTINSERTION: 0528 menudata.selectInsertion = str2bool(text); 0529 break; 0530 case UserMenuData::XML_INSERTOUTPUT: 0531 menudata.insertOutput = str2bool(text); 0532 break; 0533 } 0534 0535 e = e.nextSiblingElement(); 0536 } 0537 } 0538 0539 // add menu item, if its title is not empty 0540 if ( !menudata.menutitle.isEmpty() ) { 0541 0542 QAction *action = m_actioncollection->addAction(QString("useraction-%1").arg(actionnumber), this, SLOT(slotUserMenuAction()) ); 0543 if ( action ) { 0544 action->setText(menudata.menutitle); 0545 0546 if ( !menudata.icon.isEmpty() ) { 0547 action->setIcon( QIcon::fromTheme(menudata.icon) ); 0548 } 0549 0550 if ( !menudata.shortcut.isEmpty() ) { 0551 action->setShortcut( QKeySequence(menudata.shortcut,QKeySequence::PortableText) ); 0552 } 0553 0554 parentmenu->addAction(action); 0555 m_menudata.append(menudata); 0556 m_actionlist.append(action); 0557 if ( menudata.useContextMenu ) { 0558 m_actionlistContextMenu.append(action); 0559 m_actionsContextMenu++; 0560 } 0561 0562 actionnumber++; 0563 } 0564 } 0565 } 0566 0567 ///////////////////////////// update XML file ////////////////////////////// 0568 0569 // key bindings dialog was called, so the usermenu xml file must be updated, if one of these actions was changed 0570 // pre: xml file exists 0571 void UserMenu::updateXmlFile(const QString &filename) 0572 { 0573 KILE_DEBUG_MAIN << "update xml file: " << filename; 0574 0575 // read content of xml file 0576 QDomDocument doc("UserMenu"); 0577 QFile file(filename); 0578 file.open(QFile::ReadOnly | QFile::Text); 0579 doc.setContent(&file); 0580 file.close(); 0581 0582 KILE_DEBUG_MAIN << "parse xml ..."; 0583 0584 // parse toplevelitems 0585 bool changed = false; 0586 int actionnumber = 0; 0587 QDomElement root = doc.documentElement(); 0588 QDomElement e = root.firstChildElement(); 0589 while ( !e.isNull()) { 0590 QString tag = e.tagName(); 0591 if ( tag == "submenu" ) { 0592 changed = changed || updateXmlSubmenu(doc,e,actionnumber); 0593 } 0594 else if ( tag == "menu" ) { 0595 changed = changed || updateXmlMenuentry(doc,e,actionnumber); 0596 } 0597 e = e.nextSiblingElement(); 0598 } 0599 KILE_DEBUG_MAIN << "update finished "; 0600 0601 if ( changed ) { 0602 KILE_DEBUG_MAIN << "found changes, so write updated xml file "; 0603 QFile outfile(filename); 0604 outfile.open(QFile::WriteOnly | QFile::Text); 0605 QTextStream stream(&outfile); 0606 doc.save(stream,3); 0607 outfile.close(); 0608 } 0609 } 0610 0611 // install a submenu item 0612 bool UserMenu::updateXmlSubmenu(QDomDocument &doc, QDomElement &element, int &actionnumber) 0613 { 0614 bool changed = false; 0615 0616 if ( element.hasChildNodes() ) { 0617 QDomElement e = element.firstChildElement(); 0618 while ( !e.isNull()) { 0619 QString tag = e.tagName(); 0620 if ( tag == "submenu" ) { 0621 changed = changed || updateXmlSubmenu(doc,e,actionnumber); 0622 } 0623 else if ( tag == "menu" ) { 0624 changed = changed || updateXmlMenuentry(doc,e,actionnumber); 0625 } 0626 0627 e = e.nextSiblingElement(); 0628 } 0629 } 0630 return changed; 0631 } 0632 0633 // install a standard menu item 0634 bool UserMenu::updateXmlMenuentry(QDomDocument &doc, QDomElement &element, int &actionnumber) 0635 { 0636 bool changed = false; 0637 0638 // read values 0639 if ( element.hasChildNodes() ) { 0640 QDomElement oldElement; 0641 QDomElement e = element.firstChildElement(); 0642 while ( !e.isNull()) { 0643 QString tag = e.tagName(); 0644 0645 if ( UserMenuData::xmlMenuTag(tag) == UserMenuData::XML_SHORTCUT) { 0646 oldElement = e; 0647 //oldText = e.text(); value not needed, is also in m_menudata[] 0648 } 0649 0650 e = e.nextSiblingElement(); 0651 } 0652 0653 // keybindings dialog has already updated all actions 0654 QString currentShortcut = m_actionlist[actionnumber]->shortcut().toString(QKeySequence::PortableText); 0655 if ( currentShortcut != m_menudata[actionnumber].shortcut ) { 0656 // an existing shortcut always needs a new QDomElement 0657 if ( !currentShortcut.isEmpty() ) { 0658 // create element with new shortcut 0659 QDomElement newElement = doc.createElement( UserMenuData::xmlMenuTagName(UserMenuData::XML_SHORTCUT) ); 0660 QDomText newText = doc.createTextNode(currentShortcut); 0661 newElement.appendChild(newText); 0662 0663 // replace existing node with new node 0664 if ( !oldElement.isNull() ) { 0665 element.replaceChild(newElement,oldElement); 0666 } 0667 // or insert a new node 0668 else { 0669 element.appendChild(newElement); 0670 } 0671 } 0672 // or delete an existing QDomElement 0673 else { 0674 element.removeChild(oldElement); 0675 } 0676 changed = true; 0677 } 0678 } 0679 0680 actionnumber++; 0681 return changed; 0682 } 0683 0684 ////////////////////////////// load dir for xml files (static) ////////////////////////////// 0685 0686 // - start with search for xml files in the local directory 0687 // - if no files are present, but in global directory, start with global 0688 // - if not a single file was found: back to local directory 0689 0690 QString UserMenu::selectUserMenuDir() 0691 { 0692 QStringList dirs = KileUtilities::locateAll(QStandardPaths::AppDataLocation, "usermenu", QStandardPaths::LocateDirectory); 0693 if ( dirs.size() < 2 ) { 0694 return dirs.at(0); 0695 } 0696 0697 QStringList namefilter = QStringList() << "*.xml"; 0698 QString localDirName = dirs.at(0); 0699 QDir localDir = QDir(localDirName); 0700 QStringList localList = localDir.entryList (namefilter,QDir::Files | QDir::Readable); 0701 if ( localList.size() > 0 ) { 0702 return localDirName; 0703 } 0704 0705 QDir globalDir = QDir(dirs.at(1)); 0706 QStringList globalList = globalDir.entryList (namefilter,QDir::Files | QDir::Readable); 0707 return ( globalList.size() > 0 ) ? dirs.at(1) : localDirName; 0708 } 0709 0710 ////////////////////////////// execUserMenuAction ////////////////////////////// 0711 0712 // an action was called from the usermenu: 0713 // - identify action 0714 // - find textview 0715 // - execute action 0716 void UserMenu::slotUserMenuAction() 0717 { 0718 KILE_DEBUG_MAIN << "want to start an action from usermenu ..."; 0719 0720 QAction *action = dynamic_cast<QAction *>(sender()); 0721 if ( !action ) { 0722 return; 0723 } 0724 0725 QString actionName = action->objectName(); 0726 KILE_DEBUG_MAIN << "action name: " << actionName << "classname=" << action->metaObject()->className(); 0727 0728 QRegExp re("useraction-(\\d+)$"); 0729 if ( re.indexIn(actionName) != 0) { 0730 KILE_DEBUG_MAIN << "STOP: found wrong action name: " << actionName; 0731 return; 0732 } 0733 0734 bool ok; 0735 int actionIndex = re.cap(1).toInt(&ok); 0736 if ( actionIndex < 0 || actionIndex >= m_menudata.size() ) { 0737 KILE_DEBUG_MAIN << "STOP: invalid action (range error): " << actionIndex << " list size: " << m_menudata.size(); 0738 return; 0739 } 0740 0741 // check view and action requirements 0742 KTextEditor::View *view = m_ki->viewManager()->currentTextView(); 0743 0744 if ( !view ) { 0745 return; 0746 } 0747 0748 if ( !view->selection() && m_menudata[actionIndex].needsSelection ) { 0749 return; 0750 } 0751 0752 UserMenuData::MenuType type = m_menudata[actionIndex].menutype; 0753 0754 if ( type == UserMenuData::Text ) { 0755 execActionText(view,m_menudata[actionIndex]); 0756 } 0757 else if ( type == UserMenuData::FileContent ) { 0758 execActionFileContent(view,m_menudata[actionIndex]); 0759 } 0760 else if ( type == UserMenuData::Program ) { 0761 execActionProgramOutput(view,m_menudata[actionIndex]); 0762 } 0763 else { 0764 KILE_DEBUG_MAIN << "STOP: unknown action type: " << type; 0765 } 0766 } 0767 0768 ////////////////////////////// execActionText ////////////////////////////// 0769 0770 // execute an action: insert text 0771 void UserMenu::execActionText(KTextEditor::View *view, const UserMenuData &menudata) 0772 { 0773 KILE_DEBUG_MAIN << "want to insert text ... "; 0774 insertText(view, menudata.text, menudata.replaceSelection, menudata.selectInsertion); 0775 } 0776 0777 ////////////////////////////// execActionFileContent ////////////////////////////// 0778 0779 // execute an action: insert file contents 0780 void UserMenu::execActionFileContent(KTextEditor::View *view, const UserMenuData &menudata) 0781 { 0782 KILE_DEBUG_MAIN << "want to insert contents of a file: " << menudata.filename; 0783 0784 QFile file(menudata.filename); 0785 if ( !file.open(QFile::ReadOnly | QFile::Text) ) { 0786 KILE_DEBUG_MAIN << "STOP: could not open file " << menudata.filename; 0787 return; 0788 } 0789 0790 QTextStream stream( &file ); 0791 QString text = stream.readAll(); 0792 file.close(); 0793 0794 if ( !text.isEmpty() ) { 0795 insertText(view, text, menudata.replaceSelection, menudata.selectInsertion); 0796 } 0797 } 0798 0799 ////////////////////////////// execActionFileContent ////////////////////////////// 0800 0801 // execute an action: run a program 0802 void UserMenu::execActionProgramOutput(KTextEditor::View *view, const UserMenuData &menudata) 0803 { 0804 KILE_DEBUG_MAIN << "want to start a program ... "; 0805 0806 // delete old process 0807 if (m_proc) { 0808 delete m_proc; 0809 m_proc = Q_NULLPTR; 0810 } 0811 0812 // build commandline 0813 QString cmdline = menudata.filename + ' ' + menudata.parameter; 0814 bool useTemporaryFile = cmdline.contains("%M"); 0815 0816 bool needsSelection = menudata.needsSelection; 0817 bool hasSelection = view->selection(); 0818 0819 // check parameter 0820 if ( needsSelection && !hasSelection ) { 0821 KILE_DEBUG_MAIN << "STOP: this program needs selected text"; 0822 return; 0823 } 0824 0825 // do we need a temporary file for the selected text? 0826 if ( hasSelection && useTemporaryFile ) { 0827 KILE_DEBUG_MAIN << "selection and 'placeholder' %M found --> create temporary file"; 0828 0829 // create temporary file 0830 QTemporaryFile tempfile; 0831 //code was tempfile.setSuffix(".txt"); 0832 //Add to constructor and adapt if necessay: QDir::tempPath() + QLatin1String("/myapp_XXXXXX") + QLatin1String(".txt") 0833 tempfile.setAutoRemove(false); 0834 0835 if ( !tempfile.open() ) { 0836 KILE_DEBUG_MAIN << "STOP: could not create tempfile for selected text" ; 0837 return; 0838 } 0839 0840 // get filename 0841 QString selfile = tempfile.fileName(); 0842 0843 // write selection 0844 QTextStream stream( &tempfile ); 0845 stream << view->selectionText() << "\n"; 0846 tempfile.close(); 0847 0848 // update comamndline with temporary filename of selection 0849 cmdline.replace("%M",selfile); 0850 } 0851 0852 // replace %F with the complete base name of the file without the path. 0853 // The complete base name consists of all characters in the file up to 0854 // (but not including) the last '.' character. 0855 if ( cmdline.contains("%S") ) { 0856 QFileInfo fi(view->document()->url().toLocalFile()); 0857 QString basename = fi.completeBaseName(); 0858 cmdline.replace("%S",basename); 0859 } 0860 0861 m_proc = new KProcess(this); 0862 m_proc->setShellCommand(cmdline); 0863 m_proc->setOutputChannelMode(KProcess::MergedChannels); 0864 m_proc->setReadChannel(QProcess::StandardOutput); 0865 0866 connect(m_proc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotProcessOutput())); 0867 connect(m_proc, SIGNAL(readyReadStandardError()), this, SLOT(slotProcessOutput())); 0868 connect(m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited(int,QProcess::ExitStatus))); 0869 0870 KILE_DEBUG_MAIN << "... start proc: " << cmdline; 0871 // init and/or save important data 0872 m_procOutput.clear(); 0873 m_procView = view; 0874 m_procMenudata = &menudata; 0875 0876 m_proc->start(); 0877 } 0878 0879 void UserMenu::slotProcessOutput() 0880 { 0881 m_procOutput += m_proc->readAll(); 0882 } 0883 0884 void UserMenu::slotProcessExited(int /* exitCode */, QProcess::ExitStatus exitStatus) 0885 { 0886 KILE_DEBUG_MAIN << "... finish proc "; 0887 KILE_DEBUG_MAIN << "output: " << m_procOutput; 0888 0889 if ( exitStatus == QProcess::NormalExit && m_procMenudata->insertOutput && !m_procOutput.isEmpty() ) { 0890 insertText(m_procView, m_procOutput, m_procMenudata->replaceSelection, m_procMenudata->selectInsertion); 0891 } 0892 } 0893 0894 ////////////////////////////// auxiliary ////////////////////////////// 0895 0896 // action is finished, now insert some text 0897 void UserMenu::insertText(KTextEditor::View *view, const QString &text, bool replaceSelection, bool selectInsertion) 0898 { 0899 KILE_DEBUG_MAIN << "insert text from action: " << text; 0900 // metachars: %R - references (like \ref{%R}, \pageref{%R} ...) 0901 // %T - citations (like \cite{%T} ...) 0902 QString metachar,label; 0903 int actiontype =0; 0904 0905 if(text.contains("%R")) { 0906 metachar = "%R"; 0907 label = i18n("Label"); 0908 actiontype = KileAction::FromLabelList; 0909 } 0910 else if(text.contains("%T")) { 0911 metachar = "%T"; 0912 label = i18n("Reference"); 0913 actiontype = KileAction::FromBibItemList; 0914 } 0915 if(!metachar.isEmpty()) { 0916 QStringList list = text.split(metachar); 0917 0918 KileAction::InputTag tag(m_ki, i18n("Input Dialog"), QString(), QKeySequence(), m_receiver, SLOT(insertTag(KileAction::TagData)), m_actioncollection,"tag_temporary_action", m_ki->mainWindow(), actiontype, list.at(0)+metachar, list.at(1), list.at(0).length(), 0, QString(), label); 0919 0920 tag.activate(QAction::Trigger); 0921 return; 0922 } 0923 0924 // metachars: %B - bullet 0925 // %M - selected text 0926 // %C - place cursor 0927 // %E - indent in environment 0928 QString ins = text; 0929 bool bullet = ins.contains("%B"); 0930 0931 // deselect and/or remove current selection 0932 if(view->selection()) { 0933 if(ins.contains("%M")) { 0934 ins.replace("%M", view->selectionText()); 0935 } 0936 if(replaceSelection) { 0937 view->removeSelectionText(); 0938 } 0939 else { 0940 view->removeSelection(); 0941 } 0942 } 0943 else { 0944 ins.replace("%M", QString()); 0945 } 0946 KILE_DEBUG_MAIN << " ---> " << ins; 0947 0948 // insert new text 0949 KTextEditor::Cursor cursor1 = view->cursorPosition(); 0950 emit( sendText(ins) ); 0951 0952 // select inserted text 0953 if(selectInsertion) { 0954 KTextEditor::Cursor cursor2 = view->cursorPosition(); 0955 view->setSelection(KTextEditor::Range(cursor1, cursor2)); 0956 } 0957 0958 // text with bullet metachar %B 0959 if(bullet) { 0960 view->setCursorPosition(cursor1); 0961 m_ki->editorExtension()->gotoBullet(false, view); 0962 } 0963 } 0964 0965 ////////////////////////////// auxiliary ////////////////////////////// 0966 0967 bool UserMenu::str2bool(const QString &value) 0968 { 0969 return ( value == "true" ); 0970 } 0971 0972 0973 } 0974