File: Controllers.py

package info (click to toggle)
boa-constructor 0.3.0-3
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 8,188 kB
  • ctags: 8,857
  • sloc: python: 54,163; sh: 66; makefile: 36
file content (382 lines) | stat: -rw-r--r-- 13,312 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#-----------------------------------------------------------------------------
# Name:        Controllers.py
# Purpose:     Controller classes for the MVC pattern
#
# Author:      Riaan Booysen
#
# Created:     2001/13/08
# RCS-ID:      $Id: Controllers.py,v 1.11 2004/08/16 13:28:13 riaan Exp $
# Copyright:   (c) 2001 - 2004 Riaan Booysen
# Licence:     GPL
#-----------------------------------------------------------------------------
print 'importing Models.Controllers'

import os

from wxPython.wx import *

import Preferences, Utils
from Preferences import keyDefs, IS

import EditorHelper, PaletteStore, EditorModels

from Views import EditorViews, SourceViews, DiffView

from Explorers import ExplorerNodes

addTool = Utils.AddToolButtonBmpIS

true=1;false=0

class BaseEditorController:
    """ Between user and model operations

    Provides interface to add new and open existing models
    Manages toolbar and menu actions
    Custom classes should define Model operations as events
    """
    docked = true

    Model           = None
    DefaultViews    = []
    AdditionalViews = []
    
    plugins = ()

    def __init__(self, editor):
        self.editor = editor
        self.evts = []
        
        self.plugins = [Plugin(self) for Plugin in self.plugins]

    def getModel(self):
        return self.editor.getActiveModulePage().model

    def createModel(self, source, filename, name, editor, saved, modelParent=None):
        pass

    def createNewModel(self, modelParent=None):
        pass

    def afterAddModulePage(self, model):
        pass

    def newFileTransport(self, name, filename):
        from Explorers.FileExplorer import FileSysNode
        return FileSysNode(name, filename, None, -1, None, None, properties = {})

    def addEvt(self, wId, meth):
        EVT_MENU(self.editor, wId, meth)
        self.evts.append(wId)

    def disconnectEvts(self):
        for wId in self.evts:
            self.editor.Disconnect(wId)
        self.evts = []

    def addMenu(self, menu, wId, label, accls, code=(), bmp=''):
        Utils.appendMenuItem(menu, wId, label, code, bmp)
        if code:
            accls.append( (code[0], code[1], wId) )

#-------------------------------------------------------------------------------
    def actions(self, model):
        """ Override to define Controller/Model actions

        Should return a list of tuples in this form:
        [('Name', self.OnEvent, 'BmpPath', 'KeyDef'), ...]
        """
        return []

    def addActions(self, toolbar, menu, model):
        actions = self.actions(model)
        for plugin in self.plugins:
            actions.extend(plugin.actions(model))
        
        accls = []

        for name, event, bmp, key in actions:
            if name != '-':
                wId = wxNewId()
                self.addEvt(wId, event)
                if key: code = keyDefs[key]
                else:   code = ()
                self.addMenu(menu, wId, name, accls, code, bmp)
            else:
                menu.AppendSeparator()

            if bmp:
                if bmp != '-':
                    addTool(self.editor, toolbar, bmp, name, event)
                elif name == '-' and bmp == '-':
                    toolbar.AddSeparator()

        return accls


class EditorController(BaseEditorController):
    closeBmp = 'Images/Editor/Close.png'

    def actions(self, model):
        return BaseEditorController.actions(self, model) + \
               [('Close', self.OnClose, self.closeBmp, 'Close')]

    def OnClose(self, event):
        self.editor.closeModulePage(self.editor.getActiveModulePage())

