| 1 | /* |
| 2 | This file is part of the KDE libraries |
| 3 | |
| 4 | SPDX-FileCopyrightText: 2006 Jacob R Rideout <kde@jacobrideout.net> |
| 5 | |
| 6 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #ifndef KAUTOSAVEFILE_H |
| 10 | #define KAUTOSAVEFILE_H |
| 11 | |
| 12 | #include <kcoreaddons_export.h> |
| 13 | |
| 14 | #include <QFile> |
| 15 | #include <QList> |
| 16 | #include <QUrl> |
| 17 | |
| 18 | #include <memory> |
| 19 | |
| 20 | class KAutoSaveFilePrivate; |
| 21 | /*! |
| 22 | * \class KAutoSaveFile |
| 23 | * \inmodule KCoreAddons |
| 24 | * |
| 25 | * \brief Creates and manages a temporary "auto-save" file. |
| 26 | * |
| 27 | * Autosave files are temporary files that applications use to store |
| 28 | * the unsaved data in a file they have open for |
| 29 | * editing. KAutoSaveFile allows you to easily create and manage such |
| 30 | * files, as well as to recover the unsaved data left over by a |
| 31 | * crashed or otherwise gone process. |
| 32 | * |
| 33 | * Each KAutoSaveFile object is associated with one specific file that |
| 34 | * the application holds open. KAutoSaveFile is also a QObject, so it |
| 35 | * can be reparented to the actual opened file object, so as to manage |
| 36 | * the lifetime of the temporary file. |
| 37 | * |
| 38 | * Typical use consists of: |
| 39 | * \list |
| 40 | * \li verifying whether stale autosave files exist for the opened file |
| 41 | * \li deciding whether to recover the old, autosaved data |
| 42 | * \li if not recovering, creating a KAutoSaveFile object for the opened file |
| 43 | * \li during normal execution of the program, periodically save unsaved |
| 44 | * data into the KAutoSaveFile file. |
| 45 | * \endlist |
| 46 | * |
| 47 | * KAutoSaveFile holds a lock on the autosave file, so it's safe to |
| 48 | * delete the file and recreate it later. Because of that, disposing |
| 49 | * of stale autosave files should be done with releaseLock(). No lock is |
| 50 | * held on the managed file. |
| 51 | * |
| 52 | * Examples: |
| 53 | * Opening a new file: |
| 54 | * \code |
| 55 | * void Document::open(const QUrl &url) |
| 56 | * { |
| 57 | * // check whether autosave files exist: |
| 58 | * const QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::staleFiles(url); |
| 59 | * if (!staleFiles.isEmpty()) { |
| 60 | * if (KMessageBox::questionTwoActions(parent, |
| 61 | * "Auto-saved files exist. Do you want to recover them now?", |
| 62 | * "File Recovery", |
| 63 | * KGuiItem("Recover"), KGuiItem("Do Not recover")) == KMessageBox::PrimaryAction) { |
| 64 | * recoverFiles(staleFiles); |
| 65 | * return; |
| 66 | * } else { |
| 67 | * // remove the stale files |
| 68 | * for (KAutoSaveFile *stale : staleFiles) { |
| 69 | * stale->open(QIODevice::ReadWrite); |
| 70 | * delete stale; |
| 71 | * } |
| 72 | * } |
| 73 | * } |
| 74 | * |
| 75 | * // create new autosave object |
| 76 | * m_autosave = new KAutoSaveFile(url, this); |
| 77 | * |
| 78 | * // continue the process of opening file 'url' |
| 79 | * ... |
| 80 | * } |
| 81 | * \endcode |
| 82 | * |
| 83 | * The function recoverFiles could loop over the list of files and do this: |
| 84 | * \code |
| 85 | * for (KAutoSaveFile *stale : staleFiles) { |
| 86 | * if (!stale->open(QIODevice::ReadWrite)) { |
| 87 | * // show an error message; we could not steal the lockfile |
| 88 | * // maybe another application got to the file before us? |
| 89 | * delete stale; |
| 90 | * continue; |
| 91 | * } |
| 92 | * Document *doc = new Document; |
| 93 | * doc->m_autosave = stale; |
| 94 | * stale->setParent(doc); // reparent |
| 95 | * |
| 96 | * doc->setUrl(stale->managedFile()); |
| 97 | * doc->setContents(stale->readAll()); |
| 98 | * doc->setState(Document::Modified); // mark it as modified and unsaved |
| 99 | * |
| 100 | * documentManager->addDocument(doc); |
| 101 | * } |
| 102 | * \endcode |
| 103 | * |
| 104 | * If the file is unsaved, periodically write the contents to the save file: |
| 105 | * \code |
| 106 | * if (!m_autosave->isOpen() && !m_autosave->open(QIODevice::ReadWrite)) { |
| 107 | * // show error: could not open the autosave file |
| 108 | * } |
| 109 | * m_autosave->write(contents()); |
| 110 | * \endcode |
| 111 | * |
| 112 | * When the user saves the file, the autosaved file is no longer |
| 113 | * necessary and can be removed or emptied. |
| 114 | * \code |
| 115 | * m_autosave->resize(0); // leaves the file open |
| 116 | * \endcode |
| 117 | * |
| 118 | * \code |
| 119 | * m_autosave->remove(); // closes the file |
| 120 | * \endcode |
| 121 | * |
| 122 | */ |
| 123 | class KCOREADDONS_EXPORT KAutoSaveFile : public QFile |
| 124 | { |
| 125 | Q_OBJECT |
| 126 | public: |
| 127 | /*! |
| 128 | * Constructs a KAutoSaveFile for file \a filename. The temporary |
| 129 | * file is not opened or created until actually needed. The file |
| 130 | * \a filename does not have to exist for KAutoSaveFile to be |
| 131 | * constructed (if it exists, it will not be touched). |
| 132 | * |
| 133 | * \a filename the filename that this KAutoSaveFile refers to |
| 134 | * |
| 135 | * \a parent the parent object |
| 136 | */ |
| 137 | explicit KAutoSaveFile(const QUrl &filename, QObject *parent = nullptr); |
| 138 | |
| 139 | /*! |
| 140 | * \overload |
| 141 | * Constructs a KAutoSaveFile object. Note that you need to call |
| 142 | * setManagedFile() before calling open(). |
| 143 | * |
| 144 | * \a parent the parent object |
| 145 | */ |
| 146 | explicit KAutoSaveFile(QObject *parent = nullptr); |
| 147 | |
| 148 | /*! |
| 149 | * Destroys the KAutoSaveFile object, removes the autosave |
| 150 | * file and drops the lock being held (if any). |
| 151 | */ |
| 152 | ~KAutoSaveFile() override; |
| 153 | |
| 154 | /*! |
| 155 | * Retrieves the URL of the file managed by KAutoSaveFile. This |
| 156 | * is the same URL that was given to setManagedFile() or the |
| 157 | * KAutoSaveFile constructor. |
| 158 | * |
| 159 | * This is the name of the real file being edited by the |
| 160 | * application. To get the name of the temporary file where data |
| 161 | * can be saved, use fileName() (after you have called open()). |
| 162 | */ |
| 163 | QUrl managedFile() const; |
| 164 | |
| 165 | /*! |
| 166 | * Sets the URL of the file managed by KAutoSaveFile. This should |
| 167 | * be the name of the real file being edited by the application. |
| 168 | * If the file was previously set, this function calls releaseLock(). |
| 169 | * |
| 170 | * \a filename the filename that this KAutoSaveFile refers to |
| 171 | */ |
| 172 | void setManagedFile(const QUrl &filename); |
| 173 | |
| 174 | /*! |
| 175 | * Closes the autosave file resource and removes the lock |
| 176 | * file. The file name returned by fileName() will no longer be |
| 177 | * protected and can be overwritten by another application at any |
| 178 | * time. To obtain a new lock, call open() again. |
| 179 | * |
| 180 | * This function calls remove(), so the autosave temporary file |
| 181 | * will be removed too. |
| 182 | */ |
| 183 | virtual void releaseLock(); |
| 184 | |
| 185 | /*! |
| 186 | * Opens the autosave file and locks it if it wasn't already |
| 187 | * locked. The name of the temporary file where data can be saved |
| 188 | * to will be set by this function and can be retrieved with |
| 189 | * fileName(). It will not change unless releaseLock() is called. No |
| 190 | * other application will attempt to edit such a file either while |
| 191 | * the lock is held. |
| 192 | * |
| 193 | * \a openmode the mode that should be used to open the file, |
| 194 | * probably QIODevice::ReadWrite |
| 195 | * |
| 196 | * Returns \c true if the file could be opened (= locked and |
| 197 | * created), false if the operation failed |
| 198 | */ |
| 199 | bool open(OpenMode openmode) override; |
| 200 | |
| 201 | /*! |
| 202 | * Checks for stale autosave files for the file \a url. Returns a list |
| 203 | * of autosave files that contain autosaved data left behind by |
| 204 | * other instances of the application, due to crashing or |
| 205 | * otherwise uncleanly exiting. |
| 206 | * |
| 207 | * It is the application's job to determine what to do with such |
| 208 | * unsaved data. Generally, this is done by asking the user if he |
| 209 | * wants to see the recovered data, and then allowing the user to |
| 210 | * save if he wants to. |
| 211 | * |
| 212 | * If not given, the application name is obtained from |
| 213 | * QCoreApplication, so be sure to have set it correctly before |
| 214 | * calling this function. |
| 215 | * |
| 216 | * This function returns a list of unopened KAutoSaveFile |
| 217 | * objects. By calling open() on them, the application will steal |
| 218 | * the lock. Subsequent releaseLock() or deleting of the object will |
| 219 | * then erase the stale autosave file. |
| 220 | * |
| 221 | * The application owns all returned KAutoSaveFile objects and is |
| 222 | * responsible for deleting them when no longer needed. Remember that |
| 223 | * deleting the KAutoSaveFile will release the file lock and remove the |
| 224 | * stale autosave file. |
| 225 | */ |
| 226 | static QList<KAutoSaveFile *> staleFiles(const QUrl &url, const QString &applicationName = QString()); |
| 227 | |
| 228 | /*! |
| 229 | * Returns all stale autosave files left behind by crashed or |
| 230 | * otherwise gone instances of this application. |
| 231 | * |
| 232 | * If not given, the application name is obtained from |
| 233 | * QCoreApplication, so be sure to have set it correctly before |
| 234 | * calling this function. |
| 235 | * |
| 236 | * See staleFiles() for information on the returned objects. |
| 237 | * |
| 238 | * The application owns all returned KAutoSaveFile objects and is |
| 239 | * responsible for deleting them when no longer needed. Remember that |
| 240 | * deleting the KAutoSaveFile will release the file lock and remove the |
| 241 | * stale autosave file. |
| 242 | */ |
| 243 | static QList<KAutoSaveFile *> allStaleFiles(const QString &applicationName = QString()); |
| 244 | |
| 245 | private: |
| 246 | Q_DISABLE_COPY(KAutoSaveFile) |
| 247 | friend class KAutoSaveFilePrivate; |
| 248 | std::unique_ptr<KAutoSaveFilePrivate> const d; |
| 249 | }; |
| 250 | |
| 251 | #endif // KAUTOSAVEFILE_H |
| 252 | |