~ubuntu-branches/ubuntu/trusty/boa-constructor/trusty

« back to all changes in this revision

Viewing changes to Explorers/PrefsExplorer.py

  • Committer: Bazaar Package Importer
  • Author(s): Cédric Delfosse
  • Date: 2007-01-23 21:32:29 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070123213229-1d9lxp9c4dutjwv5
Add a .desktop file (Closes: #349081)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#-----------------------------------------------------------------------------
2
 
# Name:        PrefsExplorer.py
3
 
# Purpose:
4
 
#
5
 
# Author:      Riaan Booysen
6
 
#
7
 
# Created:     2001/06/08
8
 
# RCS-ID:      $Id: PrefsExplorer.py,v 1.15 2004/08/16 13:22:00 riaan Exp $
9
 
# Copyright:   (c) 2001 - 2004
10
 
# Licence:     GPL
11
 
#-----------------------------------------------------------------------------
12
 
print 'importing Explorers.PrefsExplorer'
13
 
import os, sys, glob, pprint, imp
14
 
import types
15
 
 
16
 
from wxPython import wx
17
 
true=1;false=0
18
 
 
19
 
import Preferences, Utils, Plugins
20
 
 
21
 
import ExplorerNodes
22
 
from Models import EditorHelper
23
 
from Views import STCStyleEditor
24
 
import methodparse, relpath
25
 
 
26
 
class PreferenceGroupNode(ExplorerNodes.ExplorerNode):
27
 
    """ Represents a group of preference collections """
28
 
    protocol = 'prefs.group'
29
 
    defName = 'PrefsGroup'
30
 
    def __init__(self, name, parent):
31
 
        ExplorerNodes.ExplorerNode.__init__(self, name, name, None,
32
 
              EditorHelper.imgPrefsFolder, None)
33
 
 
34
 
        self.vetoSort = true
35
 
        self.preferences = []
36
 
 
37
 
    def isFolderish(self):
38
 
        return true
39
 
 
40
 
    def openList(self):
41
 
        return self.preferences
42
 
 
43
 
    def notifyBeginLabelEdit(self, event):
44
 
        event.Veto()
45
 
 
46
 
class BoaPrefGroupNode(PreferenceGroupNode):
47
 
    """ The Preference node in the Explorer """
48
 
    protocol = 'boa.prefs.group'
49
 
    customPrefs = [] # list of tuples ('name', 'file')
50
 
    def __init__(self, parent):
51
 
        PreferenceGroupNode.__init__(self, 'Preferences', parent)
52
 
        self.bold = true
53
 
 
54
 
        prefImgIdx = EditorHelper.imgSystemObj
55
 
        stcPrefImgIdx = EditorHelper.imgPrefsSTCStyles
56
 
 
57
 
        self.source_pref = PreferenceGroupNode('Source', self)
58
 
 
59
 
        self.source_pref.preferences = [
60
 
            UsedModuleSrcBsdPrefColNode('Default settings',
61
 
                Preferences.exportedSTCProps, os.path.join(Preferences.rcPath,
62
 
                'prefs.rc.py'), prefImgIdx, self, Preferences, true)]
63
 
 
64
 
        for name, lang, STCClass, stylesFile in ExplorerNodes.langStyleInfoReg:
65
 
            if not os.path.isabs(stylesFile):
66
 
                stylesFile = os.path.join(Preferences.rcPath, stylesFile)
67
 
            self.source_pref.preferences.append(STCStyleEditPrefsCollNode(
68
 
                  name, lang, STCClass, stylesFile, stcPrefImgIdx, self))
69
 
        self.preferences.append(self.source_pref)
70
 
 
71
 
        self.general_pref = UsedModuleSrcBsdPrefColNode('General',
72
 
            Preferences.exportedProperties, os.path.join(Preferences.rcPath,
73
 
            'prefs.rc.py'), prefImgIdx, self, Preferences)
74
 
        self.preferences.append(self.general_pref)
75
 
 
76
 
        self.platform_pref = UsedModuleSrcBsdPrefColNode('Platform specific',
77
 
            Preferences.exportedProperties2, os.path.join(Preferences.rcPath,
78
 
            'prefs.%s.rc.py' % (wx.wxPlatform == '__WXMSW__' and 'msw' or 'gtk')),
79
 
            prefImgIdx, self, Preferences)
80
 
        self.preferences.append(self.platform_pref)
81
 
 
82
 
        self.keys_pref = KeyDefsSrcPrefColNode('Key bindings', ('*',),
83
 
            os.path.join(Preferences.rcPath, 'prefs.keys.rc.py'), prefImgIdx,
84
 
            self, Preferences.keyDefs)
85
 
        self.preferences.append(self.keys_pref)
86
 
 
87
 
        for name, filename in self.customPrefs:
88
 
            if not os.path.isabs(filename):
89
 
                filename = os.path.join(Preferences.rcPath, filename)
90
 
            self.preferences.append(UsedModuleSrcBsdPrefColNode(name,
91
 
            ('*',), filename, prefImgIdx, self, Preferences))
92
 
 
93
 
##        self.pychecker_pref = SourceBasedPrefColNode('PyChecker',
94
 
##            ('*',), Preferences.pyPath+'/.pycheckrc', prefImgIdx, self)
95
 
##        self.preferences.append(self.pychecker_pref)
96
 
 
97
 
        self.plugin_pref = PreferenceGroupNode('Plug-ins', self)
98
 
 
99
 
        self.core_plugpref = UsedModuleSrcBsdPrefColNode('Core support',
100
 
            Preferences.exportedCorePluginProps, os.path.join(Preferences.rcPath,
101
 
            'prefs.rc.py'), prefImgIdx, self, Preferences, true)
102
 
        self.plugin_plugpref = UsedModuleSrcBsdPrefColNode('Preferences', Preferences.exportedPluginProps,#('*',), 
103
 
            os.path.join(Preferences.rcPath, 'prefs.plug-ins.rc.py'), prefImgIdx, 
104
 
            self, Preferences, true)
105
 
        self.files_plugpref = PluginFilesGroupNode()
106
 
        self.transp_plugpref = PreferenceGroupNode('Transports', self)
107
 
        self.transp_plugpref.preferences = [
108
 
            TransportPluginsLoadOrderGroupNode(),
109
 
            TransportPluginsTreeDisplayOrderGroupNode(),
110
 
        ]
111
 
 
112
 
        self.plugin_pref.preferences = [
113
 
            self.files_plugpref,
114
 
            self.transp_plugpref,
115
 
            self.core_plugpref,
116
 
            self.plugin_plugpref,
117
 
        ]
118
 
 
119
 
        self.preferences.insert(1, self.plugin_pref)
120
 
 
121
 
        self.help_pref = HelpConfigBooksPGN()
122
 
 
123
 
        self.preferences.insert(2, self.help_pref)
124
 
 
125
 
 
126
 
class PreferenceCollectionNode(ExplorerNodes.ExplorerNode):
127
 
    """ Represents an inspectable preference collection """
128
 
    protocol = 'prefs'
129
 
    def __init__(self, name, props, resourcepath, imgIdx, parent):
130
 
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
131
 
              imgIdx, None, props)
132
 
 
133
 
    def open(self, editor):
134
 
        """ Populate inspector with preference items """
135
 
        comp = PreferenceCompanion(self.name, self)
136
 
        comp.updateProps()
137
 
 
138
 
        # Select in inspector
139
 
        editor.inspector.restore()
140
 
        if editor.inspector.pages.GetSelection() != 1:
141
 
            editor.inspector.pages.SetSelection(1)
142
 
        editor.inspector.selectObject(comp, false)
143
 
        return None, None
144
 
 
145
 
    def isFolderish(self):
146
 
        return false
147
 
 
148
 
    def load(self):
149
 
        raise 'Not implemented'
150
 
 
151
 
    def save(self, filename, data):
152
 
        pass
153
 
 
154
 
    def notifyBeginLabelEdit(self, event):
155
 
        event.Veto()
156
 
 
157
 
class STCStyleEditPrefsCollNode(PreferenceCollectionNode):
158
 
    protocol = 'stc.prefs'
159
 
    def __init__(self, name, lang, STCclass, resourcepath, imgIdx, parent):
160
 
        PreferenceCollectionNode.__init__(self, name, {}, resourcepath, imgIdx, parent)
161
 
        self.language = lang
162
 
        self.STCclass = STCclass
163
 
 
164
 
    def open(self, editor):
165
 
        # build list of all open STC's in the Editor
166
 
        openSTCViews = []
167
 
        for modPge in editor.modules.values():
168
 
            for view in modPge.model.views.values():
169
 
                if isinstance(view, self.STCclass):
170
 
                    openSTCViews.append(view)
171
 
        
172
 
        # also check the shell
173
 
        if Preferences.psPythonShell == 'Shell':
174
 
            if isinstance(editor.shell, self.STCclass):
175
 
                openSTCViews.append(editor.shell)
176
 
        #elif Preferences.psPythonShell == 'PyCrust':
177
 
        #    if self.language == 'python':
178
 
        #        openSTCViews.append(editor.shell.shellWin)
179
 
        
180
 
        dlg = STCStyleEditor.STCStyleEditDlg(editor, self.name, self.language,
181
 
              self.resourcepath, openSTCViews)
182
 
        try: dlg.ShowModal()
183
 
        finally: dlg.Destroy()
184
 
 
185
 
        return None, None
186
 
 
187
 
    def getURI(self):
188
 
        return '%s://%s' %(PreferenceCollectionNode.getURI(self), self.language)
189
 
 
190
 
 
191
 
class SourceBasedPrefColNode(PreferenceCollectionNode):
192
 
    """ Preference collection represented by the global names in python module
193
 
 
194
 
    Only names which are also defined in properties are returned
195
 
    except when properties is a special match all tuple; ('*',)
196
 
 
197
 
    This only applies to names assigned to values ( x = 123 ) not to global
198
 
    names defined by classes functions and imports.
199
 
    """
200
 
    def __init__(self, name, props, resourcepath, imgIdx, parent, showBreaks=true):
201
 
        PreferenceCollectionNode.__init__(self, name, props, resourcepath,
202
 
              imgIdx, parent)
203
 
        self.showBreakLines = showBreaks
204
 
 
205
 
    def load(self):
206
 
        # All preferences are local
207
 
        import moduleparse
208
 
 
209
 
        module = moduleparse.Module(self.name,
210
 
              open(self.resourcepath).readlines())
211
 
 
212
 
        values = []
213
 
        comments = []
214
 
        options = []
215
 
        # keep only names defined in the property list
216
 
        for name in module.global_order[:]:
217
 
            if name[0] == '_' or self.properties != ('*',) and \
218
 
                  name not in self.properties:
219
 
                module.global_order.remove(name)
220
 
                del module.globals[name]
221
 
            else:
222
 
                # XXX Should handle multiline assign
223
 
                code = '\n'.join(module.source[\
224
 
                      module.globals[name].start-1 : \
225
 
                      module.globals[name].end])
226
 
 
227
 
                # Extract value
228
 
                s = code.find('=')
229
 
                if s != -1:
230
 
                    values.append(code[s+1:].strip())
231
 
                else:
232
 
                    values.append('')
233
 
 
234
 
                # Read possible comment/help or options
235
 
                comment = []
236
 
                option = ''
237
 
                idx = module.globals[name].start-2
238
 
                while idx >= 0:
239
 
                    line = module.source[idx].strip()
240
 
                    if len(line) > 11 and line[:11] == '## options:':
241
 
                        option = line[11:].strip()
242
 
                        idx = idx - 1
243
 
                    elif len(line) > 8 and line[:8] == '## type:':
244
 
                        option = '##'+line[8:].strip()
245
 
                        idx = idx - 1
246
 
                    elif line and line[0] == '#':
247
 
                        comment.append(line[1:].lstrip())
248
 
                        idx = idx - 1
249
 
                    else:
250
 
                        break
251
 
                comment.reverse()
252
 
                comments.append('\n'.join(comment))
253
 
                options.append(option)
254
 
 
255
 
        if self.showBreakLines: breaks = module.break_lines
256
 
        else: breaks = {}
257
 
 
258
 
        return (module.global_order, values, module.globals, comments, options,
259
 
                breaks)
260
 
 
261
 
    def save(self, filename, data):
262
 
        """ Updates one property """
263
 
        src = open(self.resourcepath).readlines()
264
 
        src[data[2].start-1] = '%s = %s\n' % (data[0], data[1])
265
 
        open(self.resourcepath, 'w').writelines(src)
266
 
 
267
 
class UsedModuleSrcBsdPrefColNode(SourceBasedPrefColNode):
268
 
    """ Also update the value of a global attribute of an imported module """
269
 
    def __init__(self, name, props, resourcepath, imgIdx, parent, module,
270
 
          showBreaks=true):
271
 
        SourceBasedPrefColNode.__init__(self, name, props, resourcepath, imgIdx,
272
 
              parent, showBreaks)
273
 
        self.module = module
274
 
 
275
 
    def save(self, filename, data):
276
 
        SourceBasedPrefColNode.save(self, filename, data)
277
 
        if hasattr(self.module, data[0]):
278
 
            setattr(self.module, data[0], eval(data[1], vars(Preferences)))
279
 
 
280
 
class KeyDefsSrcPrefColNode(PreferenceCollectionNode):
281
 
    """ Preference collection representing the key bindings """
282
 
    def __init__(self, name, props, resourcepath, imgIdx, parent, keyDefs):
283
 
        PreferenceCollectionNode.__init__(self, name, props, resourcepath,
284
 
              imgIdx, parent)
285
 
        self.showBreakLines = true
286
 
        self._editor = None
287
 
 
288
 
    def open(self, editor):
289
 
        PreferenceCollectionNode.open(self, editor)
290
 
        self._editor = editor
291
 
 
292
 
    def load(self):
293
 
        import moduleparse
