r1014: Enable horizontal scrolling with the mouse wheel by pressing Ctrl.
[cinelerra_cv/ct.git] / cinelerra / mainundo.C
blob6488d232dcbd1ad0e835c88029b1113fce572c1c
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 "tracks.h"
13 #include <string.h>
15 // Minimum number of undoable operations on the undo stack
16 #define UNDOMINLEVELS 5
17 // Limits the bytes of memory used by the undo stack
18 #define UNDOMEMORY 50000000
21 class MainUndoStackItem : public UndoStackItem
23 public:
24         MainUndoStackItem(MainUndo* undo, char* description,
25                         uint32_t load_flags, void* creator);
26         virtual ~MainUndoStackItem();
28         void set_data_before(char *data);
29         virtual void undo();
30         virtual int get_size();
32 private:
33 // type of modification
34         unsigned long load_flags;
35         
36 // data before the modification for undos
37         char *data_before;          
39         MainUndo *main_undo;
41         void load_from_undo(FileXML *file, uint32_t load_flags);        // loads undo from the stringfile to the project
45 MainUndo::MainUndo(MWindow *mwindow)
46
47         this->mwindow = mwindow;
48         new_entry = 0;
49         data_after = 0;
50         last_update = new Timer;
52 // get the initial project so we have something that the last undo reverts to
53         capture_state();
56 MainUndo::~MainUndo()
58         delete [] data_after;
59         delete last_update;
62 void MainUndo::update_undo(char *description, uint32_t load_flags, 
63                 void *creator, int changes_made)
65         if (ignore_push(description, load_flags, creator))
66         {
67                 capture_state();
68                 return;
69         }
71         MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags, creator);
73 // the old data_after is the state before the change
74         new_entry->set_data_before(data_after);
76         push_undo_item(new_entry);
79 void MainUndo::push_undo_item(UndoStackItem *item)
81 // clear redo_stack
82         while (redo_stack.last)
83                 redo_stack.remove(redo_stack.last);
85 // move item onto undo_stack
86         undo_stack.append(item);
87         prune_undo();
89         capture_state();
91         mwindow->session->changes_made = 1;
92    mwindow->gui->lock_window("MainUndo::update_undo_before");
93    mwindow->gui->mainmenu->undo->update_caption(item->description);
94    mwindow->gui->mainmenu->redo->update_caption("");
95    mwindow->gui->unlock_window();
98 void MainUndo::capture_state()
100         FileXML file;
101         mwindow->edl->save_xml(mwindow->plugindb, 
102                 &file, 
103                 "",
104                 0,
105                 0);
106         file.terminate_string();
107         delete [] data_after;
108         data_after = new char[strlen(file.string)+1];
109         strcpy(data_after, file.string);
112 bool MainUndo::ignore_push(char *description, uint32_t load_flags, void* creator)
114 // ignore this push under certain conditions:
115 // - if nothing was undone
116         bool ignore = 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 originates from the same creator
122                 undo_stack.last->creator == creator &&
123 // - if it follows closely after the previous undo
124                 last_update->get_difference() < 300 /*millisec*/;
125         last_update->update();
126         return ignore;
129 void MainUndo::push_state(char *description, uint32_t load_flags, void* creator)
131         if (ignore_push(description, load_flags, creator))
132         {
133                 capture_state();
134         }
135         else
136         {
137                 MainUndoStackItem* new_entry = new MainUndoStackItem(this, description, load_flags, creator);
138 // the old data_after is the state before the change
139                 new_entry->set_data_before(data_after);
140                 push_undo_item(new_entry);
141         }
142         mwindow->session->changes_made = 1;
150 int MainUndo::undo()
152         UndoStackItem* current_entry = undo_stack.last;
154         if(current_entry)
155         {
156 // move item to redo_stack
157                 undo_stack.remove_pointer(current_entry);
158                 current_entry->undo();
159                 redo_stack.append(current_entry);
160                 capture_state();
162                 if(mwindow->gui)
163                 {
164                         mwindow->gui->mainmenu->redo->update_caption(current_entry->description);
166                         if(undo_stack.last)
167                                 mwindow->gui->mainmenu->undo->update_caption(undo_stack.last->description);
168                         else
169                                 mwindow->gui->mainmenu->undo->update_caption("");
170                 }
171         }
173         reset_creators();
174         return 0;
177 int MainUndo::redo()
179         UndoStackItem* current_entry = redo_stack.last;
180         
181         if(current_entry)
182         {
183 // move item to undo_stack
184                 redo_stack.remove_pointer(current_entry);
185                 current_entry->undo();
186                 undo_stack.append(current_entry);
187                 capture_state();
189                 if(mwindow->gui)
190                 {
191                         mwindow->gui->mainmenu->undo->update_caption(current_entry->description);
192                         
193                         if(redo_stack.last)
194                                 mwindow->gui->mainmenu->redo->update_caption(redo_stack.last->description);
195                         else
196                                 mwindow->gui->mainmenu->redo->update_caption("");
197                 }
198         }
199         reset_creators();
200         return 0;
203 // enforces that the undo stack does not exceed a size of UNDOMEMORY
204 // except that it always has at least UNDOMINLEVELS entries
205 void MainUndo::prune_undo()
207         int size = 0;
208         int levels = 0;
210         UndoStackItem* i = undo_stack.last;
211         while (i != 0 && (levels < UNDOMINLEVELS || size <= UNDOMEMORY))
212         {
213                 size += i->get_size();
214                 ++levels;
215                 i = i->previous;
216         }
218         if (i != 0)
219         {
220 // truncate everything before and including i
221                 while (undo_stack.first != i)
222                         undo_stack.remove(undo_stack.first);
223                 undo_stack.remove(undo_stack.first);
224         }
231 MainUndoStackItem::MainUndoStackItem(MainUndo* main_undo, char* description,
232                         uint32_t load_flags, void* creator)
234         data_before = 0;
235         this->load_flags = load_flags;
236         this->main_undo = main_undo;
237         set_description(description);
238         set_creator(creator);
241 MainUndoStackItem::~MainUndoStackItem()
243         delete [] data_before;
246 void MainUndoStackItem::set_data_before(char *data)
248         data_before = new char[strlen(data) + 1];
249         strcpy(data_before, data);
252 void MainUndoStackItem::undo()
254 // move the old data_after here
255         char* before = data_before;
256         data_before = 0;
257         set_data_before(main_undo->data_after);
259 // undo the state
260         FileXML file;
262         file.read_from_string(before);
263         load_from_undo(&file, load_flags);
266 int MainUndoStackItem::get_size()
268         return data_before ? strlen(data_before) : 0;
271 // Here the master EDL loads 
272 void MainUndoStackItem::load_from_undo(FileXML *file, uint32_t load_flags)
274         MWindow* mwindow = main_undo->mwindow;
275         mwindow->edl->load_xml(mwindow->plugindb, file, load_flags);
276         for(Asset *asset = mwindow->edl->assets->first;
277                 asset;
278                 asset = asset->next)
279         {
280                 mwindow->mainindexes->add_next_asset(0, asset);
281         }
282         mwindow->mainindexes->start_build();
286 void MainUndo::reset_creators()
288         for(UndoStackItem *current = undo_stack.first;
289                 current;
290                 current = NEXT)
291         {
292                 current->set_creator(0);
293         }