Designer.py :  » IDE » Boa-Constructor » boa-constructor-0.6.1 » Views » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » IDE » Boa Constructor 
Boa Constructor » boa constructor 0.6.1 » Views » Designer.py
#----------------------------------------------------------------------
# Name:        Designer.py
# Purpose:     Visual frame designer
#
# Author:      Riaan Booysen
#
# Created:     1999
# RCS-ID:      $Id: Designer.py,v 1.45 2007/07/02 15:01:16 riaan Exp $
# Copyright:   (c) 1999 - 2007 Riaan Booysen
# Licence:     GPL
#----------------------------------------------------------------------

print 'importing Views.Designer'

import copy, os, pprint, math

import wx

import Preferences, Utils, Help
from Preferences import IS
from Utils import _

import CtrlAlign, CtrlSize
import sourceconst

from InspectableViews import InspectableObjectView
import SelectionTags

from PropEdit import Enumerations

[wxID_CTRLPARENT, wxID_EDITCUT, wxID_EDITCOPY, wxID_EDITPASTE, wxID_EDITDELETE,
 wxID_SHOWINSP, wxID_SHOWEDTR, wxID_CTRLHELP, wxID_EDITALIGN, wxID_EDITSIZE,
 wxID_EDITRECREATE, wxID_EDITSNAPGRID, wxID_EDITRELAYOUT, wxID_EDITRELAYOUTSEL,
 wxID_EDITRELAYOUTDESGN, wxID_EDITCREATEORDER, wxID_EDITFITINSIDESIZER,
 wxID_FINDININDEX, wxID_EDITFITSIZER,
] = Utils.wxNewIds(19)

[wxID_EDITMOVELEFT, wxID_EDITMOVERIGHT, wxID_EDITMOVEUP, wxID_EDITMOVEDOWN,
 wxID_EDITWIDTHINC, wxID_EDITWIDTHDEC, wxID_EDITHEIGHTINC, wxID_EDITHEIGHTDEC,
] = Utils.wxNewIds(8)

[wxID_EDITSELECTLEFT, wxID_EDITSELECTRIGHT, wxID_EDITSELECTUP, wxID_EDITSELECTDOWN,
] = Utils.wxNewIds(4)

# XXX When opening a frame with a connected menubar, the frame is not selected
# XXX in the inspector

class DesignerView(wx.Frame, InspectableObjectView, Utils.FrameRestorerMixin):
    """ Frame Designer for design-time creation/manipulation of visual controls
        on frames. """
    viewName = 'Designer'
    viewTitle = _('Designer')
    
    docked = False
    collectionMethod = sourceconst.init_ctrls
    supportsParentView = True

    def setupArgs(self, ctrlName, params, dontEval, parent=None, compClass=None, evalDct={}, doId=True):
        """ Create a dictionary of parameters for the constructor of the
            control from a dictionary of string/source parameters.
        """
        args = InspectableObjectView.setupArgs(self, ctrlName, params, dontEval, evalDct=evalDct)

        if compClass:
            prnt = compClass.windowParentName
            wId = compClass.windowIdName
            doId = not compClass.suppressWindowId
        else:
            prnt = 'parent'
            wId = 'id'
            doId = True

        # Determine parent
        if parent:
            args[prnt] = parent
        else:
            srcPrnt = args[prnt]
            if srcPrnt == 'None':
                args[prnt] = None
            elif srcPrnt == 'self':
                try:
                    args[prnt] = self
                except AttributeError, name:
                    # XXX This isn't right
                    if self.objects.has_key(name):
                        pass
                    elif self.model.objectCollections.has_key(name):
                        pass
                    else:
                        raise
            else:
                dot = srcPrnt.find('.')
                if dot != -1:
                    srcPrnt = srcPrnt[dot + 1:]
                else: raise Exception, _('Component name illegal %s')%srcPrnt
                args[prnt] = self.objects[srcPrnt][1]

        # hack to allow stock ids to set the button
        if wId in args and args[wId] in Enumerations.wxStockIds:
            args[wId] = getattr(wx, args[wId].split('.')[1])
        elif doId: 
            args[wId] = wx.NewId()

        return args

    defPos = wx.DefaultPosition

    def __init__(self, parent, inspector, model, compPal, CompanionClass,
          dataView):
        self.controllerView = self
        self.objectNamespace = DesignerNamespace(self)

        args = self.setupArgs(model.main, model.mainConstr.params,
          CompanionClass.handledConstrParams, parent, CompanionClass,
          model.specialAttrs)

        style=wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, parent, -1, args.get('title', ''),
                                           args.get('pos', CompanionClass.defFramePos),
                                           args.get('size', CompanionClass.defFrameSize),
                                           style=CompanionClass.defFrameStyle)
        InspectableObjectView.__init__(self, inspector, model, compPal)
        self.controllerView = self

        if model.dialogLook:
            self.SetBackgroundColour(
                  wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))

        self.SetIcon(IS.load('Images/Icons/Designer.ico'))

        self.Bind(wx.EVT_MOVE, self.OnFramePos)

        self.pageIdx = -1
        self.dataView = dataView
        self.dataView.controllerView = self
        self.sizersView = None
        #self.controllerView = self
        self.saveOnClose = True
        self.confirmCancel = False

        self.ctrlEvtHandler = DesignerControlsEvtHandler(self)

        self.companion = CompanionClass(self.model.main, self, self)
        self.companion.id = Utils.windowIdentifier(self.model.main, '')

        self.companion.control = self
        self.mainMultiDrag = None

        self.lastSize = (-1, -1)

        # the objects dict has the following structure:
        #    key = componentname
        #    value = list of companion, control, deltaConstr, deltaProps, deltaEvents
        # Note that the frame itself is defined as the blank string name
        self.objects[''] = [self.companion, self, None]
        self.objectOrder.append('')
        self.SetName(self.model.main)

        self.companion.initDesignTimeControl()

        self.active = True
        self.destroying = False
        self.selection = None
        self.multiSelection = []
        # flags controlling behaviour in resize event
        self.vetoResize = False
        self.forceResize = False
        self.deletingCtrl = False
        #self.objectNamespace = DesignerNamespace(self)
        # XXX Move this definition into actions

        self.menu = wx.Menu()

        self.menu.Append(wxID_CTRLPARENT, _('Up'))
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITCUT, _('Cut'))
        self.menu.Append(wxID_EDITCOPY, _('Copy'))
        self.menu.Append(wxID_EDITPASTE, _('Paste'))
        self.menu.Append(wxID_EDITDELETE, _('Delete'))
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITRECREATE, _('Recreate'))
        self.menu.Append(wxID_EDITRELAYOUTSEL, _('Relayout selection'))
        self.menu.Append(wxID_EDITRELAYOUTDESGN, _('Relayout Designer'))
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITFITSIZER, _('Fit sizer'))
        #self.menu.Append(wxID_EDITFITINSIDESIZER, 'Fit sizer')
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITSNAPGRID, _('Snap to grid'))
        self.menu.Append(wxID_EDITALIGN, _('Align...'))
        self.menu.Append(wxID_EDITSIZE, _('Size...'))
        self.menu.AppendSeparator()
        Utils.appendMenuItem(self.menu, wxID_FINDININDEX,
              _('Find in index...'), Preferences.keyDefs['HelpFind'], '',
              _('Pops up a text input for starting a search of the help indexes'))
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITCREATEORDER, _('Creation/Tab order...'))

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.Bind(wx.EVT_MENU, self.OnControlDelete, id=wxID_EDITDELETE)
        self.Bind(wx.EVT_MENU, self.OnInspector, id=wxID_SHOWINSP)
        self.Bind(wx.EVT_MENU, self.OnEditor, id=wxID_SHOWEDTR)
        self.Bind(wx.EVT_MENU, self.OnCtrlHelp, id=wxID_CTRLHELP)
        self.Bind(wx.EVT_MENU, self.OnAlignSelected, id=wxID_EDITALIGN)
        self.Bind(wx.EVT_MENU, self.OnSizeSelected, id=wxID_EDITSIZE)
        self.Bind(wx.EVT_MENU, self.OnSelectParent, id=wxID_CTRLPARENT)
        self.Bind(wx.EVT_MENU, self.OnCutSelected, id=wxID_EDITCUT)
        self.Bind(wx.EVT_MENU, self.OnCopySelected, id=wxID_EDITCOPY)
        self.Bind(wx.EVT_MENU, self.OnPasteSelected, id=wxID_EDITPASTE)
        self.Bind(wx.EVT_MENU, self.OnRecreateSelected, id=wxID_EDITRECREATE)
        self.Bind(wx.EVT_MENU, self.OnRelayoutSelection, id=wxID_EDITRELAYOUTSEL)
        self.Bind(wx.EVT_MENU, self.OnRelayoutDesigner, id=wxID_EDITRELAYOUTDESGN)
        self.Bind(wx.EVT_MENU, self.OnSnapToGrid, id=wxID_EDITSNAPGRID)
        self.Bind(wx.EVT_MENU, self.OnCreationOrder, id=wxID_EDITCREATEORDER)
        self.Bind(wx.EVT_MENU, self.OnFindInIndex, id=wxID_FINDININDEX)
        self.Bind(wx.EVT_MENU, self.OnFitSizer, id=wxID_EDITFITSIZER)
        #self.Bind(wx.EVT_MENU, self.OnFitInsideSizer, id=wxID_EDITFITINSIDESIZER)


        self.Bind(wx.EVT_MENU, self.OnMoveLeft, id=wxID_EDITMOVELEFT)
        self.Bind(wx.EVT_MENU, self.OnMoveRight, id=wxID_EDITMOVERIGHT)
        self.Bind(wx.EVT_MENU, self.OnMoveUp, id=wxID_EDITMOVEUP)
        self.Bind(wx.EVT_MENU, self.OnMoveDown, id=wxID_EDITMOVEDOWN)
        self.Bind(wx.EVT_MENU, self.OnWidthInc, id=wxID_EDITWIDTHINC)
        self.Bind(wx.EVT_MENU, self.OnWidthDec, id=wxID_EDITWIDTHDEC)
        self.Bind(wx.EVT_MENU, self.OnHeightInc, id=wxID_EDITHEIGHTINC)
        self.Bind(wx.EVT_MENU, self.OnHeightDec, id=wxID_EDITHEIGHTDEC)

        self.Bind(wx.EVT_MENU, self.OnSelectLeft, id=wxID_EDITSELECTLEFT)
        self.Bind(wx.EVT_MENU, self.OnSelectRight, id=wxID_EDITSELECTRIGHT)
        self.Bind(wx.EVT_MENU, self.OnSelectUp, id=wxID_EDITSELECTUP)
        self.Bind(wx.EVT_MENU, self.OnSelectDown, id=wxID_EDITSELECTDOWN)

        # Key bindings
        accLst = []
        for name, wId in (('Delete', wxID_EDITDELETE),
                          ('Inspector', wxID_SHOWINSP),
                          ('Editor', wxID_SHOWEDTR),
                          ('ContextHelp', wxID_CTRLHELP),
                          ('Escape', wxID_CTRLPARENT),
                          ('Copy', wxID_EDITCOPY),
                          ('Paste', wxID_EDITPASTE),

                          ('MoveLeft', wxID_EDITMOVELEFT),
                          ('MoveRight', wxID_EDITMOVERIGHT),
                          ('MoveUp', wxID_EDITMOVEUP),
                          ('MoveDown', wxID_EDITMOVEDOWN),

                          ('WidthInc', wxID_EDITWIDTHINC),
                          ('WidthDec', wxID_EDITWIDTHDEC),
                          ('HeightInc', wxID_EDITHEIGHTINC),
                          ('HeightDec', wxID_EDITHEIGHTDEC),

                          ('SelectLeft', wxID_EDITSELECTLEFT),
                          ('SelectRight', wxID_EDITSELECTRIGHT),
                          ('SelectUp', wxID_EDITSELECTUP),
                          ('SelectDown', wxID_EDITSELECTDOWN),

                          ('HelpFind', wxID_FINDININDEX),
                        ):
            tpe, key, code = Preferences.keyDefs[name]
            accLst.append((tpe, key, wId))

        self.SetAcceleratorTable(wx.AcceleratorTable(accLst))

    def generateMenu(self):
        return Utils.duplicateMenu(self.menu)

    def saveCtrls(self, definedCtrls, module=None):
        """ Generate source code for Designer """

        if not module:
            module = self.model.getModule()
        # Remove all collection methods
        for oc in self.model.identifyCollectionMethods():
            if len(oc) > len('_init_coll_') and oc[:11] == '_init_coll_':