294
 
 
295
 
        src = open(self.resourcepath).readlines()
296
 
        module = moduleparse.Module(self.name, src)
297
 
 
298
 
        # find keydefs
299
 
        keydefs = {}
300
 
        names = []
301
 
        values = []
302
 
        start = end = idx = -1
303
 
        for line in src:
304
 
            idx = idx + 1
305
 
            line = line.strip()
306
 
            if line == 'keyDefs = {':
307
 
                start = idx
308
 
            elif start != -1 and line:
309
 
                if line[-1] == '}':
310
 
                    end = idx
311
 
                    break
312
 
                elif line[0] != '#':
313
 
                    colon = line.find(':')
314
 
                    if colon == -1: raise Exception('Invalid KeyDef item: %s'%line)
315
 
                    name = line[:colon].rstrip()[1:-1]
316
 
                    val = line[colon+1:].lstrip()
317
 
                    keydefs[name] = moduleparse.CodeBlock(val, idx+1, idx+1)
318
 
                    names.append(name)
319
 
                    values.append(val)
320
 
 
321
 
        return (names, values, keydefs, ['']*len(keydefs),
322
 
              ['## keydef']*len(keydefs), module.break_lines)
323
 
 
324
 
    def save(self, filename, data):
325
 
        """ Updates one key:val in keydefs dict """
326
 
 
327
 
        # Update source file
328
 
        src = open(self.resourcepath).readlines()
329
 
        src[data[2].start-1] = \
330
 
              "  '%s'%s: %s\n" % (data[0], (12 - len(data[0]))*' ', data[1])
331
 
        open(self.resourcepath, 'w').writelines(src)
332
 
        # Update dictionary
333
 
        Preferences.keyDefs[data[0]] = eval(data[1], vars(Preferences))[0]
334
 
        # Update editor menus
335
 
        self._editor.setupToolBar()
336
 
        self._editor.updateStaticMenuShortcuts()
337
 
        self._editor.shell.bindShortcuts()
338
 
 
339
 
 
340
 
class ConfigBasedPrefsColNode(PreferenceCollectionNode):
341
 
    """ Preferences driven by config files """
342
 
    pass
343
 
 
344
 
 
345
 
#---Companions------------------------------------------------------------------
346
 
 
347
 
from PropEdit import PropertyEditors, InspectorEditorControls
348
 
 
349
 
class KeyDefConfPropEdit(PropertyEditors.ConfPropEdit):
350
 
    def inspectorEdit(self):
351
 
        self.editorCtrl = InspectorEditorControls.ButtonIEC(self, self.value)
352
 
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)
353
 
 
354
 
    def edit(self, event):
355
 
        import KeyDefsDlg
356
 
        dlg = KeyDefsDlg.KeyDefsDialog(self.parent, self.name, self.value)
357
 
        try:
358
 
            if dlg.ShowModal() == wx.wxID_OK:
359
 
                self.editorCtrl.value = dlg.result
360
 
                self.inspectorPost(false)
361
 
        finally:
362
 
            dlg.Destroy()
363
 
 
364
 
    def getDisplayValue(self):
365
 
        try:
366
 
            return eval(self.value, wx.__dict__)[0][2]
367
 
        except Exception, err:
368
 
            return str(err)
369
 
 
370
 
class PreferenceCompanion(ExplorerNodes.ExplorerCompanion):
371
 
    def __init__(self, name, prefNode, ):
372
 
        ExplorerNodes.ExplorerCompanion.__init__(self, name)
373
 
        self.prefNode = prefNode
374
 
 
375
 
        self._breaks = {}
376
 
 
377
 
    typeMap = {}
378
 
    customTypeMap = {'filepath': PropertyEditors.FilepathConfPropEdit,
379
 
                     'dirpath': PropertyEditors.DirpathConfPropEdit,
380
 
                     'keydef': KeyDefConfPropEdit}
381
 
    def getPropEditor(self, prop):
382
 
        # XXX Using name equality to identify _breaks' prop edit is ugly !
383
 
        for aProp in self.propItems:
384
 
            if aProp[0] == prop: break
385
 
        else:
386
 
            raise Exception('Property "%s" not found'%prop)
387
 
 
388
 
        srcVal = aProp[1]
389
 
        opts = aProp[4]
390
 
 
391
 
        if prop in self._breaks.values():
392
 
            return None
393
 
 
394
 
        if opts:
395
 
            if opts[:2] == '##':
396
 
                return self.customTypeMap.get(opts[2:].strip(), None)
397
 
            else:
398
 
                return PropertyEditors.EnumConfPropEdit
399
 
 
400
 
        if srcVal.lower() in ('true', 'false'):
401
 
            return PropertyEditors.BoolConfPropEdit
402
 
 
403
 
        try:
404
 
            val = eval(srcVal, vars(Preferences))
405
 
        except Exception, error:
406
 
            return PropertyEditors.StrConfPropEdit
407
 
 
408
 
        if isinstance(val, wx.wxColour):
409
 
            return PropertyEditors.ColourConfPropEdit
410
 
        return self.typeMap.get(type(val), PropertyEditors.StrConfPropEdit)
411
 
 
412
 
    def getPropertyHelp(self, propName):
413
 
        for prop in self.propItems:
414
 
            if prop[0] == propName: return prop[3]
415
 
        else:
416
 
            return propName
417
 
 
418
 
    def getPropertyItems(self):
419
 
        order, vals, props, comments, options, self._breaks = self.prefNode.load()
420
 
 
421
 
        # remove empty break lines (-----------------------)
422
 
        for lineNo in self._breaks.keys():
423
 
            if not self._breaks[lineNo]:
424
 
                del self._breaks[lineNo]
425
 
 
426
 
        breakLinenos = self._breaks.keys()
427
 
        breakLinenos.sort()
428
 
        if len(breakLinenos):
429
 
            breaksIdx = 0
430
 
        else:
431
 
            breaksIdx = None
432
 
 
433
 
        res = []
434
 
        for name, value, comment, option in map(None, order, vals, comments, options):
435
 
            if breaksIdx is not None:
436
 
                # find closest break above property
437
 
                while breaksIdx < len(breakLinenos)-1 and \
438
 
                      props[name].start > breakLinenos[breaksIdx+1]:
439
 
                    breaksIdx += 1
440
 
                
441
 
                if breaksIdx >= len(breakLinenos):
442
 
                    breaksIdx = None
443
 
                
444
 
                if breaksIdx is not None and props[name].start > breakLinenos[breaksIdx]:
445
 
                    res.append( (self._breaks[breakLinenos[breaksIdx]], '', None, '', '') )
446
 
                    breaksIdx += 1
447
 
                    #if breaksIdx == len(self._breaks) -1:
448
 
                    #    breaksIdx = None
449
 
                    #else:
450
 
                    #    breaksIdx = breaksIdx + 1
451
 
            res.append( (name, value, props[name], comment, option) )
452
 
        return res
453
 
 
454
 
    def setPropHook(self, name, value, oldProp):
455
 
        # XXX validate etc.
456
 
        try:
457
 
            eval(value, vars(Preferences))
458
 
        except Exception, error:
459
 
            wx.wxLogError('Error: '+str(error))
460
 
            return false
461
 
        else:
462
 
            newProp = (name, value) + oldProp[2:]
463
 
            self.prefNode.save(name, newProp)
464
 
            return true
465
 
 
466
 
    def persistedPropVal(self, name, setterName):
467
 
        if name in self._breaks.values():
468
 
            return 'PROP_CATEGORY'
469
 
        else:
470
 
            return None
471
 
 
472
 
    def getPropOptions(self, name):
473
 
        for prop in self.propItems:
474
 
            if prop[0] == name:
475
 
                strOpts = prop[4]
476
 
                if strOpts and strOpts[:2] != '##':
477
 
                    return self.eval(strOpts)
478
 
                else:
479
 
                    return ()
480
 
        else:
481
 
            return ()
482
 
 
483
 
    def getPropNames(self, name):
484
 
        for prop in self.propItems:
485
 
            if prop[0] == name:
486
 
                strOpts = prop[4]
487
 
                if strOpts and strOpts[:2] != '##':
488
 
                    return methodparse.safesplitfields(strOpts, ',')
489
 
                else: return ()
490
 
        else:
491
 
            return ()
492
 
 
493
 
    def eval(self, expr):
494
 
        import PaletteMapping
495
 
        return PaletteMapping.evalCtrl(expr, vars(Preferences))
496
 
 
497
 
##    def GetProp(self, name):
498
 
##        ExplorerNodes.ExplorerCompanion.GetProp(self, name)
499
 
##        return self.findProp(name)[0][1]
500
 
 
501
 
class CorePluginsGroupNode(PreferenceGroupNode):
502
 
    """ """
503
 
    protocol = 'prefs.group.plug-in.core'
504
 
    defName = 'CorePluginPrefsGroup'
505
 
    def __init__(self):
506
 
        name = 'Core support'
507
 
        PreferenceGroupNode.__init__(self, name, None)
508
 
 
509
 
        self.vetoSort = true
510
 
        self.preferences = []
511
 
 
512
 
    def isFolderish(self):
513
 
        return true
514
 
 
515
 
    def openList(self):
516
 
        return self.preferences
517
 
 
518
 
    def notifyBeginLabelEdit(self, event):
519
 
        event.Veto()
520
 
 
521
 
def getPluginSection(pluginFile):
522
 
    pluginPath = os.path.dirname(pluginFile)
523
 
    return Preferences.pluginSections[
524
 
              Preferences.pluginPaths.index(pluginPath)]
525
 
    
526
 
class PluginFileExplNode(ExplorerNodes.ExplorerNode):
527
 
    """  """
528
 
    def __init__(self, name, enabled, status, resourcepath, imgIdx):
529
 
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
530
 
              imgIdx, None, {})
531
 
        self.pluginEnabled = enabled
532
 
        self.pluginStatus = status
533
 
 
534
 
    def open(self, editor):
535
 
        """  """
536
 
        if self.pluginEnabled:
537
 
            msg = 'Disable'
538
 
        else:
539
 
            msg = 'Enable'
540
 
 
541
 
        if wx.wxMessageBox('%s %s?'%(msg, self.name), 'Confirm Toggle Plug-in',
542
 
              wx.wxYES_NO | wx.wxICON_QUESTION) == wx.wxYES:
543
 
            section = getPluginSection(self.resourcepath)
544
 
            ordered, disabled = Plugins.readPluginsState(section)
545
 
 
546
 
            if self.pluginEnabled:
547
 
                disabled.append(self.name)
548
 
            else:
549
 
                try:
550
 
                    disabled.remove(self.name)
551
 
                except ValueError:
552
 
                    pass
553
 
 
554
 
            #Plugins.writeInitPluginGlobals(initPluginPath, initPluginGlobals)
555
 
            Plugins.writePluginsState(section, ordered, disabled)
556
 
 
557
 
            editor.explorer.list.refreshCurrent()
558
 
 
559
 
        return None, None
560
 
 
561
 
    def getURI(self):
562
 
        return '%s (%s)'%(ExplorerNodes.ExplorerNode.getURI(self),
563
 
                          self.pluginStatus)
564
 
 
565
 
    def isFolderish(self):
566
 
        return false
567
 
 
568
 
    def notifyBeginLabelEdit(self, event):
569
 
        event.Veto()
570
 
 
571
 
    def changeOrder(self, direction):
572
 
        section = getPluginSection(self.resourcepath)
573
 
        #initPluginPath = os.path.dirname(self.resourcepath)
574
 
        ordered, disabled = Plugins.readPluginsState(section)
575
 
        #ordered = initPluginGlobals['__ordered__']
576
 
        try:
577
 
            idx = ordered.index(self.name)
578
 
        except ValueError:
579
 
            idx = len(ordered)+1
580
 
        else:
581
 
            del ordered[idx]
582
 
        idx = max(idx + direction, 0)
583
 
        if idx <= len(ordered):
584
 
            ordered.insert(idx, self.name)
585
 
        #Plugins.writeInitPluginGlobals(initPluginPath, initPluginGlobals)
586
 
        Plugins.writePluginsState(section, ordered, disabled)
587
 
 
588
 
 
589
 
class PluginFilesGroupNode(PreferenceGroupNode):
590
 
    """ Represents a group of preference collections """
591
 
    protocol = 'prefs.group.plug-in.files'
592
 
    defName = 'PluginFilesPrefsGroup'
593
 
    def __init__(self):
594
 
        name = 'Plug-in files'
595
 
        PreferenceGroupNode.__init__(self, name, None)
596
 
 
597
 
    def openList(self):
598
 
        res = []
599
 
        splitext = os.path.splitext
600
 
        for filename, ordered, enabled in Plugins.buildPluginExecList():
601
 
            if os.path.basename(filename) == '__init__.plug-in.py':
602
 
                continue
603
 
 
604
 
            name = splitext(splitext(os.path.basename(filename))[0])[0]
605
 
            if not enabled:
606
 
                name = splitext(name)[0]
607
 
                status = 'Disabled'
608
 
                imgIdx = EditorHelper.imgSystemObjDisabled
609
 
            else:
610
 
                fn = filename.lower()
611
 
                if Preferences.failedPlugins.has_key(fn):
612
 
                    kind, msg = Preferences.failedPlugins[fn]
613
 
                    if kind == 'Skipped':
614
 
                        status = 'Skipped plug-in: %s'% msg
615
 
                        imgIdx = EditorHelper.imgSystemObjPending
616
 
                    else:
617
 
                        status = 'Broken plug-in: %s'% msg
618
 
                        imgIdx = EditorHelper.imgSystemObjBroken
619
 
                elif fn in Preferences.installedPlugins:
620
 
                    if ordered:
621
 
                        status = 'Installed, ordered'
622
 
                        imgIdx = EditorHelper.imgSystemObjOrdered
623
 
                    else:
624
 
                        status = 'Installed'
