/* This file is part of the KDE libraries
  Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
  Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
  Copyright (C) 2001-2010 Joseph Wenninger <jowenn@kde.org>
  Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License version 2 as published by the Free Software Foundation.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#ifndef kate_view_h
#define kate_view_h

#include <ktexteditor/view.h>
#include <ktexteditor/texthintinterface.h>
#include <ktexteditor/inlinenoteinterface.h>
#include <ktexteditor/markinterface.h>
#include <ktexteditor/codecompletioninterface.h>
#include <ktexteditor/configinterface.h>
#include <ktexteditor/annotationinterface.h>
#include <ktexteditor/mainwindow.h>

#include <QPointer>
#include <QScopedPointer>
#include <QModelIndex>
#include <QMenu>
#include <QSpacerItem>

#include <array>

#include "katetextrange.h"
#include "katetextfolding.h"
#include "katerenderer.h"

namespace KTextEditor
{
class AnnotationModel;
class Message;
class InlineNoteProvider;
}

namespace KTextEditor { class DocumentPrivate; }
class KateBookmarks;
class KateViewConfig;
class KateRenderer;
class KateSpellCheckDialog;
class KateCompletionWidget;
class KateViewInternal;
class KateViewBar;
class KateTextPreview;
class KateGotoBar;
class KateDictionaryBar;
class KateSpellingMenu;
class KateMessageWidget;
class KateIconBorder;
class KateStatusBar;
class KateViewEncodingAction;
class KateModeMenu;
class KateAbstractInputMode;
class KateScriptActionMenu;
class KateMessageLayout;
class KateInlineNoteData;

class KToggleAction;
class KSelectAction;

class QAction;

namespace KTextEditor
{

//
// Kate KTextEditor::View class ;)
//
class KTEXTEDITOR_EXPORT ViewPrivate : public KTextEditor::View,
    public KTextEditor::TextHintInterface,
    public KTextEditor::CodeCompletionInterface,
    public KTextEditor::ConfigInterface,
    public KTextEditor::InlineNoteInterface,
    public KTextEditor::AnnotationViewInterfaceV2
{
    Q_OBJECT
    Q_INTERFACES(KTextEditor::TextHintInterface)
    Q_INTERFACES(KTextEditor::ConfigInterface)
    Q_INTERFACES(KTextEditor::CodeCompletionInterface)
    Q_INTERFACES(KTextEditor::AnnotationViewInterface)
    Q_INTERFACES(KTextEditor::AnnotationViewInterfaceV2)
    Q_INTERFACES(KTextEditor::InlineNoteInterface)

    friend class KTextEditor::View;
    friend class ::KateViewInternal;
    friend class ::KateIconBorder;
    friend class ::KateTextPreview;

public:
    ViewPrivate (KTextEditor::DocumentPrivate *doc, QWidget *parent, KTextEditor::MainWindow *mainWindow = nullptr);
    ~ViewPrivate() override;

    /**
     * Get the view's main window, if any
     * \return the view's main window
     */
    KTextEditor::MainWindow *mainWindow() const override
    {
        return m_mainWindow;
    }

    KTextEditor::Document *document() const override;

    ViewMode viewMode() const override;
    QString viewModeHuman() const override;
    InputMode viewInputMode() const override;
    QString viewInputModeHuman() const override;

    void setInputMode(InputMode mode);

    //
    // KTextEditor::ClipboardInterface
    //
public Q_SLOTS:
    void paste(const QString *textToPaste = nullptr);
    void cut();
    void copy() const;

private Q_SLOTS:
    /**
     * Wrap lines touched by the selection with respect of existing paragraphs.
     * Work is done by KTextEditor::DocumentPrivate::wrapParagraph
     */
    void applyWordWrap();

    //
    // KTextEditor::PopupMenuInterface
    //
public:
    void setContextMenu(QMenu *menu) override;
    QMenu *contextMenu() const override;
    QMenu *defaultContextMenu(QMenu *menu = nullptr) const override;

private Q_SLOTS:
    void aboutToShowContextMenu();
    void aboutToHideContextMenu();

private:
    QPointer<QMenu> m_contextMenu;

    //
    // KTextEditor::ViewCursorInterface
    //
