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#include <KDirNotify>
15#include <KIO/FileCopyJob>
16#include <KJobWidgets>
17#include <KLocalizedString>
18#include <KMessageBox>
19
20#include <QApplication>
21#include <QFileDialog>
22#include <QTemporaryFile>
23
24#include <qplatformdefs.h>
25
26#ifdef Q_OS_WIN
27#include <qt_windows.h> //CreateHardLink()
28#endif
29
30using namespace KParts;
31
33 : ReadOnlyPart(*new ReadWritePartPrivate(this, data), parent)
34{
35}
36
38{
39 // parent destructor will delete temp file
40 // we can't call our own closeUrl() here, because
41 // "cancel" wouldn't cancel anything. We have to assume
42 // the app called closeUrl() before destroying us.
43}
44
45void ReadWritePart::setReadWrite(bool readwrite)
46{
48
49 // Perhaps we should check isModified here and issue a warning if true
50 d->m_bReadWrite = readwrite;
51}
52
53void ReadWritePart::setModified(bool modified)
54{
56
57 // qDebug() << "setModified(" << (modified ? "true" : "false") << ")";
58 if (!d->m_bReadWrite && modified) {
59 qCCritical(KPARTSLOG) << "Can't set a read-only document to 'modified' !";
60 return;
61 }
62 d->m_bModified = modified;
63}
64
66{
67 setModified(true);
68}
69
71{
73
74 if (!isReadWrite() || !isModified()) {
75 return true;
76 }
77
78 QString docName = url().fileName();
79 if (docName.isEmpty()) {
80 docName = i18n("Untitled");
81 }
82
83 QWidget *parentWidget = widget();
84 if (!parentWidget) {
85 parentWidget = QApplication::activeWindow();
86 }
87
88 int res = KMessageBox::warningTwoActionsCancel(parentWidget,
89 i18n("The document \"%1\" has been modified.\n"
90 "Do you want to save your changes or discard them?",
91 docName),
92 i18n("Close Document"),
95
96 bool abortClose = false;
97 bool handled = false;
98
99 switch (res) {
101 Q_EMIT sigQueryClose(&handled, &abortClose);
102 if (!handled) {
103 if (d->m_url.isEmpty()) {
104 QUrl url = QFileDialog::getSaveFileUrl(parentWidget);
105 if (url.isEmpty()) {
106 return false;
107 }
108
109 saveAs(url);
110 } else {
111 save();
112 }
113 } else if (abortClose) {
114 return false;
115 }
116 return waitSaveComplete();
118 return true;
119 default: // case KMessageBox::Cancel :
120 return false;
121 }
122}
123
125{
126 abortLoad(); // just in case
127 if (isReadWrite() && isModified()) {
128 if (!queryClose()) {
129 return false;
130 }
131 }
132 // Not modified => ok and delete temp file.
133 return ReadOnlyPart::closeUrl();
134}
135
136bool ReadWritePart::closeUrl(bool promptToSave)
137{
138 return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl();
139}
140
142{
144
145 d->m_saveOk = false;
146 if (d->m_file.isEmpty()) { // document was created empty
147 d->prepareSaving();
148 }
149 if (saveFile()) {
150 return saveToUrl();
151 } else {
153 }
154 return false;
155}
156
158{
160
161 if (!url.isValid()) {
162 qCCritical(KPARTSLOG) << "saveAs: Malformed URL" << url;
163 return false;
164 }
165 d->m_duringSaveAs = true;
166 d->m_originalURL = d->m_url;
167 d->m_originalFilePath = d->m_file;
168 d->m_url = url; // Store where to upload in saveToURL
169 d->prepareSaving();
170 bool result = save(); // Save local file and upload local file
171 if (result) {
172 if (d->m_originalURL != d->m_url) {
173 Q_EMIT urlChanged(d->m_url);
174 }
175
176 Q_EMIT setWindowCaption(d->m_url.toDisplayString());
177 } else {
178 d->m_url = d->m_originalURL;
179 d->m_file = d->m_originalFilePath;
180 d->m_duringSaveAs = false;
181 d->m_originalURL = QUrl();
182 d->m_originalFilePath.clear();
183 }
184
185 return result;
186}
187
188// Set m_file correctly for m_url
189void ReadWritePartPrivate::prepareSaving()
190{
191 // Local file
192 if (m_url.isLocalFile()) {
193 if (m_bTemp) { // get rid of a possible temp file first
194 // (happens if previous url was remote)
195 QFile::remove(m_file);
196 m_bTemp = false;
197 }
198 m_file = m_url.toLocalFile();
199 } else {
200 // Remote file
201 // We haven't saved yet, or we did but locally - provide a temp file
202 if (m_file.isEmpty() || !m_bTemp) {
203 QTemporaryFile tempFile;
204 tempFile.setAutoRemove(false);
205 tempFile.open();
206 m_file = tempFile.fileName();
207 m_bTemp = true;
208 }
209 // otherwise, we already had a temp file
210 }
211}
212
213static inline bool makeHardLink(const QString &src, const QString &dest)
214{
215#ifndef Q_OS_WIN
216 return ::link(QFile::encodeName(src).constData(), QFile::encodeName(dest).constData()) == 0;
217#else
218 return CreateHardLinkW((LPCWSTR)dest.utf16(), (LPCWSTR)src.utf16(), nullptr) != 0;
219#endif
220}
221
223{
225
226 if (d->m_url.isLocalFile()) {
227 setModified(false);
229 // if m_url is a local file there won't be a temp file -> nothing to remove
230 Q_ASSERT(!d->m_bTemp);
231 d->m_saveOk = true;
232 d->m_duringSaveAs = false;
233 d->m_originalURL = QUrl();
234 d->m_originalFilePath.clear();
235 return true; // Nothing to do
236 } else {
237 if (d->m_uploadJob) {
238 QFile::remove(d->m_uploadJob->srcUrl().toLocalFile());
239 d->m_uploadJob->kill();
240 d->m_uploadJob = nullptr;
241 }
242 QTemporaryFile *tempFile = new QTemporaryFile();
243 tempFile->open();
244 QString uploadFile = tempFile->fileName();
245 delete tempFile;
246 QUrl uploadUrl = QUrl::fromLocalFile(uploadFile);
247 // Create hardlink
248 if (!makeHardLink(d->m_file, uploadFile)) {
249 // Uh oh, some error happened.
250 return false;
251 }
252 d->m_uploadJob = KIO::file_move(uploadUrl, d->m_url, -1, KIO::Overwrite);
253 KJobWidgets::setWindow(d->m_uploadJob, widget());
254
255 connect(d->m_uploadJob, &KJob::result, this, [d](KJob *job) {
256 d->slotUploadFinished(job);
257 });
258
259 return true;
260 }
261}
262
263void ReadWritePartPrivate::slotUploadFinished(KJob *)
264{
265 Q_Q(ReadWritePart);
266
267 if (m_uploadJob->error()) {
268 QFile::remove(m_uploadJob->srcUrl().toLocalFile());
269 QString error = m_uploadJob->errorString();
270 m_uploadJob = nullptr;
271 if (m_duringSaveAs) {
272 q->setUrl(m_originalURL);
273 m_file = m_originalFilePath;
274 }
275 Q_EMIT q->canceled(error);
276 } else {
277#ifndef Q_OS_ANDROID
278 ::org::kde::KDirNotify::emitFilesAdded(m_url.adjusted(QUrl::RemoveFilename));
279#endif
280
281 m_uploadJob = nullptr;
282 q->setModified(false);
283 Q_EMIT q->completed();
284 m_saveOk = true;
285 }
286 m_duringSaveAs = false;
287 m_originalURL = QUrl();
288 m_originalFilePath.clear();
289 if (m_waitForSave) {
290 m_eventLoop.quit();
291 }
292}
293
295{
296 Q_D(const ReadWritePart);
297
298 return d->m_bReadWrite;
299}
300
302{
303 Q_D(const ReadWritePart);
304
305 return d->m_bModified;
306}
307
309{
311
312 if (!d->m_uploadJob) {
313 return d->m_saveOk;
314 }
315
316 d->m_waitForSave = true;
317
318 d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
319
320 d->m_waitForSave = false;
321
322 return d->m_saveOk;
323}
324
325#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)
RemoveFilename
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 Tue Mar 26 2024 11:15:54 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.