625
 
                        imgIdx = EditorHelper.imgSystemObj
626
 
                else:
627
 
                    status = 'Pending restart'
628
 
                    imgIdx = EditorHelper.imgSystemObjPending
629
 
 
630
 
            res.append(PluginFileExplNode(name, enabled, status, filename, imgIdx))
631
 
        return res
632
 
 
633
 
 
634
 
class PluginFilesGroupNodeController(ExplorerNodes.Controller):
635
 
    moveUpBmp = 'Images/Shared/up.png'
636
 
    moveDownBmp = 'Images/Shared/down.png'
637
 
 
638
 
    itemDescr = 'item'
639
 
 
640
 
    def __init__(self, editor, list, inspector, controllers):
641
 
        ExplorerNodes.Controller.__init__(self, editor)
642
 
        self.list = list
643
 
        self.menu = wx.wxMenu()
644
 
 
645
 
        [wxID_PF_TOGGLE, wxID_PF_OPEN, wxID_PF_UP, wxID_PF_DOWN] = Utils.wxNewIds(4)
646
 
 
647
 
        self.transpMenuDef = [ (wxID_PF_TOGGLE, 'Toggle Enable/Disabled',
648
 
                                self.OnToggleState, '-'),
649
 
                               (wxID_PF_OPEN, 'Open plug-in file',
650
 
                                self.OnOpenPlugin, '-'),
651
 
                               (-1, '-', None, ''),
652
 
                               (wxID_PF_UP, 'Move up',
653
 
                                self.OnMovePluginUp, self.moveUpBmp),
654
 
                               (wxID_PF_DOWN, 'Move down',
655
 
                                self.OnMovePluginDown, self.moveDownBmp),
656
 
                             ]
657
 
 
658
 
        self.setupMenu(self.menu, self.list, self.transpMenuDef)
659
 
        self.toolbarMenus = [self.transpMenuDef]
660
 
 
661
 
    def destroy(self):
662
 
        self.transpMenuDef = []
663
 
        self.toolbarMenus = []
664
 
        self.menu.Destroy()
665
 
 
666
 
    def OnToggleState(self, event):
667
 
        if self.list.node:
668
 
            ms = self.list.getMultiSelection()
669
 
            nodes = self.getNodesForSelection(ms)
670
 
            for node in nodes:
671
 
                node.open(self.editor)
672
 
 
673
 
    def OnOpenPlugin(self, event):
674
 
        if self.list.node:
675
 
            ms = self.list.getMultiSelection()
676
 
            nodes = self.getNodesForSelection(ms)
677
 
            for node in nodes:
678
 
                self.editor.openOrGotoModule(node.resourcepath)
679
 
 
680
 
    def OnMovePluginUp(self, event):
681
 
        if self.list.node:
682
 
            ms = self.list.getMultiSelection()
683
 
            nodes = self.getNodesForSelection(ms)
684
 
            if len(nodes) != 1:
685
 
                wx.wxLogError('Can only move 1 at a time')
686
 
            else:
687
 
                node = nodes[0]
688
 
                idx = self.list.items.index(node)
689
 
                if idx == 0:
690
 
                    wx.wxLogError('Already at the beginning')
691
 
                else:
692
 
                    name = node.name
693
 
                    node.changeOrder(-1)
694
 
                    self.list.refreshCurrent()
695
 
                    self.list.selectItemNamed(name)
696
 
 
697
 
    def OnMovePluginDown(self, event):
698
 
        if self.list.node:
699
 
            ms = self.list.getMultiSelection()
700
 
            nodes = self.getNodesForSelection(ms)
701
 
            if len(nodes) != 1:
702
 
                wx.wxLogError('Can only move 1 at a time')
703
 
            else:
704
 
                node = nodes[0]
705
 
                idx = self.list.items.index(node)
706
 
##                if idx >= len(self.list.items) -1:
707
 
##                    wx.wxLogError('Already at the end')
708
 
##                else:
709
 
                name = node.name
710
 
                node.changeOrder(1)
711
 
                self.list.refreshCurrent()
712
 
                self.list.selectItemNamed(name)
713
 
 
714
 
 
715
 
 
716
 
class TransportPluginExplNode(ExplorerNodes.ExplorerNode):
717
 
    """  """
718
 
    protocol = 'transport'
719
 
    def __init__(self, name, status, imgIdx):
720
 
        ExplorerNodes.ExplorerNode.__init__(self, name, '%s (%s)'%(name, status),
721
 
              None, imgIdx, None, {})
722
 
        self.status = status
723
 
 
724
 
 
725
 
    def open(self, editor):
726
 
        return None, None
727
 
 
728
 
class TransportPluginsController(ExplorerNodes.Controller):
729
 
    addItemBmp = 'Images/Shared/NewItem.png'
730
 
    removeItemBmp = 'Images/Shared/DeleteItem.png'
731
 
    moveUpBmp = 'Images/Shared/up.png'
732
 
    moveDownBmp = 'Images/Shared/down.png'
733
 
 
734
 
    itemDescr = 'item'
735
 
 
736
 
    def __init__(self, editor, list, inspector, controllers):
737
 
        ExplorerNodes.Controller.__init__(self, editor)
738
 
        self.list = list
739
 
        self.menu = wx.wxMenu()
740
 
 
741
 
        [wxID_TP_NEW, wxID_TP_DEL, wxID_TP_UP, wxID_TP_DOWN] = Utils.wxNewIds(4)
742
 
 
743
 
        self.transpMenuDef = [ (wxID_TP_NEW, 'Add new '+self.itemDescr,
744
 
                                self.OnNewTransport, self.addItemBmp),
745
 
                               (wxID_TP_DEL, 'Remove '+self.itemDescr,
746
 
                                self.OnDeleteTransport, self.removeItemBmp),
747
 
                               (-1, '-', None, ''),
748
 
                               (wxID_TP_UP, 'Move up',
749
 
                                self.OnMoveTransportUp, self.moveUpBmp),
750
 
                               (wxID_TP_DOWN, 'Move down',
751
 
                                self.OnMoveTransportDown, self.moveDownBmp),
752
 
                             ]
753
 
 
754
 
        self.setupMenu(self.menu, self.list, self.transpMenuDef)
755
 
        self.toolbarMenus = [self.transpMenuDef]
756
 
 
757
 
    def destroy(self):
758
 
        self.transpMenuDef = []
759
 
        self.toolbarMenus = []
760
 
        self.menu.Destroy()
761
 
 
762
 
    def editorUpdateNotify(self, info=''):
763
 
        self.OnReloadItems()
764
 
 
765
 
    def OnReloadItems(self, event=None):
766
 
        if self.list.node:
767
 
            self.list.refreshCurrent()
768
 
 
769
 
    def moveTransport(self, node, idx, direc):
770
 
        names = []
771
 
        for item in self.list.items:
772
 
            names.append(item.name)
773
 
 
774
 
        name = names[idx]
775
 
        del names[idx]
776
 
        names.insert(idx + direc, name)
777
 
 
778
 
        self.list.node.updateOrder(names)
779
 
 
780
 
        self.list.refreshCurrent()
781
 
        self.list.selectItemByIdx(idx+direc+1)
782
 
 
783
 
    def OnMoveTransportUp(self, event):
784
 
        if self.list.node:
785
 
            ms = self.list.getMultiSelection()
786
 
            nodes = self.getNodesForSelection(ms)
787
 
            if len(nodes) != 1:
788
 
                wx.wxLogError('Can only move 1 at a time')
789
 
            else:
790
 
                node = nodes[0]
791
 
                idx = self.list.items.index(node)
792
 
                if idx == 0:
793
 
                    wx.wxLogError('Already at the beginning')
794
 
                else:
795
 
                    self.moveTransport(node, idx, -1)
796
 
 
797
 
    def OnMoveTransportDown(self, event):
798
 
        if self.list.node:
799
 
            ms = self.list.getMultiSelection()
800
 
            nodes = self.getNodesForSelection(ms)
801
 
            if len(nodes) != 1:
802
 
                wx.wxLogError('Can only move 1 at a time')
803
 
            else:
804
 
                node = nodes[0]
805
 
                idx = self.list.items.index(node)
806
 
                if idx >= len(self.list.items) -1:
807
 
                    wx.wxLogError('Already at the end')
808
 
                else:
809
 
                    self.moveTransport(node, idx, 1)
810
 
 
811
 
    def OnNewTransport(self, event):
812
 
        pass
813
 
 
814
 
    def OnDeleteTransport(self, event):
815
 
        pass
816
 
 
817
 
class TransportPluginsLoadOrderController(TransportPluginsController):
818
 
    itemDescr = 'Transport module'
819
 
 
820
 
    def OnNewTransport(self, event):
821
 
        dlg = wx.wxTextEntryDialog(self.list, 'Enter the fully qualified Python '\
822
 
               'object path to \nthe Transport module. E.g. Explorers.FileExplorer',
823
 
               'New Transport', '')
824
 
        try:
825
 
            if dlg.ShowModal() != wx.wxID_OK:
826
 
                return
827
 
            transportModulePath = dlg.GetValue()
828
 
        finally:
829
 
            dlg.Destroy()
830
 
 
831
 
        if not self.list.node.checkValidModulePath(transportModulePath):
832
 
            if wx.wxMessageBox('Cannot locate the specified module path,\n'\
833
 
                         'are you sure you want to continue?',
834
 
                         'Module not found',
835
 
                         wx.wxYES_NO | wx.wxICON_EXCLAMATION) == wx.wxNO:
836
 
                return
837
 
 
838
 
        names = []
839
 
        for item in self.list.items:
840
 
            names.append(item.name)
841
 
 
842
 
        names.append(transportModulePath)
843
 
 
844
 
        self.list.node.updateOrder(names)
845
 
 
846
 
        self.list.refreshCurrent()
847
 
 
848
 
    def OnDeleteTransport(self, event):
849
 
        selNames = self.list.getMultiSelection()
850
 
        nodes = self.getNodesForSelection(selNames)
851
 
 
852
 
        names = []
853
 
        for item in self.list.items:
854
 
            names.append(item.name)
855
 
 
856
 
        for item in nodes:
857
 
            names.remove(item.name)
858
 
 
859
 
        self.list.node.updateOrder(names)
860
 
 
861
 
        self.list.refreshCurrent()
862
 
 
863
 
class TransportPluginsTreeDisplayOrderController(TransportPluginsController):
864
 
    itemDescr = 'Transports tree node'
865
 
 
866
 
    def OnNewTransport(self, event):
867
 
        dlg = wx.wxTextEntryDialog(self.list, 'Enter the protocol identifier. E.g. '\
868
 
               'ftp, ssh', 'New Transports Tree Node', '')
869
 
        try:
870
 
            if dlg.ShowModal() != wx.wxID_OK:
871
 
                return
872
 
            protocol = dlg.GetValue()
873
 
        finally:
874
 
            dlg.Destroy()
875
 
 
876
 
        names = []
877
 
        for item in self.list.items:
878
 
            names.append(item.name)
879
 
 
880
 
        names.append(protocol)
881
 
 
882
 
        self.list.node.updateOrder(names)
883
 
        self.list.node.checkConfigEntry(protocol)
884
 
 
885
 
        self.list.refreshCurrent()
886
 
 
887
 
    def OnDeleteTransport(self, event):
888
 
        selNames = self.list.getMultiSelection()
889
 
        nodes = self.getNodesForSelection(selNames)
890
 
 
891
 
        names = []
892
 
        for item in self.list.items:
893
 
            names.append(item.name)
894
 
 
895
 
        for item in nodes:
896
 
            names.remove(item.name)
897
 
            self.list.node.clearEmptyConfigEntry(item.name)
898
 
 
899
 
        self.list.node.updateOrder(names)
900
 
 
901
 
        self.list.refreshCurrent()
902
 
 
903
 
 
904
 
class TransportPluginsLoadOrderGroupNode(PreferenceGroupNode):
905
 
    """  """
906
 
    protocol = 'prefs.group.plug-in.transport.load-order'
907
 
    defName = 'TransportPluginsPrefsGroup'
908
 
    def __init__(self):
909
 
        name = 'Loading order'
910
 
        PreferenceGroupNode.__init__(self, name, None)
911
 
 
912
 
    def openList(self):
913
 
        conf = Utils.createAndReadConfig('Explorer')
914
 
 
915
 
        modules = eval(conf.get('explorer', 'installedtransports'), {})
916
 
        assert isinstance(modules, types.ListType)
917
 
 
918
 
        res = []
919
 
        for mod in modules:
920
 
            if mod in ExplorerNodes.installedModules:
921
 
                status = 'Installed'
922
 
                imgIdx = EditorHelper.imgSystemObjOrdered
923
 
            elif mod in ExplorerNodes.failedModules.keys():
924
 
                status = 'Broken: %s'%ExplorerNodes.failedModules[mod]
925
 
                imgIdx = EditorHelper.imgSystemObjBroken
926
 
            else:
927
 
                status = 'Pending restart'
928
 
                imgIdx = EditorHelper.imgSystemObjPending
929
 
 
930
 
            res.append(TransportPluginExplNode(mod, status, imgIdx))
931
 
        return res
932
 
 
933
 
    def updateOrder(self, newOrder):
934
 
        conf = Utils.createAndReadConfig('Explorer')
935
 
        conf.set('explorer', 'installedtransports', pprint.pformat(newOrder))
936
 
        Utils.writeConfig(conf)
937
 
 
938
 
    def checkValidModulePath(self, name):
939
 
        try:
940
 
            Utils.find_dotted_module(name)
941
 
        except ImportError, err:
942
 
            #print str(err)
943
 
            return false
944
 
        else:
945
 
            return true
946
 
 
947
 
 
948
 
class TransportPluginsTreeDisplayOrderGroupNode(PreferenceGroupNode):
949
 
    """  """
950
 
    protocol = 'prefs.group.plug-in.transport.tree-order'