public:
    bool setCursorPosition(KTextEditor::Cursor position) override;

    KTextEditor::Cursor cursorPosition() const override;

    KTextEditor::Cursor cursorPositionVirtual() const override;

    QPoint cursorToCoordinate(const KTextEditor::Cursor &cursor) const override;

    KTextEditor::Cursor coordinatesToCursor(const QPoint &coord) const override;

    QPoint cursorPositionCoordinates() const override;

    bool setCursorPositionVisual(const KTextEditor::Cursor &position);

    /**
     * Return the virtual cursor column, each tab is expanded into the
     * document's tabWidth characters. If word wrap is off, the cursor may be
     * behind the line's length.
     */
    int virtualCursorColumn() const;

    bool mouseTrackingEnabled() const override;
    bool setMouseTrackingEnabled(bool enable) override;

private:
    void notifyMousePositionChanged(const KTextEditor::Cursor &newPosition);

    // Internal
public:
    bool setCursorPositionInternal(const KTextEditor::Cursor &position, uint tabwidth = 1, bool calledExternally = false);

    //
    // KTextEditor::ConfigInterface
    //
public:
    QStringList configKeys() const override;
    QVariant configValue(const QString &key) override;
    void setConfigValue(const QString &key, const QVariant &value) override;

Q_SIGNALS:
    void configChanged();

public:
    /**
     * Try to fold an unfolded range starting at @p line
     * @return the new folded range on success, otherwise an unvalid range
     */
    KTextEditor::Range foldLine(int line);

    /**
     * Try to unfold a folded range starting at @p line
     * @return true when a range was unfolded, otherwise false
     */
    bool unfoldLine(int line);

    /**
     * Try to toggle the folding state of a range starting at line @p line
     * @return true when the line was toggled, false when not
     */
    bool toggleFoldingOfLine(int line);

    /**
     * Try to change all the foldings inside a folding range starting at @p line
     * but not the range itself starting there.
     * However, should the range itself be folded, will only the range unfolded
     * and the containing ranges kept untouched.
     * Should the range not contain other ranges will the range itself folded,
     * @return true when any range was folded or unfolded, otherwise false
     */
    bool toggleFoldingsInRange(int line);

    //
    // KTextEditor::CodeCompletionInterface2
    //
public:
    bool isCompletionActive() const override;
    void startCompletion(const KTextEditor::Range &word, KTextEditor::CodeCompletionModel *model) override;
    void abortCompletion() override;
    void forceCompletion() override;
    void registerCompletionModel(KTextEditor::CodeCompletionModel *model) override;
    void unregisterCompletionModel(KTextEditor::CodeCompletionModel *model) override;
    bool isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const;
    bool isAutomaticInvocationEnabled() const override;
    void setAutomaticInvocationEnabled(bool enabled = true) override;

Q_SIGNALS:
    void completionExecuted(KTextEditor::View *view, const KTextEditor::Cursor &position, KTextEditor::CodeCompletionModel *model, const QModelIndex &);
    void completionAborted(KTextEditor::View *view);

public Q_SLOTS:
    void userInvokedCompletion();

public:
    KateCompletionWidget *completionWidget() const;
    mutable KateCompletionWidget *m_completionWidget;
    void sendCompletionExecuted(const KTextEditor::Cursor &position, KTextEditor::CodeCompletionModel *model, const QModelIndex &index);
    void sendCompletionAborted();

    //
    // KTextEditor::TextHintInterface
    //
public:
    void registerTextHintProvider(KTextEditor::TextHintProvider *provider) override;
    void unregisterTextHintProvider(KTextEditor::TextHintProvider *provider) override;
    void setTextHintDelay(int delay) override;
    int textHintDelay() const override;

public:
    bool dynWordWrap() const
    {
        return m_hasWrap;
    }

    //
    // Inline Notes Interface
    //
public:
    void registerInlineNoteProvider(KTextEditor::InlineNoteProvider *provider) override;
    void unregisterInlineNoteProvider(KTextEditor::InlineNoteProvider *provider) override;
    QRect inlineNoteRect(const KateInlineNoteData& note) const;

    QVarLengthArray<KateInlineNoteData, 8> inlineNotes(int line) const;

