diff --git a/addons/preview/previewwidget.h b/addons/preview/previewwidget.h --- a/addons/preview/previewwidget.h +++ b/addons/preview/previewwidget.h @@ -28,6 +28,7 @@ class KTextEditorPreviewPlugin; namespace KTextEditor { +class Document; class MainWindow; class View; } @@ -94,13 +95,15 @@ * @param view the view or, if there is none, a nullptr */ void setTextEditorView(KTextEditor::View* view); + void resetTextEditorView(KTextEditor::Document* document); + void unsetDocument(KTextEditor::Document* document); private: void toggleDocumentLocking(bool locked); - void handleLockedDocumentClosing(); void toggleAutoUpdating(bool autoRefreshing); void updatePreview(); void showAboutKPartPlugin(); + void clearMenu(); private: KToggleAction* m_lockAction; @@ -113,7 +116,7 @@ KTextEditorPreviewPlugin* const m_core; KTextEditor::MainWindow* const m_mainWindow; - KTextEditor::View* m_previewedTextEditorView = nullptr; + KTextEditor::Document* m_previewedTextEditorDocument = nullptr; QString m_currentServiceId; KPartView* m_partView = nullptr; KXMLGUIFactory* m_xmlGuiFactory; diff --git a/addons/preview/previewwidget.cpp b/addons/preview/previewwidget.cpp --- a/addons/preview/previewwidget.cpp +++ b/addons/preview/previewwidget.cpp @@ -38,6 +38,7 @@ #include #include #include +#include // Qt #include @@ -108,8 +109,8 @@ label->setAlignment(Qt::AlignHCenter); addWidget(label); - connect(m_mainWindow, SIGNAL(viewChanged(KTextEditor::View*)), - this, SLOT(setTextEditorView(KTextEditor::View*))); + connect(m_mainWindow, &KTextEditor::MainWindow::viewChanged, + this, &PreviewWidget::setTextEditorView); setTextEditorView(m_mainWindow->activeView()); } @@ -131,59 +132,83 @@ void PreviewWidget::setTextEditorView(KTextEditor::View* view) { - if ((m_previewedTextEditorView == view) || + if ((view && view->document() == m_previewedTextEditorDocument) || + (!view && !m_previewedTextEditorDocument) || !isVisible() || m_lockAction->isChecked()) { return; } - m_previewedTextEditorView = view; + m_previewedTextEditorDocument = view ? view->document() : nullptr; - KService::Ptr service; - if (m_previewedTextEditorView) { - // TODO: mimetype is not set for new document which have not been saved yet. - // needs another way to get this info, or perhaps some proper fix in Kate/Kdevelop - // to guess the mimetype based on current content, selected mode/highlighting etc. - // which then also would needs a signal mimetypeChanged and handling here - const auto mimeType = m_previewedTextEditorView->document()->mimeType(); - service = KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("KParts/ReadOnlyPart")); - if (service) { - qCDebug(KTEPREVIEW) << "Found preferred kpart service named" << service->name() - << "with library" <library() - << "for mimetype" << mimeType; + resetTextEditorView(m_previewedTextEditorDocument); +} - if (service->library().isEmpty()) { - qCWarning(KTEPREVIEW) << "Discarding preferred kpart service due to empty library name:" << service->name(); - service.reset(); - } +void PreviewWidget::resetTextEditorView(KTextEditor::Document* document) { + if (!isVisible() || m_previewedTextEditorDocument != document) { + return; + } - // no interest in kparts which also just display the text (like katepart itself) - // TODO: what about parts which also support importing plain text and turning into richer format - // and thus have it in their mimetypes list? - // could that perhaps be solved by introducing the concept of "native" and "imported" mimetypes? - // or making a distinction between source editors/viewers and final editors/viewers? - // latter would also help other source editors/viewers like a hexeditor, which "supports" any mimetype - if (service && service->mimeTypes().contains(QStringLiteral("text/plain"))) { - qCDebug(KTEPREVIEW) << "Blindly discarding preferred service as it also supports text/plain, to avoid useless plain/text preview."; - service.reset(); + KService::Ptr service; + + if (m_previewedTextEditorDocument) { + // TODO: mimetype is not set for new documents which have not been saved yet. + // Maybe retry to guess as soon as content is inserted. + + // Get mimetypes assigned to the currently set mode. + auto mimeTypes = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("katemoderc")), m_previewedTextEditorDocument->mode()).readXdgListEntry("Mimetypes"); + // Also try to guess from the content, if the above fails. + mimeTypes << m_previewedTextEditorDocument->mimeType(); + + foreach (const auto mimeType, mimeTypes) { + service = KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("KParts/ReadOnlyPart")); + if (service) { + qCDebug(KTEPREVIEW) << "Found preferred kpart service named" << service->name() + << "with library" <library() + << "for mimetype" << mimeType; + + if (service->library().isEmpty()) { + qCWarning(KTEPREVIEW) << "Discarding preferred kpart service due to empty library name:" << service->name(); + service.reset(); + } + + // no interest in kparts which also just display the text (like katepart itself) + // TODO: what about parts which also support importing plain text and turning into richer format + // and thus have it in their mimetypes list? + // could that perhaps be solved by introducing the concept of "native" and "imported" mimetypes? + // or making a distinction between source editors/viewers and final editors/viewers? + // latter would also help other source editors/viewers like a hexeditor, which "supports" any mimetype + if (service && service->mimeTypes().contains(QStringLiteral("text/plain"))) { + qCDebug(KTEPREVIEW) << "Blindly discarding preferred service as it also supports text/plain, to avoid useless plain/text preview."; + service.reset(); + } + + if (service) { + break; + } } - } else { - qCDebug(KTEPREVIEW) << "Found no preferred kpart service for mimetype" << mimeType; } + if (!service) { + qCDebug(KTEPREVIEW) << "Found no preferred kpart service for mimetypes" << mimeTypes; + } + + // Update if the mode is changed. The signal may also be emitted, when a new + // url is loaded, therefore wait (QueuedConnection) for the document to load. + connect(m_previewedTextEditorDocument, &KTextEditor::Document::modeChanged, + this, &PreviewWidget::resetTextEditorView, Qt::QueuedConnection); + // Explicitly clear the old document, which otherwise might be accessed in + // m_partView->setDocument. + connect(m_previewedTextEditorDocument, &KTextEditor::Document::aboutToClose, + this, &PreviewWidget::unsetDocument); } // change of preview type? // TODO: find a better id than library? const QString serviceId = service ? service->library() : QString(); if (serviceId != m_currentServiceId) { if (m_partView) { - // clear kpart menu - m_xmlGuiFactory->removeClient(m_partView->kPart()); - m_kPartMenu->clear(); - - removeWidget(m_partView->widget()); - delete m_partView; + clearMenu(); } m_currentServiceId = serviceId; @@ -214,22 +239,41 @@ } if (m_partView) { - m_partView->setDocument(m_previewedTextEditorView->document()); + m_partView->setDocument(m_previewedTextEditorDocument); } m_updateAction->setEnabled(m_partView && !m_autoUpdateAction->isChecked()); const bool hasKPart = (m_partView && m_partView->kPart()); m_kPartMenuAction->setEnabled(hasKPart); m_aboutKPartAction->setEnabled(hasKPart); } +void PreviewWidget::unsetDocument(KTextEditor::Document* document) +{ + if (!m_partView || m_previewedTextEditorDocument != document) { + return; + } + + m_partView->setDocument(nullptr); + + // remove any current partview + clearMenu(); + m_partView = nullptr; + + m_currentServiceId.clear(); +} + void PreviewWidget::showEvent(QShowEvent* event) { Q_UNUSED(event); m_updateAction->setEnabled(m_partView && !m_autoUpdateAction->isChecked()); - setTextEditorView(m_mainWindow->activeView()); + if (m_lockAction->isChecked()) { + resetTextEditorView(m_previewedTextEditorDocument); + } else { + setTextEditorView(m_mainWindow->activeView()); + } } void PreviewWidget::hideEvent(QHideEvent* event) @@ -249,21 +293,7 @@ void PreviewWidget::toggleDocumentLocking(bool locked) { - if (locked) { - if (!m_partView) { - // nothing to do - return; - } - auto document = m_partView->document(); - connect(document, &KTextEditor::Document::aboutToClose, - this, &PreviewWidget::handleLockedDocumentClosing); - } else { - if (m_partView) { - auto document = m_partView->document(); - disconnect(document, &KTextEditor::Document::aboutToClose, - this, &PreviewWidget::handleLockedDocumentClosing); - } - // jump tp current view + if (!locked) { setTextEditorView(m_mainWindow->activeView()); } } @@ -281,19 +311,9 @@ void PreviewWidget::updatePreview() { - m_partView->updatePreview(); -} - -void PreviewWidget::handleLockedDocumentClosing() -{ - // remove any current partview - if (m_partView) { - removeWidget(m_partView->widget()); - delete m_partView; - m_partView = nullptr; + if (m_partView && m_partView->document()) { + m_partView->updatePreview(); } - - m_currentServiceId.clear(); } QWidget* PreviewWidget::createContainer(QWidget* parent, int index, const QDomElement& element, QAction*& containerAction) @@ -338,3 +358,17 @@ delete aboutDialog; } } + +void PreviewWidget::clearMenu() +{ + // clear kpart menu + m_xmlGuiFactory->removeClient(m_partView->kPart()); + m_kPartMenu->clear(); + + removeWidget(m_partView->widget()); + delete m_partView; + + m_updateAction->setEnabled(false); + m_kPartMenuAction->setEnabled(false); + m_aboutKPartAction->setEnabled(false); +}