951
 
    defName = 'TransportPluginsPrefsGroup'
952
 
    def __init__(self):
953
 
        name = 'Tree display order'
954
 
        PreferenceGroupNode.__init__(self, name, None)
955
 
 
956
 
    def openList(self):
957
 
        conf = Utils.createAndReadConfig('Explorer')
958
 
 
959
 
        treeOrder = eval(conf.get('explorer', 'transportstree'), {})
960
 
        assert isinstance(treeOrder, type([]))
961
 
 
962
 
        res = []
963
 
        for prot in treeOrder:
964
 
            if not ExplorerNodes.nodeRegByProt.has_key(prot):
965
 
                status = 'Protocol not installed'
966
 
                imgIdx = EditorHelper.imgSystemObjPending
967
 
            else:
968
 
                status = 'Installed'
969
 
                imgIdx = EditorHelper.imgSystemObjOrdered
970
 
 
971
 
            res.append(TransportPluginExplNode(prot, status, imgIdx))
972
 
        return res
973
 
 
974
 
    def updateOrder(self, newOrder):
975
 
        conf = Utils.createAndReadConfig('Explorer')
976
 
        conf.set('explorer', 'transportstree', pprint.pformat(newOrder))
977
 
        Utils.writeConfig(conf)
978
 
 
979
 
    def checkConfigEntry(self, protocol):
980
 
        conf = Utils.createAndReadConfig('Explorer')
981
 
        if not conf.has_option('explorer', protocol):
982
 
            conf.set('explorer', protocol, '{}')
983
 
        Utils.writeConfig(conf)
984
 
 
985
 
    def clearEmptyConfigEntry(self, protocol):
986
 
        conf = Utils.createAndReadConfig('Explorer')
987
 
        if conf.has_option('explorer', protocol) and \
988
 
              eval(conf.get('explorer', protocol).strip(), {}) == {}:
989
 
            conf.remove_option('explorer', protocol)
990
 
            Utils.writeConfig(conf)
991
 
 
992
 
 
993
 
class HelpConfigPGN(PreferenceGroupNode):
994
 
    """  """
995
 
    protocol = 'prefs.group.help.config'
996
 
    defName = 'HelpConfigPrefsGroup'
997
 
    def __init__(self):
998
 
        name = 'Help system'
999
 
        PreferenceGroupNode.__init__(self, name, None)
1000
 
 
1001
 
    def openList(self):
1002
 
        return 
1003
 
 
1004
 
 
1005
 
class HelpConfigBooksPGN(PreferenceGroupNode):
1006
 
    """  """
1007
 
    protocol = 'prefs.group.help.config.books'
1008
 
    defName = 'HelpConfigBooksPrefsGroup'
1009
 
    def __init__(self):
1010
 
        name = 'Help books'
1011
 
        PreferenceGroupNode.__init__(self, name, None)
1012
 
 
1013
 
    def openList(self):
1014
 
        bookPaths = self.readBooks()
1015
 
        return [HelpConfigBookNode(bookPath) 
1016
 
                for bookPath in bookPaths]
1017
 
 
1018
 
 
1019
 
    def readBooks(self):
1020
 
        return eval(Utils.createAndReadConfig('Explorer').get('help', 'books'), {})
1021
 
 
1022
 
    def writeBooks(self, books):
1023
 
        conf = Utils.createAndReadConfig('Explorer')
1024
 
        conf.set('help', 'books', pprint.pformat(books))
1025
 
        Utils.writeConfig(conf)
1026
 
 
1027
 
 
1028
 
    def preparePath(self, path):
1029
 
        helpPath = Preferences.pyPath+'/Docs/'
1030
 
        
1031
 
        if path.startswith('file://'):
1032
 
            path = path[7:]
1033
 
        
1034
 
        # Add relative paths for files inside Docs directory
1035
 
        if os.path.normcase(path).startswith(os.path.normcase(helpPath)):
1036
 
            return path[len(helpPath):]
1037
 
        else:
1038
 
            return path
1039
 
 
1040
 
    def editBook(self, curPath, newPath):
1041
 
        books = self.readBooks()
1042
 
        books[books.index(curPath)] = self.preparePath(newPath)
1043
 
        self.writeBooks(books)
1044
 
 
1045
 
    def addBook(self, path):
1046
 
        path = self.preparePath(path)
1047
 
        self.writeBooks(self.readBooks() + [path])
1048
 
    
1049
 
    def removeBook(self, path):
1050
 
        books = self.readBooks()
1051
 
        books.remove(path)
1052
 
        self.writeBooks(books)
1053
 
 
1054
 
    def updateOrder(self, paths):
1055
 
        self.writeBooks(paths)
1056
 
 
1057
 
 
1058
 
class HelpConfigBookNode(ExplorerNodes.ExplorerNode):
1059
 
    """  """
1060
 
    protocol = 'help.book'
1061
 
    def __init__(self, resourcepath):
1062
 
        fullpath = self.getAbsPath(resourcepath)
1063
 
 
1064
 
        name = os.path.basename(resourcepath)
1065
 
        if os.path.splitext(fullpath)[1] == '.hhp':
1066
 
            # Peek at title inside hhp file
1067
 
            for line in open(fullpath).readlines():
1068
 
                if line.startswith('Title'):
1069
 
                    name = line.split('=')[1].strip()
1070
 
 
1071
 
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
1072
 
              EditorHelper.imgHelpBook, None, {})
1073
 
 
1074
 
    def open(self, editor):
1075
 
        return None, None
1076
 
 
1077
 
##    def getURI(self):
1078
 
##        return '%s (%s)'%(ExplorerNodes.ExplorerNode.getURI(self),
1079
 
##                          self.pluginStatus)
1080
 
 
1081
 
    def isFolderish(self):
1082
 
        return false
1083
 
 
1084
 
    def notifyBeginLabelEdit(self, event):
1085
 
        event.Veto()
1086
 
 
1087
 
    def getAbsPath(self, resourcepath):
1088
 
        if not os.path.isabs(resourcepath):
1089
 
            return os.path.join(Preferences.pyPath, 'Docs', resourcepath)
1090
 
        else:
1091
 
            return resourcepath
1092
 
 
1093
 
 
1094
 
 
1095
 
class HelpConfigBooksController(ExplorerNodes.Controller):
1096
 
    addItemBmp = 'Images/Shared/NewItem.png'
1097
 
    removeItemBmp = 'Images/Shared/DeleteItem.png'
1098
 
    moveUpBmp = 'Images/Shared/up.png'
1099
 
    moveDownBmp = 'Images/Shared/down.png'
1100
 
 
1101
 
    itemDescr = 'item'
1102
 
 
1103
 
    def __init__(self, editor, list, inspector, controllers):
1104
 
        ExplorerNodes.Controller.__init__(self, editor)
1105
 
        self.list = list
1106
 
        self.menu = wx.wxMenu()
1107
 
 
1108
 
        [wxID_HB_EDIT, wxID_HB_NEW, wxID_HB_DEL, wxID_HB_UP, wxID_HB_DOWN, 
1109
 
         wxID_HB_REST, wxID_HB_CLRI, wxID_HB_OPEN] = Utils.wxNewIds(8)
1110
 
 
1111
 
        self.helpBooksMenuDef = [ (wxID_HB_EDIT, 'Edit '+self.itemDescr,
1112
 
                                   self.OnEditBookPath, '-'),
1113
 
                                  (wxID_HB_NEW, 'Add new '+self.itemDescr,
1114
 
                                   self.OnNewBook, self.addItemBmp),
1115
 
                                  (wxID_HB_DEL, 'Remove '+self.itemDescr,
1116
 
                                   self.OnRemoveBook, self.removeItemBmp),
1117
 
                                  (-1, '-', None, ''),
1118
 
                                  (wxID_HB_UP, 'Move up',
1119
 
                                   self.OnMoveBookUp, self.moveUpBmp),
1120
 
                                  (wxID_HB_DOWN, 'Move down',
1121
 
                                   self.OnMoveBookDown, self.moveDownBmp),
1122
 
                                  (-1, '-', None, '-'),
1123
 
                                  (wxID_HB_OPEN, 'Open hhp file',
1124
 
                                   self.OnOpenHHP, '-'),
1125
 
                                  (-1, '-', None, '-'),
1126
 
                                  (wxID_HB_REST, 'Restart the help system',
1127
 
                                   self.OnRestartHelp, '-'),
1128
 
                                  (wxID_HB_CLRI, 'Clear the help indexes',
1129
 
                                   self.OnClearHelpIndexes, '-'),
1130
 
                                ]
1131
 
 
1132
 
        self.setupMenu(self.menu, self.list, self.helpBooksMenuDef)
1133
 
        self.toolbarMenus = [self.helpBooksMenuDef]
1134
 
 
1135
 
    def destroy(self):
1136
 
        self.helpBooksMenuDef = ()
1137
 
        self.toolbarMenus = ()
1138
 
        self.menu.Destroy()
1139
 
 
1140
 
    def editorUpdateNotify(self, info=''):
1141
 
        self.OnReloadItems()
1142
 
 
1143
 
    def OnReloadItems(self, event=None):
1144
 
        if self.list.node:
1145
 
            self.list.refreshCurrent()
1146
 
 
1147
 
    def moveBook(self, node, idx, direc):
1148
 
        paths = [item.resourcepath for item in self.list.items]
1149
 
 
1150
 
        path = paths[idx]
1151
 
        del paths[idx]
1152
 
        paths.insert(idx + direc, path)
1153
 
 
1154
 
        self.list.node.updateOrder(paths)
1155
 
 
1156
 
        self.list.refreshCurrent()
1157
 
        self.list.selectItemByIdx(idx+direc+1)
1158
 
 
1159
 
    def OnMoveBookUp(self, event):
1160
 
        if self.list.node:
1161
 
            ms = self.list.getMultiSelection()
1162
 
            nodes = self.getNodesForSelection(ms)
1163
 
            if len(nodes) != 1:
1164
 
                wx.wxLogError('Can only move 1 at a time')
1165
 
            else:
1166
 
                node = nodes[0]
1167
 
                idx = self.list.items.index(node)
1168
 
                if idx == 0:
1169
 
                    wx.wxLogError('Already at the beginning')
1170
 
                else:
1171
 
                    self.moveBook(node, idx, -1)
1172
 
 
1173
 
    def OnMoveBookDown(self, event):
1174
 
        if self.list.node:
1175
 
            ms = self.list.getMultiSelection()
1176
 
            nodes = self.getNodesForSelection(ms)
1177
 
            if len(nodes) != 1:
1178
 
                wx.wxLogError('Can only move 1 at a time')
1179
 
            else:
1180
 
                node = nodes[0]
1181
 
                idx = self.list.items.index(node)
1182
 
                if idx >= len(self.list.items) -1:
1183
 
                    wx.wxLogError('Already at the end')
1184
 
                else:
1185
 
                    self.moveBook(node, idx, 1)
1186
 
 
1187
 
    def OnEditBookPath(self, event):
1188
 
        if self.list.node:
1189
 
            ms = self.list.getMultiSelection()
1190
 
            for node in self.getNodesForSelection(ms):
1191
 
                if not os.path.isabs(node.resourcepath):
1192
 
                    path = os.path.join(Preferences.pyPath, 
1193
 
                                        'Docs', node.resourcepath)
1194
 
                else:
1195
 
                    path = node.resourcepath
1196
 
 
1197
 
                curpath, curfile = os.path.split(path)
1198
 
                newpath = self.editor.openFileDlg('AllFiles', curdir=curpath)
1199
 
                if newpath:
1200
 
                    self.list.node.editBook(node.resourcepath, path)
1201
 
                    self.list.refreshCurrent()
1202
 
 
1203
 
    def OnNewBook(self, event):
1204
 
        path = self.editor.openFileDlg('AllFiles', curdir=Preferences.pyPath+'/Docs')
1205
 
        if path and self.list.node:
1206
 
            self.list.node.addBook(path)
1207
 
            self.list.refreshCurrent()
1208
 
 
1209
 
    def OnRemoveBook(self, event):
1210
 
        if self.list.node:
1211
 
            ms = self.list.getMultiSelection()
1212
 
            for node in self.getNodesForSelection(ms):
1213
 
                self.list.node.removeBook(node.resourcepath)
1214
 
            self.list.refreshCurrent()
1215
 
            
1216
 
    def OnRestartHelp(self, event):
1217
 
        import Help
1218
 
        Help.delHelp()
1219
 
        wx.wxYield()
1220
 
        Help.initHelp()
1221
 
 
1222
 
    def OnClearHelpIndexes(self, event):
1223
 
        import Help
1224
 
        
1225
 
        cd = Help.getCacheDir()
1226
 
        for name in os.listdir(cd):
1227
 
            if os.path.splitext(name)[1] == '.cached':
1228
 
                os.remove(os.path.join(cd, name))
1229
 
                wx.wxLogMessage('Deleted %s'%name)
1230
 
            
1231
 
    def OnOpenHHP(self, event):
1232
 
        if self.list.node:
1233
 
            ms = self.list.getMultiSelection()
1234
 
            for node in self.getNodesForSelection(ms):
1235
 
                self.editor.openOrGotoModule(node.getAbsPath(node.resourcepath))
1236
 
 
1237
 
#-------------------------------------------------------------------------------
1238
 
 
1239
 
 
1240
 
ExplorerNodes.register(BoaPrefGroupNode)
1241
 
ExplorerNodes.register(PluginFilesGroupNode,
1242
 
      controller=PluginFilesGroupNodeController)
1243
 
ExplorerNodes.register(TransportPluginsLoadOrderGroupNode,
1244
 
      controller=TransportPluginsLoadOrderController)
1245
 
ExplorerNodes.register(TransportPluginsTreeDisplayOrderGroupNode,
1246
 
      controller=TransportPluginsTreeDisplayOrderController)
1247
 