private:
    QVector<KTextEditor::InlineNoteProvider *> m_inlineNoteProviders;

private Q_SLOTS:
    void inlineNotesReset();
    void inlineNotesLineChanged(int line);

    //
    // KTextEditor::SelectionInterface stuff
    //
public Q_SLOTS:
    bool setSelection(const KTextEditor::Range &selection) override;

    bool removeSelection() override
    {
        return clearSelection();
    }

    bool removeSelectionText() override
    {
        return removeSelectedText();
    }

    bool setBlockSelection(bool on) override;
    bool toggleBlockSelection();

    bool clearSelection();
    bool clearSelection(bool redraw, bool finishedChangingSelection = true);

    bool removeSelectedText();

    bool selectAll();

public:
    bool selection() const override;
    QString selectionText() const override;
    bool blockSelection() const override;
    KTextEditor::Range selectionRange() const override;

    static void blockFix(KTextEditor::Range &range);

    //
    // Arbitrary Syntax HL + Action extensions
    //
public:
    // Action association extension
    void deactivateEditActions();
    void activateEditActions();

    //
    // internal helper stuff, for katerenderer and so on
    //
public:
    // should cursor be wrapped ? take config + blockselection state in account
    bool wrapCursor() const;

    // some internal functions to get selection state of a line/col
    bool cursorSelected(const KTextEditor::Cursor &cursor);
    bool lineSelected(int line);
    bool lineEndSelected(const KTextEditor::Cursor &lineEndPos);
    bool lineHasSelected(int line);
    bool lineIsSelection(int line);

    void ensureCursorColumnValid();

    void tagSelection(const KTextEditor::Range &oldSelection);

    void selectWord(const KTextEditor::Cursor &cursor);
    void selectLine(const KTextEditor::Cursor &cursor);

    //BEGIN EDIT STUFF
public:
    void editStart();
    void editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom);

    void editSetCursor(const KTextEditor::Cursor &cursor);
    //END

    //BEGIN TAG & CLEAR
public:
    bool tagLine(const KTextEditor::Cursor &virtualCursor);

    bool tagRange(const KTextEditor::Range &range, bool realLines = false);
    bool tagLines(int start, int end, bool realLines = false);
    bool tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false);
    bool tagLines(KTextEditor::Range range, bool realRange = false);

    void tagAll();

    void clear();

    void repaintText(bool paintOnlyDirty = false);

    void updateView(bool changed = false);
    //END

    //
    // KTextEditor::AnnotationView
    //
public:
    void setAnnotationModel(KTextEditor::AnnotationModel *model) override;
    KTextEditor::AnnotationModel *annotationModel() const override;
    void setAnnotationBorderVisible(bool visible) override;
    bool isAnnotationBorderVisible() const override;
    void setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) override;
    KTextEditor::AbstractAnnotationItemDelegate* annotationItemDelegate() const override;
    void setAnnotationUniformItemSizes(bool enable) override;
    bool uniformAnnotationItemSizes() const override;

Q_SIGNALS:
    void annotationContextMenuAboutToShow(KTextEditor::View *view, QMenu *menu, int line) override;
    void annotationActivated(KTextEditor::View *view, int line) override;
    // KF6: fix View -> KTextEditor::View
    void annotationBorderVisibilityChanged(View *view, bool visible) override;

    void navigateLeft();
    void navigateRight();
    void navigateUp();
    void navigateDown();
    void navigateAccept();
    void navigateBack();

private:
    KTextEditor::AnnotationModel *m_annotationModel;

    //
    // KTextEditor::View
    //
public:
    void emitNavigateLeft()
    {
        emit navigateLeft();
    }
    void emitNavigateRight()
    {
        emit navigateRight();
    }
    void emitNavigateUp()
    {
        emit navigateUp();
    }
    void emitNavigateDown()
    {
        emit navigateDown();
    }
    void emitNavigateAccept()
    {
        emit navigateAccept();
    }
    void emitNavigateBack()
    {
        emit navigateBack();
    }
    /**
     Return values for "save" related commands.
    */
    bool isOverwriteMode() const;

    QString currentTextLine();

    QTextLayout * textLayout(int line) const;
    QTextLayout * textLayout(const KTextEditor::Cursor &pos) const;

