]> rtime.felk.cvut.cz Git - CanFestival-3.git/blob - objdictgen/objdictedit.py
LSS patch from Edward. Now, SetNodeId is not called from nmtslave anymore in case...
[CanFestival-3.git] / objdictgen / objdictedit.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 #This file is part of CanFestival, a library implementing CanOpen Stack. 
5 #
6 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
7 #
8 #See COPYING file for copyrights details.
9 #
10 #This library is free software; you can redistribute it and/or
11 #modify it under the terms of the GNU Lesser General Public
12 #License as published by the Free Software Foundation; either
13 #version 2.1 of the License, or (at your option) any later version.
14 #
15 #This library is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 #Lesser General Public License for more details.
19 #
20 #You should have received a copy of the GNU Lesser General Public
21 #License along with this library; if not, write to the Free Software
22 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 import wx
25
26 from types import *
27 import os, re, platform, sys, time, traceback, getopt
28
29 __version__ = "$Revision: 1.41 $"
30
31 from node import OD_Subindex, OD_MultipleSubindexes, OD_IdenticalSubindexes, OD_IdenticalIndexes
32
33 from nodemanager import *
34 from subindextable import *
35 from commondialogs import *
36 from doc_index.DS301_index import *
37
38 try:
39     import wx.html
40
41     EVT_HTML_URL_CLICK = wx.NewId()
42
43     class HtmlWindowUrlClick(wx.PyEvent):
44         def __init__(self, linkinfo):
45             wx.PyEvent.__init__(self)
46             self.SetEventType(EVT_HTML_URL_CLICK)
47             self.linkinfo = (linkinfo.GetHref(), linkinfo.GetTarget())
48             
49     class UrlClickHtmlWindow(wx.html.HtmlWindow):
50         """ HTML window that generates and OnLinkClicked event.
51
52         Use this to avoid having to override HTMLWindow
53         """
54         def OnLinkClicked(self, linkinfo):
55             wx.PostEvent(self, HtmlWindowUrlClick(linkinfo))
56         
57         def Bind(self, event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY):
58             if event == HtmlWindowUrlClick:
59                 self.Connect(-1, -1, EVT_HTML_URL_CLICK, handler)
60             else:
61                 wx.html.HtmlWindow.Bind(event, handler, source=source, id=id, id2=id2)
62     
63 #-------------------------------------------------------------------------------
64 #                                Html Frame
65 #-------------------------------------------------------------------------------
66
67     [ID_HTMLFRAME, ID_HTMLFRAMEHTMLCONTENT] = [wx.NewId() for _init_ctrls in range(2)]
68
69     class HtmlFrame(wx.Frame):
70         def _init_ctrls(self, prnt):
71             wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame',
72                   parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616),
73                   style=wx.DEFAULT_FRAME_STYLE, title='')
74             self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
75             
76             self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT,
77                   name='HtmlContent', parent=self, pos=wx.Point(0, 0),
78                   size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO|wx.html.HW_NO_SELECTION)
79             self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick)
80
81         def __init__(self, parent, opened):
82             self._init_ctrls(parent)
83             self.HtmlFrameOpened = opened
84         
85         def SetHtmlCode(self, htmlcode):
86             self.HtmlContent.SetPage(htmlcode)
87             
88         def SetHtmlPage(self, htmlpage):
89             self.HtmlContent.LoadPage(htmlpage)
90             
91         def OnCloseFrame(self, event):
92             self.HtmlFrameOpened.remove(self.GetTitle())
93             event.Skip()
94         
95         def OnLinkClick(self, event):
96             url = event.linkinfo[0]
97             try:
98                 import webbrowser
99             except ImportError:
100                 wx.MessageBox('Please point your browser at: %s' % url)
101             else:
102                 webbrowser.open(url)
103
104     Html_Window = True
105 except:
106     Html_Window = False
107
108 ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]
109
110 [ID_OBJDICTEDIT, ID_OBJDICTEDITFILEOPENED, 
111  ID_OBJDICTEDITHELPBAR,
112 ] = [wx.NewId() for _init_ctrls in range(3)]
113
114 [ID_OBJDICTEDITFILEMENUIMPORTEDS, ID_OBJDICTEDITFILEMENUEXPORTEDS, 
115  ID_OBJDICTEDITFILEMENUEXPORTC,
116 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(3)]
117
118 [ID_OBJDICTEDITEDITMENUNODEINFOS, ID_OBJDICTEDITEDITMENUDS301PROFILE, 
119  ID_OBJDICTEDITEDITMENUDS302PROFILE, ID_OBJDICTEDITEDITMENUOTHERPROFILE, 
120 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
121
122 [ID_OBJDICTEDITADDMENUSDOSERVER, ID_OBJDICTEDITADDMENUSDOCLIENT, 
123  ID_OBJDICTEDITADDMENUPDOTRANSMIT, ID_OBJDICTEDITADDMENUPDORECEIVE, 
124  ID_OBJDICTEDITADDMENUMAPVARIABLE, ID_OBJDICTEDITADDMENUUSERTYPE, 
125 ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)]
126
127 class objdictedit(wx.Frame):
128     def _init_coll_MenuBar_Menus(self, parent):
129         parent.Append(menu=self.FileMenu, title='File')
130         parent.Append(menu=self.EditMenu, title='Edit')
131         parent.Append(menu=self.AddMenu, title='Add')
132         parent.Append(menu=self.HelpMenu, title='Help')
133
134     def _init_coll_FileMenu_Items(self, parent):
135         parent.Append(help='', id=wx.ID_NEW,
136               kind=wx.ITEM_NORMAL, text='New\tCTRL+N')
137         parent.Append(help='', id=wx.ID_OPEN,
138               kind=wx.ITEM_NORMAL, text='Open\tCTRL+O')
139         parent.Append(help='', id=wx.ID_CLOSE,
140               kind=wx.ITEM_NORMAL, text='Close\tCTRL+W')
141         parent.AppendSeparator()
142         parent.Append(help='', id=wx.ID_SAVE,
143               kind=wx.ITEM_NORMAL, text='Save\tCTRL+S')
144         parent.Append(help='', id=wx.ID_SAVEAS,
145               kind=wx.ITEM_NORMAL, text='Save As...\tALT+S')
146         parent.AppendSeparator()
147         parent.Append(help='', id=ID_OBJDICTEDITFILEMENUIMPORTEDS,
148               kind=wx.ITEM_NORMAL, text='Import EDS file')
149         parent.Append(help='', id=ID_OBJDICTEDITFILEMENUEXPORTEDS,
150               kind=wx.ITEM_NORMAL, text='Export to EDS file')
151         parent.Append(help='', id=ID_OBJDICTEDITFILEMENUEXPORTC,
152               kind=wx.ITEM_NORMAL, text='Build Dictionary\tCTRL+B')
153         parent.AppendSeparator()
154         parent.Append(help='', id=wx.ID_EXIT,
155               kind=wx.ITEM_NORMAL, text='Exit')
156         self.Bind(wx.EVT_MENU, self.OnNewMenu, id=wx.ID_NEW)
157         self.Bind(wx.EVT_MENU, self.OnOpenMenu, id=wx.ID_OPEN)
158         self.Bind(wx.EVT_MENU, self.OnCloseMenu, id=wx.ID_CLOSE)
159         self.Bind(wx.EVT_MENU, self.OnSaveMenu, id=wx.ID_SAVE)
160         self.Bind(wx.EVT_MENU, self.OnSaveAsMenu, id=wx.ID_SAVEAS)
161         self.Bind(wx.EVT_MENU, self.OnImportEDSMenu,
162               id=ID_OBJDICTEDITFILEMENUIMPORTEDS)
163         self.Bind(wx.EVT_MENU, self.OnExportEDSMenu,
164               id=ID_OBJDICTEDITFILEMENUEXPORTEDS)
165         self.Bind(wx.EVT_MENU, self.OnExportCMenu,
166               id=ID_OBJDICTEDITFILEMENUEXPORTC)
167         self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
168
169     def _init_coll_EditMenu_Items(self, parent):
170         parent.Append(help='', id=wx.ID_REFRESH,
171               kind=wx.ITEM_NORMAL, text='Refresh\tCTRL+R')
172         parent.AppendSeparator()
173         parent.Append(help='', id=wx.ID_UNDO,
174               kind=wx.ITEM_NORMAL, text='Undo\tCTRL+Z')
175         parent.Append(help='', id=wx.ID_REDO,
176               kind=wx.ITEM_NORMAL, text='Redo\tCTRL+Y')
177         parent.AppendSeparator()
178         parent.Append(help='', id=ID_OBJDICTEDITEDITMENUNODEINFOS,
179               kind=wx.ITEM_NORMAL, text='Node infos')
180         parent.Append(help='', id=ID_OBJDICTEDITEDITMENUDS301PROFILE,
181               kind=wx.ITEM_NORMAL, text='DS-301 Profile')
182         parent.Append(help='', id=ID_OBJDICTEDITEDITMENUDS302PROFILE,
183               kind=wx.ITEM_NORMAL, text='DS-302 Profile')
184         parent.Append(help='', id=ID_OBJDICTEDITEDITMENUOTHERPROFILE,
185               kind=wx.ITEM_NORMAL, text='Other Profile')
186         self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
187         self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO)
188         self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO)
189         self.Bind(wx.EVT_MENU, self.OnNodeInfosMenu,
190               id=ID_OBJDICTEDITEDITMENUNODEINFOS)
191         self.Bind(wx.EVT_MENU, self.OnCommunicationMenu,
192               id=ID_OBJDICTEDITEDITMENUDS301PROFILE)
193         self.Bind(wx.EVT_MENU, self.OnOtherCommunicationMenu,
194               id=ID_OBJDICTEDITEDITMENUDS302PROFILE)
195         self.Bind(wx.EVT_MENU, self.OnEditProfileMenu,
196               id=ID_OBJDICTEDITEDITMENUOTHERPROFILE)
197
198     def _init_coll_AddMenu_Items(self, parent):
199         parent.Append(help='', id=ID_OBJDICTEDITADDMENUSDOSERVER,
200               kind=wx.ITEM_NORMAL, text='SDO Server')
201         parent.Append(help='', id=ID_OBJDICTEDITADDMENUSDOCLIENT,
202               kind=wx.ITEM_NORMAL, text='SDO Client')
203         parent.Append(help='', id=ID_OBJDICTEDITADDMENUPDOTRANSMIT,
204               kind=wx.ITEM_NORMAL, text='PDO Transmit')
205         parent.Append(help='', id=ID_OBJDICTEDITADDMENUPDORECEIVE,
206               kind=wx.ITEM_NORMAL, text='PDO Receive')
207         parent.Append(help='', id=ID_OBJDICTEDITADDMENUMAPVARIABLE,
208               kind=wx.ITEM_NORMAL, text='Map Variable')
209         parent.Append(help='', id=ID_OBJDICTEDITADDMENUUSERTYPE,
210               kind=wx.ITEM_NORMAL, text='User Type')
211         self.Bind(wx.EVT_MENU, self.OnAddSDOServerMenu,
212               id=ID_OBJDICTEDITADDMENUSDOSERVER)
213         self.Bind(wx.EVT_MENU, self.OnAddSDOClientMenu,
214               id=ID_OBJDICTEDITADDMENUSDOCLIENT)
215         self.Bind(wx.EVT_MENU, self.OnAddPDOTransmitMenu,
216               id=ID_OBJDICTEDITADDMENUPDOTRANSMIT)
217         self.Bind(wx.EVT_MENU, self.OnAddPDOReceiveMenu,
218               id=ID_OBJDICTEDITADDMENUPDORECEIVE)
219         self.Bind(wx.EVT_MENU, self.OnAddMapVariableMenu,
220               id=ID_OBJDICTEDITADDMENUMAPVARIABLE)
221         self.Bind(wx.EVT_MENU, self.OnAddUserTypeMenu,
222               id=ID_OBJDICTEDITADDMENUUSERTYPE)
223
224     def _init_coll_HelpMenu_Items(self, parent):
225         parent.Append(help='', id=wx.ID_HELP,
226               kind=wx.ITEM_NORMAL, text='DS-301 Standard\tF1')
227         self.Bind(wx.EVT_MENU, self.OnHelpDS301Menu, id=wx.ID_HELP)
228         parent.Append(help='', id=wx.ID_HELP_CONTEXT,
229               kind=wx.ITEM_NORMAL, text='CAN Festival Docs\tF2')
230         self.Bind(wx.EVT_MENU, self.OnHelpCANFestivalMenu, id=wx.ID_HELP_CONTEXT)
231         if Html_Window:
232             parent.Append(help='', id=wx.ID_ABOUT,
233                   kind=wx.ITEM_NORMAL, text='About')
234             self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
235
236     def _init_coll_HelpBar_Fields(self, parent):
237         parent.SetFieldsCount(3)
238
239         parent.SetStatusText(number=0, text='')
240         parent.SetStatusText(number=1, text='')
241         parent.SetStatusText(number=2, text='')
242
243         parent.SetStatusWidths([100, 110, -1])
244
245     def _init_utils(self):
246         self.MenuBar = wx.MenuBar()
247         self.MenuBar.SetEvtHandlerEnabled(True)
248
249         self.FileMenu = wx.Menu(title='')
250         self.EditMenu = wx.Menu(title='')
251         self.AddMenu = wx.Menu(title='')
252         self.HelpMenu = wx.Menu(title='')
253
254         self._init_coll_MenuBar_Menus(self.MenuBar)
255         self._init_coll_FileMenu_Items(self.FileMenu)
256         self._init_coll_EditMenu_Items(self.EditMenu)
257         self._init_coll_AddMenu_Items(self.AddMenu)
258         self._init_coll_HelpMenu_Items(self.HelpMenu)
259
260     def _init_ctrls(self, prnt):
261         wx.Frame.__init__(self, id=ID_OBJDICTEDIT, name='objdictedit',
262               parent=prnt, pos=wx.Point(149, 178), size=wx.Size(1000, 700),
263               style=wx.DEFAULT_FRAME_STYLE, title='Objdictedit')
264         self._init_utils()
265         self.SetClientSize(wx.Size(1000, 700))
266         self.SetMenuBar(self.MenuBar)
267         self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
268
269         self.FileOpened = wx.Notebook(id=ID_OBJDICTEDITFILEOPENED,
270               name='FileOpened', parent=self, pos=wx.Point(0, 0),
271               size=wx.Size(0, 0), style=0)
272         self.FileOpened.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED,
273               self.OnFileSelectedChanged, id=ID_OBJDICTEDITFILEOPENED)
274
275         self.HelpBar = wx.StatusBar(id=ID_OBJDICTEDITHELPBAR, name='HelpBar',
276               parent=self, style=wx.ST_SIZEGRIP)
277         self._init_coll_HelpBar_Fields(self.HelpBar)
278         self.SetStatusBar(self.HelpBar)
279
280     def __init__(self, parent, filesOpen = []):
281         self._init_ctrls(parent)
282         self.HtmlFrameOpened = []
283         self.ModeSolo = True
284         
285         icon = wx.Icon(os.path.join(ScriptDirectory,"networkedit.ico"),wx.BITMAP_TYPE_ICO)
286         self.SetIcon(icon)
287         
288         self.Manager = NodeManager()
289         for filepath in filesOpen:
290             result = self.Manager.OpenFileInCurrent(filepath)
291             if type(result) == IntType:
292                 new_editingpanel = EditingPanel(self.FileOpened, self, self.Manager)
293                 new_editingpanel.SetIndex(result)
294                 self.FileOpened.AddPage(new_editingpanel, "")
295             window = self.FileOpened.GetPage(0)
296             if window:
297                 self.Manager.ChangeCurrentNode(window.GetIndex())
298                 self.FileOpened.SetSelection(0)
299         if self.Manager.CurrentDS302Defined():
300             self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, True)
301         else:
302             self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, False)
303         self.RefreshEditMenu()
304         self.RefreshBufferState()
305         self.RefreshProfileMenu()
306         self.RefreshTitle()
307         self.RefreshMainMenu()
308
309     def OnAddSDOServerMenu(self, event):
310         self.Manager.AddSDOServerToCurrent()
311         self.RefreshBufferState()
312         self.RefreshCurrentIndexList()
313         event.Skip()
314     
315     def OnAddSDOClientMenu(self, event):
316         self.Manager.AddSDOClientToCurrent()
317         self.RefreshBufferState()
318         self.RefreshCurrentIndexList()
319         event.Skip()
320
321     def OnAddPDOTransmitMenu(self, event):
322         self.Manager.AddPDOTransmitToCurrent()
323         self.RefreshBufferState()
324         self.RefreshCurrentIndexList()
325         event.Skip()
326
327     def OnAddPDOReceiveMenu(self, event):
328         self.Manager.AddPDOReceiveToCurrent()
329         self.RefreshBufferState()
330         self.RefreshCurrentIndexList()
331         event.Skip()
332
333     def OnAddMapVariableMenu(self, event):
334         self.AddMapVariable()
335         event.Skip()
336
337     def OnAddUserTypeMenu(self, event):
338         self.AddUserType()
339         event.Skip()
340
341     def OnFileSelectedChanged(self, event):
342         selected = event.GetSelection()
343         # At init selected = -1
344         if selected >= 0:
345             window = self.FileOpened.GetPage(selected)
346             if window:
347                 self.Manager.ChangeCurrentNode(window.GetIndex())
348                 wx.CallAfter(self.RefreshBufferState)
349                 self.RefreshStatusBar()
350                 self.RefreshProfileMenu()
351         event.Skip()
352
353     def OnHelpDS301Menu(self, event):
354         find_index = False
355         selected = self.FileOpened.GetSelection()
356         if selected >= 0:
357             window = self.FileOpened.GetPage(selected)
358             result = window.GetSelection()
359             if result:
360                 find_index = True
361                 index, subIndex = result
362                 result = OpenPDFDocIndex(index, ScriptDirectory)
363                 if type(result) == StringType:
364                     message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
365                     message.ShowModal()
366                     message.Destroy()
367         if not find_index:
368             result = OpenPDFDocIndex(None, ScriptDirectory)
369             if type(result) == StringType:
370                 message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
371                 message.ShowModal()
372                 message.Destroy()
373         event.Skip()
374         
375     def OnHelpCANFestivalMenu(self, event):
376         #self.OpenHtmlFrame("CAN Festival Reference", os.path.join(ScriptDirectory, "doc/canfestival.html"), wx.Size(1000, 600))
377         if wx.Platform == '__WXMSW__':
378             readerpath = get_acroversion()
379             readerexepath = os.path.join(readerpath,"AcroRd32.exe")
380             if(os.path.isfile(readerexepath)):
381                 os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"'%os.path.join(ScriptDirectory, "doc","manual_en.pdf"))
382         else:
383             os.system("xpdf -remote CANFESTIVAL %s %d &"%(os.path.join(ScriptDirectory, "doc/manual_en.pdf"),16))
384         event.Skip()
385
386     def OnAboutMenu(self, event):
387         self.OpenHtmlFrame("About CAN Festival", os.path.join(ScriptDirectory, "doc/about.html"), wx.Size(500, 450))
388         event.Skip()
389
390     def OpenHtmlFrame(self, title, file, size):
391         if title not in self.HtmlFrameOpened:
392             self.HtmlFrameOpened.append(title)
393             window = HtmlFrame(self, self.HtmlFrameOpened)
394             window.SetTitle(title)
395             window.SetHtmlPage(file)
396             window.SetClientSize(size)
397             window.Show()
398
399     def OnQuitMenu(self, event):
400         self.Close()
401         event.Skip()
402     
403     def OnCloseFrame(self, event):
404         if self.Manager.OneFileHasChanged():
405             dialog = wx.MessageDialog(self, "There are changes, do you want to save?",  "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
406             answer = dialog.ShowModal()
407             dialog.Destroy()
408             if answer == wx.ID_YES:
409                 self.Manager.ChangeCurrentNode(0)
410                 for i in xrange(self.FileOpened.GetPageCount()):
411                     window = self.FileOpened.GetPage(i)
412                     self.Manager.ChangeCurrentNode(window.GetIndex())
413                     if self.Manager.CurrentIsSaved():
414                         self.Manager.CloseCurrent()
415                     else:
416                         self.Save()
417                         self.Manager.CloseCurrent(True)
418                 event.Skip()
419             elif answer == wx.ID_NO:
420                 for i in xrange(self.FileOpened.GetPageCount()):
421                     self.Manager.CloseCurrent(True)
422                 wx.CallAfter(self.Close)
423                 event.Skip()
424         else:
425             event.Skip()
426
427 #-------------------------------------------------------------------------------
428 #                             Refresh Functions
429 #-------------------------------------------------------------------------------
430
431     def RefreshTitle(self):
432         if self.FileOpened.GetPageCount() > 0:
433             self.SetTitle("Objdictedit - %s"%self.Manager.GetCurrentFilename())
434         else:
435             self.SetTitle("Objdictedit")
436
437     def OnRefreshMenu(self, event):
438         self.RefreshCurrentIndexList()
439         event.Skip()
440
441     def RefreshCurrentIndexList(self):
442         selected = self.FileOpened.GetSelection()
443         window = self.FileOpened.GetPage(selected)
444         window.RefreshIndexList()
445
446     def RefreshStatusBar(self):
447         if self and self.HelpBar:
448             selected = self.FileOpened.GetSelection()
449             if selected >= 0:
450                 window = self.FileOpened.GetPage(selected)
451                 selection = window.GetSelection()
452                 if selection:
453                     index, subIndex = selection
454                     if self.Manager.IsCurrentEntry(index):
455                         self.HelpBar.SetStatusText("Index: 0x%04X"%index, 0)
456                         self.HelpBar.SetStatusText("Subindex: 0x%02X"%subIndex, 1)
457                         entryinfos = self.Manager.GetEntryInfos(index)
458                         name = entryinfos["name"]
459                         category = "Optional"
460                         if entryinfos["need"]:
461                             category = "Mandatory"
462                         struct = "VAR"
463                         number = ""
464                         if entryinfos["struct"] & OD_IdenticalIndexes:
465                             number = " possibly defined %d times"%entryinfos["nbmax"]
466                         if entryinfos["struct"] & OD_IdenticalSubindexes:
467                             struct = "REC"
468                         elif entryinfos["struct"] & OD_MultipleSubindexes:
469                             struct = "ARRAY"
470                         text = "%s: %s entry of struct %s%s."%(name,category,struct,number)
471                         self.HelpBar.SetStatusText(text, 2)
472                     else:
473                         for i in xrange(3):
474                             self.HelpBar.SetStatusText("", i)
475                 else:
476                     for i in xrange(3):
477                         self.HelpBar.SetStatusText("", i)
478
479     def RefreshMainMenu(self):
480         if self.FileMenu:
481             if self.FileOpened.GetPageCount() > 0:
482                 self.MenuBar.EnableTop(1, True)
483                 self.MenuBar.EnableTop(2, True)
484                 self.FileMenu.Enable(wx.ID_CLOSE, True)
485                 self.FileMenu.Enable(wx.ID_SAVE, True)
486                 self.FileMenu.Enable(wx.ID_SAVEAS, True)
487                 self.FileMenu.Enable(ID_OBJDICTEDITFILEMENUEXPORTEDS, True)
488                 self.FileMenu.Enable(ID_OBJDICTEDITFILEMENUEXPORTC, True)
489             else:
490                 self.MenuBar.EnableTop(1, False)
491                 self.MenuBar.EnableTop(2, False)
492                 self.FileMenu.Enable(wx.ID_CLOSE, False)
493                 self.FileMenu.Enable(wx.ID_SAVE, False)
494                 self.FileMenu.Enable(wx.ID_SAVEAS, False)
495                 self.FileMenu.Enable(ID_OBJDICTEDITFILEMENUEXPORTEDS, False)
496                 self.FileMenu.Enable(ID_OBJDICTEDITFILEMENUEXPORTC, False)
497
498     def RefreshEditMenu(self):
499         if self.EditMenu:
500             if self.FileOpened.GetPageCount() > 0:
501                 undo, redo = self.Manager.GetCurrentBufferState()
502                 self.EditMenu.Enable(wx.ID_UNDO, undo)
503                 self.EditMenu.Enable(wx.ID_REDO, redo)
504             else:
505                 self.EditMenu.Enable(wx.ID_UNDO, False)
506                 self.EditMenu.Enable(wx.ID_REDO, False)
507
508     def RefreshProfileMenu(self):
509         if self.EditMenu:
510             profile = self.Manager.GetCurrentProfileName()
511             edititem = self.EditMenu.FindItemById(ID_OBJDICTEDITEDITMENUOTHERPROFILE)
512             if edititem:
513                 length = self.AddMenu.GetMenuItemCount()
514                 for i in xrange(length-6):
515                     additem = self.AddMenu.FindItemByPosition(6)
516                     self.AddMenu.Delete(additem.GetId())
517                 if profile not in ("None", "DS-301"):
518                     edititem.SetText("%s Profile"%profile)
519                     edititem.Enable(True)
520                     self.AddMenu.AppendSeparator()
521                     for text, indexes in self.Manager.GetCurrentSpecificMenu():
522                         new_id = wx.NewId()
523                         self.AddMenu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=text)
524                         self.Bind(wx.EVT_MENU, self.GetProfileCallBack(text), id=new_id)
525                 else:
526                     edititem.SetText("Other Profile")
527                     edititem.Enable(False)
528         
529
530 #-------------------------------------------------------------------------------
531 #                            Buffer Functions
532 #-------------------------------------------------------------------------------
533
534     def RefreshBufferState(self):
535         fileopened = self.Manager.GetAllFilenames()
536         for idx, filename in enumerate(fileopened):
537             self.FileOpened.SetPageText(idx, filename)
538         self.RefreshEditMenu()
539         self.RefreshTitle()
540
541     def OnUndoMenu(self, event):
542         self.Manager.LoadCurrentPrevious()
543         self.RefreshCurrentIndexList()
544         self.RefreshBufferState()
545         event.Skip()
546
547     def OnRedoMenu(self, event):
548         self.Manager.LoadCurrentNext()
549         self.RefreshCurrentIndexList()
550         self.RefreshBufferState()
551         event.Skip()
552
553
554 #-------------------------------------------------------------------------------
555 #                         Load and Save Funtions
556 #-------------------------------------------------------------------------------
557
558     def OnNewMenu(self, event):
559         self.FilePath = ""
560         dialog = CreateNodeDialog(self)
561         if dialog.ShowModal() == wx.ID_OK:
562             name, id, nodetype, description = dialog.GetValues()
563             profile, filepath = dialog.GetProfile()
564             NMT = dialog.GetNMTManagement()
565             options = dialog.GetOptions()
566             result = self.Manager.CreateNewNode(name, id, nodetype, description, profile, filepath, NMT, options)
567             if type(result) == IntType:
568                 new_editingpanel = EditingPanel(self.FileOpened, self, self.Manager)
569                 new_editingpanel.SetIndex(result)
570                 self.FileOpened.AddPage(new_editingpanel, "")
571                 self.FileOpened.SetSelection(self.FileOpened.GetPageCount() - 1)
572                 self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, False)
573                 if "DS302" in options:
574                     self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, True)
575                 self.RefreshBufferState()
576                 self.RefreshProfileMenu()
577                 self.RefreshMainMenu()
578             else:
579                 message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
580                 message.ShowModal()
581                 message.Destroy()
582         dialog.Destroy()
583         event.Skip()
584
585     def OnOpenMenu(self, event):
586         filepath = self.Manager.GetCurrentFilePath()
587         if filepath != "":
588             directory = os.path.dirname(filepath)
589         else:
590             directory = os.getcwd()
591         dialog = wx.FileDialog(self, "Choose a file", directory, "",  "OD files (*.od)|*.od|All files|*.*", wx.OPEN|wx.CHANGE_DIR)
592         if dialog.ShowModal() == wx.ID_OK:
593             filepath = dialog.GetPath()
594             if os.path.isfile(filepath):
595                 result = self.Manager.OpenFileInCurrent(filepath)
596                 if type(result) == IntType:
597                     new_editingpanel = EditingPanel(self.FileOpened, self, self.Manager)
598                     new_editingpanel.SetIndex(result)
599                     self.FileOpened.AddPage(new_editingpanel, "")
600                     self.FileOpened.SetSelection(self.FileOpened.GetPageCount() - 1)
601                     if self.Manager.CurrentDS302Defined(): 
602                         self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, True)
603                     else:
604                         self.EditMenu.Enable(ID_OBJDICTEDITEDITMENUDS302PROFILE, False)
605                     self.RefreshEditMenu()
606                     self.RefreshBufferState()
607                     self.RefreshProfileMenu()
608                     self.RefreshMainMenu()
609                 else:
610                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
611                     message.ShowModal()
612                     message.Destroy()
613         dialog.Destroy()
614         event.Skip()
615
616     def OnSaveMenu(self, event):
617         self.Save()
618         event.Skip()
619     
620     def OnSaveAsMenu(self, event):
621         self.SaveAs()
622         event.Skip()
623         
624     def Save(self):
625         result = self.Manager.SaveCurrentInFile()
626         if not result:
627             self.SaveAs()
628         elif type(result) != StringType:
629             self.RefreshBufferState()
630         else:
631             message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
632             message.ShowModal()
633             message.Destroy()
634
635     def SaveAs(self):
636         filepath = self.Manager.GetCurrentFilePath()
637         if filepath != "":
638             directory, filename = os.path.split(filepath)
639         else:
640             directory, filename = os.getcwd(), "%s.od"%self.Manager.GetCurrentNodeInfos()[0]
641         dialog = wx.FileDialog(self, "Choose a file", directory, filename,  "OD files (*.od)|*.od|All files|*.*", wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
642         if dialog.ShowModal() == wx.ID_OK:
643             filepath = dialog.GetPath()
644             if os.path.isdir(os.path.dirname(filepath)):
645                 result = self.Manager.SaveCurrentInFile(filepath)
646                 if type(result) != StringType:
647                     self.RefreshBufferState()
648                 else:
649                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
650                     message.ShowModal()
651                     message.Destroy()
652             else:
653                 message = wx.MessageDialog(self, "%s is not a valid folder!"%os.path.dirname(filepath), "Error", wx.OK|wx.ICON_ERROR)
654                 message.ShowModal()
655                 message.Destroy()
656         dialog.Destroy()
657
658     def OnCloseMenu(self, event):
659         answer = wx.ID_YES
660         result = self.Manager.CloseCurrent()
661         if not result:
662             dialog = wx.MessageDialog(self, "There are changes, do you want to save?",  "Close File", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
663             answer = dialog.ShowModal()
664             dialog.Destroy()
665             if answer == wx.ID_YES:
666                 self.OnSaveMenu(event)
667                 if self.Manager.CurrentIsSaved():
668                     self.Manager.CloseCurrent()
669             elif answer == wx.ID_NO:
670                 self.Manager.CloseCurrent(True)
671         if self.FileOpened.GetPageCount() > self.Manager.GetBufferNumber():
672             current = self.FileOpened.GetSelection()
673             self.FileOpened.DeletePage(current)
674             if self.FileOpened.GetPageCount() > 0:
675                 self.FileOpened.SetSelection(min(current, self.FileOpened.GetPageCount() - 1))
676             self.RefreshBufferState()
677             self.RefreshMainMenu()
678         event.Skip()
679         
680
681 #-------------------------------------------------------------------------------
682 #                         Import and Export Functions
683 #-------------------------------------------------------------------------------
684
685     def OnImportEDSMenu(self, event):
686         dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), "",  "EDS files (*.eds)|*.eds|All files|*.*", wx.OPEN|wx.CHANGE_DIR)
687         if dialog.ShowModal() == wx.ID_OK:
688             filepath = dialog.GetPath()
689             if os.path.isfile(filepath):
690                 result = self.Manager.ImportCurrentFromEDSFile(filepath)
691                 if type(result) == IntType:
692                     new_editingpanel = EditingPanel(self.FileOpened, self, self.Manager)
693                     new_editingpanel.SetIndex(result)
694                     self.FileOpened.AddPage(new_editingpanel, "")
695                     self.FileOpened.SetSelection(self.FileOpened.GetPageCount() - 1)
696                     self.RefreshBufferState()
697                     self.RefreshCurrentIndexList()
698                     self.RefreshProfileMenu()
699                     self.RefreshMainMenu()
700                     message = wx.MessageDialog(self, "Import successful", "Information", wx.OK|wx.ICON_INFORMATION)
701                     message.ShowModal()
702                     message.Destroy()
703                 else:
704                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
705                     message.ShowModal()
706                     message.Destroy()
707             else:
708                 message = wx.MessageDialog(self, "\"%s\" is not a valid file!"%filepath, "Error", wx.OK|wx.ICON_ERROR)
709                 message.ShowModal()
710                 message.Destroy()
711         dialog.Destroy()
712         event.Skip()
713
714
715     def OnExportEDSMenu(self, event):
716         dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), self.Manager.GetCurrentNodeInfos()[0], "EDS files (*.eds)|*.eds|All files|*.*", wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
717         if dialog.ShowModal() == wx.ID_OK:
718             filepath = dialog.GetPath()
719             if os.path.isdir(os.path.dirname(filepath)):
720                 path, extend = os.path.splitext(filepath)
721                 if extend in ("", "."):
722                     filepath = path + ".eds"
723                 result = self.Manager.ExportCurrentToEDSFile(filepath)
724                 if not result:
725                     message = wx.MessageDialog(self, "Export successful", "Information", wx.OK|wx.ICON_INFORMATION)
726                     message.ShowModal()
727                     message.Destroy()
728                 else:
729                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
730                     message.ShowModal()
731                     message.Destroy()
732             else:
733                 message = wx.MessageDialog(self, "\"%s\" is not a valid folder!"%os.path.dirname(filepath), "Error", wx.OK|wx.ICON_ERROR)
734                 message.ShowModal()
735                 message.Destroy()
736         dialog.Destroy()
737         event.Skip()
738
739     def OnExportCMenu(self, event):
740         dialog = wx.FileDialog(self, "Choose a file", os.getcwd(), self.Manager.GetCurrentNodeInfos()[0],  "CANFestival C files (*.c)|*.c|All files|*.*", wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
741         if dialog.ShowModal() == wx.ID_OK:
742             filepath = dialog.GetPath()
743             if os.path.isdir(os.path.dirname(filepath)):
744                 path, extend = os.path.splitext(filepath)
745                 if extend in ("", "."):
746                     filepath = path + ".c"
747                 result = self.Manager.ExportCurrentToCFile(filepath)
748                 if not result:
749                     message = wx.MessageDialog(self, "Export successful", "Information", wx.OK|wx.ICON_INFORMATION)
750                     message.ShowModal()
751                     message.Destroy()
752                 else:
753                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
754                     message.ShowModal()
755                     message.Destroy()
756             else:
757                 message = wx.MessageDialog(self, "\"%s\" is not a valid folder!"%os.path.dirname(filepath), "Error", wx.OK|wx.ICON_ERROR)
758                 message.ShowModal()
759                 message.Destroy()
760         dialog.Destroy()
761         event.Skip()
762
763 #-------------------------------------------------------------------------------
764 #                          Editing Profiles functions
765 #-------------------------------------------------------------------------------
766
767     def OnCommunicationMenu(self, event):
768         dictionary,current = self.Manager.GetCurrentCommunicationLists()
769         self.EditProfile("Edit DS-301 Profile", dictionary, current)
770         event.Skip()
771     
772     def OnOtherCommunicationMenu(self, event):
773         dictionary,current = self.Manager.GetCurrentDS302Lists()
774         self.EditProfile("Edit DS-301 Profile", dictionary, current)
775         event.Skip()
776     
777     def OnEditProfileMenu(self, event):
778         title = "Edit %s Profile"%self.Manager.GetCurrentProfileName()
779         dictionary,current = self.Manager.GetCurrentProfileLists()
780         self.EditProfile(title, dictionary, current)
781         event.Skip()
782     
783     def EditProfile(self, title, dictionary, current):
784         dialog = CommunicationDialog(self)
785         dialog.SetTitle(title)
786         dialog.SetIndexDictionary(dictionary)
787         dialog.SetCurrentList(current)
788         dialog.RefreshLists()
789         if dialog.ShowModal() == wx.ID_OK:
790             new_profile = dialog.GetCurrentList()
791             addinglist = []
792             removinglist = []
793             for index in new_profile:
794                 if index not in current:
795                     addinglist.append(index)
796             for index in current:
797                 if index not in new_profile:
798                     removinglist.append(index)
799             self.Manager.ManageEntriesOfCurrent(addinglist, removinglist)
800             self.Manager.BufferCurrentNode()
801             self.RefreshBufferState()
802             self.RefreshCurrentIndexList()
803         dialog.Destroy()
804
805     def GetProfileCallBack(self, text):
806         def ProfileCallBack(event):
807             self.Manager.AddSpecificEntryToCurrent(text)
808             self.RefreshBufferState()
809             self.RefreshCurrentIndexList()
810             event.Skip()
811         return ProfileCallBack
812
813 #-------------------------------------------------------------------------------
814 #                         Edit Node informations function
815 #-------------------------------------------------------------------------------
816
817     def OnNodeInfosMenu(self, event):
818         dialog = NodeInfosDialog(self)
819         name, id, type, description = self.Manager.GetCurrentNodeInfos()
820         defaultstringsize = self.Manager.GetCurrentNodeDefaultStringSize()
821         dialog.SetValues(name, id, type, description, defaultstringsize)
822         if dialog.ShowModal() == wx.ID_OK:
823             name, id, type, description, defaultstringsize = dialog.GetValues()
824             self.Manager.SetCurrentNodeInfos(name, id, type, description)
825             self.Manager.SetCurrentNodeDefaultStringSize(defaultstringsize)
826             self.RefreshBufferState()
827             self.RefreshProfileMenu()
828             selected = self.FileOpened.GetSelection()
829             if selected >= 0:
830                 window = self.FileOpened.GetPage(selected)
831                 window.RefreshTable()
832         event.Skip()
833
834
835 #-------------------------------------------------------------------------------
836 #                           Add User Types and Variables
837 #-------------------------------------------------------------------------------
838         
839     def AddMapVariable(self):
840         index = self.Manager.GetCurrentNextMapIndex()
841         if index:
842             dialog = MapVariableDialog(self)
843             dialog.SetIndex(index)
844             if dialog.ShowModal() == wx.ID_OK:
845                 index, name, struct, number = dialog.GetValues()
846                 result = self.Manager.AddMapVariableToCurrent(index, name, struct, number)
847                 if type(result) != StringType:
848                     self.RefreshBufferState()
849                     self.RefreshCurrentIndexList()
850                 else:
851                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
852                     message.ShowModal()
853                     message.Destroy()
854             dialog.Destroy()
855         else:
856             message = wx.MessageDialog(self, result, "No map variable index left!", wx.OK|wx.ICON_ERROR)
857             message.ShowModal()
858             message.Destroy()
859         
860     def AddUserType(self):
861         dialog = UserTypeDialog(self)
862         dialog.SetTypeList(self.Manager.GetCustomisableTypes())
863         if dialog.ShowModal() == wx.ID_OK:
864             type, min, max, length = dialog.GetValues()
865             result = self.Manager.AddUserTypeToCurrent(type, min, max, length)
866             if not result:
867                 self.RefreshBufferState()
868                 self.RefreshCurrentIndexList()
869             else:
870                 message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
871                 message.ShowModal()
872                 message.Destroy()
873         dialog.Destroy()
874     
875
876 #-------------------------------------------------------------------------------
877 #                               Exception Handler
878 #-------------------------------------------------------------------------------
879
880 Max_Traceback_List_Size = 20
881
882 def Display_Exception_Dialog(e_type,e_value,e_tb):
883     trcbck_lst = []
884     for i,line in enumerate(traceback.extract_tb(e_tb)):
885         trcbck = " " + str(i+1) + ". "
886         if line[0].find(os.getcwd()) == -1:
887             trcbck += "file : " + str(line[0]) + ",   "
888         else:
889             trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
890         trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
891         trcbck_lst.append(trcbck)
892         
893     # Allow clicking....
894     cap = wx.Window_GetCapture()
895     if cap:
896         cap.ReleaseMouse()
897
898     dlg = wx.SingleChoiceDialog(None, 
899         """
900 An error happens.
901
902 Click on OK for saving an error report.
903
904 Please contact LOLITech at:
905 +33 (0)3 29 52 95 67
906 bugs_objdictedit@lolitech.fr
907
908
909 Error:
910 """ +
911         str(e_type) + " : " + str(e_value), 
912         "Error",
913         trcbck_lst)
914     try:
915         res = (dlg.ShowModal() == wx.ID_OK)
916     finally:
917         dlg.Destroy()
918
919     return res
920
921 def Display_Error_Dialog(e_value):
922     message = wx.MessageDialog(None, str(e_value), "Error", wx.OK|wx.ICON_ERROR)
923     message.ShowModal()
924     message.Destroy()
925
926 def get_last_traceback(tb):
927     while tb.tb_next:
928         tb = tb.tb_next
929     return tb
930
931
932 def format_namespace(d, indent='    '):
933     return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
934
935
936 ignored_exceptions = [] # a problem with a line in a module is only reported once per session
937
938 def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
939     
940     def handle_exception(e_type, e_value, e_traceback):
941         traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
942         last_tb = get_last_traceback(e_traceback)
943         ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
944         if str(e_value).startswith("!!!"):
945             Display_Error_Dialog(e_value)
946         elif ex not in ignored_exceptions:
947             ignored_exceptions.append(ex)
948             result = Display_Exception_Dialog(e_type,e_value,e_traceback)
949             if result:
950                 info = {
951                     'app-title' : wx.GetApp().GetAppName(), # app_title
952                     'app-version' : app_version,
953                     'wx-version' : wx.VERSION_STRING,
954                     'wx-platform' : wx.Platform,
955                     'python-version' : platform.python_version(), #sys.version.split()[0],
956                     'platform' : platform.platform(),
957                     'e-type' : e_type,
958                     'e-value' : e_value,
959                     'date' : time.ctime(),
960                     'cwd' : os.getcwd(),
961                     }
962                 if e_traceback:
963                     info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
964                     last_tb = get_last_traceback(e_traceback)
965                     exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
966                     info['locals'] = format_namespace(exception_locals)
967                     if 'self' in exception_locals:
968                         info['self'] = format_namespace(exception_locals['self'].__dict__)
969                 
970                 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
971                 lst = info.keys()
972                 lst.sort()
973                 for a in lst:
974                     output.write(a+":\n"+str(info[a])+"\n\n")
975
976     #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
977     sys.excepthook = handle_exception
978
979 if __name__ == '__main__':
980     def usage():
981         print "\nUsage of objdictedit.py :"
982         print "\n   %s [Filepath, ...]\n"%sys.argv[0]
983
984     try:
985         opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
986     except getopt.GetoptError:
987         # print help information and exit:
988         usage()
989         sys.exit(2)
990
991     for o, a in opts:
992         if o in ("-h", "--help"):
993             usage()
994             sys.exit()
995     
996     app = wx.PySimpleApp()
997     wx.InitAllImageHandlers()
998     
999     # Install a exception handle for bug reports
1000     AddExceptHook(os.getcwd(),__version__)
1001     
1002     frame = objdictedit(None, args)
1003
1004     frame.Show()
1005     app.MainLoop()