ExplorerNodes.register(HelpConfigBooksPGN, controller=HelpConfigBooksController)
 
1
#-----------------------------------------------------------------------------
 
2
# Name:        PrefsExplorer.py
 
3
# Purpose:
 
4
#
 
5
# Author:      Riaan Booysen
 
6
#
 
7
# Created:     2001/06/08
 
8
# RCS-ID:      $Id: PrefsExplorer.py,v 1.18 2005/05/18 12:59:39 riaan Exp $
 
9
# Copyright:   (c) 2001 - 2005
 
10
# Licence:     GPL
 
11
#-----------------------------------------------------------------------------
 
12
print 'importing Explorers.PrefsExplorer'
 
13
import os, sys, glob, pprint, imp
 
14
import types
 
15
 
 
16
import wx
 
17
 
 
18
import Preferences, Utils, Plugins
 
19
 
 
20
import ExplorerNodes
 
21
from Models import EditorHelper
 
22
from Views import STCStyleEditor
 
23
import methodparse, relpath
 
24
 
 
25
class PreferenceGroupNode(ExplorerNodes.ExplorerNode):
 
26
    """ Represents a group of preference collections """
 
27
    protocol = 'prefs.group'
 
28
    defName = 'PrefsGroup'
 
29
    def __init__(self, name, parent):
 
30
        ExplorerNodes.ExplorerNode.__init__(self, name, name, None,
 
31
              EditorHelper.imgPrefsFolder, None)
 
32
 
 
33
        self.vetoSort = True
 
34
        self.preferences = []
 
35
 
 
36
    def isFolderish(self):
 
37
        return True
 
38
 
 
39
    def openList(self):
 
40
        return self.preferences
 
41
 
 
42
    def notifyBeginLabelEdit(self, event):
 
43
        event.Veto()
 
44
 
 
45
class BoaPrefGroupNode(PreferenceGroupNode):
 
46
    """ The Preference node in the Explorer """
 
47
    protocol = 'boa.prefs.group'
 
48
    customPrefs = [] # list of tuples ('name', 'file')
 
49
    def __init__(self, parent):
 
50
        PreferenceGroupNode.__init__(self, 'Preferences', parent)
 
51
        self.bold = True
 
52
 
 
53
        prefImgIdx = EditorHelper.imgSystemObj
 
54
        stcPrefImgIdx = EditorHelper.imgPrefsSTCStyles
 
55
 
 
56
        self.source_pref = PreferenceGroupNode('Source', self)
 
57
 
 
58
        self.source_pref.preferences = [
 
59
            UsedModuleSrcBsdPrefColNode('Default settings',
 
60
                Preferences.exportedSTCProps, os.path.join(Preferences.rcPath,
 
61
                'prefs.rc.py'), prefImgIdx, self, Preferences, True)]
 
62
 
 
63
        for name, lang, STCClass, stylesFile in ExplorerNodes.langStyleInfoReg:
 
64
            if not os.path.isabs(stylesFile):
 
65
                stylesFile = os.path.join(Preferences.rcPath, stylesFile)
 
66
            self.source_pref.preferences.append(STCStyleEditPrefsCollNode(
 
67
                  name, lang, STCClass, stylesFile, stcPrefImgIdx, self))
 
68
        self.preferences.append(self.source_pref)
 
69
 
 
70
        self.general_pref = UsedModuleSrcBsdPrefColNode('General',
 
71
            Preferences.exportedProperties, os.path.join(Preferences.rcPath,
 
72
            'prefs.rc.py'), prefImgIdx, self, Preferences)
 
73
        self.preferences.append(self.general_pref)
 
74
 
 
75
        self.platform_pref = UsedModuleSrcBsdPrefColNode('Platform specific',
 
76
            Preferences.exportedProperties2, os.path.join(Preferences.rcPath,
 
77
            'prefs.%s.rc.py' % (wx.Platform == '__WXMSW__' and 'msw' or 'gtk')),
 
78
            prefImgIdx, self, Preferences)
 
79
        self.preferences.append(self.platform_pref)
 
80
 
 
81
        self.keys_pref = KeyDefsSrcPrefColNode('Key bindings', ('*',),
 
82
            os.path.join(Preferences.rcPath, 'prefs.keys.rc.py'), prefImgIdx,
 
83
            self, Preferences.keyDefs)
 
84
        self.preferences.append(self.keys_pref)
 
85
 
 
86
        for name, filename in self.customPrefs:
 
87
            if not os.path.isabs(filename):
 
88
                filename = os.path.join(Preferences.rcPath, filename)
 
89
            self.preferences.append(UsedModuleSrcBsdPrefColNode(name,
 
90
            ('*',), filename, prefImgIdx, self, Preferences))
 
91
 
 
92
##        self.pychecker_pref = SourceBasedPrefColNode('PyChecker',
 
93
##            ('*',), Preferences.pyPath+'/.pycheckrc', prefImgIdx, self)
 
94
##        self.preferences.append(self.pychecker_pref)
 
95
 
 
96
        self.plugin_pref = PreferenceGroupNode('Plug-ins', self)
 
97
 
 
98
        self.core_plugpref = UsedModuleSrcBsdPrefColNode('Core support',
 
99
            Preferences.exportedCorePluginProps, os.path.join(Preferences.rcPath,
 
100
            'prefs.rc.py'), prefImgIdx, self, Preferences, True)
 
101
        self.plugin_plugpref = UsedModuleSrcBsdPrefColNode('Preferences', Preferences.exportedPluginProps,#('*',),
 
102
            os.path.join(Preferences.rcPath, 'prefs.plug-ins.rc.py'), prefImgIdx,
 
103
            self, Preferences, True)
 
104
        self.files_plugpref = PluginFilesGroupNode()
 
105
        self.transp_plugpref = PreferenceGroupNode('Transports', self)
 
106
        self.transp_plugpref.preferences = [
 
107
            TransportPluginsLoadOrderGroupNode(),
 
108
            TransportPluginsTreeDisplayOrderGroupNode(),
 
109
        ]
 
110
 
 
111
        self.plugin_pref.preferences = [
 
112
            self.files_plugpref,
 
113
            self.transp_plugpref,
 
114
            self.core_plugpref,
 
115
            self.plugin_plugpref,
 
116
        ]
 
117
 
 
118
        self.preferences.insert(1, self.plugin_pref)
 
119
 
 
120
        self.help_pref = HelpConfigBooksPGN()
 
121
 
 
122
        self.preferences.insert(2, self.help_pref)
 
123
 
 
124
 
 
125
class PreferenceCollectionNode(ExplorerNodes.ExplorerNode):
 
126
    """ Represents an inspectable preference collection """
 
127
    protocol = 'prefs'
 
128
    def __init__(self, name, props, resourcepath, imgIdx, parent):
 
129
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
 
130
              imgIdx, None, props)
 
131
 
 
132
    def open(self, editor):
 
133
        """ Populate inspector with preference items """
 
134
        comp = PreferenceCompanion(self.name, self)
 
135
        comp.updateProps()
 
136
 
 
137
        # Select in inspector
 
138
        editor.inspector.restore()
 
139
        if editor.inspector.pages.GetSelection() != 1:
 
140
            editor.inspector.pages.SetSelection(1)
 
141
        editor.inspector.selectObject(comp, False)
 
142
        return None, None
 
143
 
 
144
    def isFolderish(self):
 
145
        return False
 
146
 
 
147
    def load(self):
 
148
        raise 'Not implemented'
 
149
 
 
150
    def save(self, filename, data):
 
151
        pass
 
152
 
 
153
    def notifyBeginLabelEdit(self, event):
 
154
        event.Veto()
 
155
 
 
156
class STCStyleEditPrefsCollNode(PreferenceCollectionNode):
 
157
    protocol = 'stc.prefs'
 
158
    def __init__(self, name, lang, STCclass, resourcepath, imgIdx, parent):
 
159
        PreferenceCollectionNode.__init__(self, name, {}, resourcepath, imgIdx, parent)
 
160
        self.language = lang
 
161
        self.STCclass = STCclass
 
162
 
 
163
    def open(self, editor):
 
164
        # build list of all open STC's in the Editor
 
165
        openSTCViews = []
 
166
        for modPge in editor.modules.values():
 
167
            for view in modPge.model.views.values():
 
168
                if isinstance(view, self.STCclass):
 
169
                    openSTCViews.append(view)
 
170
 
 
171
        # also check the shell
 
172
        if Preferences.psPythonShell == 'Shell':
 
173
            if isinstance(editor.shell, self.STCclass):
 
174
                openSTCViews.append(editor.shell)
 
175
        #elif Preferences.psPythonShell == 'PyCrust':
 
176
        #    if self.language == 'python':
 
177
        #        openSTCViews.append(editor.shell.shellWin)
 
178
 
 
179
        dlg = STCStyleEditor.STCStyleEditDlg(editor, self.name, self.language,
 
180
              self.resourcepath, openSTCViews)
 
181
        try: dlg.ShowModal()
 
182
        finally: dlg.Destroy()
 
183
 
 
184
        return None, None
 
185
 
 
186
    def getURI(self):
 
187
        return '%s://%s' %(PreferenceCollectionNode.getURI(self), self.language)
 
188
 
 
189
 
 
190
class SourceBasedPrefColNode(PreferenceCollectionNode):
 
191
    """ Preference collection represented by the global names in python module
 
192
 
 
193
    Only names which are also defined in properties are returned
 
194
    except when properties is a special match all tuple; ('*',)
 
195
 
 
196
    This only applies to names assigned to values ( x = 123 ) not to global
 
197
    names defined by classes functions and imports.
 
198
    """
 
199
    def __init__(self, name, props, resourcepath, imgIdx, parent, showBreaks=True):
 
200
        PreferenceCollectionNode.__init__(self, name, props, resourcepath,
 
201
              imgIdx, parent)
 
202
        self.showBreakLines = showBreaks
 
203
 
 
204
    def load(self):
 
205
        # All preferences are local
 
206
        import moduleparse
 
207
 
 
208
        module = moduleparse.Module(self.name,
 
209
              open(self.resourcepath).readlines())
 
210
 
 
211
        values = []
 
212
        comments = []
 
213
        options = []
 
214
        # keep only names defined in the property list
 
215
        for name in module.global_order[:]:
 
216
            if name[0] == '_' or self.properties != ('*',) and \
 
217
                  name not in self.properties:
 
218
                module.global_order.remove(name)
 
219
                del module.globals[name]
 
220
            else:
 
221
                # XXX Should handle multiline assign
 
222
                code = '\n'.join(module.source[\
 
223
                      module.globals[name].start-1 : \
 
224
                      module.globals[name].end])
 
225
 
 
226
                # Extract value
 
227
                s = code.find('=')
 
228
                if s != -1:
 
229
                    values.append(code[s+1:].strip())
 
230
                else:
 
231
                    values.append('')
 
232
 
 
233
                # Read possible comment/help or options
 
234
                comment = []
 
235
                option = ''
 
236
                idx = module.globals[name].start-2
 
237
                while idx >= 0:
 
238
                    line = module.source[idx].strip()
 
239
                    if len(line) > 11 and line[:11] == '## options:':
 
240
                        option = line[11:].strip()
 
241
                        idx = idx - 1
 
242
                    elif len(line) > 8 and line[:8] == '## type:':
 
243
                        option = '##'+line[8:].strip()
 
244
                        idx = idx - 1
 
245
                    elif line and line[0] == '#':
 
246
                        comment.append(line[1:].lstrip())
 
247
                        idx = idx - 1
 
248
                    else:
 
249
                        break
 
250
                comment.reverse()
 
251
                comments.append('\n'.join(comment))
 
252
                options.append(option)
 
253
 
 
254
        if self.showBreakLines: breaks = module.break_lines
 
255
        else: breaks = {}
 
256
 
 
257
        return (module.global_order, values, module.globals, comments, options,
 
258
                breaks)
 
259
 
 
260
    def save(self, filename, data):
 
261
        """ Updates one property """
 
262
        src = open(self.resourcepath).readlines()
 
263
        src[data[2].start-1] = '%s = %s\n' % (data[0], data[1])
 
264
        open(self.resourcepath, 'w').writelines(src)
 
265
 
 
266
class UsedModuleSrcBsdPrefColNode(SourceBasedPrefColNode):
 
267
    """ Also update the value of a global attribute of an imported module """
 
268
    def __init__(self, name, props, resourcepath, imgIdx, parent, module,
 
269
          showBreaks=True):
 
270
        SourceBasedPrefColNode.__init__(self, name, props, resourcepath, imgIdx,
 
271
              parent, showBreaks)
 
272
        self.module = module
 
273
 
 
274
    def save(self, filename, data):
 
275
        SourceBasedPrefColNode.save(self, filename, data)
 
276
        if hasattr(self.module, data[0]):
 
277
            setattr(self.module, data[0], eval(data[1], vars(Preferences)))
 
278
 
 
279
class KeyDefsSrcPrefColNode(PreferenceCollectionNode):
 
280
    """ Preference collection representing the key bindings """
 
281
    def __init__(self, name, props, resourcepath, imgIdx, parent, keyDefs):
 
282
        PreferenceCollectionNode.__init__(self, name, props, resourcepath,
 
283
              imgIdx, parent)
 
284
        self.showBreakLines = True
 
285
        self._editor = None
 
286
 
 
287
    def open(self, editor):
 
288
        PreferenceCollectionNode.open(self, editor)
 
289
        self._editor = editor
 
290
 
 
291
    def load(self):
 
292
        import moduleparse
 
293
 
 
294
        src = open(self.resourcepath).readlines()
 
295
        module = moduleparse.Module(self.name, src)
 
296
 
 
297
        # find keydefs
 
298
        keydefs = {}
 
299
        names = []
 
300
        values = []
 
301
        start = end = idx = -1
 
302
        for line in src:
 
303
            idx = idx + 1
 