public Q_SLOTS:
    void indent();
    void unIndent();
    void cleanIndent();
    void align();
    void comment();
    void uncomment();
    void toggleComment();
    void killLine();

    /**
     * Sets the cursor to the previous editing position in this document
     */
    void goToPreviousEditingPosition();

    /**
     * Sets the cursor to the next editing position in this document.
     */
    void goToNextEditingPosition();

    /**
     * Uppercases selected text, or an alphabetic character next to the cursor.
     */
    void uppercase();

    /**
     * Lowercases selected text, or an alphabetic character next to the cursor.
     */
    void lowercase();

    /**
     * Capitalizes the selection (makes each word start with an uppercase) or
     * the word under the cursor.
     */
    void capitalize();

    /**
     * Joins lines touched by the selection.
     */
    void joinLines();

    /**
     * Performs a line break (insert a new line char) at current cursor position
     * and indent the new line.
     *
     * Most work is done by @c KTextEditor::DocumentPrivate::newLine and
     * @c KateAutoIndent::userTypedChar
     * @see KTextEditor::DocumentPrivate::newLine, KateAutoIndent
     */
    void keyReturn();

    /**
     * Performs a line break (insert a new line char) at current cursor position
     * but keep all leading non word char as indent for the new line.
     */
    void smartNewline();

    /**
     * Insert a tabulator char at current cursor position.
     */
    void backspace();

    /**
     * Remove the word left from the current cursor position including all leading
     * space.
     * @see KateViewInternal::wordPrev
     */
    void deleteWordLeft();

    /**
     * Remove the current selection. When nothing is selected the char right
     * from the current cursor position is removed.
     * @see KTextEditor::DocumentPrivate::del
     */
    void keyDelete();

    /**
     * When the char right from the current cursor position is a space is all
     * space to the right removed. Otherwise is the word to the right including
     * all trialling space removed.
     * @see KateViewInternal::wordNext
     */
    void deleteWordRight();

    /**
     * Transpose the characters left and right from the current cursor position
     * and move the cursor one position to the right. If the char right to the
     * current cursor position is a new line char, nothing is done.
     * @see KTextEditor::DocumentPrivate::transpose
     */
    void transpose();
    void cursorLeft();
    void shiftCursorLeft();
    void cursorRight();
    void shiftCursorRight();
    void wordLeft();
    void shiftWordLeft();
    void wordRight();
    void shiftWordRight();
    void home();
    void shiftHome();
    void end();
    void shiftEnd();
    void up();
    void shiftUp();
    void down();
    void shiftDown();
    void scrollUp();
    void scrollDown();
    void topOfView();
    void shiftTopOfView();
    void bottomOfView();
    void shiftBottomOfView();
    void pageUp();
    void shiftPageUp();
    void pageDown();
    void shiftPageDown();
    void top();
    void shiftTop();
    void bottom();
    void shiftBottom();
    void toMatchingBracket();
    void shiftToMatchingBracket();
    void toPrevModifiedLine();
    void toNextModifiedLine();
    /**
     * Insert a tabulator char at current cursor position.
     */
    void insertTab();

    void gotoLine();

    // config file / session management functions
public:
    /**
     * Read session settings from the given \p config.
     *
     * Known flags:
     *  "SkipUrl" => don't save/restore the file
     *  "SkipMode" => don't save/restore the mode
     *  "SkipHighlighting" => don't save/restore the highlighting
     *  "SkipEncoding" => don't save/restore the encoding
     *
     * \param config read the session settings from this KConfigGroup
     * \param flags additional flags
     * \see writeSessionConfig()
     */
    void readSessionConfig(const KConfigGroup &config, const QSet<QString> &flags = QSet<QString>()) override;

    /**
     * Write session settings to the \p config.
     * See readSessionConfig() for more details.
     *
     * \param config write the session settings to this KConfigGroup
     * \param flags additional flags
     * \see readSessionConfig()
     */
    void writeSessionConfig(KConfigGroup &config, const QSet<QString> &flags = QSet<QString>()) override;

