"Fossies" - the Fresh Open Source Software Archive

Member "texstudio-3.1.1/src/qcodeedit/lib/qeditor.cpp" (21 Feb 2021, 180346 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 "qeditor.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 **
    3 ** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
    4 **
    5 ** This file is part of the Edyuk project <http://edyuk.org>
    6 **
    7 ** This file may be used under the terms of the GNU General Public License
    8 ** version 3 as published by the Free Software Foundation and appearing in the
    9 ** file GPL.txt included in the packaging of this file.
   10 **
   11 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
   12 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   13 **
   14 ****************************************************************************/
   15 
   16 #include "qeditor.h"
   17 
   18 /*!
   19     \file qeditor.cpp
   20     \brief Implementation of the QEditor class
   21 */
   22 
   23 #include "qeditorinputbindinginterface.h"
   24 
   25 #include "qdocument.h"
   26 #include "qdocument_p.h"
   27 #include "qdocumentline.h"
   28 #include "qdocumentcursor.h"
   29 #include "qformatscheme.h"
   30 
   31 #include "qlanguagedefinition.h"
   32 #include "qcodecompletionengine.h"
   33 
   34 #include "qcodeedit.h"
   35 #include "qpanellayout.h"
   36 #include "qgotolinedialog.h"
   37 #include "qlinemarksinfocenter.h"
   38 
   39 #include "qreliablefilewatch.h"
   40 #include "smallUsefulFunctions.h"
   41 #include "latexparser/latexparser.h"
   42 #include <QDrag>
   43 
   44 #include "libqmarkedscrollbar/src/markedscrollbar.h"
   45 
   46 #include <QPropertyAnimation>
   47 
   48 #include <QSaveFile>
   49 
   50 #ifdef Q_OS_MAC
   51 #include <QSysInfo>
   52 #endif
   53 
   54 #include <QPrinter>
   55 #include <QPrintDialog>
   56 //#define Q_GL_EDITOR
   57 
   58 #ifdef _QMDI_
   59 #include "qmdiserver.h"
   60 #endif
   61 
   62 #ifdef _EDYUK_
   63 #include "edyukapplication.h"
   64 #include "qshortcutmanager.h"
   65 
   66 #define Q_SHORTCUT(a, s, c) EDYUK_SHORTCUT(a, tr(c), tr(s))
   67 #else
   68 #define Q_SHORTCUT(a, s, c) a->setShortcut( QKeySequence( tr(s, c) ) )
   69 #endif
   70 
   71 #ifdef Q_GL_EDITOR
   72 #include <QGLWidget>
   73 #endif
   74 
   75 #define QCE_ACTION(name, action) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_ action; }
   76 #define QCE_TR_ACTION(name, label) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_->setText(label); }
   77 #define QCE_ENABLE_ACTION(name, yes) { QAction *_a_ = m_actions.value(name); if ( _a_ ) _a_->setEnabled(yes); }
   78 
   79 /*!
   80     \ingroup editor
   81     @{
   82 
   83     \class QEditor
   84     \brief A text editing widget
   85 
   86     QEditor is the central widget in QCE. It allows user to view and edit a
   87     document.
   88 
   89     QEditor has an API similar to that of QTextEdit and it behaves in a very
   90     similar way.
   91 
   92     Notable differences are :
   93     <ul>
   94     <li>QEditor can be given an InputBinding which can change the way it
   95     handle user inputs which enables such things as implementing emacs-like
   96     or Vi-like editing (almost) effortlessly.</li>
   97     <li>QEditor has actions instead of hard coded shortcuts and expose them
   98     so that, among other things, they can be easily added to menus/toolbars
   99     and their shortcuts can be changed</li>
  100     <li>QEditor brings the notion of cursor mirrors. Column selection and
  101     column editing are just special use case of cursor mirrors.</li>
  102     <li>QEditor brings the notion of placeholders, snippets-editing is just
  103     as special use case of placeholders.</li>
  104     <li>QEditor allows easy encodings management</li>
  105     </ul>
  106 
  107     QEditor can gain features when it is managed by a QCodeEdit class which
  108     is responsible for panels management.
  109 */
  110 
  111 
  112 /*!
  113     \enum QEditor::CodecUpdatePolicy
  114     \brief Specify the actions to take when changing the default codec
  115 
  116 */
  117 
  118 
  119 /*!
  120     \enum QEditor::EditFlag
  121     \brief Flag holding information about the state of an editor
  122 
  123     Some of these are public and can be modified freely and some
  124     others are only meant to be used internally though they can
  125     still be read.
  126 
  127 */
  128 
  129 /*!
  130     \struct QEditor::PlaceHolder
  131     \brief A small structure holding placeholder data
  132 
  133     Placeholders are basically lists of cursors. When several palceholders coexist, it is
  134     possible to navigate among them using the key assigned to that function by the current
  135     input binding (CTRL+arrows by default).
  136 
  137     Each placeholder consist of a primary cursor and a list of mirrors (modeling the internals
  138     of QEditor and allowing extended snippet replacements easily).
  139     
  140     Additionaly a placeholder can have an Affector which allows the text of the mirrors to be
  141     modified in any imaginable way
  142 */
  143 
  144 /*!
  145     \class QEditor::PlaceHolder::Affector
  146     \brief A small class allowing "placeholder scripting"
  147     
  148     The purpose of this class is to affect/process/reformat (whichever word you understand/like
  149     most) the content of placeholder mirrors.
  150     
  151     The main placeholder instance (primary cursor) will always contain the text typed by the user,
  152     only mirrors can be affected.
  153     
  154     To allow a large panel of processing to be done the affector is passed the following data :
  155     <ul>
  156         <li>A stringlist containing placeholder contents (primary cursor of all placeholders present in the editor)
  157         <li>The index of the current placeholder among these
  158         <li>The key event leading to a modification of the current placeholder
  159         <li>The index of the current mirror
  160         <li>A reference to the text of that placeholder mirror. This text has already been modified according
  161         to the key event. The original text can be retrieved from the first argument (stringlist). This text
  162         can be modified in any way you see fit or left as it is.
  163     </ul>
  164 */
  165 
  166 ////////////////////////////////////////////////////////////////////////
  167 //  Bindings handling
  168 ////////////////////////////////////////////////////////////////////////
  169 
  170 QList<QEditor*> QEditor::m_editors;
  171 QEditorInputBindingInterface* QEditor::m_defaultBinding = nullptr;
  172 QHash<QString, QEditorInputBindingInterface*> QEditor::m_registeredBindings;
  173 bool QEditor::m_defaultKeysSet = false;
  174 QHash<QString, int> QEditor::m_registeredKeys;
  175 QSet<int> QEditor::m_registeredOperations;
  176 QHash<QString, int> QEditor::m_registeredDefaultKeys;
  177 
  178 
  179 /*!
  180     \return A list of available input bindings
  181 */
  182 QStringList QEditor::registeredInputBindingIds()
  183 {
  184     return m_registeredBindings.keys();
  185 }
  186 
  187 /*!
  188     \return the name of the default input binding
  189 
  190     \note The "Default" name (or its translation, obtained via QEditor::tr())
  191     is used to indicate that no default input binding has been set.
  192 */
  193 QString QEditor::defaultInputBindingId()
  194 {
  195     return m_defaultBinding ? m_defaultBinding->name() : tr("Default");
  196 }
  197 
  198 /*!
  199     \brief Add an input binding to make it available for all editors
  200 */
  201 void QEditor::registerInputBinding(QEditorInputBindingInterface *b)
  202 {
  203     m_registeredBindings[b->id()] = b;
  204 
  205     foreach ( QEditor *e, m_editors )
  206         e->updateBindingsMenu();
  207 
  208 }
  209 
  210 /*!
  211     \brief Remove an input binding from the pool of publicly available ones
  212 */
  213 void QEditor::unregisterInputBinding(QEditorInputBindingInterface *b)
  214 {
  215     m_registeredBindings.remove(b->id());
  216 
  217     foreach ( QEditor *e, m_editors )
  218         e->updateBindingsMenu();
  219 
  220 }
  221 
  222 /*!
  223     \brief Set the default input binding
  224 
  225     \note This does not change the current input binding of existing editors
  226 */
  227 void QEditor::setDefaultInputBinding(QEditorInputBindingInterface *b)
  228 {
  229     m_defaultBinding = b;
  230 }
  231 
  232 /*!
  233     \brief Set the default input binding
  234 
  235     \note If no binding of the given name is available the default (null)
  236     binding will be set back as default binding.
  237 
  238     \note This does not change the current input binding of existing editors
  239 */
  240 void QEditor::setDefaultInputBinding(const QString& b)
  241 {
  242     m_defaultBinding = m_registeredBindings.value(b);
  243 }
  244 ////////////////////////////////////////////////////////////////////////
  245 
  246 /*!
  247     \return A pointer to the global "reliable" file monitor used by QEditor to avoid file conflicts
  248 
  249     The point of using a custom file watcher is to work around a bug (limitation) of QFileSystemWatcher
  250     which sometimes emit multiple signals for a single file save. It also enables to use a single
  251     object shared by all QEditor instances and reduce memory footprint.
  252 */
  253 QReliableFileWatch* QEditor::watcher()
  254 {
  255     static QPointer<QReliableFileWatch> _qce_shared;
  256 
  257     if ( !_qce_shared )
  258         _qce_shared = new QReliableFileWatch;
  259 
  260     return _qce_shared;
  261 }
  262 
  263 ////////////////////////////////////////////////////////////////////////
  264 
  265 int QEditor::m_defaultFlags = QEditor::MouseWheelZoom | QEditor::AutoIndent | QEditor::AdjustIndent | QEditor::AutoCloseChars | QEditor::ShowPlaceholders;
  266 
  267 /*!
  268     \return The default flags set to every QEditor upon construction
  269     \note the default flags are a configuration-oriented feature which only expose "user" flags
  270 */
  271 int QEditor::defaultFlags()
  272 {
  273     return m_defaultFlags;
  274 }
  275 
  276 /*!
  277     \brief Set the default editor flags
  278 
  279     Setting editor flags result in them being applied to ALL existing editors
  280     and editors to be created later on.
  281 
  282     These can of course be modified on a per-editor basis later on.
  283 */
  284 void QEditor::setDefaultFlags(int flags)
  285 {
  286     m_defaultFlags = flags & Accessible;
  287 
  288     foreach ( QEditor *e, m_editors )
  289     {
  290         bool ontoWrap = (m_defaultFlags & LineWrap) && !(e->m_state & LineWrap);
  291         bool outOfWrap = !(m_defaultFlags & LineWrap) && (e->m_state & LineWrap);
  292 
  293         e->m_state &= Internal;
  294         e->m_state |= m_defaultFlags;
  295 
  296         if ( ontoWrap )
  297         {
  298             e->document()->setWidthConstraint(e->wrapWidth());
  299         } else if ( outOfWrap ) {
  300             e->document()->clearWidthConstraint();
  301         }
  302 
  303         QAction *a = e->m_actions.value("wrap");
  304 
  305         if ( a && (a->isChecked() != (bool)(e->m_state & LineWrap)) )
  306             a->setChecked(e->m_state & LineWrap);
  307 
  308     }
  309 }
  310 
  311 /*!
  312     \brief ctor
  313 
  314     \note Creates builtin menus/actions
  315 */
  316 QEditor::QEditor(QWidget *p)
  317  : QAbstractScrollArea(p),
  318     pMenu(nullptr), m_lineEndingsMenu(nullptr), m_lineEndingsActions(nullptr),
  319     m_bindingsMenu(nullptr), aDefaultBinding(nullptr), m_bindingsActions(nullptr),
  320     m_doc(nullptr), m_definition(nullptr),
  321     m_doubleClickSelectionType(QDocumentCursor::WordOrCommandUnderCursor), m_tripleClickSelectionType(QDocumentCursor::LineUnderCursor),
  322     m_curPlaceHolder(-1), m_placeHolderSynchronizing(false), m_state(defaultFlags()),
  323     mDisplayModifyTime(true), m_blockKey(false), m_disableAccentHack(false), m_LineWidth(0), m_wrapAfterNumChars(0), m_scrollAnimation(nullptr)
  324 {
  325     m_editors << this;
  326 
  327     m_saveState = Undefined;
  328     
  329     init();
  330 }
  331 
  332 /*!
  333     \brief ctor
  334     \param actions Whether builtin actions and menus should be created
  335 */
  336 QEditor::QEditor(bool actions, QWidget *p,QDocument *doc)
  337  : QAbstractScrollArea(p),
  338     pMenu(nullptr), m_lineEndingsMenu(nullptr), m_lineEndingsActions(nullptr),
  339     m_bindingsMenu(nullptr), aDefaultBinding(nullptr), m_bindingsActions(nullptr),
  340     m_doc(nullptr), m_definition(nullptr),
  341     m_doubleClickSelectionType(QDocumentCursor::WordOrCommandUnderCursor), m_tripleClickSelectionType(QDocumentCursor::ParenthesesOuter),
  342     m_curPlaceHolder(-1), m_placeHolderSynchronizing(false), m_state(defaultFlags()),
  343     mDisplayModifyTime(true), m_blockKey(false), m_disableAccentHack(false), m_LineWidth(0), m_wrapAfterNumChars(0), m_scrollAnimation(nullptr)
  344 {
  345     m_editors << this;
  346 
  347     m_saveState = Undefined;
  348 
  349     init(actions,doc);
  350 }
  351 
  352 
  353 /*!
  354     \brief ctor
  355     \param s file to load
  356 
  357     \note Creates builtin menus/actions
  358 */
  359 QEditor::QEditor(const QString& s, QWidget *p)
  360  : QAbstractScrollArea(p),
  361     pMenu(nullptr), m_lineEndingsMenu(nullptr), m_lineEndingsActions(nullptr),
  362     m_bindingsMenu(nullptr), aDefaultBinding(nullptr), m_bindingsActions(nullptr),
  363     m_doc(nullptr), m_definition(nullptr), m_curPlaceHolder(-1), m_placeHolderSynchronizing(false), m_state(defaultFlags()),
  364         mDisplayModifyTime(true),m_blockKey(false),m_disableAccentHack(false),m_LineWidth(0),m_scrollAnimation(nullptr)
  365 {
  366     m_editors << this;
  367 
  368     m_saveState = Undefined;
  369 
  370     init();
  371 
  372     setText(s, false);
  373 }
  374 
  375 /*!
  376     \brief ctor
  377     \param s file to load
  378     \param actions Whether builtin actions and menus should be created
  379     \note Creates builtin menus/action
  380 */
  381 QEditor::QEditor(const QString& s, bool actions, QWidget *p)
  382  : QAbstractScrollArea(p),
  383     pMenu(nullptr), m_lineEndingsMenu(nullptr), m_lineEndingsActions(nullptr),
  384     m_bindingsMenu(nullptr), aDefaultBinding(nullptr), m_bindingsActions(nullptr),
  385     m_doc(nullptr), m_definition(nullptr), m_curPlaceHolder(-1), m_placeHolderSynchronizing(false), m_state(defaultFlags()),
  386     mDisplayModifyTime(true), m_useQSaveFile(true), m_blockKey(false), m_disableAccentHack(false), m_LineWidth(0), m_scrollAnimation(nullptr)
  387 {
  388     m_editors << this;
  389 
  390     m_saveState = Undefined;
  391 
  392     init(actions);
  393     
  394     setText(s, false);
  395 }
  396 
  397 /*!
  398     \brief dtor
  399 */
  400 QEditor::~QEditor()
  401 {
  402     m_editors.removeAll(this);
  403 
  404     if ( m_completionEngine )
  405         delete m_completionEngine;
  406 
  407     /* view class should not delete corresponding data
  408     if ( m_doc )
  409         delete m_doc;
  410     */
  411 
  412     if ( m_editors.isEmpty() )
  413     {
  414         delete watcher();
  415     } else {
  416         watcher()->removeWatch(this);
  417     }
  418 }
  419 
  420 /*!
  421     \internal
  422 */
  423 void QEditor::init(bool actions,QDocument *doc)
  424 {
  425     #ifdef Q_GL_EDITOR
  426     setViewport(new QGLWidget);
  427     #endif
  428 
  429     viewport()->setCursor(Qt::IBeamCursor);
  430     viewport()->setBackgroundRole(QPalette::Base);
  431     //viewport()->setAttribute(Qt::WA_OpaquePaintEvent, true);
  432     viewport()->setAttribute(Qt::WA_KeyCompression, true);
  433     viewport()->setAttribute(Qt::WA_InputMethodEnabled, true);
  434     viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
  435 
  436 
  437     MarkedScrollBar *scrlBar=new MarkedScrollBar();
  438     scrlBar->enableClipping(false);
  439     scrlBar->setDocument(doc);
  440     setVerticalScrollBar(scrlBar);
  441     //addMark(5,Qt::red);
  442 
  443     verticalScrollBar()->setSingleStep(1);
  444     horizontalScrollBar()->setSingleStep(20);
  445 
  446     setAcceptDrops(true);
  447     //setDragEnabled(true);
  448     setFrameStyle(QFrame::NoFrame);
  449     setFrameShadow(QFrame::Plain);
  450     setFocusPolicy(Qt::WheelFocus);
  451     setAttribute(Qt::WA_KeyCompression, true);
  452     setAttribute(Qt::WA_InputMethodEnabled, true);
  453 
  454     connect(this                            ,
  455             SIGNAL( markChanged(QString, QDocumentLineHandle*, int, bool) ),
  456             QLineMarksInfoCenter::instance(),
  457             SLOT  ( markChanged(QString, QDocumentLineHandle*, int, bool) ) );
  458 
  459     if(doc){
  460         // externally created document
  461         m_doc=doc;
  462     }else{
  463         m_doc = new QDocument(this);
  464     }
  465 
  466     connect(m_doc   , SIGNAL( formatsChange (int, int) ),
  467             this    , SLOT  ( repaintContent(int, int) ) );
  468 
  469     connect(m_doc   , SIGNAL( contentsChange(int, int) ),
  470             this    , SLOT  ( updateContent (int, int) ) );
  471 
  472     connect(m_doc       , SIGNAL( formatsChanged() ),
  473             viewport()  , SLOT  ( update() ) );
  474 
  475     connect(m_doc   , SIGNAL( widthChanged(int) ),
  476             this    , SLOT  ( documentWidthChanged(int) ) );
  477 
  478     connect(m_doc   , SIGNAL( heightChanged(int) ),
  479             this    , SLOT  ( documentHeightChanged(int) ) );
  480 
  481     connect(m_doc   , SIGNAL( cleanChanged(bool) ),
  482             this    , SLOT  ( setContentClean(bool) ) );
  483 
  484     connect(m_doc   , SIGNAL( undoAvailable(bool) ),
  485             this    , SIGNAL( undoAvailable(bool) ) );
  486 
  487     connect(m_doc   , SIGNAL( redoAvailable(bool) ),
  488             this    , SIGNAL( redoAvailable(bool) ) );
  489 
  490     connect(m_doc   , SIGNAL( markChanged(QDocumentLineHandle*, int, bool) ),
  491             this    , SLOT  ( markChanged(QDocumentLineHandle*, int, bool) ) );
  492 
  493     connect(m_doc   , SIGNAL( lineEndingChanged(int) ),
  494             this    , SLOT  ( lineEndingChanged(int) ) );
  495 
  496     connect(m_doc, SIGNAL(slowOperationStarted()), SIGNAL(slowOperationStarted()));
  497     connect(m_doc, SIGNAL(slowOperationEnded()), SIGNAL(slowOperationEnded()));
  498 
  499     m_cursorLinesFromViewTop=0;
  500     m_lastColumn=-2;
  501     m_lastLine=-2;
  502     m_hoverCount=-2;
  503     preEditSet=false;
  504     m_preEditFormat=0;
  505 
  506     if ( m_defaultBinding )
  507     {
  508         m_bindings << m_defaultBinding;
  509     }
  510 
  511     if ( actions )
  512     {
  513         pMenu = new QMenu;
  514 
  515         QAction *a, *sep;
  516 
  517         a = new QAction(QIcon(":/undo.png"), tr("&Undo"), this);
  518         a->setObjectName("undo");
  519         Q_SHORTCUT(a, "Ctrl+Z", "Edit");
  520         a->setEnabled(false);
  521         connect(this , SIGNAL( undoAvailable(bool) ),
  522                 a   , SLOT  ( setEnabled(bool) ) );
  523         connect(a   , SIGNAL( triggered() ),
  524                 this , SLOT  ( undo() ) );
  525 
  526         addAction(a, "&Edit", "Edit");
  527 
  528         a = new QAction(QIcon(":/redo.png"), tr("&Redo"), this);
  529         a->setObjectName("redo");
  530         Q_SHORTCUT(a, "Ctrl+Y", "Edit");
  531         a->setEnabled(false);
  532         connect(this , SIGNAL( redoAvailable(bool) ),
  533                 a   , SLOT  ( setEnabled(bool) ) );
  534         connect(a   , SIGNAL( triggered() ),
  535                 this , SLOT  ( redo() ) );
  536 
  537         addAction(a, "&Edit", "Edit");
  538 
  539         sep = new QAction(this);
  540         sep->setSeparator(true);
  541         addAction(sep, "&Edit", "Edit");
  542 
  543         a = new QAction(QIcon(":/cut.png"), tr("Cu&t"), this);
  544         a->setObjectName("cut");
  545         Q_SHORTCUT(a, "Ctrl+X", "Edit");
  546         a->setEnabled(false);
  547         connect(this, SIGNAL( copyAvailable(bool) ),
  548                 a   , SLOT  ( setEnabled(bool) ) );
  549         connect(a   , SIGNAL( triggered() ),
  550                 this, SLOT  ( cut() ) );
  551 
  552         addAction(a, "&Edit", "Edit");
  553 
  554         a = new QAction(QIcon(":/copy.png"), tr("&Copy"), this);
  555         a->setObjectName("copy");
  556         Q_SHORTCUT(a, "Ctrl+C", "Edit");
  557         a->setEnabled(false);
  558         connect(this , SIGNAL( copyAvailable(bool) ),
  559                 a   , SLOT  ( setEnabled(bool) ) );
  560         connect(a   , SIGNAL( triggered() ),
  561                 this , SLOT  ( copy() ) );
  562 
  563         addAction(a, "&Edit", "Edit");
  564 
  565         a = new QAction(QIcon(":/paste.png"), tr("&Paste"), this);
  566         a->setObjectName("paste");
  567         //aPaste->setEnabled(QApplication::clipboard()->text().count());
  568         Q_SHORTCUT(a, "Ctrl+V", "Edit");
  569         connect(QApplication::clipboard()   , SIGNAL( dataChanged() ),
  570                 this                        , SLOT  ( checkClipboard() ) );
  571 
  572         connect(a   , SIGNAL( triggered() ),
  573                 this, SLOT  ( paste() ) );
  574 
  575         addAction(a, "&Edit", "Edit");
  576 
  577         sep = new QAction(this);
  578         sep->setSeparator(true);
  579         addAction(sep, "&Edit", "Edit");
  580 
  581         a = new QAction(QIcon(":/indent.png"), tr("&Indent"), this);
  582         a->setObjectName("indent");
  583         Q_SHORTCUT(a, "Ctrl+I", "Edit");
  584         connect(a   , SIGNAL( triggered() ),
  585                 this, SLOT  ( indentSelection() ) );
  586 
  587         addAction(a, "&Edit", "Edit");
  588 
  589         a = new QAction(QIcon(":/unindent.png"), tr("&Unindent"), this);
  590         a->setObjectName("unindent");
  591         Q_SHORTCUT(a, "Ctrl+Shift+I", "Edit");
  592         connect(a   , SIGNAL( triggered() ),
  593                 this, SLOT  ( unindentSelection() ) );
  594 
  595         addAction(a, "&Edit", "Edit");
  596 
  597         sep = new QAction(this);
  598         sep->setSeparator(true);
  599         addAction(sep, "&Edit", "");
  600 
  601         a = new QAction(tr("Toggle &Comment"), this);
  602         a->setObjectName("togglecomment");
  603         connect(a   , SIGNAL( triggered() ),
  604                 this, SLOT  ( toggleCommentSelection() ) );
  605 
  606         a = new QAction(QIcon(":/comment.png"), tr("Co&mment"), this);
  607         a->setObjectName("comment");
  608         Q_SHORTCUT(a, "Ctrl+D", "Edit");
  609         connect(a   , SIGNAL( triggered() ),
  610                 this, SLOT  ( commentSelection() ) );
  611 
  612         addAction(a, "&Edit", "Edit");
  613 
  614         a = new QAction(QIcon(":/uncomment.png"), tr("Unc&omment"), this);
  615         a->setObjectName("uncomment");
  616         Q_SHORTCUT(a, "Ctrl+Shift+D", "Edit");
  617         connect(a   , SIGNAL( triggered() ),
  618                 this, SLOT  ( uncommentSelection() ) );
  619 
  620         addAction(a, "&Edit", "Edit");
  621 
  622         sep = new QAction(this);
  623         sep->setSeparator(true);
  624         addAction(sep, "&Edit", "");
  625 
  626         a = new QAction(tr("&Select all"), this);
  627         a->setObjectName("selectAll");
  628         Q_SHORTCUT(a, "Ctrl+A", "Edit");
  629         connect(a   , SIGNAL( triggered() ),
  630                 this, SLOT  ( selectAll() ) );
  631 
  632         addAction(a, "&Edit", "Edit");
  633 
  634         sep = new QAction(this);
  635         sep->setSeparator(true);
  636         addAction(sep, QString());
  637 
  638         a = new QAction(QIcon(":/find.png"), tr("&Find"), this);
  639         a->setObjectName("find");
  640         Q_SHORTCUT(a, "Ctrl+F", "Search");
  641         connect(a   , SIGNAL( triggered() ),
  642                 this, SLOT  ( find() ) );
  643 
  644         addAction(a, "&Search", "Search");
  645 
  646         a = new QAction(QIcon(":/next.png"), tr("Fin&d next"), pMenu);
  647         a->setObjectName("findNext");
  648         Q_SHORTCUT(a, "F3", "Search");
  649         connect(a   , SIGNAL( triggered() ),
  650                 this, SLOT  ( findNext() ) );
  651 
  652         addAction(a, "&Search", "Search");
  653 
  654         a = new QAction(QIcon(":/replace.png"), tr("&Replace"), this);
  655         a->setObjectName("replace");
  656         Q_SHORTCUT(a, "Ctrl+R", "Search");
  657         connect(a   , SIGNAL( triggered() ),
  658                 this, SLOT  ( replacePanel() ));
  659 
  660         addAction(a, "&Search", "Search");
  661 
  662         sep = new QAction(this);
  663         sep->setSeparator(true);
  664         addAction(sep, "&Search", "Search");
  665 
  666         a = new QAction(QIcon(":/goto.png"), tr("&Goto line..."), this);
  667         a->setObjectName("goto");
  668         Q_SHORTCUT(a, "Ctrl+G", "Search");
  669         connect(a   , SIGNAL( triggered() ),
  670                 this, SLOT  ( gotoLine() ) );
  671 
  672         addAction(a, "&Search", "Search");
  673 
  674         sep = new QAction(this);
  675         sep->setSeparator(true);
  676         addAction(sep, "&Edit", "");
  677 
  678         a = new QAction(tr("Dynamic line wrapping"), this);
  679         a->setObjectName("wrap");
  680         a->setCheckable(true);
  681         a->setChecked(flag(LineWrap));
  682 
  683         addAction(a, "&Edit", "");
  684 
  685         Q_SHORTCUT(a, "F10", "Edit");
  686         connect(a   , SIGNAL( toggled(bool) ),
  687                 this, SLOT  ( setLineWrapping(bool) ) );
  688 
  689 
  690         m_bindingsMenu = new QMenu(tr("Input binding"), this);
  691         m_bindingsActions = new QActionGroup(m_bindingsMenu);
  692         //m_bindingsActions->setExclusive(true);
  693 
  694         connect(m_bindingsActions   , SIGNAL( triggered(QAction*) ),
  695                 this                , SLOT  ( bindingSelected(QAction*) ) );
  696 
  697         aDefaultBinding = new QAction(tr("Default"), m_bindingsMenu);
  698         aDefaultBinding->setCheckable(true);
  699         aDefaultBinding->setData("default");
  700 
  701         m_bindingsMenu->addAction(aDefaultBinding);
  702         m_bindingsMenu->addSeparator();
  703         m_bindingsActions->addAction(aDefaultBinding);
  704         m_registeredBindings["default"] = nullptr;
  705 
  706         updateBindingsMenu();
  707 
  708         m_bindingsMenu->menuAction()->setObjectName("bindings");
  709         addAction(m_bindingsMenu->menuAction(), "&Edit", "");
  710 
  711         sep = new QAction(this);
  712         sep->setSeparator(true);
  713         addAction(sep, QString());
  714 
  715         m_lineEndingsMenu = new QMenu(tr("Line endings"), this);
  716         m_lineEndingsActions = new QActionGroup(m_lineEndingsMenu);
  717         m_lineEndingsActions->setExclusive(true);
  718 
  719         connect(m_lineEndingsActions, SIGNAL( triggered(QAction*) ),
  720                 this                , SLOT  ( lineEndingSelected(QAction*) ) );
  721 
  722         m_lineEndingsActions->addAction(tr("Conservative"))->setData("conservative");
  723         m_lineEndingsActions->addAction(tr("Local"))->setData("local");
  724         m_lineEndingsActions->addAction(tr("Unix/Linux"))->setData("unix");
  725         m_lineEndingsActions->addAction(tr("Dos/Windows"))->setData("dos");
  726         m_lineEndingsActions->addAction(tr("Old Mac"))->setData("mac");
  727         
  728         QList<QAction*> lle = m_lineEndingsActions->actions();
  729 
  730         foreach ( QAction *a, lle )
  731         {
  732             a->setCheckable(true);
  733             m_lineEndingsMenu->addAction(a);
  734         }
  735 
  736         lle.at(0)->setChecked(true);
  737 
  738         m_lineEndingsMenu->menuAction()->setObjectName("lineEndings");
  739         addAction(m_lineEndingsMenu->menuAction(), "&Edit", "");
  740 
  741         /*
  742         sep = new QAction(this);
  743         sep->setSeparator(true);
  744         addAction(sep, QString());
  745         */
  746     }
  747 
  748     if (!m_defaultKeysSet) getEditOperations();
  749 
  750     setWindowTitle("[*]"); //remove warning of setWindowModified
  751 
  752     setCursor(QDocumentCursor());
  753 }
  754 
  755 /*!
  756     \return wether the flag \a f is set
  757 */
  758 bool QEditor::flag(EditFlag f) const
  759 {
  760     return m_state & f;
  761 }
  762 
  763 /*!
  764     \brief Sets the flag \a f
  765 */
  766 void QEditor::setFlag(EditFlag f, bool b)
  767 {
  768     bool changed = flag(f) != b;
  769     if ( b )
  770     {
  771         m_state |= f;
  772     } else {
  773         m_state &= ~f;
  774     }
  775 
  776     if ( f == LineWrap || f == HardLineWrap || f==LineWidthConstraint)
  777     {
  778         m_doc->impl()->setHardLineWrap(flag(HardLineWrap));
  779         m_doc->impl()->setLineWidthConstraint(flag(LineWidthConstraint));
  780 
  781 
  782         if ( isVisible() ) {
  783             if ( flag(HardLineWrap) || flag(LineWidthConstraint) )
  784                 m_doc->setWidthConstraint( m_LineWidth > 0 ? m_LineWidth : wrapWidth() );
  785             else if ( flag(LineWrap) )
  786                 m_doc->setWidthConstraint( wrapWidth() );
  787             else
  788                 m_doc->clearWidthConstraint();
  789         }
  790 
  791         m_cursor.refreshColumnMemory();
  792 
  793         QAction *a = m_actions.value("wrap");
  794 
  795         if ( a && !a->isChecked() )
  796             a->setChecked(flag(LineWrap));
  797 
  798         // TODO : only update cpos if cursor used to be visible?
  799         ensureCursorVisible();
  800     }
  801     if (changed && f == VerticalOverScroll)
  802         setVerticalScrollBarMaximum();
  803 
  804 }
  805 
  806 /*!
  807     \return whether it is possible to call undo()
  808 */
  809 bool QEditor::canUndo() const
  810 {
  811     return m_doc ? m_doc->canUndo() : false;
  812 }
  813 
  814 /*!
  815     \return whether it is possible to call redo()
  816 */
  817 bool QEditor::canRedo() const
  818 {
  819     return m_doc ? m_doc->canRedo() : false;
  820 }
  821 
  822 /*!
  823     \brief Set line wrapping
  824     \param on line wrap on/off
  825 
  826     \note the function also enables "cursor movement within wrapped lines"
  827     which can be disabled manually using setFlag(QEditor::CursorJumpPastWrap, false);
  828 */
  829 void QEditor::setLineWrapping(bool on)
  830 {
  831     setFlag(LineWrap, on);
  832     setFlag(CursorJumpPastWrap, on);
  833 }
  834 
  835 void QEditor::setHardLineWrapping(bool on)
  836 {
  837     setFlag(HardLineWrap, on);
  838 }
  839 void QEditor::setSoftLimitedLineWrapping(bool on)
  840 {
  841     setFlag(HardLineWrap, false);
  842     setFlag(LineWidthConstraint, on);
  843 }
  844 
  845 void QEditor::setWrapLineWidth(int l){
  846     m_LineWidth=l;
  847     if(flag(HardLineWrap)||flag(LineWidthConstraint))
  848     m_doc->setWidthConstraint(m_LineWidth);
  849 }
  850 
  851 void QEditor::setWrapAfterNumChars(int numChars){
  852     if (numChars <= 0) {
  853         m_wrapAfterNumChars = 0;
  854         setWrapLineWidth(0);
  855     }
  856     m_wrapAfterNumChars = qMax(numChars, 20);
  857     int w=QFontMetrics(QDocument::font()).averageCharWidth()*(m_wrapAfterNumChars+0.5) + 5; // +5 fixed width on left side, 0.5: 1/2 a char margin on right side
  858     setWrapLineWidth(w);
  859 }
  860 
  861 /*!
  862     \return The whole text being edited
  863 */
  864 QString QEditor::text() const
  865 {
  866     return m_doc ? m_doc->text() : QString();
  867 }
  868 
  869 /*!
  870     \return The text at a given line
  871     \param line text line to extract, using C++ array conventions (start at zero)
  872 */
  873 QString QEditor::text(int line) const
  874 {
  875     return m_doc ? m_doc->line(line).text() : QString();
  876 }
  877 
  878 /*!
  879     \brief Set the text of the underlying document and update display
  880 */
  881 void QEditor::setText(const QString& s, bool allowUndo)
  882 {
  883     clearPlaceHolders();
  884 
  885     if ( m_doc )
  886         m_doc->setText(s, allowUndo);
  887 
  888     if (!allowUndo || !m_cursor.isValid()) setCursor(QDocumentCursor(m_doc));
  889     else setCursor(m_cursor);
  890 
  891     documentWidthChanged(m_doc->width());
  892     documentHeightChanged(m_doc->height());
  893     viewport()->update();
  894 }
  895 
  896 /*!
  897     \brief Save the underlying document to a file
  898 
  899     \see fileName()
  900 */
  901 void QEditor::save()
  902 {
  903     if ( !m_doc )
  904         return;
  905 
  906     if ( fileName().isEmpty() )
  907     {
  908         QString fn = QFileDialog::getSaveFileName();
  909 
  910         if ( fn.isEmpty() )
  911             return;
  912 
  913         setFileName(fn);
  914     } else if ( isInConflict() ) {
  915         QMessageBox msg (QMessageBox::Warning,
  916                      tr("Conflict!"),
  917                      tr(
  918                        "%1\nhas been modified by another application.\n"
  919                        "Press \"Save\" to overwrite the file on disk\n"
  920                        "Press \"Reset\" to reload the file from disk.\n"
  921                        "Press \"Diff\" to show differences in the editor.\n"
  922                        "Press \"Ignore\" to ignore this warning.\n"
  923                        ).arg(fileName()),
  924                      QMessageBox::NoButton,
  925                      this);
  926         msg.addButton(QMessageBox::Save);
  927         msg.addButton(QMessageBox::Reset);
  928         msg.addButton(QMessageBox::Ignore);
  929         QAbstractButton * diffBtn = msg.addButton(tr("Diff"), QMessageBox::ActionRole);
  930         msg.setDefaultButton(msg.addButton(QMessageBox::Cancel));
  931         if (msg.exec() == QMessageBox::NoButton) {
  932             if ( msg.clickedButton() == diffBtn ) {
  933                 m_saveState = Undefined;
  934                 emit fileInConflictShowDiff();
  935             }
  936             return;
  937         }
  938         QAbstractButton * button = msg.clickedButton();
  939         int ret = msg.standardButton(button);
  940 
  941         if ( ret == QMessageBox::Save )
  942         {
  943             m_saveState = Undefined;
  944         } else if ( ret == QMessageBox::Reset ) {
  945             load(fileName(),document()->codec());
  946             m_saveState = Undefined;
  947             return;
  948         } else if ( ret == QMessageBox::Ignore ) {
  949             m_saveState = Undefined;
  950             return;
  951         } else if ( button == diffBtn ){
  952             m_saveState = Undefined;
  953             emit fileInConflictShowDiff(); //guess this branch is unused
  954             return;
  955         } else {
  956             return;
  957         }
  958     }
  959 
  960     m_saveState = Saving;
  961 
  962     //QTextStream s(&f);
  963     //s << text();
  964     // insert hard line breaks on modified lines (if desired)
  965 
  966     //remove all watches (on old and new file name (setfilename above could have create one!) )
  967     Q_ASSERT(watcher());
  968     watcher()->removeWatch(QString(), this);
  969 
  970     if (!saveCopy(fileName())) {
  971         m_saveState = Undefined;
  972         reconnectWatcher();
  973 
  974         return;
  975     }
  976 
  977     m_doc->setClean();
  978 
  979     emit saved(this, fileName());
  980     m_saveState = Saved;
  981     
  982     QTimer::singleShot(100, this, SLOT( reconnectWatcher() ));
  983     
  984     update();
  985 }
  986 
  987 
  988 bool QEditor::saveCopy(const QString& filename){
  989     Q_ASSERT(m_doc);
  990 
  991     emit slowOperationStarted();
  992 
  993     // insert hard line breaks on modified lines (if desired)
  994     if(flag(HardLineWrap)){
  995         QList<QDocumentLineHandle*> handles = m_doc->impl()->getStatus().keys();
  996         m_doc->applyHardLineWrap(handles);
  997     }
  998     
  999     QString txt = m_doc->text(flag(RemoveTrailing), flag(PreserveTrailingIndent));
 1000     QByteArray data =  m_doc->codec() ? m_doc->codec()->fromUnicode(txt) : txt.toLocal8Bit();
 1001 
 1002     if (m_useQSaveFile) {
 1003         QSaveFile file(filename);
 1004         if (file.open(QIODevice::WriteOnly)) {
 1005             file.write(data);
 1006             bool success = file.commit();
 1007             if (!success) {
 1008                 QMessageBox::warning(this, tr("Saving failed"),
 1009                                      tr("%1\nCould not be written. Error (%2): %3.\n"
 1010                                         "If the file already existed on disk, it was not modified by this operation.")
 1011                                         .arg(QDir::toNativeSeparators(filename))
 1012                                         .arg(file.error())
 1013                                         .arg(file.errorString()),
 1014                                      QMessageBox::Ok);
 1015             }
 1016             return success;
 1017         }
 1018         QMessageBox::warning(this, tr("Saving failed"), tr("Could not get write permissions on file\n%1.\n\nPerhaps it is read-only or opened in another program?").arg(QDir::toNativeSeparators(filename)), QMessageBox::Ok);
 1019         return false;
 1020     } else {
 1021         return writeToFile(filename, data);
 1022     }
 1023 }
 1024 
 1025 /*!
 1026  * Securely writes data to a file. If this is not successfull, the original file stays intact.
 1027  * This procedure is only necessary for Qt < 5.1.0. More recent versions of Qt provide a standard
 1028  * way for this using QSaveFile.
 1029  *
 1030  * This is our safe saving strategy:
 1031  * 1. Prepare: If the file exists, create a copy backupFilename so that it's content is not lost in case of error.
 1032  * 2. Save: Write the file.
 1033  * 3. Cleanup: In case of error, rename the backupFilename back to the original filename.
 1034  *
 1035  * \return true if the data were written to the file successfully.
 1036  */
 1037 bool QEditor::writeToFile(const QString &filename, const QByteArray &data) {
 1038     bool sucessfullySaved = false;
 1039 
 1040     // check available disk space
 1041     quint64 freeBytes;
 1042     while (true) {
 1043         if (!getDiskFreeSpace(QFileInfo(filename).canonicalPath(), freeBytes)) break;
 1044         if (static_cast<quint64>(data.size()) < freeBytes) break;
 1045 
 1046         QMessageBox::StandardButton bt;
 1047         bt = QMessageBox::critical(this, tr("Saving failed"),
 1048                 tr("There seems to be not enough space to save the file at\n%1\n\n"
 1049                    "File size: %2 kB\n"
 1050                    "Free space: %3 kB\n\n"
 1051                    "You should clean up some space and retry. Alternatively you can\n"
 1052                    "cancel the save operation and save to another location instead.\n"
 1053                    "When ignoring this warning TeXstudio will try save to the specified\n"
 1054                    "location. However if there is really not enough space, this will\n"
 1055                    "result in data loss.\n"
 1056                    ).arg(filename).arg(data.size()/1024).arg(freeBytes/1024L),QMessageBox::Retry|QMessageBox::Ignore|QMessageBox::Cancel, QMessageBox::Retry);
 1057         if (bt == QMessageBox::Cancel) { emit slowOperationEnded(); return false; }
 1058         else if (bt == QMessageBox::Ignore) break;
 1059     }
 1060 
 1061     // 1. Prepare
 1062     QString backupFilename;
 1063     if (QFileInfo(filename).exists()) {
 1064         int MAX_TRIES = 100;
 1065         for (int i=0; i<MAX_TRIES; i++) {
 1066             QString fn = filename + QString("~txs%1").arg(i);
 1067             if (QFile::copy(filename, fn)) {
 1068                 backupFilename = fn;
 1069                 break;
 1070             }
 1071         }
 1072         if (backupFilename.isNull()) {
 1073             QMessageBox::warning(this, tr("Warning"),
 1074                                  tr("Creating a backup of the file failed. You can still continue saving. "
 1075                                     "However, if the save action fails, you may loose the data in the original file. "
 1076                                     "Do you wish to continue?"),
 1077                                  QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
 1078         }
 1079     }
 1080 
 1081     // 2. Save
 1082     QFile f(filename);
 1083     if ( !f.open(QFile::WriteOnly) ) {
 1084         QMessageBox::warning(this, tr("Saving failed"), tr("Could not get write permissions on file\n%1.\n\nPerhaps it is read-only or opened in another program?").arg(QDir::toNativeSeparators(filename)), QMessageBox::Ok);
 1085 
 1086         // 3. Cleanup
 1087         QFile::remove(backupFilename);  // original was not modified
 1088         sucessfullySaved = false;
 1089     } else {
 1090         int bytesWritten = f.write(data);
 1091         sucessfullySaved = (bytesWritten == data.size());
 1092 
 1093         // 3. Cleanup
 1094         if (sucessfullySaved) {
 1095             QFile::remove(backupFilename);
 1096         } else {
 1097             QString message = tr("Writing the document to file\n%1\nfailed.").arg(filename);
 1098             if (!backupFilename.isNull()) {
 1099                 QFile::remove(filename);
 1100                 bool ok = QFile::rename(backupFilename, filename);  // revert
 1101                 if (!ok) {
 1102                     message += "\n" + tr("The original file on disk was destroyed during the save operation.\n"
 1103                                          "You'll find a copy at\n%1").arg(backupFilename);
 1104                 }
 1105             }
 1106             QMessageBox::critical(this, tr("Saving failed"), message, QMessageBox::Ok);
 1107         }
 1108 
 1109         f.close(); //explicite close for watcher (??? is this necessary anymore?)
 1110     }
 1111 
 1112     emit slowOperationEnded();
 1113 
 1114     return sucessfullySaved;
 1115 }
 1116 
 1117 
 1118 /*!
 1119     \brief Save the content of the editor to a file
 1120 
 1121     \note This method renames the editor, stop monitoring the old
 1122     file and monitor the new one
 1123 */
 1124 void QEditor::save(const QString& fn)
 1125 {
 1126     if ( fileName().count() ) {
 1127         watcher()->removeWatch(fileName(), this);
 1128     }
 1129 
 1130     if ( !saveCopy(fn) ) {
 1131         m_saveState = Undefined;
 1132         reconnectWatcher();
 1133 
 1134         return;
 1135     }
 1136 
 1137     m_doc->setClean();
 1138 
 1139     setFileName(fn);
 1140     emit saved(this, fn);
 1141     m_saveState = Saved;
 1142     
 1143     QTimer::singleShot(100, this, SLOT( reconnectWatcher() ));
 1144 }
 1145 
 1146 
 1147 /*! 
 1148   Saves the text to filename without error checking and unmodified (e.g. without performing hard line break) 
 1149   */
 1150 void QEditor::saveEmergencyBackup(const QString& filename){
 1151     Q_ASSERT(m_doc);
 1152 
 1153     emit slowOperationStarted();
 1154 
 1155     bool sucessfullySaved = QFile::exists(filename);
 1156     do {
 1157         QString txt = m_doc->textLines().join(m_doc->lineEndingString()); 
 1158         QByteArray data = txt.toUtf8();
 1159 
 1160         quint64 freeBytes;
 1161         if (getDiskFreeSpace(QFileInfo(filename).canonicalPath(), freeBytes) && (static_cast<quint64>(data.size()) < freeBytes)) 
 1162             break;
 1163 
 1164         QFile f(filename);
 1165 
 1166         if ( !f.open(QFile::WriteOnly) ) 
 1167             break;
 1168 
 1169         sucessfullySaved = f.write(data) == data.size();
 1170         f.flush();
 1171     } while (false);
 1172 
 1173     if (!sucessfullySaved)
 1174         QFile::remove(filename);
 1175     
 1176     emit slowOperationEnded();
 1177 }
 1178 
 1179 /*!
 1180     \internal
 1181 */
 1182 void QEditor::checkClipboard()
 1183 {
 1184     // LOOKS LIKE THIS FUNCTION NEVER GETS CALLED DESPITE THE CONNECTION...
 1185 
 1186     //const QMimeData *d = QApplication::clipboard()->mimeData();
 1187 
 1188     //qDebug("checking clipboard : %s", d);
 1189 
 1190     //QCE_ENABLE_ACTION("paste", d && d->hasText())
 1191 }
 1192 
 1193 /*!
 1194     \internal
 1195 */
 1196 void QEditor::reconnectWatcher()
 1197 {
 1198     watcher()->removeWatch(this);
 1199     watcher()->addWatch(fileName(), this);
 1200 }
 1201 
 1202 /*!
 1203     \internal
 1204 */
 1205 void QEditor::fileChanged(const QString& file)
 1206 {
 1207     if ( (file != fileName()) || (m_saveState == Saving) || mIgnoreExternalChanges )
 1208         return;
 1209     
 1210     /*
 1211     if ( m_saveState == Saved )
 1212     {
 1213         qApp->processEvents();
 1214         
 1215         m_saveState = Undefined;
 1216         return;
 1217     }
 1218     */
 1219 
 1220     if ( !QFileInfo(file).exists())
 1221     {
 1222         watcher()->removeWatch(QString(), this); //no duplicated questions
 1223 
 1224         if(isHidden() && mSilentReloadOnExternalChanges){ // if hidden, just close the editor
 1225             emit requestClose();
 1226             return;
 1227         }
 1228 
 1229         int ret = QMessageBox::warning(this, tr("File deleted"), tr("The file %1 has been deleted on disk.\n"
 1230                                                                     "Should I save the document as it is to restore the file?\n").arg(fileName()), QMessageBox::Save | QMessageBox::Ignore);
 1231         if (ret == QMessageBox::Save) {
 1232             if ( QFileInfo(file).exists() ) {
 1233                 QMessageBox::warning(this, tr("File deleted"), tr("Well, this is strange: The file %1 is not deleted anymore.\n"
 1234                                                                   "Probably someone else restored it and therefore I'm not going to override the (possible modified) version on the disk.").arg(fileName()), QMessageBox::Ok);
 1235                 m_saveState = Conflict;
 1236                 reconnectWatcher();
 1237                 return;
 1238             } else {
 1239                 m_saveState = Undefined;
 1240                 save(); //save will reconnect the watcher
 1241                 return;
 1242             }
 1243         }
 1244 
 1245         if ( QFileInfo(file).exists() )
 1246             reconnectWatcher();
 1247 
 1248     } else if ( !isContentModified() )
 1249     {
 1250         // silently reload file if the editor contains no modification?
 1251         // -> result in undo/redo history loss, still ask confirmation ?
 1252         bool autoReload = true;
 1253 
 1254         if ( (canUndo() || canRedo()) && !mSilentReloadOnExternalChanges )
 1255         {
 1256             watcher()->removeWatch(QString(), this); //no duplicated questions
 1257             
 1258             int ret = QMessageBox::warning(
 1259                           this, tr("File changed"),
 1260                           tr("%1\n"
 1261                              "was changed outside of TeXstudio. Reload from disk?\n\n"
 1262                              "Notes:\n"
 1263                              "- Reloading overwrites the editor content with the file from disk. This cannot be undone.\n"
 1264                              "- You can permanently enable silent reloading in the options."
 1265                           ).arg(fileName()),
 1266                             QMessageBox::Yes | QMessageBox::No
 1267                           );
 1268 
 1269             if ( ret == QMessageBox::No )
 1270                 autoReload = false;
 1271             reconnectWatcher();
 1272         }
 1273 
 1274         if ( autoReload ){
 1275             reload();
 1276             return;
 1277         }
 1278     }
 1279 
 1280     // TODO : check for actual modification (using a checksum?)
 1281     // TODO : conflict reversible (checksum again?)
 1282     
 1283     //qDebug("conflict!");
 1284     m_saveState = Conflict;
 1285     emit fileInConflict();
 1286 }
 1287 
 1288 /*!
 1289     \return Whether a file conflict has been detected
 1290 
 1291     File conflicts happen when the loaded file is modified
 1292     on disk by another application if the text has been
 1293     modified in QCE
 1294 */
 1295 bool QEditor::isInConflict() const
 1296 {
 1297     return m_saveState == Conflict;
 1298 }
 1299 
 1300 void QEditor::emitNeedUpdatedCompleter(){
 1301     emit needUpdatedCompleter();
 1302 }
 1303 
 1304 QTextCodec* QEditor::getFileCodec() const
 1305 {
 1306  //   if (m_codec==0) QMessageBox::information(0,"abc","def",0);
 1307     return m_doc?m_doc->codec():QDocument::defaultCodec();
 1308 }
 1309 
 1310 void QEditor::setFileCodec(QTextCodec* codec){
 1311     if (!m_doc || !codec || codec==m_doc->codec()) return;
 1312     m_doc->setCodec(codec); //standard encoding in memory, file encoding set when saving
 1313    // setContentModified(true);
 1314 }
 1315 void QEditor::viewWithCodec(QTextCodec* codec){
 1316     if (!m_doc || !codec) return;
 1317     QByteArray dat= m_doc->codec()->fromUnicode(document()->text());
 1318     m_doc->setCodec(codec); //standard encoding in memory, file encoding set when saving
 1319     document()->setText(m_doc->codec()->toUnicode(dat), false);
 1320 }
 1321 
 1322 /*!
 1323     \brief Print the content of the editor
 1324 */
 1325 void QEditor::print()
 1326 {
 1327     if ( !m_doc )
 1328         return;
 1329 
 1330     QPrinter printer;
 1331 
 1332     // TODO : create a custom print dialog, page range sucks, lines range would be better
 1333     QPrintDialog dialog(&printer, this);
 1334     dialog.setWindowTitle(tr("Print Source Code"));
 1335     dialog.setEnabledOptions(QPrintDialog::PrintToFile | QPrintDialog::PrintPageRange);
 1336 
 1337     if ( dialog.exec() == QDialog::Accepted )
 1338     {
 1339         m_doc->print(&printer);
 1340     }
 1341 }
 1342 
 1343 void QEditor::relayPanelCommand(const QString& panel, const QString& command, const QList<QVariant>& args){
 1344     QCodeEdit *m = QCodeEdit::manager(this);
 1345     if (!m) {
 1346         qDebug("Unmanaged QEditor");
 1347         return;
 1348     }
 1349     if (panel == "Search") m->sendPanelCommand("Goto", "hide");
 1350     m->sendPanelCommand(panel, qPrintable(command), args);
 1351 }
 1352 
 1353 
 1354 /*!
 1355     \brief Show the search/replace panel, if any
 1356 */
 1357 void QEditor::find()
 1358 {
 1359     relayPanelCommand("Search", "display", QList<QVariant>() << 1 << false);
 1360 }
 1361 
 1362 void QEditor::find(QString text, bool highlight, bool regex, bool word, bool caseSensitive){
 1363     relayPanelCommand("Search", "find", QList<QVariant>() << text << false << highlight << regex << word << caseSensitive);
 1364 }
 1365 
 1366 void QEditor::find(QString text, bool highlight, bool regex, bool word, bool caseSensitive, bool fromCursor, bool selection){
 1367     relayPanelCommand("Search", "find", QList<QVariant>() << text << false << highlight << regex << word << caseSensitive << fromCursor << selection);
 1368 }
 1369 
 1370 void QEditor::findInSameDir()
 1371 {
 1372     relayPanelCommand("Search", "findNext");
 1373 }
 1374 void QEditor::findNext()
 1375 {
 1376     relayPanelCommand("Search", "findReplace", QList<QVariant>() << false);
 1377 }
 1378 void QEditor::findPrev()
 1379 {
 1380     relayPanelCommand("Search", "findReplace", QList<QVariant>() << true);
 1381 }
 1382 void QEditor::findCount()
 1383 {
 1384     relayPanelCommand("Search", "findReplace", QList<QVariant>() << false << false << false << true);
 1385 }
 1386 void QEditor::selectAllMatches(){
 1387     relayPanelCommand("Search", "selectAllMatches");
 1388 }
 1389 
 1390 /*!
 1391     \brief Show the search/replace panel, if any
 1392 */
 1393 void QEditor::replacePanel()
 1394 {
 1395     relayPanelCommand("Search", "display", QList<QVariant>() << 1 << true);
 1396 }
 1397 void QEditor::replaceNext()
 1398 {
 1399     relayPanelCommand("Search", "findReplace", QList<QVariant>() << false << true);
 1400 }
 1401 void QEditor::replacePrev()
 1402 {
 1403     relayPanelCommand("Search", "findReplace", QList<QVariant>() << true << true);
 1404 }
 1405 void QEditor::replaceAll()
 1406 {
 1407     relayPanelCommand("Search", "findReplace", QList<QVariant>() << false << true << true);
 1408 }
 1409 
 1410 /*!
 1411     \brief Show a panel or dialog to go to a specific line
 1412 */
 1413 void QEditor::gotoLine()
 1414 {
 1415     QCodeEdit *m = QCodeEdit::manager(this);
 1416     
 1417     if ( m && m->hasPanel("Goto") )
 1418     {
 1419         // makes sense hiding this one if present...
 1420         m->sendPanelCommand("Search", "hide");
 1421         
 1422         m->sendPanelCommand("Goto", "show");
 1423     } else {
 1424         QGotoLineDialog dlg(this);
 1425         
 1426         dlg.exec(this);
 1427     }
 1428 }
 1429 
 1430 /*!
 1431     \brief Run time translation entry point for compat with Edyuk
 1432 */
 1433 void QEditor::retranslate()
 1434 {
 1435     QCE_TR_ACTION("undo", tr("&Undo"))
 1436     QCE_TR_ACTION("redo", tr("&Redo"))
 1437 
 1438     QCE_TR_ACTION("cut", tr("Cu&t"))
 1439     QCE_TR_ACTION("copy", tr("&Copy"))
 1440     QCE_TR_ACTION("paste", tr("&Paste"))
 1441 
 1442     QCE_TR_ACTION("indent", tr("&Indent"))
 1443     QCE_TR_ACTION("unindent", tr("&Unindent"))
 1444     QCE_TR_ACTION("comment", tr("Co&mment"))
 1445     QCE_TR_ACTION("uncomment", tr("Unc&omment"))
 1446 
 1447     QCE_TR_ACTION("selectAll", tr("&Select all"))
 1448 
 1449     QCE_TR_ACTION("find", tr("&Find"))
 1450     QCE_TR_ACTION("findNext", tr("Fin&d next"))
 1451     QCE_TR_ACTION("replace", tr("&Replace"))
 1452 
 1453     QCE_TR_ACTION("goto", tr("&Goto line..."))
 1454 
 1455     if ( m_completionEngine )
 1456         m_completionEngine->retranslate();
 1457 
 1458     if ( m_bindingsMenu )
 1459         m_bindingsMenu->setTitle(tr("Input binding"));
 1460 
 1461     if ( aDefaultBinding )
 1462         aDefaultBinding->setText(tr("Default"));
 1463 
 1464     #ifdef _QMDI_
 1465     menus.setTranslation("&Edit", tr("&Edit"));
 1466     menus.setTranslation("&Search", tr("&Search"));
 1467 
 1468     toolbars.setTranslation("Edit", tr("Edit"));
 1469     toolbars.setTranslation("Search", tr("Search"));
 1470     #endif
 1471 }
 1472 
 1473 /*!
 1474     \return the action associated with a given name, if the QEditor has been created with actions on
 1475 */
 1476 QAction* QEditor::action(const QString& s)
 1477 {
 1478     QHash<QString, QAction*>::const_iterator it = m_actions.constFind(s);
 1479 
 1480     return it != m_actions.constEnd() ? *it : nullptr;
 1481 }
 1482 
 1483 /*!
 1484     \brief Add an action to the editor
 1485     \param a action to add
 1486     \param menu if not empty (and if QCE is built with qmdilib support) the action will be added to that menu
 1487     \param toolbar similar to \a menu but acts on toolbars
 1488 
 1489     \see removeAction()
 1490 */
 1491 void QEditor::addAction(QAction *a, const QString& menu, const QString& toolbar)
 1492 {
 1493     if ( !a )
 1494         return;
 1495 
 1496     QWidget::addAction(a);
 1497 
 1498     m_actions[a->objectName()] = a;
 1499 
 1500     if ( pMenu && menu.count() )
 1501     {
 1502         pMenu->addAction(a);
 1503 
 1504         #ifdef _QMDI_
 1505         menus[menu]->addAction(a);
 1506         #endif
 1507     }
 1508 
 1509     if ( toolbar.count() )
 1510     {
 1511         #ifdef _QMDI_
 1512         toolbars[toolbar]->addAction(a);
 1513         #endif
 1514     }
 1515 }
 1516 
 1517 /*!
 1518     \brief remove an action form the editor
 1519     \param a action to add
 1520     \param menu if not empty (and if QCE is built with qmdilib support) the action will be added to that menu
 1521     \param toolbar similar to \a menu but acts on toolbars
 1522 
 1523     \see addAction()
 1524 */
 1525 void QEditor::removeAction(QAction *a, const QString& menu, const QString& toolbar)
 1526 {
 1527     if ( !a )
 1528         return;
 1529 
 1530     QWidget::removeAction(a);
 1531 
 1532     //m_actions.remove(a->objectName());
 1533 
 1534     if ( pMenu )
 1535         pMenu->removeAction(a);
 1536 
 1537     #ifdef _QMDI_
 1538     if ( menu.count() )
 1539     {
 1540         menus[menu]->removeAction(a);
 1541     }
 1542 
 1543     if ( toolbar.count() )
 1544     {
 1545         toolbars[toolbar]->removeAction(a);
 1546     }
 1547     #else
 1548     Q_UNUSED(menu)
 1549     Q_UNUSED(toolbar)
 1550     #endif
 1551 }
 1552 
 1553 /*!
 1554     \brief load a text file
 1555     \param file file to load
 1556 
 1557     If the file cannot be loaded, previous content is cleared.
 1558 */
 1559 
 1560 void QEditor::load(const QString& file, QTextCodec* codec)
 1561 {
 1562     clearPlaceHolders();
 1563 
 1564     m_doc->load(file,codec);
 1565 
 1566     setCursor(QDocumentCursor(m_doc));
 1567 
 1568     documentWidthChanged(m_doc->width());
 1569     documentHeightChanged(m_doc->height());
 1570     viewport()->update();
 1571 
 1572     //m_codec=codec;
 1573     
 1574     //qDebug("checksum = %i", m_lastFileState.checksum);
 1575 
 1576     if ( m_lineEndingsActions )
 1577     {
 1578         // TODO : update Conservative to report original line endings
 1579         static const QRegExp rx(" \\[\\w+\\]");
 1580         QAction *a = m_lineEndingsActions->actions().at(0);
 1581 
 1582         if ( a )
 1583         {
 1584             QDocument::LineEnding le = m_doc->originalLineEnding();
 1585 
 1586             QString txt = a->text();
 1587             txt.remove(rx);
 1588             txt += " [";
 1589 
 1590             if ( le == QDocument::Windows )
 1591                 txt += tr("Windows");
 1592             else
 1593                 txt += tr("Unix");
 1594 
 1595             txt += ']';
 1596 
 1597             a->setText(txt);
 1598         }
 1599     }
 1600 
 1601     setFileName(file);
 1602     
 1603     emit loaded(this, file);
 1604 
 1605     reconnectWatcher();
 1606 }
 1607 
 1608 void QEditor::reload(){
 1609     emit fileAutoReloading(fileName());
 1610     // save cursor information
 1611     int lineNum = cursor().lineNumber();
 1612     int col = cursor().columnNumber();
 1613     int anchorLineOffset = cursor().anchorLineNumber() - lineNum;
 1614     int anchorCol = cursor().anchorColumnNumber();
 1615     QString lineText = cursor().line().text();
 1616 
 1617     load(fileName(),m_doc->codec());
 1618     m_saveState = Undefined;
 1619 
 1620     // restore cursor position based on lineText
 1621     int newLineNum = m_doc->findNearLine(lineText, lineNum);
 1622     QDocumentCursor cur(m_doc, newLineNum+anchorLineOffset, anchorCol, newLineNum, col);
 1623     if (newLineNum>=0 && cur.isValid()) {
 1624         setCursor(cur);
 1625     } else {
 1626         // fall back to staying on the same line number
 1627         cur = QDocumentCursor(m_doc, lineNum);
 1628         if (cur.isValid()) {
 1629             setCursor(cur);
 1630         } else {
 1631             // fall back 2: the new document contains fewer lines than the previous cursor position. -> end ist closest to previous position
 1632             setCursor(QDocumentCursor(m_doc, m_doc->lineCount()-1));
 1633         }
 1634     }
 1635 
 1636     emit fileReloaded();
 1637     return;
 1638 }
 1639 
 1640 /*!
 1641     \return a pointer to the underlying QDocument object
 1642 */
 1643 QDocument* QEditor::document() const
 1644 {
 1645     return m_doc;
 1646 }
 1647 
 1648 /*!
 1649     \internal
 1650 */
 1651 void QEditor::setDocument(QDocument *d)
 1652 {
 1653     Q_UNUSED(d)
 1654 
 1655     qWarning("QEditor::setDocument() is not working yet...");
 1656 }
 1657 
 1658 /*!
 1659     \brief Force a full re-highlighting of the document
 1660 */
 1661 void QEditor::highlight()
 1662 {
 1663     m_doc->highlight();
 1664     //updateContent(0, m_doc->lines());
 1665 }
 1666 
 1667 /*!
 1668     \return the current InputBinding
 1669 */
 1670 QList<QEditorInputBindingInterface*> QEditor::inputBindings() const
 1671 {
 1672     return m_bindings;
 1673 }
 1674 
 1675 /*!
 1676     \brief Add an input binding
 1677 */
 1678 void QEditor::addInputBinding(QEditorInputBindingInterface *b)
 1679 {
 1680     if ( b )
 1681         m_bindings << b;
 1682 
 1683     if ( !aDefaultBinding || !m_bindingsActions )
 1684         return;
 1685 
 1686     QString id = b ? b->id() : QString();
 1687     aDefaultBinding->setChecked(!b);
 1688 
 1689     if ( !b )
 1690         return;
 1691 
 1692     QList<QAction*> actions = m_bindingsActions->actions();
 1693 
 1694     foreach ( QAction *a, actions )
 1695     {
 1696         if ( a->data().toString() != id )
 1697         a->setChecked(true);
 1698     }
 1699 }
 1700 
 1701 /*!
 1702     \brief Remove an input binding
 1703 */
 1704 void QEditor::removeInputBinding(QEditorInputBindingInterface *b)
 1705 {
 1706     int n = m_bindings.removeAll(b);
 1707     
 1708     if ( !aDefaultBinding || !m_bindingsActions || !n )
 1709         return;
 1710     
 1711     QString id = b ? b->id() : QString();
 1712     aDefaultBinding->setChecked(!b);
 1713     
 1714     if ( !b )
 1715         return;
 1716     
 1717     QList<QAction*> actions = m_bindingsActions->actions();
 1718     
 1719     foreach ( QAction *a, actions )
 1720     {
 1721         if ( a->data().toString() != id )
 1722             a->setChecked(false);
 1723     }
 1724 }
 1725 
 1726 /*!
 1727     \brief Set the current input binding
 1728 */
 1729 void QEditor::setInputBinding(QEditorInputBindingInterface *b)
 1730 {
 1731     m_bindings.clear();
 1732     
 1733     if ( b )
 1734         m_bindings << b;
 1735     
 1736     if ( !aDefaultBinding || !m_bindingsActions )
 1737         return;
 1738     
 1739     QString id = b ? b->id() : QString();
 1740     aDefaultBinding->setChecked(!b);
 1741     
 1742     if ( !b )
 1743         return;
 1744     
 1745     QList<QAction*> actions = m_bindingsActions->actions();
 1746     
 1747     foreach ( QAction *a, actions )
 1748     {
 1749         if ( a )
 1750             a->setChecked(a->data().toString() != id);
 1751     }
 1752 }
 1753 
 1754 /*!
 1755     \internal
 1756 */
 1757 void QEditor::updateBindingsMenu()
 1758 {
 1759     if ( !aDefaultBinding || !m_bindingsMenu || !m_bindingsActions )
 1760         return;
 1761 
 1762     QStringList bindings = registeredInputBindingIds();
 1763     QList<QAction*> actions = m_bindingsActions->actions();
 1764 
 1765     aDefaultBinding->setChecked(m_bindings.contains(m_defaultBinding));
 1766 
 1767     foreach ( QAction *a, actions )
 1768     {
 1769         int idx = bindings.indexOf(a->data().toString());
 1770 
 1771         if ( idx == -1 )
 1772         {
 1773             m_bindingsMenu->removeAction(a);
 1774             m_bindingsActions->removeAction(a);
 1775             delete a;
 1776         } else {
 1777             bindings.removeAt(idx);
 1778 
 1779             foreach ( QEditorInputBindingInterface *b, m_bindings )
 1780                 if ( a->data().toString() == b->id() )
 1781                 a->setChecked(true);
 1782 
 1783         }
 1784     }
 1785 
 1786     bindings.removeAll("default");
 1787 
 1788     foreach ( QString s, bindings )
 1789     {
 1790         QEditorInputBindingInterface *b = m_registeredBindings.value(s);
 1791 
 1792         if ( !b )
 1793             continue;
 1794 
 1795         QAction *a = new QAction(b->name(), m_bindingsMenu);
 1796         a->setData(b->id());
 1797         a->setCheckable(true);
 1798 
 1799         m_bindingsActions->addAction(a);
 1800         m_bindingsMenu->addAction(a);
 1801     }
 1802 }
 1803 
 1804 /*!
 1805     \internal
 1806 */
 1807 void QEditor::bindingSelected(QAction *a)
 1808 {
 1809     //a = m_bindingsActions->checkedAction();
 1810 
 1811     if ( !a )
 1812         return;
 1813     
 1814     QEditorInputBindingInterface *b = m_registeredBindings.value(a->data().toString());
 1815     
 1816     if ( a->isChecked() )
 1817         addInputBinding(b);
 1818     else
 1819         removeInputBinding(b);
 1820     
 1821     //qDebug("setting binding to %s [0x%x]", qPrintable(a->data().toString()), m_binding);
 1822 
 1823     updateMicroFocus();
 1824 }
 1825 
 1826 /*!
 1827     \internal
 1828 */
 1829 void QEditor::lineEndingSelected(QAction *a)
 1830 {
 1831     a = m_lineEndingsActions->checkedAction();
 1832 
 1833     if ( !a )
 1834         return;
 1835 
 1836     QString le = a->data().toString();
 1837 
 1838     if ( le == "conservative" )
 1839         m_doc->setLineEnding(QDocument::Conservative);
 1840     else if ( le == "local" )
 1841         m_doc->setLineEnding(QDocument::Local);
 1842     else if ( le == "unix" )
 1843         m_doc->setLineEnding(QDocument::Unix);
 1844     else if ( le == "dos" )
 1845         m_doc->setLineEnding(QDocument::Windows);
 1846 
 1847 
 1848     updateMicroFocus();
 1849 }
 1850 
 1851 /*!
 1852     \internal
 1853 */
 1854 void QEditor::lineEndingChanged(int lineEnding)
 1855 {
 1856     if ( !m_lineEndingsActions )
 1857         return;
 1858 
 1859     QAction *a = m_lineEndingsActions->checkedAction(),
 1860             *n = m_lineEndingsActions->actions().at(lineEnding);
 1861 
 1862     if ( a != n )
 1863         n->setChecked(true);
 1864 
 1865 }
 1866 
 1867 /*!
 1868     \return the current cursor
 1869 */
 1870 QDocumentCursor QEditor::cursor() const
 1871 {
 1872     QDocumentCursor copy(m_cursor, false);
 1873     return copy;
 1874 }
 1875 
 1876 /*!
 1877     \return the current cursor handle
 1878 */
 1879 QDocumentCursorHandle* QEditor::cursorHandle() const
 1880 {
 1881     return m_cursor.handle();
 1882 }
 1883 
 1884 /*!
 1885     \brief Set the document cursor
 1886     \param moveView: If disabled, no check is made that the new new cursor position position is visible.
 1887            In this case you take over responsibility to call ensureCursorVisible later on
 1888 
 1889 */
 1890 void QEditor::setCursor(const QDocumentCursor& c, bool moveView)
 1891 {
 1892     repaintCursor();
 1893 
 1894     m_cursor = c.isValid() ? c : QDocumentCursor(m_doc);
 1895     m_cursor.setColumnMemory(true);
 1896     m_cursor.setAutoUpdated(true);
 1897     m_cursor.setAutoErasable(false);
 1898     clearCursorMirrors();
 1899 
 1900     if (m_cursor.columnNumber() > m_cursor.line().length())
 1901         m_cursor.setColumnNumber(m_cursor.line().length());
 1902 
 1903     if ( m_curPlaceHolder >=0 && m_curPlaceHolder < m_placeHolders.count() )
 1904     {
 1905         const PlaceHolder& ph = m_placeHolders[m_curPlaceHolder];
 1906         
 1907         if ( !ph.cursor.isWithinSelection(m_cursor) )
 1908         {
 1909             setPlaceHolder(-1);
 1910             viewport()->update();
 1911         }
 1912     }
 1913     
 1914     emitCursorPositionChanged();
 1915 
 1916     setFlag(CursorOn, true);
 1917     repaintCursor();
 1918     if (moveView) {
 1919         ensureCursorVisible();
 1920     }
 1921 
 1922     updateMicroFocus();
 1923 }
 1924 
 1925 /*!
 1926     \brief Set the cursor
 1927     \param line document line to move the cursor to (start at zero)
 1928     \param index column index of the new cursor (start at zero)
 1929     \param moveView: If disabled, no check is made that the new cursor position is visible.
 1930            In this case you take over responsibility to call ensureCursorVisible later on
 1931 
 1932 */
 1933 void QEditor::setCursorPosition(int line, int index, bool moveView)
 1934 {
 1935     setCursor(QDocumentCursor(m_doc, line, index), moveView);
 1936 }
 1937 
 1938 /*!
 1939     \brief Write the current cursor position to to integers
 1940 */
 1941 void QEditor::getCursorPosition(int &line, int &index)
 1942 {
 1943     line = m_cursor.lineNumber();
 1944     index = m_cursor.columnNumber();
 1945 }
 1946 
 1947 /*!
 1948     \brief Return the position below the cursor
 1949 */
 1950 bool QEditor::getPositionBelowCursor(QPoint& offset, int width, int height){
 1951     bool above;
 1952     return getPositionBelowCursor(offset, width, height, above);
 1953 }
 1954 
 1955 /*!
 1956  * \brief Calculate an optimal position for a widget of width and height that should be displayed close to the cursor
 1957  * \param outOffset: output Position in Editor coordinates
 1958  * \param outAbove: indicates whether it's better to place the widget above the cursor than below
 1959  * \return false if there is no valid cursor, true otherwise
 1960  */
 1961 bool QEditor::getPositionBelowCursor(QPoint& outOffset, int width, int height, bool& outAbove){
 1962     QDocumentCursor c(m_cursor, false);
 1963     QDocumentLine line = c.line();
 1964     if (!c.line().isValid()) return false;
 1965     if (c.columnNumber() < 0 || c.columnNumber() > line.length()) return false;
 1966 
 1967     outOffset = line.cursorToDocumentOffset(c.columnNumber()-1);
 1968     outOffset.setY(outOffset.y() + document()->y(c.lineNumber()) + document()->getLineSpacing());
 1969     outOffset = mapFromContents(outOffset);
 1970     int left;
 1971     int temp;
 1972     getPanelMargins(&left, &temp, &temp, &temp);
 1973     outOffset.setX(outOffset.x() + left);
 1974     if (outOffset.y() + height > this->height()) {
 1975         outOffset.setY(outOffset.y() - document()->getLineSpacing() - height);
 1976         outAbove = true;
 1977     } else {
 1978         outAbove = false;
 1979     }
 1980     if (outOffset.x() + width > this->width()) {
 1981         // box will extend beyond editor width
 1982         // move to left but not further than the left border of the editor widget
 1983         outOffset.setX(qMax(0, this->width() - width));
 1984     }
 1985     return true;
 1986 }
 1987 
 1988 /*!
 1989     \return the number of cursor mirrors currently used
 1990 */
 1991 int QEditor::cursorMirrorCount() const
 1992 {
 1993     return m_mirrors.count();
 1994 }
 1995 
 1996 /*!
 1997     \return the cursor mirror at index \a i
 1998 
 1999     Index has no extra meaning : you cannot deduce anything about
 2000     the cursor mirror it corresponds to from it. For instance, the
 2001     cursor mirror at index 0 is neither the first mirror added nor
 2002     the one at smallest document position (well : it *might* be but
 2003     that would be a coincidence...)
 2004 */
 2005 QDocumentCursor QEditor::cursorMirror(int i) const
 2006 {
 2007     return i >= 0 && i < m_mirrors.count() ? m_mirrors.at(i) : QDocumentCursor();
 2008 }
 2009 
 2010 /*!
 2011     \return the current cursor and all mirrors
 2012 
 2013     Not slow, but also not the fastest. Best to avoid in loops
 2014 */
 2015 QList<QDocumentCursor> QEditor::cursors() const{
 2016     QList<QDocumentCursor> res;
 2017     if (m_cursor.isValid()) res << m_cursor;
 2018     res << m_mirrors;
 2019     return res;
 2020 }
 2021 
 2022 /*!
 2023     \brief Clear all placeholders
 2024 */
 2025 void QEditor::clearPlaceHolders()
 2026 {
 2027     bool updateView = m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.count();
 2028     
 2029     m_curPlaceHolder = -1;
 2030 
 2031     for ( int i = 0; i < m_placeHolders.count(); ++i )
 2032     {
 2033         PlaceHolder& ph = m_placeHolders[i];
 2034 
 2035         ph.cursor.setAutoUpdated(false);
 2036 
 2037         for ( int j = 0; j < ph.mirrors.count(); ++j )
 2038         {
 2039             ph.mirrors[j].setAutoUpdated(false);
 2040         }
 2041 
 2042         ph.mirrors.clear();
 2043     }
 2044 
 2045     m_placeHolders.clear();
 2046     
 2047     if ( updateView )
 2048         viewport()->update();
 2049     
 2050 }
 2051 
 2052 /*!
 2053     \brief Add a placeholder
 2054     \param p placeholder data
 2055     \param autoUpdate whether to force auto updating of all cursors used by the placeholder
 2056 
 2057     Auto update is on by default and it is recommended not to disable it unless you know what you are doing.
 2058 */
 2059 void QEditor::addPlaceHolder(const PlaceHolder& p, bool autoUpdate)
 2060 {
 2061     m_placeHolders << p;
 2062 
 2063     PlaceHolder& ph = m_placeHolders.last();
 2064 
 2065     ph.cursor.setAutoUpdated(autoUpdate);
 2066     ph.cursor.setAutoErasable(p.autoRemove);
 2067     ph.cursor.movePosition(ph.length, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 2068 
 2069     for ( int i = 0; i < ph.mirrors.count(); ++i )
 2070     {
 2071         int mirrorLen = ph.length;
 2072         if (ph.affector)
 2073             mirrorLen = ph.affector->affect(nullptr, ph.cursor.selectedText(), m_placeHolders.size()-1, i).length();
 2074 
 2075 
 2076         ph.mirrors[i].setAutoUpdated(autoUpdate);
 2077         ph.mirrors[i].setAutoErasable(p.autoRemove);
 2078         ph.mirrors[i].movePosition(mirrorLen, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 2079     }
 2080 }
 2081 /*!
 2082   Adds a mirror to the given placeholder
 2083   */
 2084 void QEditor::addPlaceHolderMirror(int placeHolderId, const QDocumentCursor& c){
 2085     if (placeHolderId<0 || placeHolderId>=m_placeHolders.count())
 2086         return;
 2087     PlaceHolder& ph = m_placeHolders[placeHolderId];
 2088     ph.mirrors << c;
 2089     ph.mirrors.last().setAutoUpdated(true);
 2090     ph.mirrors.last().setAutoErasable(true);
 2091     int mirrorLen = ph.affector ? ph.affector->affect(nullptr, ph.cursor.selectedText(), placeHolderId, ph.mirrors.size()-1).length() : ph.length;
 2092     ph.mirrors.last().movePosition(mirrorLen, QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 2093 }
 2094 
 2095 /*!
 2096     \brief Remove a placeholder
 2097     \param i placeholder index
 2098     
 2099     \note if the current placeholder is removed there will be NO automatic switching to a remaining one.
 2100 */
 2101 void QEditor::removePlaceHolder(int id)
 2102 {
 2103     if ( id == m_curPlaceHolder){
 2104         clearCursorMirrors();
 2105         m_curPlaceHolder = -1;
 2106     }
 2107 
 2108     if ( id<0 || id>=m_placeHolders.count() ) 
 2109         return;
 2110     
 2111     PlaceHolder& ph = m_placeHolders[id];
 2112     
 2113     for ( int i = 0; i < ph.mirrors.count(); ++i ){
 2114         ph.mirrors[i].setAutoUpdated(false);
 2115         ph.mirrors[i].line().setFlag(QDocumentLine::LayoutDirty,true);
 2116         }
 2117     ph.mirrors.clear();
 2118     ph.cursor.setAutoUpdated(false);
 2119     m_placeHolders.removeAt(id);
 2120     
 2121     if ( id < m_curPlaceHolder )
 2122         --m_curPlaceHolder;
 2123     if ( id < m_lastPlaceHolder )
 2124         --m_lastPlaceHolder;
 2125     else if ( id == m_lastPlaceHolder )
 2126         m_lastPlaceHolder=-1;
 2127     
 2128     viewport()->update();
 2129 }
 2130 
 2131 /*!
 2132     \brief Replaces all placeholders with new ones.
 2133 
 2134     \note New placeholders will not be initialized
 2135 */
 2136 void QEditor::replacePlaceHolders(const QList<PlaceHolder>& newPlaceholders){
 2137     clearPlaceHolders();//is this needed?
 2138     m_placeHolders = newPlaceholders;
 2139     if ( m_curPlaceHolder >= m_placeHolders.size() )
 2140         m_curPlaceHolder = m_placeHolders.size() - 1;
 2141     m_lastPlaceHolder = -1;
 2142     viewport()->update();
 2143 }
 2144 
 2145 /*!
 2146     \return the number of placeholders currently set
 2147 */
 2148 int QEditor::placeHolderCount() const
 2149 {
 2150     return m_placeHolders.count();
 2151 }
 2152 /*!
 2153     \return the id of the current placeholder
 2154 */
 2155 int QEditor::currentPlaceHolder() const
 2156 {
 2157     return m_curPlaceHolder;
 2158 }
 2159 const PlaceHolder& QEditor::getPlaceHolder(int i) const{
 2160     return m_placeHolders.at(i);
 2161 }
 2162 QList<PlaceHolder> QEditor::getPlaceHolders(){
 2163     return m_placeHolders;
 2164 }
 2165 
 2166 /*! Checks if there exists a placeholder that will be auto overridden by inserting string s
 2167 */
 2168 bool QEditor::isAutoOverrideText(const QString& s) const{
 2169     const QDocumentCursor& c= m_cursor;
 2170     if (c.hasSelection() || (flag(Overwrite) && !c.atLineEnd()))
 2171         return false;
 2172     for ( int i = m_placeHolders.size()-1; i >= 0 ; i-- )
 2173         if ( m_placeHolders[i].autoOverride && m_placeHolders[i].cursor.lineNumber() == c.lineNumber() &&
 2174              m_placeHolders[i].cursor.anchorColumnNumber() == c.anchorColumnNumber() &&
 2175              m_placeHolders[i].cursor.selectedText().startsWith(s) ) {
 2176         return true;
 2177     }
 2178     return false;
 2179 }
 2180 
 2181 /*! Creates a temporary auto overridden placeholder at position start with length length, and
 2182     merges it with a directly following placeholder (equivalent to extending the following placeholder
 2183     by length characters to the left)
 2184 */
 2185 void QEditor::resizeAutoOverridenPlaceholder(const QDocumentCursor& start, int length){
 2186     for (int i=0;i<m_placeHolders.size();i++) {
 2187         PlaceHolder& ph = m_placeHolders[i];
 2188         if (ph.autoOverride &&
 2189             ph.cursor.lineNumber() == start.lineNumber() &&
 2190             ph.cursor.anchorColumnNumber() == start.columnNumber() + length) {
 2191             ph.cursor.setAnchorColumnNumber(start.columnNumber());
 2192         }
 2193     }
 2194 }
 2195 /*!
 2196     \brief Set the current placeholder to use
 2197 
 2198     This function change the cursor and the cursor mirrors.
 2199 */
 2200 void QEditor::setPlaceHolder(int i, bool selectCursors)
 2201 {
 2202     if (i == m_curPlaceHolder && i == -1) 
 2203         return;
 2204     
 2205     if (m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.size() && m_placeHolders[m_curPlaceHolder].autoRemoveIfLeft) {
 2206         if (i > m_curPlaceHolder) i--;
 2207         int toRemove = m_curPlaceHolder;
 2208         m_curPlaceHolder = -1; //prevent endless recursion, if cursor mirrors exist
 2209         removePlaceHolder(toRemove);
 2210     }
 2211     
 2212     m_curPlaceHolder = -1; 
 2213     clearCursorMirrors();
 2214     if ( i < 0 || i >= m_placeHolders.count() ){
 2215         viewport()->update();
 2216         return;
 2217     }
 2218 
 2219     if (m_placeHolderSynchronizing) return;
 2220     m_placeHolderSynchronizing=true; //prevent recursive calls (from updateContent) TODO: move the synchronizing there
 2221 
 2222     QDocumentCursor cc = m_placeHolders[i].cursor;
 2223     selectCursors|=!cc.isWithinSelection(m_cursor);// && cc.hasSelection() && cc.selectionStart()!=cc.selectionEnd();
 2224     
 2225     //qDebug("set placeholder: %i, select cursors: %i", i, (int)selectCursors);
 2226 
 2227     if (selectCursors)
 2228         setCursor(cc);
 2229     else if (m_cursor.hasColumnMemory()) 
 2230         m_cursor.setColumnMemory(false);
 2231 
 2232     i = -1;
 2233     for (int j=0;j<m_placeHolders.size();j++)
 2234         if (cc.equal(m_placeHolders[j].cursor)) {
 2235             i = j;
 2236             break;
 2237         }
 2238     if (i == -1) return;
 2239     
 2240     PlaceHolder& ph = m_placeHolders[i]; //using reference to change the placeholder
 2241     QString base = cc.selectedText();
 2242     for ( int j=0; j< ph.mirrors.size(); j++)
 2243     {
 2244         QDocumentCursor &mc = ph.mirrors[j];
 2245         QString mirrored = ph.affector ? ph.affector->affect(nullptr, base, i, j) : base;
 2246         if (mc.selectedText()!=mirrored){
 2247             //qDebug() << "resync placeholder mirror for " << m_curPlaceHolder << " mirror "<<j << " was: " << mc.selectedText() << " should be " << cc.selectedText() << " from " << cc.anchorLineNumber() << ":" << cc.anchorColumnNumber() << "->" << cc.lineNumber() << ":"<<cc.columnNumber()<<"\n";
 2248             //if mirror synchronization is broken => resyncronize
 2249             mc.replaceSelectedText(mirrored);
 2250         }
 2251     }
 2252 
 2253     m_placeHolderSynchronizing=false;
 2254         
 2255     /*
 2256         ditch cursor mirrors in favor of QDocumentCursor::replaceSelectedText()
 2257         
 2258             * workaround mirror limitations re movement (no way to ensure proper
 2259             synchronization when moving up/down)
 2260             
 2261             * make it relatively easy to implement affectors
 2262     */
 2263     
 2264     m_curPlaceHolder = i;
 2265     
 2266     viewport()->update();
 2267 }
 2268 
 2269 /*!
 2270     \brief Move to next placeholder
 2271 
 2272     \see setPlaceHolder
 2273     \return is there actually a next placeholder
 2274 */
 2275 bool QEditor::nextPlaceHolder()
 2276 {
 2277     if ( m_placeHolders.isEmpty() )
 2278         return false;
 2279 
 2280     /*++m_curPlaceHolder;
 2281 
 2282     if ( m_curPlaceHolder >= m_placeHolders.count() )
 2283         m_curPlaceHolder = 0;
 2284     */
 2285     int np=-1;
 2286     for (int i=0; i< m_placeHolders.count();i++){
 2287         if (m_placeHolders[i].cursor.beginBoundaryLarger(m_cursor) && 
 2288             (np==-1 || m_placeHolders[i].cursor<=m_placeHolders[np].cursor) &&
 2289             !m_placeHolders[i].autoOverride)
 2290                 np=i;
 2291     }
 2292 
 2293     setPlaceHolder(np);
 2294     return np!=-1;
 2295 }
 2296 
 2297 /*!
 2298     \brief Move to previous placeholder
 2299 
 2300     \see setPlaceHolder
 2301     \return is there actually a previous placeholder
 2302 */
 2303 bool QEditor::previousPlaceHolder()
 2304 {
 2305     if ( m_placeHolders.isEmpty() )
 2306         return false;
 2307 
 2308     /*if ( m_curPlaceHolder <= 0 )
 2309         m_curPlaceHolder = m_placeHolders.count();
 2310 
 2311     --m_curPlaceHolder;*/
 2312     int np=-1;
 2313     for (int i=0; i< m_placeHolders.count();i++){
 2314         if (m_cursor.endBoundaryLarger(m_placeHolders[i].cursor) && 
 2315             (np==-1 || m_placeHolders[i].cursor>=m_placeHolders[np].cursor) &&
 2316             !m_placeHolders[i].autoOverride)
 2317                 np=i;
 2318     }   
 2319 
 2320     setPlaceHolder(np);
 2321     return np!=-1;
 2322 }
 2323 
 2324 /*!
 2325     \return the code completion engine set to this editor, if any
 2326 */
 2327 QCodeCompletionEngine* QEditor::completionEngine() const
 2328 {
 2329     return m_completionEngine;
 2330 }
 2331 
 2332 /*!
 2333     \brief Set a code completion engine to the editor
 2334 
 2335     \warning Most completion engines can only be attached
 2336     to a single editor due to issues in the widget used to
 2337     dispaly matches so you got to clone them and, as a consequence
 2338     QEditor will take ownership of the completion engines
 2339     and delete them.
 2340 */
 2341 void QEditor::setCompletionEngine(QCodeCompletionEngine *e)
 2342 {
 2343     if ( m_completionEngine )
 2344     {
 2345         m_completionEngine->setEditor(nullptr);
 2346         m_completionEngine->deleteLater();
 2347     }
 2348 
 2349     m_completionEngine = e;
 2350 
 2351     if ( m_completionEngine )
 2352     {
 2353         m_completionEngine->setEditor(this);
 2354     }
 2355 }
 2356 
 2357 /*!
 2358     \return the language definition set to this editor, if any
 2359 */
 2360 QLanguageDefinition* QEditor::languageDefinition() const
 2361 {
 2362     return m_definition;
 2363 }
 2364 
 2365 /*!
 2366     \brief Set a language definition to the editor
 2367 */
 2368 void QEditor::setLanguageDefinition(QLanguageDefinition *d)
 2369 {
 2370     m_definition = d;
 2371 
 2372     if ( m_doc )
 2373         m_doc->setLanguageDefinition(d);
 2374 
 2375     if ( m_definition )
 2376     {
 2377         bool cuc = d->singleLineComment().count();
 2378 
 2379         QCE_ENABLE_ACTION("comment", cuc)
 2380         QCE_ENABLE_ACTION("uncomment", cuc)
 2381     } else {
 2382         QCE_ENABLE_ACTION("comment", false)
 2383         QCE_ENABLE_ACTION("uncomment", false)
 2384     }
 2385 }
 2386 
 2387 /*!
 2388     \return the line at a given viewport position
 2389 */
 2390 QDocumentLine QEditor::lineAtPosition(const QPoint& p) const
 2391 {
 2392     return m_doc ? m_doc->lineAt(p) : QDocumentLine();
 2393 }
 2394 
 2395 /*!
 2396     \return The cursor object nearest to the given viewport position
 2397 */
 2398 QDocumentCursor QEditor::cursorForPosition(const QPoint& p) const
 2399 {
 2400     //qDebug("cursor for : (%i, %i)", p.x(), p.y());
 2401 
 2402     return m_doc ? m_doc->cursorAt(p) : QDocumentCursor();
 2403 }
 2404 
 2405 /*!
 2406     \brief Set the cursor to that nearest to a given viewport position
 2407     \param moveView: If disabled, no check is made that the cursor is visible.
 2408            In this case you take over responsibility to call ensureCursorVisible later on
 2409 */
 2410 void QEditor::setCursorPosition(const QPoint& p, bool moveView)
 2411 {
 2412     //qDebug("cursor for : (%i, %i)", p.x(), p.y());
 2413 
 2414     QDocumentCursor c = cursorForPosition(p);
 2415 
 2416     if ( c.isValid() )
 2417     {
 2418         setCursor(c, moveView);
 2419     }
 2420 }
 2421 
 2422 /*!
 2423     \brief Emitted whenever the position of the cursor changes
 2424 */
 2425 void QEditor::emitCursorPositionChanged()
 2426 {
 2427     m_cursorLinesFromViewTop = m_cursor.documentPosition().y() / m_doc->getLineSpacing() - verticalScrollBar()->value();
 2428     emit cursorPositionChanged();
 2429     emit copyAvailable(m_cursor.hasSelection());
 2430 
 2431     if ( m_definition )
 2432         m_definition->match(m_cursor);
 2433 
 2434     if ( m_doc->impl()->hasMarks() )
 2435         QLineMarksInfoCenter::instance()->cursorMoved(this);
 2436 
 2437 }
 2438 
 2439 /*!
 2440     \brief Undo the last editing operation, if any on the stack
 2441 */
 2442 void QEditor::undo()
 2443 {
 2444     if ( m_doc )
 2445     {
 2446         if ( m_definition )
 2447             m_definition->clearMatches(m_doc);
 2448 
 2449         m_doc->setProposedPosition(QDocumentCursor());
 2450 
 2451         m_doc->undo();
 2452 
 2453         QDocumentCursor c=m_doc->getProposedPosition();
 2454         if(c.isValid() && !m_mirrors.size())
 2455             setCursor(c);
 2456 
 2457 
 2458         ensureCursorVisible();
 2459         setFlag(CursorOn, true);
 2460         emitCursorPositionChanged();
 2461         repaintCursor();
 2462     }
 2463 }
 2464 
 2465 /*!
 2466     \brief Redo the last undone editing operation, if any on the stack
 2467 */
 2468 void QEditor::redo()
 2469 {
 2470     if ( m_doc )
 2471     {
 2472         if ( m_definition )
 2473             m_definition->clearMatches(m_doc);
 2474 
 2475         m_doc->redo();
 2476 
 2477         ensureCursorVisible();
 2478         setFlag(CursorOn, true);
 2479         emitCursorPositionChanged();
 2480         repaintCursor();
 2481     }
 2482 }
 2483 
 2484 /*!
 2485     \brief Cut the selected text, if any
 2486 */
 2487 void QEditor::cut()
 2488 {
 2489     copy();
 2490 
 2491     bool macroing = isMirrored();
 2492 
 2493     if ( macroing )
 2494         m_doc->beginMacro();
 2495 
 2496     m_cursor.removeSelectedText();
 2497 
 2498     if ( atPlaceholder() ) // need new evaluation, because remove operation might have changed things
 2499     {
 2500         PlaceHolder& ph = m_placeHolders[m_curPlaceHolder];
 2501         QString baseText = ph.cursor.selectedText();
 2502 
 2503         QKeyEvent ev(QEvent::KeyPress, Qt::Key_Paste, Qt::NoModifier); // just a dummy to be able to pass something reasonable to affect() - currently unused
 2504         for ( int phm = 0; phm < ph.mirrors.count(); ++phm )
 2505         {
 2506             QString s = ph.affector ?  ph.affector->affect(&ev, baseText, m_curPlaceHolder, phm) : baseText;
 2507             ph.mirrors[phm].replaceSelectedText(s);
 2508         }
 2509     }
 2510 
 2511     cursorMirrorsRemoveSelectedText();
 2512 
 2513     if ( macroing )
 2514         m_doc->endMacro();
 2515 
 2516     clearCursorMirrors();
 2517 
 2518     ensureCursorVisible();
 2519     setFlag(CursorOn, true);
 2520     emitCursorPositionChanged();
 2521     repaintCursor();
 2522 }
 2523 
 2524 /*!
 2525     \brief Copy the selected text, if any
 2526 
 2527     \note Column selection may be created but the can only be copied properly in a QCE editor
 2528 */
 2529 void QEditor::copy()
 2530 {
 2531     if ( !m_cursor.hasSelection() )
 2532         return;
 2533 
 2534     QMimeData *d = createMimeDataFromSelection();
 2535     QApplication::clipboard()->setMimeData(d);
 2536     //QApplication::clipboard()->setText(m_cursor.selectedText());
 2537 }
 2538 
 2539 /*!
 2540     \brief Paste text from the clipboard to the current cursor position
 2541 
 2542     \note May paste column selections from other QCE editors
 2543 */
 2544 void QEditor::paste()
 2545 {
 2546     const QMimeData *d = QApplication::clipboard()->mimeData();
 2547 
 2548     if ( d )
 2549         insertFromMimeData(d);
 2550 }
 2551 
 2552 static bool unindent(const QDocumentCursor& cur)
 2553 {
 2554     QDocumentLine beg(cur.line());
 2555     int r = 0, n = 0, t = QDocument::tabStop();
 2556     QString txt = beg.text().left(beg.firstChar());
 2557 
 2558     while ( txt.count() && (n < t) )
 2559     {
 2560         if ( txt.at(txt.length() - 1) == '\t' )
 2561             n += t - (n % t);
 2562         else
 2563             ++n;
 2564 
 2565         ++r;
 2566         txt.chop(1);
 2567     }
 2568 
 2569     if ( !r )
 2570         return false;
 2571 
 2572     QDocumentCursor c(cur);
 2573     c.setSilent(true);
 2574     c.movePosition(1, QDocumentCursor::StartOfBlock, QDocumentCursor::MoveAnchor);
 2575     c.movePosition(r, QDocumentCursor::Right, QDocumentCursor::KeepAnchor);
 2576     c.removeSelectedText();
 2577 
 2578     return true;
 2579 }
 2580 
 2581 static void insertAtLineStart(const QDocumentCursor& cur, const QString& txt)
 2582 {
 2583     QDocumentCursor c(cur);
 2584     c.setSilent(true);
 2585     c.setColumnNumber(0);
 2586     c.insertText(txt);
 2587 }
 2588 
 2589 static void removeFromStart(const QDocumentCursor& cur, const QString& txt)
 2590 {
 2591     QDocumentLine l = cur.line();
 2592     int pos = l.firstChar();
 2593 
 2594     if ( l.text().mid(pos, txt.length()) != txt )
 2595         return;
 2596 
 2597     QDocumentCursor c(cur.document(), cur.lineNumber(), pos);
 2598     c.setSilent(true);
 2599     c.movePosition(txt.length(),
 2600                     QDocumentCursor::NextCharacter,
 2601                     QDocumentCursor::KeepAnchor);
 2602     c.removeSelectedText();
 2603 }
 2604 
 2605 /*!
 2606  * \brief inserts a tab at given cursor position. Depending on the context and the
 2607  * flags ReplaceIndentTabs and ReplaceTextTabs, this inserts spaces up to the next
 2608  * tab stop, otherwise '\t' is inserted.
 2609  */
 2610 void QEditor::insertTab(QDocumentCursor &cur)
 2611 {
 2612     bool replaceTabs = flag(ReplaceIndentTabs);
 2613     if (flag(ReplaceIndentTabs) != flag(ReplaceTextTabs)) {
 2614         // need to check if cursor is in indent or in text
 2615         QDocumentCursor cur(m_cursor);
 2616         while (!cur.atLineStart()) {
 2617             if (!cur.previousChar().isSpace()) {
 2618                 replaceTabs = flag(ReplaceTextTabs);
 2619                 break;
 2620             }
 2621             cur.movePosition(1, QDocumentCursor::PreviousCharacter);
 2622         }
 2623     }
 2624 
 2625     if (replaceTabs) {
 2626         int tabStop = m_doc->tabStop();
 2627         int spaceCount = tabStop - cur.columnNumber() % tabStop;
 2628         cur.insertText(QString(spaceCount, ' '));
 2629     } else {
 2630         insertText(cur, "\t");
 2631     }
 2632 }
 2633 
 2634 void QEditor::tabOrIndentSelection()
 2635 {
 2636     if (m_cursor.hasSelection()) {
 2637         indentSelection();
 2638     } else {
 2639         insertTab();
 2640     }
 2641 }
 2642 
 2643 void QEditor::insertTab()
 2644 {
 2645     bool macroing = m_mirrors.count();
 2646     if (macroing) m_doc->beginMacro();
 2647 
 2648     insertTab(m_cursor);
 2649     for ( int i = 0; i < m_mirrors.count(); ++i ) {
 2650         insertTab(m_mirrors[i]);
 2651     }
 2652 
 2653     if (macroing) m_doc->endMacro();
 2654 }
 2655 
 2656 /*!
 2657     \brief Indent the selection
 2658 */
 2659 void QEditor::indentSelection()
 2660 {
 2661     // TODO : respect tab stops in case of screwed up indent (correct it?)
 2662 
 2663     QString txt = flag(ReplaceIndentTabs) ? QString(m_doc->tabStop(), ' ') : QString("\t");
 2664     
 2665     if ( m_mirrors.count() )
 2666     {
 2667         m_doc->beginMacro();
 2668 
 2669         if ( !protectedCursor(m_cursor) )
 2670             insertAtLineStart(m_cursor, txt);
 2671 
 2672         foreach ( const QDocumentCursor& m, m_mirrors )
 2673             if ( !protectedCursor(m) )
 2674                 insertAtLineStart(m, txt);
 2675 
 2676         m_doc->endMacro();
 2677 
 2678     } else if ( !protectedCursor(m_cursor) ) {
 2679         if ( !m_cursor.hasSelection() )
 2680 
 2681             insertAtLineStart(m_cursor, txt);
 2682         else {
 2683             QDocumentSelection s = m_cursor.selection();
 2684             if ( s.end == 0 && s.startLine < s.endLine )
 2685                 s.endLine--; //only change last line if there is selected text
 2686             QDocumentCursor c(m_doc, s.startLine);
 2687             c.setSilent(true);
 2688             c.beginEditBlock();
 2689 
 2690             while ( c.isValid() && (c.lineNumber() <= s.endLine) )
 2691             {
 2692                 c.insertText(txt);
 2693                 c.movePosition(1, QDocumentCursor::NextLine, QDocumentCursor::ThroughFolding);
 2694 
 2695                 if ( c.atEnd() )
 2696                     break;
 2697             }
 2698 
 2699             c.endEditBlock();
 2700         }
 2701     }
 2702 }
 2703 
 2704 /*!
 2705     \brief Unindent the selection
 2706 */
 2707 void QEditor::unindentSelection()
 2708 {
 2709     if ( m_mirrors.count() )
 2710     {
 2711         m_doc->beginMacro();
 2712 
 2713         if ( !protectedCursor(m_cursor) )
 2714             unindent(m_cursor);
 2715 
 2716         foreach ( const QDocumentCursor& m, m_mirrors ){
 2717             if ( !protectedCursor(m) )
 2718                 unindent(m);
 2719         }
 2720 
 2721         m_doc->endMacro();
 2722 
 2723     } else if ( !protectedCursor(m_cursor) ) {
 2724         if ( !m_cursor.hasSelection())
 2725             unindent(m_cursor);
 2726         else {
 2727             QDocumentSelection s = m_cursor.selection();
 2728             if ( s.end == 0 && s.startLine < s.endLine )
 2729                 s.endLine--; //only change last line if there is selected text
 2730 
 2731             m_doc->beginMacro();
 2732 
 2733             for ( int i = s.startLine; i <= s.endLine; ++i )
 2734             {
 2735                 unindent(QDocumentCursor(m_doc, i));
 2736             }
 2737 
 2738             m_doc->endMacro();
 2739         }
 2740     }
 2741 }
 2742 
 2743 /*!
 2744     \brief Comment the selection (or the current line) using single line comments supported by the language
 2745 */
 2746 void QEditor::commentSelection()
 2747 {
 2748     if ( !m_definition )
 2749         return;
 2750 
 2751     QString txt = m_definition->singleLineComment();
 2752 
 2753     if ( txt.isEmpty() )
 2754         return;
 2755 
 2756     if ( m_mirrors.count() )
 2757     {
 2758         m_doc->beginMacro();
 2759 
 2760         m_definition->clearMatches(m_doc);  // Matches are not handled inside comments. We have to remove them. Otherwise they will stay forever in the comment line.
 2761 
 2762         if ( !protectedCursor(m_cursor) )
 2763         insertAtLineStart(m_cursor, txt);
 2764 
 2765         foreach ( const QDocumentCursor& m, m_mirrors )
 2766             if ( !protectedCursor(m) )
 2767                 insertAtLineStart(m, txt);
 2768 
 2769         m_doc->endMacro();
 2770 
 2771     } else if ( !protectedCursor(m_cursor) ) {
 2772         m_definition->clearMatches(m_doc);  // Matches are not handled inside comments. We have to remove them. Otherwise they will stay forever in the comment line.
 2773         if ( !m_cursor.hasSelection() )
 2774             insertAtLineStart(m_cursor, txt);
 2775         else {
 2776             QDocumentSelection s = m_cursor.selection();
 2777             if ( s.end == 0 && s.startLine < s.endLine )
 2778                 s.endLine--; //only change last line if there is selected text
 2779             QDocumentCursor c(m_doc, s.startLine);
 2780             c.setSilent(true);
 2781             c.beginEditBlock();
 2782 
 2783             while ( c.isValid() && (c.lineNumber() <= s.endLine) )
 2784             {
 2785                 c.insertText(txt);
 2786                 c.movePosition(1, QDocumentCursor::NextLine, QDocumentCursor::ThroughFolding);
 2787 
 2788                 if ( c.atEnd() )
 2789                     break;
 2790             }
 2791 
 2792             c.endEditBlock();
 2793         }
 2794     }
 2795 }
 2796 
 2797 /*!
 2798     \brief Uncomment the selection (or current line), provided it has been commented with single line comments
 2799 */
 2800 void QEditor::uncommentSelection()
 2801 {
 2802     if ( !m_definition )
 2803         return;
 2804 
 2805     QString txt = m_definition->singleLineComment();
 2806 
 2807     if ( txt.isEmpty() )
 2808         return;
 2809 
 2810     if ( m_mirrors.count() )
 2811     {
 2812         m_doc->beginMacro();
 2813 
 2814         if ( !protectedCursor(m_cursor) )
 2815             removeFromStart(m_cursor, txt);
 2816 
 2817         foreach ( const QDocumentCursor& m, m_mirrors ){
 2818             if ( !protectedCursor(m) )
 2819                 removeFromStart(m, txt);
 2820         }
 2821 
 2822         m_doc->endMacro();
 2823 
 2824     } else if ( !protectedCursor(m_cursor) ){
 2825         if ( !m_cursor.hasSelection() )
 2826             removeFromStart(m_cursor, txt);
 2827         else {
 2828             QDocumentSelection s = m_cursor.selection();
 2829             if ( s.end == 0 && s.startLine < s.endLine )
 2830                 s.endLine--; //only change last line if there is selected text
 2831             if (s.startLine<0) s.startLine=0;
 2832             if (s.endLine>m_doc->lines()-1) s.endLine=m_doc->lines()-1;
 2833 
 2834             m_doc->beginMacro();
 2835 
 2836             for ( int i = s.startLine; i <= s.endLine; ++i )
 2837             {
 2838                 removeFromStart(QDocumentCursor(m_doc, i), txt);
 2839             }
 2840 
 2841             m_doc->endMacro();
 2842         }
 2843     }
 2844 }
 2845 
 2846 /*!
 2847     \brief Toggle comments
 2848     If all lines touched by a cursor or selection are commented remove the comment mark.
 2849     Otherwise insert the comment mark
 2850 */
 2851 void QEditor::toggleCommentSelection()
 2852 {
 2853     if ( !m_definition )
 2854         return;
 2855 
 2856     QString commentMark = m_definition->singleLineComment();
 2857     bool allCommented = true;
 2858 
 2859     foreach (const QDocumentCursor &cursor, cursors()) {
 2860         if (cursor.hasSelection()) {
 2861             QDocumentCursor cur = cursor.selectionStart();
 2862             for (int i = cursor.startLineNumber(); i <= cursor.endLineNumber() ; ++i) {
 2863                 if (!cur.line().startsWith(commentMark) && (i<cursor.endLineNumber() || !cur.line().text().isEmpty())) { //special treatmenat of last line if empty
 2864                     allCommented = false;
 2865                     break;
 2866                 }
 2867                 cur.movePosition(1, QDocumentCursor::NextLine);
 2868             }
 2869         } else {
 2870             if (!cursor.line().startsWith(commentMark)) {
 2871                 allCommented = false;
 2872             }
 2873         }
 2874         if (!allCommented) break;
 2875     }
 2876 
 2877     if (allCommented) {
 2878         uncommentSelection();
 2879     } else {
 2880         commentSelection();
 2881     }
 2882 }
 2883 
 2884 /*!
 2885     \brief Select the whole text
 2886 */
 2887 void QEditor::selectAll()
 2888 {
 2889     clearCursorMirrors();
 2890 
 2891     m_cursor.movePosition(1, QDocumentCursor::Start);
 2892     m_cursor.movePosition(1, QDocumentCursor::End, QDocumentCursor::KeepAnchor);
 2893 
 2894     emitCursorPositionChanged();
 2895 
 2896     viewport()->update();
 2897 }
 2898 
 2899 void QEditor::selectNothing(){
 2900     QDocumentCursor cur=cursor();
 2901     cur.clearSelection();
 2902     setCursor(cur);
 2903 }
 2904 
 2905 void QEditor::selectExpand(QDocumentCursor::SelectionType selectionType){
 2906     m_cursor.expandSelect(selectionType);
 2907     for (int i=0;i<m_mirrors.size();i++)
 2908         m_mirrors[i].expandSelect(selectionType);
 2909 }
 2910 
 2911 /*!
 2912  * \brief searches for the next occurence of the text in the last selection and
 2913  * selects this additionally. If there is no selection, the word or command under
 2914  * the cursor is selected.
 2915  */
 2916 void QEditor::selectExpandToNextWord()
 2917 {
 2918     if (!m_cursor.hasSelection()) {
 2919         m_cursor.select(QDocumentCursor::WordOrCommandUnderCursor);
 2920     } else {
 2921         QDocumentCursor &lastCursor = m_cursor;
 2922         foreach (const QDocumentCursor &cm, m_mirrors) {
 2923             if (cm > lastCursor) lastCursor = cm;
 2924         }
 2925         QString selectedText = lastCursor.selectedText();
 2926         QDocumentCursor searchCursor(lastCursor.document(), lastCursor.endLineNumber(), lastCursor.endColumnNumber());
 2927         while (!searchCursor.atEnd()) {
 2928             int col = searchCursor.line().text().indexOf(selectedText, searchCursor.columnNumber());
 2929             if (col < 0) {
 2930                 searchCursor.movePosition(1, QDocumentCursor::EndOfLine);  // ensure searchCursor.atEnd() if no further matches are found
 2931                 searchCursor.movePosition(1, QDocumentCursor::NextLine);
 2932             } else {
 2933                 QDocumentCursor c(m_cursor);
 2934                 m_cursor.setLineNumber(searchCursor.lineNumber());
 2935                 m_cursor.setColumnNumber(col);
 2936                 m_cursor.movePosition(selectedText.length(), QDocumentCursor::NextCharacter, QDocumentCursor::KeepAnchor);
 2937                 // need to add the cursor mirror after changing m_cursor (if c == m_cursor, addCursorMirror does nothing)
 2938                 addCursorMirror(c);
 2939                 break;
 2940             }
 2941         }
 2942     }
 2943 
 2944     emitCursorPositionChanged();
 2945     viewport()->update();
 2946 }
 2947 
 2948 /*!
 2949  * \brief Expands the selection to include the full line.
 2950  * If the selection does already span full lines, the next line will be added.
 2951  * If there is no selection, the current line will be selected.
 2952  */
 2953 void QEditor::selectExpandToNextLine()
 2954 {
 2955     if (!m_cursor.hasSelection()) {
 2956         m_cursor.movePosition(1, QDocumentCursor::StartOfLine);
 2957         m_cursor.movePosition(1, QDocumentCursor::EndOfLine, QDocumentCursor::KeepAnchor);
 2958         m_cursor.movePosition(1, QDocumentCursor::NextLine, QDocumentCursor::KeepAnchor);
 2959         return;
 2960     }
 2961     if (!m_cursor.isForwardSelection()) {
 2962         m_cursor.flipSelection();
 2963     }
 2964     m_cursor.setAnchorColumnNumber(0);
 2965     m_cursor.movePosition(1, QDocumentCursor::EndOfLine, QDocumentCursor::KeepAnchor);
 2966     m_cursor.movePosition(1, QDocumentCursor::NextLine, QDocumentCursor::KeepAnchor);
 2967 
 2968     emitCursorPositionChanged();
 2969     viewport()->update();
 2970 }
 2971 
 2972 /*!
 2973  * \brief Select all occurences of the current selection or the word/command under cursor
 2974  * Note: if the selection is a word, matches are limited to complete words.
 2975  */
 2976 void QEditor::selectAllOccurences()
 2977 {
 2978     selectOccurence(false, false, true);
 2979 }
 2980 void QEditor::selectNextOccurence()
 2981 {
 2982     selectOccurence(false, false, false);
 2983 }
 2984 void QEditor::selectPrevOccurence()
 2985 {
 2986     selectOccurence(true, false, false);
 2987 }
 2988 void QEditor::selectNextOccurenceKeepMirror()
 2989 {
 2990     selectOccurence(false, true, false);
 2991 }
 2992 void QEditor::selectPrevOccurenceKeepMirror()
 2993 {
 2994     selectOccurence(true, true, false);
 2995 }
 2996 void QEditor::selectOccurence(bool backward, bool keepMirrors, bool all)
 2997 {
 2998     //backward and all are exclusive
 2999     if (!m_cursor.hasSelection()) {
 3000         m_cursor.select(QDocumentCursor::WordOrCommandUnderCursor);
 3001     }
 3002     if (!m_cursor.hasSelection()) {
 3003         return;
 3004     }
 3005     QString text = m_cursor.selectedText();
 3006     bool isWord = true;
 3007     foreach (const QChar &c, text) {
 3008         if (!c.isLetterOrNumber()) {
 3009             isWord = false;
 3010             break;
 3011         }
 3012     }
 3013     QDocumentCursor cStart(m_cursor.document(), m_cursor.startLineNumber(), m_cursor.startColumnNumber());
 3014     QDocumentCursor cEnd(m_cursor.document(), m_cursor.endLineNumber(), m_cursor.endColumnNumber());
 3015     bool atBoundaries = (cStart.atLineStart() || !cStart.previousChar().isLetterOrNumber())
 3016                      && (cEnd.atLineEnd() || !cEnd.nextChar().isLetterOrNumber());
 3017 
 3018     QList<QDocumentCursor> cursors;
 3019     if (keepMirrors) cursors = this->cursors();
 3020 
 3021     // TODO: this is a quick solution: using the search panel to select all matches
 3022     //       1. initialize the search with the required parameters
 3023     //       2. select all matches
 3024     //       3. close the search panel which was opened as a side effect of 1.
 3025     // It would be better to be able to perform the search and select without interfering
 3026     // with the search panel UI.
 3027     find(text, false, false, isWord && atBoundaries, true);
 3028     if (all) selectAllMatches();
 3029     else if (backward) {
 3030         findPrev();
 3031         findPrev();
 3032     } else {
 3033         //findNext(); find above already searched one
 3034     }
 3035     relayPanelCommand("Search", "closeElement", QList<QVariant>() << true);
 3036 
 3037     if (keepMirrors) m_mirrors = cursors;
 3038 
 3039     emitCursorPositionChanged();
 3040     viewport()->update();
 3041 }
 3042 
 3043 void QEditor::setVerticalScrollBarMaximum()
 3044 {
 3045     if (!m_doc) return;
 3046     const QSize viewportSize = viewport()->size();
 3047     int viewportHeight = viewportSize.height();
 3048     if (flag(VerticalOverScroll))
 3049         viewportHeight /= 2;
 3050     const int ls = m_doc->getLineSpacing();
 3051     QScrollBar* vsb = verticalScrollBar();
 3052     vsb->setMaximum(qMax(0, 1 + (m_doc->height() - viewportHeight) / ls));
 3053     vsb->setPageStep(viewportSize.height() / ls);
 3054 }
 3055 
 3056 /*!
 3057     \internal
 3058 */
 3059 bool QEditor::event(QEvent *e)
 3060 {
 3061     // preparations for tooltips
 3062 
 3063     if (e->type() == QEvent::ToolTip) {
 3064         QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e);
 3065         emit hovered(helpEvent->pos()-QPoint(m_margins.left(),m_margins.top()));
 3066         return true;
 3067     }
 3068 
 3069     // qcodedit ...
 3070     bool r = QAbstractScrollArea::event(e);
 3071 
 3072     if ( (e->type() == QEvent::Resize || e->type() == QEvent::Show) )
 3073         setVerticalScrollBarMaximum();
 3074 
 3075     if ( e->type() == QEvent::Resize && flag(LineWrap)  && m_doc)
 3076     {
 3077         //qDebug("resize adjust (1) : wrapping to %i", viewport()->width());
 3078         m_doc->setWidthConstraint(wrapWidth());
 3079         ensureCursorVisible(KeepDistanceFromViewTop);
 3080     }
 3081 
 3082     return r;
 3083 }
 3084 
 3085 /*!
 3086     \internal
 3087 */
 3088 void QEditor::paintEvent(QPaintEvent *e)
 3089 {
 3090     if ( !m_doc )
 3091         return;
 3092 
 3093     QPainter p(viewport());
 3094     const int yOffset = verticalOffset();
 3095     const int xOffset = horizontalOffset();
 3096 
 3097     #ifdef Q_GL_EDITOR
 3098     //QRect r(e->rect());
 3099     QRect r(0, 0, viewport()->width(), viewport()->height());
 3100     #else
 3101     QRect r(e->rect());
 3102     #endif
 3103 
 3104     //qDebug() << r;
 3105 
 3106     //p.setClipping(false);
 3107     p.translate(-xOffset, -yOffset);
 3108 
 3109     QDocument::PaintContext ctx;
 3110     ctx.xoffset = xOffset;
 3111     ctx.yoffset = r.y() + yOffset;
 3112     ctx.width = viewport()->width();
 3113     ctx.height = qMin(r.height(), viewport()->height());
 3114     ctx.palette = palette();
 3115     if (m_cursor.isValid())
 3116         ctx.cursors << m_cursor.handle();
 3117     ctx.fillCursorRect = true;
 3118     ctx.blinkingCursor = flag(CursorOn);
 3119 
 3120     if ( m_cursor.hasSelection() )
 3121     {
 3122         //qDebug("atempting to draw selected text");
 3123         QDocumentSelection s = m_cursor.selection();
 3124 
 3125         ctx.selections << s;
 3126     }
 3127 
 3128     // cursor mirrors :D
 3129     foreach ( const QDocumentCursor& m, m_mirrors )
 3130     {
 3131         if ( ctx.blinkingCursor )
 3132             ctx.extra << m.handle();
 3133 
 3134         if ( m.hasSelection() )
 3135         {
 3136             QDocumentSelection s = m.selection();
 3137 
 3138             ctx.selections << s;
 3139         }
 3140     }
 3141     
 3142     for (int i=0;i<ctx.selections.size();i++) {
 3143         if (ctx.selections[i].start < 0) ctx.selections[i].start = 0;
 3144         if (ctx.selections[i].end < 0) ctx.selections[i].end = 0;
 3145     }
 3146 
 3147     if ( m_dragAndDrop.isValid() )
 3148     {
 3149         ctx.extra << m_dragAndDrop.handle();
 3150     }
 3151     if (flag(ShowPlaceholders)) {
 3152         // put placeholder info into paint context
 3153         ctx.placeHolders=m_placeHolders;
 3154         ctx.curPlaceHolder=m_curPlaceHolder;
 3155         ctx.lastPlaceHolder=m_lastPlaceHolder;
 3156     }
 3157         //qDebug("elapsed %d ms",tm.elapsed());
 3158 
 3159     // fill all the background, including areas on which the document does not draw
 3160     // (e.g. left/right margins and the bottom of the viewport if it is larger than the document)
 3161     QBrush bg = palette().base();
 3162     if ( m_doc->getBackground().isValid() )
 3163         bg.setColor(m_doc->getBackground());
 3164     int width = qMax(viewport()->width(), m_doc->width());
 3165     int height = qMax(viewport()->height(), m_doc->height() + m_doc->getLineSpacing());
 3166     // the actual visible height may be up to one line larger than the doc height,
 3167     // because the doc lines is are aligned to the top of the viewport. The viewport
 3168     // then shows n.x lines and when scolled to the very bottom, the .x < 1 line
 3169     // exceeds the document height.
 3170     p.fillRect(0, 0, width, height, bg);
 3171 
 3172     p.save();
 3173     m_doc->draw(&p, ctx);
 3174     p.restore();
 3175         //qDebug("drawn %d ms",tm.elapsed());
 3176 
 3177     //TODO: Customizable appearance
 3178     //TODO: documentRegion is too large, isn't correctly redrawn (especially with a non fixed width font)
 3179 
 3180 
 3181     /*
 3182     debug code for cursor direction: 
 3183     p.setPen(QColor(0,128,0));
 3184     for (int i=0; i < m_placeHolders.count(); i++) {
 3185         p.drawConvexPolygon(m_placeHolders[i].cursor.selectionStart().documentRegion());
 3186         foreach (const QDocumentCursor& m, m_placeHolders[i].mirrors )
 3187             p.drawConvexPolygon(m.selectionStart().documentRegion());
 3188     }*/
 3189     
 3190 }
 3191 
 3192 /*!
 3193     \internal
 3194 */
 3195 void QEditor::timerEvent(QTimerEvent *e)
 3196 {
 3197     int id = e->timerId();
 3198 
 3199     if ( id == m_blink.timerId() )
 3200     {
 3201         bool on = !flag(CursorOn);
 3202 
 3203         if ( m_cursor.hasSelection() )
 3204             on &= style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected,
 3205                                     nullptr,
 3206                                     this) != 0;
 3207 
 3208         setFlag(CursorOn, on);
 3209 
 3210         repaintCursor();
 3211 
 3212         if(m_cursor.lineNumber()==m_lastLine && m_cursor.columnNumber()==m_lastColumn){
 3213             m_hoverCount++;
 3214             if(m_hoverCount==2) emit cursorHovered();
 3215         }else{
 3216             m_lastLine=m_cursor.lineNumber();
 3217             m_lastColumn=m_cursor.columnNumber();
 3218             m_hoverCount=0;
 3219         }
 3220 
 3221     } else if ( id == m_drag.timerId() ) {
 3222         m_drag.stop();
 3223         //startDrag();
 3224     } else if ( id == m_click.timerId() ) {
 3225         m_click.stop();
 3226     } else if ( id == m_autoScroll.timerId() ) {
 3227         const QPoint cPos = viewport()->mapFromGlobal(QCursor::pos());
 3228         const QPoint mousePos = mapToContents(cPos);
 3229 
 3230         QDocumentCursor newCursor = cursorForPosition(mousePos);
 3231 
 3232         if ( newCursor.isNull() ) {
 3233             newCursor = QDocumentCursor(m_doc, 0, 0);
 3234             if( mousePos.x() >= 0 ) {
 3235                 newCursor.movePosition( 1, QDocumentCursor::End );
 3236             }
 3237         }
 3238 
 3239         if (m_multiClickCursor.isValid()) {
 3240             m_cursor.select(m_multiClickCursor.lineNumber(), m_multiClickCursor.columnNumber(),
 3241                             newCursor.lineNumber(), newCursor.columnNumber()
 3242                             );
 3243             m_cursor.expandSelect(m_multiClickCursor.property("isTripleClick").toBool() ? QDocumentCursor::LineUnderCursor : m_doubleClickSelectionType);
 3244         } else {
 3245             m_cursor.setSelectionBoundary(newCursor);
 3246         }
 3247 
 3248         ensureCursorVisible();
 3249         //emit clearAutoCloseStack();
 3250         emitCursorPositionChanged();
 3251 
 3252         repaintCursor();
 3253     }
 3254 }
 3255 
 3256 static int max(const QList<QDocumentCursor>& l)
 3257 {
 3258     int ln = 0;
 3259 
 3260     foreach ( const QDocumentCursor& c, l )
 3261         if ( c.lineNumber() > ln )
 3262             ln = c.lineNumber();
 3263 
 3264     return ln;
 3265 }
 3266 
 3267 static int min(const QList<QDocumentCursor>& l)
 3268 {
 3269     // beware the sign bit...
 3270     int ln = 0x7fffffff;
 3271 
 3272     foreach ( const QDocumentCursor& c, l )
 3273         if ( (c.lineNumber() < ln) || (ln < 0) )
 3274             ln = c.lineNumber();
 3275 
 3276     return ln;
 3277 }
 3278 
 3279 bool QEditor::protectedCursor(const QDocumentCursor& c) const
 3280 {
 3281         Q_UNUSED(c)
 3282     /*if ( c.hasSelection() )
 3283     {
 3284         int line = qMin(c.lineNumber(), c.anchorLineNumber()), end = qMax(c.lineNumber(), c.anchorLineNumber());
 3285         return document()->linesPartiallyFolded(line,end);
 3286     } else {
 3287         return m_doc->line(c.lineNumber()).hasAnyFlag(QDocumentLine::Hidden | QDocumentLine::CollapsedBlockStart | QDocumentLine::CollapsedBlockEnd);
 3288     }*/
 3289     
 3290     /*if (!isProtected) return false;
 3291     if (!c.hasSelection()) return c.line().hasFlag(QDocumentLine::Hidden);
 3292     return c.line().hasFlag(QDocumentLine::Hidden) || c.anchorLine().hasFlag(QDocumentLine::Hidden);*/
 3293     return false;
 3294 }
 3295 
 3296 /*!
 3297     \internal
 3298 */
 3299 void QEditor::keyPressEvent(QKeyEvent *e)
 3300 {
 3301         //tm.start();
 3302      //qDebug()<<"pressed"<<QTime::currentTime().toString("HH:mm:ss:zzz");
 3303     // reset hover counter
 3304     m_hoverCount=-1;
 3305 
 3306     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3307         if ( b->keyPressEvent(e, this) )
 3308             return;
 3309 
 3310     EditOperation op = getEditOperation(e->modifiers(), (Qt::Key)e->key());
 3311     bool handled = op != NoOperation;
 3312     if (op >= EnumForCursorStart && op <= EnumForCursorEnd ){
 3313         e->accept();
 3314 
 3315         if (m_mirrors.count()) {
 3316             int maxSelect = 0;
 3317             for (int i = -1; i < m_mirrors.count(); i++) {
 3318                 QDocumentCursor &cur = (i==-1) ? m_cursor : m_mirrors[i];
 3319                 // TODO can it happen with mirrors that the selection is across different lines?
 3320                 int len = cur.hasSelection() ? cur.selection().end-cur.selection().start : 0;
 3321                 if (len > maxSelect) maxSelect = len;
 3322             }
 3323 
 3324             bool leftLine = false;
 3325             for ( int i = -1; !leftLine && (i < m_mirrors.count()); ++i ) {
 3326                 QDocumentCursor &cur = (i==-1) ? m_cursor : m_mirrors[i];
 3327                 int curLine = cur.lineNumber();
 3328                 // handle unequal line lenghts
 3329                 if ((op == SelectCursorRight || op == SelectCursorWordRight) && cur.atLineEnd()) continue;
 3330                 if (op == SelectCursorLeft) {
 3331                     // TODO can it happen with mirrors that the selection is across different lines?
 3332                     int len = cur.hasSelection() ? cur.selection().end-cur.selection().start : 0;
 3333                     if (len < maxSelect) continue;
 3334                 }
 3335                 cursorMoveOperation(cur, op);
 3336                 leftLine = cur.lineNumber() != curLine;
 3337             }
 3338 
 3339             if ( leftLine || (m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.size() && m_placeHolders[m_curPlaceHolder].autoRemoveIfLeft && !m_placeHolders[m_curPlaceHolder].cursor.isWithinSelection(m_cursor)))
 3340                 setPlaceHolder(-1);
 3341             if (leftLine) {
 3342                 clearCursorMirrors();
 3343                 viewport()->update();
 3344             } else {
 3345                 repaintCursor();
 3346             }
 3347         } else {
 3348             // normal single cursor movement
 3349             cursorMoveOperation(m_cursor, op);
 3350 
 3351             //setFlag(CursorOn, true);
 3352             //ensureCursorVisible();
 3353 
 3354             if ( m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.size() && m_placeHolders[m_curPlaceHolder].autoRemoveIfLeft && !m_placeHolders[m_curPlaceHolder].cursor.isWithinSelection(m_cursor))
 3355                 setPlaceHolder(-1);
 3356                 /*if ( m_curPlaceHolder >= 0 && m_curPlaceHolder < m_placeHolders.count() )
 3357                 {
 3358                     // allow mirror movement out of line while in placeholder
 3359                     PlaceHolder& ph = m_placeHolders[m_curPlaceHolder];
 3360                     if ( ph.cursor.isWithinSelection(cursor) )
 3361                         return true;
 3362                     for ( int i = 0; i < ph.mirrors.count(); ++i )
 3363                         if ( ph.mirrors.at(i).isWithinSelection(cursor) )
 3364                             return true;
 3365                     if ( prev == cursor.lineNumber() && m_mirrors.empty()) {
 3366                         //mark placeholder as leaved
 3367                         m_curPlaceHolder = -1;
 3368                         return false;
 3369                     }
 3370                 }*/
 3371             repaintCursor();
 3372         }
 3373     } else switch (op) {
 3374     case Invalid:
 3375         QApplication::beep();
 3376         break;
 3377     case CreateMirrorUp: case CreateMirrorDown:{
 3378         int ln = - 1;
 3379         QDocumentLine l;
 3380 
 3381         if (op == CreateMirrorUp )
 3382         {
 3383             ln = m_cursor.lineNumber();
 3384 
 3385             if ( m_mirrors.count() )
 3386                 ln = qMin(ln, min(m_mirrors));
 3387 
 3388             //qDebug("first %i", ln);
 3389 
 3390             l = m_doc->line(--ln);
 3391         } else if ( op == CreateMirrorDown ) {
 3392             ln = m_cursor.lineNumber();
 3393 
 3394             if ( m_mirrors.count() )
 3395                 ln = qMax(ln, max(m_mirrors));
 3396 
 3397             l = m_doc->line(++ln);
 3398         }
 3399 
 3400         if ( l.isValid() )
 3401         {
 3402             addCursorMirror(QDocumentCursor(m_doc, ln, m_cursor.anchorColumnNumber()));
 3403             repaintCursor();
 3404             emitCursorPositionChanged();
 3405 
 3406             break;
 3407         }
 3408         break;
 3409     }
 3410 
 3411     case ChangeOverwrite:
 3412         setFlag(Overwrite, !flag(Overwrite));
 3413         if(m_doc)
 3414             m_doc->setOverwriteMode(flag(Overwrite));
 3415 
 3416         // hack to make sure status panel gets updated...
 3417         // TODO : emit signals on flag change?
 3418         emitCursorPositionChanged();
 3419         break;
 3420 
 3421 
 3422     case NextPlaceHolder: nextPlaceHolder(); break;
 3423     case PreviousPlaceHolder: previousPlaceHolder(); break;
 3424 
 3425     case TabOrIndentSelection: tabOrIndentSelection(); break;
 3426     case InsertTab: insertTab(); break;
 3427     case IndentSelection: indentSelection(); break;
 3428     case UnindentSelection: unindentSelection(); break;
 3429 
 3430     case Undo: undo(); break;
 3431     case Redo: redo(); break;
 3432     case Copy: copy(); break;
 3433     case Paste: paste(); break;
 3434     case Cut: cut(); break;
 3435     case Print: print();break;
 3436     case SelectAll: selectAll(); break;
 3437     case Find: find(); break;
 3438     case FindNext: findNext(); break;
 3439     case FindPrevious: findPrev(); break;
 3440     case Replace: replacePanel(); break;
 3441 
 3442     case NoOperation:
 3443     case DeleteLeft:
 3444     case DeleteRight:
 3445     case NewLine:
 3446     default: {
 3447         bool bOk = true;
 3448 
 3449         if (op == NoOperation) {
 3450             QString text = e->text();
 3451 
 3452 #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
 3453             if(e->modifiers()&(Qt::MetaModifier|Qt::ControlModifier))
 3454                 break;
 3455 #endif
 3456             if ( text.isEmpty())
 3457                 break;
 3458             QChar c=text.at(0);
 3459             if(!c.isPrint() && !(c.category()==QChar::Other_Format))
 3460                 break;
 3461             if(c==QChar('\t'))
 3462                 break;
 3463         }
 3464 
 3465         bool prot = protectedCursor(m_cursor);
 3466 
 3467         foreach ( const QDocumentCursor& c, m_mirrors )
 3468             prot |= protectedCursor(c);
 3469 
 3470 
 3471         if ( prot ) break;
 3472 
 3473         handled = true;
 3474 
 3475         // clear matches to avoid offsetting and subsequent remanence of matches
 3476         if ( m_definition )
 3477             m_definition->clearMatches(m_doc);
 3478 
 3479         bool macroing = isMirrored() || m_mirrors.count() > 0;
 3480 
 3481         if ( macroing )
 3482             m_doc->beginMacro();
 3483 
 3484         //TODO: blocked key
 3485         if(!m_blockKey)
 3486             processEditOperation(m_cursor, e, op);
 3487         else
 3488             m_blockKey=false;
 3489 
 3490         if ( atPlaceholder() ) // need new evaluation, because edit operation might have changed things
 3491         {
 3492             PlaceHolder& ph = m_placeHolders[m_curPlaceHolder];
 3493 
 3494             QString baseText = ph.cursor.selectedText();
 3495 
 3496             for ( int phm = 0; phm < ph.mirrors.count(); ++phm )
 3497             {
 3498                 QString s = ph.affector ?  ph.affector->affect(e, baseText, m_curPlaceHolder, phm) : baseText;
 3499 
 3500                 ph.mirrors[phm].replaceSelectedText(s);
 3501             }
 3502         }
 3503 
 3504         if ( m_mirrors.isEmpty() )
 3505         {
 3506             // this signal is NOT emitted when cursor mirrors are used ON PURPOSE
 3507             // as it is the "standard" entry point for code completion, which cannot
 3508             // work properly with cursor mirrors (art least not always and not simply)
 3509             if ( handled )
 3510                 emit textEdited(e);
 3511 
 3512         } else {
 3513 
 3514             for ( int i = 0; bOk && (i < m_mirrors.count()); ++i )
 3515                 processEditOperation(m_mirrors[i], e, op);
 3516 
 3517         }
 3518 
 3519         if ( macroing )
 3520             m_doc->endMacro();
 3521 
 3522     }
 3523 
 3524     }
 3525 
 3526 
 3527 
 3528     if ( !handled)
 3529     {
 3530         QAbstractScrollArea::keyPressEvent(e);
 3531 
 3532         foreach ( QEditorInputBindingInterface *b, m_bindings )
 3533             b->postKeyPressEvent(e, this);
 3534         return;
 3535     }
 3536 
 3537     e->accept();
 3538     emitCursorPositionChanged();
 3539     setFlag(CursorOn, true);
 3540     ensureCursorVisible();
 3541 #ifdef  Q_OS_MAC
 3542     //repaintCursor(); // squeeze for a little speed
 3543     if (QApplication::cursorFlashTime() > 0) {
 3544         m_blink.start(QApplication::cursorFlashTime() / 2, this);
 3545     }
 3546 #else
 3547     repaintCursor();
 3548 #endif
 3549 
 3550     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3551         b->postKeyPressEvent(e, this);  
 3552 }
 3553 
 3554 /*!
 3555     \internal
 3556 */
 3557 void QEditor::keyReleaseEvent(QKeyEvent *e)
 3558 {
 3559     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3560         if ( b->keyReleaseEvent(e, this) )
 3561             return;
 3562 
 3563     // currently the editor is doing nothing on KeyRelease
 3564 
 3565     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3566         b->postKeyReleaseEvent(e, this);
 3567 }
 3568 
 3569 /*!
 3570     \internal
 3571 */
 3572 void QEditor::inputMethodEvent(QInputMethodEvent* e)
 3573 {
 3574     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3575         if ( b->inputMethodEvent(e, this) )
 3576             return;
 3577 
 3578     /*
 3579     if ( m_doc->readOnly() )
 3580     {
 3581         e->ignore();
 3582         return;
 3583     }
 3584     */
 3585 //#ifdef Q_OS_MAC
 3586     QString preEdit=e->preeditString();
 3587     if( !preEdit.isEmpty()){
 3588         int i=-1;
 3589         m_cursor.beginEditBlock();
 3590         if(preEditSet){
 3591             i=preEditColumnNumber;
 3592             m_cursor.movePosition(preEditLength,QDocumentCursor::Left,QDocumentCursor::KeepAnchor);
 3593         }else{
 3594             i=qMin(m_cursor.columnNumber(), m_cursor.anchorColumnNumber());
 3595         }
 3596         m_cursor.insertText(preEdit);
 3597         m_cursor.line().addOverlay(QFormatRange(i,preEdit.length(),m_preEditFormat));
 3598         m_cursor.endEditBlock();
 3599         preEditSet=true;
 3600         preEditColumnNumber=i;
 3601         preEditLength=preEdit.length();
 3602         preEditLineNumber=m_cursor.lineNumber();
 3603         updateMicroFocus();
 3604     }
 3605 //#endif
 3606 
 3607     if ( e->commitString().count() ) {
 3608         m_cursor.beginEditBlock();
 3609         if(preEditSet){
 3610             preEditSet=false;
 3611             if(m_cursor.lineNumber()==preEditLineNumber &&
 3612                m_cursor.columnNumber()==preEditColumnNumber+preEditLength
 3613             ){
 3614                 m_cursor.movePosition(preEditLength,QDocumentCursor::Left,QDocumentCursor::KeepAnchor);
 3615                 m_cursor.removeSelectedText();
 3616             }
 3617         }else{
 3618 #ifdef Q_OS_MAC
 3619             // work-around for bug 1723
 3620             // needs to handle chinese punctuation here, so filter only special, non-printable chars
 3621             // see bug 1770
 3622 
 3623             if(e->commitString().count()==1){
 3624                 ushort code=e->commitString().at(0).unicode();
 3625                 if(code<32)
 3626                     return;
 3627             }
 3628 #endif
 3629         }
 3630 
 3631         m_cursor.insertText(e->commitString());
 3632 
 3633         m_cursor.endEditBlock();
 3634     }
 3635 
 3636     if( preEdit.isEmpty() && e->commitString().isEmpty() && preEditSet) {
 3637         m_cursor.beginEditBlock();
 3638         m_cursor.movePosition(preEditLength, QDocumentCursor::Left, QDocumentCursor::KeepAnchor);
 3639         m_cursor.removeSelectedText();
 3640         m_cursor.endEditBlock();
 3641         preEditSet = false;
 3642         preEditLength = 0;
 3643     }
 3644 
 3645     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3646         b->postInputMethodEvent(e, this);
 3647 
 3648     e->accept();
 3649 }
 3650 
 3651 /*!
 3652     \internal
 3653 */
 3654 void QEditor::mouseMoveEvent(QMouseEvent *e)
 3655 {
 3656     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3657         if ( b->mouseMoveEvent(e, this) )
 3658             return;
 3659 
 3660     forever
 3661     {
 3662         if ( !(e->buttons() & Qt::LeftButton) )
 3663             break;
 3664 
 3665         if ( !( flag(MousePressed) || m_multiClickCursor.hasSelection() ) )
 3666             break;
 3667 
 3668         if ( flag(MaybeDrag) )
 3669         {
 3670             m_drag.stop();
 3671 
 3672             if (    (e->globalPos() - m_dragPoint).manhattanLength() >
 3673                     QApplication::startDragDistance()
 3674                 )
 3675                 startDrag();
 3676 
 3677             //emit clearAutoCloseStack();
 3678             break;
 3679         }
 3680 
 3681         repaintCursor();
 3682 
 3683         if( !(viewport()->rect().contains(e->pos())) ) {
 3684             //don't accelerate scrolling just because the mouse is moving
 3685             if( m_autoScroll.isActive() ) break;
 3686             m_autoScroll.start(33, this);
 3687         } else {
 3688             m_autoScroll.stop();
 3689         }
 3690 
 3691         const QPoint mousePos = mapToContents(e->pos());
 3692 
 3693         QDocumentCursor newCursor = cursorForPosition(mousePos);
 3694 
 3695         if ( newCursor.isNull() )
 3696             break;
 3697 
 3698         if ( e->modifiers() & Qt::ControlModifier )
 3699         {
 3700             selectCursorMirrorBlock(newCursor, e->modifiers() & Qt::ShiftModifier);
 3701         } else {
 3702             if (m_multiClickCursor.isValid()) {
 3703                 m_cursor.select(m_multiClickCursor.lineNumber(), m_multiClickCursor.columnNumber(),
 3704                                 newCursor.lineNumber(), newCursor.columnNumber()
 3705                                 );
 3706                 m_cursor.expandSelect(m_multiClickCursor.property("isTripleClick").toBool() ? QDocumentCursor::LineUnderCursor : m_doubleClickSelectionType);
 3707             } else {
 3708                 m_cursor.setSelectionBoundary(newCursor);
 3709             }
 3710         }
 3711 
 3712         ensureCursorVisible();
 3713         //emit clearAutoCloseStack();
 3714         emitCursorPositionChanged();
 3715 
 3716         repaintCursor();
 3717         break;
 3718     }
 3719 
 3720     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3721         b->postMouseMoveEvent(e, this);
 3722     
 3723 }
 3724 
 3725 /*!
 3726     \internal
 3727 */
 3728 void QEditor::mousePressEvent(QMouseEvent *e)
 3729 {
 3730     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3731         if ( b->mousePressEvent(e, this) )
 3732             return;
 3733 
 3734     forever
 3735     {
 3736         if ( !(e->buttons() & Qt::LeftButton) )
 3737             break;
 3738 
 3739         QPoint p = mapToContents(e->pos());
 3740 
 3741         setFlag(MousePressed, true);
 3742         setFlag(MaybeDrag, false);
 3743 
 3744         repaintCursor();
 3745 
 3746         if ( m_click.isActive() &&
 3747             (( e->globalPos() - m_clickPoint).manhattanLength() <
 3748                 QApplication::startDragDistance() ))
 3749         {
 3750             m_multiClickCursor = m_cursor;
 3751             m_multiClickCursor.clearSelection();  // just store the click position
 3752             m_multiClickCursor.setProperty("isTripleClick", true);
 3753             m_cursor.select(m_tripleClickSelectionType);
 3754             m_click.stop();
 3755         } else {
 3756             QDocumentCursor cursor = cursorForPosition(p);
 3757 
 3758             if (!cursor.isValid() && !cursor.line().isValid() && cursor.lineNumber() > 0) {
 3759                 // clicked beyond end of doc: lineNumber is last line + 1
 3760                 // move cursor to end of doc.
 3761                 cursor.setLineNumber(cursor.lineNumber() - 1);
 3762                 cursor.movePosition(1, QDocumentCursor::EndOfLine);
 3763             }
 3764 
 3765             if ( cursor.isNull() )
 3766                 break;
 3767 
 3768             if ( e->modifiers() == Qt::ShiftModifier )
 3769             {
 3770                 clearCursorMirrors();
 3771                 m_cursor.setSelectionBoundary(cursor);
 3772             } else if ( e->modifiers() & Qt::ControlModifier && ((e->modifiers() & Qt::ShiftModifier) || (e->modifiers() & Qt::AltModifier)) ) {
 3773                 //m_mirrors << cursor;
 3774                 if ( e->modifiers() & Qt::ShiftModifier )
 3775                 {
 3776                     selectCursorMirrorBlock(cursor, true);
 3777                 } else if ( (e->modifiers() & Qt::AltModifier) )
 3778                 {
 3779                     //remove existing mirrors if one is at the same position
 3780                     if ( m_cursor.isWithinSelection(cursor) || m_cursor.equal(cursor) )
 3781                     {
 3782                         if ( m_mirrors.size() )
 3783                         {
 3784                             m_cursor = m_mirrors.takeFirst();
 3785                             if (m_cursorMirrorBlockAnchor >= 0)
 3786                                 m_cursorMirrorBlockAnchor--;
 3787                         } else
 3788                             setCursor(cursor);
 3789                         break;
 3790                     } else {
 3791                         bool removedExisting = false;
 3792                         for ( int i = 0; i < m_mirrors.size(); i++ )
 3793                             if ( m_mirrors[i].isWithinSelection(cursor) || m_mirrors[i].equal(cursor) )
 3794                             {
 3795                                 m_mirrors.removeAt(i);
 3796                                 removedExisting = true;
 3797                                 if (m_cursorMirrorBlockAnchor >= i)
 3798                                     m_cursorMirrorBlockAnchor--;
 3799                                 break;
 3800                             }
 3801                         if ( removedExisting ) break;
 3802                     }
 3803                     m_cursorMirrorBlockAnchor = m_mirrors.size();
 3804                     addCursorMirror(cursor);
 3805                 }
 3806             } else {
 3807 
 3808                 const QDocumentCursor& cur = m_cursor;
 3809 
 3810                 if ( m_cursor.hasSelection() )
 3811                 {
 3812                     bool inSel = cur.isWithinSelection(cursor);
 3813 
 3814                     if ( !inSel )
 3815                     {
 3816                         foreach ( const QDocumentCursor& m, m_mirrors )
 3817                         {
 3818                             inSel = m.isWithinSelection(cursor);
 3819 
 3820                             if ( inSel )
 3821                                 break;
 3822                         }
 3823                     }
 3824 
 3825                     if ( inSel && flag(AllowDragAndDrop) )
 3826                     {
 3827                         setFlag(MaybeDrag, true);
 3828 
 3829                         m_dragPoint = e->globalPos();
 3830                         m_drag.start(QApplication::startDragTime(), this);
 3831 
 3832                         break;
 3833                     }
 3834                 }
 3835 
 3836                 m_multiClickCursor = QDocumentCursor();
 3837                 cutBuffer.clear();
 3838                 setCursor(cursor);
 3839                 break;
 3840             }
 3841         }
 3842 
 3843         ensureCursorVisible();
 3844         //emit clearAutoCloseStack();
 3845         cutBuffer.clear();
 3846         emitCursorPositionChanged();
 3847         repaintCursor();
 3848         break;
 3849     }
 3850 
 3851     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3852         b->postMousePressEvent(e, this);
 3853     
 3854 }
 3855 
 3856 /*!
 3857     \internal
 3858 */
 3859 void QEditor::mouseReleaseEvent(QMouseEvent *e)
 3860 {
 3861     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3862         if ( b->mouseReleaseEvent(e, this) )
 3863             return;
 3864 
 3865     m_autoScroll.stop( );
 3866     repaintCursor();
 3867 
 3868     if ( flag(MaybeDrag) )
 3869     {
 3870         setFlag(MousePressed, false);
 3871         setCursorPosition(mapToContents(e->pos()));
 3872 
 3873             m_cursor.clearSelection();
 3874     }
 3875 
 3876     if ( flag(MousePressed) )
 3877     {
 3878         setFlag(MousePressed, false);
 3879 
 3880         setClipboardSelection();
 3881     } else if ( e->button() == Qt::MidButton
 3882                 && QApplication::clipboard()->supportsSelection()) {
 3883         setCursorPosition(mapToContents(e->pos()));
 3884         //setCursorPosition(viewport()->mapFromGlobal(e->globalPos()));
 3885 
 3886         const QMimeData *md = QApplication::clipboard()
 3887                                 ->mimeData(QClipboard::Selection);
 3888 
 3889         if ( md )
 3890             insertFromMimeData(md);
 3891     }
 3892 
 3893     repaintCursor();
 3894 
 3895     if ( m_drag.isActive() )
 3896         m_drag.stop();
 3897 
 3898     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3899         b->postMouseReleaseEvent(e, this);
 3900     
 3901 }
 3902 
 3903 /*!
 3904     \internal
 3905 */
 3906 void QEditor::mouseDoubleClickEvent(QMouseEvent *e)
 3907 {
 3908     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3909         if ( b->mouseDoubleClickEvent(e, this) )
 3910             return;
 3911 
 3912     forever
 3913     {
 3914         if ( e->button() != Qt::LeftButton )
 3915         {
 3916             e->ignore();
 3917             break;
 3918         }
 3919 
 3920         setFlag(MousePressed, true);
 3921         setFlag(MaybeDrag, false);
 3922 
 3923         repaintCursor();
 3924         clearCursorMirrors();
 3925         setCursorPosition(mapToContents(e->pos()));
 3926 
 3927         m_multiClickCursor = m_cursor;
 3928         m_multiClickCursor.clearSelection();  // just store the click position
 3929         m_multiClickCursor.setProperty("isTripleClick", false);
 3930 
 3931         if ( m_cursor.isValid() )
 3932         {
 3933             m_cursor.select(m_doubleClickSelectionType);
 3934 
 3935             setClipboardSelection();
 3936             //emit clearAutoCloseStack();
 3937             emitCursorPositionChanged();
 3938             emitWordDoubleClicked();
 3939 
 3940             repaintCursor();
 3941         } else {
 3942             //qDebug("invalid cursor");
 3943         }
 3944 
 3945         m_clickPoint = e->globalPos();
 3946         m_click.start(qApp->doubleClickInterval(), this);
 3947         break;
 3948     }
 3949 
 3950     foreach ( QEditorInputBindingInterface *b, m_bindings )
 3951         b->postMouseDoubleClickEvent(e, this);
 3952     
 3953 }
 3954 
 3955 /*!
 3956     \internal
 3957 */
 3958 void QEditor::dragEnterEvent(QDragEnterEvent *e)
 3959 {
 3960     if (
 3961             e
 3962         &&
 3963             e->mimeData()
 3964         &&
 3965             (
 3966                 e->mimeData()->hasFormat("text/plain")
 3967             ||
 3968                 e->mimeData()->hasFormat("text/html")
 3969             )
 3970         &&
 3971             !e->mimeData()->hasFormat("text/uri-list")
 3972         )
 3973         e->acceptProposedAction();
 3974     else
 3975         return;
 3976 
 3977     m_dragAndDrop = QDocumentCursor();
 3978 }
 3979 
 3980 /*!
 3981     \internal
 3982 */
 3983 void QEditor::dragLeaveEvent(QDragLeaveEvent *)
 3984 {
 3985     const QRect crect = cursorRect(m_dragAndDrop);
 3986     m_dragAndDrop = QDocumentCursor();
 3987 
 3988     if ( crect.isValid() )
 3989         viewport()->update(crect);
 3990 
 3991 }
 3992 
 3993 /*!
 3994     \internal
 3995 */
 3996 void QEditor::dragMoveEvent(QDragMoveEvent *e)
 3997 {
 3998     if (
 3999             e
 4000         &&
 4001             e->mimeData()
 4002         &&
 4003             (
 4004                 e->mimeData()->hasFormat("text/plain")
 4005             ||
 4006                 e->mimeData()->hasFormat("text/html")
 4007             )
 4008         &&
 4009             !e->mimeData()->hasFormat("text/uri-list")
 4010         )
 4011         e->acceptProposedAction();
 4012     else
 4013         return;
 4014 
 4015     QDocumentCursor c = cursorForPosition(mapToContents(e->pos()));
 4016 
 4017     if ( c.isValid() )
 4018     {
 4019         QRect crect = cursorRect(m_dragAndDrop);
 4020 
 4021         if ( crect.isValid() )
 4022             viewport()->update(crect);
 4023 
 4024         m_dragAndDrop = c;
 4025 
 4026         // workaround for dragging
 4027         // TODO 1: Exchange the use of KeepSurrounding for a check
 4028         //         int lineSpacing = this->document()->getLineSpacing();
 4029         //         if (e->pos().y() < lineSpacing || e->pos().y() > (height() - 2*lineSpacing))
 4030         // TODO 2: The event is only fired when the mouse is moved, but scrolling should continue
 4031         //         while dragging if the mouse is close to the border and not moved further
 4032         //         (maybe even at different speeds depending on the distance to the border
 4033         int backup = m_cursorSurroundingLines;
 4034         m_cursorSurroundingLines = 1;  // restricting to 1 & KeepSurrounding is almost like scrolling
 4035         ensureCursorVisible(m_dragAndDrop, KeepSurrounding);
 4036         m_cursorSurroundingLines = backup;
 4037 
 4038         crect = cursorRect(m_dragAndDrop);
 4039         viewport()->update(crect);
 4040     }
 4041 
 4042     //e->acceptProposedAction();
 4043 }
 4044 
 4045 /*!
 4046     \internal
 4047 */
 4048 void QEditor::dropEvent(QDropEvent *e)
 4049 {
 4050     m_dragAndDrop = QDocumentCursor();
 4051 
 4052     QDocumentCursor c(cursorForPosition(mapToContents(e->pos())));
 4053 
 4054     if ( (e->source() == this) && (m_cursor.isWithinSelection(c)) ){
 4055         e->setDropAction(Qt::CopyAction);
 4056         e->accept();
 4057         return;
 4058     }
 4059 
 4060     if (
 4061             e
 4062         &&
 4063             e->mimeData()
 4064         &&
 4065             (
 4066                 e->mimeData()->hasFormat("text/plain")
 4067             ||
 4068                 e->mimeData()->hasFormat("text/html")
 4069             )
 4070         &&
 4071             !e->mimeData()->hasFormat("text/uri-list")
 4072         )
 4073     {
 4074         e->acceptProposedAction();
 4075     } else {
 4076         return;
 4077     }
 4078 
 4079     //repaintSelection();
 4080 
 4081     //m_doc->beginMacro();
 4082     //m_cursor.beginEditBlock();
 4083     QDocumentCursor insertCursor = cursorForPosition(mapToContents(e->pos()));
 4084     
 4085     if (
 4086             (e->dropAction() == Qt::MoveAction)
 4087         &&
 4088             (
 4089                 (e->source() == this)
 4090             ||
 4091                 (e->source() == viewport())
 4092             )
 4093         )
 4094     {
 4095         insertCursor.setAutoUpdated(true);
 4096         m_doc->beginMacro();
 4097         
 4098         m_cursor.removeSelectedText();
 4099         cursorMirrorsRemoveSelectedText();
 4100 
 4101         clearCursorMirrors();
 4102         m_cursor=insertCursor;//.moveTo(cursorForPosition(mapToContents(e->pos())));
 4103         insertFromMimeData(e->mimeData());
 4104 
 4105         m_doc->endMacro();
 4106         insertCursor.setAutoUpdated(false);
 4107     } else {
 4108         //qDebug("action : %i", e->dropAction());
 4109         m_cursor.clearSelection();
 4110         clearCursorMirrors();
 4111         m_cursor=insertCursor;//.moveTo(cursorForPosition(mapToContents(e->pos())));
 4112         insertFromMimeData(e->mimeData());
 4113     }
 4114 
 4115     //m_cursor.endEditBlock();
 4116 
 4117     //m_doc->endMacro();
 4118 }
 4119 
 4120 /*!
 4121     \internal
 4122 */
 4123 void QEditor::changeEvent(QEvent *e)
 4124 {
 4125     QAbstractScrollArea::changeEvent(e);
 4126 
 4127     if (
 4128             e->type() == QEvent::ApplicationFontChange
 4129         ||
 4130             e->type() == QEvent::FontChange
 4131         )
 4132     {
 4133         if ( !m_doc )
 4134             return;
 4135 
 4136         m_doc->setBaseFont(font());
 4137         //setTabStop(iTab);
 4138 
 4139     }
 4140 }
 4141 
 4142 /*!
 4143     \internal
 4144 */
 4145 void QEditor::showEvent(QShowEvent *e)
 4146 {
 4147     QCodeEdit *ce = QCodeEdit::manager(this);
 4148 
 4149     if ( ce )
 4150         ce->panelLayout()->update();
 4151 
 4152     QAbstractScrollArea::showEvent(e);
 4153     
 4154     if ( flag(HardLineWrap)||flag(LineWidthConstraint) )
 4155         m_doc->setWidthConstraint( m_LineWidth > 0 ? m_LineWidth : wrapWidth() );
 4156     else if ( flag(LineWrap) )
 4157         m_doc->setWidthConstraint( wrapWidth() );
 4158     else
 4159         m_doc->clearWidthConstraint();
 4160 
 4161     if ( flag(EnsureVisible) )
 4162         ensureCursorVisible();
 4163     
 4164 }
 4165 
 4166 /*!
 4167     \internal
 4168     \brief Zoom in/out upon ctrl+wheel
 4169 */
 4170 void QEditor::wheelEvent(QWheelEvent *e)
 4171 {
 4172     if ( e->modifiers() & Qt::ControlModifier && flag(MouseWheelZoom))
 4173     {
 4174         const int delta = e->angleDelta().y();
 4175 
 4176         if ( delta > 0 )
 4177             zoom(1);
 4178         else if ( delta < 0 )
 4179             zoom(-1);
 4180 
 4181         //viewport()->update();
 4182 
 4183         return;
 4184     }
 4185 
 4186     QAbstractScrollArea::wheelEvent(e);
 4187     updateMicroFocus();
 4188     //viewport()->update();
 4189 }
 4190 
 4191 /*!
 4192     \internal
 4193 */
 4194 void QEditor::resizeEvent(QResizeEvent *)
 4195 {
 4196     const QSize viewportSize = viewport()->size();
 4197 
 4198     if ( flag(HardLineWrap)||flag(LineWidthConstraint) ){
 4199         horizontalScrollBar()->setMaximum(qMax(0, m_LineWidth - viewportSize.width()));
 4200         horizontalScrollBar()->setPageStep(viewportSize.width());
 4201     } else if ( flag(LineWrap) )
 4202     {
 4203         //qDebug("resize t (2) : wrapping to %i", viewport()->width());
 4204 
 4205         m_doc->setWidthConstraint(wrapWidth());
 4206     } else {
 4207         horizontalScrollBar()->setMaximum(qMax(0, m_doc->width() - viewportSize.width()));
 4208         horizontalScrollBar()->setPageStep(viewportSize.width());
 4209     }
 4210 
 4211     setVerticalScrollBarMaximum();
 4212 
 4213     emit visibleLinesChanged();
 4214     //qDebug("page step : %i", viewportSize.height() / ls);
 4215 
 4216     //if ( isCursorVisible() && flag(LineWrap) )
 4217     //  ensureCursorVisible();
 4218 }
 4219 
 4220 /*!
 4221     \internal
 4222 */
 4223 void QEditor::focusInEvent(QFocusEvent *e)
 4224 {
 4225     setFlag(CursorOn, true);
 4226     if (QApplication::cursorFlashTime() > 0) {
 4227         m_blink.start(QApplication::cursorFlashTime() / 2, this);
 4228     }
 4229     //ensureCursorVisible();
 4230     emit focusReceived();
 4231 
 4232     QAbstractScrollArea::focusInEvent(e);
 4233 }
 4234 
 4235 /*!
 4236     \internal
 4237 */
 4238 void QEditor::focusOutEvent(QFocusEvent *e)
 4239 {
 4240     setFlag(CursorOn, false);
 4241     m_blink.stop();
 4242 
 4243     QAbstractScrollArea::focusOutEvent(e);
 4244 }
 4245 
 4246 /*!
 4247     \brief Context menu event
 4248 
 4249     All the (managed) actions added to the editor are showed in it by default.
 4250 */
 4251 void QEditor::contextMenuEvent(QContextMenuEvent *e)
 4252 {
 4253     foreach ( QEditorInputBindingInterface *b, m_bindings )
 4254         if ( b->contextMenuEvent(e, this) )
 4255             return;
 4256 
 4257     if ( !pMenu )
 4258     {
 4259         e->ignore();
 4260         return;
 4261     }
 4262 
 4263     e->accept();
 4264 
 4265     pMenu->exec(e->globalPos());
 4266 }
 4267 
 4268 /*!
 4269     \brief Close event
 4270 
 4271     When build with qmdilib support (e.g in Edyuk) this check for
 4272     modifications and a dialog pops up to offer various options
 4273     (like saving, discarding or canceling)
 4274 */
 4275 void QEditor::closeEvent(QCloseEvent *e)
 4276 {
 4277     #ifdef _QMDI_
 4278     bool bOK = true;
 4279 
 4280     if ( isContentModified() )
 4281         bOK = server()->maybeSave(this);
 4282 
 4283     if ( bOK )
 4284     {
 4285         e->accept();
 4286         notifyDeletion();
 4287     } else {
 4288         e->ignore();
 4289     }
 4290     #else
 4291     QAbstractScrollArea::closeEvent(e);
 4292     #endif
 4293 }
 4294 
 4295 #ifndef _QMDI_
 4296 /*!
 4297     \return Whether the document has been modified.
 4298 */
 4299 bool QEditor::isContentModified() const
 4300 {
 4301     return m_doc ? !m_doc->isClean() : false;
 4302 }
 4303 #endif
 4304 
 4305 /*!
 4306     \brief Notify that the content is clean (modifications undone or document saved)
 4307 
 4308     \note Don't mess with this. The document knows better.
 4309 */
 4310 void QEditor::setContentClean(bool y)
 4311 {
 4312     setContentModified(!y);
 4313 }
 4314 
 4315 /*!
 4316     \brief Notify that the content has been modified
 4317 
 4318     \note Don't mess with this. The document knows better.
 4319 */
 4320 void QEditor::setContentModified(bool y)
 4321 {
 4322     #ifdef _QMDI_
 4323     qmdiClient::setContentModified(y);
 4324     #endif
 4325 
 4326     setWindowModified(y);
 4327     emit contentModified(y);
 4328 }
 4329 
 4330 /*!
 4331     \brief Changes the file name
 4332 
 4333     This method does not affect files on disk (no save/load/move occurs)
 4334 */
 4335 void QEditor::setFileName(const QString& f)
 4336 {
 4337     REQUIRE(m_doc);
 4338     
 4339     QString prev = fileName();
 4340 
 4341     if ( f == prev )
 4342         return;
 4343 
 4344     watcher()->removeWatch(QString(), this);
 4345 
 4346     m_doc->setFileName_DONOTCALLTHIS(f);
 4347 
 4348     if ( fileName().count() )
 4349         watcher()->addWatch(fileName(), this);
 4350 
 4351     setTitle(name().count() ? name() : "untitled");
 4352 }
 4353 
 4354 /*!
 4355     \brief Set the title of the widget
 4356 
 4357     Take care of adding a "[*]" prefix so that document changes are visible
 4358     on title bars.
 4359 */
 4360 void QEditor::setTitle(const QString& title)
 4361 {
 4362     QString s(title);
 4363 
 4364     if ( !s.contains("[*]") )
 4365         s.prepend("[*]");
 4366 
 4367     setWindowTitle(s);
 4368     emit titleChanged(title);
 4369 }
 4370 
 4371 /*!
 4372     \return The name of the file being edited (without its path)
 4373 */
 4374 QString QEditor::name() const
 4375 {
 4376     REQUIRE_RET(m_doc,"");
 4377     return m_doc->getName();
 4378 }
 4379 
 4380 /*!
 4381     \return The full filename of the file being edited
 4382 */
 4383 QString QEditor::fileName() const
 4384 {
 4385     REQUIRE_RET(m_doc,"");
 4386     return m_doc->getFileName();
 4387 }
 4388 QFileInfo QEditor::fileInfo() const{
 4389     REQUIRE_RET(m_doc,QFileInfo());
 4390     return m_doc->getFileInfo();
 4391 }
 4392 
 4393 bool QEditor::isReadOnly() const
 4394 {
 4395     REQUIRE_RET(m_doc, true);
 4396     return m_doc->isReadOnly();
 4397 }
 4398 
 4399 void QEditor::setReadOnly(bool b)
 4400 {
 4401     REQUIRE(m_doc);
 4402     m_doc->setReadOnly(b);
 4403     emit readOnlyChanged(b);
 4404 }
 4405 
 4406 
 4407 /*!
 4408     \brief Prevent tab key press to be considered as widget navigation
 4409 */
 4410 bool QEditor::focusNextPrevChild(bool)
 4411 {
 4412     // make sure we catch tabs :)
 4413 
 4414     return false;
 4415 }
 4416 
 4417 void QEditor::registerEditOperation(const EditOperation& op){
 4418     m_registeredOperations << op;
 4419 }
 4420 
 4421 void QEditor::addEditOperation(const EditOperation& op, const Qt::KeyboardModifiers& modifiers, const Qt::Key& key){
 4422     QKeySequence qkey=QKeySequence(((int)modifiers & (Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier | Qt::MetaModifier)) | (int)key);
 4423     m_registeredKeys.insert(qkey.toString(), op);
 4424     m_registeredOperations << op;
 4425 }
 4426 
 4427 void QEditor::addEditOperation(const EditOperation& op, const QKeySequence::StandardKey& key){
 4428     QList<QKeySequence> sc = QKeySequence::keyBindings(key);
 4429     foreach (const QKeySequence& seq, sc){
 4430         if (!seq.count()) continue;
 4431         addEditOperation(op, (Qt::KeyboardModifiers)(seq[0] & Qt::KeyboardModifierMask), (Qt::Key)(seq[0] & ~Qt::KeyboardModifierMask));
 4432     }
 4433 }
 4434 
 4435 QEditor::EditOperation QEditor::getEditOperation(const Qt::KeyboardModifiers& modifiers, const Qt::Key& key){
 4436     QKeySequence qkey=QKeySequence(((int)modifiers & (Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier | Qt::MetaModifier)) | (int)key);
 4437     EditOperation op = (EditOperation) m_registeredKeys.value(qkey.toString() , NoOperation);
 4438     static const int MAX_JUMP_TO_PLACEHOLDER = 5;
 4439     switch (op){
 4440     case IndentSelection: case UnindentSelection:
 4441         //if (!m_cursor.hasSelection()) op = NoOperation; // works also if the cursor has no selection !
 4442         break;
 4443     case NextPlaceHolder: case PreviousPlaceHolder:
 4444         if (m_placeHolders.isEmpty()) op = NoOperation;
 4445         break;
 4446     case NextPlaceHolderOrWord:
 4447         op = CursorWordRight;
 4448         foreach (const PlaceHolder& ph, m_placeHolders)
 4449             if (ph.cursor.selectionStart() > m_cursor.selectionEnd() && !ph.autoOverride &&
 4450                 ph.cursor.selectionStart().lineNumber() - m_cursor.selectionEnd().lineNumber() <= MAX_JUMP_TO_PLACEHOLDER  ){
 4451                 op = NextPlaceHolder;
 4452                 break;
 4453             }
 4454         break;
 4455     case PreviousPlaceHolderOrWord:
 4456         op = CursorWordLeft;
 4457         foreach (const PlaceHolder& ph, m_placeHolders)
 4458             if (ph.cursor.selectionEnd() < m_cursor.selectionStart() && !ph.autoOverride &&
 4459                 m_cursor.selectionStart().lineNumber() - ph.cursor.selectionEnd().lineNumber() <= MAX_JUMP_TO_PLACEHOLDER ){
 4460                 op = PreviousPlaceHolder;
 4461                 break;
 4462             }
 4463         break;
 4464     case NextPlaceHolderOrChar:
 4465         op = CursorRight;
 4466         foreach (const PlaceHolder& ph, m_placeHolders)
 4467             if (ph.cursor.selectionStart() > m_cursor.selectionEnd() && !ph.autoOverride &&
 4468                 ph.cursor.selectionStart().lineNumber() - m_cursor.selectionEnd().lineNumber() <= MAX_JUMP_TO_PLACEHOLDER  ){
 4469                 op = NextPlaceHolder;
 4470                 break;
 4471             }
 4472         break;
 4473     case PreviousPlaceHolderOrChar:
 4474         op = CursorLeft;
 4475         foreach (const PlaceHolder& ph, m_placeHolders)
 4476             if (ph.cursor.selectionEnd() < m_cursor.selectionStart() && !ph.autoOverride &&
 4477                 m_cursor.selectionStart().lineNumber() - ph.cursor.selectionEnd().lineNumber() <= MAX_JUMP_TO_PLACEHOLDER ){
 4478                 op = PreviousPlaceHolder;
 4479                 break;
 4480             }
 4481         break;
 4482     default:;
 4483     }
 4484     return op;
 4485 }
 4486 
 4487 void QEditor::setEditOperations(const QHash<QString, int> &newOptions, bool mergeWithDefault){
 4488     if (!mergeWithDefault) {
 4489         m_registeredKeys = newOptions;
 4490         return;
 4491     }
 4492     m_registeredKeys = m_registeredDefaultKeys;
 4493     QHash<QString, int>::const_iterator i = newOptions.begin();
 4494     while (i != newOptions.constEnd()) {
 4495         m_registeredKeys.insert(i.key(),i.value());
 4496         ++i;
 4497     }
 4498 }
 4499 
 4500 QSet<int> QEditor::getAvailableOperations(){
 4501     return m_registeredOperations;
 4502 }
 4503 
 4504 QHash<QString, int> QEditor::getEditOperations(bool excludeDefault){
 4505     if (!m_defaultKeysSet) { //todo: thread safe lock
 4506         m_defaultKeysSet = true;
 4507 
 4508     addEditOperation(CursorUp, QKeySequence::MoveToPreviousLine);
 4509     addEditOperation(CursorDown, QKeySequence::MoveToNextLine);
 4510     addEditOperation(SelectCursorUp, QKeySequence::SelectPreviousLine);
 4511     addEditOperation(SelectCursorDown, QKeySequence::SelectNextLine);
 4512 
 4513     addEditOperation(CursorLeft, QKeySequence::MoveToPreviousChar);
 4514     addEditOperation(CursorRight, QKeySequence::MoveToNextChar);
 4515     addEditOperation(SelectCursorLeft, QKeySequence::SelectPreviousChar);
 4516     addEditOperation(SelectCursorRight, QKeySequence::SelectNextChar);
 4517 
 4518     addEditOperation(CursorStartOfLine, QKeySequence::MoveToStartOfLine);
 4519     addEditOperation(CursorEndOfLine, QKeySequence::MoveToEndOfLine);
 4520     addEditOperation(SelectCursorStartOfLine, QKeySequence::SelectStartOfLine);
 4521     addEditOperation(SelectCursorEndOfLine, QKeySequence::SelectEndOfLine);
 4522 
 4523     addEditOperation(CursorStartOfDocument, QKeySequence::MoveToStartOfDocument);
 4524     addEditOperation(CursorEndOfDocument, QKeySequence::MoveToEndOfDocument);
 4525     addEditOperation(SelectCursorStartOfDocument, QKeySequence::SelectStartOfDocument);
 4526     addEditOperation(SelectCursorEndOfDocument, QKeySequence::SelectEndOfDocument);
 4527 
 4528     #ifndef Q_OS_MAC  // Use the default Windows bindings.
 4529         addEditOperation(CursorWordLeft, Qt::ControlModifier, Qt::Key_Left);
 4530         addEditOperation(CursorWordRight, Qt::ControlModifier, Qt::Key_Right);
 4531         addEditOperation(SelectCursorWordLeft, Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_Left);
 4532         addEditOperation(SelectCursorWordRight, Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_Right);
 4533 
 4534         addEditOperation(CursorStartOfDocument, Qt::ControlModifier, Qt::Key_Home);
 4535         addEditOperation(CursorEndOfDocument, Qt::ControlModifier, Qt::Key_End);
 4536         addEditOperation(SelectCursorStartOfDocument, Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_Home);
 4537         addEditOperation(SelectCursorEndOfDocument, Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_End);
 4538     #else
 4539     /*
 4540         Except for pageup and pagedown, Mac OS X has very different behavior, we
 4541         don't do it all, but here's the breakdown:
 4542 
 4543         Shift still works as an anchor, but only one of the other keys can be dow
 4544         Ctrl (Command), Alt (Option), or Meta (Control).
 4545 
 4546         Command/Control + Left/Right -- Move to left or right of the line
 4547                         + Up/Down -- Move to top bottom of the file.
 4548                         (Control doesn't move the cursor)
 4549 
 4550         Option  + Left/Right -- Move one word Left/right.
 4551                 + Up/Down  -- Begin/End of Paragraph.
 4552 
 4553         Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
 4554 
 4555         There can be only one modifier (+ shift), but we also need to make sure
 4556         that we have a "move key" pressed before we reject it.
 4557     */
 4558         /* whyever fill the list with "invalid" commands ?????
 4559         QList<Qt::KeyboardModifiers> modifierPairs;
 4560         modifierPairs <<  (Qt::ControlModifier | Qt::AltModifier) << (Qt::ControlModifier | Qt::MetaModifier) << (Qt::AltModifier | Qt::MetaModifier);
 4561         QList<Qt::Key> movementKeys;
 4562         movementKeys << Qt::Key_Up << Qt::Key_Down << Qt::Key_Left << Qt::Key_Right;
 4563         foreach (Qt::KeyboardModifiers mod, modifierPairs)
 4564             foreach (Qt::Key key, movementKeys)
 4565             addEditOperation(Invalid, mod , key);
 4566         modifierPairs <<  Qt::ControlModifier << Qt::AltModifier << Qt::MetaModifier;
 4567         modifierPairs <<  (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier);
 4568         movementKeys = QList<Qt::Key>() << Qt::Key_Home << Qt::Key_End;
 4569         foreach (Qt::KeyboardModifiers mod, modifierPairs)
 4570             foreach (Qt::Key key, movementKeys)
 4571             addEditOperation(Invalid, mod , key);
 4572        */
 4573 
 4574         addEditOperation(CursorStartOfLine, Qt::AltModifier, Qt::Key_Up);
 4575         addEditOperation(CursorEndOfLine, Qt::AltModifier, Qt::Key_Down);
 4576         addEditOperation(SelectCursorStartOfLine, Qt::ShiftModifier | Qt::AltModifier, Qt::Key_Up);
 4577         addEditOperation(SelectCursorEndOfLine, Qt::ShiftModifier | Qt::AltModifier, Qt::Key_Down);
 4578 
 4579         addEditOperation(CursorStartOfLine, Qt::ControlModifier, Qt::Key_Left);
 4580         addEditOperation(CursorEndOfLine, Qt::ControlModifier, Qt::Key_Right);
 4581         addEditOperation(SelectCursorStartOfLine, Qt::ShiftModifier | Qt::ControlModifier, Qt::Key_Left);
 4582         addEditOperation(SelectCursorEndOfLine, Qt::ShiftModifier | Qt::ControlModifier, Qt::Key_Right);
 4583 
 4584         addEditOperation(CursorStartOfLine, Qt::MetaModifier, Qt::Key_Left);
 4585         addEditOperation(CursorEndOfLine, Qt::MetaModifier, Qt::Key_Right);
 4586         addEditOperation(SelectCursorStartOfLine, Qt::ShiftModifier | Qt::MetaModifier, Qt::Key_Left);
 4587         addEditOperation(SelectCursorEndOfLine, Qt::ShiftModifier | Qt::MetaModifier, Qt::Key_Right);
 4588 
 4589         addEditOperation(CursorWordLeft, Qt::AltModifier, Qt::Key_Left);
 4590         addEditOperation(CursorWordRight, Qt::AltModifier, Qt::Key_Right);
 4591         addEditOperation(SelectCursorWordLeft, Qt::ShiftModifier | Qt::AltModifier, Qt::Key_Left);
 4592         addEditOperation(SelectCursorWordRight, Qt::ShiftModifier | Qt::AltModifier, Qt::Key_Right);
 4593     #endif
 4594 
 4595         addEditOperation(CursorPageUp, Qt::NoModifier, Qt::Key_PageUp);
 4596         addEditOperation(SelectPageUp, Qt::ShiftModifier, Qt::Key_PageUp);
 4597         addEditOperation(CursorPageDown, Qt::NoModifier, Qt::Key_PageDown);
 4598         addEditOperation(SelectPageDown, Qt::ShiftModifier, Qt::Key_PageDown);
 4599 
 4600         addEditOperation(DeleteLeft, Qt::NoModifier, Qt::Key_Backspace);
 4601         addEditOperation(DeleteRight, QKeySequence::Delete);
 4602     #ifdef Q_OS_MAC
 4603         addEditOperation(DeleteRight, Qt::ShiftModifier, Qt::Key_Backspace);
 4604     #else
 4605         addEditOperation(DeleteLeft, Qt::ShiftModifier, Qt::Key_Backspace);
 4606     #endif
 4607 
 4608     #ifdef Q_OS_MAC
 4609         addEditOperation(DeleteLeftWord, Qt::AltModifier, Qt::Key_Backspace);
 4610         addEditOperation(DeleteRightWord, Qt::AltModifier, Qt::Key_Delete);
 4611     #else
 4612         addEditOperation(DeleteLeftWord, Qt::ControlModifier, Qt::Key_Backspace);
 4613         addEditOperation(DeleteRightWord, Qt::ControlModifier, Qt::Key_Delete);
 4614     #endif
 4615 
 4616         addEditOperation(NewLine, Qt::NoModifier, Qt::Key_Enter);
 4617         addEditOperation(NewLine, Qt::NoModifier, Qt::Key_Return);
 4618 
 4619         addEditOperation(ChangeOverwrite, Qt::NoModifier, Qt::Key_Insert);
 4620 
 4621         addEditOperation(CreateMirrorUp, Qt::AltModifier | Qt::ControlModifier, Qt::Key_Up);
 4622         addEditOperation(CreateMirrorDown, Qt::AltModifier | Qt::ControlModifier, Qt::Key_Down);
 4623 
 4624         registerEditOperation(NextPlaceHolder);
 4625         registerEditOperation(PreviousPlaceHolder);
 4626     #ifdef Q_OS_MAC
 4627         registerEditOperation(NextPlaceHolderOrWord);
 4628         registerEditOperation(PreviousPlaceHolderOrWord);
 4629     #else
 4630         addEditOperation(NextPlaceHolderOrWord, Qt::ControlModifier, Qt::Key_Right);
 4631         addEditOperation(PreviousPlaceHolderOrWord, Qt::ControlModifier, Qt::Key_Left);
 4632     #endif
 4633         registerEditOperation(NextPlaceHolderOrChar);
 4634         registerEditOperation(PreviousPlaceHolderOrChar);
 4635 
 4636         addEditOperation(TabOrIndentSelection, Qt::NoModifier, Qt::Key_Tab);
 4637         addEditOperation(UnindentSelection, Qt::ShiftModifier, Qt::Key_Backtab);
 4638 
 4639         addEditOperation(Undo, QKeySequence::Undo);
 4640         addEditOperation(Redo, QKeySequence::Redo);
 4641         addEditOperation(Copy, QKeySequence::Copy);
 4642         addEditOperation(Paste, QKeySequence::Paste);
 4643         addEditOperation(Cut, QKeySequence::Cut);
 4644         addEditOperation(Print, QKeySequence::Print);
 4645         addEditOperation(SelectAll, QKeySequence::SelectAll);
 4646         addEditOperation(Find, QKeySequence::Find);
 4647         addEditOperation(FindNext, QKeySequence::FindNext);
 4648         addEditOperation(FindPrevious, QKeySequence::FindPrevious);
 4649         addEditOperation(Replace, QKeySequence::Replace);
 4650 
 4651         m_registeredDefaultKeys = m_registeredKeys;
 4652     }
 4653     if (!excludeDefault) return m_registeredKeys;
 4654     else {
 4655         QHash<QString, int> result = m_registeredKeys;
 4656         foreach(QString key,m_registeredDefaultKeys.keys()){
 4657                 if(result.contains(key)){
 4658                     if(result.value(key)==m_registeredDefaultKeys.value(key)){
 4659                         result.remove(key);
 4660                     }
 4661                 }else{
 4662                     // add for removal
 4663                     result.insert("#"+key,m_registeredDefaultKeys.value(key));
 4664                 }
 4665         }
 4666         return result;
 4667     }
 4668 }
 4669 
 4670 QString QEditor::translateEditOperation(const EditOperation& op){
 4671     switch (op){
 4672     case NoOperation: return tr("None");
 4673     case Invalid: return tr("Invalid");
 4674 
 4675     case EnumForCursorStart: return tr("Internal");
 4676 
 4677     case CursorUp: return tr("Move cursor up");
 4678     case CursorDown: return tr("Move cursor down");
 4679     case CursorLeft: return tr("Move cursor left (1 character)");
 4680     case CursorRight: return tr("Move cursor right (1 character)");
 4681     case CursorWordLeft: return tr("Move cursor left (1 word)");
 4682     case CursorWordRight: return tr("Move cursor right (1 word)");
 4683     case CursorStartOfLine: return tr("Move cursor to line start");
 4684     case CursorEndOfLine: return tr("Move cursor to line end");
 4685     case CursorStartOfDocument: return tr("Move cursor to document start");
 4686     case CursorEndOfDocument: return tr("Move cursor to document end");
 4687 
 4688     case CursorPageUp: return tr("Move cursor one page up");
 4689     case CursorPageDown: return tr("Move cursor one page down");
 4690 
 4691     case EnumForSelectionStart: return tr("Internal");
 4692 
 4693     case SelectCursorUp: return tr("Select up");
 4694     case SelectCursorDown: return tr("Select down");
 4695     case SelectCursorLeft: return tr("Select left (1 character)");
 4696     case SelectCursorRight: return tr("Select right (1 character)");
 4697     case SelectCursorWordLeft: return tr("Select left (1 word)");
 4698     case SelectCursorWordRight: return tr("Select right (1 word)");
 4699     case SelectCursorStartOfLine: return tr("Select to line start");
 4700     case SelectCursorEndOfLine: return tr("Select to line end");
 4701     case SelectCursorStartOfDocument: return tr("Select to document start");
 4702     case SelectCursorEndOfDocument: return tr("Select to document end");
 4703 
 4704     case SelectPageUp: return tr("Select page up");
 4705     case SelectPageDown: return tr("Select page down");
 4706 
 4707     case EnumForCursorEnd: return tr("Internal");
 4708 
 4709     case DeleteLeft: return tr("Delete left character");
 4710     case DeleteRight: return tr("Delete right character");
 4711     case DeleteLeftWord: return tr("Delete left word");
 4712     case DeleteRightWord: return tr("Delete right word");
 4713     case NewLine: return tr("New line");
 4714 
 4715     case ChangeOverwrite: return tr("Change overwrite mode");
 4716     case Undo: return tr("Undo");
 4717     case Redo: return tr("Redo");
 4718     case Copy: return tr("Copy");
 4719     case Paste: return tr("Paste");
 4720     case Cut: return tr("Cut");
 4721     case Print: return tr("Print");
 4722     case SelectAll: return tr("Select all");
 4723     case Find: return tr("Find");
 4724     case FindNext: return tr("Find next");
 4725     case FindPrevious: return tr("Find previous");
 4726     case Replace: return tr("Replace");
 4727 
 4728     case CreateMirrorUp: return tr("Create cursor mirror up");
 4729     case CreateMirrorDown: return tr("Create cursor mirror down");
 4730     case NextPlaceHolder: return tr("Next placeholder");
 4731     case PreviousPlaceHolder: return tr("Previous placeholder");
 4732     case NextPlaceHolderOrWord: return tr("Next placeholder or one word right");
 4733     case PreviousPlaceHolderOrWord: return tr("Previous placeholder or one word left");
 4734     case NextPlaceHolderOrChar: return tr("Next placeholder or character");
 4735     case PreviousPlaceHolderOrChar: return tr("Previous placeholder or character");
 4736     case TabOrIndentSelection: return tr("Tab or Indent selection");
 4737     case InsertTab: return tr("Insert tab");
 4738     case IndentSelection: return tr("Indent selection");
 4739     case UnindentSelection: return tr("Unindent selection");
 4740 
 4741     }
 4742     return tr("Unknown");
 4743 }
 4744 
 4745 /*!
 4746     \brief Start a drag and drop operation using the current selection
 4747 */
 4748 void QEditor::startDrag()
 4749 {
 4750     setFlag(MousePressed, false);
 4751     QMimeData *data = createMimeDataFromSelection();
 4752 
 4753     QDrag *drag = new QDrag(this);
 4754     drag->setMimeData(data);
 4755 
 4756     Qt::DropActions actions = Qt::CopyAction | Qt::MoveAction;
 4757     Qt::DropAction action = drag->exec(actions, Qt::MoveAction);
 4758 
 4759     if ( (action == Qt::MoveAction) && (drag->target() != this))
 4760     {
 4761         m_cursor.removeSelectedText();
 4762         cursorMirrorsRemoveSelectedText();
 4763     }
 4764 }
 4765 
 4766 /*!
 4767     \brief Handle cursor movements upon key event
 4768 */
 4769 
 4770 void QEditor::cursorMoveOperation(QDocumentCursor &cursor, EditOperation eop){
 4771     QDocumentCursor::MoveMode mode = eop >= EnumForSelectionStart ? QDocumentCursor::KeepAnchor : QDocumentCursor::MoveAnchor;
 4772     if ( flag(LineWrap) && flag(CursorJumpPastWrap) )
 4773         mode |= QDocumentCursor::ThroughWrap;
 4774     QDocumentCursor::MoveOperation op = QDocumentCursor::NoMove;
 4775     switch (eop){
 4776     case CursorUp: case SelectCursorUp:
 4777         op = QDocumentCursor::Up;
 4778         cutBuffer.clear();
 4779         break;
 4780     case CursorDown: case SelectCursorDown:
 4781         op = QDocumentCursor::Down;
 4782         cutBuffer.clear();
 4783         break;
 4784     case CursorLeft: case SelectCursorLeft:
 4785         if (flag(BidiVisualColumnMode)) op = QDocumentCursor::Left;
 4786         else op = QDocumentCursor::PreviousCharacter;
 4787         cutBuffer.clear();
 4788         break;
 4789     case CursorRight: case SelectCursorRight:
 4790         if (flag(BidiVisualColumnMode)) op = QDocumentCursor::Right;
 4791         else op = QDocumentCursor::NextCharacter;
 4792         cutBuffer.clear();
 4793         break;
 4794     case CursorWordLeft: case SelectCursorWordLeft:
 4795         if (flag(BidiVisualColumnMode)) op = QDocumentCursor::WordLeft;
 4796         else op = QDocumentCursor::PreviousWord;
 4797         break;
 4798     case CursorWordRight: case SelectCursorWordRight:
 4799         if (flag(BidiVisualColumnMode)) op = QDocumentCursor::WordRight;
 4800         else op = QDocumentCursor::NextWord;
 4801         break;
 4802     case CursorStartOfLine: case SelectCursorStartOfLine:
 4803         op = QDocumentCursor::StartOfLine;
 4804         break;
 4805     case CursorEndOfLine: case SelectCursorEndOfLine:
 4806         op = QDocumentCursor::EndOfLine;
 4807         break;
 4808     case CursorStartOfDocument: case SelectCursorStartOfDocument:
 4809         cutBuffer.clear();
 4810         op = QDocumentCursor::Start;
 4811         break;
 4812     case CursorEndOfDocument: case SelectCursorEndOfDocument:
 4813         cutBuffer.clear();
 4814         op = QDocumentCursor::End;
 4815         break;
 4816 
 4817     case CursorPageUp: case SelectPageUp:
 4818         cutBuffer.clear();
 4819         pageUp(mode);
 4820         return;
 4821     case CursorPageDown: case SelectPageDown:
 4822         cutBuffer.clear();
 4823         pageDown(mode);
 4824         return;
 4825     default:
 4826         return;
 4827     }
 4828 
 4829     cursor.movePosition(1, op, mode);
 4830 }
 4831 
 4832 
 4833 
 4834 /*!
 4835     \brief Go up by one page
 4836 
 4837     \note This method clears all cursor mirrors and suspend placeholder edition.
 4838 */
 4839 void QEditor::pageUp(QDocumentCursor::MoveMode moveMode)
 4840 {
 4841     setPlaceHolder(-1);
 4842     clearCursorMirrors();
 4843 
 4844     if ( m_cursor.atStart() )
 4845         return;
 4846 
 4847     int n = viewport()->height() / QDocument::getLineSpacing();
 4848 
 4849     repaintCursor();
 4850     m_cursor.movePosition(n, QDocumentCursor::Up, moveMode);
 4851 
 4852     ensureCursorVisible();
 4853     emitCursorPositionChanged();
 4854     //updateMicroFocus();
 4855 }
 4856 
 4857 /*!
 4858     \brief Go down by one page
 4859 
 4860     \note This method clears all cursor mirrors.
 4861 */
 4862 void QEditor::pageDown(QDocumentCursor::MoveMode moveMode)
 4863 {
 4864     clearCursorMirrors();
 4865     setPlaceHolder(-1);
 4866 
 4867     if ( m_cursor.atEnd() )
 4868         return;
 4869 
 4870     int n = viewport()->height() / document()->getLineSpacing();
 4871 
 4872     repaintCursor();
 4873     m_cursor.movePosition(n, QDocumentCursor::Down, moveMode);
 4874 
 4875     ensureCursorVisible();
 4876     emitCursorPositionChanged();
 4877 }
 4878 
 4879 /*!
 4880     \internal
 4881     \brief Process a key event for a given cursor
 4882 
 4883     This method only take care of editing operations, not movements.
 4884 */
 4885 void QEditor::processEditOperation(QDocumentCursor& c, const QKeyEvent* e, EditOperation op)
 4886 {
 4887     bool hasSelection = c.hasSelection();
 4888 
 4889     if ( hasSelection ) {
 4890         cutBuffer=c.selectedText();
 4891         c.removeSelectedText();
 4892     }
 4893 
 4894     switch ( op )
 4895     {
 4896     case DeleteLeft :
 4897         if(!hasSelection) c.deletePreviousChar();
 4898         cutBuffer.clear();
 4899         break;
 4900     case DeleteRight :
 4901         if(!hasSelection) c.deleteChar();
 4902         cutBuffer.clear();
 4903         break;
 4904 
 4905     case DeleteLeftWord :
 4906         c.movePosition(1,QDocumentCursor::PreviousWord,QDocumentCursor::KeepAnchor);
 4907         c.removeSelectedText();
 4908         cutBuffer.clear();
 4909         break;
 4910 
 4911     case DeleteRightWord :
 4912         c.movePosition(1,QDocumentCursor::NextWord,QDocumentCursor::KeepAnchor);
 4913         c.removeSelectedText();
 4914         cutBuffer.clear();
 4915         break;
 4916 
 4917     case NewLine:
 4918         insertText(c, "\n");
 4919         cutBuffer.clear();
 4920         break;
 4921 
 4922     default :
 4923     {
 4924         QString text = e->text();
 4925 
 4926         if ( flag(ReplaceIndentTabs) )
 4927         {
 4928             text.replace("\t", QString(m_doc->tabStop(), ' '));
 4929         }
 4930 
 4931         insertText(c, text);
 4932 
 4933         break;
 4934     }
 4935     }
 4936 }
 4937 
 4938 void QEditor::selectCursorMirrorBlock(const QDocumentCursor &cursor, bool horizontalSelect)
 4939 {
 4940     QDocumentCursorHandle *blockAnchor;
 4941     if ( m_cursorMirrorBlockAnchor >= 0 && m_cursorMirrorBlockAnchor < m_mirrors.size() ) {
 4942         blockAnchor = m_mirrors[m_cursorMirrorBlockAnchor].handle();
 4943         while ( m_mirrors.size() > m_cursorMirrorBlockAnchor + 1) //remove all after m_cursorMirrorBlockAnchor
 4944             m_mirrors.removeLast();
 4945     } else {
 4946         clearCursorMirrors();
 4947         blockAnchor = m_cursor.handle();
 4948     }
 4949     if (!blockAnchor) return;
 4950     // get column number for column selection
 4951     int org = blockAnchor->anchorColumnNumber();
 4952     int dst = cursor.columnNumber();
 4953     // TODO : fix and adapt to line wrapping...
 4954 
 4955     int min = qMin(blockAnchor->lineNumber(), cursor.lineNumber());
 4956     int max = qMax(blockAnchor->lineNumber(), cursor.lineNumber());
 4957 
 4958     if ( min != max )
 4959     {
 4960         for ( int l = min; l <= max; ++l )
 4961         {
 4962             if ( l != blockAnchor->lineNumber() )
 4963                 addCursorMirror(QDocumentCursor(m_doc, l, org));
 4964 
 4965         }
 4966 
 4967         if ( horizontalSelect )
 4968         {
 4969             blockAnchor->setColumnNumber(dst, QDocumentCursor::KeepAnchor);
 4970 
 4971             for ( int i = qMax(0, m_cursorMirrorBlockAnchor); i < m_mirrors.count(); ++i )
 4972                 m_mirrors[i].setColumnNumber(dst, QDocumentCursor::KeepAnchor);
 4973         }
 4974     } else {
 4975         blockAnchor->setSelectionBoundary(cursor);
 4976     }
 4977 }
 4978 
 4979 void QEditor::preInsertUnindent(QDocumentCursor& c, const QString& s, int additionalUnindent)
 4980 {
 4981     if ( !flag(AutoIndent) || flag(WeakIndent) ) return;
 4982     if ( m_curPlaceHolder != -1 ) return;
 4983     if ( !c.columnNumber() ) return;
 4984     
 4985     int firstNS = 0;
 4986     QString txt = c.line().text();
 4987 
 4988     while ( (firstNS < txt.length()) && txt.at(firstNS).isSpace() )
 4989         ++firstNS;
 4990 
 4991     if ( !firstNS || firstNS < c.columnNumber() )
 4992         return;
 4993 
 4994     if ( m_definition && m_definition->unindent(c, s) ) additionalUnindent++;
 4995     
 4996     if ( additionalUnindent <= 0 ) return;
 4997     
 4998     
 4999     const int off = c.columnNumber() - firstNS;
 5000 
 5001     if ( off > 0 )
 5002         c.movePosition(off, QDocumentCursor::PreviousCharacter);
 5003 
 5004     //TODO: fix unindent with tab/space mix by using c.previousChar() instead of txt.at(firstNS - 1) or calculate firstNS -= off.
 5005     //      (example: "___|->", _ means space, -> tab, | cursor, write } )
 5006     
 5007     /*
 5008         It might be possible to improve that part to have a more natural/smarter unindenting
 5009         by trying to guess the scheme used by the user...
 5010     */
 5011     for (;additionalUnindent > 0 && firstNS > 0; additionalUnindent--) {
 5012         if ( txt.at(firstNS - 1) == '\t' )
 5013         {
 5014             c.movePosition(1, QDocumentCursor::Left, QDocumentCursor::KeepAnchor);
 5015             firstNS--;
 5016         } else {
 5017             const int ts = m_doc->tabStop();
 5018             
 5019             if (txt.contains(' ') && txt.contains('\t') && c.previousChar()=='\t') {
 5020                 --firstNS;
 5021                 c.movePosition(1, QDocumentCursor::Left, QDocumentCursor::KeepAnchor);
 5022             } else
 5023                 do
 5024                 {
 5025                     --firstNS;
 5026                     c.movePosition(1, QDocumentCursor::Left, QDocumentCursor::KeepAnchor);
 5027                 } while ( QDocument::screenColumn(txt.constData(), firstNS, ts) % ts );
 5028         }
 5029     }       
 5030     c.removeSelectedText();
 5031 
 5032     if ( off > 0 )
 5033         c.movePosition(off, QDocumentCursor::NextCharacter);
 5034 }
 5035 
 5036 /*!
 5037     \brief Insert some text at a given cursor position
 5038     
 5039     This function is provided to keep indenting/outdenting working when editing
 5040 */
 5041 void QEditor::insertText(QDocumentCursor& c, const QString& text)
 5042 {
 5043     if ( protectedCursor(c) || text.isEmpty())
 5044         return;
 5045 
 5046     QStringList lines = text.split('\n', QString::KeepEmptyParts);
 5047 
 5048     bool hasSelection = c.hasSelection();
 5049     if (hasSelection && c.selectedText() == text) {
 5050         if (!c.isForwardSelection())
 5051             c.flipSelection();
 5052         c.clearSelection();
 5053         return;  // replacing a selection with itself -> nothing to do. (It's more safe to directly stop here, because the below indentation correction does not get all cases right).
 5054     }
 5055 
 5056     bool beginNewMacro = !m_doc->hasMacros() && (hasSelection || flag(Overwrite) || lines.size()>1);
 5057     if (beginNewMacro)
 5058         m_doc->beginMacro();
 5059 
 5060     bool autoOverridePlaceHolder = false;
 5061     if ( !hasSelection && flag(Overwrite) && !c.atLineEnd() )
 5062         c.deleteChar();
 5063     else {
 5064         if ( hasSelection ){
 5065             cutBuffer=c.selectedText();
 5066             c.removeSelectedText();
 5067         }
 5068 
 5069         //see isAutoOverrideText()
 5070         for ( int i = m_placeHolders.size()-1; i >= 0 ; i-- )
 5071             if ( m_placeHolders[i].autoOverride && m_placeHolders[i].cursor.lineNumber() == c.lineNumber() &&
 5072                  m_placeHolders[i].cursor.anchorColumnNumber() == c.anchorColumnNumber() &&
 5073                  (m_placeHolders[i].cursor.selectedText().startsWith(text)))
 5074             {
 5075                 autoOverridePlaceHolder = true;
 5076                 if (m_placeHolders[i].cursor.selectedText() == text) removePlaceHolder(i);
 5077             }
 5078         if (autoOverridePlaceHolder) {
 5079             for (int i=0; i<text.length(); i++)
 5080                 c.deleteChar();
 5081         }
 5082         QChar text0 = text.at(0);
 5083         if (text0 == c.nextChar()) {
 5084             // special functions for overwriting existing text
 5085             if (flag(OverwriteClosingBracketFollowingPlaceholder) && (text0=='}' || text0==']')) {
 5086                 // remove placeholder when typing closing bracket at the end of a placeholder
 5087                 // e.g. \textbf{[ph]|} and typing '}'
 5088                 for ( int i = m_placeHolders.size()-1; i >= 0 ; i-- )
 5089                     if (m_placeHolders[i].cursor.lineNumber() == c.lineNumber() &&
 5090                             m_placeHolders[i].cursor.columnNumber() == c.columnNumber()
 5091                             )
 5092                     {
 5093                         QChar openBracket = (text0=='}') ? '{' : '[';
 5094                         if (findOpeningBracket(c.line().text(), c.columnNumber()-1, openBracket, text0) < m_placeHolders[i].cursor.anchorColumnNumber()) {
 5095                             removePlaceHolder(i);
 5096                             c.deleteChar();
 5097                         }
 5098                     }
 5099             } else if (flag(OverwriteOpeningBracketFollowedByPlaceholder) && (text0=='{' || text0=='[')) {
 5100                 // skip over opening bracket if followed by a placeholder
 5101                 for ( int i = m_placeHolders.size()-1; i >= 0 ; i-- )
 5102                     if (m_placeHolders[i].cursor.anchorLineNumber() == c.lineNumber() &&
 5103                             m_placeHolders[i].cursor.anchorColumnNumber() == c.columnNumber() + 1
 5104                             )  // anchor == start of placeholder
 5105                     {
 5106                         setPlaceHolder(i);
 5107                         if (text.length() == 1) {
 5108                             return; // don't insert the bracket because we've just jumped over it
 5109                         } else {
 5110                             QString remainder(text);
 5111                             remainder.remove(0,1);
 5112                             insertText(c, remainder);
 5113                             return;
 5114                         }
 5115                     }
 5116             }
 5117         }
 5118     }
 5119 
 5120     //prepare for auto bracket insertion
 5121     QString writtenBracket;
 5122     QString autoBracket;
 5123     bool autoComplete = false;
 5124     if (flag(AutoCloseChars) && !autoOverridePlaceHolder
 5125             && (m_curPlaceHolder<0 || m_curPlaceHolder>=m_placeHolders.size() || m_placeHolders[m_curPlaceHolder].mirrors.isEmpty())
 5126             && languageDefinition() && languageDefinition()->possibleEndingOfOpeningParenthesis(text)){
 5127         autoComplete = true;
 5128         foreach (const QString& s, languageDefinition()->openingParenthesis())
 5129             if (s == text){
 5130                 writtenBracket = s;
 5131                 autoBracket = languageDefinition()->getClosingParenthesis(s);
 5132                 break;
 5133             }
 5134         if (autoBracket == writtenBracket)
 5135             autoComplete = false; //don't things like "" or $$ (assuming only single letter self closing brackets exists)
 5136 
 5137         // no idea what the following code is supposed to do, it is probably erroneous
 5138         // e.g {abc} abc |   , insert "{" at | will give a false match to the previous closing brace
 5139         // check what would be the matching element if we inserted it
 5140         if(autoComplete){
 5141             c.insertText(text);
 5142             // check if we are handling a multi-chrachter parenthesis, e.g. \[
 5143             QString newAutoBracket;
 5144             const QString& lineText = c.line().text().mid(0, c.columnNumber());
 5145             foreach (const QString& s, languageDefinition()->openingParenthesis()){
 5146                 if (s.length() >= text.length() &&  //don't complete bracket of pasted text or codesnippets
 5147                         lineText.endsWith(s)){
 5148                     newAutoBracket = languageDefinition()->getClosingParenthesis(s);
 5149                     writtenBracket = s;
 5150                     break;
 5151                 }
 5152             }
 5153             if (newAutoBracket != autoBracket) {
 5154                 autoBracket=newAutoBracket;
 5155             }
 5156 
 5157             QDocumentCursor prevc(c);
 5158             QList<QList<QDocumentCursor> > matches = languageDefinition()->getMatches(prevc);
 5159             bool found=false;
 5160             for (int i=0; i < matches.size(); i++) {
 5161                 if (matches[i][0].anchorColumnNumber() == c.anchorColumnNumber()-writtenBracket.size()) {
 5162                     if(matches[i][1].selectedText()==autoBracket){
 5163                         prevc=matches[i][1];
 5164                         found=true;
 5165                         break;
 5166                     }
 5167                 } else if (matches[i][1].anchorColumnNumber()==c.anchorColumnNumber()-writtenBracket.size()) {
 5168                     if(matches[i][0].selectedText()==autoBracket){
 5169                         prevc=matches[i][0];
 5170                         found=true;
 5171                         break;
 5172                     }
 5173                 }
 5174             }
 5175             for(int k=0;k<text.size();k++){
 5176                 c.deletePreviousChar();
 5177             }
 5178             if(found){