class PersistentController(EditorController):
    saveBmp = 'Images/Editor/Save.png'
    saveAsBmp = 'Images/Editor/SaveAs.png'

    def actions(self, model):
        return EditorController.actions(self, model) + \
               [('Reload', self.OnReload, '-', ''),
                ('Save', self.OnSave, self.saveBmp, 'Save'),
                ('Save as...', self.OnSaveAs, self.saveAsBmp, 'SaveAs'),
                ('-', None, '', ''),
                ('Toggle read-only', self.OnToggleReadOnly, '-', ''),
                ('NDiff files...', self.OnNDiffFile, '-', '')]

    def createModel(self, source, filename, main, saved, modelParent=None):
        return self.Model(source, filename, self.editor, saved)

    def createNewModel(self, modelParent=None):
        name = self.editor.getValidName(self.Model)
        model = self.createModel('', name, '', false)
        model.transport = self.newFileTransport('', name)
        model.new()

        return model, name

    def checkUnsaved(self, model, checkModified=false):
        if not model.savedAs or checkModified and (model.modified or \
              len(model.viewsModified)):
            wxLogError('Cannot perform this action on an unsaved%s module'%(
                  checkModified and '/modified' or '') )
            return true
        else:
            return false

    def OnSave(self, event):
        try:
            self.editor.activeModSaveOrSaveAs()
        except ExplorerNodes.TransportModifiedSaveError, err:
            errStr = err.args[0]
            if errStr == 'Reload':
                self.OnReload(event)
            elif errStr == 'Cancel':
                pass
            else:
                wxLogError(str(err))
        except ExplorerNodes.TransportSaveError, err:
            wxLogError(str(err))

    def OnSaveAs(self, event):
        try:
            self.editor.activeModSaveOrSaveAs(forceSaveAs=true)
        except ExplorerNodes.TransportModifiedSaveError, err:
            errStr = err.args[0]
            if errStr == 'Reload':
                self.OnReload(event)
            elif errStr == 'Cancel':
                pass
            else:
                wxLogError(str(err))
        except ExplorerNodes.TransportSaveError, err:
            wxLogError(str(err))

    def OnReload(self, event):
        model = self.getModel()
        if model:
            if not model.savedAs:
                wxMessageBox('Cannot reload, this file has not been saved yet.', 
                             'Reload', wxOK | wxICON_ERROR)
                return
                
            if model.hasUnsavedChanges() and \
                  wxMessageBox('There are unsaved changes.\n'\
                  'Are you sure you want to reload?',
                  'Confirm reload', wxYES_NO | wxICON_WARNING) != wxYES:
                return
            try:
                model.load()

                self.editor.updateModuleState(model)
            except ExplorerNodes.TransportLoadError, error:
                wxLogError(str(error))

    def OnToggleReadOnly(self, event):
        model = self.getModel()
        if model and model.transport and model.transport.stdAttrs.has_key('read-only'):
            model.transport.updateStdAttrs()
            ro = model.transport.stdAttrs['read-only']
            model.transport.setStdAttr('read-only', not ro)

            if model.views.has_key('Source'):
                model.views['Source'].updateFromAttrs()

            self.editor.updateModuleState(model)
        else:
            wxLogError('Read-only not supported on this transport')

    def OnNDiffFile(self, event=None, filename=''):
        model = self.getModel()
        model.refreshFromViews()
        if model:
            if self.checkUnsaved(model): return
            if not filename:
                filename = self.editor.openFileDlg(curfile=os.path.basename(model.filename))
            if filename:
                tbName = 'Diff with : '+filename
                if not model.views.has_key(tbName):
                    resultView = self.editor.addNewView(tbName, 
                          DiffView.PythonSourceDiffView)
                else:
                    resultView = model.views[tbName]

                resultView.tabName = tbName
                resultView.diffWith = filename
                resultView.refresh()
                resultView.focus()


class SourceController(PersistentController):
    AdditionalViews = [EditorViews.CVSConflictsView]

    def OnDiffFile(self, event, diffWithFilename=''):
        model = self.getModel()
        if model:
            if self.checkUnsaved(model): return
            if not diffWithFilename:
                diffWithFilename = self.editor.openFileDlg()
            filename = model.assertLocalFile(filename)

    def OnPatchFile(self, event, patchFilename=''):
        model = self.getModel()
        if model:
            if self.checkUnsaved(model): return
            if not patchFilename:
                patchFilename = self.editor.openFileDlg()
            filename = model.assertLocalFile(filename)