##                module = self.model.getModule()
                module.removeMethod(self.model.main, oc)

        # Update all size and pos parameters possibly updated externally
        for compn, ctrl, prnt in self.objects.values():
            compn.updatePosAndSize()

        if self.sizersView and self.sizersView.objects:
            collDeps = ['%sself.%s()'%(sourceconst.bodyIndent,
                                       sourceconst.init_sizers)]
        else:
            collDeps = None

        # Generate code
        InspectableObjectView.saveCtrls(self, definedCtrls, module, collDeps)

        # Regenerate window ids
        companions = [i[0] for i in self.objects.values()]
        self.model.writeWindowIds(self.collectionMethod, companions)

    def renameCtrlAndParentRefs(self, oldName, newName, children=()):
        # rename other ctrl references like parent
        for ctrl in self.objectOrder:
            # Notify
            self.objects[ctrl][0].renameCtrlRefs(oldName, newName)
            # Rename childrens' parents
            if ctrl in children:
                self.objects[ctrl][2] = newName
        # also do collections
        for coll in self.collEditors.values():
            coll.companion.renameCtrlRefs(oldName, newName)

    def renameCtrl(self, oldName, newName):
        """ Rename control, references to control and update parent tree """

        prel, pref = self.buildParentRelationship()

        # rename other ctrl references like parent
        children = pref[oldName].keys()
        self.renameCtrlAndParentRefs(oldName, newName, children)

        InspectableObjectView.renameCtrl(self, oldName, newName)

        if self.sizersView:
            self.sizersView.designerRenameNotify(oldName, newName)
        selName = self.inspector.containment.selectedName()
        if selName == oldName:
            selName = newName

        self.refreshContainment(selName)

        if self.selection.name == oldName:
            self.selection.name = newName

    def renameFrame(self, oldName, newName):
        """ Hook that also updates the Model and window ids of the
            Frame when it's name changes """
        self.SetName(newName)

        # propagate rename to model
        self.model.renameMain(oldName, newName)

        # update window ids in designer and data
        InspectableObjectView.renameFrame(self, oldName, newName)
        self.dataView.renameFrame(oldName, newName)
        if self.sizersView:
            self.sizersView.renameFrame(oldName, newName)

        # update window ids in collection items
        collEditors = self.collEditors.values() + \
                      self.dataView.collEditors.values()
        if self.sizersView:
            collEditors.extend(self.sizersView.collEditors.values())

        for collEditor in collEditors:
            collEditor.renameFrame(oldName, newName)

        # propagate rename to inspector
        selName = self.inspector.containment.selectedName()
        if selName == oldName: selName = ''

        self.refreshContainment(selName)

    def refreshCtrl(self):
        """ Model View method that is called when the Designer should
            create itself from source
        """
        if self.destroying or self.opened: return

        # Delete previous
        comps = {}

        # Create selection if none is defined
        if not self.selection:
            self.selection = \
                  SelectionTags.SingleSelectionGroup(self, self.inspector, self)

        self.model.editor.statusBar.setHint(_('Creating frame'))

        try:
            objCol = self.model.objectCollections[self.collectionMethod]
            objCol.indexOnCtrlName()

            self.model.editor.statusBar.progress.SetValue(20)

            stepsDone = 20.0

            # Initialise the design time controls and
            # companion with default values
            # initObjectsAndCompanions(creators, props, events)

            self.inspector.vetoSelect = True
            try:
                # init main construtor
                self.companion.setConstr(self.model.mainConstr)
                ctrlCompn = self.companion
                deps = {}
                depLnks = {}

                self.initObjProps(objCol.propertiesByName, '', objCol.creators[0], deps, depLnks)
                self.initObjEvts(objCol.eventsByName, '', objCol.creators[0])

                if len(objCol.creators) > 1:
                    self.initObjectsAndCompanions(objCol.creators[1:], objCol, deps, depLnks)

                    # Track progress
                    step = (90 - stepsDone) / len(objCol.creators)
                    stepsDone = stepsDone + step
                    self.model.editor.statusBar.progress.SetValue(int(stepsDone))

                self.finaliseDepLinks(depLnks)

                self.OnRelayoutDesigner(None)

                if len(depLnks):
                    wx.LogWarning(pprint.pformat(depLnks))
                    wx.LogWarning(_('These links were not resolved (Details...)'))

            finally:
                self.inspector.vetoSelect = False

            self.model.editor.statusBar.progress.SetValue(80)
            self.refreshContainment()
            self.model.editor.statusBar.progress.SetValue(0)
            self.model.editor.statusBar.setHint(_('Designer refreshed'))
            self.opened = True
        except:
            self.model.editor.statusBar.progress.SetValue(0)
            #self.model.editor.statusBar.setHint('Error opening the Designer', 'Error')
            raise

    def refreshModel(self):
        """ Update model with streamed out controls """
        # Make source r/w
        self.model.views['Source'].disableSource(False)

        if self.saveOnClose:
            oldData = self.model.data

            module = self.model.getModule()
            # Stream out everything to Module & update model
            otherRefs = self.dataView.objectOrder[:]
            if self.sizersView:
                otherRefs.extend(self.sizersView.objectOrder[:])
            self.saveCtrls(otherRefs, module)
            self.dataView.saveCtrls([], module)
            if self.sizersView:
                self.sizersView.saveCtrls([], module)
            self.model.refreshFromModule()

            # Close data view before updates
            self.dataView.deleteFromNotebook('Source', 'Data')
            if self.sizersView:
                self.sizersView.deleteFromNotebook('Source', 'Sizers')

            # Update state (if changed)
            newData = self.model.data
            self.model.modified = self.model.modified or newData != oldData
            self.model.editor.updateModulePage(self.model)

            # Update other views
            InspectableObjectView.refreshModel(self)

            # Put the cursor somewhere (ideally at the first generated event)
            module = self.model.getModule()
            if module:
                self.model.views['Source'].GotoLine(module.classes[\
                  self.model.main].methods['__init__'].start)

            self.model.editor.setStatus(_('Designer session Posted.'))
        else:
            self.dataView.deleteFromNotebook('Source', 'Data')
            if self.sizersView:
                self.sizersView.deleteFromNotebook('Source', 'Sizers')

            self.model.editor.setStatus(_('Designer session Cancelled.'), 'Warning')

    def initSelection(self):
        """ Create a selection group """
        self.selection = SelectionTags.SingleSelectionGroup(self, self.inspector, self)

    def loadControl(self, CtrlClass, CtrlCompanion, ctrlName, params):
        """ Create and register given control and companion.
            See also: newControl
        """

        args = self.setupArgs(ctrlName, params, CtrlCompanion.handledConstrParams,
              compClass=CtrlCompanion, evalDct=self.model.specialAttrs)

        parent = Utils.ctrlNameFromSrcRef(params[CtrlCompanion.windowParentName])

        # Create control and companion
        companion = CtrlCompanion(ctrlName, self, None, CtrlClass)

        self.addObject(ctrlName, companion,
          companion.designTimeControl(None, None, args), parent)

        return ctrlName

    def newControl(self, parent, CtrlClass, CtrlCompanion, position = None, size = None):
        """ At design time, when adding a new ctrl from the palette, create and
            register given control and companion.
            See also: loadControl
        """
        self.checkHost(CtrlCompanion)

        ctrlName = self.newObjName(CtrlClass.__name__)
        companion = CtrlCompanion(ctrlName, self, parent, CtrlClass)
        params = companion.designTimeSource('wx.Point(%d, %d)' % (position.x, position.y))
        parentName, params[companion.windowParentName] = self.getParentNames(parent)

        self.addObject(ctrlName, companion,
          companion.designTimeControl(position, size), parentName)

        if not companion.suppressWindowId:
            params[companion.windowIdName] = companion.id

        companion.persistConstr(Utils.getWxPyNameForClass(CtrlClass), params)

        self.refreshContainment()

        return ctrlName

    def initObjCreator(self, constrPrs):
        # decorate class_name if it's a factory constructor
        if not constrPrs.class_name and constrPrs.factory:
            factoryObj, factoryMeth = constrPrs.factory
            if factoryObj in self.dataView.objects:
                constrPrs.class_name = self.dataView.objects[factoryObj][0].factory(factoryMeth)
            elif factoryObj in self.objects:
                constrPrs.class_name = self.objects[factoryObj][0].factory(factoryMeth)
            else:
                raise Exception, _('%s not found')%factoryObj
        InspectableObjectView.initObjCreator(self, constrPrs)

    def initSizers(self, sizersView):
        self.sizersView = sizersView
        self.sizersView.controllerView = self

    def getParentNames(self, parent):
        if parent.GetName() != self.GetName():
            return parent.GetName(), 'self.'+parent.GetName()
        else:
            return '', 'self'

    def removeEvent(self, name):
        # XXX Remove event!
        self.inspector.eventUpdate(name, True)

    def getObjectsOfClass(self, theClass):
        """ Overridden to also add objects from the other views """
        results = InspectableObjectView.getObjectsOfClass(self, theClass)
        otherResults = {}
        for objName in self.dataView.objects.keys():
            if isinstance(self.dataView.objects[objName][1], theClass):
                otherResults['self.'+objName] = self.dataView.objects[objName][1]
        if self.sizersView:
            for objName in self.sizersView.objects.keys():
                if isinstance(self.sizersView.objects[objName][1], theClass):
                    otherResults['self.'+objName] = self.sizersView.objects[objName][1]
        results.update(otherResults)
        return results

    def getAllObjects(self):
        """ Overridden to also add objects from other views """
        results = InspectableObjectView.getAllObjects(self)
        for objName in self.dataView.objects.keys():
            results[Utils.srcRefFromCtrlName(objName)] = \
                  self.dataView.objects[objName][1]
        if self.sizersView:
            for objName in self.sizersView.objects.keys():
                results[Utils.srcRefFromCtrlName(objName)] = \
                      self.sizersView.objects[objName][1]
        return results

    def selectParent(self, ctrl):
        """ Change the selection to the parent of the currently selected control. """
        if self.selection or self.multiSelection:
            if self.multiSelection:
                self.clearMultiSelection()
                self.assureSingleSelection()

            if ctrl and ctrl != self:
                parentName, dummy = self.getParentNames(ctrl.GetParent())
                self.inspector.containment.selectName(parentName)


    def deleteCtrl(self, name, parentRef = None):
        """ Delete a control, update selection and parent tree """
        ctrlInfo = self.objects[name]
        if ctrlInfo[1] == self:
            wx.MessageBox(_("Can't delete frame"), style=wx.OK | wx.ICON_ERROR, parent=self)
            return
        parRel = None
        # build relationship, this will only happen for the first call
        if not parentRef:
            # select parent so long, pretty soon won't be able to ask who
            # the parent is
            parentName, dummy = self.getParentNames(ctrlInfo[1].GetParent())

            self.selectParent(ctrlInfo[1])
            parRel, parRef = self.buildParentRelationship()
        else:
            parRef = parentRef

        # notify other components of deletion
        self.notifyAction(ctrlInfo[0], 'delete')

        # delete all children
        children = parRef[name]
        for child in children.keys():
            self.deleteCtrl(child, parRef)

        InspectableObjectView.deleteCtrl(self, name)

        ctrlInfo[1].Destroy()

        if parRel is not None:
            self.refreshContainment(parentName)

    def selectNone(self):
        if self.selection:
            self.selection.selectNone()
        elif self.multiSelection:
            for sel in self.multiSelection:
                sel.selectNone()
                sel.destroy()
            self.multiSelection = []
            self.assureSingleSelection()

    def notifyAction(self, compn, action):
        InspectableObjectView.notifyAction(self, compn, action)
        self.dataView.notifyAction(compn, action)
        if self.sizersView:
            self.sizersView.notifyAction(compn, action)

    def close(self):
        self.Close()

    def focus(self):
        self.restore()

    def getSizerConnectList(self):
        if self.sizersView:
            return self.sizersView.sizerConnectList
        else:
            return None

    ignoreWindows = [wx.ToolBar, wx.StatusBar]

    def connectToolBar(self, toolBar):
        parRel, parRef = self.buildParentRelationship()
        children = parRef['']
        for childName in children.keys():
            childCompn, childCtrl = self.objects[childName][:2]
            if not childCtrl.__class__ in self.ignoreWindows:
                pos = childCtrl.GetPosition()
                childCtrl.SetPosition( (pos.x, pos.y + toolBar.GetSize().y) )

    def disconnectToolBar(self, toolBar):
        parRel, parRef = self.buildParentRelationship()
        children = parRef['']
        for childName in children.keys():
            childCompn, childCtrl = self.objects[childName][:2]
            if not childCtrl.__class__ in self.ignoreWindows:
                pos = childCtrl.GetPosition()
                childCtrl.SetPosition( (pos.x, pos.y - toolBar.GetSize().y) )

    def checkChildCtrlClick(self, ctrlName, ctrl, companion, clickPos):
        """ Check whether the click on the control actually falls
            within a region occupied by one of it's children.
            The click is then transfered to the child. """
        selCtrl, selCompn, selPos = ctrl, companion, clickPos


        if companion.container:
            parent = ctrl
        else:
            parent = ctrl.GetParent()

        # Hack: Shortcut intersection tests if click was directly in a proxy
        #       container
        if wx.Platform == '__WXGTK__' and hasattr(ctrl, 'proxyContainer'):
            return selCtrl, selCompn, selPos

        # Workaround toolbar offset bug
        offset = [0, 0]
        if parent == self:
            tb = self.GetToolBar()
            if tb:
                offset[1] = tb.GetSize().y #* -1

        # XXX Is this going to become to slow for frames with many ctrls?
        parRel, parRef = self.buildParentRelationship()
        if ctrl == self:
            officialParent = ''
            children = parRef['']
        else:
            officialParent = ctrlName
            children = parRef[ctrlName]

        for childName in children.keys():
            childCompn, childCtrl = self.objects[childName][:2]
            pos = childCtrl.GetPosition()
            sze = childCtrl.GetSize()
            realParent = childCtrl.GetParent()
            # Compensate for BlankWindowPages's offset
            if realParent != self.objects[officialParent][1]:
                offset[0] += realParent.GetPosition().x
                offset[1] += realParent.GetPosition().y

            # Check for intersection
            if childCtrl.IsShown() and realParent.IsShown() and \
                  wx.IntersectRect(wx.Rect(clickPos.x - offset[0],
                                     clickPos.y - offset[1], 1, 1),
                                   wx.Rect(pos.x, pos.y, max(sze.x, 1),
                                     max(sze.y, 1))) is not None:

                #print clickPos, offset, pos, sze

                selCtrl = childCtrl
                selCompn = childCompn
                selPos = wx.Point(clickPos.x - offset[0] - pos.x,
                      clickPos.y - offset[1] - pos.y)
                break

        return selCtrl, selCompn, selPos

    def clearMultiSelection(self):
        """ Destroys multi selection groups """
        for sel in self.multiSelection:
            sel.destroy()
        self.multiSelection = []

    def assureSingleSelection(self):
        """ Assure that a valid single selection exists """
        if not self.selection:
            self.selection = SelectionTags.SingleSelectionGroup(self, self.inspector, self)

    def flattenParentRelationship(self, rel, lst):
        """ Add all items in a nested dictionary into a single list """
        for itm in rel.keys():
            lst.append(itm)
            self.flattenParentRelationship(rel[itm], lst)

    def expandNamesToContainers(self, ctrlNames):
        """ Expand set of names to include the names of all their children """
        exp = ctrlNames[:]
        rel, ref = self.buildParentRelationship()
        for ctrl in ctrlNames:
            children = []
            self.flattenParentRelationship(ref[ctrl], children)
            exp.extend(children)

        return exp

    def collapseNamesToContainers(self, ctrlNames):
        """ Collapse set of names to exclude the names of all their children """

        def hasParentInList(item, list):
            return intem in list
        exp = ctrlNames[:]

        colLst = [name for name in ctrlNames
                  if self.objects[name][2] not in ctrlNames]

        return colLst

    def buildSizerInfo(self, sizer, res):
        sp = sizer.GetPosition()
        ss = sizer.GetSize()
        res.append( (wx.Rect(sp.x, sp.y, ss.width, ss.height), sizer) )

        if sizer.__class__.__name__ == 'BlankSizer':
            return

        c = sizer.GetChildren()
        for sc in c:
            if sc.IsSizer():
                self.buildSizerInfo(sc.GetSizer(), res)
            else:
                sp = sc.GetPosition()
                ss = sc.GetSize()
                res.append( (wx.Rect(sp.x, sp.y, ss.width, ss.height), sc) )


    def selectControlByPos(self, ctrl, pos, multiSelect):
        """ Handle selection of a control from a users click of creation
            of a new one if a component was selected on the palette.

            Some ctrls do not register clicks, the click is then
            picked up from the parent which checks if a click
            intersects any child regions. For efficiency this
            is only applied for 2 levels.

            Also handles single and multiple selection logic.
        """

        # Patch to workaround SizerItem identity problem
        def _contains(d, k):
            for key in d:
                if str(k) == str(key):
                    return True
            return False
        def _get(d, k):
            for key in d:
                if str(k) == str(key):
                    return d[key]
            raise KeyError, k

        self.vetoResize = True
        try:
            if ctrl == self:
                companion = self.companion
            else:
                companion = self.objects[ctrl.GetName()][0]

            ctrlName = companion.name

            selCtrl, selCompn, selPos = \
                  self.checkChildCtrlClick(ctrlName, ctrl, companion, pos)

            # Component on palette selected, create it
            if self.compPal.selection:
                if selCompn.container:
                    parent = selCtrl
                    pos = selPos
                else:
                    parent = selCtrl.GetParent()
                    screenPos = selCtrl.ClientToScreen(selPos)
                    pos = parent.ScreenToClient(screenPos)

                # Workaround toolbar offset bug
                if parent == self:
                    tb = self.GetToolBar()
                    if tb:
                        pos.y = pos.y - tb.GetSize().y

                # Granularise position
                pos = wx.Point(SelectionTags.granularise(pos.x),
                               SelectionTags.granularise(pos.y))

                CtrlClass, CtrlCompanion = self.compPal.selection[1:3]
                
                destSizer, destSizerCmpn = None, None
                parentSzr = parent.GetSizer()
                if parentSzr:
                    # build a mapping from sizers to companions
                    sizerCompns = {}
                    view = self.model.views['Sizers']#CtrlCompanion.host]
                    for name, vals in view.objects.items():
                        compn = vals[0]
                        sizerCompns[compn.control] = compn
                        items = compn.control.GetChildren()
                        for idx in range(len(items)):
                            si = items[idx]
                            if si.IsSizer() and si.GetSizer().__class__.__name__=='BlankSizer':
                                sizerCompns[si.GetSizer()] = (compn.collections['Items'], idx)#compn.collections['Items'].textConstrLst[idx]
                            else:
                                sizerCompns[si] = (compn.collections['Items'], idx)
                    res = []
                    self.buildSizerInfo(parentSzr, res)
                    res.reverse() # find deepest regions first
                    for rect, s in res:
                        if wx.IntersectRect(rect, wx.Rect(pos.x, pos.y, 1, 1)):
                            #if s in sizerCompns:
                            if _contains(sizerCompns, s):
                                destSizer = s
                                #destSizerCmpn = sizerCompns[s]
                                destSizerCmpn = _get(sizerCompns, s)
                                break
                            else:
                                # sizer item
                                print 'err', s

                if CtrlCompanion.host == 'Sizers':
                    # create sizer
                    view = self.model.views[CtrlCompanion.host]
                    ctrlName = view.OnSelectOrAdd()
                    if ctrlName is not None:
                        compn, sizer = view.objects[ctrlName][:2]
                        ctrlSzr = selCompn.GetSizer(None)
                        
                        if destSizer is not None:
                            # sizer dropped on sizer item
                            if type(destSizerCmpn) is type(()):
                                destSizerCmpn, sizerItemIdx = destSizerCmpn

                                tcl = destSizerCmpn.textConstrLst[sizerItemIdx]
                                if tcl.params[0] == 'None':
                                    tcl.method = 'AddSizer'
                                    tcl.params[0] = 'self.%s'%ctrlName
                                    destSizerCmpn.recreateSizers()

                                    collEditView = SelectionTags.openCollEditorForSizerItems(
                                        self.inspector, destSizerCmpn.parentCompanion, 
                                        destSizerCmpn.designer, destSizerCmpn.parentCompanion.control)
                                    if collEditView is not None:    
                                        collEditView.refreshCtrl()
                                        if collEditView.frame:
                                            collEditView.frame.selectObject(sizerItemIdx)

                                    return
                                else:
                                    collEditView = SelectionTags.openCollEditorForSizerItems(
                                        self.inspector, destSizerCmpn.parentCompanion, 
                                        destSizerCmpn.designer, destSizerCmpn.parentCompanion.control)
                                    if collEditView is not None:    
                                        ci = collEditView.companion.appendItem(
                                              method='AddSizer', 
                                              srcParams={0: 'self.%s'%ctrlName})
                                        collEditView.refreshCtrl()
                                        collEditView.selectObject(
                                              collEditView.frame.itemList.GetItemCount() -1)
                                        return

                            # sizer dropped on sizer
                            else:
                                collEditView = SelectionTags.openCollEditorForSizerItems(
                                    self.inspector, destSizerCmpn, 
                                    destSizerCmpn.designer, destSizer)
                                if collEditView is not None:    
                                    ci = collEditView.companion.appendItem(
                                          method='AddSizer', 
                                          srcParams={0: 'self.%s'%ctrlName})
                                    collEditView.refreshCtrl()
                                    collEditView.selectObject(
                                          collEditView.frame.itemList.GetItemCount() -1)
                                    return
                            
                        
                        # no sizer on the parent ctrl, link this new sizer to it
                        if ctrlSzr is None:
                            selCompn.SetSizer(sizer)
                            selCompn.persistProp('Sizer', 'SetSizer', 'self.%s'%compn.name)
                            return 

                        # parent control already has a sizer, add this sizer as a sizer item
                        else:
                            collEditView = SelectionTags.openCollEditorForSizerItems(
                                self.inspector, selCompn)
                            if collEditView is not None:    
                                ci = collEditView.companion.appendItem(
                                      method='AddSizer', 
                                      srcParams={0: 'self.%s'%ctrlName})
                                collEditView.refreshCtrl()
                                collEditView.selectObject(
                                      collEditView.frame.itemList.GetItemCount() -1)
                                return
                    
                if CtrlCompanion.host in ('Data', 'Sizers'):
                    view = self.model.views[CtrlCompanion.host]
                    view.focus()
                    view.OnSelectOrAdd()
                    return

                # create ctrl
                ctrlName = self.newControl(parent, CtrlClass, CtrlCompanion, pos)
                self.compPal.selectNone()
                view = self.model.views[CtrlCompanion.host]
                compn, sizer = view.objects[ctrlName][:2]
                ctrlSzr = selCompn.GetSizer(None)
                if ctrlSzr is not None:
                    if destSizer is not None:
                        # ctrl dropped on sizer item
                        if type(destSizerCmpn) is type(()):
                            destSizerCmpn, sizerItemIdx = destSizerCmpn
                            tcl = destSizerCmpn.textConstrLst[sizerItemIdx]
                            tcl.method = 'AddWindow'
                            tcl.params[0] = 'self.%s'%ctrlName
                            if tcl.params[1] and tcl.params[1][0] != '(' and tcl.params[1][-1] != ')':
                                tcl.params[1] = '0'
                            destSizerCmpn.recreateSizers()

                            collEditView = SelectionTags.openCollEditorForSizerItems(
                                self.inspector, destSizerCmpn.parentCompanion, 
                                destSizerCmpn.designer, destSizerCmpn.parentCompanion.control)
                            if collEditView is not None:    
                                collEditView.refreshCtrl()
                                collEditView.selectObject(sizerItemIdx)

                            return

                        # ctrl dropped on sizer
                        collEditView = SelectionTags.openCollEditorForSizerItems(
                            self.inspector, destSizerCmpn, 
                            destSizerCmpn.designer, destSizer)
                        if collEditView is not None:    
                            ci = collEditView.companion.appendItem(
                                  srcParams={0: 'self.%s'%ctrlName})
                            collEditView.refreshCtrl()
                            collEditView.selectObject(
                                  collEditView.frame.itemList.GetItemCount() -1)
                            return

                    collEditView = SelectionTags.openCollEditorForSizerItems(
                        self.inspector, selCompn)
                    if collEditView is not None:    
                        ci = collEditView.companion.appendItem(srcParams={0: 'self.%s'%ctrlName})
                        collEditView.refreshCtrl()
                        collEditView.selectObject(collEditView.frame.itemList.GetItemCount() -1)
                else:
                    prntCtrlSzr = parent.GetSizer()
                    if prntCtrlSzr is not None:
                        if destSizer is not None:
                            # ctrl dropped on sizer item with ctrl, append to items
                            if type(destSizerCmpn) is type(()):
                                destSizerCmpn, sizerItemIdx = destSizerCmpn
                                collEditView = SelectionTags.openCollEditorForSizerItems(
                                    self.inspector, destSizerCmpn.parentCompanion, 
                                    destSizerCmpn.designer, prntCtrlSzr)#destSizer)
                                if collEditView is not None:    
                                    ci = collEditView.companion.appendItem(
                                          srcParams={0: 'self.%s'%ctrlName})
                                    collEditView.refreshCtrl()
                                    collEditView.selectObject(
                                          collEditView.frame.itemList.GetItemCount() -1)
                                return

                    
                
                if self.selection:
                    ctrl = self.objects[ctrlName][1]
                    self.selection.selectCtrl(ctrl, self.objects[ctrlName][0])
            # Select ctrl
            else:
                if self.selection or self.multiSelection:
                    # Multi selection
                    if multiSelect:
                        # Verify that it's a legal multi selection
                        # They must have the same parent
                        if self.selection:
                            if selCtrl.GetParent() != self.selection.selection.GetParent():
                                return
                        elif self.multiSelection:
                            if selCtrl.GetParent() != self.multiSelection[0].selection.GetParent():
                                return

                        if not self.multiSelection:
                            # don't select if multiselecting single selection
                            if selCtrl == self.selection.selection:
                                return

                            newSelection = SelectionTags.MultiSelectionGroup(self,
                                  self.inspector, self)
                            newSelection.assign(self.selection)
                            self.selection.destroy()
                            self.selection = None
                            self.multiSelection = [newSelection]

                        # Check that this is not a de-selection
                        # don't deselect if there's only one
                        if len(self.multiSelection) > 1:
                            for selIdx in range(len(self.multiSelection)):
                                if self.multiSelection[selIdx].selection == ctrl:
                                    self.multiSelection[selIdx].destroy()
                                    del self.multiSelection[selIdx]
                                    # Change to single selection if 2nd last one
                                    # deselected
                                    if len(self.multiSelection) == 1:
                                        self.selection = SelectionTags.SingleSelectionGroup(self,
                                            self.inspector, self)

                                        self.selection.assign(self.multiSelection[0])
                                        self.selection.selectCtrl(self.multiSelection[0].selection,
                                              self.multiSelection[0].selCompn)
                                        self.clearMultiSelection()
                                    return

                        newSelection = SelectionTags.MultiSelectionGroup(self,
                              self.inspector, self)
                        newSelection.selectCtrl(selCtrl, selCompn)
                        self.multiSelection.append(newSelection)
                    # Single selection
                    else:
                        # Deselect multiple selection or start multiple drag
                        if self.multiSelection:
                            for sel in self.multiSelection:
                                if selCtrl == sel.selection:
                                    sel.moveCapture(selCtrl, selCompn, selPos)
                                    self.mainMultiDrag = selCtrl
                                    others = self.multiSelection[:]
                                    others.remove(sel)
                                    for capSel in others:
                                        capSel.moveCapture(capSel.selection, capSel.selCompn, selPos)
                                    return

                            self.clearMultiSelection()

                        self.assureSingleSelection()

                        self.selection.selectCtrl(selCtrl, selCompn)
                        self.selection.moveCapture(selCtrl, selCompn, selPos)

                        return
        finally:
            self.vetoResize = False

    def OnFramePos(self, event):
        """ Called when frame is repositioned """
