"Fossies" - the Fresh Open Source Software Archive

Member "texstudio-3.1.1/src/texstudio.cpp" (21 Feb 2021, 427661 Bytes) of package /linux/misc/texstudio-3.1.1.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: 3.1.0_vs_3.1.1.

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