r726: Implementing ability to add textural info to the labels
[cinelerra_cv/mob.git] / cinelerra / mainundo.C
blob88aba6a1926da280cedbdb104add88c817843192
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, void* creator);
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;
49         last_update = new Timer;
51 // get the initial project so we have something that the last undo reverts to
52         capture_state();
55 MainUndo::~MainUndo()
57         delete [] data_after;
58         delete last_update;
61 void MainUndo::update_undo(char *description, uint32_t load_flags, 
62                 void *creator, int changes_made)
64         if (ignore_push(description, load_flags, creator))
65         {
66                 capture_state();
67                 return;
68         }
70         MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags, creator);
72 // the old data_after is the state before the change
73         new_entry->set_data_before(data_after);
75         push_undo_item(new_entry);
78 void MainUndo::push_undo_item(UndoStackItem *item)
80 // clear redo_stack
81         while (redo_stack.last)
82                 redo_stack.remove(redo_stack.last);
84 // move item onto undo_stack
85         undo_stack.append(item);
86         prune_undo();
88         capture_state();
90         mwindow->session->changes_made = 1;
91    mwindow->gui->lock_window("MainUndo::update_undo_before");
92    mwindow->gui->mainmenu->undo->update_caption(item->description);
93    mwindow->gui->mainmenu->redo->update_caption("");
94    mwindow->gui->unlock_window();
97 void MainUndo::capture_state()
99         FileXML file;
100         mwindow->edl->save_xml(mwindow->plugindb, 
101                 &file, 
102                 "",
103                 0,
104                 0);
105         file.terminate_string();
106         delete [] data_after;
107         data_after = new char[strlen(file.string)+1];
108         strcpy(data_after, file.string);
111 bool MainUndo::ignore_push(char *description, uint32_t load_flags, void* creator)
113 // ignore this push under certain conditions:
114 // - if nothing was undone
115         bool ignore = redo_stack.last == 0 &&
116 // - if it is not the first push
117                 undo_stack.last &&
118 // - if it has the same description as the previous undo
119                 strcmp(undo_stack.last->description, description) == 0 &&
120 // - if it originates from the same creator
121                 undo_stack.last->creator == creator &&
122 // - if it follows closely after the previous undo
123                 last_update->get_difference() < 300 /*millisec*/;
124         last_update->update();
125         return ignore;
128 void MainUndo::push_state(char *description, uint32_t load_flags, void* creator)
130         if (ignore_push(description, load_flags, creator))
131         {
132                 capture_state();
133         }
134         else
135         {
136                 MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags, creator);
137 // the old data_after is the state before the change
138                 new_entry->set_data_before(data_after);
139                 push_undo_item(new_entry);
140         }
141         mwindow->session->changes_made = 1;
149 int MainUndo::undo()
151         UndoStackItem* current_entry = undo_stack.last;
153         if(current_entry)
154         {
155 // move item to redo_stack
156                 undo_stack.remove_pointer(current_entry);
157                 current_entry->undo();
158                 redo_stack.append(current_entry);
159                 capture_state();
161                 if(mwindow->gui)
162                 {
163                         mwindow->gui->mainmenu->redo->update_caption(current_entry->description);
165                         if(undo_stack.last)
166                                 mwindow->gui->mainmenu->undo->update_caption(undo_stack.last->description);
167                         else
168                                 mwindow->gui->mainmenu->undo->update_caption("");
169                 }
170         }
172         reset_creators();
173         return 0;
176 int MainUndo::redo()
178         UndoStackItem* current_entry = redo_stack.last;
179         
180         if(current_entry)
181         {
182 // move item to undo_stack
183                 redo_stack.remove_pointer(current_entry);
184                 current_entry->undo();
185                 undo_stack.append(current_entry);
186                 capture_state();
188                 if(mwindow->gui)
189                 {
190                         mwindow->gui->mainmenu->undo->update_caption(current_entry->description);
191                         
192                         if(redo_stack.last)
193                                 mwindow->gui->mainmenu->redo->update_caption(redo_stack.last->description);
194                         else
195                                 mwindow->gui->mainmenu->redo->update_caption("");
196                 }
197         }
198         reset_creators();
199         return 0;
202 // enforces that the undo stack does not exceed a size of UNDOMEMORY
203 // except that it always has at least UNDOMINLEVELS entries
204 void MainUndo::prune_undo()
206         int size = 0;
207         int levels = 0;
209         UndoStackItem* i = undo_stack.last;
210         while (i != 0 && (levels < UNDOMINLEVELS || size <= UNDOMEMORY))
211         {
212                 size += i->get_size();
213                 ++levels;
214                 i = i->previous;
215         }
217         if (i != 0)
218         {
219 // truncate everything before and including i
220                 while (undo_stack.first != i)
221                         undo_stack.remove(undo_stack.first);
222                 undo_stack.remove(undo_stack.first);
223         }
230 MainUndoStackItem::MainUndoStackItem(MainUndo* main_undo, char* description,
231                         uint32_t load_flags, void* creator)
233         data_before = 0;
234         this->load_flags = load_flags;
235         this->main_undo = main_undo;
236         set_description(description);
237         set_creator(creator);
240 MainUndoStackItem::~MainUndoStackItem()
242         delete [] data_before;
245 void MainUndoStackItem::set_data_before(char *data)
247         data_before = new char[strlen(data) + 1];
248         strcpy(data_before, data);
251 void MainUndoStackItem::undo()
253 // move the old data_after here
254         char* before = data_before;
255         data_before = 0;
256         set_data_before(main_undo->data_after);
258 // undo the state
259         FileXML file;
261         file.read_from_string(before);
262         load_from_undo(&file, load_flags);
265 int MainUndoStackItem::get_size()
267         return data_before ? strlen(data_before) : 0;
270 // Here the master EDL loads 
271 void MainUndoStackItem::load_from_undo(FileXML *file, uint32_t load_flags)
273         MWindow* mwindow = main_undo->mwindow;
274         mwindow->edl->load_xml(mwindow->plugindb, file, load_flags);
275         for(Asset *asset = mwindow->edl->assets->first;
276                 asset;
277                 asset = asset->next)
278         {
279                 mwindow->mainindexes->add_next_asset(0, asset);
280         }
281         mwindow->mainindexes->start_build();
285 void MainUndo::reset_creators()
287         for(UndoStackItem *current = undo_stack.first;
288                 current;
289                 current = NEXT)
290         {
291                 current->set_creator(0);
292         }