add transition caching with pref, rev read frame caching, cache lock tweaks, fix...
[goodguy/cinelerra.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 "clipedls.h"
26 #include "edl.h"
27 #include "filexml.h"
28 #include "indexfile.h"
29 #include "mainindexes.h"
30 #include "mainmenu.h"
31 #include "mainsession.h"
32 #include "mainundo.h"
33 #include "mwindow.h"
34 #include "mwindowgui.h"
35 #include <string.h>
36 #include "undostack.h"
37
38 MainUndo::MainUndo(MWindow *mwindow)
39 {
40         this->mwindow = mwindow;
41         undo_stack = new UndoStack;
42         last_update = new Timer;
43 }
44
45 MainUndo::~MainUndo()
46 {
47         delete undo_stack;
48         delete last_update;
49 }
50
51
52 void MainUndo::update_undo_entry(const char *description,
53         uint32_t load_flags,
54         void *creator,
55         int changes_made)
56 {
57         FileXML file;
58
59         mwindow->edl->save_xml(&file, "");
60         file.terminate_string();
61         if(changes_made) mwindow->session->changes_made = 1;
62
63 // Remove all entries after current and create new one
64         UndoStackItem *current = undo_stack->push();
65
66         current->set_flags(load_flags);
67         current->set_data(file.string());
68         current->set_description((char*)description);
69         current->set_creator(creator);
70         current->set_filename(mwindow->session->filename);
71 //printf("MainUndo::update_undo_entry %d %p %s\n", __LINE__, current, current->get_filename());
72
73 // Can't undo only 1 record.
74         if(undo_stack->total() > 1)
75         {
76 //              mwindow->gui->lock_window("MainUndo::update_undo");
77                 mwindow->gui->mainmenu->undo->update_caption(description);
78                 mwindow->gui->mainmenu->redo->update_caption("");
79 //              mwindow->gui->unlock_window();
80         }
81
82 // Update timer
83         last_update->update();
84 }
85
86 void MainUndo::update_undo_before(const char *description, void *creator)
87 {
88 //printf("MainUndo::update_undo_before %d\n", __LINE__);
89         if(undo_stack->current && !(undo_stack->number_of(undo_stack->current) % 2))
90         {
91                 printf("MainUndo::update_undo_before %d \"%s\": must be on an after entry to do this. size=%d\n",
92                         __LINE__,
93                         description,
94                         undo_stack->total());
95
96 // dump stack
97                 dump();
98
99 // Move up an entry to get back in sync
100 //              return;
101         }
102
103         mwindow->commit_commercial();
104
105 // Discard if creator matches previous before entry and within a time limit
106         if(creator)
107         {
108                 UndoStackItem *current = undo_stack->current;
109 // Currently on an after entry
110                 if(current)
111                 {
112                         current = PREVIOUS;
113                 }
114
115 // Now on a before entry
116                 if(current)
117                 {
118                         if(current->get_creator() == creator &&
119                                 !strcmp(current->get_description(), description) &&
120                                 last_update->get_difference() < UNDO_SPAN)
121                         {
122 // Before entry has same creator within minimum time.  Reuse it.
123 // Stack must point to the before entry
124                                 undo_stack->current = current;
125                                 return;
126                         }
127                 }
128         }
129
130 // Append new entry after current position
131         update_undo_entry("", 0, creator, 0);
132 }
133
134 void MainUndo::update_undo_after(const char *description,
135         uint32_t load_flags,
136         int changes_made)
137 {
138 //printf("MainUndo::update_undo_after %d\n", __LINE__);
139         if(undo_stack->number_of(undo_stack->current) % 2)
140         {
141                 printf("MainUndo::update_undo_after %d \"%s\": must be on a before entry to do this. size=%d\n",
142                         __LINE__,
143                         description,
144                         undo_stack->total());
145
146 // dump stack
147                 dump();
148 // Not getting any update_undo_before to get back in sync, so just append 1 here
149 //              return;
150         }
151
152         update_undo_entry(description, load_flags, 0, changes_made);
153
154 // Update the before entry flags
155         UndoStackItem *current = undo_stack->last;
156         if(current) current = PREVIOUS;
157         if(current)
158         {
159                 current->set_flags(load_flags);
160                 current->set_description((char*)description);
161         }
162 }
163
164
165 UndoStackItem *MainUndo::next_undo()
166 {
167         return undo_stack->get_current_undo();
168 }
169
170 UndoStackItem *MainUndo::next_redo()
171 {
172         return undo_stack->get_current_redo();
173 }
174
175 int MainUndo::undo_load_flags()
176 {
177         UndoStackItem *item = next_undo();
178         return item ? item->get_flags() : 0;
179 }
180
181 int MainUndo::redo_load_flags()
182 {
183         UndoStackItem *item = next_redo();
184         return item ? item->get_flags() : 0;
185 }
186
187
188 int MainUndo::undo()
189 {
190         mwindow->gui->close_keyvalue_popup();
191         mwindow->undo_commercial();
192
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                 char *current_data = current->get_data();
207                 if( current_data ) {
208                         FileXML file;
209                         file.read_from_string(current_data);
210                         delete [] current_data;
211                         load_from_undo(&file, current->get_flags());
212 //printf("MainUndo::undo %d %s\n", __LINE__, current->get_filename());
213                         mwindow->set_filename(current->get_filename());
214
215 // Now update the menu with the after entry
216                         UndoStackItem *prev = PREVIOUS;
217                         mwindow->gui->mainmenu->undo->
218                                 update_caption(prev ? prev->get_description() : "");
219                 }
220         }
221
222         mwindow->reset_caches(1);
223         reset_creators();
224         return 0;
225 }
226
227 int MainUndo::redo()
228 {
229         mwindow->gui->close_keyvalue_popup();
230         UndoStackItem *current = next_redo();
231         if( current ) {
232                 undo_stack->current = current;
233                 char *current_data = current->get_data();
234                 if( current_data ) {
235                         mwindow->set_filename(current->get_filename());
236                         FileXML file;
237                         file.read_from_string(current_data);
238                         load_from_undo(&file, current->get_flags());
239                         delete [] current_data;
240 // Update menu
241                         mwindow->gui->mainmenu->undo->
242                                 update_caption(current->get_description());
243 // Get next after entry
244                         if( (current=NEXT) ) current = NEXT;
245                         mwindow->gui->mainmenu->redo->
246                                 update_caption(current ? current->get_description() : "");
247                 }
248         }
249         mwindow->reset_caches(1);
250         reset_creators();
251 //dump();
252         return 0;
253 }
254
255
256 // Here the master EDL loads
257 int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags)
258 {
259         if( load_flags & LOAD_SESSION ) {
260                 mwindow->gui->unlock_window();
261                 mwindow->close_mixers();
262                 mwindow->gui->lock_window("MainUndo::load_from_undo");
263         }
264         EDL *prev_edl = mwindow->edl;
265         if( (load_flags & LOAD_ALL) == LOAD_ALL )
266                 mwindow->init_edl();
267         mwindow->edl->load_xml(file, load_flags);
268         for( Asset *asset=mwindow->edl->assets->first; asset; asset=asset->next ) {
269                 mwindow->mainindexes->add_indexable(asset);
270         }
271         if( prev_edl != mwindow->edl ) {
272                 for( int i=0; i<mwindow->edl->nested_edls.size(); ++i ) {
273                         EDL *nested_edl = mwindow->edl->nested_edls[i];
274                         if( !nested_edl->path[0] ) continue;
275                         int k = prev_edl->nested_edls.size();
276                         while( --k >= 0 && // if nested edl was updated, force index rebuild
277                                 strcmp(nested_edl->path, prev_edl->nested_edls[k]->path) );
278                         if( k >= 0 && prev_edl->nested_edls[k]->equivalent_output(nested_edl) >= 0 )
279                                 IndexFile::delete_index_files(mwindow->preferences, nested_edl);
280                         mwindow->mainindexes->add_indexable(nested_edl);
281                 }
282                 prev_edl->remove_user();
283         }
284         mwindow->mainindexes->start_build();
285         mwindow->update_plugin_guis(1);
286         if( load_flags & LOAD_SESSION ) {
287                 mwindow->gui->unlock_window();
288                 mwindow->open_mixers();
289                 mwindow->gui->lock_window("MainUndo::load_from_undo");
290         }
291         return 0;
292 }
293
294
295 void MainUndo::reset_creators()
296 {
297         for( UndoStackItem *current=undo_stack->first; current; current=NEXT ) {
298                 current->set_creator(0);
299         }
300 }
301
302
303 void MainUndo::dump(FILE *fp)
304 {
305         undo_stack->dump(fp);
306 }
307
308 void MainUndo::save(FILE *fp)
309 {
310         undo_stack->save(fp);
311 }
312
313 void MainUndo::load(FILE *fp)
314 {
315         undo_stack->load(fp);
316         UndoStackItem *current = undo_stack->current;
317         char *current_data = current ? current->get_data() : 0;
318         if( !current_data ) return;
319         mwindow->gui->lock_window("MainUndo::load");
320         UndoStackItem *next = current->next;
321         mwindow->gui->mainmenu->redo->
322                 update_caption(next ? next->get_description() : "");
323         mwindow->set_filename(current->get_filename());
324         FileXML file;
325         file.read_from_string(current_data);
326         load_from_undo(&file, LOAD_ALL);
327         delete [] current_data;
328         UndoStackItem *prev = current->previous;
329         mwindow->gui->mainmenu->undo->
330                 update_caption(prev ? prev->get_description() : "");
331         mwindow->update_project(LOADMODE_REPLACE);
332         mwindow->gui->unlock_window();
333 }
334