/********************************************/ /* 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->m_StructType ) { 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 DRAW_LABEL_STRUCT_TYPE: case DRAW_GLOBAL_LABEL_STRUCT_TYPE: case DRAW_TEXT_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ((DrawTextStruct*)Item) #define DEST ((DrawTextStruct*)image) DEST->SwapData(SOURCE); break; case DRAW_LIB_ITEM_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ((EDA_SchComponentStruct*)Item) #define DEST ((EDA_SchComponentStruct*)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_SHEETLABEL_STRUCT_TYPE: #undef SOURCE #undef DEST #define SOURCE ((DrawSheetLabelStruct*)Item) #define DEST ((DrawSheetLabelStruct*)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(EDA_BaseStruct * 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 */ { EDA_BaseStruct * 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 */ NewList->m_Son = CopyItem = DuplicateStruct(ItemToCopy); if ( ItemToCopy->m_StructType == 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 = (DrawPickedStruct *)PickedList->Pnext; } } else { CopyItem->m_Flags = flag_type_command; CopyItem->m_Image = ItemToCopy; } break; case IS_NEW: if ( ItemToCopy->m_StructType == DRAW_PICK_ITEM_STRUCT_TYPE) { NewList->m_Son = 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 = (DrawPickedStruct *)PickedList->Pnext; } } else { NewList->m_Image = ItemToCopy; } break; case IS_NEW|IS_CHANGED: case IS_WIRE_IMAGE: NewList->m_Son = ItemToCopy; break; case IS_DELETED: NewList->m_Son = ItemToCopy; ItemToCopy->m_Flags = flag_type_command; if ( ItemToCopy->m_StructType == 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 = (DrawPickedStruct *)PickedList->Pnext; } } 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 */ GetScreen()->ClearUndoORRedoList(GetScreen()->m_RedoList); GetScreen()->m_RedoList = NULL; } /**********************************************************/ void WinEDA_SchematicFrame::GetSchematicFromRedoList(void) /**********************************************************/ /* Redo the last edition: - Save the current schematic in undo list - Get the old version */ { if ( GetScreen()->m_RedoList == NULL ) return; /* 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(); } /***************************************************************************/ 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 */ { EDA_BaseStruct * FirstItem = List->m_Son; EDA_BaseStruct * 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->m_StructType == 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->m_StructType == DRAW_PICK_ITEM_STRUCT_TYPE) { PickedList = (DrawPickedStruct *) FirstItem; while ( PickedList ) { item = PickedList->m_Image; GetScreen()->RemoveFromDrawList(item); item->m_Flags = IS_DELETED; PickedList->m_PickedStruct = item; PickedList->m_Flags = IS_DELETED; PickedList = PickedList->Next(); } } else { FirstItem = List->m_Image; GetScreen()->RemoveFromDrawList(FirstItem); FirstItem->m_Flags = IS_DELETED; List->m_Son = FirstItem; } break; case IS_DELETED: /* deleted items are put in EEdrawList, as new items */ List->m_Flags = IS_NEW; if ( FirstItem->m_StructType == DRAW_PICK_ITEM_STRUCT_TYPE) { PickedList = (DrawPickedStruct *) FirstItem; while ( PickedList ) { item = PickedList->m_PickedStruct; item->Pnext = 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->Pnext = GetScreen()->EEDrawList; GetScreen()->EEDrawList = FirstItem; FirstItem->m_Flags = 0; List->m_Image = List->m_Son; List->m_Son = NULL; } break; case IS_WIRE_IMAGE: /* Exchange the current wires and the oild wires */ List->m_Son = GetScreen()->ExtractWires(FALSE); while ( FirstItem ) { EDA_BaseStruct * nextitem = FirstItem->Pnext; FirstItem->Pnext = 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->m_StructType != 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 = PickedList->m_Image; 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->Pnext = 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; } } /**********************************************************/ void WinEDA_SchematicFrame::GetSchematicFromUndoList(void) /**********************************************************/ /* Undo the last edition: - Save the current schematic in Redo list - Get an old version of the schematic */ { if ( GetScreen()->m_UndoList == NULL ) return; /* 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(); } /*********************************************************/ 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->Pnext; FirstItem = List->m_Son; CmdType = List->m_Flags; delete List; if (FirstItem == NULL ) continue; if ( FirstItem->m_StructType == 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 ( item->m_StructType == DRAW_SHEET_STRUCT_TYPE ) { 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; } } } if ( (item->m_Flags & IS_NEW) == 0 ) delete item; } DrawPickedStruct * wrapper = PickedList; PickedList = PickedList->Next(); delete wrapper; } } else // This is a single item: deleted copy { if ( CmdType == IS_WIRE_IMAGE ) { while ( FirstItem ) { EDA_BaseStruct * nextitem = FirstItem->Pnext; delete FirstItem; FirstItem = nextitem; } } else { if ( FirstItem->m_StructType == 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 ) { sheet->EEDrawList = NULL; sheet->m_UndoList = NULL; sheet->m_RedoList = NULL; } } } if ( (FirstItem->m_Flags & IS_NEW) == 0 ) delete FirstItem; } } } } /*****************************************/ void SCH_SCREEN::ClearUndoRedoList(void) /*****************************************/ /* 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->Pback = newitem; newitem->Pnext = m_UndoList; newitem->Pback = NULL; m_UndoList = newitem; /* Free oldest items, if count max reached */ for ( ii = 0, item = m_UndoList; ii < m_UndoRedoCountMax; ii++ ) { if ( item->Pnext == NULL ) return; item = item->Pnext; } if ( item == NULL ) return; nextitem = item->Pnext; item->Pnext = 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->Pback = NULL; newitem->Pnext = m_RedoList; m_RedoList = newitem; /* Free first items, if count max reached */ for ( ii = 0, item = m_RedoList; ii < m_UndoRedoCountMax; ii++ ) { if ( item->Pnext == NULL ) break; item = item->Pnext; } if ( item == NULL ) return; nextitem = item->Pnext; item->Pnext = NULL; // Set end of chain // Delete the extra items ClearUndoORRedoList(nextitem); }