304
            line = line.strip()
 
305
            if line == 'keyDefs = {':
 
306
                start = idx
 
307
            elif start != -1 and line:
 
308
                if line[-1] == '}':
 
309
                    end = idx
 
310
                    break
 
311
                elif line[0] != '#':
 
312
                    colon = line.find(':')
 
313
                    if colon == -1: raise Exception('Invalid KeyDef item: %s'%line)
 
314
                    name = line[:colon].rstrip()[1:-1]
 
315
                    val = line[colon+1:].lstrip()
 
316
                    keydefs[name] = moduleparse.CodeBlock(val, idx+1, idx+1)
 
317
                    names.append(name)
 
318
                    values.append(val)
 
319
 
 
320
        return (names, values, keydefs, ['']*len(keydefs),
 
321
              ['## keydef']*len(keydefs), module.break_lines)
 
322
 
 
323
    def save(self, filename, data):
 
324
        """ Updates one key:val in keydefs dict """
 
325
 
 
326
        # Update source file
 
327
        src = open(self.resourcepath).readlines()
 
328
        src[data[2].start-1] = \
 
329
              "  '%s'%s: %s\n" % (data[0], (12 - len(data[0]))*' ', data[1])
 
330
        open(self.resourcepath, 'w').writelines(src)
 
331
        # Update dictionary
 
332
        Preferences.keyDefs[data[0]] = eval(data[1], vars(Preferences))[0]
 
333
        # Update editor menus
 
334
        self._editor.setupToolBar()
 
335
        self._editor.updateStaticMenuShortcuts()
 
336
        self._editor.shell.bindShortcuts()
 
337
 
 
338
 
 
339
class ConfigBasedPrefsColNode(PreferenceCollectionNode):
 
340
    """ Preferences driven by config files """
 
341
    pass
 
342
 
 
343
 
 
344
#---Companions------------------------------------------------------------------
 
345
 
 
346
from PropEdit import PropertyEditors, InspectorEditorControls
 
347
 
 
348
class KeyDefConfPropEdit(PropertyEditors.ConfPropEdit):
 
349
    def inspectorEdit(self):
 
350
        self.editorCtrl = InspectorEditorControls.ButtonIEC(self, self.value)
 
351
        self.editorCtrl.createControl(self.parent, self.idx, self.width, self.edit)
 
352
 
 
353
    def edit(self, event):
 
354
        import KeyDefsDlg
 
355
        dlg = KeyDefsDlg.KeyDefsDialog(self.parent, self.name, self.value)
 
356
        try:
 
357
            if dlg.ShowModal() == wx.ID_OK:
 
358
                self.editorCtrl.value = dlg.result
 
359
                self.inspectorPost(False)
 
360
        finally:
 
361
            dlg.Destroy()
 
362
 
 
363
    def getDisplayValue(self):
 
364
        try:
 
365
            return eval(self.value, {'wx': wx})[0][2]
 
366
        except Exception, err:
 
367
            return str(err)
 
368
 
 
369
class PreferenceCompanion(ExplorerNodes.ExplorerCompanion):
 
370
    def __init__(self, name, prefNode, ):
 
371
        ExplorerNodes.ExplorerCompanion.__init__(self, name)
 
372
        self.prefNode = prefNode
 
373
 
 
374
        self._breaks = {}
 
375
 
 
376
    typeMap = {}
 
377
    customTypeMap = {'filepath': PropertyEditors.FilepathConfPropEdit,
 
378
                     'dirpath': PropertyEditors.DirpathConfPropEdit,
 
379
                     'keydef': KeyDefConfPropEdit}
 
380
    def getPropEditor(self, prop):
 
381
        # XXX Using name equality to identify _breaks' prop edit is ugly !
 
382
        for aProp in self.propItems:
 
383
            if aProp[0] == prop: break
 
384
        else:
 
385
            raise Exception('Property "%s" not found'%prop)
 
386
 
 
387
        srcVal = aProp[1]
 
388
        opts = aProp[4]
 
389
 
 
390
        if prop in self._breaks.values():
 
391
            return None
 
392
 
 
393
        if opts:
 
394
            if opts[:2] == '##':
 
395
                return self.customTypeMap.get(opts[2:].strip(), None)
 
396
            else:
 
397
                return PropertyEditors.EnumConfPropEdit
 
398
 
 
399
        if srcVal.lower() in ('true', 'false'):
 
400
            return PropertyEditors.BoolConfPropEdit
 
401
 
 
402
        try:
 
403
            val = eval(srcVal, vars(Preferences))
 
404
        except Exception, error:
 
405
            return PropertyEditors.StrConfPropEdit
 
406
 
 
407
        if isinstance(val, wx.Colour):
 
408
            return PropertyEditors.ColourConfPropEdit
 
409
        return self.typeMap.get(type(val), PropertyEditors.StrConfPropEdit)
 
410
 
 
411
    def getPropertyHelp(self, propName):
 
412
        for prop in self.propItems:
 
413
            if prop[0] == propName: return prop[3]
 
414
        else:
 
415
            return propName
 
416
 
 
417
    def getPropertyItems(self):
 
418
        order, vals, props, comments, options, self._breaks = self.prefNode.load()
 
419
 
 
420
        # remove empty break lines (-----------------------)
 
421
        for lineNo in self._breaks.keys():
 
422
            if not self._breaks[lineNo]:
 
423
                del self._breaks[lineNo]
 
424
 
 
425
        breakLinenos = self._breaks.keys()
 
426
        breakLinenos.sort()
 
427
        if len(breakLinenos):
 
428
            breaksIdx = 0
 
429
        else:
 
430
            breaksIdx = None
 
431
 
 
432
        res = []
 
433
        for name, value, comment, option in map(None, order, vals, comments, options):
 
434
            if breaksIdx is not None:
 
435
                # find closest break above property
 
436
                while breaksIdx < len(breakLinenos)-1 and \
 
437
                      props[name].start > breakLinenos[breaksIdx+1]:
 
438
                    breaksIdx += 1
 
439
 
 
440
                if breaksIdx >= len(breakLinenos):
 
441
                    breaksIdx = None
 
442
 
 
443
                if breaksIdx is not None and props[name].start > breakLinenos[breaksIdx]:
 
444
                    res.append( (self._breaks[breakLinenos[breaksIdx]], '', None, '', '') )
 
445
                    breaksIdx += 1
 
446
                    #if breaksIdx == len(self._breaks) -1:
 
447
                    #    breaksIdx = None
 
448
                    #else:
 
449
                    #    breaksIdx = breaksIdx + 1
 
450
            res.append( (name, value, props[name], comment, option) )
 
451
        return res
 
452
 
 
453
    def setPropHook(self, name, value, oldProp):
 
454
        # XXX validate etc.
 
455
        try:
 
456
            eval(value, vars(Preferences))
 
457
        except Exception, error:
 
458
            wx.LogError('Error: '+str(error))
 
459
            return False
 
460
        else:
 
461
            newProp = (name, value) + oldProp[2:]
 
462
            self.prefNode.save(name, newProp)
 
463
            return True
 
464
 
 
465
    def persistedPropVal(self, name, setterName):
 
466
        if name in self._breaks.values():
 
467
            return 'PROP_CATEGORY'
 
468
        else:
 
469
            return None
 
470
 
 
471
    def getPropOptions(self, name):
 
472
        for prop in self.propItems:
 
473
            if prop[0] == name:
 
474
                strOpts = prop[4]
 
475
                if strOpts and strOpts[:2] != '##':
 
476
                    return self.eval(strOpts)
 
477
                else:
 
478
                    return ()
 
479
        else:
 
480
            return ()
 
481
 
 
482
    def getPropNames(self, name):
 
483
        for prop in self.propItems:
 
484
            if prop[0] == name:
 
485
                strOpts = prop[4]
 
486
                if strOpts and strOpts[:2] != '##':
 
487
                    return methodparse.safesplitfields(strOpts, ',')
 
488
                else: return ()
 
489
        else:
 
490
            return ()
 
491
 
 
492
    def eval(self, expr):
 
493
        import PaletteMapping
 
494
        return PaletteMapping.evalCtrl(expr, vars(Preferences))
 
495
 
 
496
##    def GetProp(self, name):
 
497
##        ExplorerNodes.ExplorerCompanion.GetProp(self, name)
 
498
##        return self.findProp(name)[0][1]
 
499
 
 
500
class CorePluginsGroupNode(PreferenceGroupNode):
 
501
    """ """
 
502
    protocol = 'prefs.group.plug-in.core'
 
503
    defName = 'CorePluginPrefsGroup'
 
504
    def __init__(self):
 
505
        name = 'Core support'
 
506
        PreferenceGroupNode.__init__(self, name, None)
 
507
 
 
508
        self.vetoSort = True
 
509
        self.preferences = []
 
510
 
 
511
    def isFolderish(self):
 
512
        return True
 
513
 
 
514
    def openList(self):
 
515
        return self.preferences
 
516
 
 
517
    def notifyBeginLabelEdit(self, event):
 
518
        event.Veto()
 
519
 
 
520
def getPluginSection(pluginFile):
 
521
    pluginPath = os.path.dirname(pluginFile)
 
522
    return Preferences.pluginSections[
 
523
              Preferences.pluginPaths.index(pluginPath)]
 
524
 
 
525
class PluginFileExplNode(ExplorerNodes.ExplorerNode):
 
526
    """  """
 
527
    def __init__(self, name, enabled, status, resourcepath, imgIdx):
 
528
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
 
529
              imgIdx, None, {})
 
530
        self.pluginEnabled = enabled
 
531
        self.pluginStatus = status
 
532
 
 
533
    def open(self, editor):
 
534
        """  """
 
535
        if self.pluginEnabled:
 
536
            msg = 'Disable'
 
537
        else:
 
538
            msg = 'Enable'
 