public Q_SLOTS:
    void setEol(int eol);
    void setAddBom(bool enabled);
    void find();
    void findSelectedForwards();
    void findSelectedBackwards();
    void replace();
    void findNext();
    void findPrevious();

    void setFoldingMarkersOn(bool enable);   // Not in KTextEditor::View, but should be
    void setIconBorder(bool enable);
    void setLineNumbersOn(bool enable);
    void setScrollBarMarks(bool enable);
    void setScrollBarMiniMap(bool enable);
    void setScrollBarMiniMapAll(bool enable);
    void setScrollBarMiniMapWidth(int width);
    void toggleFoldingMarkers();
    void toggleIconBorder();
    void toggleLineNumbersOn();
    void toggleScrollBarMarks();
    void toggleScrollBarMiniMap();
    void toggleScrollBarMiniMapAll();
    void toggleDynWordWrap();
    void setDynWrapIndicators(int mode);

public:
    int getEol() const;

public:
    KateRenderer *renderer();

    bool iconBorder();
    bool lineNumbersOn();
    bool scrollBarMarks();
    bool scrollBarMiniMap();
    bool scrollBarMiniMapAll();
    int dynWrapIndicators();
    bool foldingMarkersOn();

private Q_SLOTS:
    /**
     * used to update actions after selection changed
     */
    void slotSelectionChanged();

    void toggleInputMode();
    void cycleInputMode();

public:
    /**
     * accessor to katedocument pointer
     * @return pointer to document
     */
    KTextEditor::DocumentPrivate  *doc()
    {
        return m_doc;
    }
    const KTextEditor::DocumentPrivate  *doc() const
    {
        return m_doc;
    }

public Q_SLOTS:
    void slotUpdateUndo();
    void toggleInsert();
    void reloadFile();
    void toggleWWMarker();
    void toggleNPSpaces();
    void toggleWordCount(bool on);
    void toggleWriteLock();
    void switchToCmdLine();
    void slotReadWriteChanged();
    void slotClipboardHistoryChanged();

Q_SIGNALS:
    void dropEventPass(QDropEvent *);

public:
    /**
     * Folding handler for this view.
     * @return folding handler
     */
    Kate::TextFolding &textFolding()
    {
        return m_textFolding;
    }

public:
    void slotTextInserted(KTextEditor::View *view, const KTextEditor::Cursor &position, const QString &text);

    void exportHtmlToFile(const QString &file);

private Q_SLOTS:
    void slotDocumentReloaded();
    void slotDocumentAboutToReload();
    void slotGotFocus();
    void slotLostFocus();
    void slotSaveCanceled(const QString &error);
    void slotConfigDialog();
    void exportHtmlToClipboard();
    void exportHtmlToFile();

public Q_SLOTS:
    void slotFoldToplevelNodes();
    void slotExpandToplevelNodes();
    void slotToggleFolding();
    void slotToggleFoldingsInRange();

private:
    void setupLayout();
    void setupConnections();
    void setupActions();
    void setupEditActions();
    void setupCodeFolding();

    QList<QAction *>       m_editActions;
    QAction               *m_editUndo;
    QAction               *m_editRedo;
    QAction               *m_pasteMenu;
    bool                   m_gotoBottomAfterReload;
    KToggleAction         *m_toggleFoldingMarkers;
    KToggleAction         *m_toggleIconBar;
    KToggleAction         *m_toggleLineNumbers;
    KToggleAction         *m_toggleScrollBarMarks;
    KToggleAction         *m_toggleScrollBarMiniMap;
    KToggleAction         *m_toggleScrollBarMiniMapAll;
    KToggleAction         *m_toggleDynWrap;
    KSelectAction         *m_setDynWrapIndicators;
    KToggleAction         *m_toggleWWMarker;
    KToggleAction         *m_toggleNPSpaces;
    KToggleAction         *m_toggleWordCount;
    QAction               *m_switchCmdLine;
    KToggleAction         *m_viInputModeAction;

    KSelectAction         *m_setEndOfLine;
    KToggleAction         *m_addBom;

    QAction *m_cut;
    QAction *m_copy;
    QAction *m_copyHtmlAction;
    QAction *m_paste;
    QAction *m_selectAll;
    QAction *m_deSelect;

    QActionGroup *m_inputModeActions;

    KToggleAction *m_toggleBlockSelection;
    KToggleAction *m_toggleInsert;
    KToggleAction *m_toggleWriteLock;

    bool m_hasWrap;

    KTextEditor::DocumentPrivate     *const m_doc;
    Kate::TextFolding m_textFolding;
    KateViewConfig   *const m_config;
    KateRenderer     *const m_renderer;
    KateViewInternal *const m_viewInternal;
    KateSpellCheckDialog  *m_spell;
    KateBookmarks    *const m_bookmarks;

    //* margins
    QSpacerItem *m_topSpacer;
    QSpacerItem *m_leftSpacer;
    QSpacerItem *m_rightSpacer;
    QSpacerItem *m_bottomSpacer;

