/********************************************/ /* library editor: undo and redo functions */ /********************************************/ #include "fctsys.h" #include "gr_basic.h" #include "common.h" #include "program.h" #include "libcmp.h" #include "general.h" #include "id.h" #include "protos.h" /* Functions to undo and redo edit commands. * commmands to undo are in CurrentScreen->m_UndoList * commmands to redo are in CurrentScreen->m_RedoList * * m_UndoList and m_RedoList are a linked list of DrawPickedStruct. * each DrawPickedStruct has its .m_Son member pointing to an item to undo or redo, * or to a list of DrawPickedStruct which points (.m_PickedStruct membre) * the items to undo or redo * * there are 3 cases: * - delete item(s) command * - change item(s) command * - add item(s) command * * Undo command * - delete item(s) command: * deleted items are moved in undo list * * - change item(s) command * A copy of item(s) is made (a DrawPickedStruct list of wrappers) * the .m_Image member of each wrapper points the modified item. * * - add item(s) command * A list of item(s) is made * the .m_Image member of each wrapper points the new item. * * Redo command * - delete item(s) old command: * deleted items are moved in EEDrawList list * * - change item(s) command * the copy of item(s) is moved in Undo list * * - add item(s) command * The list of item(s) is used to create a deleted list in undo list * (same as a delete command) * * A problem is the hierarchical sheet handling. * the data associated (subhierarchy, uno/redo list) is deleted only * when the sheet is really deleted (i.e. when deleted from undo or redo list) * and not when it is a copy. */ /************************************/ void SwapData( EDA_BaseStruct* Item ) /************************************/ /* Used if undo / redo command: * swap data between Item and its copy, pointed by its .m_Image member */ { if( Item == NULL ) return; EDA_BaseStruct* image = Item->m_Image; if( image == NULL ) return; switch( Item->Type() ) { case DRAW_POLYLINE_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawPolylineStruct*) Item ) #define DEST ( (DrawPolylineStruct*) image ) break; case DRAW_JUNCTION_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawJunctionStruct*) Item ) #define DEST ( (DrawJunctionStruct*) image ) EXCHG( SOURCE->m_Pos, DEST->m_Pos ); break; case TYPE_SCH_LABEL: case TYPE_SCH_GLOBALLABEL: case TYPE_SCH_HIERLABEL: case TYPE_SCH_TEXT: #undef SOURCE #undef DEST #define SOURCE ( (SCH_TEXT*) Item ) #define DEST ( (SCH_TEXT*) image ) DEST->SwapData( SOURCE ); break; case TYPE_SCH_COMPONENT: #undef SOURCE #undef DEST #define SOURCE ( (SCH_COMPONENT*) Item ) #define DEST ( (SCH_COMPONENT*) image ) DEST->SwapData( SOURCE ); break; case DRAW_SEGMENT_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (EDA_DrawLineStruct*) Item ) #define DEST ( (EDA_DrawLineStruct*) image ) EXCHG( SOURCE->m_Start, DEST->m_Start ); EXCHG( SOURCE->m_End, DEST->m_End ); break; case DRAW_BUSENTRY_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawBusEntryStruct*) Item ) #define DEST ( (DrawBusEntryStruct*) image ) EXCHG( SOURCE->m_Pos, DEST->m_Pos ); EXCHG( SOURCE->m_Size, DEST->m_Size ); break; case DRAW_SHEET_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawSheetStruct*) Item ) #define DEST ( (DrawSheetStruct*) image ) DEST->SwapData( SOURCE ); break; case DRAW_MARKER_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawMarkerStruct*) Item ) #define DEST ( (DrawMarkerStruct*) image ) EXCHG( SOURCE->m_Pos, DEST->m_Pos ); break; case DRAW_HIERARCHICAL_PIN_SHEET_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (Hierarchical_PIN_Sheet_Struct*) Item ) #define DEST ( (Hierarchical_PIN_Sheet_Struct*) image ) EXCHG( SOURCE->m_Edge, DEST->m_Edge ); EXCHG( SOURCE->m_Shape, DEST->m_Shape ); break; case DRAW_NOCONNECT_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawNoConnectStruct*) Item ) #define DEST ( (DrawNoConnectStruct*) image ) EXCHG( SOURCE->m_Pos, DEST->m_Pos ); break; case DRAW_PART_TEXT_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ( (DrawPolylineStruct*) Item ) #define DEST ( (DrawPolylineStruct*) image ) break; // not directly used in schematic: default: DisplayInfo( NULL, wxT( "SwapData() error: unexpected type" ) ); break; } } /***********************************************************************/ void WinEDA_SchematicFrame::SaveCopyInUndoList( SCH_ITEM * ItemToCopy, int flag_type_command ) /***********************************************************************/ /* Create a copy of the current schematic draw list, and put it in the undo list. * A DrawPickedStruct wrapper is created to handle the draw list. * the .m_Son of this wrapper points the list of items * * flag_type_command = * 0 (unspecified) * IS_CHANGED * IS_NEW * IS_DELETED * IS_WIRE_IMAGE * * for 0: only a wrapper is created. The used must init the .Flags member of the * wrapper, and add the item list to the wrapper * If it is a delete command, items are put on list with the .Flags member set to IS_DELETED. * When it will be really deleted, the EEDrawList and the subhierarchy will be deleted. * If it is only a copy, the EEDrawList and the subhierarchy must NOT be deleted. * * Note: * Edit wires and busses is a bit complex. * because when a new wire is added, modifications in wire list * (wire concatenation) there are modified items, deleted items and new items * so flag_type_command is IS_WIRE_IMAGE: the struct ItemToCopy is a list of wires * saved in Undo List (for Undo or Redo commands, saved wires will be exchanged with current wire list */ { SCH_ITEM* CopyItem; DrawPickedStruct* NewList = new DrawPickedStruct( NULL ); NewList->m_Flags = flag_type_command; if( ItemToCopy ) { switch( flag_type_command ) { case 0: break; case IS_CHANGED: /* Create a copy of schematic */ CopyItem = DuplicateStruct( ItemToCopy ); NewList->SetSon( CopyItem ); if( ItemToCopy->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { DrawPickedStruct* PickedList = (DrawPickedStruct*) CopyItem; while( PickedList ) { CopyItem = PickedList->m_PickedStruct; CopyItem->m_Flags = flag_type_command; PickedList->m_Image = CopyItem->m_Image; PickedList = PickedList->Next(); } } else { CopyItem->m_Flags = flag_type_command; CopyItem->m_Image = ItemToCopy; } break; case IS_NEW: if( ItemToCopy->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { NewList->SetSon( ItemToCopy ); DrawPickedStruct* PickedList = (DrawPickedStruct*) ItemToCopy; while( PickedList ) { CopyItem = PickedList->m_PickedStruct; PickedList->m_Image = CopyItem; PickedList->m_PickedStruct = NULL; PickedList->m_Flags = flag_type_command; PickedList = PickedList->Next(); } } else { NewList->m_Image = ItemToCopy; } break; case IS_NEW | IS_CHANGED: case IS_WIRE_IMAGE: NewList->SetSon( ItemToCopy ); break; case IS_DELETED: NewList->SetSon( ItemToCopy ); ItemToCopy->m_Flags = flag_type_command; if( ItemToCopy->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { DrawPickedStruct* PickedList = (DrawPickedStruct*) ItemToCopy; while( PickedList ) { CopyItem = PickedList->m_PickedStruct; CopyItem->m_Flags = flag_type_command; PickedList->m_Flags = flag_type_command; PickedList = PickedList->Next(); } } break; default: DisplayError( this, wxT( "SaveCopyInUndoList() error" ) ); break; } } /* Save the copy in undo list */ GetScreen()->AddItemToUndoList( NewList ); /* Clear redo list, because after new save there is no redo to do */ ( (SCH_SCREEN*) GetScreen() )->ClearUndoORRedoList( GetScreen()->m_RedoList ); GetScreen()->m_RedoList = NULL; } /**********************************************************/ bool WinEDA_SchematicFrame::GetSchematicFromRedoList() /**********************************************************/ /* Redo the last edition: * - Save the current schematic in undo list * - Get the old version * @return FALSE if nothing done, else TRUE */ { if( GetScreen()->m_RedoList == NULL ) return FALSE; /* Get the old wrapper and put it in UndoList */ DrawPickedStruct* List = (DrawPickedStruct*) GetScreen()->GetItemFromRedoList(); GetScreen()->AddItemToUndoList( List ); /* Redo the command: */ PutDataInPreviousState( List ); CurrentDrawItem = NULL; GetScreen()->SetModify(); ReCreateHToolbar(); SetToolbars(); return TRUE; } /***************************************************************************/ void WinEDA_SchematicFrame::PutDataInPreviousState( DrawPickedStruct* List ) /***************************************************************************/ /* Used in undo or redo command. * Put data pointed by List in the previous state, i.e. the state memorised by List */ { SCH_ITEM* FirstItem = (SCH_ITEM*) List->GetSon(); SCH_ITEM* item; DrawPickedStruct* PickedList; switch( List->m_Flags ) { case IS_CHANGED: /* Exchange old and new data for each item */ if( FirstItem == NULL ) { DisplayError( this, wxT( "PutDataInPreviousState() error : Null item" ) ); break; } if( FirstItem->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { DrawPickedStruct* PickedList = (DrawPickedStruct*) FirstItem; while( PickedList ) { SwapData( PickedList->m_PickedStruct ); PickedList = PickedList->Next(); } } else { SwapData( FirstItem ); } break; case IS_NEW: /* new items are deleted */ List->m_Flags = IS_DELETED; if( FirstItem && FirstItem->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { PickedList = (DrawPickedStruct*) FirstItem; while( PickedList ) { item = (SCH_ITEM*) PickedList->m_Image; ( (SCH_SCREEN*) GetScreen() )->RemoveFromDrawList( item ); item->m_Flags = IS_DELETED; PickedList->m_PickedStruct = item; PickedList->m_Flags = IS_DELETED; PickedList = PickedList->Next(); } } else { FirstItem = (SCH_ITEM*)List->m_Image; ( (SCH_SCREEN*) GetScreen() )->RemoveFromDrawList( FirstItem ); FirstItem->m_Flags = IS_DELETED; List->SetSon( FirstItem ); } break; case IS_DELETED: /* deleted items are put in EEdrawList, as new items */ List->m_Flags = IS_NEW; if( FirstItem->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { PickedList = (DrawPickedStruct*) FirstItem; while( PickedList ) { item = PickedList->m_PickedStruct; item->SetNext( GetScreen()->EEDrawList ); GetScreen()->EEDrawList = item; item->m_Flags = 0; PickedList->m_PickedStruct = NULL; PickedList->m_Image = item; PickedList->m_Flags = IS_NEW; PickedList = PickedList->Next(); } } else { FirstItem->SetNext( GetScreen()->EEDrawList ); GetScreen()->EEDrawList = FirstItem; FirstItem->m_Flags = 0; List->m_Image = List->GetSon(); List->SetSon( NULL ); } break; case IS_WIRE_IMAGE: /* Exchange the current wires and the oild wires */ List->SetSon( ( (SCH_SCREEN*) GetScreen() )->ExtractWires( FALSE ) ); while( FirstItem ) { SCH_ITEM* nextitem = FirstItem->Next(); FirstItem->SetNext( GetScreen()->EEDrawList ); GetScreen()->EEDrawList = FirstItem; FirstItem->m_Flags = 0; FirstItem = nextitem; } break; case IS_NEW | IS_CHANGED: case IS_DELETED | IS_CHANGED: if( !FirstItem || FirstItem->Type() != DRAW_PICK_ITEM_STRUCT_TYPE ) { DisplayError( this, wxT( "GetSchematicFromUndoList() error: Pickstruct expected" ) ); break; } PickedList = (DrawPickedStruct*) FirstItem; while( PickedList ) { switch( PickedList->m_Flags ) { case IS_CHANGED: SwapData( PickedList->m_PickedStruct ); break; case IS_NEW: item = (SCH_ITEM*) PickedList->m_Image; ( (SCH_SCREEN*) GetScreen() )->RemoveFromDrawList( item ); item->m_Flags = IS_DELETED; PickedList->m_PickedStruct = item; PickedList->m_Flags = IS_DELETED; break; case IS_DELETED: item = PickedList->m_PickedStruct; item->SetNext( GetScreen()->EEDrawList ); GetScreen()->EEDrawList = item; item->m_Flags = 0; PickedList->m_PickedStruct = NULL; PickedList->m_Image = item; PickedList->m_Flags = IS_NEW; break; } PickedList = PickedList->Next(); } break; default: DisplayError( this, wxT( "GetSchematicFromUndoList() error: Unknown cmd" ) ); break; } } /**********************************************************/ bool WinEDA_SchematicFrame::GetSchematicFromUndoList() /**********************************************************/ /* Undo the last edition: * - Save the current schematic in Redo list * - Get an old version of the schematic * @return FALSE if nothing done, else TRUE */ { if( GetScreen()->m_UndoList == NULL ) return FALSE; /* Get the old wrapper and put it in RedoList (the real data list is the m_Son member) */ DrawPickedStruct* List = (DrawPickedStruct*) GetScreen()->GetItemFromUndoList(); GetScreen()->AddItemToRedoList( List ); /* Undo the command */ PutDataInPreviousState( List ); CurrentDrawItem = NULL; GetScreen()->SetModify(); ReCreateHToolbar(); SetToolbars(); return TRUE; } /*********************************************************/ void SCH_SCREEN::ClearUndoORRedoList( EDA_BaseStruct* List ) /*********************************************************/ /* free the undo or redo list from List element * Wrappers are deleted. * datas pointed by wrappers are deleted if not flagged IS_NEW * because they are copy of used data or they are not in use (DELETED) */ { EDA_BaseStruct* nextitem; EDA_BaseStruct* FirstItem; int CmdType; if( List == NULL ) return; for( ; List != NULL; List = nextitem ) { nextitem = List->Next(); FirstItem = List->GetSon(); CmdType = List->m_Flags; SAFE_DELETE( List ); if( FirstItem == NULL ) continue; if( FirstItem->Type() == DRAW_PICK_ITEM_STRUCT_TYPE ) { EDA_BaseStruct* item; DrawPickedStruct* PickedList = (DrawPickedStruct*) FirstItem; while( PickedList ) // delete wrapper list and copies { item = PickedList->m_PickedStruct; if( item ) { #if 0 if( item->Type() == DRAW_SHEET_STRUCT_TYPE ) { printf( "schematic undo_redo.cpp: undo_redo with a DRAW_SHEET_STRUCT_TYPE, checkme!!\n" ); DrawSheetStruct* sheet = (DrawSheetStruct*) item; /* Delete sub hierarchy if the sheet must be deleted */ if( (sheet->m_Flags & IS_DELETED) != 0 ) DeleteSubHierarchy( sheet, FALSE ); else /* EEDrawList, UndoList and Redo list are shared, and are deleted * only if sheet is deleted */ { if( (item->m_Flags & IS_NEW) == 0 ) { /* * sheet->EEDrawList = NULL; * sheet->m_UndoList = NULL; * sheet->m_RedoList = NULL; */ } } } #endif if( (item->m_Flags & IS_NEW) == 0 ) { SAFE_DELETE( item ); } } DrawPickedStruct* wrapper = PickedList; PickedList = PickedList->Next(); SAFE_DELETE( wrapper ); } } else // This is a single item: deleted copy { if( CmdType == IS_WIRE_IMAGE ) { while( FirstItem ) { EDA_BaseStruct* nextitem = FirstItem->Next(); delete FirstItem; FirstItem = nextitem; } } else { #if 0 if( FirstItem->Type() == DRAW_SHEET_STRUCT_TYPE ) { DrawSheetStruct* sheet = (DrawSheetStruct*) FirstItem; /* Delete hierarchy if the sheet must be deleted */ if( (sheet->m_Flags & IS_DELETED) == IS_DELETED ) DeleteSubHierarchy( sheet, FALSE ); else { if( (FirstItem->m_Flags & IS_NEW) == 0 ) { printf( "schematic undo_redo.cpp undo_redo with a DRAW_SHEET_STRUCT_TYPE, checkme!!\n" ); /* * sheet->EEDrawList = NULL; * sheet->m_UndoList = NULL; * sheet->m_RedoList = NULL; */ } } } #endif if( (FirstItem->m_Flags & IS_NEW) == 0 ) { SAFE_DELETE( FirstItem ); } } } } } /*****************************************/ void SCH_SCREEN::ClearUndoRedoList() /*****************************************/ /* free the undo and the redo lists */ { ClearUndoORRedoList( m_UndoList ); m_UndoList = NULL; ClearUndoORRedoList( m_RedoList ); m_RedoList = NULL; } /***********************************************************/ void SCH_SCREEN::AddItemToUndoList( EDA_BaseStruct* newitem ) /************************************************************/ /* Put newitem in head of undo list * Deletes olds items if > count max. */ { int ii; EDA_BaseStruct* item, * nextitem; if( newitem == NULL ) return; if( m_UndoList ) m_UndoList->SetBack( newitem ); newitem->SetNext( m_UndoList ); newitem->SetBack( NULL ); m_UndoList = newitem; /* Free oldest items, if count max reached */ for( ii = 0, item = m_UndoList; ii < m_UndoRedoCountMax; ii++ ) { if( item->Next() == NULL ) return; item = item->Next(); } if( item == NULL ) return; nextitem = item->Next(); item->SetNext( NULL ); // Set end of chain // Delete the extra items ClearUndoORRedoList( nextitem ); } /***********************************************************/ void SCH_SCREEN::AddItemToRedoList( EDA_BaseStruct* newitem ) /***********************************************************/ { int ii; EDA_BaseStruct* item, * nextitem; if( newitem == NULL ) return; newitem->SetBack( NULL ); newitem->SetNext( m_RedoList ); m_RedoList = newitem; /* Free first items, if count max reached */ for( ii = 0, item = m_RedoList; ii < m_UndoRedoCountMax; ii++ ) { if( item->Next() == NULL ) break; item = item->Next(); } if( item == NULL ) return; nextitem = item->Next(); item->SetNext( NULL ); // Set end of chain // Delete the extra items ClearUndoORRedoList( nextitem ); }