Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / 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 "edl.h"
26 #include "filexml.h"
27 #include "mainindexes.h"
28 #include "mainmenu.h"
29 #include "mainsession.h"
30 #include "mainundo.h"
31 #include "mwindow.h"
32 #include "mwindowgui.h"
33 #include "nestededls.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                 "",
60                 0,
61                 0);
62         file.terminate_string();
63         if(changes_made) mwindow->session->changes_made = 1;
64
65 // Remove all entries after current and create new one
66         UndoStackItem *current = undo_stack->push();
67
68         current->set_flags(load_flags);
69         current->set_data(file.string());
70         current->set_description((char*)description);
71         current->set_creator(creator);
72         current->set_filename(mwindow->session->filename);
73 //printf("MainUndo::update_undo_entry %d %p %s\n", __LINE__, current, current->get_filename());
74
75 // Can't undo only 1 record.
76         if(undo_stack->total() > 1)
77         {
78 //              mwindow->gui->lock_window("MainUndo::update_undo");
79                 mwindow->gui->mainmenu->undo->update_caption(description);
80                 mwindow->gui->mainmenu->redo->update_caption("");
81 //              mwindow->gui->unlock_window();
82         }
83
84 // Update timer
85         last_update->update();
86 }
87
88 void MainUndo::update_undo_before(const char *description, void *creator)
89 {
90 //printf("MainUndo::update_undo_before %d\n", __LINE__);
91         if(undo_stack->current && !(undo_stack->number_of(undo_stack->current) % 2))
92         {
93                 printf("MainUndo::update_undo_before %d \"%s\": must be on an after entry to do this. size=%d\n",
94                         __LINE__,
95                         description,
96                         undo_stack->total());
97
98 // dump stack
99                 dump();
100
101 // Move up an entry to get back in sync
102 //              return;
103         }
104
105         mwindow->commit_commercial();
106
107 // Discard if creator matches previous before entry and within a time limit
108         if(creator)
109         {
110                 UndoStackItem *current = undo_stack->current;
111 // Currently on an after entry
112                 if(current)
113                 {
114                         current = PREVIOUS;
115                 }
116
117 // Now on a before entry
118                 if(current)
119                 {
120                         if(current->get_creator() == creator &&
121                                 !strcmp(current->get_description(), description) &&
122                                 last_update->get_difference() < UNDO_SPAN)
123                         {
124 // Before entry has same creator within minimum time.  Reuse it.
125 // Stack must point to the before entry
126                                 undo_stack->current = current;
127                                 return;
128                         }
129                 }
130         }
131
132 // Append new entry after current position
133         update_undo_entry("", 0, creator, 0);
134 }
135
136 void MainUndo::update_undo_after(const char *description, 
137         uint32_t load_flags,
138         int changes_made)
139 {
140 //printf("MainUndo::update_undo_after %d\n", __LINE__);
141         if(undo_stack->number_of(undo_stack->current) % 2)
142         {
143                 printf("MainUndo::update_undo_after %d \"%s\": must be on a before entry to do this. size=%d\n",
144                         __LINE__,
145                         description,
146                         undo_stack->total());
147
148 // dump stack
149                 dump();
150 // Not getting any update_undo_before to get back in sync, so just append 1 here
151 //              return;
152         }
153
154         update_undo_entry(description, load_flags, 0, changes_made);
155
156 // Update the before entry flags
157         UndoStackItem *current = undo_stack->last;
158         if(current) current = PREVIOUS;
159         if(current)
160         {
161                 current->set_flags(load_flags);
162                 current->set_description((char*)description);
163         }
164 }
165
166
167 int MainUndo::undo()
168 {
169         UndoStackItem *current = undo_stack->current;
170         char after_description[BCTEXTLEN];
171         after_description[0] = 0;
172
173         mwindow->undo_commercial();
174
175 //printf("MainUndo::undo 1\n");
176 //dump();
177
178 // Rewind to an after entry
179         if(current && !(undo_stack->number_of(current) % 2))
180         {
181                 current = PREVIOUS;
182         }
183
184 // Rewind to a before entry
185         if(current && (undo_stack->number_of(current) % 2))
186         {
187                 strcpy(after_description, current->get_description());
188                 current = PREVIOUS;
189         }
190
191 // Now have an even number
192         if(current)
193         {
194                 undo_stack->current = current;
195 // Set the redo text to the current description
196                 if(mwindow->gui) 
197                         mwindow->gui->mainmenu->redo->update_caption(
198                                 after_description);
199
200                 FileXML file;
201                 char *current_data = current->get_data();
202                 if(current_data)
203                 {
204                         file.read_from_string(current_data);
205                         load_from_undo(&file, current->get_flags());
206 //printf("MainUndo::undo %d %s\n", __LINE__, current->get_filename());
207                         mwindow->set_filename(current->get_filename());
208                         delete [] current_data;
209
210 // move current entry back one step
211                         undo_stack->pull();    
212
213
214                         if(mwindow->gui)
215                         {
216 // Now update the menu with the after entry
217                                 current = PREVIOUS;
218 // Must be a previous entry to perform undo
219                                 if(current)
220                                         mwindow->gui->mainmenu->undo->update_caption(
221                                                 current->get_description());
222                                 else
223                                         mwindow->gui->mainmenu->undo->update_caption("");
224                         }
225                 }
226         }
227
228
229 //dump();
230         reset_creators();
231         return 0;
232 }
233
234 int MainUndo::redo()
235 {
236         UndoStackItem *current = undo_stack->current;
237 //printf("MainUndo::redo 1\n");
238 //dump();
239
240 // Get 1st entry
241         if(!current) current = undo_stack->first;
242
243 // Advance to a before entry
244         if(current && (undo_stack->number_of(current) % 2))
245         {
246                 current = NEXT;
247         }
248
249 // Advance to an after entry
250         if(current && !(undo_stack->number_of(current) % 2))
251         {
252                 current = NEXT;
253         }
254
255         if(current)
256         {
257                 FileXML file;
258                 char *current_data = current->get_data();
259                 undo_stack->current = current;
260
261                 if(current_data)
262                 {
263                         mwindow->set_filename(current->get_filename());
264                         file.read_from_string(current_data);
265                         load_from_undo(&file, current->get_flags());
266                         delete [] current_data;
267
268                         if(mwindow->gui)
269                         {
270 // Update menu
271                                 mwindow->gui->mainmenu->undo->update_caption(current->get_description());
272
273 // Get next after entry
274                                 current = NEXT;                 
275                                 if(current)
276                                         current = NEXT;
277
278                                 if(current)
279                                         mwindow->gui->mainmenu->redo->update_caption(current->get_description());
280                                 else
281                                         mwindow->gui->mainmenu->redo->update_caption("");
282                         }
283                 }
284         }
285         reset_creators();
286 //dump();
287         return 0;
288 }
289
290
291 // Here the master EDL loads 
292 int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags)
293 {
294         mwindow->edl->load_xml(file, load_flags);
295         for(Asset *asset = mwindow->edl->assets->first;
296                 asset;
297                 asset = asset->next)
298         {
299                 mwindow->mainindexes->add_next_asset(0, asset);
300         }
301         
302         for(int i = 0; i < mwindow->edl->nested_edls->size(); i++)
303         {
304                 EDL *nested_edl = mwindow->edl->nested_edls->get(i);
305                 mwindow->mainindexes->add_next_asset(0, nested_edl);
306         }
307         mwindow->mainindexes->start_build();
308         return 0;
309 }
310
311
312 void MainUndo::reset_creators()
313 {
314         for(UndoStackItem *current = undo_stack->first;
315                 current;
316                 current = NEXT)
317         {
318                 current->set_creator(0);
319         }
320 }
321
322
323 void MainUndo::dump(FILE *fp)
324 {
325         undo_stack->dump(fp);
326 }
327