private Q_SLOTS:
    void slotHlChanged();

    /**
     * Configuration
     */
public:
    inline KateViewConfig *config()
    {
        return m_config;
    }

    void updateConfig();

    void updateDocumentConfig();

    void updateRendererConfig();

private Q_SLOTS:
    void updateFoldingConfig();

private:
    bool m_startingUp;
    bool m_updatingDocumentConfig;

    // stores the current selection
    Kate::TextRange m_selection;

    // do we select normal or blockwise ?
    bool blockSelect;

    // templates
public:
    bool insertTemplateInternal(const KTextEditor::Cursor& insertPosition,
                                const QString& templateString,
                                const QString& script = QString());

    /**
     * Accessors to the bars...
     */
public:
    KateViewBar *bottomViewBar() const;
    KateDictionaryBar *dictionaryBar();

private:
    KateGotoBar *gotoBar();

    /**
     * viewbar + its widgets
     * they are created on demand...
     */
private:
    // created in constructor of the view
    KateViewBar *m_bottomViewBar;

    // created on demand..., only access them through the above accessors....
    KateGotoBar *m_gotoBar;
    KateDictionaryBar *m_dictionaryBar;

    // input modes
public:
    KateAbstractInputMode *currentInputMode() const;
public:
    KTextEditor::Range visibleRange();

Q_SIGNALS:
    void displayRangeChanged(KTextEditor::ViewPrivate *view);

protected:
    bool event(QEvent *e) override;
    void paintEvent(QPaintEvent *e) override;

    KToggleAction               *m_toggleOnTheFlySpellCheck;
    KateSpellingMenu *m_spellingMenu;

protected Q_SLOTS:
    void toggleOnTheFlySpellCheck(bool b);

public Q_SLOTS:
    void changeDictionary();
    void reflectOnTheFlySpellCheckStatus(bool enabled);

public:
    KateSpellingMenu *spellingMenu();
private:
    bool m_userContextMenuSet;

private Q_SLOTS:
    /**
     * save folding state before document reload
     */
    void saveFoldingState();

    /**
     * restore folding state after document reload
     */
    void applyFoldingState();

    void clearHighlights();
    void createHighlights();

private:
    void selectionChangedForHighlights();

    /**
     * saved folding state
     */
    QJsonDocument m_savedFoldingState;

    QString m_currentTextForHighlights;

    QList<KTextEditor::MovingRange*> m_rangesForHighlights;

public:
    /**
     * Attribute of a range changed or range with attribute changed in given line range.
     * @param startLine start line of change
     * @param endLine end line of change
     * @param rangeWithAttribute attribute changed or is active, this will perhaps lead to repaints
     */
    void notifyAboutRangeChange(int startLine, int endLine, bool rangeWithAttribute);

private Q_SLOTS:
    /**
     * Delayed update for view after text ranges changed
     */
    void slotDelayedUpdateOfView();

Q_SIGNALS:
    /**
     * Delayed update for view after text ranges changed
     */
    void delayedUpdateOfView();

    /**
     * Emitted whenever the caret enter or leave a range.
     * ATM only used by KateStatusBar to update the dict button
     */
    void caretChangedRange(KTextEditor::View *);

