/* * CINELERRA * Copyright (C) 1997-2011 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "asset.h" #include "assets.h" #include "bctimer.h" #include "edl.h" #include "filexml.h" #include "mainindexes.h" #include "mainmenu.h" #include "mainsession.h" #include "mainundo.h" #include "mwindow.h" #include "mwindowgui.h" #include "nestededls.h" #include #include "undostack.h" MainUndo::MainUndo(MWindow *mwindow) { this->mwindow = mwindow; undo_stack = new UndoStack; last_update = new Timer; } MainUndo::~MainUndo() { delete undo_stack; delete last_update; } void MainUndo::update_undo_entry(const char *description, uint32_t load_flags, void *creator, int changes_made) { FileXML file; mwindow->edl->save_xml(&file, "", 0, 0); file.terminate_string(); if(changes_made) mwindow->session->changes_made = 1; // Remove all entries after current and create new one UndoStackItem *current = undo_stack->push(); current->set_flags(load_flags); current->set_data(file.string()); current->set_description((char*)description); current->set_creator(creator); current->set_filename(mwindow->session->filename); //printf("MainUndo::update_undo_entry %d %p %s\n", __LINE__, current, current->get_filename()); // Can't undo only 1 record. if(undo_stack->total() > 1) { // mwindow->gui->lock_window("MainUndo::update_undo"); mwindow->gui->mainmenu->undo->update_caption(description); mwindow->gui->mainmenu->redo->update_caption(""); // mwindow->gui->unlock_window(); } // Update timer last_update->update(); } void MainUndo::update_undo_before(const char *description, void *creator) { //printf("MainUndo::update_undo_before %d\n", __LINE__); if(undo_stack->current && !(undo_stack->number_of(undo_stack->current) % 2)) { printf("MainUndo::update_undo_before %d \"%s\": must be on an after entry to do this. size=%d\n", __LINE__, description, undo_stack->total()); // dump stack dump(); // Move up an entry to get back in sync // return; } mwindow->commit_commercial(); // Discard if creator matches previous before entry and within a time limit if(creator) { UndoStackItem *current = undo_stack->current; // Currently on an after entry if(current) { current = PREVIOUS; } // Now on a before entry if(current) { if(current->get_creator() == creator && !strcmp(current->get_description(), description) && last_update->get_difference() < UNDO_SPAN) { // Before entry has same creator within minimum time. Reuse it. // Stack must point to the before entry undo_stack->current = current; return; } } } // Append new entry after current position update_undo_entry("", 0, creator, 0); } void MainUndo::update_undo_after(const char *description, uint32_t load_flags, int changes_made) { //printf("MainUndo::update_undo_after %d\n", __LINE__); if(undo_stack->number_of(undo_stack->current) % 2) { printf("MainUndo::update_undo_after %d \"%s\": must be on a before entry to do this. size=%d\n", __LINE__, description, undo_stack->total()); // dump stack dump(); // Not getting any update_undo_before to get back in sync, so just append 1 here // return; } update_undo_entry(description, load_flags, 0, changes_made); // Update the before entry flags UndoStackItem *current = undo_stack->last; if(current) current = PREVIOUS; if(current) { current->set_flags(load_flags); current->set_description((char*)description); } } int MainUndo::undo() { UndoStackItem *current = undo_stack->current; char after_description[BCTEXTLEN]; after_description[0] = 0; mwindow->undo_commercial(); //printf("MainUndo::undo 1\n"); //dump(); // Rewind to an after entry if(current && !(undo_stack->number_of(current) % 2)) { current = PREVIOUS; } // Rewind to a before entry if(current && (undo_stack->number_of(current) % 2)) { strcpy(after_description, current->get_description()); current = PREVIOUS; } // Now have an even number if(current) { undo_stack->current = current; // Set the redo text to the current description if(mwindow->gui) mwindow->gui->mainmenu->redo->update_caption( after_description); FileXML file; char *current_data = current->get_data(); if(current_data) { file.read_from_string(current_data); load_from_undo(&file, current->get_flags()); //printf("MainUndo::undo %d %s\n", __LINE__, current->get_filename()); mwindow->set_filename(current->get_filename()); delete [] current_data; // move current entry back one step undo_stack->pull(); if(mwindow->gui) { // Now update the menu with the after entry current = PREVIOUS; // Must be a previous entry to perform undo if(current) mwindow->gui->mainmenu->undo->update_caption( current->get_description()); else mwindow->gui->mainmenu->undo->update_caption(""); } } } //dump(); reset_creators(); return 0; } int MainUndo::redo() { UndoStackItem *current = undo_stack->current; //printf("MainUndo::redo 1\n"); //dump(); // Get 1st entry if(!current) current = undo_stack->first; // Advance to a before entry if(current && (undo_stack->number_of(current) % 2)) { current = NEXT; } // Advance to an after entry if(current && !(undo_stack->number_of(current) % 2)) { current = NEXT; } if(current) { FileXML file; char *current_data = current->get_data(); undo_stack->current = current; if(current_data) { mwindow->set_filename(current->get_filename()); file.read_from_string(current_data); load_from_undo(&file, current->get_flags()); delete [] current_data; if(mwindow->gui) { // Update menu mwindow->gui->mainmenu->undo->update_caption(current->get_description()); // Get next after entry current = NEXT; if(current) current = NEXT; if(current) mwindow->gui->mainmenu->redo->update_caption(current->get_description()); else mwindow->gui->mainmenu->redo->update_caption(""); } } } reset_creators(); //dump(); return 0; } // Here the master EDL loads int MainUndo::load_from_undo(FileXML *file, uint32_t load_flags) { mwindow->edl->load_xml(file, load_flags); for(Asset *asset = mwindow->edl->assets->first; asset; asset = asset->next) { mwindow->mainindexes->add_next_asset(0, asset); } for(int i = 0; i < mwindow->edl->nested_edls->size(); i++) { EDL *nested_edl = mwindow->edl->nested_edls->get(i); mwindow->mainindexes->add_next_asset(0, nested_edl); } mwindow->mainindexes->start_build(); return 0; } void MainUndo::reset_creators() { for(UndoStackItem *current = undo_stack->first; current; current = NEXT) { current->set_creator(0); } } void MainUndo::dump(FILE *fp) { undo_stack->dump(fp); }