diff --git a/src/data/katepart5ui.rc b/src/data/katepart5ui.rc --- a/src/data/katepart5ui.rc +++ b/src/data/katepart5ui.rc @@ -1,5 +1,5 @@ - + &File @@ -86,6 +86,7 @@ + diff --git a/src/dialogs/katedialogs.h b/src/dialogs/katedialogs.h --- a/src/dialogs/katedialogs.h +++ b/src/dialogs/katedialogs.h @@ -318,7 +318,7 @@ * This dialog will prompt the user for what do with a file that is * modified on disk. * If the file wasn't deleted, it has a 'diff' button, which will create - * a diff file (uing diff(1)) and launch that using KRun. + * a diff file (using diff(1)) and launch that using KRun. */ class KateModOnHdPrompt : public QObject { @@ -340,6 +340,7 @@ void saveAsTriggered(); void ignoreTriggered(); void reloadTriggered(); + void autoReloadTriggered(); private Q_SLOTS: /** diff --git a/src/dialogs/katedialogs.cpp b/src/dialogs/katedialogs.cpp --- a/src/dialogs/katedialogs.cpp +++ b/src/dialogs/katedialogs.cpp @@ -1272,6 +1272,12 @@ // If the file isn't deleted, present a diff button const bool onDiskDeleted = modtype == KTextEditor::ModificationInterface::OnDiskDeleted; if (!onDiskDeleted) { + QAction * aAutoReload = new QAction(i18n("Enable Auto Reload"), this); + aAutoReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); + aAutoReload->setToolTip(i18n("Will never again warn about on disk changes but always reload.")); + m_message->addAction(aAutoReload, false); + connect(aAutoReload, &QAction::triggered, this, &KateModOnHdPrompt::autoReloadTriggered); + if (!QStandardPaths::findExecutable(QStringLiteral("diff")).isEmpty()) { m_diffAction = new QAction(i18n("View &Difference"), this); m_diffAction->setToolTip(i18n("Shows a diff of the changes")); diff --git a/src/document/katedocument.h b/src/document/katedocument.h --- a/src/document/katedocument.h +++ b/src/document/katedocument.h @@ -69,6 +69,7 @@ class KateAutoIndent; class KateModOnHdPrompt; +class KToggleAction; /** * @brief Backend of KTextEditor::Document related public KTextEditor interfaces. @@ -760,6 +761,13 @@ void setModified(bool m) override; + bool isAutoReload(); + KToggleAction* autoReloadToggleAction() { return m_autoReloadMode; }; + void delayAutoReload(); + +private Q_SLOTS: + void autoReloadToggled(bool b); + private: void activateDirWatch(const QString &useFileName = QString()); void deactivateDirWatch(); @@ -982,6 +990,7 @@ private Q_SLOTS: void onModOnHdSaveAs(); void onModOnHdReload(); + void onModOnHdAutoReload(); void onModOnHdIgnore(); public: @@ -1098,6 +1107,8 @@ bool m_userSetEncodingForNextReload = false; bool m_modOnHd = false; + KToggleAction* m_autoReloadMode; + QTimer m_autoReloadThrottle; ModifiedOnDiskReason m_modOnHdReason = OnDiskUnmodified; ModifiedOnDiskReason m_prevModOnHdReason = OnDiskUnmodified; diff --git a/src/document/katedocument.cpp b/src/document/katedocument.cpp --- a/src/document/katedocument.cpp +++ b/src/document/katedocument.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -261,6 +262,15 @@ m_modOnHdTimer.setInterval(200); connect(&m_modOnHdTimer, SIGNAL(timeout()), this, SLOT(slotDelayedHandleModOnHd())); + // Setup auto reload stuff + m_autoReloadMode = new KToggleAction(i18n("Auto Reload Document"), this); + m_autoReloadMode->setWhatsThis(i18n("Automatic reload the docuemnt when it was changed on disk")); + connect(m_autoReloadMode, &KToggleAction::triggered, this, &DocumentPrivate::autoReloadToggled); + // Prepare some reload amok protector + m_autoReloadThrottle.setSingleShot(true); + m_autoReloadThrottle.setInterval(3000); + connect(&m_autoReloadThrottle, &QTimer::timeout, this, &DocumentPrivate::onModOnHdAutoReload); + /** * load handling * this is needed to ensure we signal the user if a file ist still loading @@ -4240,6 +4250,11 @@ return; } + if (!isModified() && isAutoReload()) { + onModOnHdAutoReload(); + return; + } + if (!m_fileChangedDialogsActivated || m_modOnHdHandler) { return; } @@ -4253,6 +4268,7 @@ m_modOnHdHandler = new KateModOnHdPrompt(this, m_modOnHdReason, reasonedMOHString()); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::saveAsTriggered, this, &DocumentPrivate::onModOnHdSaveAs); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::reloadTriggered, this, &DocumentPrivate::onModOnHdReload); + connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::autoReloadTriggered, this, &DocumentPrivate::onModOnHdAutoReload); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::ignoreTriggered, this, &DocumentPrivate::onModOnHdIgnore); } @@ -4285,6 +4301,48 @@ delete m_modOnHdHandler; } +void KTextEditor::DocumentPrivate::autoReloadToggled(bool b) +{ + m_autoReloadMode->setChecked(b); + if (b) { + connect(&m_modOnHdTimer, &QTimer::timeout, this, &DocumentPrivate::onModOnHdAutoReload); + } else { + disconnect(&m_modOnHdTimer, &QTimer::timeout, this, &DocumentPrivate::onModOnHdAutoReload); + } +} + +bool KTextEditor::DocumentPrivate::isAutoReload() +{ + return m_autoReloadMode->isChecked(); +} + +void KTextEditor::DocumentPrivate::delayAutoReload() +{ + if (isAutoReload()) { + m_autoReloadThrottle.start(); + } +} + +void KTextEditor::DocumentPrivate::onModOnHdAutoReload() +{ + if (m_modOnHdHandler) { + delete m_modOnHdHandler; + autoReloadToggled(true); + } + + if (!isAutoReload()) { + return; + } + + if (m_modOnHd && !m_reloading && !m_autoReloadThrottle.isActive()) { + m_modOnHd = false; + m_prevModOnHdReason = OnDiskUnmodified; + emit modifiedOnDisk(this, false, OnDiskUnmodified); + documentReload(); + m_autoReloadThrottle.start(); + } +} + void KTextEditor::DocumentPrivate::onModOnHdIgnore() { // ignore as long as m_prevModOnHdReason == m_modOnHdReason diff --git a/src/view/kateview.h b/src/view/kateview.h --- a/src/view/kateview.h +++ b/src/view/kateview.h @@ -683,12 +683,14 @@ 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 (); + void exportHtmlToClipboard(); + void exportHtmlToFile(); public Q_SLOTS: void slotFoldToplevelNodes(); @@ -703,10 +705,11 @@ void setupEditActions(); void setupCodeFolding(); - QList m_editActions; + QList m_editActions; QAction *m_editUndo; QAction *m_editRedo; QAction *m_pasteMenu; + bool m_gotoBottomAfterReload; KToggleAction *m_toggleFoldingMarkers; KToggleAction *m_toggleIconBar; KToggleAction *m_toggleLineNumbers; diff --git a/src/view/kateview.cpp b/src/view/kateview.cpp --- a/src/view/kateview.cpp +++ b/src/view/kateview.cpp @@ -235,6 +235,9 @@ connect(m_doc, SIGNAL(aboutToReload(KTextEditor::Document*)), SLOT(saveFoldingState())); connect(m_doc, SIGNAL(reloaded(KTextEditor::Document*)), SLOT(applyFoldingState())); + connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::slotDocumentReloaded); + connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::slotDocumentAboutToReload); + // update highlights on scrolling and co connect(this, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(createHighlights())); @@ -748,6 +751,9 @@ a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.

The mini-map shows an overview of the whole document.")); connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMap())); + a = m_doc->autoReloadToggleAction(); + ac->addAction(QStringLiteral("view_auto_reload"), a); + // a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this); // ac->addAction(QLatin1String("view_scrollbar_minimap_all"), a); // a->setWhatsThis(i18n("Display the whole document in the mini-map.

With this option set the whole document will be visible in the mini-map.")); @@ -1316,6 +1322,30 @@ emit viewModeChanged(this, viewMode()); } +void KTextEditor::ViewPrivate::slotDocumentAboutToReload() +{ + if (doc()->isAutoReload()) { + const int lastVisibleLine = m_viewInternal->endLine(); + const int currentLine = cursorPosition().line(); + m_gotoBottomAfterReload = (lastVisibleLine == currentLine) && (currentLine == doc()->lastLine()); + if (!m_gotoBottomAfterReload) { + // Ensure the view jumps not back when user scrolls around + const int firstVisibleLine = 1 + lastVisibleLine - m_viewInternal->linesDisplayed(); + const int newLine = qBound(firstVisibleLine, currentLine, lastVisibleLine); + setCursorPositionVisual(KTextEditor::Cursor(newLine, cursorPosition().column())); + } + } else { + m_gotoBottomAfterReload = false; + } +} + +void KTextEditor::ViewPrivate::slotDocumentReloaded() +{ + if (m_gotoBottomAfterReload) { + bottom(); + } +} + void KTextEditor::ViewPrivate::slotGotFocus() { //qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotGotFocus"; diff --git a/src/view/kateviewinternal.cpp b/src/view/kateviewinternal.cpp --- a/src/view/kateviewinternal.cpp +++ b/src/view/kateviewinternal.cpp @@ -612,6 +612,7 @@ return; //When this view is not visible, don't do anything } + view()->doc()->delayAutoReload(); // Don't reload while user scrolls around bool blocked = m_lineScroll->blockSignals(true); int wrapWidth = width();