r665: Merged the official release 2.0.
[cinelerra_cv.git] / cinelerra / mainundo.C
blob517b04f82c0de0d92068fd1a2ee41d35845e832d
1 #include "asset.h"
2 #include "assets.h"
3 #include "edl.h"
4 #include "filexml.h"
5 #include "mainindexes.h"
6 #include "mainmenu.h"
7 #include "mainsession.h"
8 #include "mainundo.h"
9 #include "mwindow.h"
10 #include "mwindowgui.h"
11 #include "undostackitem.h"
12 #include <string.h>
14 // Minimum number of undoable operations on the undo stack
15 #define UNDOMINLEVELS 5
16 // Limits the bytes of memory used by the undo stack
17 #define UNDOMEMORY 50000000
20 class MainUndoStackItem : public UndoStackItem
22 public:
23         MainUndoStackItem(MainUndo* undo, char* description,
24                         uint32_t load_flags);
25         virtual ~MainUndoStackItem();
27         void set_data_before(char *data);
28         virtual void undo();
29         virtual int get_size();
31 private:
32 // type of modification
33         unsigned long load_flags;
34         
35 // data before the modification for undos
36         char *data_before;          
38         MainUndo *main_undo;
40         void load_from_undo(FileXML *file, uint32_t load_flags);        // loads undo from the stringfile to the project
44 MainUndo::MainUndo(MWindow *mwindow)
45
46         this->mwindow = mwindow;
47         new_entry = 0;
48         data_after = 0;
50 // get the initial project so we have something that the last undo reverts to
51         capture_state();
54 MainUndo::~MainUndo()
56         delete [] data_after;
59 void MainUndo::update_undo(char *description, uint32_t load_flags, 
60                 void *creator, int changes_made)
62         MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags);
64 // the old data_after is the state before the change
65         new_entry->set_data_before(data_after);
67         push_undo_item(new_entry);
70 void MainUndo::push_undo_item(UndoStackItem *item)
72 // clear redo_stack
73         while (redo_stack.last)
74                 redo_stack.remove(redo_stack.last);
76 // move item onto undo_stack
77         undo_stack.append(item);
78         prune_undo();
80         capture_state();
82         mwindow->session->changes_made = 1;
83    mwindow->gui->lock_window("MainUndo::update_undo_before");
84    mwindow->gui->mainmenu->undo->update_caption(item->description);
85    mwindow->gui->mainmenu->redo->update_caption("");
86    mwindow->gui->unlock_window();
89 void MainUndo::capture_state()
91         FileXML file;
92         mwindow->edl->save_xml(mwindow->plugindb, 
93                 &file, 
94                 "",
95                 0,
96                 0);
97         file.terminate_string();
98         delete [] data_after;
99         data_after = new char[strlen(file.string)+1];
100         strcpy(data_after, file.string);
103 void MainUndo::push_state(char *description, uint32_t load_flags)
105 // ignore this push under certain conditions:
106 // - if nothing was undone
107         if (redo_stack.last == 0 &&
108 // - if it is not the first push
109                 undo_stack.last &&
110 // - if it has the same description as the previous undo
111                 strcmp(undo_stack.last->description, description) == 0 &&
112 // - if it follows closely after the previous undo
113                 timestamp.get_difference() < 300 /*millisec*/)
114         {
115                 capture_state();
116         }
117         else
118         {
119                 MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags);
120 // the old data_after is the state before the change
121                 new_entry->set_data_before(data_after);
122                 push_undo_item(new_entry);
123         }
124         mwindow->session->changes_made = 1;
125         timestamp.update();
133 int MainUndo::undo()
135         UndoStackItem* current_entry = undo_stack.last;
137         if(current_entry)
138         {
139 // move item to redo_stack
140                 undo_stack.remove_pointer(current_entry);
141                 current_entry->undo();
142                 redo_stack.append(current_entry);
143                 capture_state();
145                 if(mwindow->gui)
146                 {
147                         mwindow->gui->mainmenu->redo->update_caption(current_entry->description);
149                         if(undo_stack.last)
150                                 mwindow->gui->mainmenu->undo->update_caption(undo_stack.last->description);
151                         else
152                                 mwindow->gui->mainmenu->undo->update_caption("");
153                 }
154         }
155         return 0;
158 int MainUndo::redo()
160         UndoStackItem* current_entry = redo_stack.last;
161         
162         if(current_entry)
163         {
164 // move item to undo_stack
165                 redo_stack.remove_pointer(current_entry);
166                 current_entry->undo();
167                 undo_stack.append(current_entry);
168                 capture_state();
170                 if(mwindow->gui)
171                 {
172                         mwindow->gui->mainmenu->undo->update_caption(current_entry->description);
173                         
174                         if(redo_stack.last)
175                                 mwindow->gui->mainmenu->redo->update_caption(redo_stack.last->description);
176                         else
177                                 mwindow->gui->mainmenu->redo->update_caption("");
178                 }
179         }
180         return 0;
183 // enforces that the undo stack does not exceed a size of UNDOMEMORY
184 // except that it always has at least UNDOMINLEVELS entries
185 void MainUndo::prune_undo()
187         int size = 0;
188         int levels = 0;
190         UndoStackItem* i = undo_stack.last;
191         while (i != 0 && (levels < UNDOMINLEVELS || size <= UNDOMEMORY))
192         {
193                 size += i->get_size();
194                 ++levels;
195                 i = i->previous;
196         }
198         if (i != 0)
199         {
200 // truncate everything before and including i
201                 while (undo_stack.first != i)
202                         undo_stack.remove(undo_stack.first);
203                 undo_stack.remove(undo_stack.first);
204         }
211 MainUndoStackItem::MainUndoStackItem(MainUndo* main_undo, char* description,
212                         uint32_t load_flags)
214         data_before = 0;
215         this->load_flags = load_flags;
216         this->main_undo = main_undo;
217         set_description(description);
220 MainUndoStackItem::~MainUndoStackItem()
222         delete [] data_before;
225 void MainUndoStackItem::set_data_before(char *data)
227         data_before = new char[strlen(data) + 1];
228         strcpy(data_before, data);
231 void MainUndoStackItem::undo()
233 // move the old data_after here
234         char* before = data_before;
235         data_before = 0;
236         set_data_before(main_undo->data_after);
238 // undo the state
239         FileXML file;
241         file.read_from_string(before);
242         load_from_undo(&file, load_flags);
245 int MainUndoStackItem::get_size()
247         return data_before ? strlen(data_before) : 0;
250 // Here the master EDL loads 
251 void MainUndoStackItem::load_from_undo(FileXML *file, uint32_t load_flags)
253         MWindow* mwindow = main_undo->mwindow;
254         mwindow->edl->load_xml(mwindow->plugindb, file, load_flags);
255         for(Asset *asset = mwindow->edl->assets->first;
256                 asset;
257                 asset = asset->next)
258         {
259                 mwindow->mainindexes->add_next_asset(0, asset);
260         }
261         mwindow->mainindexes->start_build();