539
 
 
540
        if wx.MessageBox('%s %s?'%(msg, self.name), 'Confirm Toggle Plug-in',
 
541
              wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
 
542
            section = getPluginSection(self.resourcepath)
 
543
            ordered, disabled = Plugins.readPluginsState(section)
 
544
 
 
545
            if self.pluginEnabled:
 
546
                disabled.append(self.name)
 
547
            else:
 
548
                try:
 
549
                    disabled.remove(self.name)
 
550
                except ValueError:
 
551
                    pass
 
552
 
 
553
            #Plugins.writeInitPluginGlobals(initPluginPath, initPluginGlobals)
 
554
            Plugins.writePluginsState(section, ordered, disabled)
 
555
 
 
556
            editor.explorer.list.refreshCurrent()
 
557
 
 
558
        return None, None
 
559
 
 
560
    def getURI(self):
 
561
        return '%s (%s)'%(ExplorerNodes.ExplorerNode.getURI(self),
 
562
                          self.pluginStatus)
 
563
 
 
564
    def isFolderish(self):
 
565
        return False
 
566
 
 
567
    def notifyBeginLabelEdit(self, event):
 
568
        event.Veto()
 
569
 
 
570
    def changeOrder(self, direction):
 
571
        section = getPluginSection(self.resourcepath)
 
572
        #initPluginPath = os.path.dirname(self.resourcepath)
 
573
        ordered, disabled = Plugins.readPluginsState(section)
 
574
        #ordered = initPluginGlobals['__ordered__']
 
575
        try:
 
576
            idx = ordered.index(self.name)
 
577
        except ValueError:
 
578
            idx = len(ordered)+1
 
579
        else:
 
580
            del ordered[idx]
 
581
        idx = max(idx + direction, 0)
 
582
        if idx <= len(ordered):
 
583
            ordered.insert(idx, self.name)
 
584
        #Plugins.writeInitPluginGlobals(initPluginPath, initPluginGlobals)
 
585
        Plugins.writePluginsState(section, ordered, disabled)
 
586
 
 
587
 
 
588
class PluginFilesGroupNode(PreferenceGroupNode):
 
589
    """ Represents a group of preference collections """
 
590
    protocol = 'prefs.group.plug-in.files'
 
591
    defName = 'PluginFilesPrefsGroup'
 
592
    def __init__(self):
 
593
        name = 'Plug-in files'
 
594
        PreferenceGroupNode.__init__(self, name, None)
 
595
 
 
596
    def openList(self):
 
597
        res = []
 
598
        splitext = os.path.splitext
 
599
        for filename, ordered, enabled in Plugins.buildPluginExecList():
 
600
            if os.path.basename(filename) == '__init__.plug-in.py':
 
601
                continue
 
602
 
 
603
            name = splitext(splitext(os.path.basename(filename))[0])[0]
 
604
            if not enabled:
 
605
                name = splitext(name)[0]
 
606
                status = 'Disabled'
 
607
                imgIdx = EditorHelper.imgSystemObjDisabled
 
608
            else:
 
609
                fn = filename.lower()
 
610
                if Preferences.failedPlugins.has_key(fn):
 
611
                    kind, msg = Preferences.failedPlugins[fn]
 
612
                    if kind == 'Skipped':
 
613
                        status = 'Skipped plug-in: %s'% msg
 
614
                        imgIdx = EditorHelper.imgSystemObjPending
 
615
                    else:
 
616
                        status = 'Broken plug-in: %s'% msg
 
617
                        imgIdx = EditorHelper.imgSystemObjBroken
 
618
                elif fn in Preferences.installedPlugins:
 
619
                    if ordered:
 
620
                        status = 'Installed, ordered'
 
621
                        imgIdx = EditorHelper.imgSystemObjOrdered
 
622
                    else:
 
623
                        status = 'Installed'
 
624
                        imgIdx = EditorHelper.imgSystemObj
 
625
                else:
 
626
                    status = 'Pending restart'
 
627
                    imgIdx = EditorHelper.imgSystemObjPending
 
628
 
 
629
            res.append(PluginFileExplNode(name, enabled, status, filename, imgIdx))
 
630
        return res
 
631
 
 
632
 
 
633
class PluginFilesGroupNodeController(ExplorerNodes.Controller):
 
634
    moveUpBmp = 'Images/Shared/up.png'
 
635
    moveDownBmp = 'Images/Shared/down.png'
 
636
 
 
637
    itemDescr = 'item'
 
638
 
 
639
    def __init__(self, editor, list, inspector, controllers):
 
640
        ExplorerNodes.Controller.__init__(self, editor)
 
641
        self.list = list
 
642
        self.menu = wx.Menu()
 
643
 
 
644
        [wxID_PF_TOGGLE, wxID_PF_OPEN, wxID_PF_UP, wxID_PF_DOWN] = Utils.wxNewIds(4)
 
645
 
 
646
        self.transpMenuDef = [ (wxID_PF_TOGGLE, 'Toggle Enable/Disabled',
 
647
                                self.OnToggleState, '-'),
 
648
                               (wxID_PF_OPEN, 'Open plug-in file',
 
649
                                self.OnOpenPlugin, '-'),
 
650
                               (-1, '-', None, ''),
 
651
                               (wxID_PF_UP, 'Move up',
 
652
                                self.OnMovePluginUp, self.moveUpBmp),
 
653
                               (wxID_PF_DOWN, 'Move down',
 
654
                                self.OnMovePluginDown, self.moveDownBmp),
 
655
                             ]
 
656
 
 
657
        self.setupMenu(self.menu, self.list, self.transpMenuDef)
 
658
        self.toolbarMenus = [self.transpMenuDef]
 
659
 
 
660
    def destroy(self):
 
661
        self.transpMenuDef = []
 
662
        self.toolbarMenus = []
 
663
        self.menu.Destroy()
 
664
 
 
665
    def OnToggleState(self, event):
 
666
        if self.list.node:
 
667
            ms = self.list.getMultiSelection()
 
668
            nodes = self.getNodesForSelection(ms)
 
669
            for node in nodes:
 
670
                node.open(self.editor)
 
671
 
 
672
    def OnOpenPlugin(self, event):
 
673
        if self.list.node:
 
674
            ms = self.list.getMultiSelection()
 
675
            nodes = self.getNodesForSelection(ms)
 
676
            for node in nodes:
 
677
                self.editor.openOrGotoModule(node.resourcepath)
 
678
 
 
679
    def OnMovePluginUp(self, event):
 
680
        if self.list.node:
 
681
            ms = self.list.getMultiSelection()
 
682
            nodes = self.getNodesForSelection(ms)
 
683
            if len(nodes) != 1:
 
684
                wx.LogError('Can only move 1 at a time')
 
685
            else:
 
686
                node = nodes[0]
 
687
                idx = self.list.items.index(node)
 
688
                if idx == 0:
 
689
                    wx.LogError('Already at the beginning')
 
690
                else:
 
691
                    name = node.name
 
692
                    node.changeOrder(-1)
 
693
                    self.list.refreshCurrent()
 
694
                    self.list.selectItemNamed(name)
 
695
 
 
696
    def OnMovePluginDown(self, event):
 
697
        if self.list.node:
 
698
            ms = self.list.getMultiSelection()
 
699
            nodes = self.getNodesForSelection(ms)
 
700
            if len(nodes) != 1:
 
701
                wx.LogError('Can only move 1 at a time')
 
702
            else:
 
703
                node = nodes[0]
 
704
                idx = self.list.items.index(node)
 
705
##                if idx >= len(self.list.items) -1:
 
706
##                    wx.LogError('Already at the end')
 
707
##                else:
 
708
                name = node.name
 
709
                node.changeOrder(1)
 
710
                self.list.refreshCurrent()
 
711
                self.list.selectItemNamed(name)
 
712
 
 
713
 
 
714
 
 
715
class TransportPluginExplNode(ExplorerNodes.ExplorerNode):
 
716
    """  """
 
717
    protocol = 'transport'
 
718
    def __init__(self, name, status, imgIdx):
 
719
        ExplorerNodes.ExplorerNode.__init__(self, name, '%s (%s)'%(name, status),
 
720
              None, imgIdx, None, {})
 
721
        self.status = status
 
722
 
 
723
 
 
724
    def open(self, editor):
 
725
        return None, None
 
726
 
 
727
class TransportPluginsController(ExplorerNodes.Controller):
 
728
    addItemBmp = 'Images/Shared/NewItem.png'
 
729
    removeItemBmp = 'Images/Shared/DeleteItem.png'
 
730
    moveUpBmp = 'Images/Shared/up.png'
 
731
    moveDownBmp = 'Images/Shared/down.png'
 
732
 
 
733
    itemDescr = 'item'
 
734
 
 
735
    def __init__(self, editor, list, inspector, controllers):
 
736
        ExplorerNodes.Controller.__init__(self, editor)
 
737
        self.list = list
 
738
        self.menu = wx.Menu()
 
739
 
 
740
        [wxID_TP_NEW, wxID_TP_DEL, wxID_TP_UP, wxID_TP_DOWN] = Utils.wxNewIds(4)
 
741
 
 
742
        self.transpMenuDef = [ (wxID_TP_NEW, 'Add new '+self.itemDescr,
 
743
                                self.OnNewTransport, self.addItemBmp),
 
744
                               (wxID_TP_DEL, 'Remove '+self.itemDescr,
 
745
                                self.OnDeleteTransport, self.removeItemBmp),
 
746
                               (-1, '-', None, ''),
 
747
                               (wxID_TP_UP, 'Move up',
 
748
                                self.OnMoveTransportUp, self.moveUpBmp),
 
749
                               (wxID_TP_DOWN, 'Move down',
 
750
                                self.OnMoveTransportDown, self.moveDownBmp),
 
751
                             ]
 
752
 
 
753
        self.setupMenu(self.menu, self.list, self.transpMenuDef)
 
754
        self.toolbarMenus = [self.transpMenuDef]
 
755
 
 
756
    def destroy(self):
 
757
        self.transpMenuDef = []
 
758
        self.toolbarMenus = []
 
759
        self.menu.Destroy()
 
760
 
 
761
    def editorUpdateNotify(self, info=''):
 
762
        self.OnReloadItems()
 
763
 
 
764
    def OnReloadItems(self, event=None):
 
765
        if self.list.node:
 
766
            self.list.refreshCurrent()
 
767
 
 
768
    def moveTransport(self, node, idx, direc):
 
769
        names = []
 
770
        for item in self.list.items:
 
771
            names.append(item.name)
 
772
 
 
773
        name = names[idx]
 
774
        del names[idx]
 
775
        names.insert(idx + direc, name)
 
776
 
 
777
        self.list.node.updateOrder(names)
 
778
 
 
779
        self.list.refreshCurrent()
 
780
        self.list.selectItemByIdx(idx+direc+1)
 
781
 
 
782
    def OnMoveTransportUp(self, event):
 
783
        if self.list.node:
 
784
            ms = self.list.getMultiSelection()
 
785
            nodes = self.getNodesForSelection(ms)
 
786
            if len(nodes) != 1:
 
787
                wx.LogError('Can only move 1 at a time')
 
788
            else:
 
789
                node = nodes[0]
 
790
                idx = self.list.items.index(node)
 
791
                if idx == 0:
 
792
                    wx.LogError('Already at the beginning')
 
793
                else:
 
794
                    self.moveTransport(node, idx, -1)
 
795
 
 
796
    def OnMoveTransportDown(self, event):
 
797
        if self.list.node:
 
798
            ms = self.list.getMultiSelection()
 
799
            nodes = self.getNodesForSelection(ms)
 
800
            if len(nodes) != 1:
 
801
                wx.LogError('Can only move 1 at a time')
 
802
            else:
 
803
                node = nodes[0]
 
804
                idx = self.list.items.index(node)
 
805
                if idx >= len(self.list.items) -1:
 
806
                    wx.LogError('Already at the end')
 
807
                else:
 
808
                    self.moveTransport(node, idx, 1)
 
809
 
 
810
    def OnNewTransport(self, event):
 
811
        pass
 
812
 
 
813
    def OnDeleteTransport(self, event):
 
814
        pass
 
815
 
 
816
class TransportPluginsLoadOrderController(TransportPluginsController):
 
817
    itemDescr = 'Transport module'
 
818
 
 
819
    def OnNewTransport(self, event):
 
820
        dlg = wx.TextEntryDialog(self.list, 'Enter the fully qualified Python '\
 
821
               'object path to \nthe Transport module. E.g. Explorers.FileExplorer',
 
822
               'New Transport', '')
 
823
        try:
 
824
            if dlg.ShowModal() != wx.ID_OK:
 
825
                return
 
826
            transportModulePath = dlg.GetValue()
 
827
        finally:
 
828
            dlg.Destroy()
 
829
 
 
830
        if not self.list.node.checkValidModulePath(transportModulePath):
 
831
            if wx.MessageBox('Cannot locate the specified module path,\n'\
 
832
                         'are you sure you want to continue?',
 
833
                         'Module not found',
 
834
                         wx.YES_NO | wx.ICON_EXCLAMATION) == wx.NO:
 
835
                return
 
836
 
 
837
        names = []
 
838
        for item in self.list.items:
 
839
            names.append(item.name)
 
840
 
 
841
        names.append(transportModulePath)
 
842
 
 
843
        self.list.node.updateOrder(names)
 
844
 
 
845
        self.list.refreshCurrent()
 
846
 
 
847
    def OnDeleteTransport(self, event):
 
848
        selNames = self.list.getMultiSelection()
 
849
        nodes = self.getNodesForSelection(selNames)
 
850
 
 
851
        names = []
 
852
        for item in self.list.items:
 
853
            names.append(item.name)
 
854
 
 
855
        for item in nodes:
 
856
            names.remove(item.name)
 
857
 
 
858
        self.list.node.updateOrder(names)
 
859
 
 
860
        self.list.refreshCurrent()
 
861
 
 
862
class TransportPluginsTreeDisplayOrderController(TransportPluginsController):
 
863
    itemDescr = 'Transports tree node'
 
864
 
 
865
    def OnNewTransport(self, event):
 
866
        dlg = wx.TextEntryDialog(self.list, 'Enter the protocol identifier. E.g. '\
 
867
               'ftp, ssh', 'New Transports Tree Node', '')
 
868
        try:
 
869
            if dlg.ShowModal() != wx.ID_OK:
 
870
                return
 
871
            protocol = dlg.GetValue()
 
872
        finally:
 
873
            dlg.Destroy()
 
874
 
 
875
        names = []
 
876
        for item in self.list.items:
 
877
            names.append(item.name)
 
878
 
 
879
        names.append(protocol)
 
880
 
 
881
        self.list.node.updateOrder(names)
 
882
        self.list.node.checkConfigEntry(protocol)
 
883
 
 
884
        self.list.refreshCurrent()
 
885
 
 
886
    def OnDeleteTransport(self, event):
 
887
        selNames = self.list.getMultiSelection()
 
888
        nodes = self.getNodesForSelection(selNames)
 
889
 
 
890
        names = []
 
891
        for item in self.list.items:
 
892
            names.append(item.name)
 
893
 
 
894
        for item in nodes:
 
895
            names.remove(item.name)
 
896
            self.list.node.clearEmptyConfigEntry(item.name)
 
897
 
 
898
        self.list.node.updateOrder(names)
 
899
 
 
900
        self.list.refreshCurrent()
 
901
 
 
902
 
 
903
class TransportPluginsLoadOrderGroupNode(PreferenceGroupNode):
 
904
    """  """
 
905
    protocol = 'prefs.group.plug-in.transport.load-order'
 
906
    defName = 'TransportPluginsPrefsGroup'
 
907
    def __init__(self):
 
908
        name = 'Loading order'
 
909
        PreferenceGroupNode.__init__(self, name, None)
 
910
 
 
911
    def openList(self):
 
912
        conf = Utils.createAndReadConfig('Explorer')
 
913
 
 
914
        modules = eval(conf.get('explorer', 'installedtransports'), {})
 
915
        assert isinstance(modules, types.ListType)
 
916
 
 
917
        res = []
 
918
        for mod in modules:
 
919
            if mod in ExplorerNodes.installedModules:
 
920
                status = 'Installed'
 
921
                imgIdx = EditorHelper.imgSystemObjOrdered
 
922
            elif mod in ExplorerNodes.failedModules.keys():
 
923
                status = 'Broken: %s'%ExplorerNodes.failedModules[mod]
 
924
                imgIdx = EditorHelper.imgSystemObjBroken
 
925
            else:
 
926
                status = 'Pending restart'
 
927
                imgIdx = EditorHelper.imgSystemObjPending
 
928
 
 
929
            res.append(TransportPluginExplNode(mod, status, imgIdx))
 
930
        return res
 
931
 
 
932
    def updateOrder(self, newOrder):
 
933
        conf = Utils.createAndReadConfig('Explorer')
 
934
        conf.set('explorer', 'installedtransports', pprint.pformat(newOrder))
 
935
        Utils.writeConfig(conf)
 
936
 
 
937
    def checkValidModulePath(self, name):
 
938
        try:
 
939
            Utils.find_dotted_module(name)
 
940
        except ImportError, err:
 
941
            #print str(err)
 
942
            return False
 
943
        else:
 
944
            return True
 
945
 
 
946
 
 
947
class TransportPluginsTreeDisplayOrderGroupNode(PreferenceGroupNode):
 
948
    """  """
 
949
    protocol = 'prefs.group.plug-in.transport.tree-order'
 
950
    defName = 'TransportPluginsPrefsGroup'
 
951
    def __init__(self):
 
952
        name = 'Tree display order'
 
953
        PreferenceGroupNode.__init__(self, name, None)
 
954
 
 
955
    def openList(self):
 
956
        conf = Utils.createAndReadConfig('Explorer')
 
957
 
 
958
        treeOrder = eval(conf.get('explorer', 'transportstree'), {})
 
959
        assert isinstance(treeOrder, type([]))
 
960
 
 
961
        res = []
 
962
        for prot in treeOrder:
 
963
            if not ExplorerNodes.nodeRegByProt.has_key(prot):
 
964
                status = 'Protocol not installed'
 
965
                imgIdx = EditorHelper.imgSystemObjPending
 
966
            else:
 
967
                status = 'Installed'
 
968
                imgIdx = EditorHelper.imgSystemObjOrdered
 
969
 
 
970
            res.append(TransportPluginExplNode(prot, status, imgIdx))
 
971
        return res
 
972
 
 
973
    def updateOrder(self, newOrder):
 
974
        conf = Utils.createAndReadConfig('Explorer')
 
975
        conf.set('explorer', 'transportstree', pprint.pformat(newOrder))
 
976
        Utils.writeConfig(conf)
 
977
 
 
978
    def checkConfigEntry(self, protocol):
 
979
        conf = Utils.createAndReadConfig('Explorer')
 
980
        if not conf.has_option('explorer', protocol):
 
981
            conf.set('explorer', protocol, '{}')
 
982
        Utils.writeConfig(conf)
 
983
 
 
984
    def clearEmptyConfigEntry(self, protocol):
 
985
        conf = Utils.createAndReadConfig('Explorer')
 
986
        if conf.has_option('explorer', protocol) and \
 
987
              eval(conf.get('explorer', protocol).strip(), {}) == {}:
 
988
            conf.remove_option('explorer', protocol)
 
989
            Utils.writeConfig(conf)
 
990
 
 
991
 
 
992
class HelpConfigPGN(PreferenceGroupNode):
 
993
    """  """
 
994
    protocol = 'prefs.group.help.config'
 
995
    defName = 'HelpConfigPrefsGroup'
 
996
    def __init__(self):
 
997
        name = 'Help system'
 
998
        PreferenceGroupNode.__init__(self, name, None)
 
999
 
 
1000
    def openList(self):
 
1001
        return
 
1002
 
 
1003
 
 
1004
class HelpConfigBooksPGN(PreferenceGroupNode):
 
1005
    """  """
 
1006
    protocol = 'prefs.group.help.config.books'
 
1007
    defName = 'HelpConfigBooksPrefsGroup'
 
1008
    def __init__(self):
 
1009
        name = 'Help books'
 
1010
        PreferenceGroupNode.__init__(self, name, None)
 
1011
 
 
1012
    def openList(self):
 
1013
        bookPaths = self.readBooks()
 
1014
        res = []
 
1015
        for bookPath in bookPaths:
 
1016
            try:
 
1017
                res.append(HelpConfigBookNode(bookPath))
 
1018
            except IOError, err:
 
1019
                # too disruptive to display an error
 
1020
                pass
 
1021
        return res
 
1022
 
 
1023
#        return [HelpConfigBookNode(bookPath)
 
1024
#                for bookPath in bookPaths]
 
1025
 
 
1026
 
 
1027
    def readBooks(self):
 
1028
        return eval(Utils.createAndReadConfig('Explorer').get('help', 'books'), {})
 
1029
 
 
1030
    def writeBooks(self, books):
 
1031
        conf = Utils.createAndReadConfig('Explorer')
 
1032
        conf.set('help', 'books', pprint.pformat(books))
 
1033
        Utils.writeConfig(conf)
 
1034
 
 
1035
 
 
1036
    def preparePath(self, path):
 
1037
        helpPath = Preferences.pyPath+'/Docs/'
 
1038
 
 
1039
        if path.startswith('file://'):
 
1040
            path = path[7:]
 
1041
 
 
1042
        # Add relative paths for files inside Docs directory
 
1043
        if os.path.normcase(path).startswith(os.path.normcase(helpPath)):
 
1044
            return path[len(helpPath):]
 
1045
        else:
 
1046
            return path
 
1047
 
 
1048
    def editBook(self, curPath, newPath):
 
1049
        books = self.readBooks()
 
1050
        books[books.index(curPath)] = self.preparePath(newPath)
 
1051
        self.writeBooks(books)
 
1052
 
 
1053
    def addBook(self, path):
 
1054
        path = self.preparePath(path)
 
1055
        self.writeBooks(self.readBooks() + [path])
 
1056
 
 
1057
    def removeBook(self, path):
 
1058
        books = self.readBooks()
 
1059
        books.remove(path)
 
1060
        self.writeBooks(books)
 
1061
 
 
1062
    def updateOrder(self, paths):
 
1063
        self.writeBooks(paths)
 
1064
 
 
1065
 
 
1066
class HelpConfigBookNode(ExplorerNodes.ExplorerNode):
 
1067
    """  """
 
1068
    protocol = 'help.book'
 
1069
    def __init__(self, resourcepath):
 
1070
        fullpath = self.getAbsPath(resourcepath)
 
1071
 
 
1072
        name = os.path.basename(resourcepath)
 
1073
        if os.path.splitext(fullpath)[1] == '.hhp':
 
1074
            # Peek at title inside hhp file
 
1075
            for line in open(fullpath).readlines():
 
1076
                if line.startswith('Title'):
 
1077
                    name = line.split('=')[1].strip()
 
1078
 
 
1079
        ExplorerNodes.ExplorerNode.__init__(self, name, resourcepath, None,
 
1080
              EditorHelper.imgHelpBook, None, {})
 
1081
 
 
1082
    def open(self, editor):
 
1083
        return None, None
 
1084
 
 
1085
##    def getURI(self):
 
1086
##        return '%s (%s)'%(ExplorerNodes.ExplorerNode.getURI(self),
 
1087
##                          self.pluginStatus)
 
1088
 
 
1089
    def isFolderish(self):
 
1090
        return False
 
1091
 
 
1092
    def notifyBeginLabelEdit(self, event):
 
1093
        event.Veto()
 
1094
 
 
1095
    def getAbsPath(self, resourcepath):
 
1096
        if not os.path.isabs(resourcepath):
 
1097
            return os.path.join(Preferences.pyPath, 'Docs', resourcepath)
 
1098
        else:
 
1099
            return resourcepath
 
1100
 
 
1101
 
 
1102
 
 
1103
class HelpConfigBooksController(ExplorerNodes.Controller):
 
1104
    addItemBmp = 'Images/Shared/NewItem.png'
 
1105
    removeItemBmp = 'Images/Shared/DeleteItem.png'
 
1106
    moveUpBmp = 'Images/Shared/up.png'
 
1107
    moveDownBmp = 'Images/Shared/down.png'
 
1108
 
 
1109
    itemDescr = 'item'
 
1110
 
 
1111
    def __init__(self, editor, list, inspector, controllers):
 
1112
        ExplorerNodes.Controller.__init__(self, editor)
 
1113
        self.list = list
 
1114
        self.menu = wx.Menu()
 
1115
 
 
1116
        [wxID_HB_EDIT, wxID_HB_NEW, wxID_HB_DEL, wxID_HB_UP, wxID_HB_DOWN,
 
1117
         wxID_HB_REST, wxID_HB_CLRI, wxID_HB_OPEN] = Utils.wxNewIds(8)
 
1118
 
 
1119
        self.helpBooksMenuDef = [ (wxID_HB_EDIT, 'Edit '+self.itemDescr,
 
1120
                                   self.OnEditBookPath, '-'),
 
1121
                                  (wxID_HB_NEW, 'Add new '+self.itemDescr,
 
1122
                                   self.OnNewBook, self.addItemBmp),
 
1123
                                  (wxID_HB_DEL, 'Remove '+self.itemDescr,
 
1124
                                   self.OnRemoveBook, self.removeItemBmp),
 
1125
                                  (-1, '-', None, ''),
 
1126
                                  (wxID_HB_UP, 'Move up',
 
1127
                                   self.OnMoveBookUp, self.moveUpBmp),
 
1128
                                  (wxID_HB_DOWN, 'Move down',
 
1129
                                   self.OnMoveBookDown, self.moveDownBmp),
 
1130
                                  (-1, '-', None, '-'),
 
1131
                                  (wxID_HB_OPEN, 'Open hhp file',
 
1132
                                   self.OnOpenHHP, '-'),
 
1133
                                  (-1, '-', None, '-'),
 
1134
                                  (wxID_HB_REST, 'Restart the help system',
 
1135
                                   self.OnRestartHelp, '-'),
 
1136
                                  (wxID_HB_CLRI, 'Clear the help indexes',
 
1137
                                   self.OnClearHelpIndexes, '-'),
 
1138
                                ]
 
1139
 
 
1140
        self.setupMenu(self.menu, self.list, self.helpBooksMenuDef)
 
1141
        self.toolbarMenus = [self.helpBooksMenuDef]
 
1142
 
 
1143
    def destroy(self):
 
1144
        self.helpBooksMenuDef = ()
 
1145
        self.toolbarMenus = ()
 
1146
        self.menu.Destroy()
 
1147
 
 
1148
    def editorUpdateNotify(self, info=''):
 
1149
        self.OnReloadItems()
 
1150
 
 
1151
    def OnReloadItems(self, event=None):
 
1152
        if self.list.node:
 
1153
            self.list.refreshCurrent()
 
1154
 
 
1155
    def moveBook(self, node, idx, direc):
 
1156
        paths = [item.resourcepath for item in self.list.items]
 
1157
 
 
1158
        path = paths[idx]
 
1159
        del paths[idx]
 
1160
        paths.insert(idx + direc, path)
 
1161
 
 
1162
        self.list.node.updateOrder(paths)
 
1163
 
 
1164
        self.list.refreshCurrent()
 
1165
        self.list.selectItemByIdx(idx+direc+1)
 
1166
 
 
1167
    def OnMoveBookUp(self, event):
 
1168
        if self.list.node:
 
1169
            ms = self.list.getMultiSelection()
 
1170
            nodes = self.getNodesForSelection(ms)
 
1171
            if len(nodes) != 1:
 
1172
                wx.LogError('Can only move 1 at a time')
 
1173
            else:
 
1174
                node = nodes[0]
 
1175
                idx = self.list.items.index(node)
 
1176
                if idx == 0:
 
1177
                    wx.LogError('Already at the beginning')
 
1178
                else:
 
1179
                    self.moveBook(node, idx, -1)
 
1180
 
 
1181
    def OnMoveBookDown(self, event):
 
1182
        if self.list.node:
 
1183
            ms = self.list.getMultiSelection()
 
1184
            nodes = self.getNodesForSelection(ms)
 
1185
            if len(nodes) != 1:
 
1186
                wx.LogError('Can only move 1 at a time')
 
1187
            else:
 
1188
                node = nodes[0]
 
1189
                idx = self.list.items.index(node)
 
1190
                if idx >= len(self.list.items) -1:
 
1191
                    wx.LogError('Already at the end')
 
1192
                else:
 
1193
                    self.moveBook(node, idx, 1)
 
1194
 
 
1195
    def OnEditBookPath(self, event):
 
1196
        if self.list.node:
 
1197
            ms = self.list.getMultiSelection()
 
1198
            for node in self.getNodesForSelection(ms):
 
1199
                if not os.path.isabs(node.resourcepath):
 
1200
                    path = os.path.join(Preferences.pyPath,
 
1201
                                        'Docs', node.resourcepath)
 
1202
                else:
 
1203
                    path = node.resourcepath
 
1204
 
 
1205
                curpath, curfile = os.path.split(path)
 
1206
                newpath = self.editor.openFileDlg('AllFiles', curdir=curpath)
 
1207
                if newpath:
 
1208
                    self.list.node.editBook(node.resourcepath, path)
 
1209
                    self.list.refreshCurrent()
 
1210
 
 
1211
    def OnNewBook(self, event):
 
1212
        path = self.editor.openFileDlg('AllFiles', curdir=Preferences.pyPath+'/Docs')
 
1213
        if path and self.list.node:
 
1214
            self.list.node.addBook(path)
 
1215
            self.list.refreshCurrent()
 
1216
 
 
1217
    def OnRemoveBook(self, event):
 
1218
        if self.list.node:
 
1219
            ms = self.list.getMultiSelection()
 
1220
            for node in self.getNodesForSelection(ms):
 
1221
                self.list.node.removeBook(node.resourcepath)
 
1222
            self.list.refreshCurrent()
 
1223
 
 
1224
    def OnRestartHelp(self, event):
 
1225
        import Help
 
1226
        Help.delHelp()
 
1227
        wx.Yield()
 
1228
        Help.initHelp()
 
1229
 
 
1230
    def OnClearHelpIndexes(self, event):
 
1231
        import Help
 
1232
 
 
1233
        cd = Help.getCacheDir()
 
1234
        for name in os.listdir(cd):
 
1235
            if os.path.splitext(name)[1] == '.cached':
 
1236
                os.remove(os.path.join(cd, name))
 
1237
                wx.LogMessage('Deleted %s'%name)
 
1238
 
 
1239
    def OnOpenHHP(self, event):
 
1240
        if self.list.node:
 
1241
            ms = self.list.getMultiSelection()
 
1242
            for node in self.getNodesForSelection(ms):
 
1243
                self.editor.openOrGotoModule(node.getAbsPath(node.resourcepath))
 
1244
 
 
1245
#-------------------------------------------------------------------------------
 
1246
 
 
1247
 
 
1248
ExplorerNodes.register(BoaPrefGroupNode)
 
1249
ExplorerNodes.register(PluginFilesGroupNode,
 
1250
      controller=PluginFilesGroupNodeController)
 
1251
ExplorerNodes.register(TransportPluginsLoadOrderGroupNode,
 
1252
      controller=TransportPluginsLoadOrderController)
 
1253
ExplorerNodes.register(TransportPluginsTreeDisplayOrderGroupNode,
 
1254
      controller=TransportPluginsTreeDisplayOrderController)
 
1255
ExplorerNodes.register(HelpConfigBooksPGN, controller=HelpConfigBooksController)