public:
    /**
     * set of ranges which had the mouse inside last time, used for rendering
     * @return set of ranges which had the mouse inside last time checked
     */
    const QSet<Kate::TextRange *> *rangesMouseIn() const
    {
        return &m_rangesMouseIn;
    }

    /**
     * set of ranges which had the caret inside last time, used for rendering
     * @return set of ranges which had the caret inside last time checked
     */
    const QSet<Kate::TextRange *> *rangesCaretIn() const
    {
        return &m_rangesCaretIn;
    }

    /**
     * check if ranges changed for mouse in and caret in
     * @param activationType type of activation to check
     */
    void updateRangesIn(KTextEditor::Attribute::ActivationType activationType);

    //
    // helpers for delayed view update after ranges changes
    //
private:
    /**
     * update already inited?
     */
    bool m_delayedUpdateTriggered;

    /**
     * minimal line to update
     */
    int m_lineToUpdateMin;

    /**
     * maximal line to update
     */
    int m_lineToUpdateMax;

    /**
     * set of ranges which had the mouse inside last time
     */
    QSet<Kate::TextRange *> m_rangesMouseIn;

    /**
     * set of ranges which had the caret inside last time
     */
    QSet<Kate::TextRange *> m_rangesCaretIn;

    //
    // forward impl for KTextEditor::MessageInterface
    //
public:
    /**
     * Used by Document::postMessage().
     */
    void postMessage(KTextEditor::Message *message, QList<QSharedPointer<QAction> > actions);

private:
    /**
     * Message widgets showing KTextEditor::Messages.
     * The index of the array maps to the enum KTextEditor::Message::MessagePosition.
     */
    std::array<KateMessageWidget *, 5> m_messageWidgets{{nullptr}};
    /** Layout for floating notifications */
    KateMessageLayout *m_notificationLayout = nullptr;

    // for unit test 'tests/messagetest.cpp'
public:
    KateMessageWidget *messageWidget();

private:
    /**
     * The main window responsible for this view, if any
     */
    QPointer<KTextEditor::MainWindow> m_mainWindow;

    //
    // KTextEditor::PrintInterface
    //
public Q_SLOTS:
    bool print() override;
    void printPreview() override;

public:
    /**
     * Get the view status bar
     * @return status bar, in enabled
     */
    KateStatusBar *statusBar () const {
        return m_statusBar;
    }

    /**
     * Toogle status bar, e.g. create or remove it
     */
    void toggleStatusBar ();

    /**
     * Get the encoding menu
     * @return the encoding menu
     */
    KateViewEncodingAction *encodingAction () const {
        return m_encodingAction;
    }

    /**
     * Get the mode menu
     * @return the mode menu
     */
    KateModeMenu *modeAction () const {
        return m_modeAction;
    }

private:
    /**
     * the status bar of this view
     */
    KateStatusBar *m_statusBar;

    /**
     * the encoding selection menu, used by view + status bar
     */
    KateViewEncodingAction *m_encodingAction;

    /**
     * the mode selection menu, used by view
     */
    KateModeMenu *m_modeAction;

    /**
     * is automatic invocation of completion disabled temporarily?
     */
    bool m_temporaryAutomaticInvocationDisabled;

public:
    /**
     * Returns the attribute for the default style \p defaultStyle.
     */
    Attribute::Ptr defaultStyleAttribute(DefaultStyle defaultStyle) const override;

    /**
     * Get the list of AttributeBlocks for a given \p line in the document.
     *
     * \return list of AttributeBlocks for given \p line.
     */
    QList<KTextEditor::AttributeBlock> lineAttributes(int line) override;

private:
    // remember folding state to prevent refolding the first line if it was manually unfolded,
    // e.g. when saving a file or changing other config vars
    bool m_autoFoldedFirstLine;

public:
    void setScrollPositionInternal(KTextEditor::Cursor &cursor);

    void setHorizontalScrollPositionInternal(int x);

    KTextEditor::Cursor maxScrollPositionInternal() const;

    int firstDisplayedLineInternal(LineType lineType) const;

    int lastDisplayedLineInternal(LineType lineType) const;

    QRect textAreaRectInternal() const;

private:
    /**
     * script action menu, stored in scoped pointer to ensure
     * destruction before other QObject auto-cleanup as it
     * manage sub objects on its own that have this view as parent
     */
    QScopedPointer<KateScriptActionMenu> m_scriptActionMenu;
};

}

#endif

