KParts

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

KDE's Doxygen guidelines are available online.