File indexing completed on 2024-05-12 03:54:54

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 2006 Jacob R Rideout <kde@jacobrideout.net>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef KAUTOSAVEFILE_H
0010 #define KAUTOSAVEFILE_H
0011 
0012 #include <kcoreaddons_export.h>
0013 
0014 #include <QFile>
0015 #include <QList>
0016 #include <QUrl>
0017 
0018 #include <memory>
0019 
0020 class KAutoSaveFilePrivate;
0021 /**
0022  * \class KAutoSaveFile kautosavefile.h <KAutoSaveFile>
0023  *
0024  * @brief Creates and manages a temporary "auto-save" file.
0025  * Autosave files are temporary files that applications use to store
0026  * the unsaved data in a file they have open for
0027  * editing. KAutoSaveFile allows you to easily create and manage such
0028  * files, as well as to recover the unsaved data left over by a
0029  * crashed or otherwise gone process.
0030  *
0031  * Each KAutoSaveFile object is associated with one specific file that
0032  * the application holds open. KAutoSaveFile is also a QObject, so it
0033  * can be reparented to the actual opened file object, so as to manage
0034  * the lifetime of the temporary file.
0035  *
0036  * Typical use consists of:
0037  * - verifying whether stale autosave files exist for the opened file
0038  * - deciding whether to recover the old, autosaved data
0039  * - if not recovering, creating a KAutoSaveFile object for the opened file
0040  * - during normal execution of the program, periodically save unsaved
0041  *   data into the KAutoSaveFile file.
0042  *
0043  * KAutoSaveFile holds a lock on the autosave file, so it's safe to
0044  * delete the file and recreate it later. Because of that, disposing
0045  * of stale autosave files should be done with releaseLock(). No lock is
0046  * held on the managed file.
0047  *
0048  * Examples:
0049  * Opening a new file:
0050  * @code
0051  *   void Document::open(const QUrl &url)
0052  *   {
0053  *       // check whether autosave files exist:
0054  *       const QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::staleFiles(url);
0055  *       if (!staleFiles.isEmpty()) {
0056  *           if (KMessageBox::questionTwoActions(parent,
0057  *                                               "Auto-saved files exist. Do you want to recover them now?",
0058  *                                               "File Recovery",
0059  *                                               KGuiItem("Recover"), KGuiItem("Do Not recover")) == KMessageBox::PrimaryAction) {
0060  *               recoverFiles(staleFiles);
0061  *               return;
0062  *           } else {
0063  *               // remove the stale files
0064  *               for (KAutoSaveFile *stale : staleFiles) {
0065  *                   stale->open(QIODevice::ReadWrite);
0066  *                   delete stale;
0067  *               }
0068  *           }
0069  *       }
0070  *
0071  *       // create new autosave object
0072  *       m_autosave = new KAutoSaveFile(url, this);
0073  *
0074  *       // continue the process of opening file 'url'
0075  *       ...
0076  *   }
0077  * @endcode
0078  *
0079  * The function recoverFiles could loop over the list of files and do this:
0080  * @code
0081  *   for (KAutoSaveFile *stale : staleFiles) {
0082  *       if (!stale->open(QIODevice::ReadWrite)) {
0083  *           // show an error message; we could not steal the lockfile
0084  *           // maybe another application got to the file before us?
0085  *           delete stale;
0086  *           continue;
0087  *       }
0088  *       Document *doc = new Document;
0089  *       doc->m_autosave = stale;
0090  *       stale->setParent(doc); // reparent
0091  *
0092  *       doc->setUrl(stale->managedFile());
0093  *       doc->setContents(stale->readAll());
0094  *       doc->setState(Document::Modified); // mark it as modified and unsaved
0095  *
0096  *       documentManager->addDocument(doc);
0097  *   }
0098  * @endcode
0099  *
0100  * If the file is unsaved, periodically write the contents to the save file:
0101  * @code
0102  *   if (!m_autosave->isOpen() && !m_autosave->open(QIODevice::ReadWrite)) {
0103  *       // show error: could not open the autosave file
0104  *   }
0105  *   m_autosave->write(contents());
0106  * @endcode
0107  *
0108  * When the user saves the file, the autosaved file is no longer
0109  * necessary and can be removed or emptied.
0110  * @code
0111  *    m_autosave->resize(0);    // leaves the file open
0112  * @endcode
0113  *
0114  * @code
0115  *    m_autosave->remove();     // closes the file
0116  * @endcode
0117  *
0118  * @author Jacob R Rideout <kde@jacobrideout.net>
0119  */
0120 class KCOREADDONS_EXPORT KAutoSaveFile : public QFile
0121 {
0122     Q_OBJECT
0123 public:
0124     /**
0125      * Constructs a KAutoSaveFile for file @p filename. The temporary
0126      * file is not opened or created until actually needed. The file
0127      * @p filename does not have to exist for KAutoSaveFile to be
0128      * constructed (if it exists, it will not be touched).
0129      *
0130      * @param filename the filename that this KAutoSaveFile refers to
0131      * @param parent the parent object
0132      */
0133     explicit KAutoSaveFile(const QUrl &filename, QObject *parent = nullptr);
0134 
0135     /**
0136      * @overload
0137      * Constructs a KAutoSaveFile object. Note that you need to call
0138      * setManagedFile() before calling open().
0139      *
0140      * @param parent the parent object
0141      */
0142     explicit KAutoSaveFile(QObject *parent = nullptr);
0143 
0144     /**
0145      * Destroys the KAutoSaveFile object, removes the autosave
0146      * file and drops the lock being held (if any).
0147      */
0148     ~KAutoSaveFile() override;
0149 
0150     /**
0151      * Retrieves the URL of the file managed by KAutoSaveFile. This
0152      * is the same URL that was given to setManagedFile() or the
0153      * KAutoSaveFile constructor.
0154      *
0155      * This is the name of the real file being edited by the
0156      * application. To get the name of the temporary file where data
0157      * can be saved, use fileName() (after you have called open()).
0158      */
0159     QUrl managedFile() const;
0160 
0161     /**
0162      * Sets the URL of the file managed by KAutoSaveFile. This should
0163      * be the name of the real file being edited by the application.
0164      * If the file was previously set, this function calls releaseLock().
0165      *
0166      * @param filename the filename that this KAutoSaveFile refers to
0167      */
0168     void setManagedFile(const QUrl &filename);
0169 
0170     /**
0171      * Closes the autosave file resource and removes the lock
0172      * file. The file name returned by fileName() will no longer be
0173      * protected and can be overwritten by another application at any
0174      * time. To obtain a new lock, call open() again.
0175      *
0176      * This function calls remove(), so the autosave temporary file
0177      * will be removed too.
0178      */
0179     virtual void releaseLock();
0180 
0181     /**
0182      * Opens the autosave file and locks it if it wasn't already
0183      * locked. The name of the temporary file where data can be saved
0184      * to will be set by this function and can be retrieved with
0185      * fileName(). It will not change unless releaseLock() is called. No
0186      * other application will attempt to edit such a file either while
0187      * the lock is held.
0188      *
0189      * @param openmode the mode that should be used to open the file,
0190      *        probably QIODevice::ReadWrite
0191      * @returns true if the file could be opened (= locked and
0192      * created), false if the operation failed
0193      */
0194     bool open(OpenMode openmode) override;
0195 
0196     /**
0197      * Checks for stale autosave files for the file @p url. Returns a list
0198      * of autosave files that contain autosaved data left behind by
0199      * other instances of the application, due to crashing or
0200      * otherwise uncleanly exiting.
0201      *
0202      * It is the application's job to determine what to do with such
0203      * unsaved data. Generally, this is done by asking the user if he
0204      * wants to see the recovered data, and then allowing the user to
0205      * save if he wants to.
0206      *
0207      * If not given, the application name is obtained from
0208      * QCoreApplication, so be sure to have set it correctly before
0209      * calling this function.
0210      *
0211      * This function returns a list of unopened KAutoSaveFile
0212      * objects. By calling open() on them, the application will steal
0213      * the lock. Subsequent releaseLock() or deleting of the object will
0214      * then erase the stale autosave file.
0215      *
0216      * The application owns all returned KAutoSaveFile objects and is
0217      * responsible for deleting them when no longer needed. Remember that
0218      * deleting the KAutoSaveFile will release the file lock and remove the
0219      * stale autosave file.
0220      */
0221     static QList<KAutoSaveFile *> staleFiles(const QUrl &url, const QString &applicationName = QString());
0222 
0223     /**
0224      * Returns all stale autosave files left behind by crashed or
0225      * otherwise gone instances of this application.
0226      *
0227      * If not given, the application name is obtained from
0228      * QCoreApplication, so be sure to have set it correctly before
0229      * calling this function.
0230      *
0231      * See staleFiles() for information on the returned objects.
0232      *
0233      * The application owns all returned KAutoSaveFile objects and is
0234      * responsible for deleting them when no longer needed. Remember that
0235      * deleting the KAutoSaveFile will release the file lock and remove the
0236      * stale autosave file.
0237      */
0238     static QList<KAutoSaveFile *> allStaleFiles(const QString &applicationName = QString());
0239 
0240 private:
0241     Q_DISABLE_COPY(KAutoSaveFile)
0242     friend class KAutoSaveFilePrivate;
0243     std::unique_ptr<KAutoSaveFilePrivate> const d;
0244 };
0245 
0246 #endif // KAUTOSAVEFILE_H