"Fossies" - the Fresh Open Source Software Archive

Member "texstudio-2.12.22/src/texstudio.cpp" (15 Jan 2020, 392973 Bytes) of package /linux/misc/texstudio-2.12.22.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "texstudio.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.12.20_vs_2.12.22.

    1 /***************************************************************************
    2  *   copyright       : (C) 2003-2008 by Pascal Brachet                     *
    3  *   addons by Luis Silvestre                                              *
    4  *   http://www.xm1math.net/texmaker/                                      *
    5  *                                                                         *
    6  *   This program is free software; you can redistribute it and/or modify  *
    7  *   it under the terms of the GNU General Public License as published by  *
    8  *   the Free Software Foundation  either version 2 of the License, or     *
    9  *   (at your option) any later version.                                   *
   10  *                                                                         *
   11  ***************************************************************************/
   12 //#include <stdlib.h>
   13 //#include "/usr/include/valgrind/callgrind.h"
   14 
   15 #include "texstudio.h"
   16 #include "latexeditorview.h"
   17 
   18 #include "smallUsefulFunctions.h"
   19 
   20 #include "cleandialog.h"
   21 
   22 #include "debughelper.h"
   23 #include "debugloggermain.h"
   24 
   25 #include "dblclickmenubar.h"
   26 #include "filechooser.h"
   27 #include "filedialog.h"
   28 #include "tabdialog.h"
   29 #include "arraydialog.h"
   30 #include "bibtexdialog.h"
   31 #include "tabbingdialog.h"
   32 #include "letterdialog.h"
   33 #include "quickdocumentdialog.h"
   34 #include "quickbeamerdialog.h"
   35 #include "mathassistant.h"
   36 #include "maketemplatedialog.h"
   37 #include "templateselector.h"
   38 #include "templatemanager.h"
   39 #include "usermenudialog.h"
   40 #include "aboutdialog.h"
   41 #include "encodingdialog.h"
   42 #include "encoding.h"
   43 #include "randomtextgenerator.h"
   44 #include "webpublishdialog.h"
   45 #include "thesaurusdialog.h"
   46 #include "qsearchreplacepanel.h"
   47 #include "latexcompleter_config.h"
   48 #include "universalinputdialog.h"
   49 #include "insertgraphics.h"
   50 #include "latexeditorview_config.h"
   51 #include "scriptengine.h"
   52 #include "grammarcheck.h"
   53 #include "qmetautils.h"
   54 #include "updatechecker.h"
   55 #include "session.h"
   56 #include "svn.h"
   57 #include "help.h"
   58 #include "searchquery.h"
   59 #include "fileselector.h"
   60 #include "utilsUI.h"
   61 #include "utilsSystem.h"
   62 #include "minisplitter.h"
   63 #include "latexpackage.h"
   64 #include "latexparser/argumentlist.h"
   65 #include "latexparser/latextokens.h"
   66 #include "latexparser/latexparser.h"
   67 #include "latexparser/latexparsing.h"
   68 #include "latexstructure.h"
   69 #include "structuretreeview.h"
   70 #include "symbollistmodel.h"
   71 #include "symbolwidget.h"
   72 #include "execprogram.h"
   73 
   74 #include <QScreen>
   75 
   76 #ifndef QT_NO_DEBUG
   77 #include "tests/testmanager.h"
   78 #endif
   79 
   80 #include "qdocument.h"
   81 #include "qdocumentcursor.h"
   82 #include "qdocumentline.h"
   83 #include "qdocumentline_p.h"
   84 
   85 #include "qnfadefinition.h"
   86 
   87 #include "PDFDocument_config.h"
   88 
   89 /*! \file texstudio.cpp
   90  * contains the GUI definition as well as some helper functions
   91  */
   92 
   93 /*!
   94     \defgroup txs Mainwindow
   95     \ingroup txs
   96     @{
   97 */
   98 
   99 /*! \class Texstudio
  100  * This class sets up the GUI and handles the GUI interaction (menus and toolbar).
  101  * It uses QEditor with LatexDocument as actual text editor and PDFDocument for viewing pdf.
  102  *
  103  * \see QEditor
  104  * \see LatexDocument
  105  * \see PDFDocument
  106  */
  107 
  108 
  109 
  110 const QString APPICON(":appicon");
  111 
  112 bool programStopped = false;
  113 Texstudio *txsInstance = nullptr;
  114 QCache<QString, QIcon> iconCache;
  115 
  116 // workaround needed on OSX due to https://bugreports.qt.io/browse/QTBUG-49576
  117 void hideSplash()
  118 {
  119 #ifdef Q_OS_MAC
  120     if (txsInstance)
  121         txsInstance->hideSplash();
  122 #endif
  123 }
  124 /*!
  125  * \brief constructor
  126  *
  127  * set-up GUI
  128  *
  129  * \param parent
  130  * \param flags
  131  * \param splash
  132  */
  133 Texstudio::Texstudio(QWidget *parent, Qt::WindowFlags flags, QSplashScreen *splash)
  134         : QMainWindow(parent, flags), textAnalysisDlg(nullptr), spellDlg(nullptr), mDontScrollToItem(false), runBibliographyIfNecessaryEntered(false)
  135 {
  136 
  137     splashscreen = splash;
  138     programStopped = false;
  139     spellLanguageActions = nullptr;
  140     currentLine = -1;
  141     svndlg = nullptr;
  142     userMacroDialog = nullptr;
  143     mCompleterNeedsUpdate = false;
  144     latexStyleParser = nullptr;
  145     packageListReader = nullptr;
  146     bibtexEntryActions = nullptr;
  147     biblatexEntryActions = nullptr;
  148     bibTypeActions = nullptr;
  149     highlightLanguageActions = nullptr;
  150     runningPDFCommands = runningPDFAsyncCommands = 0;
  151     completerPreview = false;
  152     recheckLabels = true;
  153     cursorHistory = nullptr;
  154     recentSessionList = nullptr;
  155     editors = nullptr;
  156     m_languages = nullptr; //initial state to avoid crash on OSX
  157 
  158     connect(&buildManager, SIGNAL(hideSplash()), this, SLOT(hideSplash()));
  159 
  160     readSettings();
  161 
  162 #if (QT_VERSION > 0x050000) && (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
  163     QCoreApplication::instance()->installEventFilter(this);
  164 #endif
  165 #ifdef Q_OS_WIN
  166     // work-around for ยด+t bug
  167     QCoreApplication::instance()->installEventFilter(this);
  168 #endif
  169 
  170     latexReference = new LatexReference();
  171     latexReference->setFile(findResourceFile("latex2e.html"));
  172 
  173     qRegisterMetaType<QSet<QString> >();
  174 
  175     txsInstance = this;
  176     static int crashHandlerType = 1;
  177     configManager.registerOption("Crash Handler Type", &crashHandlerType, 1);
  178 
  179     initCrashHandler(crashHandlerType);
  180 
  181     QTimer *t  = new QTimer(this);
  182     connect(t, SIGNAL(timeout()), SLOT(iamalive()));
  183     t->start(9500);
  184 
  185     setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
  186     setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
  187     setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
  188     setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
  189 
  190     QFile styleSheetFile(configManager.configBaseDir + "stylesheet.qss");
  191     if (styleSheetFile.exists()) {
  192         if(styleSheetFile.open(QFile::ReadOnly)){
  193             setStyleSheet(styleSheetFile.readAll());
  194             styleSheetFile.close();
  195         }
  196     }
  197 
  198     // dpi aware icon scaling
  199     // screen dpi is read and the icon are scaled up in reference to 96 dpi
  200     // this should be helpful on X11 (Xresouces) and possibly windows
  201 #if QT_VERSION> 0x050000
  202     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
  203     double scale=dpi/96;
  204 #else
  205     double scale=1;
  206 #endif
  207 
  208     setWindowIcon(QIcon(":/images/logo128.png"));
  209 
  210     int iconSize = qRound(qMax(16, configManager.guiToolbarIconSize)*scale);
  211     setIconSize(QSize(iconSize, iconSize));
  212 
  213     leftPanel = nullptr;
  214     sidePanel = nullptr;
  215     structureTreeView = nullptr;
  216     outputView = nullptr;
  217 
  218     qRegisterMetaType<LatexParser>();
  219     latexParser.importCwlAliases(findResourceFile("completion/cwlAliases.dat"));
  220 
  221     m_formatsOldDefault = QDocument::defaultFormatScheme();
  222     QDocument::setDefaultFormatScheme(m_formats);
  223     SpellerUtility::spellcheckErrorFormat = m_formats->id("spellingMistake");
  224 
  225     qRegisterMetaType<QList<LineInfo> >();
  226     qRegisterMetaType<QList<GrammarError> >();
  227     qRegisterMetaType<LatexParser>();
  228     qRegisterMetaType<GrammarCheckerConfig>();
  229 
  230     grammarCheck = new GrammarCheck();
  231     grammarCheck->moveToThread(&grammarCheckThread);
  232     GrammarCheck::staticMetaObject.invokeMethod(grammarCheck, "init", Qt::QueuedConnection, Q_ARG(LatexParser, latexParser), Q_ARG(GrammarCheckerConfig, *configManager.grammarCheckerConfig));
  233     connect(grammarCheck, SIGNAL(checked(const void *, const void *, int, QList<GrammarError>)), &documents, SLOT(lineGrammarChecked(const void *, const void *, int, QList<GrammarError>)));
  234     connect(grammarCheck, SIGNAL(errorMessage(QString)),this,SLOT(LTErrorMessage(QString)));
  235     if (configManager.autoLoadChildren)
  236         connect(&documents, SIGNAL(docToLoad(QString)), this, SLOT(addDocToLoad(QString)));
  237     connect(&documents, SIGNAL(updateQNFA()), this, SLOT(updateTexQNFA()));
  238 
  239     grammarCheckThread.start();
  240 
  241     if (configManager.autoDetectEncodingFromLatex || configManager.autoDetectEncodingFromChars) QDocument::setDefaultCodec(nullptr);
  242     else QDocument::setDefaultCodec(configManager.newFileEncoding);
  243     if (configManager.autoDetectEncodingFromLatex)
  244         QDocument::addGuessEncodingCallback(&Encoding::guessEncoding); // encodingcallbacks before restoer session !!!
  245     if (configManager.autoDetectEncodingFromChars)
  246         QDocument::addGuessEncodingCallback(&ConfigManager::getDefaultEncoding);
  247 
  248     QString qxsPath = QFileInfo(findResourceFile("qxs/tex.qnfa")).path();
  249     m_languages = new QLanguageFactory(m_formats, this);
  250     m_languages->addDefinitionPath(qxsPath);
  251     m_languages->addDefinitionPath(configManager.configBaseDir + "languages");  // definitions here overwrite previous ones
  252 
  253     // custom evironments & structure commands
  254     updateTexQNFA();
  255 
  256     QLineMarksInfoCenter::instance()->loadMarkTypes(qxsPath + "/marks.qxm");
  257     QList<QLineMarkType> &marks = QLineMarksInfoCenter::instance()->markTypes();
  258     for (int i = 0; i < marks.size(); i++)
  259         if (m_formats->format("line:" + marks[i].id).background.isValid())
  260             marks[i].color = m_formats->format("line:" + marks[i].id).background;
  261         else
  262             marks[i].color = Qt::transparent;
  263 
  264     LatexEditorView::updateFormatSettings();
  265 
  266     // TAB WIDGET EDITEUR
  267     documents.indentationInStructure = configManager.indentationInStructure;
  268     documents.showCommentedElementsInStructure = configManager.showCommentedElementsInStructure;
  269     documents.indentIncludesInStructure = configManager.indentIncludesInStructure;
  270     documents.markStructureElementsBeyondEnd = configManager.markStructureElementsBeyondEnd;
  271     documents.markStructureElementsInAppendix = configManager.markStructureElementsInAppendix;
  272     documents.showLineNumbersInStructure = configManager.showLineNumbersInStructure;
  273     connect(&documents, SIGNAL(masterDocumentChanged(LatexDocument *)), SLOT(masterDocumentChanged(LatexDocument *)));
  274     connect(&documents, SIGNAL(aboutToDeleteDocument(LatexDocument *)), SLOT(aboutToDeleteDocument(LatexDocument *)));
  275 
  276     centralFrame = new QFrame(this);
  277     centralFrame->setLineWidth(0);
  278     centralFrame->setFrameShape(QFrame::NoFrame);
  279     centralFrame->setFrameShadow(QFrame::Plain);
  280 
  281 
  282 
  283     //edit
  284     centralToolBar = new QToolBar(tr("Central"), this);
  285     centralToolBar->setFloatable(false);
  286     centralToolBar->setOrientation(Qt::Vertical);
  287     centralToolBar->setMovable(false);
  288     iconSize = qRound(qMax(16, configManager.guiSecondaryToolbarIconSize)*scale);
  289     centralToolBar->setIconSize(QSize(iconSize, iconSize));
  290 
  291     editors = new Editors(centralFrame);
  292     editors->setFocus();
  293 
  294     TxsTabWidget *leftEditors = new TxsTabWidget(this);
  295     leftEditors->setActive(true);
  296     editors->addTabWidget(leftEditors);
  297     TxsTabWidget *rightEditors = new TxsTabWidget(this);
  298     editors->addTabWidget(rightEditors);
  299 
  300     connect(&documents, SIGNAL(docToHide(LatexEditorView *)), editors, SLOT(removeEditor(LatexEditorView *)));
  301     connect(editors, SIGNAL(currentEditorChanged()), SLOT(currentEditorChanged()));
  302     connect(editors, SIGNAL(listOfEditorsChanged()), SLOT(updateOpenDocumentMenu()));
  303     connect(editors, SIGNAL(editorsReordered()), SLOT(onEditorsReordered()));
  304     connect(editors, SIGNAL(closeCurrentEditorRequested()), this, SLOT(fileClose()));
  305     connect(editors, SIGNAL(editorAboutToChangeByTabClick(LatexEditorView *, LatexEditorView *)), this, SLOT(editorAboutToChangeByTabClick(LatexEditorView *, LatexEditorView *)));
  306 
  307     cursorHistory = new CursorHistory(&documents);
  308     bookmarks = new Bookmarks(&documents, this);
  309 
  310     QLayout *centralLayout = new QHBoxLayout(centralFrame);
  311     centralLayout->setSpacing(0);
  312     centralLayout->setMargin(0);
  313     centralLayout->addWidget(centralToolBar);
  314     centralLayout->addWidget(editors);
  315 
  316     centralVSplitter = new MiniSplitter(Qt::Vertical, this);
  317     centralVSplitter->setChildrenCollapsible(false);
  318     centralVSplitter->addWidget(centralFrame);
  319     centralVSplitter->setStretchFactor(0, 1);  // all stretch goes to the editor (0th widget)
  320 
  321     sidePanelSplitter = new MiniSplitter(Qt::Horizontal, this);
  322     sidePanelSplitter->addWidget(centralVSplitter);
  323 
  324     mainHSplitter = new MiniSplitter(Qt::Horizontal, this);  // top-level element: splits: [ everything else | PDF ]
  325     mainHSplitter->addWidget(sidePanelSplitter);
  326     mainHSplitter->setChildrenCollapsible(false);
  327     setCentralWidget(mainHSplitter);
  328 
  329     setContextMenuPolicy(Qt::ActionsContextMenu);
  330 
  331     setupDockWidgets();
  332 
  333     setMenuBar(new DblClickMenuBar());
  334     setupMenus();
  335     TitledPanelPage *logPage = outputView->pageFromId(outputView->LOG_PAGE);
  336     if (logPage) {
  337         logPage->addToolbarAction(getManagedAction("main/tools/logmarkers"));
  338         logPage->addToolbarAction(getManagedAction("main/edit2/goto/errorprev"));
  339         logPage->addToolbarAction(getManagedAction("main/edit2/goto/errornext"));
  340     }
  341     setupToolBars();
  342     connect(&configManager, SIGNAL(watchedMenuChanged(QString)), SLOT(updateToolBarMenu(QString)));
  343 
  344     restoreState(windowstate, 0);
  345     //workaround as toolbar central seems not be be handled by windowstate
  346     centralToolBar->setVisible(configManager.centralVisible);
  347     if (tobemaximized) showMaximized();
  348     if (tobefullscreen) {
  349         showFullScreen();
  350         restoreState(stateFullScreen, 1);
  351         fullscreenModeAction->setChecked(true);
  352     }
  353 
  354     createStatusBar();
  355     completer = nullptr;
  356     updateCaption();
  357     updateMasterDocumentCaption();
  358     setStatusMessageProcess(QString(" %1 ").arg(tr("Ready")));
  359 
  360     show();
  361     if (splash)
  362         splash->raise();
  363 
  364     setAcceptDrops(true);
  365     //installEventFilter(this);
  366 
  367     completer = new LatexCompleter(latexParser, this);
  368     completer->setConfig(configManager.completerConfig);
  369     completer->setPackageList(&latexPackageList);
  370     connect(completer, SIGNAL(showImagePreview(QString)), this, SLOT(showImgPreview(QString)));
  371     connect(completer, SIGNAL(showPreview(QString)), this, SLOT(showPreview(QString)));
  372     connect(this, SIGNAL(imgPreview(QString)), completer, SLOT(bibtexSectionFound(QString)));
  373     //updateCompleter();
  374     LatexEditorView::setCompleter(completer);
  375     completer->setLatexReference(latexReference);
  376     completer->updateAbbreviations();
  377 
  378     TemplateManager::setConfigBaseDir(configManager.configBaseDir);
  379     TemplateManager::ensureUserTemplateDirExists();
  380     TemplateManager::checkForOldUserTemplates();
  381 
  382     /* The encoding detection works as follow:
  383         If QDocument detects the file is UTF16LE/BE, use that encoding
  384         Else If QDocument detects UTF-8 {
  385     If LatexParser::guessEncoding finds an encoding, use that
  386     Else use UTF-8
  387     } Else {
  388       If LatexParser::guessEncoding finds an encoding use that
  389       Else if QDocument detects ascii (only 7bit characters) {
  390     if default encoding == utf16: use utf-8 as fallback (because utf16 can be reliable detected and the user seems to like unicode)
  391     else use default encoding
  392       }
  393       Else {
  394     if default encoding == utf16/8: use latin1 (because the file contains invalid unicode characters )
  395     else use default encoding
  396       }
  397     }
  398 
  399     */
  400 
  401     connect(&svn, SIGNAL(statusMessage(QString)), this, SLOT(setStatusMessageProcess(QString)));
  402     connect(&svn, SIGNAL(runCommand(QString,QString*)), this, SLOT(runCommandNoSpecialChars(QString,QString*)));
  403 
  404     QStringList filters;
  405     filters << tr("TeX files") + " (*.tex *.bib *.sty *.cls *.mp *.dtx *.cfg *.ins *.ltx *.tikz *.pdf_tex *.ctx)";
  406     filters << tr("LilyPond files") + " (*.lytex)";
  407     filters << tr("Plaintext files") + " (*.txt)";
  408     filters << tr("Pweave files") + " (*.Pnw)";
  409     filters << tr("Sweave files") + " (*.Snw *.Rnw)";
  410     filters << tr("Asymptote files") + " (*.asy)";
  411     filters << tr("PDF files") + " (*.pdf)";
  412     filters << tr("All files") + " (*)";
  413     fileFilters = filters.join(";;");
  414     if (!configManager.rememberFileFilter)
  415         selectedFileFilter = filters.first();
  416 
  417     enlargedViewer=false;
  418 
  419     //setup autosave timer
  420     connect(&autosaveTimer, SIGNAL(timeout()), this, SLOT(fileSaveAllFromTimer()));
  421     if (configManager.autosaveEveryMinutes > 0) {
  422         autosaveTimer.start(configManager.autosaveEveryMinutes * 1000 * 60);
  423     }
  424     connect(&previewDelayTimer,SIGNAL(timeout()),this,SLOT(showPreviewQueue()));
  425     previewDelayTimer.setSingleShot(true);
  426 
  427     connect(this, SIGNAL(infoFileSaved(QString, int)), this, SLOT(checkinAfterSave(QString, int)));
  428 
  429     //script things
  430     setProperty("applicationName", TEXSTUDIO);
  431     QTimer::singleShot(500, this, SLOT(autoRunScripts()));
  432     connectWithAdditionalArguments(this, SIGNAL(infoNewFile()), this, "runScripts", QList<QVariant>() << Macro::ST_NEW_FILE);
  433     connectWithAdditionalArguments(this, SIGNAL(infoNewFromTemplate()), this, "runScripts", QList<QVariant>() << Macro::ST_NEW_FROM_TEMPLATE);
  434     connectWithAdditionalArguments(this, SIGNAL(infoLoadFile(QString)), this, "runScripts", QList<QVariant>() << Macro::ST_LOAD_FILE);
  435     connectWithAdditionalArguments(this, SIGNAL(infoFileSaved(QString)), this, "runScripts", QList<QVariant>() << Macro::ST_FILE_SAVED);
  436     connectWithAdditionalArguments(this, SIGNAL(infoFileClosed()), this, "runScripts", QList<QVariant>() << Macro::ST_FILE_CLOSED);
  437     connectWithAdditionalArguments(&documents, SIGNAL(masterDocumentChanged(LatexDocument *)), this, "runScripts", QList<QVariant>() << Macro::ST_MASTER_CHANGED);
  438     connectWithAdditionalArguments(this, SIGNAL(infoAfterTypeset()), this, "runScripts", QList<QVariant>() << Macro::ST_AFTER_TYPESET);
  439     connectWithAdditionalArguments(&buildManager, SIGNAL(endRunningCommands(QString, bool, bool, bool)), this, "runScripts", QList<QVariant>() << Macro::ST_AFTER_COMMAND_RUN);
  440 
  441     if (configManager.sessionRestore && !ConfigManager::dontRestoreSession) {
  442         fileRestoreSession(false, false);
  443     }
  444     splashscreen = nullptr;
  445 }
  446 /*!
  447  * \brief destructor
  448  */
  449 Texstudio::~Texstudio()
  450 {
  451 
  452     iconCache.clear();
  453     QDocument::setDefaultFormatScheme(m_formatsOldDefault); //prevents crash when deleted latexeditorview accesses the default format scheme, as m_format is going to be deleted
  454 
  455     programStopped = true;
  456 
  457     Guardian::shutdown();
  458 
  459     if (latexStyleParser) latexStyleParser->stop();
  460     if (packageListReader) packageListReader->stop();
  461     GrammarCheck::staticMetaObject.invokeMethod(grammarCheck, "shutdown", Qt::BlockingQueuedConnection);
  462     grammarCheckThread.quit();
  463 
  464     if (latexStyleParser) latexStyleParser->wait();
  465     if (packageListReader) packageListReader->wait();
  466     grammarCheckThread.wait(5000); //TODO: timeout causes sigsegv, is there any better solution?
  467 }
  468 
  469 /*!
  470  * \brief code to be executed at end of start-up
  471  *
  472  * Check for Latex installation.
  473  * Read in all package names for usepackage completion.
  474  */
  475 void Texstudio::startupCompleted()
  476 {
  477     if (configManager.checkLatexConfiguration) {
  478         bool noWarnAgain = false;
  479         buildManager.checkLatexConfiguration(noWarnAgain);
  480         configManager.checkLatexConfiguration = !noWarnAgain;
  481     }
  482     UpdateChecker::instance()->autoCheck();
  483     // package reading (at least with Miktex) apparently slows down the startup
  484     // the first rendering of lines in QDocumentPrivate::draw() gets very slow
  485     // therefore we defer it until the main window is completely loaded
  486     readinAllPackageNames(); // asynchrnous read in of all available sty/cls
  487 }
  488 
  489 QAction *Texstudio::newManagedAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QKeySequence &shortCut, const QString &iconFile, const QList<QVariant> &args)
  490 {
  491     QAction *tmp = configManager.newManagedAction(menu, id, text, args.isEmpty() ? slotName : SLOT(relayToOwnSlot()), QList<QKeySequence>() << shortCut, iconFile);
  492     if (!args.isEmpty()) {
  493         QString slot = QString(slotName).left(QString(slotName).indexOf("("));
  494         Q_ASSERT(staticMetaObject.indexOfSlot(createMethodSignature(qPrintable(slot), args)) != -1);
  495         tmp->setProperty("slot", qPrintable(slot));
  496         tmp->setProperty("args", QVariant::fromValue<QList<QVariant> >(args));
  497     }
  498     return tmp;
  499 }
  500 
  501 QAction *Texstudio::newManagedAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QList<QKeySequence> &shortCuts, const QString &iconFile, const QList<QVariant> &args)
  502 {
  503     QAction *tmp = configManager.newManagedAction(menu, id, text, args.isEmpty() ? slotName : SLOT(relayToOwnSlot()), shortCuts, iconFile);
  504     if (!args.isEmpty()) {
  505         QString slot = QString(slotName).left(QString(slotName).indexOf("("));
  506         Q_ASSERT(staticMetaObject.indexOfSlot(createMethodSignature(qPrintable(slot), args)) != -1);
  507         tmp->setProperty("slot", qPrintable(slot));
  508         tmp->setProperty("args", QVariant::fromValue<QList<QVariant> >(args));
  509     }
  510     return tmp;
  511 }
  512 
  513 QAction *Texstudio::newManagedEditorAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QKeySequence &shortCut, const QString &iconFile, const QList<QVariant> &args)
  514 {
  515         QAction *tmp = configManager.newManagedAction(menu, id, text, nullptr, QList<QKeySequence>() << shortCut, iconFile);
  516     linkToEditorSlot(tmp, slotName, args);
  517     return tmp;
  518 }
  519 
  520 QAction *Texstudio::newManagedEditorAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QList<QKeySequence> &shortCuts, const QString &iconFile, const QList<QVariant> &args)
  521 {
  522         QAction *tmp = configManager.newManagedAction(menu, id, text, nullptr, shortCuts, iconFile);
  523     linkToEditorSlot(tmp, slotName, args);
  524     return tmp;
  525 }
  526 
  527 QAction *Texstudio::insertManagedAction(QAction *before, const QString &id, const QString &text, const char *slotName, const QKeySequence &shortCut, const QString &iconFile)
  528 {
  529     QMenu *menu = before->menu();
  530     REQUIRE_RET(menu, nullptr);
  531     QAction *inserted = newManagedAction(menu, id, text, slotName, shortCut, iconFile);
  532     menu->removeAction(inserted);
  533     menu->insertAction(before, inserted);
  534     return inserted;
  535 }
  536 
  537 /*!
  538  * \brief add TagList to side panel
  539  *
  540  * add Taglist to side panel.
  541  *
  542  * \param id
  543  * \param iconName icon used for selecting taglist
  544  * \param text name of taglist
  545  * \param tagFile file to be read as tag list
  546  */
  547 void Texstudio::addTagList(const QString &id, const QString &iconName, const QString &text, const QString &tagFile)
  548 {
  549     XmlTagsListWidget *list = qobject_cast<XmlTagsListWidget *>(leftPanel->widget(id));
  550     if (!list) {
  551         list = new XmlTagsListWidget(this, ":/tags/" + tagFile);
  552         list->setObjectName("tags/" + tagFile.left(tagFile.indexOf("_tags.xml")));
  553         UtilsUi::enableTouchScrolling(list);
  554         connect(list, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(insertXmlTag(QListWidgetItem *)));
  555         leftPanel->addWidget(list, id, text, iconName);
  556         //(*list)->setProperty("mType",2);
  557     } else leftPanel->setWidgetText(list, text);
  558 }
  559 
  560 /*!
  561  * \brief add all macros as TagList to side panel
  562  *
  563  * add Macros as Taglist to side panel as an alternative way to call them.
  564  * This may be helpful if the number of macros becomes large and overcrowd the menu or are too many for generic keyboard shortcuts
  565  *
  566  */
  567 void Texstudio::addMacrosAsTagList()
  568 {
  569     bool addToPanel=true;
  570     QListWidget *list = qobject_cast<QListWidget *>(leftPanel->widget("txs-macros"));
  571     if (!list) {
  572         list = new QListWidget(this);
  573         list->setObjectName("tags/txs-macros");
  574     }else{
  575         list->clear();
  576         addToPanel=false;
  577     }
  578     // add elements
  579     for(const auto &m:configManager.completerConfig->userMacros) {
  580         if (m.name == "TMX:Replace Quote Open" || m.name == "TMX:Replace Quote Close" || m.document)
  581             continue;
  582         QListWidgetItem* item=new QListWidgetItem(m.name);
  583         item->setData(Qt::UserRole, m.typedTag());
  584         list->addItem(item);
  585     }
  586     UtilsUi::enableTouchScrolling(list);
  587     connect(list, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(insertFromTagList(QListWidgetItem *)));
  588     if(addToPanel)
  589         leftPanel->addWidget(list, "txs-macros", tr("Macros"), getRealIconFile("executeMacro"));
  590 }
  591 
  592 /*! set-up side- and bottom-panel
  593  */
  594 void Texstudio::setupDockWidgets()
  595 {
  596     //to allow retranslate this function must be able to be called multiple times
  597 
  598     // adapt icon size to dpi
  599 #if QT_VERSION> 0x050000
  600     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
  601     double scale=dpi/96;
  602 #else
  603     double scale=1;
  604 #endif
  605 
  606     if (!sidePanel) {
  607         sidePanel = new SidePanel(this);
  608         sidePanel->toggleViewAction()->setIcon(getRealIcon("sidebar"));
  609         sidePanel->toggleViewAction()->setText(tr("Side Panel"));
  610         sidePanel->toggleViewAction()->setChecked(configManager.getOption("GUI/sidePanel/visible", true).toBool());
  611         addAction(sidePanel->toggleViewAction());
  612 
  613         sidePanelSplitter->insertWidget(0, sidePanel);
  614         sidePanelSplitter->setStretchFactor(0, 0);  // panel does not get rescaled
  615         sidePanelSplitter->setStretchFactor(1, 1);
  616     }
  617 
  618     //Structure panel
  619     if (!leftPanel) {
  620         leftPanel = new CustomWidgetList(this);
  621         leftPanel->setObjectName("leftPanel");
  622         TitledPanelPage *page = new TitledPanelPage(leftPanel, "leftPanel", "TODO");
  623         sidePanel->appendPage(page);
  624         if (hiddenLeftPanelWidgets != "") {
  625             leftPanel->setHiddenWidgets(hiddenLeftPanelWidgets);
  626             hiddenLeftPanelWidgets = ""; //not needed anymore after the first call
  627         }
  628         connect(leftPanel, SIGNAL(titleChanged(QString)), page, SLOT(setTitle(QString)));
  629     }
  630 
  631     if (!structureTreeView) {
  632         structureTreeView = new StructureTreeView(editors, documents, configManager, this);
  633         structureTreeView->setModel(documents.model);
  634 
  635         connect(structureTreeView, SIGNAL(requestCloseDocument(LatexDocument*)), this, SLOT(structureContextMenuCloseDocument(LatexDocument*)));
  636         connect(structureTreeView, SIGNAL(requestToggleMasterDocument(LatexDocument*)), this, SLOT(structureContextMenuToggleMasterDocument(LatexDocument*)));
  637         connect(structureTreeView, SIGNAL(requestOpenAllRelatedDocuments(LatexDocument*)), this, SLOT(structureContextMenuOpenAllRelatedDocuments(LatexDocument*)));
  638         connect(structureTreeView, SIGNAL(requestCloseAllRelatedDocuments(LatexDocument*)), this, SLOT(structureContextMenuCloseAllRelatedDocuments(LatexDocument*)));
  639         connect(structureTreeView, SIGNAL(requestGotoLine(LatexDocument*,int,int)), this, SLOT(gotoLine(LatexDocument*,int,int)));
  640         connect(structureTreeView, SIGNAL(requestOpenExternalFile(QString)), this, SLOT(openExternalFile(QString)));
  641         connect(structureTreeView, SIGNAL(insertText(QString)), this, SLOT(insertText(QString)));
  642         connect(structureTreeView, SIGNAL(findLabelUsages(LatexDocument*,QString)), this, SLOT(findLabelUsages(LatexDocument*,QString)));
  643         connect(structureTreeView, SIGNAL(createLabelForStructureEntry(const StructureEntry*)), this, SLOT(createLabelForStructureEntry(const StructureEntry*)));
  644 
  645         //disabled because it also reacts to expand, connect(structureTreeView, SIGNAL(activated(const QModelIndex &)), SLOT(clickedOnStructureEntry(const QModelIndex &))); //enter or double click (+single click on some platforms)
  646         connect(structureTreeView, SIGNAL(pressed(const QModelIndex &)), SLOT(clickedOnStructureEntry(const QModelIndex &))); //single click
  647 
  648         leftPanel->addWidget(structureTreeView, "structureTreeView", tr("Structure"), getRealIconFile("structure"));
  649     } else leftPanel->setWidgetText(structureTreeView, tr("Structure"));
  650     if (!leftPanel->widget("bookmarks")) {
  651         QListWidget *bookmarksWidget = bookmarks->widget();
  652         connect(bookmarks, SIGNAL(loadFileRequest(QString)), this, SLOT(load(QString)));
  653         connect(bookmarks, SIGNAL(gotoLineRequest(int, int, LatexEditorView *)), this, SLOT(gotoLine(int, int, LatexEditorView *)));
  654         leftPanel->addWidget(bookmarksWidget, "bookmarks", tr("Bookmarks"), getRealIconFile("bookmarks"));
  655     } else leftPanel->setWidgetText("bookmarks", tr("Bookmarks"));
  656 
  657     if (!leftPanel->widget("symbols")) {
  658         symbolWidget = new SymbolWidget(symbolListModel, configManager.insertSymbolsAsUnicode, this);
  659         symbolWidget->setSymbolSize(qRound(configManager.guiSymbolGridIconSize*scale));
  660         connect(symbolWidget, SIGNAL(insertSymbol(QString)), this, SLOT(insertSymbol(QString)));
  661         leftPanel->addWidget(symbolWidget, "symbols", tr("Symbols"), getRealIconFile("symbols"));
  662     } else leftPanel->setWidgetText("symbols", tr("Symbols"));
  663 
  664     addTagList("brackets", getRealIconFile("leftright"), tr("Left/Right Brackets"), "brackets_tags.xml");
  665     addTagList("pstricks", getRealIconFile("pstricks"), tr("Pstricks Commands"), "pstricks_tags.xml");
  666     addTagList("metapost", getRealIconFile("metapost"), tr("MetaPost Commands"), "metapost_tags.xml");
  667     addTagList("tikz", getRealIconFile("tikz"), tr("Tikz Commands"), "tikz_tags.xml");
  668     addTagList("asymptote", getRealIconFile("asymptote"), tr("Asymptote Commands"), "asymptote_tags.xml");
  669     addTagList("beamer", getRealIconFile("beamer"), tr("Beamer Commands"), "beamer_tags.xml");
  670     addTagList("xymatrix", getRealIconFile("xy"), tr("XY Commands"), "xymatrix_tags.xml");
  671 
  672     addMacrosAsTagList();
  673 
  674     leftPanel->showWidgets();
  675 
  676     // OUTPUT WIDGETS
  677     if (!outputView) {
  678         outputView = new OutputViewWidget(this);
  679         outputView->setObjectName("OutputView");
  680         centralVSplitter->addWidget(outputView);
  681         outputView->toggleViewAction()->setChecked(configManager.getOption("GUI/outputView/visible", true).toBool());
  682         centralVSplitter->setStretchFactor(1, 0);
  683         centralVSplitter->restoreState(configManager.getOption("centralVSplitterState").toByteArray());
  684 
  685         connect(outputView->getLogWidget(), SIGNAL(logEntryActivated(int)), this, SLOT(gotoLogEntryEditorOnly(int)));
  686         connect(outputView->getLogWidget(), SIGNAL(logLoaded()), this, SLOT(updateLogEntriesInEditors()));
  687         connect(outputView->getLogWidget(), SIGNAL(logResetted()), this, SLOT(clearLogEntriesInEditors()));
  688         connect(outputView, SIGNAL(pageChanged(QString)), this, SLOT(outputPageChanged(QString)));
  689         connect(outputView->getSearchResultWidget(), SIGNAL(jumpToSearchResult(QDocument *, int, const SearchQuery *)), this, SLOT(jumpToSearchResult(QDocument *, int, const SearchQuery *)));
  690         connect(outputView->getSearchResultWidget(), SIGNAL(runSearch(SearchQuery *)), this, SLOT(runSearch(SearchQuery *)));
  691 
  692         connect(&buildManager, SIGNAL(previewAvailable(const QString &, const PreviewSource &)), this, SLOT(previewAvailable(const QString &, const PreviewSource &)));
  693         connect(&buildManager, SIGNAL(processNotification(QString)), SLOT(processNotification(QString)));
  694         connect(&buildManager, SIGNAL(clearLogs()), SLOT(clearLogs()));
  695 
  696         connect(&buildManager, SIGNAL(beginRunningCommands(QString, bool, bool, bool)), SLOT(beginRunningCommand(QString, bool, bool, bool)));
  697         connect(&buildManager, SIGNAL(beginRunningSubCommand(ProcessX *, QString, QString, RunCommandFlags)), SLOT(beginRunningSubCommand(ProcessX *, QString, QString, RunCommandFlags)));
  698         connect(&buildManager, SIGNAL(endRunningSubCommand(ProcessX *, QString, QString, RunCommandFlags)), SLOT(endRunningSubCommand(ProcessX *, QString, QString, RunCommandFlags)));
  699         connect(&buildManager, SIGNAL(endRunningCommands(QString, bool, bool, bool)), SLOT(endRunningCommand(QString, bool, bool, bool)));
  700         connect(&buildManager, SIGNAL(latexCompiled(LatexCompileResult *)), SLOT(viewLogOrReRun(LatexCompileResult *)));
  701         connect(&buildManager, SIGNAL(runInternalCommand(QString, QFileInfo, QString)), SLOT(runInternalCommand(QString, QFileInfo, QString)));
  702         connect(&buildManager, SIGNAL(commandLineRequested(QString, QString *, bool *)), SLOT(commandLineRequested(QString, QString *, bool *)));
  703 
  704         addAction(outputView->toggleViewAction());
  705         QAction *temp = new QAction(this);
  706         temp->setSeparator(true);
  707         addAction(temp);
  708     }
  709     sidePanelSplitter->restoreState(configManager.getOption("GUI/sidePanelSplitter/state").toByteArray());
  710 }
  711 
  712 void Texstudio::updateToolBarMenu(const QString &menuName)
  713 {
  714     QMenu *menu = configManager.getManagedMenu(menuName);
  715     if (!menu) return;
  716     LatexEditorView *edView = currentEditorView();
  717     foreach (const ManagedToolBar &tb, configManager.managedToolBars)
  718         if (tb.toolbar && tb.actualActions.contains(menuName))
  719             foreach (QObject *w, tb.toolbar->children())
  720                 if (w->property("menuID").toString() == menuName) {
  721                     QToolButton *combo = qobject_cast<QToolButton *>(w);
  722                     REQUIRE(combo);
  723 
  724                     QFontMetrics fontMetrics(tb.toolbar->font());
  725                     QStringList actionTexts;
  726                     QList<QIcon> actionIcons;
  727                     int defaultIndex = -1;
  728                     foreach (const QAction *act, menu->actions())
  729                         if (!act->isSeparator()) {
  730                             actionTexts.append(act->text());
  731                             actionIcons.append(act->icon());
  732                             if (menuName == "main/view/documents" && edView == act->data().value<LatexEditorView *>()) {
  733                                 defaultIndex = actionTexts.length() - 1;
  734                             }
  735                         }
  736 
  737                     //qDebug() << "**" << actionTexts;
  738                     UtilsUi::createComboToolButton(tb.toolbar, actionTexts, actionIcons, -1, this, SLOT(callToolButtonAction()), defaultIndex, combo);
  739 
  740                     if (menuName == "main/view/documents") {
  741                         // workaround to select the current document
  742                         // combobox uses separate actions. So we have to get the current action from the menu (by comparing its data()
  743                         // attribute to the currentEditorView(). Then map it to a combobox action using the index.
  744                         // TODO: should this menu be provided by Editors?
  745                         LatexEditorView *edView = currentEditorView();
  746                         foreach (QAction* act, menu->actions()) {
  747                             qDebug() << act->data().value<LatexEditorView *>() << combo;
  748                             if (edView == act->data().value<LatexEditorView *>()) {
  749                                 int i = menu->actions().indexOf(act);
  750                                 qDebug() << i << combo->menu()->actions().length();
  751                                 if (i < 0 || i>= combo->menu()->actions().length()) continue;
  752                                 foreach (QAction *act, menu->actions()) {
  753                                     qDebug() << "menu" << act->text();
  754                                 }
  755                                 foreach (QAction *act, combo->menu()->actions()) {
  756                                     qDebug() << "cmb" << act->text();
  757                                 }
  758 
  759                                 combo->setDefaultAction(combo->menu()->actions()[i]);
  760                             }
  761                         }
  762                     }
  763                 }
  764 }
  765 
  766 // we different native shortcuts on OSX and Win/Linux
  767 // note: in particular many key combination with arrows are reserved for text navigation in OSX
  768 // and we already set them in QEditor. Don't overwrite them here.
  769 #ifdef Q_OS_MAC
  770 #define MAC_OTHER(shortcutMac, shortcutOther) shortcutMac
  771 #else
  772 #define MAC_OTHER(shortcutMac, shortcutOther) shortcutOther
  773 #endif
  774 /*! \brief set-up all menus in the menu-bar
  775  *
  776  * This function is called whenever the menu changes (= start and retranslation)
  777  * This means if you call it repeatedly with the same language setting it should not change anything
  778  * Currently this is not true, because it adds additional separator, which are invisible
  779  * creates new action groups and new context menu, although all invisible, they are a memory leak
  780  * But not a bad one, because no one is expected to change the language multiple times
  781  */
  782 void Texstudio::setupMenus()
  783 {
  784     //This function is called whenever the menu changes (= start and retranslation)
  785     //This means if you call it repeatedly with the same language setting it should not change anything
  786     //Currently this is not true, because it adds additional separator, which are invisible
  787     //creates new action groups and new context menu, although all invisible, they are a memory leak
  788     //But not a bad one, because no one is expected to change the language multiple times
  789     //TODO: correct somewhen
  790 
  791     configManager.menuParent = this;
  792     if(configManager.menuParents.isEmpty()){
  793         configManager.menuParents.append(this);
  794     }
  795     configManager.menuParentsBar = menuBar();
  796 
  797     //file
  798     QMenu *menu = newManagedMenu("main/file", tr("&File"));
  799     //getManagedMenu("main/file");
  800     newManagedAction(menu, "new", tr("&New"), SLOT(fileNew()), QKeySequence::New, "document-new");
  801     newManagedAction(menu, "newfromtemplate", tr("New From &Template..."), SLOT(fileNewFromTemplate()));
  802     newManagedAction(menu, "open", tr("&Open..."), SLOT(fileOpen()), QKeySequence::Open, "document-open");
  803 
  804     QMenu *submenu = newManagedMenu(menu, "openrecent", tr("Open &Recent")); //only create the menu here, actions are created by config manager
  805 
  806     submenu = newManagedMenu(menu, "session", tr("Session"));
  807     newManagedAction(submenu, "loadsession", tr("Load Session..."), SLOT(fileLoadSession()));
  808     newManagedAction(submenu, "savesession", tr("Save Session..."), SLOT(fileSaveSession()));
  809     newManagedAction(submenu, "restoresession", tr("Restore Previous Session"), SLOT(fileRestoreSession()));
  810     submenu->addSeparator();
  811     if (!recentSessionList) {
  812         recentSessionList = new SessionList(submenu, this);
  813         connect(recentSessionList, SIGNAL(loadSessionRequest(QString)), this, SLOT(loadSession(QString)));
  814     }
  815     recentSessionList->updateMostRecentMenu();
  816 
  817     menu->addSeparator();
  818     actSave = newManagedAction(menu, "save", tr("&Save"), SLOT(fileSave()), QKeySequence::Save, "document-save");
  819     newManagedAction(menu, "saveas", tr("Save &As..."), SLOT(fileSaveAs()), filterLocaleShortcut(Qt::CTRL + Qt::ALT + Qt::Key_S));
  820     newManagedAction(menu, "saveall", tr("Save A&ll"), SLOT(fileSaveAll()), Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_S);
  821     newManagedAction(menu, "maketemplate", tr("&Make Template..."), SLOT(fileMakeTemplate()));
  822 
  823 
  824     submenu = newManagedMenu(menu, "utilities", tr("Fifi&x"));
  825     newManagedAction(submenu, "rename", tr("Save renamed/&moved file..."), "fileUtilCopyMove", 0, QString(), QList<QVariant>() << true);
  826     newManagedAction(submenu, "copy", tr("Save copied file..."), "fileUtilCopyMove", 0, QString(), QList<QVariant>() << false);
  827     newManagedAction(submenu, "delete", tr("&Delete file"), SLOT(fileUtilDelete()));
  828     newManagedAction(submenu, "chmod", tr("Set &permissions..."), SLOT(fileUtilPermissions()));
  829     submenu->addSeparator();
  830     newManagedAction(submenu, "revert", tr("&Revert to saved..."), SLOT(fileUtilRevert()));
  831     submenu->addSeparator();
  832     newManagedAction(submenu, "copyfilename", tr("Copy filename to &clipboard"), SLOT(fileUtilCopyFileName()));
  833     newManagedAction(submenu, "copymasterfilename", tr("Copy master filename to clipboard"), SLOT(fileUtilCopyMasterFileName()));
  834 
  835     QMenu *svnSubmenu = newManagedMenu(menu, "svn", tr("S&VN..."));
  836     newManagedAction(svnSubmenu, "checkin", tr("Check &in..."), SLOT(fileCheckin()));
  837     newManagedAction(svnSubmenu, "svnupdate", tr("SVN &update..."), SLOT(fileUpdate()));
  838     newManagedAction(svnSubmenu, "svnupdatecwd", tr("SVN update &work directory"), SLOT(fileUpdateCWD()));
  839     newManagedAction(svnSubmenu, "showrevisions", tr("Sh&ow old Revisions"), SLOT(showOldRevisions()));
  840     newManagedAction(svnSubmenu, "lockpdf", tr("Lock &PDF"), SLOT(fileLockPdf()));
  841     newManagedAction(svnSubmenu, "checkinpdf", tr("Check in P&DF"), SLOT(fileCheckinPdf()));
  842     newManagedAction(svnSubmenu, "difffiles", tr("Show difference between two files"), SLOT(fileDiff()));
  843     newManagedAction(svnSubmenu, "diff3files", tr("Show difference between two files in relation to base file"), SLOT(fileDiff3()));
  844     newManagedAction(svnSubmenu, "checksvnconflict", tr("Check SVN Conflict"), SLOT(checkSVNConflicted()));
  845     newManagedAction(svnSubmenu, "mergediff", tr("Try to merge differences"), SLOT(fileDiffMerge()));
  846     newManagedAction(svnSubmenu, "removediffmakers", tr("Remove Difference-Markers"), SLOT(removeDiffMarkers()));
  847     newManagedAction(svnSubmenu, "declareresolved", tr("Declare Conflict Resolved"), SLOT(declareConflictResolved()));
  848     newManagedAction(svnSubmenu, "nextdiff", tr("Jump to next difference"), SLOT(jumpNextDiff()), 0, "go-next-diff");
  849     newManagedAction(svnSubmenu, "prevdiff", tr("Jump to previous difference"), SLOT(jumpPrevDiff()), 0, "go-previous-diff");
  850 
  851     menu->addSeparator();
  852     newManagedAction(menu, "close", tr("&Close"), SLOT(fileClose()), Qt::CTRL + Qt::Key_W, "document-close");
  853     newManagedAction(menu, "closeall", tr("Clos&e All"), SLOT(fileCloseAll()));
  854 
  855     menu->addSeparator();
  856     newManagedEditorAction(menu, "print", tr("Print Source Code..."), "print", Qt::CTRL + Qt::Key_P);
  857 
  858     menu->addSeparator();
  859     newManagedAction(menu, "exit", tr("Exit"), SLOT(fileExit()), Qt::CTRL + Qt::Key_Q)->setMenuRole(QAction::QuitRole);
  860 
  861     //edit
  862     menu = newManagedMenu("main/edit", tr("&Edit"));
  863     actUndo = newManagedAction(menu, "undo", tr("&Undo"), SLOT(editUndo()), QKeySequence::Undo, "edit-undo");
  864     actRedo = newManagedAction(menu, "redo", tr("&Redo"), SLOT(editRedo()), QKeySequence::Redo, "edit-redo");
  865 #ifndef QT_NO_DEBUG
  866     newManagedAction(menu, "debughistory", tr("Debug undo stack"), SLOT(editDebugUndoStack()));
  867 #endif
  868     menu->addSeparator();
  869     newManagedAction(menu, "copy", tr("&Copy"), SLOT(editCopy()), QKeySequence::Copy, "edit-copy");
  870     newManagedEditorAction(menu, "cut", tr("C&ut"), "cut", QKeySequence::Cut, "edit-cut");
  871     newManagedAction(menu, "paste", tr("&Paste"), SLOT(editPaste()), QKeySequence::Paste, "edit-paste");
  872 
  873     submenu = newManagedMenu(menu, "selection", tr("&Selection"));
  874     newManagedEditorAction(submenu, "selectAll", tr("Select &All"), "selectAll", Qt::CTRL + Qt::Key_A);
  875     newManagedEditorAction(submenu, "selectAllOccurences", tr("Select All &Occurrences"), "selectAllOccurences");
  876     newManagedEditorAction(submenu, "expandSelectionToWord", tr("Expand Selection to Word"), "selectExpandToNextWord", Qt::CTRL + Qt::Key_D);
  877     newManagedEditorAction(submenu, "expandSelectionToLine", tr("Expand Selection to Line"), "selectExpandToNextLine", Qt::CTRL + Qt::Key_L);
  878 
  879     submenu = newManagedMenu(menu, "lineoperations", tr("&Line Operations"));
  880     newManagedAction(submenu, "deleteLine", tr("Delete &Line"), SLOT(editDeleteLine()), Qt::CTRL + Qt::Key_K);
  881     newManagedAction(submenu, "deleteToEndOfLine", tr("Delete To &End Of Line"), SLOT(editDeleteToEndOfLine()), MAC_OTHER(Qt::CTRL + Qt::Key_Delete,  Qt::AltModifier + Qt::Key_K));
  882     newManagedAction(submenu, "deleteFromStartOfLine", tr("Delete From &Start Of Line"), SLOT(editDeleteFromStartOfLine()), MAC_OTHER(Qt::CTRL + Qt::Key_Backspace, 0));
  883     newManagedAction(submenu, "moveLineUp", tr("Move Line &Up"), SLOT(editMoveLineUp()));
  884     newManagedAction(submenu, "moveLineDown", tr("Move Line &Down"), SLOT(editMoveLineDown()));
  885     newManagedAction(submenu, "duplicateLine", tr("Du&plicate Line"), SLOT(editDuplicateLine()));
  886     newManagedAction(submenu, "alignMirrors", tr("&Align Cursors"), SLOT(editAlignMirrors()));
  887 
  888     submenu = newManagedMenu(menu, "textoperations", tr("&Text Operations"));
  889     newManagedAction(submenu, "textToLowercase", tr("To Lowercase"), SLOT(editTextToLowercase()));
  890     newManagedAction(submenu, "textToUppercase", tr("To Uppercase"), SLOT(editTextToUppercase()));
  891     newManagedAction(submenu, "textToTitlecaseStrict", tr("To Titlecase (strict)"), SLOT(editTextToTitlecase()));
  892     newManagedAction(submenu, "textToTitlecaseSmart", tr("To Titlecase (smart)"), SLOT(editTextToTitlecaseSmart()));
  893 
  894 
  895     menu->addSeparator();
  896     submenu = newManagedMenu(menu, "searching", tr("&Searching"));
  897     newManagedAction(submenu, "find", tr("&Find"), SLOT(editFind()), QKeySequence::Find);
  898     newManagedEditorAction(submenu, "findnext", tr("Find &Next"), "findNext", MAC_OTHER(Qt::CTRL + Qt::Key_G, Qt::Key_F3));
  899     newManagedEditorAction(submenu, "findprev", tr("Find &Prev"), "findPrev", MAC_OTHER(Qt::CTRL + Qt::SHIFT + Qt::Key_G, Qt::SHIFT + Qt::Key_F3));
  900     newManagedEditorAction(submenu, "findinsamedir", tr("Continue F&ind"), "findInSameDir");
  901     newManagedEditorAction(submenu, "findcount", tr("&Count"), "findCount");
  902     newManagedEditorAction(submenu, "select", tr("&Select all matches..."), "selectAllMatches");
  903     submenu->addSeparator();
  904     newManagedEditorAction(submenu, "replace", tr("&Replace"), "replacePanel", Qt::CTRL + Qt::Key_R);
  905     newManagedEditorAction(submenu, "replacenext", tr("Replace Next"), "replaceNext");
  906     newManagedEditorAction(submenu, "replaceprev", tr("Replace Prev"), "replacePrev");
  907     newManagedEditorAction(submenu, "replaceall", tr("Replace &All"), "replaceAll");
  908 
  909     menu->addSeparator();
  910     submenu = newManagedMenu(menu, "goto", tr("Go to"));
  911 
  912     newManagedEditorAction(submenu, "line", tr("Line"), "gotoLine", MAC_OTHER(Qt::CTRL + Qt::Key_L, Qt::CTRL + Qt::Key_G), "goto");
  913     newManagedEditorAction(submenu, "lastchange", tr("Previous Change"), "jumpChangePositionBackward", Qt::CTRL + Qt::Key_H);
  914     newManagedEditorAction(submenu, "nextchange", tr("Next Change"), "jumpChangePositionForward", Qt::CTRL + Qt::SHIFT + Qt::Key_H);
  915     submenu->addSeparator();
  916     newManagedAction(submenu, "markprev", tr("Previous mark"), "gotoMark", MAC_OTHER(0, Qt::CTRL + Qt::Key_Up), "", QList<QVariant>() << true << -1); //, ":/images/errorprev.png");
  917     newManagedAction(submenu, "marknext", tr("Next mark"), "gotoMark", MAC_OTHER(0, Qt::CTRL + Qt::Key_Down), "", QList<QVariant>() << false << -1); //, ":/images/errornext.png");
  918     submenu->addSeparator();
  919     if (cursorHistory) {
  920         cursorHistory->setBackAction(newManagedAction(submenu, "goback", tr("Go Back"), SLOT(goBack()), MAC_OTHER(0, Qt::ALT + Qt::Key_Left), "back"));
  921         cursorHistory->setForwardAction(newManagedAction(submenu, "goforward", tr("Go Forward"), SLOT(goForward()), MAC_OTHER(0, Qt::ALT + Qt::Key_Right), "forward"));
  922     }
  923 
  924     submenu = newManagedMenu(menu, "gotoBookmark", tr("Goto Bookmark"));
  925     QList<int> bookmarkIndicies = QList<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 0;
  926     foreach (int i, bookmarkIndicies) {
  927         QKeySequence shortcut;
  928         if (i != 0)
  929             shortcut = Qt::CTRL + Qt::Key_0 + i;
  930         newManagedEditorAction(submenu, QString("bookmark%1").arg(i), tr("Bookmark %1").arg(i), "jumpToBookmark", shortcut, "", QList<QVariant>() << i);
  931     }
  932 
  933 
  934     submenu = newManagedMenu(menu, "toggleBookmark", tr("Toggle Bookmark"));
  935     newManagedEditorAction(submenu, QString("bookmark"), tr("Unnamed Bookmark"), "toggleBookmark", Qt::CTRL + Qt::SHIFT + Qt::Key_B, "", QList<QVariant>() << -1);
  936     foreach (int i, bookmarkIndicies)
  937         newManagedEditorAction(submenu, QString("bookmark%1").arg(i), tr("Bookmark %1").arg(i), "toggleBookmark", Qt::CTRL + Qt::SHIFT + Qt::Key_0 + i, "", QList<QVariant>() << i);
  938 
  939 
  940     menu->addSeparator();
  941     submenu = newManagedMenu(menu, "lineend", tr("Line Ending"));
  942     QActionGroup *lineEndingGroup = new QActionGroup(this);
  943     QAction *act = newManagedAction(submenu, "crlf", tr("DOS/Windows (CR LF)"), SLOT(editChangeLineEnding()));
  944     act->setData(QDocument::Windows);
  945     act->setCheckable(true);
  946     lineEndingGroup->addAction(act);
  947     act = newManagedAction(submenu, "lf", tr("Unix (LF)"), SLOT(editChangeLineEnding()));
  948     act->setData(QDocument::Unix);
  949     act->setCheckable(true);
  950     lineEndingGroup->addAction(act);
  951     act = newManagedAction(submenu, "cr", tr("Old Mac (CR)"), SLOT(editChangeLineEnding()));
  952     act->setData(QDocument::Mac);
  953     act->setCheckable(true);
  954     lineEndingGroup->addAction(act);
  955 
  956 
  957     newManagedAction(menu, "encoding", tr("Setup Encoding..."), SLOT(editSetupEncoding()))->setMenuRole(QAction::NoRole); // with the default "QAction::TextHeuristicRole" this was interperted as Preferences on OSX
  958     newManagedAction(menu, "unicodeChar", tr("Insert Unicode Character..."), SLOT(editInsertUnicode()), filterLocaleShortcut(Qt::ALT + Qt::CTRL + Qt::Key_U));
  959 
  960 
  961 
  962     //Edit 2 (for LaTeX related things)
  963     menu = newManagedMenu("main/edit2", tr("&Idefix"));
  964     newManagedAction(menu, "eraseWord", tr("Erase &Word/Cmd/Env"), SLOT(editEraseWordCmdEnv()), MAC_OTHER(0, Qt::ALT + Qt::Key_Delete));
  965 
  966     menu->addSeparator();
  967     newManagedAction(menu, "pasteAsLatex", tr("Pas&te as LaTeX"), SLOT(editPasteLatex()), Qt::CTRL + Qt::SHIFT + Qt::Key_V, "editpaste");
  968     newManagedAction(menu, "convertTo", tr("Co&nvert to LaTeX"), SLOT(convertToLatex()));
  969     newManagedAction(menu, "previewLatex", tr("Pre&view Selection/Parentheses"), SLOT(previewLatex()), Qt::ALT + Qt::Key_P);
  970     newManagedAction(menu, "removePreviewLatex", tr("C&lear Inline Preview"), SLOT(clearPreview()));
  971 
  972     menu->addSeparator();
  973     newManagedEditorAction(menu, "togglecomment", tr("Toggle &Comment"), "toggleCommentSelection", Qt::CTRL + Qt::Key_T);
  974     newManagedEditorAction(menu, "comment", tr("&Comment"), "commentSelection");
  975     newManagedEditorAction(menu, "uncomment", tr("&Uncomment"), "uncommentSelection", Qt::CTRL + Qt::Key_U);
  976     newManagedEditorAction(menu, "indent", tr("&Indent"), "indentSelection");
  977     newManagedEditorAction(menu, "unindent", tr("Unin&dent"), "unindentSelection");
  978     newManagedAction(menu, "hardbreak", tr("Hard Line &Break..."), SLOT(editHardLineBreak()));
  979     newManagedAction(menu, "hardbreakrepeat", tr("R&epeat Hard Line Break"), SLOT(editHardLineBreakRepeat()));
  980 
  981     menu->addSeparator();
  982     submenu = newManagedMenu(menu, "goto", tr("&Go to"));
  983 
  984     newManagedAction(submenu, "errorprev", tr("Previous Error"), "gotoNearLogEntry", MAC_OTHER(0, Qt::CTRL + Qt::SHIFT + Qt::Key_Up), "errorprev", QList<QVariant>() << LT_ERROR << true << tr("No LaTeX errors detected !"));
  985     newManagedAction(submenu, "errornext", tr("Next Error"), "gotoNearLogEntry", MAC_OTHER(0, Qt::CTRL + Qt::SHIFT + Qt::Key_Down), "errornext", QList<QVariant>() << LT_ERROR << false << tr("No LaTeX errors detected !"));
  986     newManagedAction(submenu, "warningprev", tr("Previous Warning"), "gotoNearLogEntry", QKeySequence(), "", QList<QVariant>() << LT_WARNING << true << tr("No LaTeX warnings detected !")); //, ":/images/errorprev.png");
  987     newManagedAction(submenu, "warningnext", tr("Next Warning"), "gotoNearLogEntry", QKeySequence(), "", QList<QVariant>() << LT_WARNING << false << tr("No LaTeX warnings detected !")); //, ":/images/errornext.png");
  988     newManagedAction(submenu, "badboxprev", tr("Previous Bad Box"), "gotoNearLogEntry", MAC_OTHER(0, Qt::SHIFT + Qt::ALT + Qt::Key_Up), "", QList<QVariant>() << LT_BADBOX << true << tr("No bad boxes detected !")); //, ":/images/errorprev.png");
  989     newManagedAction(submenu, "badboxnext", tr("Next Bad Box"), "gotoNearLogEntry", MAC_OTHER(0, Qt::SHIFT + Qt::ALT + Qt::Key_Down), "", QList<QVariant>() << LT_BADBOX << true << tr("No bad boxes detected !")); //, ":/images/errornext.png");
  990     submenu->addSeparator();
  991     newManagedAction(submenu, "definition", tr("Definition"), SLOT(editGotoDefinition()), filterLocaleShortcut(Qt::CTRL + Qt::ALT + Qt::Key_F));
  992 
  993     menu->addSeparator();
  994     newManagedAction(menu, "generateMirror", tr("Re&name Environment"), SLOT(generateMirror()));
  995 
  996     submenu = newManagedMenu(menu, "parens", tr("Parenthesis"));
  997     newManagedAction(submenu, "jump", tr("Jump to Match"), SLOT(jumpToBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_J));
  998     newManagedAction(submenu, "selectBracketInner", tr("Select Inner"), SLOT(selectBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_I))->setProperty("type", "inner");
  999     newManagedAction(submenu, "selectBracketOuter", tr("Select Outer"), SLOT(selectBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_O))->setProperty("type", "outer");
 1000     newManagedAction(submenu, "selectBracketCommand", tr("Select Command"), SLOT(selectBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_C))->setProperty("type", "command");
 1001     newManagedAction(submenu, "selectBracketLine", tr("Select Line"), SLOT(selectBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_L))->setProperty("type", "line");
 1002     newManagedAction(submenu, "generateInvertedBracketMirror", tr("Select Inverting"), SLOT(generateBracketInverterMirror()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_S));
 1003 
 1004     submenu->addSeparator();
 1005     newManagedAction(submenu, "findMissingBracket", tr("Find Mismatch"), SLOT(findMissingBracket()), QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_P, Qt::Key_M));
 1006 
 1007     submenu = newManagedMenu(menu, "complete", tr("Complete"));
 1008     newManagedAction(submenu, "normal", tr("Normal"), SLOT(normalCompletion()), MAC_OTHER(Qt::META + Qt::Key_Space, Qt::CTRL + Qt::Key_Space));
 1009     newManagedAction(submenu, "environment", tr("\\begin{ Completion"), SLOT(insertEnvironmentCompletion()), Qt::CTRL + Qt::ALT + Qt::Key_Space);
 1010     newManagedAction(submenu, "text", tr("Normal Text"), SLOT(insertTextCompletion()), Qt::SHIFT + Qt::ALT + Qt::Key_Space);
 1011     newManagedAction(submenu, "closeEnvironment", tr("Close latest open environment"), SLOT(closeEnvironment()), Qt::ALT + Qt::Key_Return);
 1012 
 1013     menu->addSeparator();
 1014     newManagedAction(menu, "reparse", tr("Refresh Structure"), SLOT(updateStructure()));
 1015     act = newManagedAction(menu, "refreshQNFA", tr("Refresh Language Model"), SLOT(updateTexQNFA()));
 1016     act->setStatusTip(tr("Force an update of the dynamic language model used for highlighting and folding. Likely, you do not need to call this because updates are usually automatic."));
 1017     newManagedAction(menu, "removePlaceHolders", tr("Remove Placeholders"), SLOT(editRemovePlaceHolders()), Qt::CTRL + Qt::SHIFT + Qt::Key_K);
 1018     newManagedAction(menu, "removeCurrentPlaceHolder", tr("Remove Current Placeholder"), SLOT(editRemoveCurrentPlaceHolder()));
 1019 
 1020     //tools
 1021 
 1022 
 1023     menu = newManagedMenu("main/tools", tr("&Tools"));
 1024     menu->setProperty("defaultSlot", QByteArray(SLOT(commandFromAction())));
 1025     newManagedAction(menu, "quickbuild", tr("&Build && View"), SLOT(commandFromAction()), (QList<QKeySequence>() << Qt::Key_F5 << Qt::Key_F1), "build")->setData(BuildManager::CMD_QUICK);
 1026     newManagedAction(menu, "compile", tr("&Compile"), SLOT(commandFromAction()), Qt::Key_F6, "compile")->setData(BuildManager::CMD_COMPILE);
 1027     QAction *stopAction = new QAction(getRealIcon("stop"), tr("Stop Compile"), menu);
 1028     connect(stopAction, SIGNAL(triggered()), &buildManager, SLOT(killCurrentProcess()));
 1029     newManagedAction(menu, "stopcompile", stopAction)->setEnabled(false);
 1030     connect(&buildManager,SIGNAL(buildRunning(bool)),this,SLOT(setBuildButtonsDisabled(bool)));
 1031     newManagedAction(menu, "view", tr("&View"), SLOT(commandFromAction()), Qt::Key_F7, "viewer")->setData(BuildManager::CMD_VIEW);
 1032     newManagedAction(menu, "bibtex", tr("&Bibliography"), SLOT(commandFromAction()), Qt::Key_F8)->setData(BuildManager::CMD_BIBLIOGRAPHY);
 1033     newManagedAction(menu, "glossary", tr("&Glossary"), SLOT(commandFromAction()), Qt::Key_F9)->setData(BuildManager::CMD_GLOSSARY);
 1034     newManagedAction(menu, "index", tr("&Index"), SLOT(commandFromAction()))->setData(BuildManager::CMD_INDEX);
 1035 
 1036     menu->addSeparator();
 1037     submenu = newManagedMenu(menu, "commands", tr("&Commands", "menu"));
 1038     newManagedAction(submenu, "latexmk", tr("&Latexmk"), SLOT(commandFromAction()))->setData(BuildManager::CMD_LATEXMK);
 1039     submenu->addSeparator();
 1040     newManagedAction(submenu, "latex", tr("&LaTeX"), SLOT(commandFromAction()), QKeySequence(), "compile-latex")->setData(BuildManager::CMD_LATEX);
 1041     newManagedAction(submenu, "pdflatex", tr("&PDFLaTeX"), SLOT(commandFromAction()), QKeySequence(), "compile-pdf")->setData(BuildManager::CMD_PDFLATEX);
 1042     newManagedAction(submenu, "xelatex", "&XeLaTeX", SLOT(commandFromAction()), QKeySequence(), "compile-xelatex")->setData(BuildManager::CMD_XELATEX);
 1043     newManagedAction(submenu, "lualatex", "L&uaLaTeX", SLOT(commandFromAction()), QKeySequence(), "compile-lua")->setData(BuildManager::CMD_LUALATEX);
 1044     submenu->addSeparator();
 1045     newManagedAction(submenu, "dvi2ps", tr("DVI->PS"), SLOT(commandFromAction()), QKeySequence(), "convert-dvips")->setData(BuildManager::CMD_DVIPS);
 1046     newManagedAction(submenu, "ps2pdf", tr("P&S->PDF"), SLOT(commandFromAction()), QKeySequence(), "convert-pspdf")->setData(BuildManager::CMD_PS2PDF);
 1047     newManagedAction(submenu, "dvipdf", tr("DV&I->PDF"), SLOT(commandFromAction()), QKeySequence(), "convert-dvipdf")->setData(BuildManager::CMD_DVIPDF);
 1048     submenu->addSeparator();
 1049     newManagedAction(submenu, "viewdvi", tr("View &DVI"), SLOT(commandFromAction()), QKeySequence(), "view-doc-dvi")->setData(BuildManager::CMD_VIEW_DVI);
 1050     newManagedAction(submenu, "viewps", tr("Vie&w PS"), SLOT(commandFromAction()), QKeySequence(), "view-doc-ps")->setData(BuildManager::CMD_VIEW_PS);
 1051     newManagedAction(submenu, "viewpdf", tr("View PD&F"), SLOT(commandFromAction()), QKeySequence(), "view-doc-pdf")->setData(BuildManager::CMD_VIEW_PDF);
 1052     submenu->addSeparator();
 1053     newManagedAction(submenu, "bibtex", tr("&Bibtex"), SLOT(commandFromAction()))->setData(BuildManager::CMD_BIBTEX);
 1054     newManagedAction(submenu, "bibtex8", tr("&Bibtex 8-Bit"), SLOT(commandFromAction()))->setData(BuildManager::CMD_BIBTEX8);
 1055     newManagedAction(submenu, "biber", tr("Bibe&r"), SLOT(commandFromAction()))->setData(BuildManager::CMD_BIBER);
 1056     submenu->addSeparator();
 1057     newManagedAction(submenu, "makeindex", tr("&MakeIndex"), SLOT(commandFromAction()))->setData(BuildManager::CMD_MAKEINDEX);
 1058     newManagedAction(submenu, "texindy", tr("&TexIndy"), SLOT(commandFromAction()), QKeySequence())->setData(BuildManager::CMD_TEXINDY);
 1059     newManagedAction(submenu, "makeglossaries", tr("&Makeglossaries"), SLOT(commandFromAction()), QKeySequence())->setData(BuildManager::CMD_MAKEGLOSSARIES);
 1060     submenu->addSeparator();
 1061     newManagedAction(submenu, "metapost", tr("&MetaPost"), SLOT(commandFromAction()))->setData(BuildManager::CMD_METAPOST);
 1062     newManagedAction(submenu, "asymptote", tr("&Asymptote"), SLOT(commandFromAction()))->setData(BuildManager::CMD_ASY);
 1063 
 1064     submenu = newManagedMenu(menu, "user", tr("&User", "menu"));
 1065     updateUserToolMenu();
 1066     menu->addSeparator();
 1067     newManagedAction(menu, "clean", tr("Cle&an Auxiliary Files..."), SLOT(cleanAll()));
 1068     newManagedAction(menu, "terminal", tr("Open &Terminal"), SLOT(openTerminal()));
 1069     menu->addSeparator();
 1070     newManagedAction(menu, "viewlog", tr("View &Log"), SLOT(commandFromAction()), QKeySequence(), "viewlog")->setData(BuildManager::CMD_VIEW_LOG);
 1071     act = newManagedAction(menu, "logmarkers", tr("Show Log Markers"), nullptr, 0, "logmarkers");
 1072     act->setCheckable(true);
 1073     connect(act, SIGNAL(triggered(bool)), SLOT(setLogMarksVisible(bool)));
 1074     menu->addSeparator();
 1075     newManagedAction(menu, "htmlexport", tr("C&onvert to Html..."), SLOT(webPublish()));
 1076     newManagedAction(menu, "htmlsourceexport", tr("C&onvert Source to Html..."), SLOT(webPublishSource()));
 1077     menu->addSeparator();
 1078     newManagedAction(menu, "analysetext", tr("A&nalyse Text..."), SLOT(analyseText()));
 1079     newManagedAction(menu, "generaterandomtext", tr("Generate &Random Text..."), SLOT(generateRandomText()));
 1080     menu->addSeparator();
 1081     newManagedAction(menu, "spelling", tr("Check Spelling..."), SLOT(editSpell()), MAC_OTHER(Qt::CTRL + Qt::SHIFT + Qt::Key_F7, Qt::CTRL + Qt::Key_Colon));
 1082     newManagedAction(menu, "thesaurus", tr("Thesaurus..."), SLOT(editThesaurus()), Qt::CTRL + Qt::SHIFT + Qt::Key_F8);
 1083     newManagedAction(menu, "wordrepetions", tr("Find Word Repetitions..."), SLOT(findWordRepetions()));
 1084 
 1085     //  Latex/Math external
 1086     configManager.loadManagedMenus(":/uiconfig.xml");
 1087     // add some additional items
 1088     menu = newManagedMenu("main/latex", tr("&LaTeX"));
 1089     menu->setProperty("defaultSlot", QByteArray(SLOT(insertFromAction())));
 1090     newManagedAction(menu, "insertrefnextlabel", tr("Insert \\ref to Next Label"), SLOT(editInsertRefToNextLabel()), Qt::ALT + Qt::CTRL + Qt::Key_R);
 1091     newManagedAction(menu, "insertrefprevlabel", tr("Insert \\ref to Previous Label"), SLOT(editInsertRefToPrevLabel()));
 1092     submenu = newManagedMenu(menu, "tabularmanipulation", tr("Manipulate Tables", "table"));
 1093     newManagedAction(submenu, "addRow", tr("Add Row", "table"), SLOT(addRowCB()), QKeySequence(), "addRow");
 1094     newManagedAction(submenu, "addColumn", tr("Add Column", "table"), SLOT(addColumnCB()), QKeySequence(), "addCol");
 1095     newManagedAction(submenu, "removeRow", tr("Remove Row", "table"), SLOT(removeRowCB()), QKeySequence(), "remRow");
 1096     newManagedAction(submenu, "removeColumn", tr("Remove Column", "table"), SLOT(removeColumnCB()), QKeySequence(), "remCol");
 1097     newManagedAction(submenu, "cutColumn", tr("Cut Column", "table"), SLOT(cutColumnCB()), QKeySequence(), "cutCol");
 1098     newManagedAction(submenu, "pasteColumn", tr("Paste Column", "table"), SLOT(pasteColumnCB()), QKeySequence(), "pasteCol");
 1099     newManagedAction(submenu, "addHLine", tr("Add \\hline", "table"), SLOT(addHLineCB()));
 1100     newManagedAction(submenu, "remHLine", tr("Remove \\hline", "table"), SLOT(remHLineCB()));
 1101     newManagedAction(submenu, "insertTableTemplate", tr("Remodel Table Using Template", "table"), SLOT(insertTableTemplate()));
 1102     newManagedAction(submenu, "alignColumns", tr("Align Columns"), SLOT(alignTableCols()), QKeySequence(), "alignCols");
 1103     submenu = newManagedMenu(menu, "magicComments", tr("Add magic comments ..."));
 1104     newManagedAction(submenu, "addMagicRoot", tr("Insert root document name as TeX comment"), SLOT(addMagicRoot()));
 1105     newManagedAction(submenu, "addMagicLang", tr("Insert language as TeX comment"), SLOT(insertSpellcheckMagicComment()));
 1106     newManagedAction(submenu, "addMagicCoding", tr("Insert document coding as TeX comment"), SLOT(addMagicCoding()));
 1107     newManagedAction(submenu, "addMagicProgram", tr("Insert program as TeX comment"), SLOT(addMagicProgram()));
 1108     newManagedAction(submenu, "addMagicBibliography", tr("Insert bibliography tool as TeX comment"), SLOT(addMagicBibliography()));
 1109 
 1110     menu = newManagedMenu("main/math", tr("&Math"));
 1111     menu->setProperty("defaultSlot", QByteArray(SLOT(insertFromAction())));
 1112     //wizards
 1113 
 1114     menu = newManagedMenu("main/wizards", tr("&Wizards"));
 1115     newManagedAction(menu, "start", tr("Quick &Start..."), SLOT(quickDocument()));
 1116     newManagedAction(menu, "beamer", tr("Quick &Beamer Presentation..."), SLOT(quickBeamer()));
 1117     newManagedAction(menu, "letter", tr("Quick &Letter..."), SLOT(quickLetter()));
 1118 
 1119     menu->addSeparator();
 1120     newManagedAction(menu, "tabular", tr("Quick &Tabular..."), SLOT(quickTabular()));
 1121     newManagedAction(menu, "tabbing", tr("Quick T&abbing..."), SLOT(quickTabbing()));
 1122     newManagedAction(menu, "array", tr("Quick &Array..."), SLOT(quickArray()));
 1123     newManagedAction(menu, "graphic", tr("Insert &Graphic..."), SLOT(quickGraphics()), QKeySequence(), "image");
 1124 #ifdef Q_OS_WIN
 1125     if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
 1126         newManagedAction(menu, "math", tr("Math Assistant..."), SLOT(quickMath()), QKeySequence(), "TexTablet");
 1127     }
 1128 #endif
 1129 
 1130     menu = newManagedMenu("main/bibliography", tr("&Bibliography"));
 1131     if (!bibtexEntryActions) {
 1132         bibtexEntryActions = new QActionGroup(this);
 1133         foreach (const BibTeXType &bt, BibTeXDialog::getPossibleEntryTypes(BibTeXDialog::BIBTEX)) {
 1134             QAction *act = newManagedAction(menu, "bibtex/" + bt.name.mid(1), bt.description, SLOT(insertBibEntryFromAction()));
 1135             act->setData(bt.name);
 1136             act->setActionGroup(bibtexEntryActions);
 1137         }
 1138     } else {
 1139         foreach (const BibTeXType &bt, BibTeXDialog::getPossibleEntryTypes(BibTeXDialog::BIBTEX))
 1140             newManagedAction(menu, "bibtex/" + bt.name.mid(1), bt.description, SLOT(insertBibEntryFromAction()))->setData(bt.name);
 1141     }
 1142 
 1143     if (!biblatexEntryActions) {
 1144         biblatexEntryActions = new QActionGroup(this);
 1145         foreach (const BibTeXType &bt, BibTeXDialog::getPossibleEntryTypes(BibTeXDialog::BIBLATEX)) {
 1146             QAction *act = newManagedAction(menu, "biblatex/" + bt.name.mid(1), bt.description, SLOT(insertBibEntryFromAction()));
 1147             act->setData(bt.name);
 1148             act->setActionGroup(biblatexEntryActions);
 1149         }
 1150     } else {
 1151         foreach (const BibTeXType &bt, BibTeXDialog::getPossibleEntryTypes(BibTeXDialog::BIBLATEX))
 1152             newManagedAction(menu, "biblatex/" + bt.name.mid(1), bt.description, SLOT(insertBibEntryFromAction()))->setData(bt.name);
 1153     }
 1154     menu->addSeparator();
 1155     newManagedEditorAction(menu, "clean", tr("&Clean"), "cleanBib");
 1156     menu->addSeparator();
 1157     newManagedAction(menu, "dialog", tr("&Insert Bibliography Entry..."), SLOT(insertBibEntry()));
 1158     menu->addSeparator();
 1159     QMenu *bibTypeMenu = newManagedMenu(menu, "type", tr("Type"));
 1160     if (!bibTypeActions) {
 1161         bibTypeActions = new QActionGroup(this);
 1162         bibTypeActions->setExclusive(true);
 1163         act = newManagedAction(bibTypeMenu, "bibtex", tr("BibTeX"), SLOT(setBibTypeFromAction()));
 1164         act->setData("bibtex");
 1165         act->setCheckable(true);
 1166         act->setChecked(true);
 1167         bibTypeActions->addAction(act);
 1168         act = newManagedAction(bibTypeMenu, "biblatex", tr("BibLaTeX"), SLOT(setBibTypeFromAction()));
 1169         act->setData("biblatex");
 1170         act->setCheckable(true);
 1171         bibTypeActions->addAction(act);
 1172     }
 1173     act = newManagedAction(bibTypeMenu, "bibtex", tr("BibTeX"), SLOT(setBibTypeFromAction()));
 1174     act = newManagedAction(bibTypeMenu, "biblatex", tr("BibLaTeX"), SLOT(setBibTypeFromAction()));
 1175     act->trigger(); // initialize menu for specified type
 1176 
 1177     //  User
 1178     newManagedMenu("main/macros", tr("Ma&cros"));
 1179     updateUserMacros();
 1180     scriptengine::macros = &configManager.completerConfig->userMacros;
 1181 
 1182     //---view---
 1183     menu = newManagedMenu("main/view", tr("&View"));
 1184     newManagedAction(menu, "prevdocument", tr("Previous Document"), SLOT(gotoPrevDocument()), QList<QKeySequence>() << Qt::CTRL + Qt::Key_PageUp << Qt::CTRL + Qt::SHIFT + Qt::Key_Tab);
 1185     newManagedAction(menu, "nextdocument", tr("Next Document"), SLOT(gotoNextDocument()), QList<QKeySequence>() << Qt::CTRL + Qt::Key_PageDown << Qt::CTRL + Qt::Key_Tab);
 1186     newManagedMenu(menu, "documents", tr("Open Documents"));
 1187     newManagedAction(menu, "documentlist", tr("List Of Open Documents"), SLOT(viewDocumentList()));
 1188     newManagedAction(menu, "documentlisthidden", tr("List Of Hidden Documents"), SLOT(viewDocumentListHidden()));
 1189 
 1190     newManagedAction(menu, "focuseditor", tr("Focus Editor"), SLOT(focusEditor()), QList<QKeySequence>() << Qt::ALT + Qt::CTRL + Qt::Key_Left);
 1191     newManagedAction(menu, "focusviewer", tr("Focus Viewer"), SLOT(focusViewer()), QList<QKeySequence>() << Qt::ALT + Qt::CTRL + Qt::Key_Right);
 1192 
 1193     menu->addSeparator();
 1194     submenu = newManagedMenu(menu, "show", tr("Show"));
 1195     newManagedAction(submenu, "structureview", sidePanel->toggleViewAction());
 1196     newManagedAction(submenu, "outputview", outputView->toggleViewAction());
 1197     act = newManagedAction(submenu, "statusbar", tr("Statusbar"), SLOT(showStatusbar()));
 1198     act->setCheckable(true);
 1199     act->setChecked(configManager.getOption("View/ShowStatusbar").toBool());
 1200 
 1201     newManagedAction(menu, "enlargePDF", tr("Show embedded PDF large"), SLOT(enlargeEmbeddedPDFViewer()));
 1202     newManagedAction(menu, "shrinkPDF", tr("Show embedded PDF small"), SLOT(shrinkEmbeddedPDFViewer()));
 1203 
 1204     newManagedAction(menu, "closeelement", tr("Close Element"), SLOT(viewCloseElement()), Qt::Key_Escape);
 1205 
 1206     menu->addSeparator();
 1207     submenu = newManagedMenu(menu, "collapse", tr("Collapse"));
 1208     newManagedEditorAction(submenu, "all", tr("Everything"), "foldEverything", 0, "", QList<QVariant>() << false);
 1209     newManagedAction(submenu, "block", tr("Nearest Block"), SLOT(viewCollapseBlock()));
 1210     for (int i = 1; i <= 4; i++)
 1211         newManagedEditorAction(submenu, QString::number(i), tr("Level %1").arg(i), "foldLevel", 0, "", QList<QVariant>() << false << i);
 1212     submenu = newManagedMenu(menu, "expand", tr("Expand"));
 1213     newManagedEditorAction(submenu, "all", tr("Everything"), "foldEverything", 0, "", QList<QVariant>() << true);
 1214     newManagedAction(submenu, "block", tr("Nearest Block"), SLOT(viewExpandBlock()));
 1215     for (int i = 1; i <= 4; i++)
 1216         newManagedEditorAction(submenu, QString::number(i), tr("Level %1").arg(i), "foldLevel", 0, "", QList<QVariant>() << true << i);
 1217 
 1218     submenu = newManagedMenu(menu, "grammar", tr("Grammar errors"));
 1219     static bool showGrammarType[8] = {false};
 1220     for (int i = 0; i < 8; i++) configManager.registerOption(QString("Grammar/Display Error %1").arg(i), &showGrammarType[i], true);
 1221     newManagedAction(submenu, "0", tr("Word Repetition"), "toggleGrammar", 0, "", QList<QVariant>() << 0);
 1222     newManagedAction(submenu, "1", tr("Long-range Word Repetition"), "toggleGrammar", 0, "", QList<QVariant>() << 1);
 1223     newManagedAction(submenu, "2", tr("Bad words"), "toggleGrammar", 0, "", QList<QVariant>() << 2);
 1224     newManagedAction(submenu, "3", tr("Grammar Mistake"), "toggleGrammar", 0, "", QList<QVariant>() << 3);
 1225     for (int i = 4; i < 8; i++)
 1226         newManagedAction(submenu, QString("%1").arg(i), tr("Grammar Mistake Special %1").arg(i - 3), "toggleGrammar", 0, "", QList<QVariant>() << i);
 1227     for (int i = 0; i < submenu->actions().size(); i++)
 1228         if (!submenu->actions()[i]->isCheckable()) {
 1229             submenu->actions()[i]->setCheckable(true);
 1230             configManager.linkOptionToObject(&showGrammarType[i], submenu->actions()[i], nullptr);
 1231             LatexEditorView::setGrammarOverlayDisabled(i, !submenu->actions()[i]->isChecked());
 1232         }
 1233 
 1234     menu->addSeparator();
 1235     submenu = newManagedMenu(menu, "editorZoom", tr("Editor Zoom"));
 1236     newManagedEditorAction(submenu, "zoomIn", tr("Zoom In"), "zoomIn", Qt::CTRL + Qt::Key_Plus);
 1237     newManagedEditorAction(submenu, "zoomOut", tr("Zoom Out"), "zoomOut", Qt::CTRL + Qt::Key_Minus);
 1238     newManagedEditorAction(submenu, "resetZoom", tr("Reset Zoom"), "resetZoom", Qt::CTRL + Qt::Key_0);
 1239 
 1240 #if QT_VERSION>=0x050000
 1241     fullscreenModeAction = newManagedAction(menu, "fullscreenmode", tr("Full &Screen"), nullptr, QKeySequence::FullScreen);
 1242 #else
 1243     fullscreenModeAction = newManagedAction(menu, "fullscreenmode", tr("Full &Screen"), 0, MAC_OTHER(Qt::CTRL + Qt::META + Qt::Key_F, Qt::Key_F11));
 1244 #endif
 1245     fullscreenModeAction->setCheckable(true);
 1246     connectUnique(fullscreenModeAction, SIGNAL(toggled(bool)), this, SLOT(setFullScreenMode()));
 1247     connectUnique(menuBar(), SIGNAL(doubleClicked()), fullscreenModeAction, SLOT(toggle()));
 1248 
 1249     menu->addSeparator();
 1250     QMenu *hlMenu = newManagedMenu(menu, "highlighting", tr("Highlighting"));
 1251     if (!highlightLanguageActions) {
 1252         highlightLanguageActions = new QActionGroup(this);
 1253         highlightLanguageActions->setExclusive(true);
 1254         connect(highlightLanguageActions, SIGNAL(triggered(QAction *)), this, SLOT(viewSetHighlighting(QAction *)));
 1255         connect(hlMenu, SIGNAL(aboutToShow()), this, SLOT(showHighlightingMenu()));
 1256         int id = 0;
 1257         foreach (const QString &s, m_languages->languages()) {
 1258 #ifdef QT_NO_DEBUG
 1259             if (s == "TXS Test Results") continue;
 1260 #endif
 1261             QAction *act = newManagedAction(hlMenu, QString::number(id++), tr(qPrintable(s)));
 1262             act->setData(s);
 1263             act->setCheckable(true);
 1264             hlMenu->addAction(act);
 1265             highlightLanguageActions->addAction(act);
 1266         }
 1267     } else {
 1268         int id = 0;
 1269         foreach (const QString &s, m_languages->languages())
 1270             newManagedAction(hlMenu, QString::number(id++), tr(qPrintable(s)));
 1271     }
 1272 
 1273     //---options---
 1274     menu = newManagedMenu("main/options", tr("&Options"));
 1275     newManagedAction(menu, "config", tr("&Configure TeXstudio..."), SLOT(generalOptions()), 0, "configure")->setMenuRole(QAction::PreferencesRole);
 1276 
 1277     menu->addSeparator();
 1278     newManagedAction(menu, "loadProfile", tr("Load &Profile..."), SLOT(loadProfile()));
 1279     newManagedAction(menu, "saveProfile", tr("S&ave Profile..."), SLOT(saveProfile()));
 1280     newManagedAction(menu, "saveSettings", tr("Save &Current Settings", "menu"), SLOT(saveSettings()));
 1281     newManagedAction(menu, "restoreDefaultSettings", tr("Restore &Default Settings..."), SLOT(restoreDefaultSettings()));
 1282     menu->addSeparator();
 1283 
 1284     submenu = newManagedMenu(menu, "rootdoc", tr("Root Document", "menu"));
 1285     actgroupRootDocMode = new QActionGroup(this);
 1286     actgroupRootDocMode->setExclusive(true);
 1287     actRootDocAutomatic = newManagedAction(submenu, "auto", tr("Detect &Automatically"), SLOT(setAutomaticRootDetection()));
 1288     actRootDocAutomatic->setCheckable(true);
 1289     actRootDocAutomatic->setChecked(true);
 1290     actgroupRootDocMode->addAction(actRootDocAutomatic);
 1291     actRootDocExplicit = newManagedAction(submenu, "currentExplicit", "Shows Current Explicit Root");
 1292     actRootDocExplicit->setCheckable(true);
 1293     actRootDocExplicit->setVisible(false);
 1294     actgroupRootDocMode->addAction(actRootDocExplicit);
 1295     actRootDocSetExplicit = newManagedAction(submenu, "setExplicit", tr("Set Current Document As Explicit Root"), SLOT(setCurrentDocAsExplicitRoot()));
 1296 
 1297     //---help---
 1298     menu = newManagedMenu("main/help", tr("&Help"));
 1299     newManagedAction(menu, "latexreference", tr("LaTeX Reference..."), SLOT(latexHelp()), 0, "help-contents");
 1300     newManagedAction(menu, "usermanual", tr("User Manual..."), SLOT(userManualHelp()), 0, "help-contents");
 1301     newManagedAction(menu, "texdocdialog", tr("Packages Help..."), SLOT(texdocHelp()));
 1302 
 1303     menu->addSeparator();
 1304     newManagedAction(menu, "checkinstall", tr("Check LaTeX Installation"), SLOT(checkLatexInstall()));
 1305     newManagedAction(menu, "checkcwls", tr("Check Active Completion Files"), SLOT(checkCWLs()));
 1306     newManagedAction(menu, "checklt", tr("Check LanguageTool"), SLOT(checkLanguageTool()));
 1307     newManagedAction(menu, "appinfo", tr("About TeXstudio..."), SLOT(helpAbout()), 0, APPICON)->setMenuRole(QAction::AboutRole);
 1308 
 1309     //additional elements for development
 1310 
 1311 
 1312     //-----context menus-----
 1313     if (LatexEditorView::getBaseActions().empty()) { //only called at first menu created
 1314         QList<QAction *> baseContextActions;
 1315         QAction *sep = new QAction(menu);
 1316         sep->setSeparator(true);
 1317         baseContextActions << getManagedActions(QStringList() << "copy" << "cut" << "paste", "main/edit/");
 1318         baseContextActions << getManagedActions(QStringList() << "main/edit2/pasteAsLatex" << "main/edit2/convertTo" << "main/edit/selection/selectAll");
 1319         baseContextActions << sep;
 1320         baseContextActions << getManagedActions(QStringList() << "previewLatex" << "removePreviewLatex", "main/edit2/");
 1321         LatexEditorView::setBaseActions(baseContextActions);
 1322     }
 1323 
 1324     configManager.updateRecentFiles(true);
 1325 
 1326     configManager.modifyMenuContents();
 1327     configManager.modifyManagedShortcuts();
 1328 }
 1329 /*! \brief set-up all tool-bars
 1330  */
 1331 void Texstudio::setupToolBars()
 1332 {
 1333     //This method will be called multiple times and must not create something if this something already exists
 1334 
 1335     configManager.watchedMenus.clear();
 1336 
 1337     //customizable toolbars
 1338     //first apply custom icons
 1339     QMap<QString, QVariant>::const_iterator i = configManager.replacedIconsOnMenus.constBegin();
 1340     while (i != configManager.replacedIconsOnMenus.constEnd()) {
 1341         QString id = i.key();
 1342         QString iconFilename = configManager.parseDir(i.value().toString());
 1343         QObject *obj = configManager.menuParent->findChild<QObject *>(id);
 1344         QAction *act = qobject_cast<QAction *>(obj);
 1345         if (act && !iconFilename.isEmpty()) act->setIcon(QIcon(iconFilename));
 1346         ++i;
 1347     }
 1348     //setup customizable toolbars
 1349     for (int i = 0; i < configManager.managedToolBars.size(); i++) {
 1350         ManagedToolBar &mtb = configManager.managedToolBars[i];
 1351         if (!mtb.toolbar) { //create actual toolbar on first call
 1352             if (mtb.name == "Central") mtb.toolbar = centralToolBar;
 1353             else mtb.toolbar = addToolBar(tr(qPrintable(mtb.name)));
 1354             mtb.toolbar->setObjectName(mtb.name);
 1355             addAction(mtb.toolbar->toggleViewAction());
 1356             if (mtb.name == "Spelling") addToolBarBreak();
 1357         } else mtb.toolbar->clear();
 1358         foreach (const QString &actionName, mtb.actualActions) {
 1359             if (actionName == "separator") mtb.toolbar->addSeparator(); //Case 1: Separator
 1360             else if (actionName.startsWith("tags/")) {
 1361                 //Case 2: One of the xml tag widgets mapped on a toolbutton
 1362                 int tagCategorySep = actionName.indexOf("/", 5);
 1363                 XmlTagsListWidget *tagsWidget = findChild<XmlTagsListWidget *>(actionName.left(tagCategorySep));
 1364                 if (!tagsWidget) continue;
 1365                 if (!tagsWidget->isPopulated())
 1366                     tagsWidget->populate();
 1367                 QStringList list = tagsWidget->tagsTxtFromCategory(actionName.mid(tagCategorySep + 1));
 1368                 if (list.isEmpty()) continue;
 1369                 QToolButton *combo = UtilsUi::createComboToolButton(mtb.toolbar, list, QList<QIcon>(), 0, this, SLOT(insertXmlTagFromToolButtonAction()));
 1370                 combo->setProperty("tagsID", actionName);
 1371                 mtb.toolbar->addWidget(combo);
 1372             } else {
 1373                 QObject *obj = configManager.menuParent->findChild<QObject *>(actionName);
 1374                 QAction *act = qobject_cast<QAction *>(obj);
 1375                 if (act) {
 1376                     //Case 3: A normal QAction
 1377                     if (act->icon().isNull())
 1378                         act->setIcon(QIcon(APPICON));
 1379                     UtilsUi::updateToolTipWithShortcut(act, configManager.showShortcutsInTooltips);
 1380                     mtb.toolbar->addAction(act);
 1381                 } else {
 1382                     QMenu *menu = qobject_cast<QMenu *>(obj);
 1383                     if (!menu) {
 1384                         qWarning("Unknown toolbar command %s", qPrintable(actionName));
 1385                         continue;
 1386                     }
 1387                     //Case 4: A submenu mapped on a toolbutton
 1388                     configManager.watchedMenus << actionName;
 1389                     QStringList list;
 1390                     QList<QIcon> icons;
 1391                     foreach (const QAction *act, menu->actions())
 1392                         if (!act->isSeparator()) {
 1393                             list.append(act->text());
 1394                             icons.append(act->icon());
 1395                         }
 1396                     //TODO: Is the callToolButtonAction()-slot really needed? Can't we just add the menu itself as the menu of the qtoolbutton, without creating a copy? (should be much faster)
 1397                     QToolButton *combo = UtilsUi::createComboToolButton(mtb.toolbar, list, icons, 0, this, SLOT(callToolButtonAction()));
 1398                     combo->setProperty("menuID", actionName);
 1399                     mtb.toolbar->addWidget(combo);
 1400                 }
 1401             }
 1402         }
 1403         if (mtb.actualActions.empty()) mtb.toolbar->setVisible(false);
 1404     }
 1405 }
 1406 
 1407 void Texstudio::updateAvailableLanguages()
 1408 {
 1409     delete spellLanguageActions;
 1410 
 1411     spellLanguageActions = new QActionGroup(statusTbLanguage);
 1412     spellLanguageActions->setExclusive(true);
 1413 
 1414     foreach (const QString &s, spellerManager.availableDicts()) {
 1415         QAction *act = new QAction(spellLanguageActions);
 1416         act->setText(spellerManager.prettyName(s));
 1417         act->setData(QVariant(s));
 1418         act->setCheckable(true);
 1419         connect(act, SIGNAL(triggered()), this, SLOT(changeEditorSpeller()));
 1420     }
 1421 
 1422     QAction *act = new QAction(spellLanguageActions);
 1423     act->setSeparator(true);
 1424     act = new QAction(spellLanguageActions);
 1425     act->setText(tr("Default") + QString(": %1").arg(spellerManager.prettyName(spellerManager.defaultSpellerName())));
 1426     act->setData(QVariant("<default>"));
 1427     connect(act, SIGNAL(triggered()), this, SLOT(changeEditorSpeller()));
 1428     act->setCheckable(true);
 1429     act->setChecked(true);
 1430 
 1431     act = new QAction(spellLanguageActions);
 1432     act->setSeparator(true);
 1433     act = new QAction(spellLanguageActions);
 1434     act->setText(tr("Insert language as TeX comment"));
 1435     connect(act, SIGNAL(triggered()), this, SLOT(insertSpellcheckMagicComment()));
 1436 
 1437     statusTbLanguage->addActions(spellLanguageActions->actions());
 1438 
 1439     if (currentEditorView()) {
 1440         editorSpellerChanged(currentEditorView()->getSpeller());
 1441     } else {
 1442         editorSpellerChanged("<default>");
 1443     }
 1444 }
 1445 
 1446 void Texstudio::updateLanguageToolStatus()
 1447 {
 1448     // adapt icon size to dpi
 1449 #if QT_VERSION> 0x050000
 1450     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
 1451     double scale=dpi/96;
 1452 #else
 1453     double scale=1;
 1454 #endif
 1455     int iconWidth=qRound(configManager.guiSecondaryToolbarIconSize*scale);
 1456 
 1457     QIcon icon = getRealIconCached("languagetool");
 1458     QSize iconSize = QSize(iconWidth, iconWidth);
 1459     switch (grammarCheck->languageToolStatus()) {
 1460         case GrammarCheck::LTS_Working:
 1461             statusLabelLanguageTool->setPixmap(icon.pixmap(iconSize));
 1462             statusLabelLanguageTool->setToolTip(QString(tr("Connected to LanguageTool at %1")).arg(grammarCheck->serverUrl()));
 1463             break;
 1464         case GrammarCheck::LTS_Error:
 1465             statusLabelLanguageTool->setPixmap(icon.pixmap(iconSize, QIcon::Disabled));
 1466             statusLabelLanguageTool->setToolTip(QString(tr("No LanguageTool server found at %1")).arg(grammarCheck->serverUrl()));
 1467             break;
 1468         case GrammarCheck::LTS_Unknown:
 1469             statusLabelLanguageTool->setPixmap(icon.pixmap(iconSize, QIcon::Disabled));
 1470             statusLabelLanguageTool->setToolTip(tr("LanguageTool status unknown"));
 1471     }
 1472     if (!configManager.editorConfig->realtimeChecking || !configManager.editorConfig->inlineGrammarChecking) {
 1473         statusLabelLanguageTool->setPixmap(icon.pixmap(iconSize, QIcon::Disabled));
 1474         statusLabelLanguageTool->setToolTip(tr("Inline grammar checking disabled by user!"));
 1475     }
 1476 }
 1477 
 1478 /*! \brief set-up status bar
 1479  */
 1480 void Texstudio::createStatusBar()
 1481 {
 1482     QStatusBar *status = statusBar();
 1483     status->setContextMenuPolicy(Qt::PreventContextMenu);
 1484     status->setVisible(configManager.getOption("View/ShowStatusbar").toBool());
 1485 
 1486     // adapt icon size to dpi
 1487 #if QT_VERSION> 0x050000
 1488     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
 1489     double scale=dpi/96;
 1490 #else
 1491     double scale=1;
 1492 #endif
 1493     int iconWidth=qRound(configManager.guiSecondaryToolbarIconSize*scale);
 1494 
 1495     QSize iconSize = QSize(iconWidth, iconWidth);
 1496     QAction *act;
 1497     QToolButton *tb;
 1498     act = getManagedAction("main/view/show/structureview");
 1499     if (act) {
 1500         tb = new QToolButton(status);
 1501         tb->setCheckable(true);
 1502         tb->setChecked(act->isChecked());
 1503         tb->setAutoRaise(true);
 1504         tb->setIcon(act->icon());
 1505         tb->setIconSize(iconSize);
 1506         tb->setToolTip(act->toolTip());
 1507         connect(tb, SIGNAL(clicked()), act, SLOT(trigger()));
 1508         connect(act, SIGNAL(toggled(bool)), tb, SLOT(setChecked(bool)));
 1509         status->addPermanentWidget(tb, 0);
 1510     }
 1511     act = getManagedAction("main/view/show/outputview");
 1512     if (act) {
 1513         tb = new QToolButton(status);
 1514         tb->setCheckable(true);
 1515         tb->setChecked(act->isChecked());
 1516         tb->setAutoRaise(true);
 1517         tb->setIcon(act->icon());
 1518         tb->setIconSize(iconSize);
 1519         tb->setToolTip(act->toolTip());
 1520         connect(tb, SIGNAL(clicked()), act, SLOT(trigger()));
 1521         connect(act, SIGNAL(toggled(bool)), tb, SLOT(setChecked(bool)));
 1522         status->addPermanentWidget(tb, 0);
 1523     }
 1524 
 1525     // spacer eating up all the space between "left" and "right" permanent widgets.
 1526     QLabel *messageArea = new QLabel(status);
 1527     connect(status, SIGNAL(messageChanged(QString)), messageArea, SLOT(setText(QString)));
 1528     status->addPermanentWidget(messageArea, 1);
 1529 
 1530     // LanguageTool
 1531     connect(grammarCheck, SIGNAL(languageToolStatusChanged()), this, SLOT(updateLanguageToolStatus()));
 1532     statusLabelLanguageTool = new QLabel();
 1533     updateLanguageToolStatus();
 1534     status->addPermanentWidget(statusLabelLanguageTool);
 1535 
 1536     // language
 1537     statusTbLanguage = new QToolButton(status);
 1538     statusTbLanguage->setToolTip(tr("Language"));
 1539     statusTbLanguage->setPopupMode(QToolButton::InstantPopup);
 1540     statusTbLanguage->setAutoRaise(true);
 1541     statusTbLanguage->setMinimumWidth(status->fontMetrics().width("OOOOOOO"));
 1542     connect(&spellerManager, SIGNAL(dictPathChanged()), this, SLOT(updateAvailableLanguages()));
 1543     connect(&spellerManager, SIGNAL(defaultSpellerChanged()), this, SLOT(updateAvailableLanguages()));
 1544     updateAvailableLanguages();
 1545     statusTbLanguage->setText(spellerManager.defaultSpellerName());
 1546     status->addPermanentWidget(statusTbLanguage, 0);
 1547 
 1548     // encoding
 1549     statusTbEncoding = new QToolButton(status);
 1550     statusTbEncoding->setToolTip(tr("Encoding"));
 1551     statusTbEncoding->setText(tr("Encoding") + "  ");
 1552     statusTbEncoding->setPopupMode(QToolButton::InstantPopup);
 1553     statusTbEncoding->setAutoRaise(true);
 1554     statusTbEncoding->setMinimumWidth(status->fontMetrics().width("OOOOO"));
 1555 
 1556     QSet<int> encodingMibs;
 1557     foreach (const QString &s, configManager.commonEncodings) {
 1558         QTextCodec *codec = QTextCodec::codecForName(s.toLocal8Bit());
 1559         if (!codec) continue;
 1560         encodingMibs.insert(codec->mibEnum());
 1561     }
 1562     foreach (int mib, encodingMibs) {
 1563         QAction *act = new QAction(statusTbEncoding);
 1564         act->setText(QTextCodec::codecForMib(mib)->name());
 1565         act->setData(mib);
 1566         statusTbEncoding->addAction(act);
 1567         connect(act, SIGNAL(triggered()), this, SLOT(changeTextCodec()));
 1568     }
 1569     act = new QAction(statusTbEncoding);
 1570     act->setSeparator(true);
 1571     statusTbEncoding->addAction(act);
 1572     act = new QAction(statusTbEncoding);
 1573     act->setText(tr("More Encodings..."));
 1574     statusTbEncoding->addAction(act);
 1575     connect(act, SIGNAL(triggered()), this, SLOT(editSetupEncoding()));
 1576     act = new QAction(statusTbEncoding);
 1577     act->setSeparator(true);
 1578     statusTbEncoding->addAction(act);
 1579 
 1580     act = new QAction(statusTbEncoding);
 1581     act->setText(tr("Insert encoding as TeX comment"));
 1582     statusTbEncoding->addAction(act);
 1583     connect(act, SIGNAL(triggered()), this, SLOT(addMagicCoding()));
 1584 
 1585     status->addPermanentWidget(statusTbEncoding, 0);
 1586 
 1587 
 1588     statusLabelMode = new QLabel(status);
 1589     statusLabelProcess = new QLabel(status);
 1590     status->addPermanentWidget(statusLabelProcess, 0);
 1591     status->addPermanentWidget(statusLabelMode, 0);
 1592     for (int i = 1; i <= 3; i++) {
 1593         QPushButton *pb = new QPushButton(getRealIcon(QString("bookmark%1").arg(i)), "", status);
 1594         pb->setIconSize(iconSize);
 1595         pb->setToolTip(tr("Go to bookmark") + QString(" %1").arg(i));
 1596         connect(pb, SIGNAL(clicked()), getManagedAction(QString("main/edit/gotoBookmark/bookmark%1").arg(i)), SIGNAL(triggered()));
 1597         pb->setFlat(true);
 1598         status->addPermanentWidget(pb, 0);
 1599     }
 1600 }
 1601 
 1602 void Texstudio::updateCaption()
 1603 {
 1604         if (!currentEditorView()) documents.currentDocument = nullptr;
 1605     else {
 1606         documents.currentDocument = currentEditorView()->document;
 1607         documents.updateStructure();
 1608         structureTreeView->setExpanded(documents.model->index(documents.currentDocument->baseStructure), true);
 1609     }
 1610     if (completer && completer->isVisible()) completer->close();
 1611     QString title;
 1612     if (!currentEditorView())   {
 1613         title = TEXSTUDIO;
 1614     } else {
 1615         QString file = QDir::toNativeSeparators(getCurrentFileName());
 1616         if (file.isEmpty())
 1617             file = currentEditorView()->displayNameForUI();
 1618         title = file + " - " + TEXSTUDIO;
 1619         updateStatusBarEncoding();
 1620         updateOpenDocumentMenu(true);
 1621         newDocumentLineEnding();
 1622     }
 1623     setWindowTitle(title);
 1624     //updateStructure();
 1625     updateUndoRedoStatus();
 1626     cursorPositionChanged();
 1627     if (documents.singleMode()) {
 1628         //outputView->resetMessagesAndLog();
 1629         if (currentEditorView()) completerNeedsUpdate();
 1630     }
 1631     QString finame = getCurrentFileName();
 1632     if (finame != "") configManager.lastDocument = finame;
 1633 }
 1634 
 1635 void Texstudio::updateMasterDocumentCaption()
 1636 {
 1637     if (documents.singleMode()) {
 1638         actRootDocAutomatic->setChecked(true);
 1639         actRootDocExplicit->setVisible(false);
 1640         statusLabelMode->setText(QString(" %1 ").arg(tr("Automatic")));
 1641         statusLabelMode->setToolTip(tr("Automatic root document detection active"));
 1642     } else {
 1643         QString shortName = documents.masterDocument->getFileInfo().fileName();
 1644         actRootDocExplicit->setChecked(true);
 1645         actRootDocExplicit->setVisible(true);
 1646         actRootDocExplicit->setText(tr("&Explicit") + ": " + shortName);
 1647         statusLabelMode->setText(QString(" %1 ").arg(tr("Root", "explicit root document") + ": " + shortName));
 1648         statusLabelMode->setToolTip(QString(tr("Explict root document:\n%1")).arg(shortName));
 1649     }
 1650 }
 1651 
 1652 void Texstudio::currentEditorChanged()
 1653 {
 1654     updateCaption();
 1655     if (!currentEditorView()) return;
 1656     if (configManager.watchedMenus.contains("main/view/documents"))
 1657         updateToolBarMenu("main/view/documents");
 1658     editorSpellerChanged(currentEditorView()->getSpeller());
 1659     currentEditorView()->lastUsageTime = QDateTime::currentDateTime();
 1660     currentEditorView()->checkRTLLTRLanguageSwitching();
 1661 }
 1662 
 1663 /*!
 1664  * \brief called when a editor tab is moved in position
 1665  * \param from starting position
 1666  * \param to ending position
 1667  */
 1668 void Texstudio::editorTabMoved(int from, int to)
 1669 {
 1670     //documents.aboutToUpdateLayout();
 1671     documents.move(from, to);
 1672     //documents.updateLayout();
 1673 }
 1674 
 1675 void Texstudio::editorAboutToChangeByTabClick(LatexEditorView *edFrom, LatexEditorView *edTo)
 1676 {
 1677     Q_UNUSED(edTo)
 1678     saveEditorCursorToHistory(edFrom);
 1679 }
 1680 
 1681 void Texstudio::showMarkTooltipForLogMessage(QList<int> errors)
 1682 {
 1683     if (!currentEditorView()) return;
 1684     REQUIRE(outputView->getLogWidget());
 1685     REQUIRE(outputView->getLogWidget()->getLogModel());
 1686     QString msg = outputView->getLogWidget()->getLogModel()->htmlErrorTable(errors);
 1687     currentEditorView()->setLineMarkToolTip(msg);
 1688 }
 1689 
 1690 void Texstudio::newDocumentLineEnding()
 1691 {
 1692     if (!currentEditorView()) return;
 1693     QDocument::LineEnding le = currentEditorView()->editor->document()->lineEnding();
 1694     if (le == QDocument::Conservative) le = currentEditorView()->editor->document()->originalLineEnding();
 1695     switch (le) {
 1696 #ifdef Q_OS_WIN32
 1697     case QDocument::Local:
 1698 #endif
 1699     case QDocument::Windows:
 1700         getManagedAction("main/edit/lineend/crlf")->setChecked(true);
 1701         break;
 1702     case QDocument::Mac:
 1703         getManagedAction("main/edit/lineend/cr")->setChecked(true);
 1704         break;
 1705     default:
 1706         getManagedAction("main/edit/lineend/lf")->setChecked(true);
 1707     }
 1708 }
 1709 
 1710 void Texstudio::updateUndoRedoStatus()
 1711 {
 1712     if (currentEditor()) {
 1713         actSave->setEnabled(!currentEditor()->document()->isClean() || currentEditor()->fileName().isEmpty());
 1714         bool canUndo = currentEditor()->document()->canUndo();
 1715         if (!canUndo && configManager.svnUndo) {
 1716             QVariant zw = currentEditor()->property("undoRevision");
 1717             int undoRevision = zw.canConvert<int>() ? zw.toInt() : 0;
 1718             if (undoRevision >= 0)
 1719                 canUndo = true;
 1720         }
 1721         actUndo->setEnabled(canUndo);
 1722         actRedo->setEnabled(currentEditor()->document()->canRedo());
 1723     } else {
 1724         actSave->setEnabled(false);
 1725         actUndo->setEnabled(false);
 1726         actRedo->setEnabled(false);
 1727     }
 1728 }
 1729 /*!
 1730  * \brief return current editor
 1731  *
 1732  * return current editorview
 1733  * \return current editor (LatexEditorView)
 1734  */
 1735 LatexEditorView *Texstudio::currentEditorView() const
 1736 {
 1737     return editors->currentEditor();
 1738 }
 1739 
 1740 /*!
 1741  * \brief return current editor
 1742  *
 1743  * return current editor
 1744  * \return current editor (QEditor)
 1745  */
 1746 QEditor *Texstudio::currentEditor() const
 1747 {
 1748     LatexEditorView *edView = currentEditorView();
 1749     if (!edView) return nullptr;
 1750     return edView->editor;
 1751 }
 1752 
 1753 void Texstudio::configureNewEditorView(LatexEditorView *edit)
 1754 {
 1755     REQUIRE(m_languages);
 1756     REQUIRE(edit->codeeditor);
 1757     m_languages->setLanguage(edit->codeeditor->editor(), ".tex");
 1758 
 1759     //edit->setFormats(m_formats->id("environment"),m_formats->id("referenceMultiple"),m_formats->id("referencePresent"),m_formats->id("referenceMissing"));
 1760 
 1761     connect(edit->editor, SIGNAL(undoAvailable(bool)), this, SLOT(updateUndoRedoStatus()));
 1762     connect(edit->editor, SIGNAL(requestClose()), &documents, SLOT(requestedClose()));
 1763     connect(edit->editor, SIGNAL(redoAvailable(bool)), this, SLOT(updateUndoRedoStatus()));
 1764     connect(edit->editor->document(), SIGNAL(lineEndingChanged(int)), this, SLOT(newDocumentLineEnding()));
 1765     connect(edit->editor, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
 1766     connect(edit->editor, SIGNAL(cursorHovered()), this, SLOT(cursorHovered()));
 1767     connect(edit->editor, SIGNAL(emitWordDoubleClicked()), this, SLOT(cursorHovered()));
 1768     connect(edit, SIGNAL(showMarkTooltipForLogMessage(QList<int>)), this, SLOT(showMarkTooltipForLogMessage(QList<int>)));
 1769     connect(edit, SIGNAL(needCitation(const QString &)), this, SLOT(insertBibEntry(const QString &)));
 1770     connect(edit, SIGNAL(showPreview(QString)), this, SLOT(showPreview(QString)));
 1771     connect(edit, SIGNAL(showImgPreview(QString)), this, SLOT(showImgPreview(QString)));
 1772     connect(edit, SIGNAL(showPreview(QDocumentCursor)), this, SLOT(showPreview(QDocumentCursor)));
 1773     connect(edit, SIGNAL(gotoDefinition(QDocumentCursor)), this, SLOT(editGotoDefinition(QDocumentCursor)));
 1774     connect(edit, SIGNAL(findLabelUsages(LatexDocument *, QString)), this, SLOT(findLabelUsages(LatexDocument *, QString)));
 1775     connect(edit, SIGNAL(syncPDFRequested(QDocumentCursor)), this, SLOT(syncPDFViewer(QDocumentCursor)));
 1776     connect(edit, SIGNAL(openFile(QString)), this, SLOT(openExternalFile(QString)));
 1777     connect(edit, SIGNAL(openFile(QString, QString)), this, SLOT(openExternalFile(QString, QString)));
 1778     connect(edit, SIGNAL(bookmarkRemoved(QDocumentLineHandle *)), bookmarks, SLOT(bookmarkDeleted(QDocumentLineHandle *)));
 1779     connect(edit, SIGNAL(bookmarkAdded(QDocumentLineHandle *, int)), bookmarks, SLOT(bookmarkAdded(QDocumentLineHandle *, int)));
 1780     connect(edit, SIGNAL(mouseBackPressed()), this, SLOT(goBack()));
 1781     connect(edit, SIGNAL(mouseForwardPressed()), this, SLOT(goForward()));
 1782     connect(edit, SIGNAL(cursorChangeByMouse()), this, SLOT(saveCurrentCursorToHistory()));
 1783     connect(edit, SIGNAL(openCompleter()), this, SLOT(normalCompletion()));
 1784     connect(edit, SIGNAL(openInternalDocViewer(QString, QString)), this, SLOT(openInternalDocViewer(QString, QString)));
 1785     connect(edit, SIGNAL(showExtendedSearch()), this, SLOT(showExtendedSearch()));
 1786     connect(edit, SIGNAL(execMacro(Macro, MacroExecContext)), this, SLOT(execMacro(Macro, MacroExecContext)));
 1787 
 1788     connect(edit->editor, SIGNAL(fileReloaded()), this, SLOT(fileReloaded()));
 1789     connect(edit->editor, SIGNAL(fileInConflictShowDiff()), this, SLOT(fileInConflictShowDiff()));
 1790     connect(edit->editor, SIGNAL(fileAutoReloading(QString)), this, SLOT(fileAutoReloading(QString)));
 1791 
 1792     if (Guardian::instance()) { // Guardian is not yet there when this is called at program startup
 1793         connect(edit->editor, SIGNAL(slowOperationStarted()), Guardian::instance(), SLOT(slowOperationStarted()));
 1794         connect(edit->editor, SIGNAL(slowOperationEnded()), Guardian::instance(), SLOT(slowOperationEnded()));
 1795     }
 1796     connect(edit, SIGNAL(linesChanged(QString, const void *, QList<LineInfo>, int)), grammarCheck, SLOT(check(QString, const void *, QList<LineInfo>, int)));
 1797 
 1798     connect(edit, SIGNAL(spellerChanged(QString)), this, SLOT(editorSpellerChanged(QString)));
 1799     connect(edit->editor, SIGNAL(focusReceived()), edit, SIGNAL(focusReceived()));
 1800     edit->setSpellerManager(&spellerManager);
 1801     edit->setSpeller("<default>");
 1802 
 1803 }
 1804 
 1805 /*!
 1806  * \brief complete the new editor view configuration (edit->document is set)
 1807  * \param edit used editorview
 1808  * \param reloadFromDoc
 1809  * \param hidden if editor is not shown
 1810  */
 1811 void Texstudio::configureNewEditorViewEnd(LatexEditorView *edit, bool reloadFromDoc, bool hidden)
 1812 {
 1813     REQUIRE(edit->document);
 1814     //patch Structure
 1815     //disconnect(edit->editor->document(),SIGNAL(contentsChange(int, int))); // force order of contentsChange update
 1816     connect(edit->editor->document(), SIGNAL(contentsChange(int, int)), edit->document, SLOT(patchStructure(int, int)));
 1817     //connect(edit->editor->document(),SIGNAL(contentsChange(int, int)),edit,SLOT(documentContentChanged(int,int))); now directly called by patchStructure
 1818     connect(edit->editor->document(), SIGNAL(lineRemoved(QDocumentLineHandle *)), edit->document, SLOT(patchStructureRemoval(QDocumentLineHandle *)));
 1819     connect(edit->editor->document(), SIGNAL(lineDeleted(QDocumentLineHandle *)), edit->document, SLOT(patchStructureRemoval(QDocumentLineHandle *)));
 1820     connect(edit->document, SIGNAL(updateCompleter()), this, SLOT(completerNeedsUpdate()));
 1821     connect(edit->editor, SIGNAL(needUpdatedCompleter()), this, SLOT(needUpdatedCompleter()));
 1822     connect(edit->document, SIGNAL(importPackage(QString)), this, SLOT(importPackage(QString)));
 1823     connect(edit->document, SIGNAL(bookmarkLineUpdated(int)), bookmarks, SLOT(updateLineWithBookmark(int)));
 1824     connect(edit->document, SIGNAL(encodingChanged()), this, SLOT(updateStatusBarEncoding()));
 1825     connect(edit, SIGNAL(thesaurus(int, int)), this, SLOT(editThesaurus(int, int)));
 1826     connect(edit, SIGNAL(changeDiff(QPoint)), this, SLOT(editChangeDiff(QPoint)));
 1827     connect(edit, SIGNAL(saveCurrentCursorToHistoryRequested()), this, SLOT(saveCurrentCursorToHistory()));
 1828     edit->document->saveLineSnapshot(); // best guess of the lines used during last latex compilation
 1829 
 1830     if (!hidden) {
 1831         int index = reloadFromDoc ? documents.documents.indexOf(edit->document, 0) : -1; // index: we still assume here that the order of documents and editors is synchronized
 1832         editors->insertEditor(edit, index);
 1833         edit->editor->setFocus();
 1834         updateCaption();
 1835     }
 1836 }
 1837 /*!
 1838  * \brief get editor which handles FileName
 1839  *
 1840  * get editor which handles FileName
 1841  *
 1842  * \param fileName
 1843  * \param checkTemporaryNames
 1844  * \return editorview, 0 if no editor matches
 1845  */
 1846 LatexEditorView *Texstudio::getEditorViewFromFileName(const QString &fileName, bool checkTemporaryNames)
 1847 {
 1848     LatexDocument *document = documents.findDocument(fileName, checkTemporaryNames);
 1849     if (!document) return nullptr;
 1850     return document->getEditorView();
 1851 }
 1852 
 1853 /*!
 1854  * \brief get the editor referenced by a given line handle
 1855  * \param dlh the line handle
 1856  * \return the editor view, null if the handle is null
 1857  */
 1858 LatexEditorView *Texstudio::getEditorViewFromHandle(const QDocumentLineHandle *dlh)
 1859 {
 1860     if (!dlh) return nullptr;
 1861     LatexDocument *targetDoc = qobject_cast<LatexDocument *>(dlh->document());
 1862     REQUIRE_RET(targetDoc, nullptr);
 1863     return qobject_cast<LatexEditorView *>(targetDoc->getEditorView());
 1864 }
 1865 
 1866 /*!
 1867  * \brief get filename of current editor
 1868  *
 1869  * get filename of current editor
 1870  * \return filename
 1871  */
 1872 QString Texstudio::getCurrentFileName()
 1873 {
 1874     return documents.getCurrentFileName();
 1875 }
 1876 
 1877 QString Texstudio::getAbsoluteFilePath(const QString &relName, const QString &extension)
 1878 {
 1879     return documents.getAbsoluteFilePath(relName, extension);
 1880 }
 1881 
 1882 QString Texstudio::getRelativeFileName(const QString &file, QString basepath, bool keepSuffix)
 1883 {
 1884     return getRelativeBaseNameToPath(file, basepath, true, keepSuffix);
 1885 }
 1886 
 1887 bool Texstudio::activateEditorForFile(QString f, bool checkTemporaryNames, bool setFocus)
 1888 {
 1889     LatexEditorView *edView = getEditorViewFromFileName(f, checkTemporaryNames);
 1890     if (!edView) return false;
 1891     saveCurrentCursorToHistory();
 1892     if (!editors->containsEditor(edView)) return false;
 1893     editors->setCurrentEditor(edView, setFocus);
 1894     return true;
 1895 }
 1896 
 1897 ///////////////////FILE//////////////////////////////////////
 1898 
 1899 void guessLanguageFromContent(QLanguageFactory *m_languages, QEditor *e)
 1900 {
 1901     QDocument *doc = e->document();
 1902     if (doc->lineCount() == 0) return;
 1903     if (doc->line(0).text().startsWith("<?xml") ||
 1904             doc->line(0).text().startsWith("<!DOCTYPE"))
 1905         m_languages->setLanguage(e, ".xml");
 1906 }
 1907 /*!
 1908  * \brief load file
 1909  *
 1910  * load file from disc
 1911  * \param f filename
 1912  * \param asProject load file as master-file
 1913  * \param hidden hide editor
 1914  * \param recheck
 1915  * \param dontAsk
 1916  * \return
 1917  */
 1918 LatexEditorView *Texstudio::load(const QString &f , bool asProject, bool hidden, bool recheck, bool dontAsk)
 1919 {
 1920     QString f_real = f;
 1921 #ifdef Q_OS_WIN32
 1922     QRegExp regcheck("/([a-zA-Z]:[/\\\\].*)");
 1923     if (regcheck.exactMatch(f)) f_real = regcheck.cap(1);
 1924 #endif
 1925 
 1926 #ifndef NO_POPPLER_PREVIEW
 1927     if (f_real.endsWith(".pdf", Qt::CaseInsensitive)) {
 1928         if (PDFDocument::documentList().isEmpty())
 1929             newPdfPreviewer();
 1930         PDFDocument::documentList().first()->loadFile(f_real);
 1931         PDFDocument::documentList().first()->show();
 1932         PDFDocument::documentList().first()->setFocus();
 1933         return nullptr;
 1934     }
 1935     if ((f_real.endsWith(".synctex.gz", Qt::CaseInsensitive) ||
 1936             f_real.endsWith(".synctex", Qt::CaseInsensitive))
 1937             && UtilsUi::txsConfirm(tr("Do you want to debug a SyncTeX file?"))) {
 1938         fileNewInternal();
 1939         currentEditor()->document()->setText(PDFDocument::debugSyncTeX(f_real), false);
 1940         return currentEditorView();
 1941     }
 1942 #endif
 1943 
 1944     if (f_real.endsWith(".log", Qt::CaseInsensitive) &&
 1945             UtilsUi::txsConfirm(QString("Do you want to load file %1 as LaTeX log file?").arg(QFileInfo(f).completeBaseName()))) {
 1946         outputView->getLogWidget()->loadLogFile(f, documents.getTemporaryCompileFileName(), QTextCodec::codecForName(configManager.logFileEncoding.toLatin1()));
 1947         setLogMarksVisible(true);
 1948         return nullptr;
 1949     }
 1950 
 1951     if (!hidden)
 1952         raise();
 1953 
 1954     //test is already opened
 1955     LatexEditorView *existingView = getEditorViewFromFileName(f_real);
 1956     LatexDocument *doc=nullptr;
 1957     if (!existingView) {
 1958         doc = documents.findDocumentFromName(f_real);
 1959         if (doc) existingView = doc->getEditorView();
 1960     }
 1961     if (existingView) {
 1962         if (hidden)
 1963             return existingView;
 1964         if (asProject) documents.setMasterDocument(existingView->document);
 1965         if (existingView->document->isHidden()) {
 1966             existingView->editor->setLineWrapping(configManager.editorConfig->wordwrap > 0);
 1967             documents.deleteDocument(existingView->document, true);
 1968             existingView->editor->setSilentReloadOnExternalChanges(existingView->document->remeberAutoReload);
 1969             existingView->editor->setHidden(false);
 1970             documents.addDocument(existingView->document, false);
 1971             editors->addEditor(existingView);
 1972             updateStructure(false, existingView->document, true);
 1973             existingView->editor->setFocus();
 1974             updateCaption();
 1975             return existingView;
 1976         }
 1977         editors->setCurrentEditor(existingView);
 1978         return existingView;
 1979     }
 1980 
 1981     // find closed master doc
 1982     if (doc) {
 1983             LatexEditorView *edit = new LatexEditorView(nullptr, configManager.editorConfig, doc);
 1984         edit->setLatexPackageList(&latexPackageList);
 1985         edit->document = doc;
 1986         edit->editor->setFileName(doc->getFileName());
 1987         disconnect(edit->editor->document(), SIGNAL(contentsChange(int, int)), edit->document, SLOT(patchStructure(int, int)));
 1988         configureNewEditorView(edit);
 1989         if (edit->editor->fileInfo().suffix().toLower() != "tex")
 1990             m_languages->setLanguage(edit->editor, f_real);
 1991         if (!edit->editor->languageDefinition())
 1992             guessLanguageFromContent(m_languages, edit->editor);
 1993 
 1994         doc->setLineEnding(edit->editor->document()->originalLineEnding());
 1995         doc->setEditorView(edit); //update file name (if document didn't exist)
 1996 
 1997         configureNewEditorViewEnd(edit, !hidden, hidden);
 1998         //edit->document->initStructure();
 1999         //updateStructure(true);
 2000         if (!hidden) {
 2001             showStructure();
 2002             bookmarks->restoreBookmarks(edit);
 2003         }
 2004         return edit;
 2005     }
 2006 
 2007     //load it otherwise
 2008     if (!QFile::exists(f_real)) return nullptr;
 2009     QFile file(f_real);
 2010     if (!file.open(QIODevice::ReadOnly)) {
 2011         if (!hidden && !dontAsk)
 2012             QMessageBox::warning(this, tr("Error"), tr("You do not have read permission to the file %1.").arg(f_real));
 2013         return nullptr;
 2014     }
 2015     file.close();
 2016 
 2017     bool bibTeXmodified = documents.bibTeXFilesModified;
 2018 
 2019     doc = new LatexDocument(this);
 2020     doc->enableSyntaxCheck(configManager.editorConfig->inlineSyntaxChecking);
 2021     LatexEditorView *edit = new LatexEditorView(nullptr, configManager.editorConfig, doc);
 2022     edit->setLatexPackageList(&latexPackageList);
 2023     if (hidden) {
 2024         edit->editor->setLineWrapping(false); //disable linewrapping in hidden docs to speed-up updates
 2025         doc->clearWidthConstraint();
 2026     }
 2027     configureNewEditorView(edit);
 2028 
 2029     edit->document = documents.findDocument(f_real);
 2030     if (!edit->document) {
 2031         edit->document = doc;
 2032         edit->document->setEditorView(edit);
 2033         documents.addDocument(edit->document, hidden);
 2034     } else edit->document->setEditorView(edit);
 2035 
 2036     if (configManager.recentFileHighlightLanguage.contains(f_real))
 2037         m_languages->setLanguage(edit->editor, configManager.recentFileHighlightLanguage.value(f_real));
 2038     else if (edit->editor->fileInfo().suffix().toLower() != "tex")
 2039         m_languages->setLanguage(edit->editor, f_real);
 2040 
 2041     //QTime time;
 2042     //time.start();
 2043     edit->editor->load(f_real, QDocument::defaultCodec());
 2044 
 2045     if (!edit->editor->languageDefinition())
 2046         guessLanguageFromContent(m_languages, edit->editor);
 2047 
 2048 
 2049     //qDebug() << "Load time: " << time.elapsed();
 2050     edit->editor->document()->setLineEndingDirect(edit->editor->document()->originalLineEnding());
 2051 
 2052     edit->document->setEditorView(edit); //update file name (if document didn't exist)
 2053 
 2054     configureNewEditorViewEnd(edit, asProject, hidden);
 2055 
 2056     //check for svn conflict
 2057     if (!hidden) {
 2058         checkSVNConflicted();
 2059 
 2060         MarkCurrentFileAsRecent();
 2061     }
 2062 
 2063     documents.updateMasterSlaveRelations(doc, recheck);
 2064 
 2065     if (recheck || hidden) {
 2066         doc->updateLtxCommands();
 2067     }
 2068 
 2069     if (!hidden) {
 2070         if (QFile::exists(f_real + ".recover.bak~")
 2071                 && QFileInfo(f_real + ".recover.bak~").lastModified() > QFileInfo(f_real).lastModified()) {
 2072             if (UtilsUi::txsConfirm(tr("A crash recover file from %1 has been found for \"%2\".\nDo you want to restore it?").arg(QFileInfo(f_real + ".recover.bak~").lastModified().toString()).arg(f_real))) {
 2073                 QFile f(f_real + ".recover.bak~");
 2074                 if (f.open(QFile::ReadOnly)) {
 2075                     QByteArray ba = f.readAll();
 2076                     QString recovered = QTextCodec::codecForName("UTF-8")->toUnicode(ba); //TODO: chunk loading?
 2077                     edit->document->setText(recovered, true);
 2078                 } else UtilsUi::txsWarning(tr("Failed to open recover file \"%1\".").arg(f_real + ".recover.bak~"));
 2079             }
 2080         }
 2081 
 2082     }
 2083 
 2084     updateStructure(true, doc, true);
 2085 
 2086     if (!hidden)
 2087         showStructure();
 2088     bookmarks->restoreBookmarks(edit);
 2089 
 2090     if (asProject) documents.setMasterDocument(edit->document);
 2091 
 2092     if (outputView->getLogWidget()->logPresent()) {
 2093         updateLogEntriesInEditors();
 2094         setLogMarksVisible(true);
 2095     }
 2096     if (!bibTeXmodified)
 2097         documents.bibTeXFilesModified = false; //loading a file can change the list of included bib files, but we won't consider that as a modification of them, because then they don't have to be recompiled
 2098     LatexDocument *rootDoc = edit->document->getRootDocument();
 2099     if (rootDoc) foreach (const FileNamePair &fnp, edit->document->mentionedBibTeXFiles().values()) {
 2100             Q_ASSERT(!fnp.absolute.isEmpty());
 2101             rootDoc->lastCompiledBibTeXFiles.insert(fnp.absolute);
 2102         }
 2103 
 2104 #ifndef Q_OS_MAC
 2105     if (!hidden) {
 2106         if (windowState() == Qt::WindowMinimized || !isVisible() || !QApplication::activeWindow()) {
 2107             show();
 2108             if (windowState() == Qt::WindowMinimized)
 2109                 setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
 2110             show();
 2111             raise();
 2112             QApplication::setActiveWindow(this);
 2113             activateWindow();
 2114             setFocus();
 2115             edit->editor->setFocus();
 2116         }
 2117     }
 2118     //raise();
 2119     //#ifdef Q_OS_WIN32
 2120     //        if (IsIconic (this->winId())) ShowWindow(this->winId(), SW_RESTORE);
 2121     //#endif
 2122 #endif
 2123 
 2124     runScriptsInList(Macro::ST_LOAD_THIS_FILE, doc->localMacros);
 2125 
 2126     emit infoLoadFile(f_real);
 2127 
 2128     return edit;
 2129 }
 2130 
 2131 void Texstudio::completerNeedsUpdate()
 2132 {
 2133     mCompleterNeedsUpdate = true;
 2134 }
 2135 
 2136 void Texstudio::needUpdatedCompleter()
 2137 {
 2138     if (mCompleterNeedsUpdate)
 2139         updateCompleter();
 2140 }
 2141 
 2142 void Texstudio::updateUserToolMenu()
 2143 {
 2144     CommandMapping cmds = buildManager.getAllCommands();
 2145     QStringList order = buildManager.getCommandsOrder();
 2146     QStringList ids;
 2147     QStringList displayName;
 2148     for (int i = 0; i < order.size(); i++) {
 2149         const CommandInfo &ci = cmds.value(order[i]);
 2150         if (!ci.user) continue;
 2151         ids << ci.id;
 2152         displayName << ci.displayName;
 2153     }
 2154     configManager.updateListMenu("main/tools/user", displayName, "cmd", true, SLOT(commandFromAction()), Qt::ALT + Qt::SHIFT + Qt::Key_F1, false, 0);
 2155     QMenu *m = getManagedMenu("main/tools/user");
 2156     REQUIRE(m);
 2157     QList<QAction *> actions = m->actions();
 2158     for (int i = 0; i < actions.size(); i++)
 2159         actions[i]->setData(BuildManager::TXS_CMD_PREFIX + ids[i]);
 2160 }
 2161 
 2162 #include "QMetaMethod"
 2163 void Texstudio::linkToEditorSlot(QAction *act, const char *methodName, const QList<QVariant> &args)
 2164 {
 2165     REQUIRE(act);
 2166     connectUnique(act, SIGNAL(triggered()), this, SLOT(relayToEditorSlot()));
 2167     act->setProperty("primarySlot", QString(SLOT(relayToEditorSlot())));
 2168     QByteArray signature = createMethodSignature(methodName, args);
 2169     if (!args.isEmpty())
 2170         act->setProperty("args", QVariant::fromValue<QList<QVariant> >(args));
 2171     for (int i = 0; i < LatexEditorView::staticMetaObject.methodCount(); i++)
 2172 #if QT_VERSION>=0x050000
 2173         if (signature == LatexEditorView::staticMetaObject.method(i).methodSignature()) {
 2174             act->setProperty("editorViewSlot", methodName);
 2175             return;
 2176         } //else qDebug() << LatexEditorView::staticMetaObject.method(i).signature();
 2177     for (int i = 0; i < QEditor::staticMetaObject.methodCount(); i++)
 2178         if (signature == QEditor::staticMetaObject.method(i).methodSignature()) {
 2179             act->setProperty("editorSlot", methodName);
 2180             return;
 2181         }
 2182 #else
 2183         if (signature == LatexEditorView::staticMetaObject.method(i).signature()) {
 2184             act->setProperty("editorViewSlot", methodName);
 2185             return;
 2186         } //else qDebug() << LatexEditorView::staticMetaObject.method(i).signature();
 2187     for (int i = 0; i < QEditor::staticMetaObject.methodCount(); i++)
 2188         if (signature == QEditor::staticMetaObject.method(i).signature()) {
 2189             act->setProperty("editorSlot", methodName);
 2190             return;
 2191         }
 2192 #endif
 2193 
 2194     qDebug() << methodName << signature;
 2195     Q_ASSERT(false);
 2196 }
 2197 
 2198 void Texstudio::relayToEditorSlot()
 2199 {
 2200     if (!currentEditorView()) return;
 2201     QAction *act = qobject_cast<QAction *>(sender());
 2202     REQUIRE(act);
 2203     if (act->property("editorViewSlot").isValid()) QMetaObjectInvokeMethod(currentEditorView(), qPrintable(act->property("editorViewSlot").toString()), act->property("args").value<QList<QVariant> >());
 2204     else if (act->property("editorSlot").isValid()) QMetaObjectInvokeMethod(currentEditor(), qPrintable(act->property("editorSlot").toString()), act->property("args").value<QList<QVariant> >());
 2205 }
 2206 
 2207 void Texstudio::relayToOwnSlot()
 2208 {
 2209     QAction *act = qobject_cast<QAction *>(sender());
 2210     REQUIRE(act && act->property("slot").isValid());
 2211     QMetaObjectInvokeMethod(this, qPrintable(act->property("slot").toString()), act->property("args").value<QList<QVariant> >());
 2212 }
 2213 
 2214 void Texstudio::autoRunScripts()
 2215 {
 2216     QStringList vers = QString(QT_VERSION_STR).split('.');
 2217     Q_ASSERT(vers.length() >= 2);
 2218     int major = vers.at(0).toInt();
 2219     int minor = vers.at(1).toInt();
 2220     if (!hasAtLeastQt(major, minor))
 2221         UtilsUi::txsWarning(tr("%1 has been compiled with Qt %2, but is running with Qt %3.\nPlease get the correct runtime library (e.g. .dll or .so files).\nOtherwise there might be random errors and crashes.")
 2222                    .arg(TEXSTUDIO).arg(QT_VERSION_STR).arg(qVersion()));
 2223     runScripts(Macro::ST_TXS_START);
 2224 }
 2225 
 2226 void Texstudio::runScripts(int trigger)
 2227 {
 2228     runScriptsInList(trigger, configManager.completerConfig->userMacros);
 2229 }
 2230 
 2231 void Texstudio::runScriptsInList(int trigger, const QList<Macro> &scripts)
 2232 {
 2233     foreach (const Macro &macro, scripts) {
 2234         if (macro.type == Macro::Script && macro.isActiveForTrigger((Macro::SpecialTrigger) trigger))
 2235             runScript(macro.script(), MacroExecContext(trigger));
 2236     }
 2237 }
 2238 
 2239 void Texstudio::fileNewInternal(QString fileName)
 2240 {
 2241     LatexDocument *doc = new LatexDocument(this);
 2242     doc->enableSyntaxCheck(configManager.editorConfig->inlineSyntaxChecking);
 2243     LatexEditorView *edit = new LatexEditorView (nullptr, configManager.editorConfig, doc);
 2244     edit->setLatexPackageList(&latexPackageList);
 2245     if (configManager.newFileEncoding)
 2246         edit->editor->setFileCodec(configManager.newFileEncoding);
 2247     else
 2248         edit->editor->setFileCodec(QTextCodec::codecForName("utf-8"));
 2249     doc->clearUndo(); // inital file codec setting should not be undoable
 2250     edit->editor->setFileName(fileName);
 2251 
 2252     configureNewEditorView(edit);
 2253 
 2254     edit->document = doc;
 2255     edit->document->setEditorView(edit);
 2256     documents.addDocument(edit->document);
 2257 
 2258     configureNewEditorViewEnd(edit);
 2259     doc->updateLtxCommands();
 2260     if (!fileName.isEmpty())
 2261         fileSave(true);
 2262 }
 2263 
 2264 void Texstudio::fileNew(QString fileName)
 2265 {
 2266     fileNewInternal(fileName);
 2267     emit infoNewFile();
 2268 }
 2269 
 2270 void Texstudio::fileAutoReloading(QString fname)
 2271 {
 2272     LatexDocument *document = documents.findDocument(fname);
 2273     if (!document) return;
 2274     document->initClearStructure();
 2275 }
 2276 /* \brief called when file has been reloaded from disc
 2277  */
 2278 void Texstudio::fileReloaded()
 2279 {
 2280     QEditor *mEditor = qobject_cast<QEditor *>(sender());
 2281     if (mEditor == currentEditor()) {
 2282         currentEditorView()->document->initClearStructure();
 2283         updateStructure(true);
 2284     } else {
 2285         LatexDocument *document = documents.findDocument(mEditor->fileName());
 2286         if (!document) return;
 2287         document->initClearStructure();
 2288         document->patchStructure(0, -1);
 2289     }
 2290 }
 2291 /*!
 2292  * \brief make a template from current editor
 2293  */
 2294 void Texstudio::fileMakeTemplate()
 2295 {
 2296     if (!currentEditorView())
 2297         return;
 2298 
 2299     MakeTemplateDialog templateDialog(TemplateManager::userTemplateDir(), currentEditor()->fileName());
 2300     if (templateDialog.exec()) {
 2301         // save file
 2302         QString fn = templateDialog.suggestedFile();
 2303         LatexDocument *doc = currentEditorView()->document;
 2304         QString txt = doc->text();
 2305         //txt.replace("%","%%"); not necessary any more
 2306         QFile file_txt(fn);
 2307         if (!file_txt.open(QIODevice::WriteOnly | QIODevice::Text)) {
 2308             UtilsUi::txsInformation(tr("Could not write template data:") + "\n" + fn);
 2309             return;
 2310         } else {
 2311             QTextStream out(&file_txt);
 2312             out.setCodec("UTF-8");
 2313             out << txt;
 2314             file_txt.close();
 2315         }
 2316 
 2317         /* alternate code in order to double %
 2318 
 2319         QString old_name=currentEditor()->fileName();
 2320         QTextCodec *mCodec=currentEditor()->getFileCodec();
 2321         currentEditor()->setFileCodec(QTextCodec::codecForName("utf-8"));
 2322         currentEditor()->save(fn);
 2323         currentEditor()->setFileName(old_name);
 2324         currentEditor()->setFileCodec(mCodec);
 2325         */
 2326 
 2327         // save metaData
 2328         QFileInfo fi(fn);
 2329         fn = fi.absoluteFilePath();
 2330         fn.remove(fn.lastIndexOf('.'), 4);
 2331         fn.append(".json");
 2332         QFile file(fn);
 2333         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 2334             UtilsUi::txsInformation(tr("Could not write template meta data:") + "\n" + fn);
 2335         } else {
 2336             QTextStream out(&file);
 2337             out.setCodec("UTF-8");
 2338             out << templateDialog.generateMetaData();
 2339             file.close();
 2340         }
 2341     }
 2342 }
 2343 /*!
 2344  * \brief load template file for editing
 2345  * \param fname filename
 2346  */
 2347 void Texstudio::templateEdit(const QString &fname)
 2348 {
 2349     load(fname, false);
 2350 }
 2351 /*!
 2352  * \brief generate new file from template
 2353  */
 2354 void Texstudio::fileNewFromTemplate()
 2355 {
 2356     TemplateManager tmplMgr;
 2357     connectUnique(&tmplMgr, SIGNAL(editRequested(QString)), this, SLOT(templateEdit(QString)));
 2358 
 2359     TemplateSelector *dialog = tmplMgr.createLatexTemplateDialog();
 2360     if (!dialog->exec()) return;
 2361 
 2362     TemplateHandle th = dialog->selectedTemplate();
 2363     if (!th.isValid()) return;
 2364 
 2365     if (dialog->createInFolder()) {
 2366         th.createInFolder(dialog->creationFolder());
 2367         if (th.isMultifile()) {
 2368             QDir dir(dialog->creationFolder());
 2369             foreach (const QString &f, th.filesToOpen()) {
 2370                 QFileInfo fi(dir, f);
 2371                 if (fi.exists() && fi.isFile())
 2372                     load(fi.absoluteFilePath());
 2373             }
 2374         } else {
 2375             QDir dir(dialog->creationFolder());
 2376             QFileInfo fi(dir, QFileInfo(th.file()).fileName());
 2377             load(fi.absoluteFilePath());
 2378         }
 2379     } else {
 2380         QString fname = th.file();
 2381         QFile file(fname);
 2382         if (!file.exists()) {
 2383             UtilsUi::txsWarning(tr("File not found:") + QString("\n%1").arg(fname));
 2384             return;
 2385         }
 2386         if (!file.open(QIODevice::ReadOnly)) {
 2387             UtilsUi::txsWarning(tr("You do not have read permission to this file:") + QString("\n%1").arg(fname));
 2388             return;
 2389         }
 2390 
 2391         //set up new editor with template
 2392         fileNewInternal();
 2393         LatexEditorView *edit = currentEditorView();
 2394 
 2395         QString mTemplate;
 2396         bool loadAsSnippet = false;
 2397         QTextStream in(&file);
 2398         in.setCodec(QTextCodec::codecForName("UTF-8"));
 2399         QString line = in.readLine();
 2400         if (line.contains(QRegExp("^%\\s*!TXS\\s+template"))) {
 2401             loadAsSnippet = true;
 2402         } else {
 2403             mTemplate += line + '\n';
 2404         }
 2405         while (!in.atEnd()) {
 2406             line = in.readLine();
 2407             mTemplate += line + "\n";
 2408         }
 2409         if (loadAsSnippet) {
 2410             bool flag = edit->editor->flag(QEditor::AutoIndent);
 2411             edit->editor->setFlag(QEditor::AutoIndent, false);
 2412             CodeSnippet toInsert(mTemplate, false);
 2413             toInsert.insert(edit->editor);
 2414             edit->editor->setFlag(QEditor::AutoIndent, flag);
 2415             edit->editor->setCursorPosition(0, 0, false);
 2416             edit->editor->nextPlaceHolder();
 2417             edit->editor->ensureCursorVisible(QEditor::KeepSurrounding);
 2418         } else {
 2419             edit->editor->setText(mTemplate, false);
 2420         }
 2421 
 2422         emit infoNewFromTemplate();
 2423     }
 2424     delete dialog;
 2425 }
 2426 /*!
 2427  * \brief insert table template
 2428  */
 2429 void Texstudio::insertTableTemplate()
 2430 {
 2431     QEditor *m_edit = currentEditor();
 2432     if (!m_edit)
 2433         return;
 2434     QDocumentCursor c = m_edit->cursor();
 2435     if (!LatexTables::inTableEnv(c))
 2436         return;
 2437 
 2438     // select Template
 2439     TemplateManager tmplMgr;
 2440     connectUnique(&tmplMgr, SIGNAL(editRequested(QString)), this, SLOT(templateEdit(QString)));
 2441     if (tmplMgr.tableTemplateDialogExec()) {
 2442         QString fname = tmplMgr.selectedTemplateFile();
 2443         QFile file(fname);
 2444         if (!file.exists()) {
 2445             UtilsUi::txsWarning(tr("File not found:") + QString("\n%1").arg(fname));
 2446             return;
 2447         }
 2448         if (!file.open(QIODevice::ReadOnly)) {
 2449             UtilsUi::txsWarning(tr("You do not have read permission to this file:") + QString("\n%1").arg(fname));
 2450             return;
 2451         }
 2452         QString tableDef = LatexTables::getSimplifiedDef(c);
 2453         QString tableText = LatexTables::getTableText(c);
 2454         QString widthDef;
 2455         //remove table
 2456         c.removeSelectedText();
 2457         m_edit->setCursor(c);
 2458         // split table text into line/column list
 2459         QStringList values;
 2460         QList<int> starts;
 2461         QString env;
 2462         //tableText.remove("\n");
 2463         tableText.remove("\\hline");
 2464         if (tableText.startsWith("\\begin")) {
 2465             LatexParser::resolveCommandOptions(tableText, 0, values, &starts);
 2466             env = values.takeFirst();
 2467             env.remove(0, 1);
 2468             env.remove(env.length() - 1, 1);
 2469             // special treatment for tabu/longtabu
 2470             if ((env == "tabu" || env == "longtabu") && values.count() < 1) {
 2471                 int startExtra = env.length() + 8;
 2472                 int endExtra = tableText.indexOf("{", startExtra);
 2473                 if (endExtra >= 0 && endExtra > startExtra) {
 2474                     QString textHelper = tableText;
 2475                     widthDef = textHelper.mid(startExtra, endExtra - startExtra);
 2476                     textHelper.remove(startExtra, endExtra - startExtra); // remove to/spread definition
 2477                     values.clear();
 2478                     starts.clear();
 2479                     LatexParser::resolveCommandOptions(textHelper, 0, values, &starts);
 2480                     for (int i = 1; i < starts.count(); i++) {
 2481                         starts[i] += endExtra - startExtra;
 2482                     }
 2483                 }
 2484                 if (!values.isEmpty())
 2485                     values.takeFirst();
 2486             }
 2487             if (LatexTables::tabularNames.contains(env)) {
 2488                 if (!values.isEmpty()) {
 2489                     int i = starts.at(1);
 2490                     i += values.first().length();
 2491                     tableText.remove(0, i);
 2492                 }
 2493             }
 2494             if (LatexTables::tabularNamesWithOneOption.contains(env)) {
 2495                 if (values.size() > 1) {
 2496                     int i = starts.at(2);
 2497                     i += values.at(1).length();
 2498                     tableText.remove(0, i);
 2499                 }
 2500             }
 2501             tableText.remove(QRegExp("\\\\end\\{" + env + "\\}$"));
 2502         }
 2503         tableText.replace("\\endhead", "\\\\");
 2504         QStringList lines = tableText.split("\\\\");
 2505         QList<QStringList> tableContent;
 2506         foreach (QString line, lines) {
 2507             //line=line.simplified();
 2508             if (line.simplified().isEmpty())
 2509                 continue;
 2510             QStringList elems = line.split(QRegExp("&"));
 2511             if (elems.count() > 0) {
 2512                 if (elems[0].startsWith("\n"))
 2513                     elems[0] = elems[0].mid(1);
 2514             }
 2515             //QList<QString>::iterator i;
 2516             /*for(i=elems.begin();i!=elems.end();i++){
 2517                 QString elem=*i;
 2518                 *i=elem.simplified();
 2519             }*/
 2520 
 2521             // handle \& correctly
 2522             for (int i = elems.size() - 1; i >= 0; --i) {
 2523                 if (elems.at(i).endsWith("\\")) {
 2524                     QString add = elems.at(i) + elems.at(i + 1);
 2525                     elems.replace(i, add);
 2526                     elems.removeAt(i + 1);
 2527                 }
 2528             }
 2529             tableContent << elems;
 2530         }
 2531         LatexTables::generateTableFromTemplate(currentEditorView(), fname, tableDef, tableContent, env, widthDef);
 2532     }
 2533 }
 2534 /*! \brief align columns of latex table in editor
 2535  */
 2536 void Texstudio::alignTableCols()
 2537 {
 2538     if (!currentEditor()) return;
 2539     QDocumentCursor cur(currentEditor()->cursor());
 2540     int linenr = cur.lineNumber();
 2541     int col = cur.columnNumber();
 2542     if (!cur.isValid())
 2543         return;
 2544     LatexTables::alignTableCols(cur);
 2545     cur.setLineNumber(linenr);
 2546     cur.setColumnNumber(col);
 2547     currentEditor()->setCursor(cur);
 2548 }
 2549 /*! \brief open file
 2550  *
 2551  * open file is triggered from menu action.
 2552  * It opens a file dialog and lets the user to select a file.
 2553  * If the file is already open, the apropriate editor subwindow is brought to front.
 2554  * If the file is open as hidden, an editor is created and brought to front.
 2555  * pdf files are handled as well and they are forwarded to the pdf viewer.
 2556  */
 2557 void Texstudio::fileOpen()
 2558 {
 2559     QString currentDir = QDir::homePath();
 2560     if (!configManager.lastDocument.isEmpty()) {
 2561         QFileInfo fi(configManager.lastDocument);
 2562         if (fi.exists() && fi.isReadable()) {
 2563             currentDir = fi.absolutePath();
 2564         }
 2565     }
 2566     QStringList files = FileDialog::getOpenFileNames(this, tr("Open Files"), currentDir, fileFilters,  &selectedFileFilter);
 2567 
 2568     recheckLabels = false; // impede label rechecking on hidden docs
 2569     QList<LatexEditorView *>listViews;
 2570     foreach (const QString &fn, files)
 2571         listViews << load(fn);
 2572 
 2573     // update ref/labels in one go;
 2574     QList<LatexDocument *> completedDocs;
 2575     foreach (LatexEditorView *edView, listViews) {
 2576         if (!edView)
 2577             continue;
 2578         LatexDocument *docBase = edView->getDocument();
 2579         foreach (LatexDocument *doc, docBase->getListOfDocs()) {
 2580             doc->recheckRefsLabels();
 2581             if (completedDocs.contains(doc))
 2582                 continue;
 2583             doc->updateLtxCommands(true);
 2584             completedDocs << doc->getListOfDocs();
 2585         }
 2586     }
 2587     recheckLabels = true;
 2588     // update completer
 2589     if (currentEditorView())
 2590         updateCompleter(currentEditorView());
 2591 }
 2592 
 2593 void Texstudio::fileRestoreSession(bool showProgress, bool warnMissing)
 2594 {
 2595 
 2596     QFileInfo f(QDir(configManager.configBaseDir), "lastSession.txss");
 2597     Session s;
 2598     if (f.exists()) {
 2599         if (!s.load(f.filePath())) {
 2600             UtilsUi::txsCritical(tr("Loading of last session failed."));
 2601         }
 2602     }
 2603     restoreSession(s, showProgress, warnMissing);
 2604 }
 2605 /*!
 2606  * \brief save current editor content
 2607  *
 2608  * \param saveSilently
 2609  */
 2610 void Texstudio::fileSave(const bool saveSilently)
 2611 {
 2612     if (!currentEditor())
 2613         return;
 2614 
 2615     if (currentEditor()->fileName() == "" || !QFileInfo(currentEditor()->fileName()).exists()) {
 2616         removeDiffMarkers();// clean document from diff markers first
 2617         fileSaveAs(currentEditor()->fileName(), saveSilently);
 2618     } else {
 2619         /*QFile file( *filenames.find( currentEditorView() ) );
 2620         if ( !file.open( QIODevice::WriteOnly ) )
 2621         {
 2622         QMessageBox::warning( this,tr("Error"),tr("The file could not be saved. Please check if you have write permission."));
 2623         return;
 2624         }*/
 2625         removeDiffMarkers();// clean document from diff markers first
 2626         currentEditor()->save();
 2627         currentEditor()->document()->markViewDirty();//force repaint of line markers (yellow -> green)
 2628         MarkCurrentFileAsRecent();
 2629         int checkIn = (configManager.autoCheckinAfterSaveLevel > 0 && !saveSilently) ? 2 : 1;
 2630         emit infoFileSaved(currentEditor()->fileName(), checkIn);
 2631     }
 2632     updateCaption();
 2633     //updateStructure(); (not needed anymore for autoupdate)
 2634 }
 2635 /*!
 2636  * \brief save current editor content to new filename
 2637  *
 2638  * save current editor content to new filename
 2639  * \param fileName
 2640  * \param saveSilently don't ask for new filename if fileName is empty
 2641  */
 2642 void Texstudio::fileSaveAs(const QString &fileName, const bool saveSilently)
 2643 {
 2644     if (!currentEditorView())
 2645         return;
 2646 
 2647     // select a directory/filepath
 2648     QString currentDir = QDir::homePath();
 2649     if (fileName.isEmpty()) {
 2650         if (currentEditor()->fileInfo().isFile()) {
 2651             currentDir = currentEditor()->fileInfo().absoluteFilePath();
 2652         } else {
 2653             if (!configManager.lastDocument.isEmpty())
 2654                 currentDir = configManager.lastDocument;
 2655             static QString saveAsDefault;
 2656             configManager.registerOption("Files/Save As Default", &saveAsDefault, "?a)/document");
 2657             currentDir = buildManager.parseExtendedCommandLine(saveAsDefault, currentDir, currentDir).value(0, currentDir);
 2658         }
 2659     } else {
 2660         currentDir = fileName;
 2661         /*QFileInfo currentFile(fileName);
 2662         if (currentFile.absoluteDir().exists())
 2663         currentDir = fileName;*/
 2664     }
 2665 
 2666     // get a file name
 2667     QString fn = fileName;
 2668     if (!saveSilently || fn.isEmpty()) {
 2669         fn = FileDialog::getSaveFileName(this, tr("Save As"), currentDir, fileFilters, &selectedFileFilter);
 2670         if (!fn.isEmpty()) {
 2671             static QRegExp fileExt("\\*(\\.[^ )]+)");
 2672             if (fileExt.indexIn(selectedFileFilter) > -1) {
 2673                 //add
 2674                 int lastsep = qMax(fn.lastIndexOf("/"), fn.lastIndexOf("\\"));
 2675                 int lastpoint = fn.lastIndexOf(".");
 2676                 if (lastpoint <= lastsep) //if both aren't found or point is in directory name
 2677                     fn.append(fileExt.cap(1));
 2678             }
 2679         }
 2680     }
 2681     if (!fn.isEmpty()) {
 2682         if (getEditorViewFromFileName(fn) && getEditorViewFromFileName(fn) != currentEditorView()) {
 2683             // trying to save with same name as another already existing file
 2684             LatexEditorView *otherEdView = getEditorViewFromFileName(fn);
 2685             if (!otherEdView->document->isClean()) {
 2686                 UtilsUi::txsWarning(tr("Saving under the name\n"
 2687                               "%1\n"
 2688                               "is currently not possible because a modified version of a file\n"
 2689                               "with this name is open in TeXstudio. You have to save or close\n"
 2690                               "this other file before you can overwrite it.").arg(fn));
 2691                 return;
 2692             }
 2693             // other editor does not contain unsaved changes, so it can be closed
 2694             LatexEditorView *currentEdView = currentEditorView();
 2695             editors->setCurrentEditor(otherEdView);
 2696             fileClose();
 2697             editors->setCurrentEditor(currentEdView);
 2698         }
 2699 #ifndef NO_POPPLER_PREVIEW
 2700         // show message in viewer
 2701         if (currentEditor()->fileInfo() != QFileInfo(fn)) {
 2702             foreach (PDFDocument *viewer, PDFDocument::documentList())
 2703                 if (viewer->getMasterFile() == currentEditor()->fileInfo())
 2704                     viewer->showMessage(tr("This pdf cannot be synchronized with the tex source any more because the source file has been renamed due to a Save As operation. You should recompile the renamed file and view its result."));
 2705         }
 2706 #endif
 2707         // save file
 2708         removeDiffMarkers();// clean document from diff markers first
 2709         currentEditor()->save(fn);
 2710         currentEditorView()->document->setEditorView(currentEditorView()); //update file name
 2711         MarkCurrentFileAsRecent();
 2712 
 2713         //update Master/Child relations
 2714         LatexDocument *doc = currentEditorView()->document;
 2715         documents.updateMasterSlaveRelations(doc);
 2716 
 2717         updateOpenDocumentMenu(true);  // TODO: currently duplicate functionality with updateCaption() below
 2718         if (currentEditor()->fileInfo().suffix().toLower() != "tex")
 2719             m_languages->setLanguage(currentEditor(), fn);
 2720 
 2721         emit infoFileSaved(currentEditor()->fileName());
 2722     }
 2723 
 2724     updateCaption();
 2725 }
 2726 /*!
 2727  * \brief save all files
 2728  *
 2729  * This functions is called from menu-action.
 2730  */
 2731 void Texstudio::fileSaveAll()
 2732 {
 2733     fileSaveAll(true, true);
 2734 }
 2735 /*!
 2736  * \brief save all files
 2737  *
 2738  * This functions is called from timer (auto save).
 2739  * It does *not* save unnamed files.
 2740  */
 2741 void Texstudio::fileSaveAllFromTimer()
 2742 {
 2743     fileSaveAll(false, false);
 2744 }
 2745 /*!
 2746  * \brief save all files
 2747  *
 2748  * \param alsoUnnamedFiles
 2749  * \param alwaysCurrentFile
 2750  */
 2751 void Texstudio::fileSaveAll(bool alsoUnnamedFiles, bool alwaysCurrentFile)
 2752 {
 2753     //LatexEditorView *temp = new LatexEditorView(EditorView,colorMath,colorCommand,colorKeyword);
 2754     //temp=currentEditorView();
 2755     REQUIRE(editors);
 2756 
 2757     LatexEditorView *currentEdView = currentEditorView();
 2758 
 2759     foreach (LatexEditorView *edView, editors->editors()) {
 2760         REQUIRE(edView->editor);
 2761 
 2762         if (edView->editor->fileName().isEmpty()) {
 2763             if ((alsoUnnamedFiles || (alwaysCurrentFile && edView == currentEdView) ) ) {
 2764                 editors->setCurrentEditor(edView);
 2765                 fileSaveAs();
 2766             } else if (!edView->document->getTemporaryFileName().isEmpty())
 2767                 edView->editor->saveCopy(edView->document->getTemporaryFileName());
 2768             //else if (edView->editor->isInConflict()) {
 2769             //edView->editor->save();
 2770             //}
 2771         } else if (edView->editor->isContentModified() || edView->editor->isInConflict()) {
 2772             removeDiffMarkers();// clean document from diff markers first
 2773             edView->editor->save(); //only save modified documents
 2774 
 2775             if (edView->editor->fileName().endsWith(".bib")) {
 2776                 QString temp = edView->editor->fileName();
 2777                 temp = temp.replace(QDir::separator(), "/");
 2778                 documents.bibTeXFilesModified = documents.bibTeXFilesModified  || documents.mentionedBibTeXFiles.contains(temp);//call bibtex on next compilation (this would also set as soon as the user switch to a tex file, but he could compile before switching)
 2779             }
 2780 
 2781             emit infoFileSaved(edView->editor->fileName());
 2782         }
 2783     }
 2784 
 2785     if (currentEditorView() != currentEdView)
 2786         editors->setCurrentEditor(currentEdView);
 2787     documents.updateStructure(); //remove italics status from previous unsaved files
 2788     updateUndoRedoStatus();
 2789 }
 2790 
 2791 //TODO: handle svn in all these methods
 2792 
 2793 void Texstudio::fileUtilCopyMove(bool move)
 2794 {
 2795     QString fn = documents.getCurrentFileName();
 2796     if (fn.isEmpty()) return;
 2797     QString newfn = FileDialog::getSaveFileName(this, move ? tr("Rename/Move") : tr("Copy"), fn, fileFilters, &selectedFileFilter);
 2798     if (newfn.isEmpty()) return;
 2799     if (fn == newfn) return;
 2800     QFile::Permissions permissions = QFile(fn).permissions();
 2801     if (move) fileSaveAs(newfn, true);
 2802     else currentEditor()->saveCopy(newfn);
 2803     if (documents.getCurrentFileName() != newfn) return;
 2804     if (move) QFile(fn).remove();
 2805     QFile(newfn).setPermissions(permissions); //keep permissions. (better: actually move the file, keeping the inode. but then all that stuff (e.g. master/slave) has to be updated here
 2806 }
 2807 
 2808 void Texstudio::fileUtilDelete()
 2809 {
 2810     QString fn = documents.getCurrentFileName();
 2811     if (fn.isEmpty()) return;
 2812     if (UtilsUi::txsConfirmWarning(tr("Do you really want to delete the file \"%1\"?").arg(fn)))
 2813         QFile(fn).remove();
 2814 }
 2815 
 2816 void Texstudio::fileUtilRevert()
 2817 {
 2818     if (!currentEditor()) return;
 2819     QString fn = documents.getCurrentFileName();
 2820     if (fn.isEmpty()) return;
 2821     if (UtilsUi::txsConfirmWarning(tr("Do you really want to revert the file \"%1\"?").arg(documents.getCurrentFileName())))
 2822         currentEditor()->reload();
 2823 }
 2824 
 2825 void Texstudio::fileUtilPermissions()
 2826 {
 2827     QString fn = documents.getCurrentFileName();
 2828     if (fn.isEmpty()) return;
 2829 
 2830     QFile f(fn);
 2831 
 2832     int permissionsRaw = f.permissions();
 2833     int permissionsUnixLike = ((permissionsRaw & 0x7000) >> 4) | (permissionsRaw & 0x0077); //ignore qt "user" flags
 2834     QString permissionsUnixLikeHex = QString::number(permissionsUnixLike, 16);
 2835     QString permissions;
 2836     const QString PERMISSIONSCODES = "rwx";
 2837     int flag = QFile::ReadUser;
 2838     REQUIRE(QFile::ReadUser == 0x400);
 2839     int temp = permissionsUnixLike;
 2840     for (int b = 0; b < 3; b++) {
 2841         for (int i = 0; i < 3; i++, flag >>= 1)
 2842             permissions += (temp & flag) ? PERMISSIONSCODES[i] : QChar('-');
 2843         flag >>= 1;
 2844     }
 2845     QString oldPermissionsUnixLikeHex = permissionsUnixLikeHex, oldPermissions = permissions;
 2846 
 2847     UniversalInputDialog uid;
 2848     uid.addVariable(&permissionsUnixLikeHex, tr("Numeric permissions"));
 2849     uid.addVariable(&permissions, tr("Verbose permissions"));
 2850     if (uid.exec() == QDialog::Accepted && (permissionsUnixLikeHex != oldPermissionsUnixLikeHex || permissions != oldPermissions)) {
 2851         if (permissionsUnixLikeHex != oldPermissionsUnixLikeHex)
 2852                 permissionsRaw = permissionsUnixLikeHex.toInt(nullptr, 16);
 2853         else {
 2854             permissionsRaw = 0;
 2855             int flag = QFile::ReadUser;
 2856             int p = 0;
 2857             for (int b = 0; b < 3; b++) {
 2858                 for (int i = 0; i < 3; i++, flag >>= 1) {
 2859                     if (permissions[p] == '-') p++;
 2860                     else if (permissions[p] == PERMISSIONSCODES[i]) {
 2861                         permissionsRaw |= flag;
 2862                         p++;
 2863                     } else if (!QString("rwx").contains(permissions[p])) {
 2864                         UtilsUi::txsWarning("invalid character in permission: " + permissions[p]);
 2865                         return;
 2866                     }
 2867                     if (p >= permissions.length()) p = 0; //wrap around
 2868                 }
 2869                 flag >>= 1;
 2870             }
 2871         }
 2872         permissionsRaw = ((permissionsRaw << 4) & 0x7000) | permissionsRaw; //use qt "user" as owner flags
 2873         f.setPermissions(QFile::Permissions(permissionsRaw)) ;
 2874     }
 2875 }
 2876 
 2877 void Texstudio::fileUtilCopyFileName()
 2878 {
 2879     QApplication::clipboard()->setText(documents.getCurrentFileName());
 2880 }
 2881 
 2882 void Texstudio::fileUtilCopyMasterFileName()
 2883 {
 2884     QApplication::clipboard()->setText(documents.getCompileFileName());
 2885 }
 2886 
 2887 void Texstudio::fileClose()
 2888 {
 2889     if (!currentEditorView())   return;
 2890     bookmarks->updateBookmarks(currentEditorView());
 2891     QFileInfo fi = currentEditorView()->document->getFileInfo();
 2892 
 2893 repeatAfterFileSavingFailed:
 2894     if (currentEditorView()->editor->isContentModified()) {
 2895         switch (QMessageBox::warning(this, TEXSTUDIO,
 2896                                      tr("The document \"%1\" contains unsaved work. "
 2897                                         "Do you want to save it before closing?").arg(currentEditorView()->displayName()),
 2898                                      tr("Save and Close"), tr("Close without Saving"), tr("Cancel"),
 2899                                      0,
 2900                                      2)) {
 2901         case 0:
 2902             fileSave();
 2903             if (currentEditorView()->editor->isContentModified())
 2904                 goto repeatAfterFileSavingFailed;
 2905             documents.deleteDocument(currentEditorView()->document);
 2906             break;
 2907         case 1:
 2908             documents.deleteDocument(currentEditorView()->document);
 2909             break;
 2910         case 2:
 2911         default:
 2912             return;
 2913         }
 2914     } else documents.deleteDocument(currentEditorView()->document);
 2915     //UpdateCaption(); unnecessary as called by tabChanged (signal)
 2916 
 2917 #ifndef NO_POPPLER_PREVIEW
 2918     //close associated embedded pdf viewer
 2919     foreach (PDFDocument *viewer, PDFDocument::documentList())
 2920         if (viewer->autoClose && viewer->getMasterFile() == fi)
 2921             viewer->close();
 2922 #endif
 2923 }
 2924 
 2925 void Texstudio::fileCloseAll()
 2926 {
 2927     bool accept = saveAllFilesForClosing();
 2928     if (accept) {
 2929         closeAllFiles();
 2930     }
 2931 }
 2932 
 2933 void Texstudio::fileExit()
 2934 {
 2935     if (canCloseNow())
 2936     qApp->quit();
 2937 }
 2938 
 2939 void Texstudio::fileExitWithError()
 2940 {
 2941     if (canCloseNow()){
 2942         qApp->exit(1);
 2943     }
 2944 }
 2945 
 2946 bool Texstudio::saveAllFilesForClosing()
 2947 {
 2948     return saveFilesForClosing(editors->editors());
 2949 }
 2950 
 2951 bool Texstudio::saveFilesForClosing(const QList<LatexEditorView *> &editorList)
 2952 {
 2953     LatexEditorView *savedCurrentEditorView = currentEditorView();
 2954     foreach (LatexEditorView *edView, editorList) {
 2955 repeatAfterFileSavingFailed:
 2956         if (edView->editor->isContentModified()) {
 2957             editors->setCurrentEditor(edView);
 2958             switch (QMessageBox::warning(this, TEXSTUDIO,
 2959                                          tr("The document \"%1\" contains unsaved work. "
 2960                                             "Do you want to save it before closing?").arg(edView->displayName()),
 2961                                          tr("Save and Close"), tr("Close without Saving"), tr("Cancel"),
 2962                                          0,
 2963                                          2)) {
 2964             case 0:
 2965                 fileSave();
 2966                 if (currentEditorView()->editor->isContentModified())
 2967                     goto repeatAfterFileSavingFailed;
 2968                 break;
 2969             case 1:
 2970                 break;
 2971             case 2:
 2972             default:
 2973                 editors->setCurrentEditor(savedCurrentEditorView);
 2974                 return false;
 2975             }
 2976         }
 2977     }
 2978     editors->setCurrentEditor(savedCurrentEditorView);
 2979     return true;
 2980 }
 2981 /*! \brief close all files
 2982  */
 2983 void Texstudio::closeAllFiles()
 2984 {
 2985     while (currentEditorView())
 2986         documents.deleteDocument(currentEditorView()->document);
 2987 #ifndef NO_POPPLER_PREVIEW
 2988     foreach (PDFDocument *viewer, PDFDocument::documentList())
 2989         viewer->close();
 2990 #endif
 2991     documents.setMasterDocument(nullptr);
 2992     updateCaption();
 2993 }
 2994 
 2995 bool Texstudio::canCloseNow(bool saveSettings)
 2996 {
 2997     if (!saveAllFilesForClosing()) return false;
 2998 #ifndef NO_POPPLER_PREVIEW
 2999     foreach (PDFDocument *viewer, PDFDocument::documentList())
 3000         viewer->saveGeometryToConfig();
 3001 #endif
 3002     if (saveSettings)
 3003         this->saveSettings();
 3004     closeAllFiles();
 3005     delete userMacroDialog;
 3006     spellerManager.unloadAll();  //this saves the ignore list
 3007     programStopped = true;
 3008     Guardian::shutdown();
 3009     return true;
 3010 }
 3011 /*!
 3012  * \brief closeEvent
 3013  * \param e event
 3014  */
 3015 void Texstudio::closeEvent(QCloseEvent *e)
 3016 {
 3017     if (canCloseNow())  e->accept();
 3018     else e->ignore();
 3019 }
 3020 
 3021 void Texstudio::updateUserMacros(bool updateMenu)
 3022 {
 3023     if (updateMenu) configManager.updateUserMacroMenu();
 3024     for (int i = 0; i < configManager.completerConfig->userMacros.size(); i++) {
 3025         configManager.completerConfig->userMacros[i].parseTriggerLanguage(m_languages);
 3026     }
 3027 }
 3028 
 3029 /*!
 3030  * \brief open file from recent list
 3031  *
 3032  * The filename is determind from the sender-action, where it is encoded in data.
 3033  */
 3034 void Texstudio::fileOpenRecent()
 3035 {
 3036     QAction *action = qobject_cast<QAction *>(sender());
 3037     if (!action) return;
 3038     QString fn = action->data().toString();
 3039     if (!QFile::exists(fn)) {
 3040         if (UtilsUi::txsConfirmWarning(tr("The file \"%1\" does not exist anymore. Do you want to remove it from the recent file list?").arg(fn))) {
 3041             if (configManager.recentFilesList.removeAll(fn))
 3042                 configManager.updateRecentFiles();
 3043             return;
 3044         }
 3045     }
 3046     load(fn);
 3047 }
 3048 
 3049 void Texstudio::fileOpenAllRecent()
 3050 {
 3051     foreach (const QString &s, configManager.recentFilesList)
 3052         load(s);
 3053 }
 3054 
 3055 QRect appendToBottom(QRect r, const QRect &s)
 3056 {
 3057     r.setBottom(r.bottom() + s.height());
 3058     return r;
 3059 }
 3060 
 3061 void Texstudio::fileRecentList()
 3062 {
 3063     if (fileSelector) fileSelector.data()->deleteLater();
 3064     fileSelector = new FileSelector(editors, true);
 3065 
 3066     fileSelector.data()->init(QStringList() << configManager.recentProjectList << configManager.recentFilesList, 0);
 3067 
 3068     connect(fileSelector.data(), SIGNAL(fileChoosen(QString, int, int, int)), SLOT(fileDocumentOpenFromChoosen(QString, int, int, int)));
 3069     fileSelector.data()->setVisible(true);
 3070 }
 3071 
 3072 void Texstudio::viewDocumentListHidden()
 3073 {
 3074     if (fileSelector) fileSelector.data()->deleteLater();
 3075     fileSelector = new FileSelector(editors, true);
 3076 
 3077     QStringList hiddenDocs;
 3078     foreach (LatexDocument *d, documents.hiddenDocuments)
 3079         hiddenDocs << d->getFileName();
 3080     fileSelector.data()->init(hiddenDocs, 0);
 3081 
 3082     connect(fileSelector.data(), SIGNAL(fileChoosen(QString, int, int, int)), SLOT(fileDocumentOpenFromChoosen(QString, int, int, int)));
 3083     fileSelector.data()->setVisible(true);
 3084 }
 3085 
 3086 void Texstudio::fileDocumentOpenFromChoosen(const QString &doc, int duplicate, int lineNr, int column)
 3087 {
 3088     Q_UNUSED(duplicate)
 3089     if (!QFile::exists(doc)) {
 3090         if (UtilsUi::txsConfirmWarning(tr("The file \"%1\" does not exist anymore. Do you want to remove it from the recent file list?").arg(doc))) {
 3091             if (configManager.recentFilesList.removeAll(doc) + configManager.recentProjectList.removeAll(doc) > 0)
 3092                 configManager.updateRecentFiles();
 3093             return;
 3094         }
 3095     }
 3096 
 3097     if (!load(doc, duplicate < configManager.recentProjectList.count(doc))) return;
 3098     if (lineNr < 0) return;
 3099     REQUIRE(currentEditor());
 3100     currentEditor()->setCursorPosition(lineNr, column);
 3101 }
 3102 
 3103 bool mruEditorViewLessThan(const LatexEditorView *e1, const LatexEditorView *e2)
 3104 {
 3105     return e1->lastUsageTime > e2->lastUsageTime;
 3106 }
 3107 
 3108 void Texstudio::viewDocumentList()
 3109 {
 3110     if (fileSelector) fileSelector.data()->deleteLater();
 3111     fileSelector = new FileSelector(editors, false);
 3112 
 3113     LatexEditorView *curEdView = currentEditorView();
 3114     int curIndex = 0;
 3115     QList<LatexEditorView *> editorList = editors->editors();
 3116 
 3117     if (configManager.mruDocumentChooser) {
 3118         qSort(editorList.begin(), editorList.end(), mruEditorViewLessThan);
 3119         if (editorList.size() > 1)
 3120             if (editorList.first() == currentEditorView())
 3121                 curIndex = 1;
 3122     }
 3123 
 3124     int i = 0;
 3125     QStringList names;
 3126     foreach (LatexEditorView *edView, editorList) {
 3127         names << edView->displayName();
 3128         if (!configManager.mruDocumentChooser && edView == curEdView) curIndex = i;
 3129         i++;
 3130     }
 3131 
 3132     fileSelector.data()->init(names, curIndex);
 3133     connect(fileSelector.data(), SIGNAL(fileChoosen(QString, int, int, int)), SLOT(viewDocumentOpenFromChoosen(QString, int, int, int)));
 3134     fileSelector.data()->setVisible(true);
 3135 }
 3136 
 3137 void Texstudio::viewDocumentOpenFromChoosen(const QString &doc, int duplicate, int lineNr, int column)
 3138 {
 3139     if (duplicate < 0) return;
 3140     foreach (LatexEditorView *edView, editors->editors()) {
 3141         QString  name = edView->displayName();
 3142         if (name == doc) {
 3143             duplicate -= 1;
 3144             if (duplicate < 0) {
 3145                 editors->setCurrentEditor(edView);
 3146                 if (lineNr >= 0)
 3147                     edView->editor->setCursorPosition(lineNr, column);
 3148                 edView->setFocus();
 3149                 return;
 3150             }
 3151         }
 3152     }
 3153 }
 3154 
 3155 void Texstudio::fileOpenFirstNonOpen()
 3156 {
 3157     foreach (const QString &f, configManager.recentFilesList)
 3158         if (!getEditorViewFromFileName(f)) {
 3159             load(f);
 3160             break;
 3161         }
 3162 }
 3163 
 3164 void Texstudio::fileOpenRecentProject()
 3165 {
 3166     QAction *action = qobject_cast<QAction *>(sender());
 3167     if (!action) return;
 3168     QString fn = action->data().toString();
 3169     if (!QFile::exists(fn)) {
 3170         if (UtilsUi::txsConfirmWarning(tr("The file \"%1\" does not exist anymore. Do you want to remove it from the recent file list?").arg(fn))) {
 3171             if (configManager.recentProjectList.removeAll(fn))
 3172                 configManager.updateRecentFiles();
 3173             return;
 3174         }
 3175     }
 3176     load(fn, true);
 3177 }
 3178 
 3179 void Texstudio::loadSession(const QString &fileName)
 3180 {
 3181     Session s;
 3182     if (!s.load(fileName)) {
 3183         UtilsUi::txsCritical(tr("Loading of session failed."));
 3184         return;
 3185     }
 3186     restoreSession(s);
 3187 }
 3188 
 3189 void Texstudio::fileLoadSession()
 3190 {
 3191     QString openDir = QDir::homePath();
 3192     if (currentEditorView()) {
 3193         LatexDocument *doc = currentEditorView()->document;
 3194         if (doc->getMasterDocument()) {
 3195             openDir = doc->getMasterDocument()->getFileInfo().path();
 3196         } else {
 3197             openDir = doc->getFileInfo().path();
 3198         }
 3199     }
 3200     QString fn = FileDialog::getOpenFileName(this, tr("Load Session"), openDir, tr("TeXstudio Session") + " (*." + Session::fileExtension() + ")");
 3201     if (fn.isNull()) return;
 3202     loadSession(fn);
 3203     recentSessionList->addFilenameToList(fn);
 3204 }
 3205 
 3206 void Texstudio::fileSaveSession()
 3207 {
 3208     QString openDir = QDir::homePath();
 3209     if (currentEditorView()) {
 3210         LatexDocument *doc = currentEditorView()->document;
 3211         if (doc->getMasterDocument()) {
 3212             openDir = replaceFileExtension(doc->getMasterDocument()->getFileName(), Session::fileExtension());
 3213         } else {
 3214             openDir = replaceFileExtension(doc->getFileName(), Session::fileExtension());
 3215         }
 3216     }
 3217 
 3218     QString fn = FileDialog::getSaveFileName(this, tr("Save Session"), openDir, tr("TeXstudio Session") + " (*." + Session::fileExtension() + ")");
 3219     if (fn.isNull()) return;
 3220     if (!getCurrentSession().save(fn, configManager.sessionStoreRelativePaths)) {
 3221         UtilsUi::txsCritical(tr("Saving of session failed."));
 3222         return;
 3223     }
 3224     recentSessionList->addFilenameToList(fn);
 3225 }
 3226 /*!
 3227  * \brief restore session s
 3228  *
 3229  * closes all files and loads all files given in session s
 3230  * \param s session
 3231  * \param showProgress
 3232  * \param warnMissing give warning if files are missing
 3233  */
 3234 void Texstudio::restoreSession(const Session &s, bool showProgress, bool warnMissing)
 3235 {
 3236     fileCloseAll();
 3237 
 3238     cursorHistory->setInsertionEnabled(false);
 3239     QProgressDialog progress(this);
 3240     if (showProgress) {
 3241         progress.setMaximum(s.files().size());
 3242         progress.setCancelButton(nullptr);
 3243         progress.setMinimumDuration(3000);
 3244         progress.setLabel(new QLabel());
 3245     }
 3246     recheckLabels = false; // impede label rechecking on hidden docs
 3247 
 3248     bookmarks->setBookmarks(s.bookmarks()); // set before loading, so that bookmarks are automatically restored on load
 3249 
 3250     QStringList missingFiles;
 3251     for (int i = 0; i < s.files().size(); i++) {
 3252         FileInSession f = s.files().at(i);
 3253 
 3254         if (showProgress) {
 3255             progress.setValue(i);
 3256             progress.setLabelText(QFileInfo(f.fileName).fileName());
 3257         }
 3258         LatexEditorView *edView = load(f.fileName, f.fileName == s.masterFile(), false, false, true);
 3259         if (edView) {
 3260             int line = f.cursorLine;
 3261             int col = f.cursorCol;
 3262             if (line >= edView->document->lineCount()) {
 3263                 line = 0;
 3264                 col = 0;
 3265             } else {
 3266                 if (edView->document->line(line).length() < col) {
 3267                     col = 0;
 3268                 }
 3269             }
 3270             edView->editor->setCursorPosition(line, col);
 3271             edView->editor->scrollToFirstLine(f.firstLine);
 3272             edView->document->foldLines(f.foldedLines);
 3273             editors->moveToTabGroup(edView, f.editorGroup, -1);
 3274         } else {
 3275             missingFiles.append(f.fileName);
 3276         }
 3277     }
 3278     // update ref/labels in one go;
 3279     QList<LatexDocument *> completedDocs;
 3280     foreach (LatexDocument *doc, documents.getDocuments()) {
 3281         doc->recheckRefsLabels();
 3282         if (completedDocs.contains(doc))
 3283             continue;
 3284 
 3285         doc->updateLtxCommands(true);
 3286         completedDocs << doc->getListOfDocs();
 3287     }
 3288     recheckLabels = true;
 3289 
 3290     if (showProgress) {
 3291         progress.setValue(progress.maximum());
 3292     }
 3293     activateEditorForFile(s.currentFile());
 3294     cursorHistory->setInsertionEnabled(true);
 3295 
 3296     if (!s.PDFFile().isEmpty()) {
 3297         runInternalCommand("txs:///view-pdf-internal", QFileInfo(s.PDFFile()), s.PDFEmbedded() ? "--embedded" : "--windowed");
 3298     }
 3299     // update completer
 3300     if (currentEditorView())
 3301         updateCompleter(currentEditorView());
 3302 
 3303     if (warnMissing && !missingFiles.isEmpty()) {
 3304         UtilsUi::txsInformation(tr("The following files could not be loaded:") + "\n" + missingFiles.join("\n"));
 3305     }
 3306 }
 3307 
 3308 Session Texstudio::getCurrentSession()
 3309 {
 3310     Session s;
 3311 
 3312     foreach (LatexEditorView *edView, editors->editors()) {
 3313         FileInSession f;
 3314         f.fileName = edView->editor->fileName();
 3315         f.editorGroup = editors->tabGroupIndexFromEditor(edView);
 3316         f.cursorLine = edView->editor->cursor().lineNumber();
 3317         f.cursorCol = edView->editor->cursor().columnNumber();
 3318         f.firstLine = edView->editor->getFirstVisibleLine();
 3319         f.foldedLines = edView->document->foldedLines();
 3320         if (!f.fileName.isEmpty())
 3321             s.addFile(f);
 3322     }
 3323     s.setMasterFile(documents.masterDocument ? documents.masterDocument->getFileName() : "");
 3324     s.setCurrentFile(currentEditorView() ? currentEditor()->fileName() : "");
 3325 
 3326     s.setBookmarks(bookmarks->getBookmarks());
 3327 #ifndef NO_POPPLER_PREVIEW
 3328     if (!PDFDocument::documentList().isEmpty()) {
 3329         PDFDocument *doc = PDFDocument::documentList().at(0);
 3330         s.setPDFEmbedded(doc->embeddedMode);
 3331         s.setPDFFile(doc->fileName());
 3332     }
 3333 #endif
 3334 
 3335     return s;
 3336 }
 3337 
 3338 void Texstudio::MarkCurrentFileAsRecent()
 3339 {
 3340     configManager.addRecentFile(getCurrentFileName(), documents.masterDocument == currentEditorView()->document);
 3341 }
 3342 
 3343 //////////////////////////// EDIT ///////////////////////
 3344 void Texstudio::editUndo()
 3345 {
 3346     if (!currentEditorView()) return;
 3347 
 3348     QVariant zw = currentEditor()->property("undoRevision");
 3349     int undoRevision = zw.canConvert<int>() ? zw.toInt() : 0;
 3350 
 3351     if (currentEditor()->document()->canUndo()) {
 3352         currentEditor()->undo();
 3353         if (undoRevision > 0) {
 3354             undoRevision = -1;
 3355             currentEditor()->setProperty("undoRevision", undoRevision);
 3356         }
 3357     } else {
 3358         if (configManager.svnUndo && (undoRevision >= 0)) {
 3359             svnUndo();
 3360         }
 3361     }
 3362 }
 3363 
 3364 void Texstudio::editRedo()
 3365 {
 3366     if (!currentEditorView()) return;
 3367 
 3368     QVariant zw = currentEditor()->property("undoRevision");
 3369     int undoRevision = zw.canConvert<int>() ? zw.toInt() : 0;
 3370 
 3371     if (currentEditor()->document()->canRedo()) {
 3372         currentEditorView()->editor->redo();
 3373     } else {
 3374         if (configManager.svnUndo && (undoRevision > 0)) {
 3375             svnUndo(true);
 3376         }
 3377     }
 3378 }
 3379 
 3380 void Texstudio::editDebugUndoStack()
 3381 {
 3382     if (!currentEditor()) return;
 3383     QString history = currentEditor()->document()->debugUndoStack();
 3384     fileNew();
 3385     currentEditor()->document()->setText(history, false);
 3386 }
 3387 
 3388 void Texstudio::editCopy()
 3389 {
 3390     if ((!currentEditor() || !currentEditor()->hasFocus()) &&
 3391             outputView->childHasFocus() ) {
 3392         outputView->copy();
 3393         return;
 3394     }
 3395     if (!currentEditorView()) return;
 3396     currentEditorView()->editor->copy();
 3397 }
 3398 
 3399 void Texstudio::editPaste()
 3400 {
 3401     if (!currentEditorView()) return;
 3402 
 3403     const QMimeData *d = QApplication::clipboard()->mimeData();
 3404     if ((d->hasFormat("application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"")||d->hasFormat("application/x-qt-windows-mime;value=\"Star Embed Source (XML)\"")) && d->hasFormat("text/plain")) {
 3405         // workaround for LibreOffice (im "application/x-qt-image" has a higher priority for them than "text/plain")
 3406         currentEditorView()->paste();
 3407         return;
 3408     }
 3409 
 3410     foreach (const QString &format, d->formats()) {
 3411         // formats is a priority order. Use the first (== most suitable) format
 3412         if (format == "text/uri-list") {
 3413             QList<QUrl> uris = d->urls();
 3414 
 3415             bool onlyLocalImages = true;
 3416             for (int i = 0; i < uris.length(); i++) {
 3417                 QFileInfo fi = QFileInfo(uris.at(i).toLocalFile());
 3418                 if (!fi.exists() || !InsertGraphics::imageFormats().contains(fi.suffix())) {
 3419                     onlyLocalImages = false;
 3420                     break;
 3421                 }
 3422             }
 3423 
 3424             if (onlyLocalImages) {
 3425                 for (int i = 0; i < uris.length(); i++) {
 3426                     quickGraphics(uris.at(i).toLocalFile());
 3427                 }
 3428             } else {
 3429                 currentEditorView()->paste();
 3430             }
 3431             return;
 3432         } else if (format == "text/plain" || format == "text/html") {
 3433             currentEditorView()->paste();
 3434             return;
 3435         } else if ((format == "application/x-qt-image" || format.startsWith("image/")) && d->hasImage()) {
 3436             editPasteImage(qvariant_cast<QImage>(d->imageData()));
 3437             return;
 3438         }
 3439     }
 3440     currentEditorView()->paste();  // fallback
 3441 }
 3442 
 3443 void Texstudio::editPasteImage(QImage image)
 3444 {
 3445     if (!currentEditorView()) return;
 3446     static QString filenameSuggestion;  // keep for future calls
 3447     QString rootDir = currentEditorView()->document->getRootDocument()->getFileInfo().absolutePath();
 3448     if (filenameSuggestion.isEmpty()) {
 3449         filenameSuggestion = rootDir + "/screenshot001.png";
 3450     }
 3451     QStringList filters;
 3452     foreach (const QByteArray fmt, QImageWriter::supportedImageFormats()) {
 3453         filters << "*." + fmt;
 3454     }
 3455     QString filter = tr("Image Formats (%1)").arg(filters.join(" "));
 3456     filenameSuggestion = getNonextistentFilename(filenameSuggestion, rootDir);
 3457     QString filename = FileDialog::getSaveFileName(this, tr("Save Image"), filenameSuggestion, filter, &filter);
 3458     if (filename.isEmpty()) return;
 3459     filenameSuggestion = filename;
 3460 
 3461     if (!image.save(filename)) {
 3462         UtilsUi::txsCritical(tr("Could not save the image file."));
 3463         return;
 3464     }
 3465     quickGraphics(filename);
 3466 }
 3467 
 3468 void Texstudio::editPasteLatex()
 3469 {
 3470     if (!currentEditorView()) return;
 3471     // manipulate clipboard text
 3472     QClipboard *clipboard = QApplication::clipboard();
 3473     QString originalText = clipboard->text();
 3474     QString newText = textToLatex(originalText);
 3475     //clipboard->setText(newText);
 3476     // insert
 3477     //currentEditorView()->editor->paste();
 3478     QMimeData md;
 3479     md.setText(newText);
 3480     currentEditorView()->editor->insertFromMimeData(&md);
 3481 }
 3482 
 3483 void Texstudio::convertToLatex()
 3484 {
 3485     if (!currentEditorView()) return;
 3486     // get selection and change it
 3487     QString originalText = currentEditor()->cursor().selectedText();
 3488     QString newText = textToLatex(originalText);
 3489     // insert
 3490     currentEditor()->write(newText);
 3491 }
 3492 
 3493 void Texstudio::editDeleteLine()
 3494 {
 3495     if (!currentEditorView()) return;
 3496     currentEditorView()->deleteLines(true, true);
 3497 }
 3498 
 3499 void Texstudio::editDeleteToEndOfLine()
 3500 {
 3501     if (!currentEditorView()) return;
 3502     currentEditorView()->deleteLines(false, true);
 3503 }
 3504 
 3505 void Texstudio::editDeleteFromStartOfLine()
 3506 {
 3507     if (!currentEditorView()) return;
 3508     currentEditorView()->deleteLines(true, false);
 3509 }
 3510 
 3511 void Texstudio::editMoveLineUp()
 3512 {
 3513     if (!currentEditorView()) return;
 3514     currentEditorView()->moveLines(-1);
 3515 }
 3516 
 3517 void Texstudio::editMoveLineDown()
 3518 {
 3519     if (!currentEditorView()) return;
 3520     currentEditorView()->moveLines(1);
 3521 }
 3522 
 3523 void Texstudio::editDuplicateLine()
 3524 {
 3525     if (!currentEditor()) return;
 3526     QEditor *ed = currentEditor();
 3527     QList<QDocumentCursor> cursors = ed->cursors();
 3528     for (int i = 0; i < cursors.length(); i++){
 3529         cursors[i].setAutoUpdated(false);
 3530     }
 3531     QList<QPair<int, int> > blocks = currentEditorView()->getSelectedLineBlocks();
 3532     for (int i = blocks.size() - 1; i >= 0; i--) {
 3533         QDocumentCursor edit = ed->document()->cursor(blocks[i].first, 0, blocks[i].second);
 3534         QString text = edit.selectedText();
 3535         edit.selectionEnd().insertText("\n" + text);
 3536     }
 3537     if(cursors.length()>0)
 3538         ed->setCursor(cursors[0]);
 3539 }
 3540 
 3541 void Texstudio::editAlignMirrors()
 3542 {
 3543     if (!currentEditor()) return;
 3544     currentEditorView()->alignMirrors();
 3545 }
 3546 
 3547 void Texstudio::editEraseWordCmdEnv()
 3548 {
 3549     if (!currentEditorView()) return;
 3550     QDocumentCursor cursor = currentEditorView()->editor->cursor();
 3551     if (cursor.isNull()) return;
 3552     QString line = cursor.line().text();
 3553     QDocumentLineHandle *dlh = cursor.line().handle();
 3554     QString command, value;
 3555 
 3556     // Hack to fix problem problem reported in bug report 3443336 (see also detailed description there):
 3557     // findContext does not work at beginning of commands. Actually it would need
 3558     // pre-context and post-context otherwise, what context is either ambigous, like in
 3559     // \cmd|\cmd or it cannot work simultaneously at beginning and and (you cannot assign
 3560     // the context for |\cmd and \cmd| even if \cmd is surrounded by spaces. The latter
 3561     // both assignments make sense for editEraseWordCmdEnv().
 3562     //
 3563     // pre-context and post-context may be added when revising the latex parser
 3564     //
 3565     // Prelimiary solution part I:
 3566     // Predictable behaviour on selections: do nothing except in easy cases
 3567     if (cursor.hasSelection()) {
 3568         QRegExp partOfWordOrCmd("\\\\?\\w*");
 3569         if (!partOfWordOrCmd.exactMatch(cursor.selectedText()))
 3570             return;
 3571     }
 3572     // Prelimiary solution part II:
 3573     // If the case |\cmd is encountered, we shift the
 3574     // cursor by one to \|cmd so the regular erase approach works.
 3575     int col = cursor.columnNumber();
 3576     if ((col < line.count())                        // not at end of line
 3577             && (line.at(col) == '\\')                   // likely a command/env - check further
 3578             && (col == 0 || line.at(col - 1).isSpace()))    // cmd is at start or previous char is space: non-ambiguous situation like word|\cmd
 3579         // don't need to finally check for command |\c should be handled like \|c for any c (even space and empty)
 3580     {
 3581         cursor.movePosition(1);
 3582     }
 3583 
 3584     TokenList tl = dlh->getCookieLocked(QDocumentLine::LEXER_COOKIE).value<TokenList>();
 3585     int tkPos = Parsing::getTokenAtCol(tl, cursor.columnNumber());
 3586     Token tk;
 3587     if (tkPos > -1)
 3588         tk = tl.at(tkPos);
 3589 
 3590     switch (tk.type) {
 3591 
 3592     case Token::command:
 3593         command = tk.getText();
 3594         if (command == "\\begin" || command == "\\end") {
 3595             value = Parsing::getArg(tl.mid(tkPos + 1), dlh, 0, ArgumentList::Mandatory);
 3596             //remove environment (surrounding)
 3597             currentEditorView()->editor->document()->beginMacro();
 3598             cursor.select(QDocumentCursor::WordOrCommandUnderCursor);
 3599             cursor.removeSelectedText();
 3600             // remove curly brakets as well
 3601             if (cursor.nextChar() == QChar('{')) {
 3602                 cursor.deleteChar();
 3603                 line = cursor.line().text();
 3604                 int col = cursor.columnNumber();
 3605                 int i = findClosingBracket(line, col);
 3606                 if (i > -1) {
 3607                     cursor.movePosition(i - col + 1, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 3608                     cursor.removeSelectedText();
 3609                     QDocument *doc = currentEditorView()->editor->document();
 3610                     QString searchWord = "\\end{" + value + "}";
 3611                     QString inhibitor = "\\begin{" + value + "}";
 3612                     bool backward = (command == "\\end");
 3613                     int step = 1;
 3614                     if (backward) {
 3615                         qSwap(searchWord, inhibitor);
 3616                         step = -1;
 3617                     }
 3618                     int startLine = cursor.lineNumber();
 3619                     int startCol = cursor.columnNumber();
 3620                     int endLine = doc->findLineContaining(searchWord, startLine, Qt::CaseSensitive, backward);
 3621                     int inhibitLine = doc->findLineContaining(inhibitor, startLine, Qt::CaseSensitive, backward); // not perfect (same line end/start ...)
 3622                     while (inhibitLine > 0 && endLine > 0 && inhibitLine * step < endLine * step) {
 3623                         endLine = doc->findLineContaining(searchWord, endLine + step, Qt::CaseSensitive, backward); // not perfect (same line end/start ...)
 3624                         inhibitLine = doc->findLineContaining(inhibitor, inhibitLine + step, Qt::CaseSensitive, backward);
 3625                     }
 3626                     if (endLine > -1) {
 3627                         line = doc->line(endLine).text();
 3628                         int start = line.indexOf(searchWord);
 3629                         cursor.moveTo(endLine, start);
 3630                         cursor.movePosition(searchWord.length(), QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 3631                         cursor.removeSelectedText();
 3632                         cursor.moveTo(startLine, startCol); // move cursor back to text edit pos
 3633                     }
 3634                 }
 3635             }
 3636 
 3637             currentEditorView()->editor->document()->endMacro();
 3638         } else {
 3639             currentEditorView()->editor->document()->beginMacro();
 3640             cursor.select(QDocumentCursor::WordOrCommandUnderCursor);
 3641             cursor.removeSelectedText();
 3642             // remove curly brakets as well
 3643             if (cursor.nextChar() == QChar('{')) {
 3644                 cursor.deleteChar();
 3645                 line = cursor.line().text();
 3646                 int col = cursor.columnNumber();
 3647                 int i = findClosingBracket(line, col);
 3648                 if (i > -1) {
 3649                     cursor.moveTo(cursor.lineNumber(), i);
 3650                     cursor.deleteChar();
 3651                     cursor.moveTo(cursor.lineNumber(), col);
 3652                 }
 3653             }
 3654             currentEditorView()->editor->document()->endMacro();
 3655         }
 3656         break;
 3657 
 3658     default:
 3659         cursor.select(QDocumentCursor::WordUnderCursor);
 3660         cursor.removeSelectedText();
 3661         break;
 3662     }
 3663     currentEditorView()->editor->setCursor(cursor);
 3664 }
 3665 
 3666 void Texstudio::editGotoDefinition(QDocumentCursor c)
 3667 {
 3668     if (!currentEditorView()) return;
 3669     if (!c.isValid()) c = currentEditor()->cursor();
 3670     saveCurrentCursorToHistory();
 3671 
 3672     LatexDocument *doc = qobject_cast<LatexDocument *>(c.document());
 3673     Token tk = Parsing::getTokenAtCol(c.line().handle(), c.columnNumber());
 3674 
 3675     switch (tk.type) {
 3676     case Token::labelRef:
 3677     case Token::labelRefList: {
 3678         QMultiHash<QDocumentLineHandle *, int> defs = doc->getLabels(tk.getText());
 3679         if (defs.isEmpty()) return;
 3680         QDocumentLineHandle *target = defs.keys().first();
 3681         LatexEditorView *edView = getEditorViewFromHandle(target);
 3682         if (!edView) return;
 3683         if (edView != currentEditorView()) {
 3684             editors->setCurrentEditor(edView);
 3685         }
 3686         edView->gotoLineHandleAndSearchLabel(target, tk.getText());
 3687         break;
 3688     }
 3689     case Token::bibItem: {
 3690         QString bibID = trimLeft(tk.getText());
 3691         // try local \bibitems
 3692         QMultiHash<QDocumentLineHandle *, int> defs = doc->getBibItems(bibID);
 3693         if (!defs.isEmpty()) {
 3694             QDocumentLineHandle *target = defs.keys().first();
 3695             bool found = currentEditorView()->gotoLineHandleAndSearchBibItem(target, bibID);
 3696             if (found) break;
 3697         }
 3698         // try bib files
 3699         QString bibFile = currentEditorView()->document->findFileFromBibId(bibID);
 3700         LatexEditorView *edView = getEditorViewFromFileName(bibFile);
 3701         if (!edView) {
 3702             if (!load(bibFile)) return;
 3703             edView = currentEditorView();
 3704         }
 3705         int line = edView->document->findLineRegExp("@\\w+{\\s*" + bibID, 0, Qt::CaseSensitive, true, true);
 3706         if (line < 0) {
 3707             line = edView->document->findLineContaining(bibID); // fallback in case the above regexp does not reflect the most general case
 3708             if (line < 0) return;
 3709         }
 3710         int col = edView->document->line(line).text().indexOf(bibID);
 3711         if (col < 0) col = 0;
 3712         gotoLine(line, col, edView);
 3713         break;
 3714     }
 3715     case Token::commandUnknown:
 3716     case Token::command:
 3717     case Token::beginEnv:
 3718     case Token::env: {
 3719         QDocumentLineHandle *target = doc->findCommandDefinition(tk.getText());
 3720         if (target) {
 3721             // command is user-defined, jump to definition
 3722             LatexEditorView *edView = getEditorViewFromHandle(target);
 3723             if (edView) {
 3724                 if (edView != currentEditorView()) {
 3725                     editors->setCurrentEditor(edView);
 3726                 }
 3727                 edView->gotoLineHandleAndSearchString(target, tk.getText());
 3728                 break;
 3729             }
 3730         }
 3731         // command might be defined by a package, jump to \usepackage (if possible)
 3732         QString command = tk.getText();
 3733         if (tk.type == Token::beginEnv || tk.type == Token::env) {
 3734             command = "\\begin{" + command + "}";
 3735         }
 3736         QString package = doc->parent->findPackageByCommand(command);
 3737         package.chop(4);
 3738         // skip builtin packages (we cannot goto the \usepackage in this case)
 3739         if (package == "tex" || package == "latex-document")
 3740             return;
 3741         target = doc->findUsePackage(package);
 3742         if (!target) return;
 3743         LatexEditorView *edView = getEditorViewFromHandle(target);
 3744         if (!edView) return;
 3745         if (edView != currentEditorView()) {
 3746             editors->setCurrentEditor(edView);
 3747         }
 3748         edView->gotoLineHandleAndSearchString(target, tk.getText());
 3749         break;
 3750     }
 3751     default:;
 3752     }
 3753 }
 3754