tools.py 8.65 KB
# Name:         tools.py
# Purpose:      XRC editor, toolbar
# Author:       Roman Rolinsky <rolinsky@mema.ucl.ac.be>
# Created:      19.03.2003
# RCS-ID:       $Id: tools.py 67456 2011-04-13 18:02:41Z RD $

from globals import *
from component import Manager, DEFAULT_POS
import view
import images
import wx.lib.foldpanelbar as fpb

#if wx.Platform in ['__WXMAC__', '__WXMSW__']:
    # Mac and Win are better off with generic
import wx.lib.buttons
BitmapButton = wx.lib.buttons.GenBitmapButton
#else:
#    wx.BitmapButton.SetBezelWidth = lambda self, w: None

class ToolPanel(wx.PyPanel):
    '''Manages a Listbook with tool bitmap buttons.'''
    defaultPos = wx.GBPosition(*DEFAULT_POS)
    def __init__(self, parent):
        if wx.Platform == '__WXGTK__':
            wx.PyPanel.__init__(self, parent, -1,
                             style=wx.RAISED_BORDER|wx.WANTS_CHARS)
        else:
            wx.PyPanel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
        self.bg = wx.Colour(115, 180, 215)
        # Top sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        # Use toolbook or foldpanelbar depending of preferences
        if g.conf.toolPanelType == 'TB':
            self.tp = wx.Toolbook(self, -1, style=wx.BK_TOP)
            sizer.Add(self.tp, 1, wx.EXPAND)
            # Image list
            thumbSize = g.conf.toolThumbSize
            il = wx.ImageList(thumbSize, thumbSize, True)
            # Default Id 0
            il.Add(images.ToolPanel_Default.GetImage().Scale(thumbSize, thumbSize).ConvertToBitmap())
            self.il = il
            self.tp.AssignImageList(il)
        elif g.conf.toolPanelType == 'FPB':
            self.tp = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, wx.DefaultSize, 
                                       agwStyle=fpb.FPB_VERTICAL)
            sizer.Add(self.tp, 1, wx.EXPAND)
        self.panels = []
        for name in Manager.panelNames:
            panelData = Manager.getPanelData(name)
            if not panelData: continue
            try:
                im = Manager.panelImages[name]
                imageId = il.Add(im.Scale(thumbSize, thumbSize).ConvertToBitmap())
            except:
                imageId = 0
            panel = self.AddPanel(name)
            self.panels.append(panel)
            for pos,span,comp,bmp in panelData:
                self.AddButton(panel, pos, span, comp.id, bmp, comp.klass)
            panel.Fit()
            if g.conf.toolPanelType == 'TB':
                self.tp.AddPage(panel, '', imageId=imageId)
            else:
                p = self.tp.AddFoldPanel(name, collapsed=False)
                p.SetBackgroundColour(self.bg)
                panel.Reparent(p)
                p.AddWindow(panel, fpb.FPB_ALIGN_WIDTH)
        self.tp.Fit()
        
        self.SetSizer(sizer)
        # Allow to be resized in horizontal direction only
        # Events
#        wx.EVT_KEY_DOWN(self, self.OnKeyDown)
#        wx.EVT_KEY_UP(self, self.OnKeyUp)
        self.drag = None

    def DoGetBestSize(self):
        # Do our own DoGetBestSize because the FoldPanelBar doesn't
        h = w = 0
        for p in self.panels:
            ems = p.GetEffectiveMinSize()
            w = max(w, ems.width)
            h = max(h, ems.height)
        h += 64
        return wx.Size(w,h)

    def AddButton(self, panel, pos, span, id, bmp, text):
        button = BitmapButton(panel, id, bmp, 
                              style=wx.NO_BORDER)# | wx.WANTS_CHARS)
        button.SetBezelWidth(0)