#        self.assureSingleSelection()
#        self.selection.selectCtrl(self, self.companion)
        if self.selection and self.selection.selection == self:
            self.inspector.constructorUpdate('Position')
            self.inspector.propertyUpdate('Position')
        event.Skip()

    def OnCloseWindow(self, event):
        """ When the Designer closes, the code generation process is started.
            General Inspector and Designer clean-up """

        if not self.saveOnClose and self.confirmCancel and wx.MessageBox(
              _('Cancel Designer session?'), _('Cancel'),
              wx.YES_NO | wx.ICON_WARNING, parent=None) == wx.NO:
            self.saveOnClose = True
            self.confirmCancel = False
            return

        if self.IsIconized():
            self.Iconize(False)

        # XXX Should handle errors more gracefully here
        self.destroying = True
        self.vetoResize = True
        try:
            if self.selection:
                self.selection.destroy()
                self.selection = None
            if self.multiSelection:
                for sel in self.multiSelection:
                    sel.destroy()
                self.multiSelection = None

            self.inspector.cleanup()
            self.inspector.containment.cleanup()

            # generate source
            self.refreshModel()
        except:
            self.destroying = False
            self.vetoResize = False
            raise

        self.menu.Destroy()
        self.cleanup()
        self.Show(False)
        self.Destroy()

        del self.model.views['Designer']
        del self.companion

        self.destroy()
        event.Skip()

    def OnRightDown(self, event):
        """ Store popup position of the menu relative to the control that
            triggered the event """
        ctrl = event.GetEventObject()
        screenPos = ctrl.ClientToScreen(wx.Point(event.GetX(), event.GetY()))
        parentPos = self.ScreenToClient(screenPos)
        self.popx = parentPos.x
        self.popy = parentPos.y

    def OnEditor(self, event):
        """ Bring Editor to the front """
        self.model.editor.restore()
        self.model.editor.modules[self.model.filename].focus()

    def OnInspector(self, event):
        """ Bring Inspector to the front """
        self.inspector.restore()
        if self.inspector.pages.GetSelection() > 3:
            self.inspector.pages.SetSelection(0)


    def OnControlDelete(self, event):
        """ Delete the currently selected controls """
        if self.deletingCtrl: return
        self.deletingCtrl = True
        try:
            ctrls = []
            if self.selection:
                if self.selection.isProxySelection():
                    wx.LogError(_('Nothing to delete'))
                    return
                ctrls = [self.selection.name]
            elif self.multiSelection:
                ctrls = [sel.name for sel in self.multiSelection]

            #map(self.deleteCtrl, ctrls)
            for ctrlName in ctrls:
                self.deleteCtrl(ctrlName)
        finally:
            self.deletingCtrl = False

    def OnCtrlHelp(self, event):
        """ Show help for the selected control """
        if self.inspector.selCmp:
            Help.showCtrlHelp(self.inspector.selCmp.GetClass())

    def OnAlignSelected(self, event=None):
        """ Show alignment dialog for multi selections"""
        if self.multiSelection:
            dlg = CtrlAlign.ControlAlignmentFrame(self, self.multiSelection)
            try: dlg.ShowModal()
            finally: dlg.Destroy()

    def OnSizeSelected(self, event=None):
        """ Show size dialog for multi selections"""
        if self.multiSelection:
            dlg = CtrlSize.ControlSizeFrame(self, self.multiSelection)
            try: dlg.ShowModal()
            finally: dlg.Destroy()

    def OnSelectParent(self, event=None):
        """ Select parent of the selected control """
        if self.selection:
            self.selectParent(self.selection.selection)
        elif self.multiSelection:
            self.selectParent(self.multiSelection[0].selection)

