r618: Undo items are only pushed if some time has passed since the last push.
[cinelerra_cv.git] / cinelerra / mainundo.C
blob7f1adaf2e7aaf3bf717d9696ed9e00398324bf68
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::push_undo_item(UndoStackItem *item)
61 // clear redo_stack
62         while (redo_stack.last)
63                 redo_stack.remove(redo_stack.last);
65 // move item onto undo_stack
66         undo_stack.append(item);
67         prune_undo();
69         capture_state();
71         mwindow->session->changes_made = 1;
72    mwindow->gui->lock_window("MainUndo::update_undo_before");
73    mwindow->gui->mainmenu->undo->update_caption(item->description);
74    mwindow->gui->mainmenu->redo->update_caption("");
75    mwindow->gui->unlock_window();
78 void MainUndo::capture_state()
80         FileXML file;
81         mwindow->edl->save_xml(mwindow->plugindb, 
82                 &file, 
83                 "",
84                 0,
85                 0);
86         file.terminate_string();
87         delete [] data_after;
88         data_after = new char[strlen(file.string)+1];
89         strcpy(data_after, file.string);
92 void MainUndo::update_undo_before(char *description, uint32_t load_flags)
94         if(!new_entry)
95         {
96                 new_entry = new MainUndoStackItem(this, description, load_flags);
97         }
100 void MainUndo::update_undo_after()
102         if(new_entry)
103         {
104 // the old data_after is the state before the change
105                 new_entry->set_data_before(data_after);
107                 push_undo_item(new_entry);
108                 new_entry = 0;
109         }
112 void MainUndo::push_state(char *description, uint32_t load_flags)
114 // ignore this push under certain conditions:
115 // - if nothing was undone
116         if (redo_stack.last == 0 &&
117 // - if it is not the first push
118                 undo_stack.last &&
119 // - if it has the same description as the previous undo
120                 strcmp(undo_stack.last->description, description) == 0 &&
121 // - if it follows closely after the previous undo
122                 timestamp.get_difference() < 300 /*millisec*/)
123         {
124                 capture_state();
125         }
126         else
127         {
128                 MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags);
129 // the old data_after is the state before the change
130                 new_entry->set_data_before(data_after);
131                 push_undo_item(new_entry);
132         }
133         mwindow->session->changes_made = 1;
134         timestamp.update();
142 int MainUndo::undo()
144         UndoStackItem* current_entry = undo_stack.last;
146         if(current_entry)
147         {
148 // move item to redo_stack
149                 undo_stack.remove_pointer(current_entry);
150                 current_entry->undo();
151                 redo_stack.append(current_entry);
152                 capture_state();
154                 if(mwindow->gui)
155                 {
156                         mwindow->gui->mainmenu->redo->update_caption(current_entry->description);
158                         if(undo_stack.last)
159                                 mwindow->gui->mainmenu->undo->update_caption(undo_stack.last->description);
160                         else
161                                 mwindow->gui->mainmenu->undo->update_caption("");
162                 }
163         }
164         return 0;
167 int MainUndo::redo()
169         UndoStackItem* current_entry = redo_stack.last;
170         
171         if(current_entry)
172         {
173 // move item to undo_stack
174                 redo_stack.remove_pointer(current_entry);
175                 current_entry->undo();
176                 undo_stack.append(current_entry);
177                 capture_state();
179                 if(mwindow->gui)
180                 {
181                         mwindow->gui->mainmenu->undo->update_caption(current_entry->description);
182                         
183                         if(redo_stack.last)
184                                 mwindow->gui->mainmenu->redo->update_caption(redo_stack.last->description);
185                         else
186                                 mwindow->gui->mainmenu->redo->update_caption("");
187                 }
188         }
189         return 0;
192 // enforces that the undo stack does not exceed a size of UNDOMEMORY
193 // except that it always has at least UNDOMINLEVELS entries
194 void MainUndo::prune_undo()
196         int size = 0;
197         int levels = 0;
199         UndoStackItem* i = undo_stack.last;
200         while (i != 0 && (levels < UNDOMINLEVELS || size <= UNDOMEMORY))
201         {
202                 size += i->get_size();
203                 ++levels;
204                 i = i->previous;
205         }
207         if (i != 0)
208         {
209 // truncate everything before and including i
210                 while (undo_stack.first != i)
211                         undo_stack.remove(undo_stack.first);
212                 undo_stack.remove(undo_stack.first);
213         }
220 MainUndoStackItem::MainUndoStackItem(MainUndo* main_undo, char* description,
221                         uint32_t load_flags)
223         data_before = 0;
224         this->load_flags = load_flags;
225         this->main_undo = main_undo;
226         set_description(description);
229 MainUndoStackItem::~MainUndoStackItem()
231         delete [] data_before;
234 void MainUndoStackItem::set_data_before(char *data)
236         data_before = new char[strlen(data) + 1];
237         strcpy(data_before, data);
240 void MainUndoStackItem::undo()
242 // move the old data_after here
243         char* before = data_before;
244         data_before = 0;
245         set_data_before(main_undo->data_after);
247 // undo the state
248         FileXML file;
250         file.read_from_string(before);
251         load_from_undo(&file, load_flags);
254 int MainUndoStackItem::get_size()
256         return data_before ? strlen(data_before) : 0;
259 // Here the master EDL loads 
260 void MainUndoStackItem::load_from_undo(FileXML *file, uint32_t load_flags)
262         MWindow* mwindow = main_undo->mwindow;
263         mwindow->edl->load_xml(mwindow->plugindb, file, load_flags);
264         for(Asset *asset = mwindow->edl->assets->first;
265                 asset;
266                 asset = asset->next)
267         {
268                 mwindow->mainindexes->add_next_asset(asset);
269         }
270         mwindow->mainindexes->start_build();