1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18 
19 /**
20 \ingroup libpgmodeler
21 \class OperationList
22 \brief Implements the operations to maintain a list of modifications made
23 by the user on database model objects. This class permits that user
24 undo / redo all the operations made.
25 \note <strong>Creation date:</strong> 17/07/2006
26 */
27 
28 #ifndef OPERATIONLIST_H
29 #define OPERATIONLIST_H
30 
31 #include "databasemodel.h"
32 #include "pgmodelerns.h"
33 #include "operation.h"
34 
35 class OperationList: public QObject {
36 	private:
37 		Q_OBJECT
38 
39 		//! \brief Inidcates that operation chaining is ignored temporarily
40 		bool ignore_chain;
41 
42 		XmlParser *xmlparser;
43 
44 		//! \brief List of objects that were removed / modified on the model
45 		vector<BaseObject *> object_pool;
46 
47 		/*! \brief List of objects that at the time of deletion from pool were still referenced
48 		 somehow on the model. The object is stored in this secondary list and
49 		 deleted when the whole list of operations is destroyed */
50 		vector<BaseObject *> not_removed_objs;
51 
52 		/*! \brief Stores the objects that were unallocated on the removeOperations() method. This maps
53 		is used in order to avoid double delete on pointers. */
54 		map<BaseObject *, bool> unallocated_objs;
55 
56 		//! \brief Stores the operations executed by the user
57 		vector<Operation *> operations;
58 
59 		//! \brief Database model that is linked with this operation list
60 		DatabaseModel *model;
61 
62 		//! \brief Maximum number of stored operations (global)
63 		static unsigned max_size;
64 
65 		/*! \brief Stores the type of chain to the next operation to be stored
66 		 in the list. This attribute is used in conjunction with the chaining
67 		 initialization / finalization methods. */
68 		unsigned next_op_chain;
69 
70 		//! \brief Current operation index
71 		int current_index;
72 
73 		/*! \brief Validates operations by checking whether they have registered objects in the pool.
74 		 If found any operation whose object is not in the pool it will be removed
75 		 because an object outside the pool does not give a guarantee that is being
76 		 referenced in the model. */
77 		void validateOperations();
78 
79 		//! \brief Checks whether the passed object is in the pool
80 		bool isObjectOnPool(BaseObject *object);
81 
82 		//! \brief Adds the object on the pool according to the operation type passed
83 		void addToPool(BaseObject *object, unsigned op_type);
84 
85 		/*! \brief Removes one object from the pool using its index and deallocating
86 		 it in case the object is not referenced on the model */
87 		void removeFromPool(unsigned obj_idx);
88 
89 		/*! \brief Executes the passed operation. The default behavior is the 'undo' if
90 		 the user passes the parameter 'redo=true' the method executes the
91 		 redo function */
92 		void executeOperation(Operation *operacao, bool redo);
93 
94 		//! \brief Returns the chain size from the current element
95 		unsigned getChainSize();
96 
97 	public:
98 		OperationList(DatabaseModel *model);
99 		virtual ~OperationList();
100 
101 		/*! \brief Starts chaining operations.
102 		 This means that all operations added after calling this
103 		 method will be considered to be performed all at once
104 		 with a single call to the redoOperation / undoOperation methods */
105 		void startOperationChain();
106 
107 		/*! \brief Finalizes the chaining marking the last operation on the list
108 		 as the end of operation chain */
109 		void finishOperationChain();
110 
111 		/*! \brief Cancels the execution of operations in the form of chaining,
112 		 but if the list is open with chaining operations included will be chained too.
113 		 This method helps in situations where is necessary to remove operations or
114 		 execute them one by one but keeping the chaining created earlier.
115 
116 		 Note: The user must cancel the annulment of chaining
117 					 to be able to finalize the operations chaining. If it does not
118 					 happens the operations will be created chained indefinitely */
119 		void ignoreOperationChain(bool value);
120 
121 		//! \brief Returns if the operation chaining where started
122 		bool isOperationChainStarted();
123 
124 		//! \brief Returns if an operation of the specified op_type is already registered for the object
125 		bool isObjectRegistered(BaseObject *object, unsigned op_type);
126 
127 		//! \brief Undo the current operation on the list
128 		void undoOperation();
129 
130 		//! \brief Redo the current operation on the list
131 		void redoOperation();
132 
133 		//! \brief Removes all the operations from the list
134 		void removeOperations();
135 
136 		//! \brief Gets the data from the operation with specified index
137 		void getOperationData(unsigned oper_idx, unsigned &oper_type, QString &obj_name, ObjectType &obj_type);
138 
139 		//! \brief Sets the maximum size for the list
140 		static void setMaximumSize(unsigned max);
141 
142 		/*! \brief Registers in the list of operations that the passed object suffered some kind
143 		 of modification (modified, removed, inserted, moved) in addition the method stores
144 		 its original content.
145 		 This method should ALWAYS be called before the object in question
146 		 suffers any operation in the model. If this method is called after an operation on the
147 		 object the order of restoration / re-execution of operations can be broken and cause
148 	 segmentations fault.
149 
150 	 In case of success this method returns an integer indicating the last registered operation ID */
151 		int registerObject(BaseObject *object, unsigned op_type, int object_idx=-1, BaseObject *parent_obj=nullptr);
152 
153 		//! \brief Gets the maximum size for the operation list
154 		unsigned getMaximumSize();
155 
156 		//! \brief Gets the current size for the operation list
157 		unsigned getCurrentSize();
158 
159 		//! \brief Gets the current operation index
160 		int getCurrentIndex();
161 
162 		//! \brief Returns if the list is prepared to execute redo operations
163 		bool isRedoAvailable();
164 
165 		//! \brief Returns if the list is prepared to execute undo operations
166 		bool isUndoAvailable();
167 
168 		/*! \brief Removes the last operation from the list. This method should be used with
169 		 care as it can break the chain of operations. It should be
170 		 used only when an exception is thrown after adding
171 		 some object on the list and if the user wants to discard this operation due
172 		 the thrown exception. If the last operation is part of a chain the entire chain
173 		 of operations is removed.
174 		 Warning: The execution of this method is different from the undo method because
175 		 the objects are removed pool but their states prior to adding it to the list are not
176 		 restored so this method can not be used deliberately. */
177 		void removeLastOperation();
178 
179 		/*! \brief Updates the index of the object when it suffers a movement in the parente object.
180 		 Generally this method need not be called manually but in the case of table objects
181 		 (like columns, rules, constraints, indexes and triggers) which can be moved
182 		 (to have their position changed in the parent object). This method updates the index
183 		 of the object with the new value for the operations which refer the object is not
184 		 executed incorrectly using previous index */
185 		void updateObjectIndex(BaseObject *object, unsigned new_idx);
186 };
187 
188 #endif
189