add bump floatautos, add time_references for align timecodes, add menuitem create_key...
[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         reset_creators();
223         mwindow->reset_caches();
224         return 0;
225 }
226
227
228 int MainUndo::redo()
229 {
230         mwindow->gui->close_keyvalue_popup();
231         UndoStackItem *current = next_redo();
232         if( current ) {
233                 undo_stack->current = current;
234                 char *current_data = current->get_data();
235                 if( current_data ) {
236                         mwindow->set_filename(current->get_filename());
237                         FileXML file;
238                         file.read_from_string(current_data);
239                         load_from_undo(&file, current->get_flags());
240                         delete [] current_data;
241 // Update menu
242                         mwindow->gui->mainmenu->undo->
243                                 update_caption(current->get_description());
244 // Get next after entry
245                         if( (current=NEXT) ) current = NEXT;
246                         mwindow->gui->mainmenu->redo->
247                                 update_caption(current ? current->get_description() : "");
248                 }
249         }
250         reset_creators();
251         mwindow->reset_caches();
252 //dump();
253         return 0;
254 }
255
256
257 // Here the master EDL loads
258 int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags)
259 {
260         if( load_flags & LOAD_SESSION ) {
261                 mwindow->gui->unlock_window();
262                 mwindow->close_mixers();
263                 mwindow->gui->lock_window("MainUndo::load_from_undo");
264         }
265         EDL *prev_edl = mwindow->edl;
266         if( (load_flags & LOAD_ALL) == LOAD_ALL )
267                 mwindow->init_edl();
268         mwindow->edl->load_xml(file, load_flags);
269         for( Asset *asset=mwindow->edl->assets->first; asset; asset=asset->next ) {
270                 mwindow->mainindexes->add_indexable(asset);
271         }
272         if( prev_edl != mwindow->edl ) {
273                 for( int i=0; i<mwindow->edl->nested_edls.size(); ++i ) {
274                         EDL *nested_edl = mwindow->edl->nested_edls[i];
275                         if( !nested_edl->path[0] ) continue;
276                         int k = prev_edl->nested_edls.size();
277                         while( --k >= 0 && // if nested edl was updated, force index rebuild
278                                 strcmp(nested_edl->path, prev_edl->nested_edls[k]->path) );
279                         if( k >= 0 && prev_edl->nested_edls[k]->equivalent_output(nested_edl) >= 0 )
280                                 IndexFile::delete_index_files(mwindow->preferences, nested_edl);
281                         mwindow->mainindexes->add_indexable(nested_edl);
282                 }
283                 prev_edl->remove_user();
284         }
285         mwindow->mainindexes->start_build();
286         mwindow->update_plugin_guis(1);
287         if( load_flags & LOAD_SESSION ) {
288                 mwindow->gui->unlock_window();
289                 mwindow->open_mixers();
290                 mwindow->gui->lock_window("MainUndo::load_from_undo");
291         }
292         return 0;
293 }
294
295
296 void MainUndo::reset_creators()
297 {
298         for( UndoStackItem *current=undo_stack->first; current; current=NEXT ) {
299                 current->set_creator(0);
300         }
301 }
302
303
304 void MainUndo::dump(FILE *fp)
305 {
306         undo_stack->dump(fp);
307 }
308
309 void MainUndo::save(FILE *fp)
310 {
311         undo_stack->save(fp);
312 }
313
314 void MainUndo::load(FILE *fp)
315 {
316         undo_stack->load(fp);
317         UndoStackItem *current = undo_stack->current;
318         char *current_data = current ? current->get_data() : 0;
319         if( !current_data ) return;
320         mwindow->gui->lock_window("MainUndo::load");
321         UndoStackItem *next = current->next;
322         mwindow->gui->mainmenu->redo->
323                 update_caption(next ? next->get_description() : "");
324         mwindow->set_filename(current->get_filename());
325         FileXML file;
326         file.read_from_string(current_data);
327         load_from_undo(&file, LOAD_ALL);
328         delete [] current_data;
329         UndoStackItem *prev = current->previous;
330         mwindow->gui->mainmenu->undo->
331                 update_caption(prev ? prev->get_description() : "");
332         mwindow->update_project(LOADMODE_REPLACE);
333         mwindow->gui->unlock_window();
334 }
335