File indexing completed on 2024-12-08 04:27:18

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #pragma once
0007 
0008 /** This file contains a collection of macros that can be used in model related classes.
0009     The class only needs to have the following members:
0010     - For Push_undo : std::weak_ptr<DocUndoStack> m_undoStack;  this is a pointer to the undoStack
0011     - For Update_undo_redo: mutable QReadWriteLock m_lock; This is a lock that ensures safety in case of concurrent access. Note that the mutex must be
0012    recursive.
0013 
0014     See for example TimelineModel.
0015 
0016     Note that there also exists a version of update_undo_redo without the need for a lock (but prefer the mutex version where applicable)
0017 */
0018 
0019 /** This convenience macro adds lock/unlock ability to a given lambda function
0020    Note that it is automatically called when you push the lambda so you shouldn't have
0021    to call it directly yourself
0022 */
0023 #define LOCK_IN_LAMBDA(lambda)                                                                                                                                 \
0024     lambda = [this, lambda]() {                                                                                                                                \
0025         m_lock.lockForWrite();                                                                                                                                 \
0026         bool res_lambda = lambda();                                                                                                                            \
0027         m_lock.unlock();                                                                                                                                       \
0028         return res_lambda;                                                                                                                                     \
0029     };
0030 
0031 /** This convenience macro locks the mutex for reading.
0032 Note that it might happen that a thread is executing a write operation that requires
0033 reading a Read-protected property. In that case, we try to write lock it first (this will be granted since the lock is recursive)
0034 */
0035 #define READ_LOCK()                                                                                                                                            \
0036     std::unique_ptr<QReadLocker> rlocker(new QReadLocker(nullptr));                                                                                            \
0037     std::unique_ptr<QWriteLocker> wlocker(new QWriteLocker(nullptr));                                                                                          \
0038     if (m_lock.tryLockForWrite()) {                                                                                                                            \
0039         /*we yield ownership of the lock to the WriteLocker*/                                                                                                  \
0040         m_lock.unlock();                                                                                                                                       \
0041         wlocker.reset(new QWriteLocker(&m_lock));                                                                                                              \
0042     } else {                                                                                                                                                   \
0043         rlocker.reset(new QReadLocker(&m_lock));                                                                                                               \
0044     }
0045 
0046 /** @brief This macro takes some lambdas that represent undo/redo for an operation and the text (name) associated with this operation
0047  * The lambdas are transformed to make sure they lock access to the class they operate on.
0048  * Then they are added on the undoStack
0049  */
0050 #define PUSH_UNDO(undo, redo, text)                                                                                                                            \
0051     if (auto ptr = m_undoStack.lock()) {                                                                                                                       \
0052         ptr->push(new FunctionalUndoCommand(undo, redo, text));                                                                                                \
0053     } else {                                                                                                                                                   \
0054         qDebug() << "ERROR : unable to access undo stack";                                                                                                     \
0055         Q_ASSERT(false);                                                                                                                                       \
0056     }
0057 
0058 /** @brief This macro takes as parameter one atomic operation and its reverse, and update
0059  * the undo and redo functional stacks/queue accordingly
0060  * This should be used in the rare case where we don't need a lock mutex. In general, prefer the other version
0061  */
0062 #define UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo)                                                                                                \
0063     undo = [reverse, undo]() {                                                                                                                                 \
0064         bool v = reverse();                                                                                                                                    \
0065         return undo() && v;                                                                                                                                    \
0066     };                                                                                                                                                         \
0067     redo = [operation, redo]() {                                                                                                                               \
0068         bool v = redo();                                                                                                                                       \
0069         return operation() && v;                                                                                                                               \
0070     };
0071 /** @brief This macro takes as parameter one atomic operation and its reverse, and update
0072  *  the undo and redo functional stacks/queue accordingly
0073  *  It will also ensure that operation and reverse are dealing with mutexes
0074  */
0075 #define UPDATE_UNDO_REDO(operation, reverse, undo, redo)                                                                                                       \
0076     LOCK_IN_LAMBDA(operation)                                                                                                                                  \
0077     LOCK_IN_LAMBDA(reverse)                                                                                                                                    \
0078     UPDATE_UNDO_REDO_NOLOCK(operation, reverse, undo, redo)