KParts

readwritepart.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "readwritepart.h"
10#include "readwritepart_p.h"
11
12#include "kparts_logging.h"
13
14#define HAVE_KDIRNOTIFY __has_include(<KDirNotify>)
15#if HAVE_KDIRNOTIFY
16#include <KDirNotify>
17#endif
18
19#include <KIO/FileCopyJob>
20#include <KJobWidgets>
21#include <KLocalizedString>
22#include <KMessageBox>
23
24#include <QApplication>
25#include <QFileDialog>
26#include <QTemporaryFile>
27
28#include <qplatformdefs.h>
29
30#ifdef Q_OS_WIN
31#include <qt_windows.h> //CreateHardLink()
32#endif
33
34using namespace KParts;
35
37 : ReadOnlyPart(*new ReadWritePartPrivate(this, data), parent)
38{
39}
40
42{
43 // parent destructor will delete temp file
44 // we can't call our own closeUrl() here, because
45 // "cancel" wouldn't cancel anything. We have to assume
46 // the app called closeUrl() before destroying us.
47}
48
49void ReadWritePart::setReadWrite(bool readwrite)
50{
52
53 // Perhaps we should check isModified here and issue a warning if true
54 d->m_bReadWrite = readwrite;
55}
56
57void ReadWritePart::setModified(bool modified)
58{
60
61 // qDebug() << "setModified(" << (modified ? "true" : "false") << ")";
62 if (!d->m_bReadWrite && modified) {
63 qCCritical(KPARTSLOG) << "Can't set a read-only document to 'modified' !";
64 return;
65 }
66 d->m_bModified = modified;
67}
68
70{
71 setModified(true);
72}
73
75{
77
78 if (!isReadWrite() || !isModified()) {
79 return true;
80 }
81
82 QString docName = url().fileName();
83 if (docName.isEmpty()) {
84 docName = i18n("Untitled");
85 }
86
87 QWidget *parentWidget = widget();
88 if (!parentWidget) {
89 parentWidget = QApplication::activeWindow();
90 }
91
92 int res = KMessageBox::warningTwoActionsCancel(parentWidget,
93 i18n("The document \"%1\" has been modified.\n"
94 "Do you want to save your changes or discard them?",
95 docName),
96 i18n("Close Document"),
99
100 bool abortClose = false;
101 bool handled = false;
102
103 switch (res) {
105 Q_EMIT sigQueryClose(&handled, &abortClose);
106 if (!handled) {
107 if (d->m_url.isEmpty()) {
108 QUrl url = QFileDialog::getSaveFileUrl(parentWidget);
109 if (url.isEmpty()) {
110 return false;
111 }
112
113 saveAs(url);
114 } else {
115 save();
116 }
117 } else if (abortClose) {
118 return false;
119 }
120 return waitSaveComplete();
122 return true;
123 default: // case KMessageBox::Cancel :
124 return false;
125 }
126}
127
129{
130 abortLoad(); // just in case
131 if (isReadWrite() && isModified()) {
132 if (!queryClose()) {
133 return false;
134 }
135 }
136 // Not modified => ok and delete temp file.
137 return ReadOnlyPart::closeUrl();
138}
139
140bool ReadWritePart::closeUrl(bool promptToSave)
141{
142 return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl();
143}
144
146{
148
149 d->m_saveOk = false;
150 if (d->m_file.isEmpty()) { // document was created empty
151 d->prepareSaving();
152 }
153 if (saveFile()) {
154 return saveToUrl();
155 } else {
157 }
158 return false;
159}
160
162{
164
165 if (!url.isValid()) {
166 qCCritical(KPARTSLOG) << "saveAs: Malformed URL" << url;
167 return false;
168 }
169 d->m_duringSaveAs = true;
170 d->m_originalURL = d->m_url;
171 d->m_originalFilePath = d->m_file;
172 d->m_url = url; // Store where to upload in saveToURL
173 d->prepareSaving();
174 bool result = save(); // Save local file and upload local file
175 if (result) {
176 if (d->m_originalURL != d->m_url) {
177 Q_EMIT urlChanged(d->m_url);
178 }
179
180 Q_EMIT setWindowCaption(d->m_url.toDisplayString(QUrl::PreferLocalFile));
181 } else {
182 d->m_url = d->m_originalURL;
183 d->m_file = d->m_originalFilePath;
184 d->m_duringSaveAs = false;
185 d->m_originalURL = QUrl();
186 d->m_originalFilePath.clear();
187 }
188
189 return result;
190}
191
192// Set m_file correctly for m_url
193void ReadWritePartPrivate::prepareSaving()
194{
195 // Local file
196 if (m_url.isLocalFile()) {
197 if (m_bTemp) { // get rid of a possible temp file first
198 // (happens if previous url was remote)
199 QFile::remove(m_file);
200 m_bTemp = false;
201 }
202 m_file = m_url.toLocalFile();
203 } else {
204 // Remote file
205 // We haven't saved yet, or we did but locally - provide a temp file
206 if (m_file.isEmpty() || !m_bTemp) {
207 QTemporaryFile tempFile;
208 tempFile.setAutoRemove(false);
209 tempFile.open();
210 m_file = tempFile.fileName();
211 m_bTemp = true;
212 }
213 // otherwise, we already had a temp file
214 }
215}
216
217static inline bool makeHardLink(const QString &src, const QString &dest)
218{
219#ifndef Q_OS_WIN
220 return ::link(QFile::encodeName(src).constData(), QFile::encodeName(dest).constData()) == 0;
221#else
222 return CreateHardLinkW((LPCWSTR)dest.utf16(), (LPCWSTR)src.utf16(), nullptr) != 0;
223#endif
224}
225
227{
229
230 if (d->m_url.isLocalFile()) {
231 setModified(false);
233 // if m_url is a local file there won't be a temp file -> nothing to remove
234 Q_ASSERT(!d->m_bTemp);
235 d->m_saveOk = true;
236 d->m_duringSaveAs = false;
237 d->m_originalURL = QUrl();
238 d->m_originalFilePath.clear();
239 return true; // Nothing to do
240 } else {
241 if (d->m_uploadJob) {
242 QFile::remove(d->m_uploadJob->srcUrl().toLocalFile());
243 d->m_uploadJob->kill();
244 d->m_uploadJob = nullptr;
245 }
246 QTemporaryFile *tempFile = new QTemporaryFile();
247 tempFile->open();
248 QString uploadFile = tempFile->fileName();
249 delete tempFile;
250 QUrl uploadUrl = QUrl::fromLocalFile(uploadFile);
251 // Create hardlink
252 if (!makeHardLink(d->m_file, uploadFile)) {
253 // Uh oh, some error happened.
254 return false;
255 }
256 d->m_uploadJob = KIO::file_move(uploadUrl, d->m_url, -1, KIO::Overwrite);
257 KJobWidgets::setWindow(d->m_uploadJob, widget());
258
259 connect(d->m_uploadJob, &KJob::result, this, [d](KJob *job) {
260 d->slotUploadFinished(job);
261 });
262
263 return true;
264 }
265}
266
267void ReadWritePartPrivate::slotUploadFinished(KJob *)
268{
269 Q_Q(ReadWritePart);
270
271 if (m_uploadJob->error()) {
272 QFile::remove(m_uploadJob->srcUrl().toLocalFile());
273 QString error = m_uploadJob->errorString();
274 m_uploadJob = nullptr;
275 if (m_duringSaveAs) {
276 q->setUrl(m_originalURL);
277 m_file = m_originalFilePath;
278 }
279 Q_EMIT q->canceled(error);
280 } else {
281#if HAVE_KDIRNOTIFY
282 ::org::kde::KDirNotify::emitFilesAdded(m_url.adjusted(QUrl::RemoveFilename));
283#endif
284
285 m_uploadJob = nullptr;
286 q->setModified(false);
287 Q_EMIT q->completed();
288 m_saveOk = true;
289 }
290 m_duringSaveAs = false;
291 m_originalURL = QUrl();
292 m_originalFilePath.clear();
293 if (m_waitForSave) {
294 m_eventLoop.quit();
295 }
296}
297
299{
300 Q_D(const ReadWritePart);
301
302 return d->m_bReadWrite;
303}
304
306{
307 Q_D(const ReadWritePart);
308
309 return d->m_bModified;
310}
311
313{
315
316 if (!d->m_uploadJob) {
317 return d->m_saveOk;
318 }
319
320 d->m_waitForSave = true;
321
322 d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
323
324 d->m_waitForSave = false;
325
326 return d->m_saveOk;
327}
328
329#include "moc_readwritepart.cpp"
void result(KJob *job)
void setWindowCaption(const QString &caption)
Emitted by the part, to set the caption of the window(s) hosting this part.
virtual QWidget * widget()
Definition part.cpp:61
Base class for any "viewer" part.
void urlChanged(const QUrl &url)
Emitted by the part when url() changes.
void completed()
Emit this when you have completed loading data.
void canceled(const QString &errMsg)
Emit this if loading is canceled by the user or by an error.
virtual bool closeUrl()
Called when closing the current URL (for example, a document), for instance when switching to another...
Base class for an "editor" part.
virtual void setReadWrite(bool readwrite=true)
Changes the behavior of this part to readonly or readwrite.
~ReadWritePart() override
Destructor Applications using a ReadWritePart should make sure, before destroying it,...
virtual bool queryClose()
If the document has been modified, ask the user to save changes.
virtual bool saveAs(const QUrl &url)
Save the file to a new location.
virtual bool saveFile()=0
Save to a local file.
virtual bool saveToUrl()
Save the file.
bool waitSaveComplete()
Waits for any pending upload job to finish and returns whether the last save() action was successful.
virtual bool save()
Save the file in the location from which it was opened.
void sigQueryClose(bool *handled, bool *abortClosing)
set handled to true, if you don't want the default handling set abortClosing to true,...
ReadWritePart(QObject *parent=nullptr, const KPluginMetaData &data={})
Constructor See parent constructor for instructions.
bool closeUrl() override
Called when closing the current url (e.g.
void setModified()
Call setModified() whenever the contents get modified.
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT FileCopyJob * file_move(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
void setWindow(QObject *job, QWidget *widget)
ButtonCode warningTwoActionsCancel(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
The KParts namespace,.
KGuiItem save()
KGuiItem discard()
QWidget * activeWindow()
QByteArray encodeName(const QString &fileName)
bool remove()
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
const ushort * utf16() const const
virtual QString fileName() const const override
void setAutoRemove(bool b)
PreferLocalFile
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isValid() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 12:01:32 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.