Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / cinelerra / mainundo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
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; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bctimer.h"
25 #include "clipedls.h"
26 #include "edl.h"
27 #include "filexml.h"
28 #include "mainindexes.h"
29 #include "mainmenu.h"
30 #include "mainsession.h"
31 #include "mainundo.h"
32 #include "mwindow.h"
33 #include "mwindowgui.h"
34 #include <string.h>
35 #include "undostack.h"
36
37 MainUndo::MainUndo(MWindow *mwindow)
38 {
39         this->mwindow = mwindow;
40         undo_stack = new UndoStack;
41         last_update = new Timer;
42 }
43
44 MainUndo::~MainUndo()
45 {
46         delete undo_stack;
47         delete last_update;
48 }
49
50
51 void MainUndo::update_undo_entry(const char *description,
52         uint32_t load_flags,
53         void *creator,
54         int changes_made)
55 {
56         FileXML file;
57
58         mwindow->edl->save_xml(&file, "");
59         file.terminate_string();
60         if(changes_made) mwindow->session->changes_made = 1;
61
62 // Remove all entries after current and create new one
63         UndoStackItem *current = undo_stack->push();
64
65         current->set_flags(load_flags);
66         current->set_data(file.string());
67         current->set_description((char*)description);
68         current->set_creator(creator);
69         current->set_filename(mwindow->session->filename);
70 //printf("MainUndo::update_undo_entry %d %p %s\n", __LINE__, current, current->get_filename());
71
72 // Can't undo only 1 record.
73         if(undo_stack->total() > 1)
74         {
75 //              mwindow->gui->lock_window("MainUndo::update_undo");
76                 mwindow->gui->mainmenu->undo->update_caption(description);
77                 mwindow->gui->mainmenu->redo->update_caption("");
78 //              mwindow->gui->unlock_window();
79         }
80
81 // Update timer
82         last_update->update();
83 }
84
85 void MainUndo::update_undo_before(const char *description, void *creator)
86 {
87 //printf("MainUndo::update_undo_before %d\n", __LINE__);
88         if(undo_stack->current && !(undo_stack->number_of(undo_stack->current) % 2))
89         {
90                 printf("MainUndo::update_undo_before %d \"%s\": must be on an after entry to do this. size=%d\n",
91                         __LINE__,
92                         description,
93                         undo_stack->total());
94
95 // dump stack
96                 dump();
97
98 // Move up an entry to get back in sync
99 //              return;
100         }
101
102         mwindow->commit_commercial();
103
104 // Discard if creator matches previous before entry and within a time limit
105         if(creator)
106         {
107                 UndoStackItem *current = undo_stack->current;
108 // Currently on an after entry
109                 if(current)
110                 {
111                         current = PREVIOUS;
112                 }
113
114 // Now on a before entry
115                 if(current)
116                 {
117                         if(current->get_creator() == creator &&
118                                 !strcmp(current->get_description(), description) &&
119                                 last_update->get_difference() < UNDO_SPAN)
120                         {
121 // Before entry has same creator within minimum time.  Reuse it.
122 // Stack must point to the before entry
123                                 undo_stack->current = current;
124                                 return;
125                         }
126                 }
127         }
128
129 // Append new entry after current position
130         update_undo_entry("", 0, creator, 0);
131 }
132
133 void MainUndo::update_undo_after(const char *description,
134         uint32_t load_flags,
135         int changes_made)
136 {
137 //printf("MainUndo::update_undo_after %d\n", __LINE__);
138         if(undo_stack->number_of(undo_stack->current) % 2)
139         {
140                 printf("MainUndo::update_undo_after %d \"%s\": must be on a before entry to do this. size=%d\n",
141                         __LINE__,
142                         description,
143                         undo_stack->total());
144
145 // dump stack
146                 dump();
147 // Not getting any update_undo_before to get back in sync, so just append 1 here
148 //              return;
149         }
150
151         update_undo_entry(description, load_flags, 0, changes_made);
152
153 // Update the before entry flags
154         UndoStackItem *current = undo_stack->last;
155         if(current) current = PREVIOUS;
156         if(current)
157         {
158                 current->set_flags(load_flags);
159                 current->set_description((char*)description);
160         }
161 }
162
163
164 UndoStackItem *MainUndo::next_undo()
165 {
166         return undo_stack->get_current_undo();
167 }
168
169 UndoStackItem *MainUndo::next_redo()
170 {
171         return undo_stack->get_current_redo();
172 }
173
174 int MainUndo::undo_load_flags()
175 {
176         UndoStackItem *item = next_undo();
177         return item ? item->get_flags() : 0;
178 }
179
180 int MainUndo::redo_load_flags()
181 {
182         UndoStackItem *item = next_redo();
183         return item ? item->get_flags() : 0;
184 }
185
186
187 int MainUndo::undo()
188 {
189         mwindow->undo_commercial();
190         UndoStackItem *current = undo_stack->current;
191         if( current ) {
192                 undo_stack->current = next_undo();
193                 if( undo_stack->number_of(current) % 2 )
194                         current = PREVIOUS; // Now have an even number
195         }
196         if( current ) {
197 // Set the redo text to the current description
198                 if( mwindow->gui ) {
199                         UndoStackItem *next = NEXT;
200                         mwindow->gui->mainmenu->redo->
201                                 update_caption(next ? next->get_description() : "");
202                 }
203                 char *current_data = current->get_data();
204                 if( current_data ) {
205                         FileXML file;
206                         file.read_from_string(current_data);
207                         delete [] current_data;
208                         load_from_undo(&file, current->get_flags());
209 //printf("MainUndo::undo %d %s\n", __LINE__, current->get_filename());
210                         mwindow->set_filename(current->get_filename());
211
212                         if( mwindow->gui ) {
213 // Now update the menu with the after entry
214                                 UndoStackItem *prev = PREVIOUS;
215                                 mwindow->gui->mainmenu->undo->
216                                         update_caption(prev ? prev->get_description() : "");
217                         }
218                 }
219         }
220
221         reset_creators();
222         mwindow->reset_caches();
223         return 0;
224 }
225
226
227 int MainUndo::redo()
228 {
229         UndoStackItem *current = next_redo();
230         if( current ) {
231                 undo_stack->current = current;
232                 char *current_data = current->get_data();
233                 if( current_data ) {
234                         mwindow->set_filename(current->get_filename());
235                         FileXML file;
236                         file.read_from_string(current_data);
237                         load_from_undo(&file, current->get_flags());
238                         delete [] current_data;
239 // Update menu
240                         mwindow->gui->mainmenu->undo->
241                                 update_caption(current->get_description());
242 // Get next after entry
243                         if( (current=NEXT) ) current = NEXT;
244                         mwindow->gui->mainmenu->redo->
245                                 update_caption(current ? current->get_description() : "");
246                 }
247         }
248         reset_creators();
249         mwindow->reset_caches();
250 //dump();
251         return 0;
252 }
253
254
255 // Here the master EDL loads
256 int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags)
257 {
258         delete mwindow->gui->keyvalue_popup;
259         mwindow->gui->keyvalue_popup = 0;
260
261         if( load_flags & LOAD_SESSION ) {
262                 mwindow->gui->unlock_window();
263                 mwindow->close_mixers();
264                 mwindow->gui->lock_window("MainUndo::load_from_undo");
265         }
266         if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
267                 mwindow->edl->remove_user();
268                 mwindow->init_edl();
269         }
270         mwindow->edl->load_xml(file, load_flags);
271         for( Asset *asset=mwindow->edl->assets->first; asset; asset=asset->next ) {
272                 mwindow->mainindexes->add_next_asset(0, asset);
273         }
274         for( int i=0; i<mwindow->edl->nested_edls.size(); ++i ) {
275                 EDL *nested_edl = mwindow->edl->nested_edls[i];
276                 mwindow->mainindexes->add_next_asset(0, nested_edl);
277         }
278         mwindow->mainindexes->start_build();
279         mwindow->update_plugin_guis(1);
280         if( load_flags & LOAD_SESSION ) {
281                 mwindow->gui->unlock_window();
282                 mwindow->open_mixers();
283                 mwindow->gui->lock_window("MainUndo::load_from_undo");
284         }
285         return 0;
286 }
287
288
289 void MainUndo::reset_creators()
290 {
291         for( UndoStackItem *current=undo_stack->first; current; current=NEXT ) {
292                 current->set_creator(0);
293         }
294 }
295
296
297 void MainUndo::dump(FILE *fp)
298 {
299         undo_stack->dump(fp);
300 }
301
302 void MainUndo::save(FILE *fp)
303 {
304         undo_stack->save(fp);
305 }
306
307 void MainUndo::load(FILE *fp)
308 {
309         undo_stack->load(fp);
310         UndoStackItem *current = undo_stack->current;
311         char *current_data = current ? current->get_data() : 0;
312         if( !current_data ) return;
313         mwindow->gui->lock_window("MainUndo::load");
314         UndoStackItem *next = current->next;
315         mwindow->gui->mainmenu->redo->
316                 update_caption(next ? next->get_description() : "");
317         mwindow->set_filename(current->get_filename());
318         FileXML file;
319         file.read_from_string(current_data);
320         load_from_undo(&file, LOAD_ALL);
321         delete [] current_data;
322         UndoStackItem *prev = current->previous;
323         mwindow->gui->mainmenu->undo->
324                 update_caption(prev ? prev->get_description() : "");
325         mwindow->update_project(LOADMODE_REPLACE);
326         mwindow->gui->unlock_window();
327 }
328