#---Clipboard operations--------------------------------------------------------
    def OnCutSelected(self, event):
        """ Cut current selection to the clipboard """
        if self.selection:
            if self.selection.isProxySelection():
                wx.LogError(_('Nothing to cut'))
                return
            else:
                ctrls = [self.selection.name]
            #self.selectParent(self.selection.selection)
        elif self.multiSelection:
            ctrls = [sel.name for sel in self.multiSelection]

        output = []
        self.cutCtrls(ctrls, [], output)

        Utils.writeTextToClipboard(os.linesep.join(output))

        self.refreshContainment()

    def OnCopySelected(self, event):
        """ Copy current selection to the clipboard """
        if self.selection:
            if self.selection.isProxySelection():
                wx.LogError(_('Nothing to copy'))
                return
            else:
                ctrls = [self.selection.name]
        elif self.multiSelection:
            ctrls = [sel.name for sel in self.multiSelection]
        output = []
        self.copyCtrls(ctrls, [], output)

        Utils.writeTextToClipboard(os.linesep.join(output))

    def OnPasteSelected(self, event):
        """ Paste current clipboard contents into the current selection """
        if self.selection:
            # If the selection is not a container, select it's parent (a container)
            if not self.selection.selCompn.container:
                self.selectParent(self.selection.selection)

            pasted = self.pasteCtrls(self.selection.name,
                  str(Utils.readTextFromClipboard()).split(os.linesep))

            if len(pasted):
                self.refreshContainment()
                pasted = self.collapseNamesToContainers(pasted)
                # Single control pasted, select it
                if len(pasted) == 1:
                    if self.selection.isProxySelection():
                        self.selection.selection.linkToNewestControl()
                        self.objects[pasted[0]][1].Reparent(self.selection.selection)

                    self.selection.selectCtrl(self.objects[pasted[0]][1],
                          self.objects[pasted[0]][0])
                # Multiple controls pasted, select them
                else:
                    if self.selection.isProxySelection():
                        # Undo the pasting
                        for ctrlName in pasted:
                            self.deleteCtrl(ctrlName)
                        self.selection.selectNone()
                        self.inspector.cleanup()
                        wx.LogError(_('Only 1 control can be pasted into this container'))
                    else:
                        self.selection.destroy()
                        self.selection = None
                        self.multiSelection = []
                        for ctrlName in pasted:
                            selCompn, selCtrl, prnt = self.objects[ctrlName]
                            newSelection = SelectionTags.MultiSelectionGroup(self,
                                  self.inspector, self)
                            newSelection.selectCtrl(selCtrl, selCompn)
                            self.multiSelection.append(newSelection)

    def OnRecreateSelected(self, event):
        """ Recreate the current selection by cutting and pasting it.
            The clipboard is not disturbed.
            This is useful for applying changes to constructor parameters """
        if self.selection and self.selection.selection != self:
            output = []
            ctrlName = self.selection.name
            # XXX Boa should be able to tell me this
            parent = self.selection.selection.GetParent()
            parentName, dummy = self.getParentNames(parent)

            self.cutCtrls([ctrlName], [], output)
            self.pasteCtrls(parentName, output)

            self.refreshContainment()
            self.inspector.containment.selectName(ctrlName)

