//***************************************************************************************** // Truevision - a 3d modeler for gnome and povray // // undo.cc // // Vincent LE PRINCE // Copyright (C) 2000-2005 Vincent LE PRINCE // This file is part of the TRUEVISION Package // 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ //******************************************************************************************* #include "include/undo.h" #include "include/main.h" #include "include/viewmanager.h" #include "include/preferences.h" #include "include/objectlist.h" #include "include/matlist.h" //****************************************** // Undo Item // each undo item describes an action that may be undone // They are stored by the UndoRedo Manager in two lists // one for undo and the other for redo //****************************************** // Constructor used for undo action on Objects UndoItem::UndoItem( UndoAction act, Object3D *obj, ObjectLayer *dest ) { action = act; layer = dest; switch ( action ) { case TV_UNDO_OBJ_CREATE: created = obj; break; case TV_UNDO_OBJ_DELETE: deleted = obj; parent_object = deleted->get_parent(); position = ( parent_object == NULL ) ? layer->get_object_pos( deleted ) : parent_object->get_child_pos( deleted ); break; case TV_UNDO_OBJ_MOVE: created = obj; parent_object = created->get_parent(); position = ( parent_object == NULL ) ? layer->get_object_pos( created ) : parent_object->get_child_pos( created ); break; default: break; } } // Constructor used for undo action on Layers UndoItem::UndoItem( UndoAction act, ObjectLayer *lay, int sens ) { action = act; layer = lay; position = sens; } // Constructor used for undo action on Materials UndoItem::UndoItem( UndoAction act, Material *mat, int pos ) { action = act; material = mat; position = pos; } // Constructor used for undo action on ObjParams UndoItem::UndoItem( UndoAction act, ObjParam *par, ObjParam *parc ) { action = act; param = par; param_copy = parc; } // Constructor used for undo action on Obj3dobjects UndoItem::UndoItem( UndoAction act, Object3D *obj1, Object3D*obj2 ) { action = act; changed = obj1; changed_copy = obj2; } // Destructor UndoItem::~UndoItem() { if ( action == TV_UNDO_OBJ_DELETE ) delete deleted; if ( action == TV_UNDO_LAYER_DELETE ) delete layer; if ( action == TV_UNDO_MAT_DELETE ) delete material; if ( action == TV_UNDO_PARAM_CHANGED ) delete param_copy; if ( action == TV_UNDO_OBJ3D_CHANGED ) delete changed_copy; } //****************************************** // UndoRedoManager // Called by the interface to 'push' undo items, and to // undo or redo them. //****************************************** // Constructor UndoRedoManager::UndoRedoManager( app_objs *appref ) { app_ref = appref; appref->undoman = this; UndoList.clear(); RedoList.clear(); } // Init void UndoRedoManager::init() { for ( unsigned int i = 0 ; i < UndoList.size() ; i ++ ) delete UndoList[i]; UndoList.clear(); for ( unsigned int i = 0 ; i < RedoList.size() ; i ++ ) delete RedoList[i]; RedoList.clear(); set_widgets_sensitivity(); } // Purge the lists // used when loading a new scene void UndoRedoManager::purge_redo_list() { //for ( unsigned int i = 0 ; i < RedoList.size() ; i ++ ) delete RedoList[i]; RedoList.clear(); } // Set widgets sensitivity // Called to set undo / redo widgets ( toolbar buttons & menu items ) // when list contents change. void UndoRedoManager::set_widgets_sensitivity( ) { /*if ( tbw_undo != NULL ) { if ( UndoList.size() < 1 ) gtk_widget_set_sensitive( tbw_undo, FALSE ); else gtk_widget_set_sensitive( tbw_undo, TRUE ); } if ( tbw_redo != NULL ) { if ( RedoList.size() < 1 ) gtk_widget_set_sensitive( tbw_redo, FALSE ); else gtk_widget_set_sensitive( tbw_redo, TRUE ); } if ( mew_undo != NULL ) { if ( UndoList.size() < 1 ) gtk_widget_set_sensitive( mew_undo, FALSE ); else gtk_widget_set_sensitive( mew_undo, TRUE ); } if ( mew_redo != NULL ) { if ( RedoList.size() < 1 ) gtk_widget_set_sensitive( mew_redo, FALSE ); else gtk_widget_set_sensitive( mew_redo, TRUE ); }*/ if ( UndoList.size() < 1 ) gtk_action_set_sensitive( undo_action, false ); else gtk_action_set_sensitive( undo_action, true ); if ( RedoList.size() < 1 ) gtk_action_set_sensitive( redo_action, false ); else gtk_action_set_sensitive( redo_action, true ); } // Undo object creation / deletion / move void UndoRedoManager::push( UndoAction act, Object3D *obj, ObjectLayer *src ) { UndoItem *item = new UndoItem( act, obj, src ); UndoList.push_back( item ); purge_redo_list(); set_widgets_sensitivity(); limit_undo_level(); } // Undo layers creation / deletion / move void UndoRedoManager::push( UndoAction act, ObjectLayer *lay, int sens ) { UndoItem *item = new UndoItem( act, lay, sens ); UndoList.push_back( item ); purge_redo_list(); set_widgets_sensitivity(); limit_undo_level(); } // Undo materials creation / deletion / move void UndoRedoManager::push( UndoAction act, Material *mat, int position ) { UndoItem *item = new UndoItem( act, mat, position ); UndoList.push_back( item ); purge_redo_list(); set_widgets_sensitivity(); limit_undo_level(); } // Undo ObjParam changes void UndoRedoManager::push( UndoAction act, ObjParam *param, ObjParam *param_copy ) { UndoItem *item = new UndoItem( act, param, param_copy ); UndoList.push_back( item ); purge_redo_list(); set_widgets_sensitivity(); limit_undo_level(); } // Undo Obj3D changes void UndoRedoManager::push( UndoAction act, Object3D *obj1, Object3D *obj2 ) { UndoItem *item = new UndoItem( act, obj1, obj2 ); UndoList.push_back( item ); purge_redo_list(); set_widgets_sensitivity(); limit_undo_level(); } // Limit undo level // Delete oldest undo item from the list // when its size exceed the limit defined in preferences void UndoRedoManager::limit_undo_level() { PREF_DEF if ( UndoList.size() > (unsigned int)pref->undo_limit->value() ) { delete UndoList[0]; UndoList.erase( UndoList.begin() ); } } //*************************************************** // Undo function // The main function used // in fact it is also used for redo ;-) //*************************************************** void UndoRedoManager::undo( bool doredo ) { // Define source & destination lists // The determine if we act as undo or redo... vector & src_list = doredo ? RedoList : UndoList; vector & dest_list= doredo ? UndoList : RedoList; UndoItem *item = src_list.back(); VMAN_DEF OBJLIST_DEF //cout << "\nGonna undo -> " << item->action; cout.flush(); switch ( item->action ) { // Undo an object deletion case TV_UNDO_OBJ_DELETE: { if ( item->parent_object != NULL ) item->parent_object->insert_child( item->deleted, item->position ); else item->layer->insert_object( item->deleted, item->position ); UndoItem *inverse = new UndoItem( TV_UNDO_OBJ_CREATE, item->deleted, item->layer ); dest_list.push_back( inverse ); src_list.pop_back(); item->set_action( TV_UNDO_NULL ); delete item; } break; // Undo an object creation case TV_UNDO_OBJ_CREATE: { item->layer->delete_object( item->created ); vmanager->refresh(); UndoItem *inverse = new UndoItem( TV_UNDO_OBJ_DELETE, item->created, item->layer ); dest_list.push_back( inverse ); src_list.pop_back(); delete item; } break; // Undo an object move case TV_UNDO_OBJ_MOVE: { ObjectLayer *nlayer = item->created->get_layer(); UndoItem *inverse = new UndoItem( TV_UNDO_OBJ_MOVE, item->created, nlayer ); nlayer->delete_object( item->created ); if ( item->parent_object != NULL ) item->parent_object->insert_child( item->created, item->position ); else item->layer->insert_object( item->created, item->position ); dest_list.push_back( inverse ); src_list.pop_back(); delete item; } break; // Undo a layer creation case TV_UNDO_LAYER_CREATE: { UndoItem *inverse = new UndoItem( TV_UNDO_LAYER_DELETE, item->layer, objlist->get_layer_index(item->layer) ); objlist->remove_layer( item->layer ); dest_list.push_back( inverse ); src_list.pop_back(); delete item; } break; // Undo a layer deletion case TV_UNDO_LAYER_DELETE: { objlist->insert_layer( item->layer, item->position-1 ); UndoItem *inverse = new UndoItem( TV_UNDO_LAYER_CREATE, item->layer, -1 ); dest_list.push_back( inverse ); src_list.pop_back(); item->set_action( TV_UNDO_NULL ); delete item; } break; // Undo a layer deletion case TV_UNDO_LAYER_MOVE: { objlist->move_layer( item->layer, item->position ); item->position = ( item->position == 1 ) ? 2 : 1; dest_list.push_back( item ); src_list.pop_back(); } break; // Undo a material creation case TV_UNDO_MAT_CREATE: { MATLIST_DEF UndoItem *inverse = new UndoItem( TV_UNDO_MAT_DELETE, item->material, matlist->get_index_by_pointer(item->material) ); matlist->remove_material( item->material ); dest_list.push_back( inverse ); src_list.pop_back(); delete item; } break; // Undo a material deletion case TV_UNDO_MAT_DELETE: { MATLIST_DEF matlist->insert_material( item->material, item->position ); UndoItem *inverse = new UndoItem( TV_UNDO_MAT_CREATE, item->material, -1 ); dest_list.push_back( inverse ); src_list.pop_back(); item->set_action( TV_UNDO_NULL ); delete item; } break; // Undo an Object param change case TV_UNDO_PARAM_CHANGED: { item->param->swap_data( item->param_copy ); dest_list.push_back( item ); src_list.pop_back(); } break; case TV_UNDO_OBJ3D_CHANGED: { item->changed->undo( item->changed_copy ); dest_list.push_back( item ); src_list.pop_back(); } break; default: break; } set_widgets_sensitivity(); }