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