#        wx.EVT_KEY_DOWN(button, self.OnKeyDown)
#        wx.EVT_KEY_UP(button, self.OnKeyUp)
        button.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDownOnButton)
        button.Bind(wx.EVT_MOTION, self.OnMotionOnButton)
        button.Bind(wx.EVT_BUTTON, self.OnButton)
        button.SetToolTipString(text)
        # Look for an available place if not specified
        r0,c0 = 0,0
        if pos != self.defaultPos:
            if panel.sizer.CheckForIntersectionPos(pos, span):
                r0,c0 = pos     # start from pos
                pos = self.defaultPos   # reset to default
        if pos == self.defaultPos:
            # Find the smallest position we can insert into
            try:
                for r in range(r0, panel.size.rowspan):
                    for c in range(c0, panel.size.colspan):
                        if not panel.sizer.CheckForIntersectionPos((r,c), span):
                            pos = (r,c)
                            raise StopIteration
            except StopIteration:
                pass
            if pos == self.defaultPos:  # insert new col/row
                if panel.size.colspan + span[0] <= panel.size.rowspan + span[1]:
                    pos = (0,panel.size.colspan)
                else:
                    pos = (panel.size.rowspan,0)
        assert pos[0] >= 0 and pos[1] >= 0, 'invalid position'
        panel.sizer.Add(button, pos, span, wx.ALIGN_CENTRE)
        panel.controls[id] = button
        panel.size.rowspan = max(panel.size.rowspan, pos[0]+span[0])
        panel.size.colspan = max(panel.size.colspan, pos[1]+span[1])

    def AddPanel(self, name):
        # Each group is inside box
        panel = wx.Panel(self.tp)
        panel.SetBackgroundColour(self.bg)
        panel.name = name
        panel.controls = {}
        panel.size = wx.GBSpan(1, 1) # current size
        topSizer = wx.BoxSizer()
        panel.sizer = wx.GridBagSizer(0, 0)
        panel.sizer.SetEmptyCellSize((24, 24))
        topSizer.Add(panel.sizer, 1, wx.EXPAND | wx.ALL, 5)
        panel.SetSizer(topSizer)
        return panel

    # Mouse events for DnD and append/insert mode
    def OnLeftDownOnButton(self, evt):
        self.posDown = evt.GetPosition()
        self.btnDown = evt.GetEventObject()
        forceSibling = evt.ControlDown()
        forceInsert = evt.ShiftDown()
        g.Presenter.updateCreateState(forceSibling, forceInsert)
        evt.Skip()

    def OnButton(self, evt):
        if not self.drag: evt.Skip()
        self.drag = False
        if view.frame.miniFrame:
            if not g.useAUI and not g.conf.embedPanel:
                view.frame.miniFrame.Raise()
        else:
            view.frame.Raise()

    def OnMotionOnButton(self, evt):
        # Detect dragging
        if evt.Dragging() and evt.LeftIsDown():
            d = evt.GetPosition() - self.posDown
            if max(abs(d[0]), abs(d[1])) >= 5:
                if self.btnDown.HasCapture(): 
                    # Generate up event to release mouse
                    evt = wx.MouseEvent(wx.EVT_LEFT_UP.typeId)
                    #evt.SetId(self.btnDown.GetId())
                    # Set flag to prevent normal button operation
                    self.drag = True
                    self.btnDown.ProcessEvent(evt)
                self.StartDrag()
        evt.Skip()

    def StartDrag(self):
        bm = self.btnDown.GetBitmapLabel()
        # wxGTK requires wxIcon cursor, wxWIN and wxMAC require wxCursor
        if wx.Platform == '__WXGTK__':
            icon = wx.EmptyIcon()
            icon.CopyFromBitmap(bm)
            dragSource = wx.DropSource(self, icon)
        else:
            curs = wx.CursorFromImage(wx.ImageFromBitmap(bm))
            dragSource = wx.DropSource(self, curs)
        do = MyDataObject(str(self.btnDown.GetId()))
        dragSource.SetData(do)
        view.frame.SetStatusText('Release the mouse button over the test window')
        dragSource.DoDragDrop()
        view.testWin.RemoveHighlightDT()
        view.testWin.EmptyTrash()

    # Process key events
    def OnKeyDown(self, evt):
        print evt.GetEventObject(), evt.GetKeyCode()
        evt.Skip()
        return
        if evt.GetKeyCode() == wx.WXK_CONTROL:
            g.tree.ctrl = True
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            g.tree.shift = True
        self.UpdateIfNeeded()
        evt.Skip()

    def OnKeyUp(self, evt):
        if evt.GetKeyCode() == wx.WXK_CONTROL:
            g.tree.ctrl = False
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            g.tree.shift = False
        self.UpdateIfNeeded()
        evt.Skip()

    def OnMouse(self, evt):
        # Update control and shift states
        g.tree.ctrl = evt.ControlDown()
        g.tree.shift = evt.ShiftDown()
        self.UpdateIfNeeded()
        evt.Skip()

    # Update UI after key presses, if necessary
    def UpdateIfNeeded(self):
        tree = g.tree
        if self.ctrl != tree.ctrl or self.shift != tree.shift:
            # Enabling is needed only for ctrl
            if self.ctrl != tree.ctrl: self.UpdateUI()
            self.ctrl = tree.ctrl
            self.shift = tree.shift
            if tree.ctrl:
                status = 'SBL'
            elif tree.shift:
                status = 'INS'
            else:
                status = ''
            g.frame.SetStatusText(status, 1)