#---Moving/Sizing selections with the keyboard----------------------------------
    def getSelAsList(self):
        if self.selection:
            return [self.selection]
        elif self.multiSelection:
            return self.multiSelection
        else:
            return []

    def moveUpdate(self, sel):
        sel.setSelection(True)
        sel.resizeCtrl()

    def OnMoveLeft(self, event):
        for sel in self.getSelAsList():
            if sel.selection != self:
                sel.position.x = sel.position.x - 1
                sel.startPos.x = sel.startPos.x - 1
                self.moveUpdate(sel)
    def OnMoveRight(self, event):
        for sel in self.getSelAsList():
            if sel.selection != self:
                sel.position.x = sel.position.x + 1
                sel.startPos.x = sel.startPos.x + 1
                self.moveUpdate(sel)
    def OnMoveUp(self, event):
        for sel in self.getSelAsList():
            if sel.selection != self:
                sel.position.y = sel.position.y - 1
                sel.startPos.y = sel.startPos.y - 1
                self.moveUpdate(sel)
    def OnMoveDown(self, event):
        for sel in self.getSelAsList():
            if sel.selection != self:
                sel.position.y = sel.position.y + 1
                sel.startPos.y = sel.startPos.y + 1
                self.moveUpdate(sel)

    def sizeUpdate(self, sel):
        sel.resizeCtrl()
        sel.setSelection(True)

    def OnWidthInc(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            sel.size.x = sel.size.x + 1
            self.sizeUpdate(sel)
    def OnWidthDec(self, event):
        sel = self.selection
        if sel and sel.selection != self and sel.size.x > 0:
            sel.size.x = sel.size.x - 1
            self.sizeUpdate(sel)
    def OnHeightInc(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            sel.size.y = sel.size.y + 1
            self.sizeUpdate(sel)
    def OnHeightDec(self, event):
        sel = self.selection
        if sel and sel.selection != self and sel.size.y > 0:
            sel.size.y = sel.size.y - 1
            self.sizeUpdate(sel)

#---Cursor selection------------------------------------------------------------
    def selectInDimentionDirection(self, selctrl, dim, dir):
        def compSides(selctrl, ctrl, dim, dir):
            selpos, selsize = selctrl.GetPosition(), selctrl.GetSize()
            pos, size = ctrl.GetPosition(), ctrl.GetSize()
            selMidPoint = wx.Point(selpos.x + selsize.x/2, selpos.y + selsize.y/2)
            ctrlMidPoint = wx.Point(pos.x + size.x/2, pos.y + size.y/2)
            if (dim, dir) == ('x', 1):
                return (wx.Point(selpos.x + selsize.x, selpos.y), selMidPoint,
                        wx.Point(selpos.x + selsize.x, selpos.y + selsize.y),
                        wx.Point(pos.x, pos.y), ctrlMidPoint,
                        wx.Point(pos.x, pos.y + size.y) )
            if (dim, dir) == ('x', -1):
                return (wx.Point(selpos.x, selpos.y), selMidPoint,
                        wx.Point(selpos.x, selpos.y + selsize.y),
                        wx.Point(pos.x + size.x, pos.y), ctrlMidPoint,
                        wx.Point(pos.x + size.x, pos.y + size.y) )
            if (dim, dir) == ('y', 1):
                return (wx.Point(selpos.x, selpos.y + selsize.y), selMidPoint,
                        wx.Point(selpos.x + selsize.x, selpos.y + selsize.y),
                        wx.Point(pos.x, pos.y), ctrlMidPoint,
                        wx.Point(pos.x + size.x, pos.y) )
            if (dim, dir) == ('y', -1):
                return (wx.Point(selpos.x, selpos.y), selMidPoint,
                        wx.Point(selpos.x + selsize.x, selpos.y),
                        wx.Point(pos.x, pos.y + size.y), ctrlMidPoint,
                        wx.Point(pos.x + size.x, pos.y + size.y) )

        dims = ['x', 'y']
        otherdim = dims[not dims.index(dim)]
        parentName, dummy = self.getParentNames(selctrl.GetParent())
        selName = selctrl.GetName()

        distLo = -1
        nearestCtrl = None
        for objName in self.objects.keys():
            ctrl, parent = self.objects[objName][1:3]
            if parent == parentName and objName != selName:

                pos1p, pos0p, pos2p, cpos1p, cpos0p, cpos2p = compSides(selctrl, ctrl, dim, dir)
                pos, otherpos1, otherpos2  = getattr(pos1p, dim), \
                      getattr(pos1p, otherdim), getattr(pos2p, otherdim)
                pos0, otherpos0 = getattr(pos0p, dim), getattr(pos0p, otherdim)
                cpos, cotherpos1, cotherpos2 = getattr(cpos1p, dim), \
                      getattr(cpos1p, otherdim), getattr(cpos2p, otherdim)
                cpos0, cotherpos0 = getattr(cpos0p, dim), getattr(cpos0p, otherdim)

                dpos, dotherpos1, dotherpos2 = cpos - pos, \
                      cotherpos1 - otherpos1, cotherpos2 - otherpos2
                dpos0, dotherpos0 = cpos0 - pos0, cotherpos0 - otherpos0

                if (abs(dpos) >= min(abs(dotherpos1), abs(dotherpos2)) and \
                    (not dpos or dpos/abs(dpos) == dir)) or \
                   (abs(dpos0) >= abs(dotherpos0) and \
                    (not dpos0 or dpos0/abs(dpos0) == dir)):
                    dist = min(math.sqrt(dpos*dpos+dotherpos1*dotherpos1),
                               math.sqrt(dpos0*dpos0+dotherpos0*dotherpos0),
                               math.sqrt(dpos*dpos+dotherpos2*dotherpos2))

                    if distLo == -1 or dist < distLo:
                        distLo = dist
                        nearestCtrl = ctrl

        if nearestCtrl:
            self.inspector.containment.selectName(nearestCtrl.GetName())

    def OnSelectLeft(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            self.selectInDimentionDirection(sel.selection, 'x', -1)

    def OnSelectRight(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            self.selectInDimentionDirection(sel.selection, 'x', 1)

    def OnSelectUp(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            self.selectInDimentionDirection(sel.selection, 'y', -1)

    def OnSelectDown(self, event):
        sel = self.selection
        if sel and sel.selection != self:
            self.selectInDimentionDirection(sel.selection, 'y', 1)

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

    def OnSnapToGrid(self, event):
        for sel in self.getSelAsList():
            if sel.selection != self:
                sel.position.x = SelectionTags.granularise(sel.position.x)
                sel.position.y = SelectionTags.granularise(sel.position.y)
                sel.startPos.x = sel.position.x
                sel.startPos.y = sel.position.y
                self.moveUpdate(sel)

    def relayoutCtrl(self, ctrl):
        self.forceResize = True # cleared by the event
        sizer = ctrl.GetSizer()
        if sizer:
            sizer.Layout()
        wx.PostEvent(ctrl, wx.SizeEvent(ctrl.GetSize(), ctrl.GetId()))
        wx.CallAfter(ctrl.Refresh)

    def OnRelayoutSelection(self, event):
        # for ctrl in [sel.selection for sel in self.getSelAsList()]:
        for sel in self.getSelAsList():
            self.relayoutCtrl(sel.selection)

    def OnRelayoutDesigner(self, event):
        self.relayoutCtrl(self)

    def OnFitSizer(self, event):
        for sel in self.getSelAsList():
            sizer = sel.selection.GetSizer()
            if sizer:
                sizer.Fit(sel.selection)

    def OnCreationOrder(self, event):
        sel = self.selection
        if sel:
            selName = sel.selection.GetName()
            if selName == self.GetName():
                selName = ''
            self.showCreationOrderDlg(selName)

    def OnFindInIndex(self, event):
        self.model.editor.OnHelpFindIndex(event)

#---Inspector session-----------------------------------------------------------
    def doPost(self, inspector):
        self.saveOnClose = True
        self.Close()

    def doCancel(self, inspector):
        self.saveOnClose = False
        self.confirmCancel = True
        self.Close()


class DesignerNamespace:
    def __init__(self, designer):
        self._designer = designer

    def __getattr__(self, name):
        designer = self.__dict__['_designer']
        if designer.objects.has_key(name):
            #return designer.objects[name][1]
            obj = designer.objects[name]
            if isinstance(obj[1], wx._core._wxPyDeadObject):
                obj[0].parent = designer.objects[obj[2]][1]
                obj[1] = obj[0].designTimeControl(wx.DefaultPosition, wx.DefaultSize)
                obj[0].control = obj[1]
            return obj[1]
        elif designer.dataView.objects.has_key(name):
            return designer.dataView.objects[name][1]
        elif designer.sizersView and \
              designer.sizersView.objects.has_key(name):
            return designer.sizerView.objects[name][1]
        else:
            raise AttributeError, name



class DesignerControlsEvtHandler(wx.EvtHandler):
    def __init__(self, designer):
        wx.EvtHandler.__init__(self)
        self.designer = designer

        self.drawGridMethods = {'lines' : self.drawGrid_intersectingLines,
                                'dots'  : self.drawGrid_dots,
                                'bitmap': self.drawGrid_bitmap,
                                'grid'  : self.drawGrid_grid}
        self._points = (0, 0), []

    def connectEvts(self, ctrl, connectChildren=False):
        ctrls = [ctrl]
        if connectChildren:
            ctrls.extend(ctrl.GetChildren())
        for ctrl in ctrls:
            ctrl.Bind(wx.EVT_MOTION, self.OnMouseOver)
            ctrl.Bind(wx.EVT_LEFT_DOWN, self.OnControlSelect)
            ctrl.Bind(wx.EVT_LEFT_UP, self.OnControlRelease)
            ctrl.Bind(wx.EVT_LEFT_DCLICK, self.OnControlDClick)
            ctrl.Bind(wx.EVT_SIZE, self.OnControlResize)
            ctrl.Bind(wx.EVT_MOVE, self.OnControlMove)

        if isinstance(ctrl, (wx.Panel, wx.ScrolledWindow)) or \
           ctrl.__class__ == DesignerView:
            ctrl.Bind(wx.EVT_PAINT, self.OnPaint)


    def OnMouseOver(self, event):
        if event.Dragging():
            dsgn = self.designer
            pos = event.GetPosition()
            ctrl = event.GetEventObject()

            if dsgn.selection:
                dsgn.selection.moving(ctrl, pos)
            elif dsgn.multiSelection:
                for sel in dsgn.multiSelection:
                    sel.moving(ctrl, pos, dsgn.mainMultiDrag)

        event.Skip()

    def getCtrlAndPosFromEvt(self, event):
        pos = event.GetPosition()
        ctrl = event.GetEventObject()
        # XXX only here for when testing
        if not ctrl:
            ctrl = self.designer
        else:
            if hasattr(ctrl, '_composite_child'):
                pos = ctrl.ClientToScreen(pos)
                ctrl = ctrl.GetParent()
                pos = ctrl.ScreenToClient(pos)
        return ctrl, pos

    def OnControlSelect(self, event):
        """ Control is clicked. Either select it or add control from palette """
        dsgn = self.designer
        ctrl, pos = self.getCtrlAndPosFromEvt(event)
        dsgn.selectControlByPos(ctrl, pos, event.ShiftDown())
        event.Skip()

    def OnControlRelease(self, event):
        """ A select or drag operation is ended """
        dsgn = self.designer
        if dsgn.selection:
            dsgn.selection.moveRelease()
        elif dsgn.multiSelection:
            for sel in dsgn.multiSelection:
                sel.moveRelease()
            dsgn.mainMultiDrag = None
        event.Skip()

    def OnControlResize(self, event):
        """ Control is resized, emulate native wxWidgets layout behaviour """
        dsgn = self.designer
        try:
            if dsgn.vetoResize:
                return
            if event.GetId() == dsgn.GetId():
                if event.GetSize().Get() == dsgn.lastSize and not dsgn.forceResize:
                    return
                dsgn.lastSize = event.GetSize().Get()

                if dsgn.selection:
                    dsgn.selection.selectCtrl(dsgn, dsgn.companion)
                elif dsgn.multiSelection:
                    dsgn.clearMultiSelection()
                    dsgn.assureSingleSelection()
                    dsgn.selection.selectCtrl(dsgn, dsgn.companion)
                    return

                # Compensate for autolayout=False and 1 ctrl on frame behaviour
                # Needed because incl selection tags there are actually 5 ctrls

                if not dsgn.GetAutoLayout() and not dsgn.companion.dialogLayout:
                    # Count children
                    c = 0
                    ctrl = None
                    for ctrlLst in dsgn.objects.values():
                        if len(ctrlLst) > 2 and ctrlLst[2] == '' and \
                          (ctrlLst[1].__class__ not in dsgn.ignoreWindows):
                            c = c + 1
                            ctrl = ctrlLst[1]

                    if c == 1:
                        s = dsgn.GetClientSize()
                        ctrl.SetDimensions(0, 0, s.x, s.y)

            if dsgn.selection:
                s, p = dsgn.selection.size, dsgn.selection.position
                dsgn.selection.sizeFromCtrl()
                dsgn.selection.setSelection()

                if (s, p) != (dsgn.selection.size, dsgn.selection.position):
                    dsgn.selection.sizeUpdate()
                    dsgn.selection.positionUpdate()

        finally:
            dsgn.forceResize = False
            dsgn.Refresh()
            event.Skip()

    def OnControlDClick(self, event):
        dsgn = self.designer

        if dsgn.selection:
            #ctrl = event.GetEventObject()
            ctrl, pos = self.getCtrlAndPosFromEvt(event)

            dsgn.selectControlByPos(ctrl, pos, event.ShiftDown())
            if ctrl == dsgn:
                companion = dsgn.companion
                ctrlName = ''
            else:
                ctrlName = ctrl.GetName()
                companion = dsgn.objects[ctrlName][0]

            selCtrl, selCompn, selPos = \
                  dsgn.checkChildCtrlClick(ctrlName, ctrl, companion,
                  event.GetPosition())

            selCompn.defaultAction()

            dsgn.selection.moveRelease()

    def OnControlMove(self, event):
        ctrl = event.GetEventObject()
        # Prevent infinite event loop by not sending siz events to statusbar
        # Only applies to sizered statusbars
        if ctrl and not isinstance(ctrl, wx.StatusBar):
            parent = ctrl.GetParent()
            if parent:
                wx.PostEvent(parent, wx.SizeEvent( parent.GetSize() ))
        event.Skip()

#---Grid drawing----------------------------------------------------------------

    def _drawLines(self, dc, col, loglFunc, sze, sg):
        """ Draw horizontal and vertical lines
        """
        pen1 = wx.Pen(col)
        dc.SetPen(pen1)
        dc.SetLogicalFunction(loglFunc)
        lines = []
        for y in range(sze.y / sg + 1):
            lines.append( (0, y * sg, sze.x, y * sg) )

        for x in range(sze.x / sg + 1):
            lines.append( (x * sg, 0, x * sg, sze.y) )

        dc.DrawLineList(lines)

    def drawGrid_intersectingLines(self, dc, sze, sg):
        """ Cute hack to draw dots by intersecting lines
        """
        bgCol = dc.GetBackground().GetColour()
        xorBgCol = wx.Colour(255^bgCol.Red(), 255^bgCol.Green(), 255^bgCol.Blue())

        self._drawLines(dc, xorBgCol, wx.COPY, sze, sg)
        self._drawLines(dc, wx.WHITE, wx.XOR, sze, sg)

    darken = 15
    def drawGrid_grid(self, dc, sze, sg):
        """ The default method, drawing horizontal and vertical grid lines.
        """
        bgCol = dc.GetBackground().GetColour()
        darkerBgCol = wx.Colour(max(bgCol.Red()   -self.darken, 0),
                                max(bgCol.Green() -self.darken, 0),
                                max(bgCol.Blue()  -self.darken, 0))

        self._drawLines(dc, darkerBgCol, wx.COPY, sze, sg)

    def drawGrid_dots(self, dc, sze, sg):
        """ The slowest method, drawing each dot of the grid individually
        """
        pen1 = wx.Pen(wx.BLACK)
        dc.SetPen(pen1)
        (szex, szey), points = self._points
        if (szex, szey) != (sze.x, sze.y):
            points = []
            for y in range(sze.y / sg + 1):
                for x in range(sze.x / sg + 1):
                    points.append( (x * sg, y * sg) )
            self._points = (szex, szey), points
        dc.DrawPointList(points)

    def drawGrid_bitmap(self, dc, sze, sg):
        """ This should be the most efficient method, when the granularity is
            changed, a new (possibly +-32x32) bitmap should be created with
            transparent background and black grid points. This can then be
            blitted over background
        """
        pass

    def updateDCProps(self, dc, sizer, validCol):
        if sizer.__class__.__name__ == 'BlankSizer':
            pen = wx.Pen(wx.RED)
            brush = wx.Brush(wx.RED, wx.FDIAGONAL_HATCH)
        else:
            pen = wx.Pen(validCol, 3, wx.SOLID)
            brush = wx.TRANSPARENT_BRUSH
        dc.SetPen(pen)
        dc.SetBrush(brush)

    def drawSizerInfo(self, dc, sizer, yoffset=0):
        self.updateDCProps(dc, sizer, Preferences.dsHasSizerCol)

        sp = sizer.GetPosition()
        ss = sizer.GetSize()
        dc.DrawRectangle(sp.x, sp.y+yoffset, ss.width, ss.height)

        c = sizer.GetChildren()
        for sc in c:
            if sc.IsSizer():
                self.drawSizerInfo(dc, sc.GetSizer())
            else:
                self.updateDCProps(dc, sizer, Preferences.dsInSizerCol)
                sp = sc.GetPosition()
                ss = sc.GetSize()
                dc.DrawRectangle(sp.x, sp.y+yoffset, ss.width, ss.height)


    def OnPaint(self, event):
        # XXX Paint event fired after destruction, should remove EVT ?
        ctrl = event.GetEventObject()
        if ctrl:
            dc = wx.PaintDC(ctrl)
#            sze = ctrl.GetClientSize()
            sze = ctrl.GetSize()
            sg = Preferences.dsGridSize
            # Workaround toolbar offset bug
            yoffset = 0
            if ctrl == self.designer:
                tb = self.designer.GetToolBar()
                if tb:
                    yoffset = tb.GetSize().y

            drawGrid = self.drawGridMethods[Preferences.drawGridMethod]

            dc.BeginDrawing()
            try:
                if Preferences.drawDesignerGrid:
                    drawGrid(dc, sze, sg)

                sizer = ctrl.GetSizer()
                if sizer:
                    self.drawSizerInfo(dc, sizer, yoffset)

            finally:
                dc.EndDrawing()


        event.Skip()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.