# HG changeset patch # User lbessard # Date 1185463401 -7200 # Node ID 7187e1c00975d7e5e72c2e9cf4623cf9b02b40e0 # Parent e24d2f917c7ee2a7eb5f7266917cef04c4a52ae7 Adding support for Undo/Redo and Unsaved File On Close detection diff -r e24d2f917c7e -r 7187e1c00975 Dialogs.py --- a/Dialogs.py Wed Jul 25 10:06:29 2007 +0200 +++ b/Dialogs.py Thu Jul 26 17:23:21 2007 +0200 @@ -384,21 +384,23 @@ self.RefreshNameList() def SetValues(self, values): - for name, value in values.items(): - if name == "type": - if value == INPUT: - self.Class.SetStringSelection("Input") - if value == OUTPUT: - self.Class.SetStringSelection("Output") - if value == INOUT: - self.Class.SetStringSelection("InOut") - elif name == "name" and value != "": - if self.Name.FindString(value) != wxNOT_FOUND: - self.Name.SetStringSelection(value) - self.Expression.Enable(False) - else: - self.Expression.SetValue(value) - self.Name.Enable(False) + value_type = values.get("type", None) + value_name = values.get("name", None) + if value_type: + if value_type == INPUT: + self.Class.SetStringSelection("Input") + if value_type == OUTPUT: + self.Class.SetStringSelection("Output") + if value_type == INOUT: + self.Class.SetStringSelection("InOut") + self.RefreshNameList() + if value_name: + if self.Name.FindString(value_name) != wxNOT_FOUND: + self.Name.SetStringSelection(value_name) + self.Expression.Enable(False) + else: + self.Expression.SetValue(value_name) + self.Name.Enable(False) self.RefreshPreview() def GetValues(self): diff -r e24d2f917c7e -r 7187e1c00975 LDViewer.py --- a/LDViewer.py Wed Jul 25 10:06:29 2007 +0200 +++ b/LDViewer.py Thu Jul 26 17:23:21 2007 +0200 @@ -411,7 +411,29 @@ self.DeleteWire(self.SelectedElement) else: self.SelectedElement.Delete() - self.Refresh() + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() + elif keycode == WXK_LEFT: + if event.ControlDown() and event.ShiftDown(): + self.Scroll(0, ypos) + elif event.ControlDown(): + self.Scroll(max(0, xpos - 1), ypos) + elif keycode == WXK_RIGHT: + if event.ControlDown() and event.ShiftDown(): + self.Scroll(xmax, ypos) + elif event.ControlDown(): + self.Scroll(min(xpos + 1, xmax), ypos) + elif keycode == WXK_UP: + if event.ControlDown() and event.ShiftDown(): + self.Scroll(xpos, 0) + elif event.ControlDown(): + self.Scroll(xpos, max(0, ypos - 1)) + elif keycode == WXK_DOWN: + if event.ControlDown() and event.ShiftDown(): + self.Scroll(xpos, ymax) + elif event.ControlDown(): + self.Scroll(xpos, min(ypos + 1, ymax)) event.Skip() #------------------------------------------------------------------------------- @@ -493,6 +515,8 @@ rung.SelectElement(wire) self.RefreshPosition(coil) self.Rungs.append(rung) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() def AddLadderContact(self): @@ -584,6 +608,8 @@ rung.RefreshBoundingBox() new_bbox = rung.GetBoundingBox() self.RefreshRungs(new_bbox.height - old_bbox.height, rungindex + 1) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() else: message = wxMessageDialog(self, "You must select the wire where a contact should be added!", "Error", wxOK|wxICON_ERROR) @@ -803,6 +829,8 @@ rung.RefreshBoundingBox() new_bbox = rung.GetBoundingBox() self.RefreshRungs(new_bbox.height - old_bbox.height, rungindex + 1) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() else: message = wxMessageDialog(self, "The group of block must be coherent!", "Error", wxOK|wxICON_ERROR) @@ -1158,6 +1186,8 @@ contact.SetName(values["name"]) contact.SetType(values["type"]) contact.RefreshModel(False) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1182,6 +1212,8 @@ coil.SetName(values["name"]) coil.SetType(values["type"]) coil.RefreshModel(False) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() diff -r e24d2f917c7e -r 7187e1c00975 PLCControler.py --- a/PLCControler.py Wed Jul 25 10:06:29 2007 +0200 +++ b/PLCControler.py Thu Jul 26 17:23:21 2007 +0200 @@ -162,6 +162,7 @@ self.VerifyXML = False self.Project = None self.ProjectBuffer = None + self.Buffering = False self.FilePath = "" self.FileName = "" self.ProgramFilePath = "" @@ -187,16 +188,10 @@ # Create the project self.Project = plcopen.project() self.Project.setName(name) + self.SetFilePath("") # Initialize the project buffer - #self.ProjectBuffer = UndoBuffer(self.Copy(self.Project)) - - # Change project name - def SetProjectName(self, name): - self.Project.setName(name) - - # Return project name - def GetProjectName(self): - return self.Project.getName() + self.ProjectBuffer = UndoBuffer(self.Copy(self.Project), False) + self.Buffering = False # Return project pou names def GetProjectPouNames(self): @@ -219,6 +214,10 @@ variables.append(action.getName()) return variables + # Return if project is saved + def ProjectIsSaved(self): + return self.ProjectBuffer.IsCurrentSaved() + # Return file path if project is an open file def GetFilePath(self): return self.FilePath @@ -244,12 +243,19 @@ self.FileName = os.path.splitext(os.path.basename(filepath))[0] # Change project properties - def SetProjectProperties(self, values): - self.Project.setFileHeader(values) - + def SetProjectProperties(self, name = None, properties = None): + if name != None: + self.Project.setName(name) + if properties != None: + self.Project.setFileHeader(properties) + if name != None or properties != None: + self.BufferProject() + # Return project properties def GetProjectProperties(self): - return self.Project.getFileHeader() + properties = self.Project.getFileHeader() + properties["projectName"] = self.Project.getName() + return properties # Return project informations def GetProjectInfos(self): @@ -376,15 +382,15 @@ def GenerateProgram(self, filepath): if self.Project: - #try: - program = GenerateCurrentProgram(self.Project) - programfile = open(filepath, "w") - programfile.write(program) - programfile.close() - self.ProgramFilePath = filepath - return True - #except: - # pass + try: + program = GenerateCurrentProgram(self.Project) + programfile = open(filepath, "w") + programfile.write(program) + programfile.close() + self.ProgramFilePath = filepath + return True + except: + pass return False #------------------------------------------------------------------------------- @@ -397,6 +403,7 @@ self.Project.appendPou(name, pou_type, body_type) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Remove a pou from project def ProjectRemovePou(self, name): @@ -418,40 +425,47 @@ self.Project.removePou(name) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Add a configuration to Project def ProjectAddConfiguration(self, name): self.Project.addConfiguration(name) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Remove a configuration from project def ProjectRemoveConfiguration(self, name): self.Project.removeConfiguration(name) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Add a resource to a configuration of the Project def ProjectAddConfigurationResource(self, config, name): self.Project.addConfigurationResource(config, name) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Remove a resource from a configuration of the project def ProjectRemoveConfigurationResource(self, config, name): self.Project.removeConfigurationResource(config, name) self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Add a Transition to a Project Pou def ProjectAddPouTransition(self, pou_name, transition_name, transition_type): pou = self.Project.getPou(pou_name) pou.addTransition(transition_name, transition_type) + self.BufferProject() # Add a Transition to a Project Pou def ProjectAddPouAction(self, pou_name, action_name, action_type): pou = self.Project.getPou(pou_name) pou.addAction(action_name, action_type) + self.BufferProject() # Change the name of a pou def ChangePouName(self, old_name, new_name): @@ -464,6 +478,7 @@ self.ElementsOpened[idx] = new_name self.RefreshPouUsingTree() self.RefreshBlockTypes() + self.BufferProject() # Change the name of a pou transition def ChangePouTransitionName(self, pou_name, old_name, new_name): @@ -477,6 +492,7 @@ if old_computedname in self.ElementsOpened: idx = self.ElementsOpened.index(old_computedname) self.ElementsOpened[idx] = new_computedname + self.BufferProject() # Change the name of a pou action def ChangePouActionName(self, pou_name, old_name, new_name): @@ -490,7 +506,8 @@ if old_computedname in self.ElementsOpened: idx = self.ElementsOpened.index(old_computedname) self.ElementsOpened[idx] = new_computedname - + self.BufferProject() + # Change the name of a pou action def ChangePouVariableName(self, pou_name, old_name, new_name): # Found the pou action corresponding to old name and change its name to new name @@ -500,6 +517,7 @@ if var.getName() == old_name: var.setName(new_name) self.RefreshBlockTypes() + self.BufferProject() # Change the name of a configuration def ChangeConfigurationName(self, old_name, new_name): @@ -509,7 +527,8 @@ # If configuration is currently opened, change its name in the list of opened elements for idx, element in enumerate(self.ElementsOpened): self.ElementsOpened[idx] = element.replace(old_name, new_name) - + self.BufferProject() + # Change the name of a configuration resource def ChangeConfigurationResourceName(self, config_name, old_name, new_name): # Found the resource corresponding to old name and change its name to new name @@ -521,6 +540,7 @@ if old_computedname in self.ElementsOpened: idx = self.ElementsOpened.index(old_computedname) self.ElementsOpened[idx] = new_computedname + self.BufferProject() # Return the type of the pou given by its name def GetPouType(self, name): @@ -579,11 +599,13 @@ def ProjectRemovePouTransition(self, pou_name, transition_name): pou = self.Project.getPou(pou_name) pou.removeTransition(transition_name) + self.BufferProject() # Add a Transition to a Project Pou def ProjectRemovePouAction(self, pou_name, action_name): pou = self.Project.getPou(pou_name) pou.removeAction(action_name) + self.BufferProject() # Extract varlists from a list of vars def ExtractVarLists(self, vars): @@ -627,7 +649,8 @@ for vartype, varlist in self.ExtractVarLists(vars): configuration.globalVars.append(varlist) self.RefreshBlockTypes() - + self.BufferProject() + # Return the configuration globalvars def GetConfigurationGlobalVars(self, name): vars = [] @@ -669,7 +692,8 @@ for vartype, varlist in self.ExtractVarLists(vars): resource.globalVars.append(varlist) self.RefreshBlockTypes() - + self.BufferProject() + # Return the resource globalvars def GetConfigurationResourceGlobalVars(self, config_name, name): vars = [] @@ -745,7 +769,8 @@ # Set Pou interface pou.setVars(self.ExtractVarLists(vars)) self.RefreshBlockTypes() - + self.BufferProject() + # Replace the return type of the pou given by its name (only for functions) def SetPouInterfaceReturnType(self, name, type): pou = self.Project.getPou(name) @@ -759,6 +784,7 @@ # Change return type return_type.setValue(type) self.RefreshBlockTypes() + self.BufferProject() # Return the return type of the pou given by its name def GetPouInterfaceReturnTypeByName(self, name): @@ -1951,8 +1977,9 @@ self.Project = plcopen.project() self.Project.loadXMLTree(tree.childNodes[0]) - self.UndoBuffer = UndoBuffer(self.Copy(self.Project), True) self.SetFilePath(filepath) + self.ProjectBuffer = UndoBuffer(self.Copy(self.Project), True) + self.Buffering = False self.ElementsOpened = [] self.CurrentElementEditing = None self.RefreshPouUsingTree() @@ -1982,7 +2009,7 @@ xmlfile = open(self.FilePath,"w") xmlfile.write(text) xmlfile.close() - #self.ProjectBuffer.CurrentSaved() + self.ProjectBuffer.CurrentSaved() if filepath: self.SetFilePath(filepath) return True @@ -1998,18 +2025,25 @@ return cPickle.loads(cPickle.dumps(model)) def BufferProject(self): - self.ProjectBuffer.Buffering(self.Copy(self)) + self.ProjectBuffer.Buffering(self.Copy(self.Project)) + + def StartBuffering(self): + self.ProjectBuffer.Buffering(self.Project) + self.Buffering = True + + def EndBuffering(self): + if self.Buffering: + self.Project = self.Copy(self.Project) + self.Buffering = False def ProjectIsSaved(self): return self.ProjectBuffer.IsCurrentSaved() def LoadPrevious(self): self.Project = self.Copy(self.ProjectBuffer.Previous()) - self.RefreshElementsOpened() def LoadNext(self): self.Project = self.Copy(self.ProjectBuffer.Next()) - self.RefreshElementsOpened() def GetBufferState(self): first = self.ProjectBuffer.IsFirst() diff -r e24d2f917c7e -r 7187e1c00975 PLCOpenEditor.py --- a/PLCOpenEditor.py Wed Jul 25 10:06:29 2007 +0200 +++ b/PLCOpenEditor.py Thu Jul 26 17:23:21 2007 +0200 @@ -190,6 +190,10 @@ kind=wx.ITEM_NORMAL, text=u'Remove Configuration') self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wxID_PLCOPENEDITOREDITMENUITEMS0) + self.Bind(wx.EVT_MENU, self.OnUndoMenu, + id=wxID_PLCOPENEDITOREDITMENUITEMS1) + self.Bind(wx.EVT_MENU, self.OnRedoMenu, + id=wxID_PLCOPENEDITOREDITMENUITEMS2) self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wxID_PLCOPENEDITOREDITMENUITEMS4) self.Bind(wx.EVT_MENU, self.OnCopyMenu, @@ -373,6 +377,7 @@ self._init_utils() self.SetClientSize(wx.Size(1000, 600)) self.SetMenuBar(self.menuBar1) + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame, id=wxID_PLCOPENEDITOR) self.splitterWindow1 = wx.SplitterWindow(id=wxID_PLCOPENEDITORSPLITTERWINDOW1, name='splitterWindow1', parent=self, point=wx.Point(0, 0), @@ -443,11 +448,18 @@ self.RefreshFileMenu() self.RefreshEditMenu() + self.RefreshTitle() self.RefreshToolBar() def GetDrawingMode(self): return self.DrawingMode + def RefreshTitle(self): + if self.Controler.HasOpenedProject() > 0: + self.SetTitle("PLCOpenEditor - %s"%self.Controler.GetFilename()) + else: + self.SetTitle("PLCOpenEditor") + def RefreshFileMenu(self): if self.FileMenu: if self.Controler.HasOpenedProject(): @@ -470,8 +482,13 @@ def RefreshEditMenu(self): if self.EditMenu: - self.EditMenu.FindItemByPosition(1).Enable(False) - self.EditMenu.FindItemByPosition(2).Enable(False) + if self.Controler.HasOpenedProject(): + undo, redo = self.Controler.GetBufferState() + self.EditMenu.Enable(wxID_PLCOPENEDITOREDITMENUITEMS1, undo) + self.EditMenu.Enable(wxID_PLCOPENEDITOREDITMENUITEMS2, redo) + else: + self.EditMenu.Enable(wxID_PLCOPENEDITOREDITMENUITEMS1, False) + self.EditMenu.Enable(wxID_PLCOPENEDITOREDITMENUITEMS2, False) if self.Controler.HasOpenedProject(): if self.TabsOpened.GetPageCount() > 0: self.EditMenu.FindItemByPosition(0).Enable(True) @@ -495,18 +512,29 @@ def ShowProperties(self): old_values = self.Controler.GetProjectProperties() - old_values["projectName"] = self.Controler.GetProjectName() dialog = ProjectDialog(self) dialog.SetValues(old_values) if dialog.ShowModal() == wxID_OK: new_values = dialog.GetValues() projectname = new_values.pop("projectName") new_values["creationDateTime"] = old_values["creationDateTime"] - self.Controler.SetProjectName(projectname) - self.Controler.SetProjectProperties(new_values) + self.Controler.SetProjectProperties(projectname, new_values) self.RefreshProjectTree() dialog.Destroy() + def OnCloseFrame(self, event): + if not self.Controler.ProjectIsSaved(): + dialog = wxMessageDialog(self, "There are changes, do you want to save?", "Close Application", wxYES_NO|wxCANCEL|wxICON_QUESTION) + answer = dialog.ShowModal() + dialog.Destroy() + if answer == wxID_YES: + self.SaveProject() + event.Skip() + elif answer == wxID_NO: + event.Skip() + else: + event.Skip() + def OnNewProjectMenu(self, event): dialog = ProjectDialog(self) if dialog.ShowModal() == wxID_OK: @@ -514,7 +542,8 @@ projectname = values.pop("projectName") values["creationDateTime"] = datetime(*localtime()[:6]) self.Controler.CreateNewProject(projectname) - self.Controler.SetProjectProperties(values) + self.Controler.SetProjectProperties(projectname, values) + self.RefreshTitle() self.RefreshFileMenu() self.RefreshEditMenu() self.RefreshProjectTree() @@ -533,6 +562,7 @@ self.Controler.OpenXMLFile(filepath) self.TabsOpened.DeleteAllPages() self.RefreshProjectTree() + self.RefreshTitle() self.RefreshFileMenu() self.RefreshEditMenu() self.RefreshToolBar() @@ -546,6 +576,7 @@ self.TabsOpened.DeletePage(selected) if self.TabsOpened.GetPageCount() > 0: self.TabsOpened.SetSelection(min(selected, self.TabsOpened.GetPageCount() - 1)) + self.RefreshTitle() self.RefreshFileMenu() self.RefreshEditMenu() self.RefreshToolBar() @@ -555,6 +586,7 @@ self.Controler.Reset() self.TabsOpened.DeleteAllPages() self.ProjectTree.DeleteAllItems() + self.RefreshTitle() self.RefreshFileMenu() self.RefreshEditMenu() self.RefreshToolBar() @@ -589,13 +621,15 @@ result = self.Controler.SaveXMLFile() if not result: self.SaveProjectAs() + else: + self.RefreshTitle() def SaveProjectAs(self): filepath = self.Controler.GetFilePath() if filepath != "": directory, filename = os.path.split(filepath) else: - directory, filename = os.getcwd(), "%s.xml"%self.Controler.GetProjectName() + directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties() dialog = wxFileDialog(self, "Choose a file", directory, filename, "PLCOpen files (*.xml)|*.xml|All files|*.*", wxSAVE|wxOVERWRITE_PROMPT) if dialog.ShowModal() == wxID_OK: filepath = dialog.GetPath() @@ -609,6 +643,7 @@ message = wxMessageDialog(self, "%s is not a valid folder!"%os.path.dirname(filepath), "Error", wxOK|wxICON_ERROR) message.ShowModal() message.Destroy() + self.RefreshTitle() dialog.Destroy() def OnPropertiesMenu(self, event): @@ -768,6 +803,9 @@ event.Skip() def OnPouSelectedChanged(self, event): + old_selected = self.TabsOpened.GetSelection() + if old_selected >= 0: + self.TabsOpened.GetPage(old_selected).ResetBuffer() selected = event.GetSelection() if selected >= 0: self.Controler.RefreshCurrentElementEditing(selected) @@ -809,7 +847,7 @@ item = event.GetItem() itemtype = self.ProjectTree.GetPyData(item) if itemtype == ITEM_PROJECT: - self.Controler.SetProjectName(new_name) + self.Controler.SetProjectProperties(name = new_name) elif itemtype == ITEM_POU: if new_name.upper() in self.Controler.GetProjectPouNames(): message = "\"%s\" pou already exists!"%new_name @@ -1088,6 +1126,9 @@ else: idx = self.Controler.ChangePouActionEditing(grandparent_name, name) if idx != None: + old_selected = self.TabsOpened.GetSelection() + if old_selected >= 0: + self.TabsOpened.GetPage(old_selected).ResetBuffer() self.TabsOpened.SetSelection(idx) window = self.TabsOpened.GetPage(idx) window.RefreshView() @@ -1197,9 +1238,30 @@ def OnRefreshMenu(self, event): selected = self.TabsOpened.GetSelection() if selected != -1: + self.TabsOpened.GetPage(selected).RefreshView() self.TabsOpened.GetPage(selected).Refresh() event.Skip() - + + def OnUndoMenu(self, event): + self.Controler.LoadPrevious() + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).RefreshView() + self.TabsOpened.GetPage(selected).Refresh() + self.RefreshTitle() + self.RefreshEditMenu() + event.Skip() + + def OnRedoMenu(self, event): + self.Controler.LoadNext() + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).RefreshView() + self.TabsOpened.GetPage(selected).Refresh() + self.RefreshTitle() + self.RefreshEditMenu() + event.Skip() + def OnCutMenu(self, event): selected = self.TabsOpened.GetSelection() if selected != -1: @@ -1244,9 +1306,12 @@ selected = dialog.GetStringSelection() if not self.Controler.PouIsUsed(selected): self.Controler.ProjectRemovePou(selected) + deleted = None for i in xrange(self.TabsOpened.GetPageCount()): if self.TabsOpened.GetPageText(i) == selected: - self.TabsOpened.DeletePage(i) + deleted = i + if deleted != None: + self.TabsOpened.DeletePage(i) self.RefreshProjectTree() self.RefreshToolBar() else: @@ -2173,6 +2238,8 @@ self.Viewer = wx.Panel(id=wxID_POUEDITORPANELVIEWER, name='ConfigPanel', parent=self, pos=wx.Point(0, 0), size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL) + self.Viewer.ResetBuffer = lambda: None + self.Viewer.RefreshView = lambda: None elif element_type == "resource": self.Viewer = ResourceEditor(self, self.Parent, self.Controler) elif element_type == "FBD": @@ -2327,6 +2394,9 @@ if language not in ["IL", "ST"]: self.Viewer.SetMode(mode) + def ResetBuffer(self): + self.Viewer.ResetBuffer() + def RefreshView(self): self.PouNames = self.Controler.GetProjectPouNames() @@ -2360,9 +2430,8 @@ self.ReturnType.SetStringSelection(returnType) self.RefreshValues() self.RefreshButtons() - if self.ElementType != "config": - self.Viewer.RefreshView() - + self.Viewer.RefreshView() + def OnClassFilter(self, event): self.Filter = self.FilterChoiceTransfer[self.ClassFilter.GetStringSelection()] self.RefreshTypeList() @@ -2381,15 +2450,16 @@ self.ClassList = [self.Filter] def RefreshButtons(self): - table_length = len(self.Table.data) - row_class = None - if table_length and self.PouIsUsed: - row = self.VariablesGrid.GetGridCursorRow() - row_class = self.Table.GetValueByName(row, "Class") - self.AddButton.Enable(not self.PouIsUsed or self.Filter not in ["Interface", "Input", "Output", "InOut"]) - self.DeleteButton.Enable(table_length > 0 and row_class not in ["Input", "Output", "InOut"]) - self.UpButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"]) - self.DownButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"]) + if self.Table: + table_length = len(self.Table.data) + row_class = None + if table_length and self.PouIsUsed: + row = self.VariablesGrid.GetGridCursorRow() + row_class = self.Table.GetValueByName(row, "Class") + self.AddButton.Enable(not self.PouIsUsed or self.Filter not in ["Interface", "Input", "Output", "InOut"]) + self.DeleteButton.Enable(table_length > 0 and row_class not in ["Input", "Output", "InOut"]) + self.UpButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"]) + self.DownButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"]) def OnAddButton(self, event): new_row = self.DefaultValue.copy() diff -r e24d2f917c7e -r 7187e1c00975 RessourceEditor.py --- a/RessourceEditor.py Wed Jul 25 10:06:29 2007 +0200 +++ b/RessourceEditor.py Thu Jul 26 17:23:21 2007 +0200 @@ -419,6 +419,16 @@ def RefreshModel(self): self.Controler.SetCurrentResourceEditingInfos(self.TasksTable.GetData(), self.InstancesTable.GetData()) + self.RefreshBuffer() + + def ResetBuffer(self): + pass + + # Buffer the last model state + def RefreshBuffer(self): + self.Controler.BufferProject() + self.Parent.RefreshTitle() + self.Parent.RefreshEditMenu() def RefreshView(self): tasks, instances = self.Controler.GetCurrentResourceEditingInfos() @@ -433,7 +443,6 @@ def OnAddTaskButton(self, event): self.TasksTable.AppendRow(self.TasksDefaultValue.copy()) self.RefreshModel() - self.RefreshView() event.Skip() def OnDeleteTaskButton(self, event): diff -r e24d2f917c7e -r 7187e1c00975 SFCViewer.py --- a/SFCViewer.py Wed Jul 25 10:06:29 2007 +0200 +++ b/SFCViewer.py Thu Jul 26 17:23:21 2007 +0200 @@ -313,6 +313,9 @@ if keycode == WXK_DELETE and self.SelectedElement: self.SelectedElement.Delete() self.SelectedElement = None + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_LEFT: if event.ControlDown() and event.ShiftDown(): self.Scroll(0, ypos) @@ -320,6 +323,9 @@ self.Scroll(max(0, xpos - 1), ypos) elif self.SelectedElement: self.SelectedElement.Move(-scaling[0], 0) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_RIGHT: if event.ControlDown() and event.ShiftDown(): self.Scroll(xmax, ypos) @@ -327,6 +333,9 @@ self.Scroll(min(xpos + 1, xmax), ypos) elif self.SelectedElement: self.SelectedElement.Move(scaling[0], 0) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_UP: if event.ControlDown() and event.ShiftDown(): self.Scroll(xpos, 0) @@ -334,6 +343,9 @@ self.Scroll(xpos, max(0, ypos - 1)) elif self.SelectedElement: self.SelectedElement.Move(0, -scaling[1]) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_DOWN: if event.ControlDown() and event.ShiftDown(): self.Scroll(xpos, ymax) @@ -341,7 +353,9 @@ self.Scroll(xpos, min(ypos + 1, ymax)) elif self.SelectedElement: self.SelectedElement.Move(0, scaling[1]) - self.Refresh() + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() #------------------------------------------------------------------------------- # Adding element functions @@ -364,6 +378,8 @@ self.Controler.AddCurrentElementEditingStep(id) self.RefreshStepModel(step) self.Parent.RefreshProjectTree() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -417,6 +433,8 @@ self.SelectedElement = step self.SelectedElement.SetSelected(True) self.Parent.RefreshProjectTree() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -444,6 +462,8 @@ self.AddBlock(actionblock) self.Controler.AddCurrentElementEditingActionBlock(id) self.RefreshActionBlockModel(actionblock) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -638,6 +658,8 @@ transition_connectors = transition.GetConnectors() previous = transition_connectors["output"] self.CreateStep("Step", previous) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -654,6 +676,8 @@ else: previous = divergence_connectors["outputs"][-1] step = self.CreateStep("Step", previous) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() def AddJump(self): @@ -681,23 +705,10 @@ transition.RefreshOutputPosition() wire.SetPoints([wxPoint(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wxPoint(pos.x, pos.y)]) self.RefreshJumpModel(jump) + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() - - def AddComment(self, bbox): - dialog = wxTextEntryDialog(self.Parent, "Add a new comment", "Please enter comment text", "", wxOK|wxCANCEL|wxTE_MULTILINE) - if dialog.ShowModal() == wxID_OK: - value = dialog.GetValue() - id = self.GetNewId() - comment = Comment(self, value, id) - comment.SetPosition(bbox.x, bbox.y) - min_width, min_height = comment.GetMinSize() - comment.SetSize(max(min_width,bbox.width),max(min_height,bbox.height)) - self.AddComment(comment) - self.Controler.AddCurrentElementEditingComment(id) - self.RefreshCommentModel(comment) - self.Refresh() - dialog.Destroy() def EditStepContent(self, step): dialog = StepNameDialog(self.Parent, "Edit step name", "Please enter step name", step.GetName(), wxOK|wxCANCEL) @@ -711,6 +722,8 @@ size = step.GetSize() step.UpdateSize(max(min_size[0], size[0]), max(min_size[1], size[1])) step.RefreshModel() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -723,6 +736,8 @@ transition.SetType(values["type"]) transition.SetCondition(values["value"]) transition.RefreshModel() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -737,6 +752,8 @@ value = dialog.GetStringSelection() jump.SetTarget(value) jump.RefreshModel() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -750,6 +767,8 @@ actions = dialog.GetValues() actionblock.SetActions(actions) actionblock.RefreshModel() + self.RefreshBuffer() + self.RefreshScrollBars() self.Refresh() dialog.Destroy() diff -r e24d2f917c7e -r 7187e1c00975 TextViewer.py --- a/TextViewer.py Wed Jul 25 10:06:29 2007 +0200 +++ b/TextViewer.py Thu Jul 26 17:23:21 2007 +0200 @@ -25,6 +25,7 @@ from wxPython.wx import * from wxPython.stc import * import wx +from types import * import re @@ -68,6 +69,27 @@ IDENTIFIER_MODEL = re.compile(re_texts["identifier"]) LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts) +def GetCursorPos(old, new): + old_length = len(old) + new_length = len(new) + common_length = min(old_length, new_length) + i = 0 + for i in xrange(common_length): + if old[i] != new[i]: + break + if old_length < new_length: + if common_length > 0 and old[i] != new[i]: + return i + new_length - old_length + else: + return i + new_length - old_length + 1 + elif old_length > new_length or i < min(old_length, new_length) - 1: + if common_length > 0 and old[i] != new[i]: + return i + else: + return i + 1 + else: + return None + class TextViewer(wxStyledTextCtrl): def __init__(self, parent, window, controler): @@ -112,14 +134,40 @@ self.Functions = [] self.Jumps = [] self.TextChanged = False + self.DisableEvents = True self.TextSyntax = "ST" - + self.CurrentAction = None + + self.Parent = window self.Controler = controler + self.SetModEventMask(wxSTC_MOD_BEFOREINSERT|wxSTC_MOD_BEFOREDELETE) + EVT_KEY_DOWN(self, self.OnKeyDown) EVT_STC_STYLENEEDED(self, wxID_TEXTVIEWER, self.OnStyleNeeded) EVT_STC_DO_DROP(self, wxID_TEXTVIEWER, self.OnDoDrop) EVT_KILL_FOCUS(self, self.OnKillFocus) + EVT_STC_MODIFIED(self, wxID_TEXTVIEWER, self.OnModification) + + def OnModification(self, event): + if not self.DisableEvents: + mod_type = event.GetModificationType() + if not (mod_type&wxSTC_PERFORMED_UNDO or mod_type&wxSTC_PERFORMED_REDO): + if mod_type&wxSTC_MOD_BEFOREINSERT: + if self.CurrentAction == None: + self.StartBuffering() + elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: + self.Controler.EndBuffering() + self.StartBuffering() + self.CurrentAction = ("Add", event.GetPosition()) + elif mod_type&wxSTC_MOD_BEFOREDELETE: + if self.CurrentAction == None: + self.StartBuffering() + elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: + self.Controler.EndBuffering() + self.StartBuffering() + self.CurrentAction = ("Delete", event.GetPosition()) + event.Skip() def OnDoDrop(self, event): try: @@ -156,10 +204,39 @@ self.Jumps = [jump.upper() for jump in LABEL_MODEL.findall(self.GetText())] self.Colourise(0, -1) + # Buffer the last model state + def RefreshBuffer(self): + self.Controler.BufferProject() + self.Parent.RefreshTitle() + self.Parent.RefreshEditMenu() + + def StartBuffering(self): + self.Controler.StartBuffering() + self.Parent.RefreshTitle() + self.Parent.RefreshEditMenu() + + def ResetBuffer(self): + if self.CurrentAction != None: + self.Controler.EndBuffering() + self.CurrentAction = None + def RefreshView(self): - self.SetText(self.Controler.GetCurrentElementEditingText()) + self.ResetBuffer() + self.DisableEvents = True + old_cursor_pos = self.GetCurrentPos() + old_text = self.GetText() + new_text = self.Controler.GetCurrentElementEditingText() + self.SetText(new_text) + new_cursor_pos = GetCursorPos(old_text, new_text) + if new_cursor_pos != None: + self.SetSelection(new_cursor_pos, new_cursor_pos) + self.EnsureCaretVisible() + else: + self.SetSelection(old_cursor_pos, old_cursor_pos) self.RefreshJumpList() - + self.EmptyUndoBuffer() + self.DisableEvents = False + def OnStyleNeeded(self, event): self.TextChanged = True line = self.LineFromPosition(self.GetEndStyled()) @@ -273,13 +350,17 @@ event.Skip() def Cut(self): + self.ResetBuffer() self.CmdKeyExecute(wxSTC_CMD_CUT) - + self.RefreshBuffer() + def Copy(self): self.CmdKeyExecute(wxSTC_CMD_COPY) def Paste(self): + self.ResetBuffer() self.CmdKeyExecute(wxSTC_CMD_PASTE) + self.RefreshBuffer() def RefreshModel(self): if self.TextChanged: diff -r e24d2f917c7e -r 7187e1c00975 Viewer.py --- a/Viewer.py Wed Jul 25 10:06:29 2007 +0200 +++ b/Viewer.py Thu Jul 26 17:23:21 2007 +0200 @@ -266,15 +266,24 @@ self.SelectedElement.SetSelected(False) self.SelectedElement = None self.Refresh() - + # Return current drawing mode def GetDrawingMode(self): return self.Parent.GetDrawingMode() + + # Buffer the last model state + def RefreshBuffer(self): + self.Controler.BufferProject() + self.Parent.RefreshTitle() + self.Parent.RefreshEditMenu() #------------------------------------------------------------------------------- # Refresh functions #------------------------------------------------------------------------------- + def ResetBuffer(self): + pass + # Refresh Viewer elements def RefreshView(self): self.current_id = 0 @@ -849,6 +858,7 @@ self.SelectedElement.GeneratePoints() self.SelectedElement.RefreshModel() self.SelectedElement.SetSelected(True) + self.RefreshBuffer() else: self.SelectedElement.Delete() self.SelectedElement = None @@ -927,6 +937,9 @@ self.SelectedElement.Clean() self.SelectedElement.Delete() self.SelectedElement = None + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_LEFT: if event.ControlDown() and event.ShiftDown(): self.Scroll(0, ypos) @@ -934,6 +947,9 @@ self.Scroll(max(0, xpos - 1), ypos) elif self.SelectedElement: self.SelectedElement.Move(-scaling[0], 0) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_RIGHT: if event.ControlDown() and event.ShiftDown(): self.Scroll(xmax, ypos) @@ -941,6 +957,9 @@ self.Scroll(min(xpos + 1, xmax), ypos) elif self.SelectedElement: self.SelectedElement.Move(scaling[0], 0) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_UP: if event.ControlDown() and event.ShiftDown(): self.Scroll(xpos, 0) @@ -948,6 +967,9 @@ self.Scroll(xpos, max(0, ypos - 1)) elif self.SelectedElement: self.SelectedElement.Move(0, -scaling[1]) + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() elif keycode == WXK_DOWN: if event.ControlDown() and event.ShiftDown(): self.Scroll(xpos, ymax) @@ -955,7 +977,9 @@ self.Scroll(xpos, min(ypos + 1, ymax)) elif self.SelectedElement: self.SelectedElement.Move(0, scaling[1]) - self.Refresh() + self.RefreshBuffer() + self.RefreshScrollBars() + self.Refresh() #------------------------------------------------------------------------------- # Model adding functions @@ -977,6 +1001,7 @@ self.AddBlock(block) self.Controler.AddCurrentElementEditingBlock(id) self.RefreshBlockModel(block) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1002,6 +1027,7 @@ self.AddBlock(variable) self.Controler.AddCurrentElementEditingVariable(id, values["type"]) self.RefreshVariableModel(variable) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1018,6 +1044,7 @@ self.AddBlock(connection) self.Controler.AddCurrentElementEditingConnection(id, values["type"]) self.RefreshConnectionModel(connection) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1034,6 +1061,7 @@ self.AddComment(comment) self.Controler.AddCurrentElementEditingComment(id) self.RefreshCommentModel(comment) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1058,6 +1086,7 @@ self.AddBlock(contact) self.Controler.AddCurrentElementEditingContact(id) self.RefreshContactModel(contact) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1085,6 +1114,7 @@ self.AddBlock(coil) self.Controler.AddCurrentElementEditingCoil(id) self.RefreshCoilModel(contact) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1101,6 +1131,7 @@ self.AddBlock(powerrail) self.Controler.AddCurrentElementEditingPowerRail(id, values["type"]) self.RefreshPowerRailModel(powerrail) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1118,6 +1149,7 @@ self.AddBlock(transition) self.Controler.AddCurrentElementEditingTransition(id) self.RefreshTransitionModel(transition) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1135,6 +1167,7 @@ self.AddBlock(divergence) self.Controler.AddCurrentElementEditingDivergence(id, values["type"]) self.RefreshDivergenceModel(divergence) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1158,6 +1191,7 @@ block.SetSize(values["width"], values["height"]) block.SetType(values["type"], values["extension"]) self.RefreshBlockModel(block) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1187,6 +1221,7 @@ self.Controler.RemoveCurrentElementEditingInstance(id) self.Controler.AddCurrentElementEditingVariable(id, values["type"]) self.RefreshVariableModel(variable) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1207,6 +1242,7 @@ self.Controler.RemoveCurrentElementEditingInstance(id) self.Controler.AddCurrentElementEditingConnection(id, values["type"]) self.RefreshConnectionModel(connection) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1229,6 +1265,7 @@ contact.SetType(values["type"]) contact.SetSize(values["width"], values["height"]) self.RefreshContactModel(contact) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1254,6 +1291,7 @@ coil.SetType(values["type"]) coil.SetSize(values["width"], values["height"]) self.RefreshContactModel(coil) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() @@ -1271,6 +1309,7 @@ self.Controler.RemoveCurrentElementEditingInstance(id) self.Controler.AddCurrentElementEditingPowerRail(id, values["type"]) self.RefreshPowerRailModel(powerrail) + self.RefreshBuffer() self.RefreshScrollBars() self.Refresh() dialog.Destroy() diff -r e24d2f917c7e -r 7187e1c00975 examples/example.xml --- a/examples/example.xml Wed Jul 25 10:06:29 2007 +0200 +++ b/examples/example.xml Thu Jul 26 17:23:21 2007 +0200 @@ -862,6 +862,7 @@ Control_State := TRUE; ELSE Control_State := FALSE; + PumpSpeed := 10.0; END_IF; @@ -873,11 +874,11 @@ - + - + @@ -885,19 +886,18 @@ - + - + - diff -r e24d2f917c7e -r 7187e1c00975 graphics/GraphicCommons.py --- a/graphics/GraphicCommons.py Wed Jul 25 10:06:29 2007 +0200 +++ b/graphics/GraphicCommons.py Thu Jul 26 17:23:21 2007 +0200 @@ -401,6 +401,7 @@ movey = pos.y - self.oldPos.y self.ProcessDragging(movex, movey) self.RefreshModel() + self.Parent.RefreshBuffer() self.SetSelected(True) self.oldPos = None @@ -1601,7 +1602,6 @@ if self.EndConnected: self.UnConnectEndPoint() self.MoveEndPoint(new_pos) - self.RefreshModel() # A segment has been handled, move a segment elif handle_type == HANDLE_SEGMENT: self.MoveSegment(handle[0], movex, movey)