Changeset View
Changeset View
Standalone View
Standalone View
addons/preview/previewwidget.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (C) 2017 by Friedrich W. H. Kossebau <kossebau@kde.org> | ||||
3 | * | ||||
4 | * This library is free software; you can redistribute it and/or | ||||
5 | * modify it under the terms of the GNU Library General Public | ||||
6 | * License as published by the Free Software Foundation; either | ||||
7 | * version 2 of the License, or (at your option) any later version. | ||||
8 | * | ||||
9 | * This library is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
12 | * Library General Public License for more details. | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU Library General Public License | ||||
15 | * along with this library; see the file COPYING.LIB. If not, write to | ||||
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
17 | * Boston, MA 02110-1301, USA. | ||||
18 | */ | ||||
19 | | ||||
20 | #include "previewwidget.h" | ||||
21 | | ||||
22 | #include "ktexteditorpreviewplugin.h" | ||||
23 | #include "kpartview.h" | ||||
24 | #include <ktexteditorpreview_debug.h> | ||||
25 | | ||||
26 | // KF | ||||
27 | #include <KTextEditor/View> | ||||
28 | #include <KTextEditor/Document> | ||||
29 | #include <KTextEditor/MainWindow> | ||||
30 | | ||||
31 | #include <KService> | ||||
32 | #include <KMimeTypeTrader> | ||||
33 | #include <KConfigGroup> | ||||
34 | #include <KLocalizedString> | ||||
35 | #include <KToggleAction> | ||||
36 | #include <KGuiItem> | ||||
37 | | ||||
38 | // Qt | ||||
39 | #include <QLabel> | ||||
40 | #include <QIcon> | ||||
41 | #include <QAction> | ||||
42 | | ||||
43 | | ||||
44 | using namespace KTextEditorPreview; | ||||
45 | | ||||
46 | PreviewWidget::PreviewWidget(KTextEditorPreviewPlugin* core, KTextEditor::MainWindow* mainWindow, | ||||
47 | QWidget* parent) | ||||
48 | : QStackedWidget(parent) | ||||
49 | , m_core(core) | ||||
50 | , m_mainWindow(mainWindow) | ||||
51 | { | ||||
52 | m_lockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-unlocked")), i18n("Lock Current Document"), this); | ||||
53 | m_lockAction->setToolTip(i18n("Lock preview to current document")); | ||||
54 | m_lockAction->setCheckedState(KGuiItem(i18n("Unlock Current View"), QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Unlock current view"))); | ||||
55 | m_lockAction->setChecked(false); | ||||
56 | connect(m_lockAction, &QAction::triggered, this, &PreviewWidget::toggleDocumentLocking); | ||||
57 | addAction(m_lockAction); | ||||
58 | | ||||
59 | // TODO: better icon(s) | ||||
60 | const QIcon autoUpdateIcon = QIcon::fromTheme(QStringLiteral("media-playback-start")); | ||||
61 | m_autoUpdateAction = new KToggleAction(autoUpdateIcon, i18n("Automatically Update Preview"), this); | ||||
62 | m_autoUpdateAction->setToolTip(i18n("Enable automatic updates of the preview to the current document content")); | ||||
63 | m_autoUpdateAction->setCheckedState(KGuiItem(i18n("Manually Update Preview"), autoUpdateIcon, i18n("Disable automatic updates of the preview to the current document content"))); | ||||
64 | m_autoUpdateAction->setChecked(true); | ||||
65 | connect(m_autoUpdateAction, &QAction::triggered, this, &PreviewWidget::toggleAutoUpdating); | ||||
66 | addAction(m_autoUpdateAction); | ||||
67 | | ||||
68 | m_updateAction = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Update Preview"), this); | ||||
69 | m_updateAction->setToolTip(i18n("Update the preview to the current document content")); | ||||
70 | connect(m_updateAction, &QAction::triggered, this, &PreviewWidget::updatePreview); | ||||
71 | m_updateAction->setEnabled(false); | ||||
72 | addAction(m_updateAction); | ||||
73 | | ||||
74 | auto label = new QLabel(i18n("No preview available."), this); | ||||
75 | label->setAlignment(Qt::AlignHCenter); | ||||
76 | addWidget(label); | ||||
77 | | ||||
78 | connect(m_mainWindow, SIGNAL(viewChanged(KTextEditor::View*)), | ||||
79 | this, SLOT(setTextEditorView(KTextEditor::View*))); | ||||
80 | | ||||
81 | setTextEditorView(m_mainWindow->activeView()); | ||||
82 | } | ||||
83 | | ||||
84 | PreviewWidget::~PreviewWidget() = default; | ||||
85 | | ||||
86 | void PreviewWidget::readSessionConfig(const KConfigGroup& configGroup) | ||||
87 | { | ||||
88 | // TODO: also store document id/url and see to catch the same document on restoring config | ||||
89 | m_lockAction->setChecked(configGroup.readEntry("documentLocked", false)); | ||||
90 | m_autoUpdateAction->setChecked(configGroup.readEntry("automaticUpdate", true)); | ||||
91 | } | ||||
92 | | ||||
93 | void PreviewWidget::writeSessionConfig(KConfigGroup& configGroup) const | ||||
94 | { | ||||
95 | configGroup.writeEntry("documentLocked", m_lockAction->isChecked()); | ||||
96 | configGroup.writeEntry("automaticUpdate", m_autoUpdateAction->isChecked()); | ||||
97 | } | ||||
98 | | ||||
99 | void PreviewWidget::setTextEditorView(KTextEditor::View* view) | ||||
100 | { | ||||
101 | if ((m_previewedTextEditorView == view) || | ||||
102 | !isVisible() || | ||||
103 | m_lockAction->isChecked()) { | ||||
104 | return; | ||||
105 | } | ||||
106 | | ||||
107 | m_previewedTextEditorView = view; | ||||
108 | | ||||
109 | KService::Ptr service; | ||||
110 | if (m_previewedTextEditorView) { | ||||
111 | // TODO: mimetype is not set for new document which have not been saved yet. | ||||
112 | // needs another way to get this info, or perhaps some proper fix in Kate/Kdevelop | ||||
113 | // to guess the mimetype based on current content, selected mode/highlighting etc. | ||||
114 | // which then also would needs a signal mimetypeChanged and handling here | ||||
115 | const auto mimeType = m_previewedTextEditorView->document()->mimeType(); | ||||
116 | service = KMimeTypeTrader::self()->preferredService(mimeType, QStringLiteral("KParts/ReadOnlyPart")); | ||||
117 | if (service) { | ||||
118 | qCDebug(KTEPREVIEW) << "Found preferred kpart service named" << service->name() | ||||
119 | << "with library" <<service->library() | ||||
120 | << "for mimetype" << mimeType; | ||||
121 | | ||||
122 | if (service->library().isEmpty()) { | ||||
123 | qCWarning(KTEPREVIEW) << "Discarding preferred kpart service due to empty library name:" << service->name(); | ||||
124 | service.reset(); | ||||
125 | } | ||||
126 | | ||||
127 | // no interest in kparts which also just display the text (like katepart itself) | ||||
128 | // TODO: what about parts which also support importing plain text and turning into richer format | ||||
129 | // and thus have it in their mimetypes list? | ||||
130 | // could that perhaps be solved by introducing the concept of "native" and "imported" mimetypes? | ||||
131 | // or making a distinction between source editors/viewers and final editors/viewers? | ||||
132 | // latter would also help other source editors/viewers like a hexeditor, which "supports" any mimetype | ||||
133 | if (service && service->mimeTypes().contains(QStringLiteral("text/plain"))) { | ||||
134 | qCDebug(KTEPREVIEW) << "Blindly discarding preferred service as it also supports text/plain, to avoid useless plain/text preview."; | ||||
135 | service.reset(); | ||||
136 | } | ||||
137 | } else { | ||||
138 | qCDebug(KTEPREVIEW) << "Found no preferred kpart service for mimetype" << mimeType; | ||||
139 | } | ||||
140 | } | ||||
141 | | ||||
142 | // change of preview type? | ||||
143 | // TODO: find a better id than library? | ||||
144 | const QString serviceId = service ? service->library() : QString(); | ||||
145 | | ||||
146 | if (serviceId != m_currentServiceId) { | ||||
147 | if (m_partView) { | ||||
148 | removeWidget(m_partView->widget()); | ||||
149 | delete m_partView; | ||||
150 | } | ||||
151 | | ||||
152 | m_currentServiceId = serviceId; | ||||
153 | | ||||
154 | if (service) { | ||||
155 | qCDebug(KTEPREVIEW) << "Creating new kpart service instance."; | ||||
156 | m_partView = new KPartView(service, this); | ||||
157 | m_partView->setAutoUpdating(m_autoUpdateAction->isChecked()); | ||||
158 | int index = addWidget(m_partView->widget()); | ||||
159 | setCurrentIndex(index); | ||||
160 | } else { | ||||
161 | m_partView = nullptr; | ||||
162 | } | ||||
163 | } else { | ||||
164 | if (m_partView) { | ||||
165 | qCDebug(KTEPREVIEW) << "Reusing active kpart service instance."; | ||||
166 | } | ||||
167 | } | ||||
168 | | ||||
169 | if (m_partView) { | ||||
170 | m_partView->setDocument(m_previewedTextEditorView->document()); | ||||
171 | } | ||||
172 | | ||||
173 | m_updateAction->setEnabled(m_partView && !m_autoUpdateAction->isChecked()); | ||||
174 | } | ||||
175 | | ||||
176 | void PreviewWidget::showEvent(QShowEvent* event) | ||||
177 | { | ||||
178 | Q_UNUSED(event); | ||||
179 | | ||||
180 | m_updateAction->setEnabled(m_partView && !m_autoUpdateAction->isChecked()); | ||||
181 | | ||||
182 | setTextEditorView(m_mainWindow->activeView()); | ||||
183 | } | ||||
184 | | ||||
185 | void PreviewWidget::hideEvent(QHideEvent* event) | ||||
186 | { | ||||
187 | Q_UNUSED(event); | ||||
188 | | ||||
189 | // keep active part for reuse, but close preview document | ||||
190 | if (m_partView) { | ||||
191 | // TODO: we also get hide event in kdevelop when the view is changed, | ||||
192 | // need to find out how to filter this out or how to fix kdevelop | ||||
193 | // so currently keep the preview document | ||||
194 | // m_partView->setDocument(nullptr); | ||||
195 | } | ||||
196 | | ||||
197 | m_updateAction->setEnabled(false); | ||||
198 | } | ||||
199 | | ||||
200 | void PreviewWidget::toggleDocumentLocking(bool locked) | ||||
201 | { | ||||
202 | if (locked) { | ||||
203 | if (!m_partView) { | ||||
204 | // nothing to do | ||||
205 | return; | ||||
206 | } | ||||
207 | auto document = m_partView->document(); | ||||
208 | connect(document, &KTextEditor::Document::aboutToClose, | ||||
209 | this, &PreviewWidget::handleLockedDocumentClosing); | ||||
210 | } else { | ||||
211 | if (m_partView) { | ||||
212 | auto document = m_partView->document(); | ||||
213 | disconnect(document, &KTextEditor::Document::aboutToClose, | ||||
214 | this, &PreviewWidget::handleLockedDocumentClosing); | ||||
215 | } | ||||
216 | // jump tp current view | ||||
217 | setTextEditorView(m_mainWindow->activeView()); | ||||
218 | } | ||||
219 | } | ||||
220 | | ||||
221 | void PreviewWidget::toggleAutoUpdating(bool autoRefreshing) | ||||
222 | { | ||||
223 | if (!m_partView) { | ||||
224 | // nothing to do | ||||
225 | return; | ||||
226 | } | ||||
227 | | ||||
228 | m_updateAction->setEnabled(!autoRefreshing && isVisible()); | ||||
229 | m_partView->setAutoUpdating(autoRefreshing); | ||||
230 | } | ||||
231 | | ||||
232 | void PreviewWidget::updatePreview() | ||||
233 | { | ||||
234 | m_partView->updatePreview(); | ||||
235 | } | ||||
236 | | ||||
237 | void PreviewWidget::handleLockedDocumentClosing() | ||||
238 | { | ||||
239 | // remove any current partview | ||||
240 | if (m_partView) { | ||||
241 | removeWidget(m_partView->widget()); | ||||
242 | delete m_partView; | ||||
243 | m_partView = nullptr; | ||||
244 | } | ||||
245 | | ||||
246 | m_currentServiceId.clear(); | ||||
247 | } |