17
17
__author__ = "Cody Precord <cprecord@editra.org>"
18
__svnid__ = "$Id: ed_main.py 63826 2010-04-02 03:50:15Z CJP $"
19
__revision__ = "$Revision: 63826 $"
18
__svnid__ = "$Id: ed_main.py 67857 2011-06-05 00:16:24Z CJP $"
19
__revision__ = "$Revision: 67857 $"
21
21
#--------------------------------------------------------------------------#
56
56
#--------------------------------------------------------------------------#
58
58
class MainWindow(wx.Frame, viewmgr.PerspectiveManager):
59
"""Editras Main Window
60
@todo: modularize the event handling more (pubsub?)
59
"""Editras Main Window"""
63
60
# Clipboard ring is limited to 25, why? Because any more is a waste of
64
# memory and an inefficient waste of your time to cyle through.
61
# memory and an inefficient waste of your time to cycle through.
65
62
CLIPBOARD = util.EdClipboard(25)
74
71
wx.Frame.__init__(self, parent, id_, title, size=wsize,
75
72
style=wx.DEFAULT_FRAME_STYLE)
77
hint = wx.aui.AUI_MGR_TRANSPARENT_HINT
78
if wx.Platform == '__WXGTK__':
79
# Use venetian blinds style as trasparent can cause crashes
80
# on linux when desktop compositing is used.
81
hint = wx.aui.AUI_MGR_VENETIAN_BLINDS_HINT
83
self._mgr = wx.aui.AuiManager(flags=wx.aui.AUI_MGR_DEFAULT |
84
wx.aui.AUI_MGR_TRANSPARENT_DRAG |
86
wx.aui.AUI_MGR_ALLOW_ACTIVE_PANE)
87
self._mgr.SetManagedWindow(self)
88
viewmgr.PerspectiveManager.__init__(self, self._mgr, \
73
viewmgr.PerspectiveManager.__init__(self, CONFIG['CACHE_DIR'])
91
75
# Setup app icon and title
93
76
util.SetWindowIcon(self)
96
79
self._loaded = False
80
self._initialized = False # for GTK OnActivate HACK
81
self._mlock = ebmlib.CallLock()
97
82
self._last_save = u''
98
83
self.LOG = wx.GetApp().GetLog()
99
84
self._exiting = False
100
85
self._handlers = dict(menu=list(), ui=list())
102
87
#---- Setup File History ----#
103
self.filehistory = wx.FileHistory(_PGET('FHIST_LVL', 'int', 9))
88
self.filehistory = ebmlib.EFileHistory(_PGET('FHIST_LVL', 'int', 9))
105
90
#---- Status bar on bottom of window ----#
106
91
self.SetStatusBar(ed_statbar.EdStatBar(self))
107
92
self.GetStatusBar().Show(_PGET('STATBAR', default=True))
109
93
#---- End Statusbar Setup ----#
111
#---- Notebook that contains the editting buffers ----#
95
#---- Notebook that contains the editing buffers ----#
112
96
self._mpane = ed_mpane.MainPanel(self)
113
97
self.nb = self._mpane.GetWindow()
114
self._mgr.AddPane(self._mpane, wx.aui.AuiPaneInfo(). \
115
Name("EditPane").Center().Layer(1).Dockable(False). \
116
CloseButton(False).MaximizeButton(False). \
117
CaptionVisible(False))
118
self._mpane.InitCommandBar() # <- required due to nb dependancies...
98
self.PanelMgr.AddPane(self._mpane, wx.aui.AuiPaneInfo(). \
99
Name("EditPane").Center().Layer(1).Dockable(False). \
100
CloseButton(False).MaximizeButton(False). \
101
CaptionVisible(False))
102
self._mpane.InitCommandBar() # <- required due to nb dependencies...
120
104
#---- Command Bar ----#
121
105
self._mpane.HideCommandBar()
179
162
(ID_COLUMN_MODE, self.DispatchToControl),
180
163
(ID_TOGGLE_FOLD, self.DispatchToControl),
181
164
(ID_TOGGLE_ALL_FOLDS, self.DispatchToControl),
165
(ID_SHOW_AUTOCOMP, self.DispatchToControl),
166
(ID_SHOW_CALLTIP, self.DispatchToControl),
182
167
(ID_QUICK_FIND, self.OnCommandBar),
183
168
(ID_PREF, OnPreferences),
271
256
self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
272
257
self.Bind(wx.EVT_CLOSE, self.OnClose)
258
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy, self)
273
259
self.Bind(ed_event.EVT_STATUS, self.OnStatus)
289
275
self._handlers['menu'].extend(addons.GetEventHandlers())
290
276
self._handlers['ui'].extend(addons.GetEventHandlers(ui_evt=True))
291
277
shelf = ed_shelf.Shelf(plgmgr)
293
self._handlers['ui'].extend(shelf.GetUiHandlers())
294
self._shelf = ed_shelf.Shelf.delegate
295
ed_shelf.Shelf.delegate = None
278
self._shelf = shelf.Init(self)
279
self._handlers['ui'].extend(shelf.GetUiHandlers(self._shelf))
297
281
self.LOG("[ed_main][info] Loading Generator plugins")
298
282
generator.Generator(plgmgr).InstallMenu(menbar.GetMenuByName("tools"))
300
284
# Set Perspective and other UI settings
301
285
self.SetPerspective(_PGET('DEFAULT_VIEW'))
286
self.PanelMgr.Update()
287
# Make sure all clients are updated to user specified display font.
303
288
ed_msg.PostMessage(ed_msg.EDMSG_DSP_FONT,
304
289
_PGET('FONT3', 'font', wx.NORMAL_FONT))
313
298
__name__ = u"MainWindow"
316
ed_msg.Unsubscribe(self.OnUpdateFileHistory)
300
def OnDestroy(self, evt):
301
"""Disconnect Message Handlers"""
302
if evt.GetId() == self.GetId():
303
ed_msg.Unsubscribe(self.OnUpdateFileHistory)
318
306
#---- End Private Member Functions/Variables ----#
324
312
@type evt: wx.ActivateEvent
315
if self._mlock.IsLocked():
316
# Deactivated for popup, leave handlers hooked up
317
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_ALL)
327
321
app = wx.GetApp()
328
322
active = evt.GetActive()
330
# Don't needlessly push and pop events if we only have one window open
331
if self._loaded and len(app.GetMainWindows()) == 1:
333
wx.GetApp().SetTopWindow(self)
334
wx.UpdateUIEvent.SetUpdateInterval(215)
335
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_SPECIFIED)
336
self.SetExtraStyle(wx.WS_EX_PROCESS_UI_UPDATES)
338
self.SetExtraStyle(0)
339
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_ALL)
343
324
# Add or remove handlers from the event stack
325
if active and not self._loaded:
345
327
app.SetTopWindow(self)
348
329
# Slow the update interval to reduce overhead
349
330
wx.UpdateUIEvent.SetUpdateInterval(215)
360
341
# window doesn't get activated until later than it does on the
361
342
# other platforms. So for panels that depend on updating their
362
343
# initial state we need to send out a fake update message here.
363
if wx.Platform == '__WXGTK__':
344
if wx.Platform == '__WXGTK__' and not self._initialized:
345
self._initialized = True
364
346
nb = self.GetNotebook()
365
347
ed_msg.PostMessage(ed_msg.EDMSG_UI_NB_CHANGED,
366
348
(nb, nb.GetSelection()))
368
self.SetExtraStyle(0)
370
# HACK set update ui events back to process all here in case
371
# opened dialog needs them. Not sure why this is necessary but it
372
# is the only solution I could find to fix the external find
373
# dialogs so that their buttons become enabled when typing in the
376
# If the windows that took the active position is another mainwindow
377
# it will set the events back to UPDATE_UI_PROCESS_SPECIFIED to
378
# prevent all the toolbars/menu items of each window from updating
379
# when they dont need to.
380
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_ALL)
381
for handler in self._handlers['menu']:
382
app.RemoveHandlerForID(handler[0])
384
for handler in self._handlers['ui']:
385
app.RemoveUIHandlerForID(handler[0])
387
350
self._loaded = False
393
357
@param msg: Message object (data == filename)
397
self.filehistory.AddFileToHistory(msg.GetData())
398
except wx.PyAssertionError:
399
# ignore errors that wxMac sometimes raises about unicode data
360
# May get notified after/during delete
363
self.filehistory.AddFileToHistory(msg.GetData())
364
except wx.PyAssertionError:
365
# ignore errors that wxMac sometimes raises about unicode data
402
368
def AddFileToHistory(self, fname):
403
369
"""Add a file to the windows file history as well as any
461
427
dlg = wx.FileDialog(self, _("Editra: Open"), fdir, "",
462
428
''.join(syntax.GenFileFilters()),
463
wx.OPEN | wx.MULTIPLE)
429
wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR)
464
430
dlg.SetFilterIndex(_PGET('FFILTER', 'int', 0))
466
if dlg.ShowModal() == wx.ID_OK:
432
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
467
433
_PSET('FFILTER', dlg.GetFilterIndex())
468
434
for path in dlg.GetPaths():
469
435
if _PGET('OPEN_NW', default=False):
456
def DeActivate(self):
457
"""Helper method for the App to tell this window to remove
458
all its event handlers.
461
self.SetExtraStyle(0)
463
# HACK set update ui events back to process all here in case
464
# opened dialog needs them. Not sure why this is necessary but it
465
# is the only solution I could find to fix the external find
466
# dialogs so that their buttons become enabled when typing in the
469
# If the windows that took the active position is another mainwindow
470
# it will set the events back to UPDATE_UI_PROCESS_SPECIFIED to
471
# prevent all the toolbars/menu items of each window from updating
472
# when they dont need to.
473
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_ALL)
474
self.FlushEventStack()
476
def FlushEventStack(self):
477
"""Clear the Menu and UpdateUI event handler stack
478
@note: only unregisters this frames handlers from the app
482
for handler in self._handlers['menu']:
483
app.RemoveHandlerForID(handler[0])
485
for handler in self._handlers['ui']:
486
app.RemoveUIHandlerForID(handler[0])
490
488
def GetCommandbar(self):
491
489
"""Get this windows command bar
492
490
@return: ed_cmdbar.CommandBarBase
549
540
hist_list = _PGET('FHIST', default=list())
550
541
if len(hist_list) > size:
551
542
hist_list = hist_list[:size]
553
for fname in hist_list:
554
if isinstance(fname, basestring) and fname:
555
# TODO: find out why these errors are happening
556
# when loading the pickled strings on some systems
557
# The pickled strings are not in unicode format.
559
self.filehistory.AddFileToHistory(fname)
560
except wx.PyAssertionError:
543
self.filehistory.History = hist_list
562
544
except UnicodeEncodeError, msg:
563
self.LOG("[ed_main][err] Filehistory load failed: %s" % str(msg))
545
self.LOG("[ed_main][err] Filehistory load failed: %s" % msg)
565
547
def OnNew(self, evt):
566
548
"""Start a New File in a new tab
611
def SaveFile(self, tablbl, buf):
612
"""Save the given page in the notebook
613
@param tablbl: main notebook tab label
614
@param buf: EdEditView instance
615
@note: intended for internal use! method signature may change
618
fname = ebmlib.GetFileName(buf.GetFileName())
620
fpath = buf.GetFileName()
621
result = buf.SaveFile(fpath)
622
self._last_save = fpath
624
self.PushStatusText(_("Saved File: %s") % fname, SB_INFO)
626
err = buf.GetDocument().GetLastError()
627
self.PushStatusText(_("ERROR: %s") % err, SB_INFO)
628
ed_mdlg.SaveErrorDlg(self, fname, err)
629
buf.GetDocument().ResetAll()
631
self.OnSaveAs(ID_SAVEAS, tablbl, buf)
633
def SaveCurrentBuffer(self):
634
"""Save the file in the currently selected editor buffer"""
635
page = self.nb.GetSelection()
636
self.SaveFile(self.nb.GetPageText(page), self.nb.GetCurrentCtrl())
638
def SaveAllBuffers(self):
639
"""Save all open editor buffers"""
640
for page in xrange(self.nb.GetPageCount()):
641
buff = self.nb.GetPage(page)
642
if isinstance(buff, wx.stc.StyledTextCtrl):
644
self.SaveFile(self.nb.GetPageText(page), buff)
629
646
def OnSave(self, evt):
630
647
"""Save Current or All Buffers
631
648
@param evt: Event fired that called this handler
635
652
e_id = evt.GetId()
637
653
if e_id == ID_SAVE:
638
page = self.nb.GetSelection()
639
ctrls = [(self.nb.GetPageText(page),
640
self.nb.GetCurrentCtrl(), page)]
654
self.SaveCurrentBuffer()
641
655
elif e_id == ID_SAVEALL:
642
# Collect all open editor buffers
643
for page in xrange(self.nb.GetPageCount()):
644
if issubclass(self.nb.GetPage(page).__class__,
645
wx.stc.StyledTextCtrl):
646
ctrls.append((self.nb.GetPageText(page),
647
self.nb.GetPage(page), page))
656
self.SaveAllBuffers()
652
# TODO: detect when save fails due to encoding and prompt to
653
# request an encoding from the user.
655
fname = ebmlib.GetFileName(ctrl[1].GetFileName())
657
fpath = ctrl[1].GetFileName()
658
result = ctrl[1].SaveFile(fpath)
659
self._last_save = fpath
661
self.PushStatusText(_("Saved File: %s") % fname, SB_INFO)
663
err = ctrl[1].GetDocument().GetLastError()
664
self.PushStatusText(_("ERROR: %s") % err, SB_INFO)
665
ed_mdlg.SaveErrorDlg(self, fname, err)
666
ctrl[1].GetDocument().ResetAll()
668
self.OnSaveAs(ID_SAVEAS, ctrl[0], ctrl[1])
670
661
def OnSaveAs(self, evt, title=u'', page=None):
671
662
"""Save File Using a new/different name
672
663
@param evt: Event fired that called this handler
725
716
_("Profile") + " (*.ppb)|*.ppb",
726
717
wx.SAVE | wx.OVERWRITE_PROMPT)
728
if dlg.ShowModal() == wx.ID_OK:
719
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
729
720
profiler.TheProfile.Write(dlg.GetPath())
730
721
self.PushStatusText(_("Profile Saved as: %s") % \
731
722
dlg.GetFilename(), SB_INFO)
745
736
CONFIG['PROFILE_DIR'], "default.ppb",
746
737
_("Profile") + " (*.ppb)|*.ppb", wx.OPEN)
748
result = dlg.ShowModal()
749
if result == wx.ID_OK:
739
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
750
740
profiler.TheProfile.Load(dlg.GetPath())
751
741
self.PushStatusText(_("Loaded Profile: %s") % \
752
742
dlg.GetFilename(), SB_INFO)
770
760
_("Session") + " (*.session)|*.session",
771
761
wx.SAVE | wx.OVERWRITE_PROMPT)
773
if dlg.ShowModal() == wx.ID_OK:
763
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
774
764
fname = dlg.GetPath()
775
765
if fname is None or not len(fname):
796
786
CONFIG['SESSION_DIR'], u"",
797
787
_("Session") + " (*.session)|*.session", wx.OPEN)
799
if dlg.ShowModal() == wx.ID_OK:
789
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
800
790
fname = dlg.GetPath()
801
791
nbook = self.GetNotebook()
802
792
rval = nbook.LoadSessionFile(fname)
818
808
@param evt: event that called this handler
821
statbar = self.GetStatusBar()
823
# The frames Set/PushStatusText methods don't seem to call the
824
# method of the statusbar class instance when setting the text so
825
# get and set the text in the status bar directly so that our
826
# statusbar's overridden settext method gets called
827
statbar.SetStatusText(evt.GetMessage(), evt.GetSection())
828
# self.SetStatusText(evt.GetMessage(), evt.GetSection())
811
self.SetStatusText(evt.GetMessage(), evt.GetSection())
830
813
def OnPrint(self, evt):
831
814
"""Handles sending the current document to the printer,
872
855
# Save session files
873
session = _PGET('LAST_SESSION')
874
if not isinstance(session, basestring) or not len(session):
875
session = os.path.join(CONFIG['SESSION_DIR'], u"__default.session")
876
_PSET('LAST_SESSION', session)
877
result = self.nb.SaveSessionFile(session)
878
if result is not None:
879
pass # TODO: report error?
856
self.nb.SaveCurrentSession()
881
858
# Cleanup Controls
882
859
self._exiting = True
883
860
controls = self.nb.GetPageCount()
884
861
self.LOG("[ed_main][evt] OnClose: Number of controls: %d" % controls)
886
864
if controls <= 0:
887
865
self.Close(True) # Force exit since there is a problem
889
867
self.LOG("[ed_main][evt] OnClose: Requesting Page Close")
890
868
if not self.nb.ClosePage():
891
870
self._exiting = False
892
871
ed_msg.PostMessage(ed_msg.EDMSG_UI_NB_CHANGED,
893
872
(self.nb, self.nb.GetSelection()))
897
877
### If we get to here there is no turning back so cleanup
898
878
### additional items and save user settings
904
884
# Save Shelf contents
905
885
_PSET('SHELF_ITEMS', self._shelf.GetItemStack())
886
_PSET('SHELF_LAYOUT', self._shelf.GetPerspective())
887
_PSET('SHELF_SELECTION', self._shelf.GetSelection())
907
889
# Save Window Size/Position for next launch
908
890
self.UpdateAutoPerspective()
948
929
# Finally close the window
949
930
self.LOG("[ed_main][evt] OnClose: Closing Main Frame")
950
931
wx.GetApp().UnRegisterWindow(repr(self))
933
# Ensure that event handlers have been un registered from the app
934
wx.UpdateUIEvent.SetMode(wx.UPDATE_UI_PROCESS_ALL)
953
939
#---- End File Menu Functions ----#
998
984
@param evt: CommandEvent instance
1001
paneInfo = self._mgr.GetPane("EditPane")
987
paneInfo = self.PanelMgr.GetPane("EditPane")
1002
988
if paneInfo.IsMaximized():
1003
self._mgr.RestorePane(paneInfo)
989
self.PanelMgr.RestorePane(paneInfo)
1004
990
ed_msg.PostMessage(ed_msg.EDMSG_UI_STC_RESTORE, context=self.GetId())
1006
self._mgr.RestoreMaximizedPane()
1007
self._mgr.MaximizePane(paneInfo)
992
self.PanelMgr.RestoreMaximizedPane()
993
self.PanelMgr.MaximizePane(paneInfo)
994
self.PanelMgr.Update()
1010
996
#---- End View Menu Functions ----#
1024
1010
fdata = wx.FontData()
1025
1011
fdata.SetInitialFont(ctrl.GetDefaultFont())
1026
1012
dlg = wx.FontDialog(self, fdata)
1027
result = dlg.ShowModal()
1013
result = ebmlib.LockCall(self._mlock, dlg.ShowModal)
1028
1014
data = dlg.GetFontData()
1030
1016
if result == wx.ID_OK:
1031
1017
font = data.GetChosenFont()
1032
1018
ctrl.SetGlobalFont(self.nb.control.FONT_PRIMARY, \
1033
1019
font.GetFaceName(), font.GetPointSize())
1020
ctrl.SetGlobalFont(self.nb.control.FONT_SECONDARY, \
1021
font.GetFaceName(), font.GetPointSize())
1034
1022
ctrl.UpdateAllStyles()
1114
1102
ID_PASTE, ID_LINE_BEFORE, ID_LINE_AFTER, ID_DUP_LINE,
1115
1103
ID_PASTE_AFTER, ID_COLUMN_MODE, ID_TOGGLE_FOLD,
1116
1104
ID_CYCLE_CLIPBOARD,
1117
ID_TOGGLE_ALL_FOLDS, ID_DELETE_LINE ]
1105
ID_TOGGLE_ALL_FOLDS, ID_DELETE_LINE,
1106
ID_SHOW_AUTOCOMP, ID_SHOW_CALLTIP ]
1119
1108
# Special handling for common clipboard related actions
1120
1109
has_focus = self.FindFocus()
1210
1199
dlg.SetBitmap(bmp)
1211
1200
dlg.CenterOnParent()
1213
if dlg.ShowModal() == wx.ID_OK:
1202
if ebmlib.LockCall(self._mlock, dlg.ShowModal) == wx.ID_OK:
1214
1203
nenc = dlg.GetEncoding()
1215
1204
doc.SetEncoding(nenc)
1216
1205
success = ctrl.ReloadFile()[0]
1217
1206
if not success:
1218
1207
msg = _("Failed to reload the file with: %(encoding)s") % dict(encoding=nenc)
1219
1208
wx.MessageBox(msg, style=wx.OK|wx.ICON_ERROR)
1209
# Revert to previous encoding
1220
1210
doc.SetEncoding(cenc)
1221
1211
ctrl.ReloadFile()
1235
1225
bmp = wx.ArtProvider.GetBitmap(str(ID_NEW_WINDOW), wx.ART_MENU)
1236
self._paneNavi = eclib.AuiPaneNavigator(self, self._mgr, bmp,
1226
self._paneNavi = eclib.AuiPaneNavigator(self, self.PanelMgr, bmp,
1237
1227
_("Aui Pane Navigator"))
1238
1228
self._paneNavi.SetReturnCode(wx.ID_OK)
1239
self._paneNavi.ShowModal()
1229
ebmlib.LockCall(self._mlock, self._paneNavi.ShowModal)
1241
1231
sel = self._paneNavi.GetSelection()
1242
1232
self._paneNavi.Destroy()
1243
1233
self._paneNavi = None
1245
1235
if isinstance(sel, basestring):
1246
paneInfo = self._mgr.GetPane(sel)
1236
paneInfo = self.PanelMgr.GetPane(sel)
1247
1237
if paneInfo.IsOk():
1248
1238
if not paneInfo.IsShown():
1249
1239
paneInfo.Show()
1240
self.PanelMgr.Update()
1241
# Notify activation if the window supports it
1242
if hasattr(paneInfo.window, "OnShowAUIPane"):
1243
paneInfo.window.OnShowAUIPane()
1251
1244
paneInfo.window.SetFocus()
1405
1398
elif e_id == ID_INDENT_GUIDES:
1406
1399
evt.Check(bool(ctrl.GetIndentationGuides()))
1407
1400
elif e_id == ID_MAXIMIZE_EDITOR:
1408
paneInfo = self._mgr.GetPane("EditPane")
1401
paneInfo = self.PanelMgr.GetPane("EditPane")
1409
1402
binder = self.MenuBar.GetKeyBinder()
1410
1403
binding = binder.GetBinding(ID_MAXIMIZE_EDITOR)
1411
1404
txt = _("Maximize Editor")
1500
1494
@type title: string
1503
name = "%s v%s" % (PROG_NAME, VERSION)
1497
name = u"%s v%s" % (PROG_NAME, VERSION)
1506
wx.Frame.SetTitle(self, title + name)
1499
name = u" - " + name
1500
super(MainWindow, self).SetTitle(title + name)
1502
def SetupToolBar(self):
1503
"""Setup or reinitialize the windows ToolBar"""
1504
tb = self.GetToolBar()
1507
self.SetToolBar(ed_toolbar.EdToolBar(self))
1508
self.GetToolBar().Show(_PGET('TOOLBAR'))
1509
1512
def UpdateClipboardRing(cls):
1595
1598
handlers = list()
1596
1599
for observer in self.observers:
1599
items = observer.GetUIHandlers()
1603
if hasattr(observer, 'GetUIHandlers'):
1604
items = observer.GetUIHandlers()
1605
assert isinstance(items, list), "Must be a list()!"
1601
items = observer.GetMenuHandlers()
1607
if hasattr(observer, 'GetMenuHandlers'):
1608
items = observer.GetMenuHandlers()
1609
assert isinstance(items, list), "Must be a list()!"
1602
1610
except Exception, msg:
1603
util.Log("[ed_main][err] MainWindoAddOn.GetEventHandlers: %s" % str(msg))
1611
util.Log("[ed_main][err] MainWindowAddOn.GetEventHandlers: %s" % str(msg))
1605
handlers.extend(items)
1614
if items is not None:
1615
handlers.extend(items)
1606
1617
return handlers