undo fixes, viewer change_source rework, cleanup
[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 UndoStackItem *MainUndo::next_undo()
168 {
169         return undo_stack->get_current_undo();
170 }
171
172 UndoStackItem *MainUndo::next_redo()
173 {
174         return undo_stack->get_current_redo();
175 }
176
177 int MainUndo::undo_load_flags()
178 {
179         UndoStackItem *item = next_undo();
180         return item ? item->get_flags() : 0;
181 }
182
183 int MainUndo::redo_load_flags()
184 {
185         UndoStackItem *item = next_redo();
186         return item ? item->get_flags() : 0;
187 }
188
189
190 int MainUndo::undo()
191 {
192         mwindow->undo_commercial();
193         UndoStackItem *current = undo_stack->current;
194         if( current ) {
195                 undo_stack->current = next_undo();
196                 if( undo_stack->number_of(current) % 2 )
197                         current = PREVIOUS; // Now have an even number
198         }
199         if( current ) {
200 // Set the redo text to the current description
201                 if( mwindow->gui ) {
202                         UndoStackItem *next = NEXT;
203                         mwindow->gui->mainmenu->redo->
204                                 update_caption(next ? next->get_description() : "");
205                 }
206                 FileXML file;
207                 char *current_data = current->get_data();
208                 if( current_data ) {
209                         file.read_from_string(current_data);
210                         load_from_undo(&file, current->get_flags());
211 //printf("MainUndo::undo %d %s\n", __LINE__, current->get_filename());
212                         mwindow->set_filename(current->get_filename());
213                         delete [] current_data;
214
215                         if( mwindow->gui ) {
216 // Now update the menu with the after entry
217                                 UndoStackItem *prev = PREVIOUS;
218                                 mwindow->gui->mainmenu->undo->
219                                         update_caption(prev ? prev->get_description() : "");
220                         }
221                 }
222         }
223
224         reset_creators();
225         mwindow->reset_caches();
226         return 0;
227 }
228
229
230 int MainUndo::redo()
231 {
232         UndoStackItem *current = next_redo();
233         if( current ) {
234                 undo_stack->current = current;
235                 char *current_data = current->get_data();
236                 if( current_data ) {
237                         FileXML file;
238                         mwindow->set_filename(current->get_filename());
239                         file.read_from_string(current_data);
240                         load_from_undo(&file, current->get_flags());
241                         delete [] current_data;
242
243                         if( mwindow->gui ) {
244 // Update menu
245                                 mwindow->gui->mainmenu->undo->
246                                         update_caption(current->get_description());
247 // Get next after entry
248                                 if( (current=NEXT) ) current = NEXT;
249                                 mwindow->gui->mainmenu->redo->
250                                         update_caption(current ? current->get_description() : "");
251                         }
252                 }
253         }
254         reset_creators();
255         mwindow->reset_caches();
256 //dump();
257         return 0;
258 }
259
260
261 // Here the master EDL loads
262 int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags)
263 {
264         if( load_flags & LOAD_SESSION )
265                 mwindow->close_mixers();
266         mwindow->edl->load_xml(file, load_flags);
267         for(Asset *asset = mwindow->edl->assets->first;
268                 asset;
269                 asset = asset->next)
270         {
271                 mwindow->mainindexes->add_next_asset(0, asset);
272         }
273
274         for(int i = 0; i < mwindow->edl->nested_edls->size(); i++)
275         {
276                 EDL *nested_edl = mwindow->edl->nested_edls->get(i);
277                 mwindow->mainindexes->add_next_asset(0, nested_edl);
278         }
279         mwindow->mainindexes->start_build();
280         mwindow->update_plugin_guis(1);
281         if( load_flags & LOAD_SESSION )
282                 mwindow->open_mixers();
283         return 0;
284 }
285
286
287 void MainUndo::reset_creators()
288 {
289         for(UndoStackItem *current = undo_stack->first;
290                 current;
291                 current = NEXT)
292         {
293                 current->set_creator(0);
294         }
295 }
296
297
298 void MainUndo::dump(FILE *fp)
299 {
300         undo_stack->dump(fp);
301 }
302