class TextController(PersistentController):
    Model           = EditorModels.TextModel
    DefaultViews    = [SourceViews.TextView]
    AdditionalViews = []

class UndockedController(BaseEditorController):
    docked          = false
    def createModel(self, source, filename, main, saved, modelParent=None):
        return self.Model(source, filename, self.editor, saved)

    def display(self, model):
        """ Override to display undocked interface """

class BitmapFileController(UndockedController):
    Model           = EditorModels.BitmapFileModel
    DefaultViews    = []
    AdditionalViews = []

    def display(self, model):
        from ZopeLib import ImageViewer
        ImageViewer.create(self.editor).showImage(model.filename, model.transport)

# XXX move to a new module PythonComControllers
class MakePyController(BaseEditorController):
    docked          = false
    Model           = None
    DefaultViews    = []
    AdditionalViews = []

    def createNewModel(self, modelParent=None):
        return None, None

    def display(self, model):
        import makepydialog
        dlg = makepydialog.create(self.editor)
        try:
            if dlg.ShowModal() == wxID_OK and dlg.generatedFilename:
                self.editor.openOrGotoModule(dlg.generatedFilename)
        finally:
            dlg.Destroy()

#-------------------------------------------------------------------------------

def identifyFilename(filename):
    dummy, name = os.path.split(filename)
    base, ext = os.path.splitext(filename)
    lext = ext.lower()

    if fullnameTypes.has_key(name):
        return fullnameTypes[name]
    if not ext and base.upper() == base:
        return EditorModels.TextModel, '', lext
    if EditorHelper.extMap.has_key(lext):
        return EditorHelper.extMap[lext], '', lext
    if lext in EditorHelper.internalFilesReg:
        return EditorModels.InternalFileModel, '', lext
    return None, '', lext

def identifyFile(filename, source=None, localfs=true):
    """ Return appropriate model for given source file.
        Assumes header will be part of the first continious comment block """
    Model, main, lext = identifyFilename(filename)
    if Model is not None:
        return Model, main

    if lext == defaultExt:
        BaseModel = DefaultModel
    else:
        BaseModel = EditorModels.UnknownFileModel

    if source is None and not localfs:
        if lext in EditorHelper.inspectableFilesReg.keys():
            return EditorHelper.inspectableFilesReg[lext], ''
        else:
            return BaseModel, ''
    elif lext in EditorHelper.inspectableFilesReg.keys():
        BaseModel = EditorHelper.inspectableFilesReg[lext]
        if source is not None:
            return identifySource[lext](source.split('\n'))
        elif not Preferences.exInspectInspectableFiles:
            return BaseModel, ''
        if os.path.exists(filename):
            f = open(filename)
            try:
                while 1:
                    line = f.readline()
                    if not line: break
                    line = line.strip()
                    if line and headerStartChar.has_key(lext):
                        if line[0] != headerStartChar[lext]:
                            return BaseModel, ''
                        headerInfo = identifyHeader[lext](line)
                        if headerInfo[0] != BaseModel:
                            return headerInfo
                return BaseModel, ''
            finally:
                f.close()
    return BaseModel, ''


#-Registration of this modules classes---------------------------------------
modelControllerReg = {EditorModels.TextModel: TextController,
                      EditorModels.BitmapFileModel: BitmapFileController}

# Default filetype
DefaultController = TextController
DefaultModel = EditorModels.TextModel
defaultExt = EditorModels.TextModel.ext

# Model identifiers for application type files
appModelIdReg = []

# Dictionaries of functions keyed on file extension
headerStartChar = {}
identifyHeader = {}
identifySource = {}
# dictionary of filetypes recognised by the whole name
fullnameTypes = {}

# Classed from which the Designer can load resources
resourceClasses = []