git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38062 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			773 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			773 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#----------------------------------------------------------------------
 | 
						|
# Name:        wx.lib.splitter
 | 
						|
# Purpose:     A class similar to wx.SplitterWindow but that allows more
 | 
						|
#              than a single split
 | 
						|
#
 | 
						|
# Author:      Robin Dunn
 | 
						|
#
 | 
						|
# Created:     9-June-2005
 | 
						|
# RCS-ID:      $Id$
 | 
						|
# Copyright:   (c) 2005 by Total Control Software
 | 
						|
# Licence:     wxWindows license
 | 
						|
#----------------------------------------------------------------------
 | 
						|
"""
 | 
						|
This module provides the `MultiSplitterWindow` class, which is very
 | 
						|
similar to the standard `wx.SplitterWindow` except it can be split
 | 
						|
more than once.
 | 
						|
"""
 | 
						|
 | 
						|
import wx
 | 
						|
import sys
 | 
						|
 | 
						|
_RENDER_VER = (2,6,1,1)
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
class MultiSplitterWindow(wx.PyPanel):
 | 
						|
    """
 | 
						|
    This class is very similar to `wx.SplitterWindow` except that it
 | 
						|
    allows for more than two windows and more than one sash.  Many of
 | 
						|
    the same styles, constants, and methods behave the same as in
 | 
						|
    wx.SplitterWindow.  The key differences are seen in the methods
 | 
						|
    that deal with the child windows managed by the splitter, and also
 | 
						|
    those that deal with the sash positions.  In most cases you will
 | 
						|
    need to pass an index value to tell the class which window or sash
 | 
						|
    you are refering to.
 | 
						|
 | 
						|
    The concept of the sash position is also different than in
 | 
						|
    wx.SplitterWindow.  Since the wx.Splitterwindow has only one sash
 | 
						|
    you can think of it's position as either relative to the whole
 | 
						|
    splitter window, or as relative to the first window pane managed
 | 
						|
    by the splitter.  Once there is more than one sash then the
 | 
						|
    distinciton between the two concepts needs to be clairified.  I've
 | 
						|
    chosen to use the second definition, and sash positions are the
 | 
						|
    distance (either horizontally or vertically) from the origin of
 | 
						|
    the window just before the sash in the splitter stack.
 | 
						|
 | 
						|
    NOTE: These things are not yet supported:
 | 
						|
 | 
						|
        * Using negative sash positions to indicate a position offset
 | 
						|
          from the end.
 | 
						|
          
 | 
						|
        * User controlled unsplitting (with double clicks on the sash
 | 
						|
          or dragging a sash until the pane size is zero.)
 | 
						|
          
 | 
						|
        * Sash gravity
 | 
						|
       
 | 
						|
    """
 | 
						|
    def __init__(self, parent, id=-1,
 | 
						|
                 pos = wx.DefaultPosition, size = wx.DefaultSize,
 | 
						|
                 style = 0, name="multiSplitter"):
 | 
						|
        
 | 
						|
        # always turn on tab traversal
 | 
						|
        style |= wx.TAB_TRAVERSAL
 | 
						|
 | 
						|
        # and turn off any border styles
 | 
						|
        style &= ~wx.BORDER_MASK
 | 
						|
        style |= wx.BORDER_NONE
 | 
						|
 | 
						|
        # initialize the base class
 | 
						|
        wx.PyPanel.__init__(self, parent, id, pos, size, style, name)
 | 
						|
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
 | 
						|
 | 
						|
        # initialize data members
 | 
						|
        self._windows = []
 | 
						|
        self._sashes = []
 | 
						|
        self._pending = {}
 | 
						|
        self._permitUnsplitAlways = self.HasFlag(wx.SP_PERMIT_UNSPLIT)
 | 
						|
        self._orient = wx.HORIZONTAL
 | 
						|
        self._dragMode = wx.SPLIT_DRAG_NONE
 | 
						|
        self._activeSash = -1
 | 
						|
        self._oldX = 0
 | 
						|
        self._oldY = 0
 | 
						|
        self._checkRequestedSashPosition = False
 | 
						|
        self._minimumPaneSize = 0
 | 
						|
        self._sashCursorWE = wx.StockCursor(wx.CURSOR_SIZEWE)
 | 
						|
        self._sashCursorNS = wx.StockCursor(wx.CURSOR_SIZENS)
 | 
						|
        self._sashTrackerPen = wx.Pen(wx.BLACK, 2, wx.SOLID)
 | 
						|
        self._needUpdating = False
 | 
						|
        self._isHot = False
 | 
						|
 | 
						|
        # Bind event handlers
 | 
						|
        self.Bind(wx.EVT_PAINT,        self._OnPaint)
 | 
						|
        self.Bind(wx.EVT_IDLE,         self._OnIdle)
 | 
						|
        self.Bind(wx.EVT_SIZE,         self._OnSize)
 | 
						|
        self.Bind(wx.EVT_MOUSE_EVENTS, self._OnMouse)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    def SetOrientation(self, orient):
 | 
						|
        """
 | 
						|
        Set whether the windows managed by the splitter will be
 | 
						|
        stacked vertically or horizontally.  The default is
 | 
						|
        horizontal.
 | 
						|
        """
 | 
						|
        assert orient in [ wx.VERTICAL, wx.HORIZONTAL ]
 | 
						|
        self._orient = orient
 | 
						|
 | 
						|
    def GetOrientation(self):
 | 
						|
        """
 | 
						|
        Returns the current orientation of the splitter, either
 | 
						|
        wx.VERTICAL or wx.HORIZONTAL.
 | 
						|
        """
 | 
						|
        return self._orient
 | 
						|
 | 
						|
 | 
						|
    def SetMinimumPaneSize(self, minSize):
 | 
						|
        """
 | 
						|
        Set the smallest size that any pane will be allowed to be
 | 
						|
        resized to.
 | 
						|
        """
 | 
						|
        self._minimumPaneSize = minSize
 | 
						|
 | 
						|
    def GetMinimumPaneSize(self):
 | 
						|
        """
 | 
						|
        Returns the smallest allowed size for a window pane.
 | 
						|
        """
 | 
						|
        return self._minimumPaneSize
 | 
						|
 | 
						|
    
 | 
						|
 | 
						|
    def AppendWindow(self, window, sashPos=-1):
 | 
						|
        """
 | 
						|
        Add a new window to the splitter at the right side or bottom
 | 
						|
        of the window stack.  If sashPos is given then it is used to
 | 
						|
        size the new window.
 | 
						|
        """
 | 
						|
        self.InsertWindow(sys.maxint, window, sashPos)
 | 
						|
 | 
						|
 | 
						|
    def InsertWindow(self, idx, window, sashPos=-1):
 | 
						|
        """
 | 
						|
        Insert a new window into the splitter at the position given in
 | 
						|
        ``idx``.
 | 
						|
        """
 | 
						|
        assert window not in self._windows, "A window can only be in the splitter once!"
 | 
						|
        self._windows.insert(idx, window)
 | 
						|
        self._sashes.insert(idx, -1)
 | 
						|
        if not window.IsShown():
 | 
						|
            window.Show()
 | 
						|
        if sashPos != -1:
 | 
						|
            self._pending[window] = sashPos
 | 
						|
        self._checkRequestedSashPosition = False
 | 
						|
        self._SizeWindows()
 | 
						|
 | 
						|
 | 
						|
    def DetachWindow(self, window):
 | 
						|
        """
 | 
						|
        Removes the window from the stack of windows managed by the
 | 
						|
        splitter.  The window will still exist so you should `Hide` or
 | 
						|
        `Destroy` it as needed.
 | 
						|
        """
 | 
						|
        assert window in self._windows, "Unknown window!"
 | 
						|
        idx = self._windows.index(window)
 | 
						|
        del self._windows[idx]
 | 
						|
        del self._sashes[idx]
 | 
						|
        self._SizeWindows()
 | 
						|
 | 
						|
 | 
						|
    def ReplaceWindow(self, oldWindow, newWindow):
 | 
						|
        """
 | 
						|
        Replaces oldWindow (which is currently being managed by the
 | 
						|
        splitter) with newWindow.  The oldWindow window will still
 | 
						|
        exist so you should `Hide` or `Destroy` it as needed.
 | 
						|
        """
 | 
						|
        assert oldWindow in self._windows, "Unknown window!"
 | 
						|
        idx = self._windows.index(oldWindow)
 | 
						|
        self._windows[idx] = newWindow
 | 
						|
        if not newWindow.IsShown():
 | 
						|
            newWindow.Show()
 | 
						|
        self._SizeWindows()
 | 
						|
 | 
						|
 | 
						|
    def ExchangeWindows(self, window1, window2):
 | 
						|
        """
 | 
						|
        Trade the positions in the splitter of the two windows.
 | 
						|
        """
 | 
						|
        assert window1 in self._windows, "Unknown window!"
 | 
						|
        assert window2 in self._windows, "Unknown window!"
 | 
						|
        idx1 = self._windows.index(window1)
 | 
						|
        idx2 = self._windows.index(window2)
 | 
						|
        self._windows[idx1] = window2
 | 
						|
        self._windows[idx2] = window1
 | 
						|
        self._SizeWindows()
 | 
						|
 | 
						|
 | 
						|
    def GetWindow(self, idx):
 | 
						|
        """
 | 
						|
        Returns the idx'th window being managed by the splitter.
 | 
						|
        """
 | 
						|
        assert idx < len(self._windows)
 | 
						|
        return self._windows[idx]
 | 
						|
 | 
						|
 | 
						|
    def GetSashPosition(self, idx):
 | 
						|
        """
 | 
						|
        Returns the position of the idx'th sash, measured from the
 | 
						|
        left/top of the window preceding the sash.
 | 
						|
        """
 | 
						|
        assert idx < len(self._sashes)
 | 
						|
        return self._sashes[idx]
 | 
						|
 | 
						|
 | 
						|
    def SizeWindows(self):
 | 
						|
        """
 | 
						|
        Reposition and size the windows managed by the splitter.
 | 
						|
        Useful when windows have been added/removed or when styles
 | 
						|
        have been changed.
 | 
						|
        """
 | 
						|
        self._SizeWindows()
 | 
						|
        
 | 
						|
 | 
						|
    def DoGetBestSize(self):
 | 
						|
        """
 | 
						|
        Overridden base class virtual.  Determines the best size of
 | 
						|
        the control based on the best sizes of the child windows.
 | 
						|
        """
 | 
						|
        best = wx.Size(0,0)
 | 
						|
        if not self._windows:
 | 
						|
            best = wx.Size(10,10)
 | 
						|
 | 
						|
        sashsize = self._GetSashSize()
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            for win in self._windows:
 | 
						|
                winbest = win.GetAdjustedBestSize()
 | 
						|
                best.width += max(self._minimumPaneSize, winbest.width)
 | 
						|
                best.height = max(best.height, winbest.height)
 | 
						|
            best.width += sashsize * (len(self._windows)-1)
 | 
						|
 | 
						|
        else:
 | 
						|
            for win in self._windows:
 | 
						|
                winbest = win.GetAdjustedBestSize()
 | 
						|
                best.height += max(self._minimumPaneSize, winbest.height)
 | 
						|
                best.width = max(best.width, winbest.width)
 | 
						|
            best.height += sashsize * (len(self._windows)-1)
 | 
						|
            
 | 
						|
        border = 2 * self._GetBorderSize()
 | 
						|
        best.width += border
 | 
						|
        best.height += border
 | 
						|
        return best
 | 
						|
 | 
						|
    # -------------------------------------
 | 
						|
    # Event handlers
 | 
						|
    
 | 
						|
    def _OnPaint(self, evt):
 | 
						|
        dc = wx.PaintDC(self)
 | 
						|
        self._DrawSash(dc)
 | 
						|
 | 
						|
 | 
						|
    def _OnSize(self, evt):
 | 
						|
        parent = wx.GetTopLevelParent(self)
 | 
						|
        if parent.IsIconized():
 | 
						|
            evt.Skip()
 | 
						|
            return
 | 
						|
        self._SizeWindows()
 | 
						|
 | 
						|
 | 
						|
    def _OnIdle(self, evt):
 | 
						|
        evt.Skip()
 | 
						|
        # if this is the first idle time after a sash position has
 | 
						|
        # potentially been set, allow _SizeWindows to check for a
 | 
						|
        # requested size.
 | 
						|
        if not self._checkRequestedSashPosition:
 | 
						|
            self._checkRequestedSashPosition = True
 | 
						|
            self._SizeWindows()
 | 
						|
 | 
						|
        if self._needUpdating:
 | 
						|
            self._SizeWindows()
 | 
						|
 | 
						|
            
 | 
						|
 | 
						|
    def _OnMouse(self, evt):
 | 
						|
        if self.HasFlag(wx.SP_NOSASH):
 | 
						|
            return
 | 
						|
 | 
						|
        x, y = evt.GetPosition()
 | 
						|
        isLive = self.HasFlag(wx.SP_LIVE_UPDATE)
 | 
						|
        adjustNeighbor = evt.ShiftDown()
 | 
						|
 | 
						|
        # LeftDown: set things up for dragging the sash
 | 
						|
        if evt.LeftDown() and self._SashHitTest(x, y) != -1:
 | 
						|
            self._activeSash = self._SashHitTest(x, y)
 | 
						|
            self._dragMode = wx.SPLIT_DRAG_DRAGGING
 | 
						|
 | 
						|
            self.CaptureMouse()
 | 
						|
            self._SetResizeCursor()
 | 
						|
 | 
						|
            if not isLive:
 | 
						|
                self._pendingPos = (self._sashes[self._activeSash],
 | 
						|
                                    self._sashes[self._activeSash+1])
 | 
						|
                self._DrawSashTracker(x, y)
 | 
						|
 | 
						|
            self._oldX = x
 | 
						|
            self._oldY = y
 | 
						|
            return
 | 
						|
 | 
						|
        # LeftUp: Finsish the drag
 | 
						|
        elif evt.LeftUp() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
 | 
						|
            self._dragMode = wx.SPLIT_DRAG_NONE
 | 
						|
            self.ReleaseMouse()
 | 
						|
            self.SetCursor(wx.STANDARD_CURSOR)
 | 
						|
 | 
						|
            if not isLive:
 | 
						|
                # erase the old tracker
 | 
						|
                self._DrawSashTracker(self._oldX, self._oldY)
 | 
						|
 | 
						|
            diff = self._GetMotionDiff(x, y)
 | 
						|
 | 
						|
            # determine if we can change the position
 | 
						|
            if isLive:
 | 
						|
                oldPos1, oldPos2 = (self._sashes[self._activeSash],
 | 
						|
                                    self._sashes[self._activeSash+1])
 | 
						|
            else:
 | 
						|
                oldPos1, oldPos2 = self._pendingPos
 | 
						|
            newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
 | 
						|
                                                            oldPos1 + diff,
 | 
						|
                                                            oldPos2 - diff,
 | 
						|
                                                            adjustNeighbor)
 | 
						|
            if newPos1 == -1:
 | 
						|
                # the change was not allowed
 | 
						|
                return
 | 
						|
 | 
						|
            # TODO: check for unsplit?
 | 
						|
 | 
						|
            self._SetSashPositionAndNotify(self._activeSash, newPos1, newPos2, adjustNeighbor)
 | 
						|
            self._activeSash = -1
 | 
						|
            self._pendingPos = (-1, -1)
 | 
						|
            self._SizeWindows()
 | 
						|
 | 
						|
        # Entering or Leaving a sash: Change the cursor
 | 
						|
        elif (evt.Moving() or evt.Leaving() or evt.Entering()) and self._dragMode == wx.SPLIT_DRAG_NONE:
 | 
						|
            if evt.Leaving() or self._SashHitTest(x, y) == -1:
 | 
						|
                self._OnLeaveSash()
 | 
						|
            else:
 | 
						|
                self._OnEnterSash()
 | 
						|
 | 
						|
        # Dragging the sash
 | 
						|
        elif evt.Dragging() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
 | 
						|
            diff = self._GetMotionDiff(x, y)
 | 
						|
            if not diff:
 | 
						|
                return  # mouse didn't move far enough
 | 
						|
 | 
						|
            # determine if we can change the position
 | 
						|
            if isLive:
 | 
						|
                oldPos1, oldPos2 = (self._sashes[self._activeSash],
 | 
						|
                                    self._sashes[self._activeSash+1])
 | 
						|
            else:
 | 
						|
                oldPos1, oldPos2 = self._pendingPos
 | 
						|
            newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
 | 
						|
                                                            oldPos1 + diff,
 | 
						|
                                                            oldPos2 - diff,
 | 
						|
                                                            adjustNeighbor)
 | 
						|
            if newPos1 == -1:
 | 
						|
                # the change was not allowed
 | 
						|
                return
 | 
						|
 | 
						|
            if newPos1 == self._sashes[self._activeSash]:
 | 
						|
                return  # nothing was changed
 | 
						|
 | 
						|
            if not isLive:
 | 
						|
                # erase the old tracker
 | 
						|
                self._DrawSashTracker(self._oldX, self._oldY)
 | 
						|
           
 | 
						|
            if self._orient == wx.HORIZONTAL:
 | 
						|
                 x = self._SashToCoord(self._activeSash, newPos1)
 | 
						|
            else:
 | 
						|
                 y = self._SashToCoord(self._activeSash, newPos1)
 | 
						|
 | 
						|
            # Remember old positions
 | 
						|
            self._oldX = x
 | 
						|
            self._oldY = y
 | 
						|
 | 
						|
            if not isLive:
 | 
						|
                # draw a new tracker
 | 
						|
                self._pendingPos = (newPos1, newPos2)
 | 
						|
                self._DrawSashTracker(self._oldX, self._oldY)
 | 
						|
            else:
 | 
						|
                self._DoSetSashPosition(self._activeSash, newPos1, newPos2, adjustNeighbor)
 | 
						|
                self._needUpdating = True
 | 
						|
 | 
						|
 | 
						|
    # -------------------------------------
 | 
						|
    # Internal helpers
 | 
						|
    
 | 
						|
    def _RedrawIfHotSensitive(self, isHot):
 | 
						|
        if not wx.VERSION >= _RENDER_VER:
 | 
						|
            return
 | 
						|
        if wx.RendererNative.Get().GetSplitterParams(self).isHotSensitive:
 | 
						|
            self._isHot = isHot
 | 
						|
            dc = wx.ClientDC(self)
 | 
						|
            self._DrawSash(dc)
 | 
						|
 | 
						|
 | 
						|
    def _OnEnterSash(self):
 | 
						|
        self._SetResizeCursor()
 | 
						|
        self._RedrawIfHotSensitive(True)
 | 
						|
 | 
						|
 | 
						|
    def _OnLeaveSash(self):
 | 
						|
        self.SetCursor(wx.STANDARD_CURSOR)
 | 
						|
        self._RedrawIfHotSensitive(False)
 | 
						|
 | 
						|
 | 
						|
    def _SetResizeCursor(self):
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            self.SetCursor(self._sashCursorWE)
 | 
						|
        else:
 | 
						|
            self.SetCursor(self._sashCursorNS)
 | 
						|
 | 
						|
 | 
						|
    def _OnSashPositionChanging(self, idx, newPos1, newPos2, adjustNeighbor):
 | 
						|
        # TODO: check for possibility of unsplit (pane size becomes zero)
 | 
						|
 | 
						|
        # make sure that minsizes are honored
 | 
						|
        newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
 | 
						|
 | 
						|
        # sanity check
 | 
						|
        if newPos1 <= 0:
 | 
						|
            newPos2 += newPos1
 | 
						|
            newPos1 = 0 
 | 
						|
 | 
						|
        # send the events
 | 
						|
        evt = MultiSplitterEvent(
 | 
						|
            wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, self)
 | 
						|
        evt.SetSashIdx(idx)
 | 
						|
        evt.SetSashPosition(newPos1)
 | 
						|
        if not self._DoSendEvent(evt):
 | 
						|
            # the event handler vetoed the change
 | 
						|
            newPos1 = -1
 | 
						|
        else:
 | 
						|
            # or it might have changed the value
 | 
						|
            newPos1 = evt.GetSashPosition()
 | 
						|
 | 
						|
        if adjustNeighbor and newPos1 != -1:
 | 
						|
            evt.SetSashIdx(idx+1)
 | 
						|
            evt.SetSashPosition(newPos2)
 | 
						|
            if not self._DoSendEvent(evt):
 | 
						|
                # the event handler vetoed the change
 | 
						|
                newPos2 = -1
 | 
						|
            else:
 | 
						|
                # or it might have changed the value
 | 
						|
                newPos2 = evt.GetSashPosition()
 | 
						|
            if newPos2 == -1:
 | 
						|
                newPos1 = -1
 | 
						|
 | 
						|
        return (newPos1, newPos2)
 | 
						|
 | 
						|
 | 
						|
    def _AdjustSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
 | 
						|
        total = newPos1 + newPos2
 | 
						|
 | 
						|
        # these are the windows on either side of the sash
 | 
						|
        win1 = self._windows[idx]
 | 
						|
        win2 = self._windows[idx+1]
 | 
						|
 | 
						|
        # make adjustments for window min sizes
 | 
						|
        minSize = self._GetWindowMin(win1)
 | 
						|
        if minSize == -1 or self._minimumPaneSize > minSize:
 | 
						|
            minSize = self._minimumPaneSize
 | 
						|
        minSize += self._GetBorderSize()
 | 
						|
        if newPos1 < minSize:
 | 
						|
            newPos1 = minSize
 | 
						|
            newPos2 = total - newPos1
 | 
						|
 | 
						|
        if adjustNeighbor:
 | 
						|
            minSize = self._GetWindowMin(win2)
 | 
						|
            if minSize == -1 or self._minimumPaneSize > minSize:
 | 
						|
                minSize = self._minimumPaneSize
 | 
						|
            minSize += self._GetBorderSize()
 | 
						|
            if newPos2 < minSize:
 | 
						|
                newPos2 = minSize
 | 
						|
                newPos1 = total - newPos2
 | 
						|
        
 | 
						|
        return (newPos1, newPos2)
 | 
						|
 | 
						|
 | 
						|
    def _DoSetSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
 | 
						|
        newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
 | 
						|
        if newPos1 == self._sashes[idx]:
 | 
						|
            return False
 | 
						|
        self._sashes[idx] = newPos1
 | 
						|
        if adjustNeighbor:
 | 
						|
            self._sashes[idx+1] = newPos2
 | 
						|
        return True
 | 
						|
        
 | 
						|
 | 
						|
    def _SetSashPositionAndNotify(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
 | 
						|
        # TODO:  what is the thing about _requestedSashPosition for?
 | 
						|
 | 
						|
        self._DoSetSashPosition(idx, newPos1, newPos2, adjustNeighbor)
 | 
						|
 | 
						|
        evt = MultiSplitterEvent(
 | 
						|
            wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, self)
 | 
						|
        evt.SetSashIdx(idx)
 | 
						|
        evt.SetSashPosition(newPos1)
 | 
						|
        self._DoSendEvent(evt)
 | 
						|
 | 
						|
        if adjustNeighbor:
 | 
						|
            evt.SetSashIdx(idx+1)
 | 
						|
            evt.SetSashPosition(newPos2)
 | 
						|
            self._DoSendEvent(evt)
 | 
						|
 | 
						|
 | 
						|
    def _GetMotionDiff(self, x, y):
 | 
						|
        # find the diff from the old pos
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            diff = x - self._oldX
 | 
						|
        else:
 | 
						|
            diff = y - self._oldY
 | 
						|
        return diff
 | 
						|
 | 
						|
 | 
						|
    def _SashToCoord(self, idx, sashPos):
 | 
						|
        coord = 0
 | 
						|
        for i in range(idx):
 | 
						|
            coord += self._sashes[i]
 | 
						|
            coord += self._GetSashSize()
 | 
						|
        coord += sashPos
 | 
						|
        return coord
 | 
						|
 | 
						|
 | 
						|
    def _GetWindowMin(self, window):
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            return window.GetMinWidth()
 | 
						|
        else:
 | 
						|
            return window.GetMinHeight()
 | 
						|
        
 | 
						|
            
 | 
						|
    def _GetSashSize(self):
 | 
						|
        if self.HasFlag(wx.SP_NOSASH):
 | 
						|
            return 0
 | 
						|
        if wx.VERSION >= _RENDER_VER:
 | 
						|
            return wx.RendererNative.Get().GetSplitterParams(self).widthSash
 | 
						|
        else:
 | 
						|
            return 5
 | 
						|
 | 
						|
 | 
						|
    def _GetBorderSize(self):
 | 
						|
        if wx.VERSION >= _RENDER_VER:
 | 
						|
            return wx.RendererNative.Get().GetSplitterParams(self).border
 | 
						|
        else:
 | 
						|
            return 0
 | 
						|
        
 | 
						|
 | 
						|
    def _DrawSash(self, dc):
 | 
						|
        if wx.VERSION >= _RENDER_VER:
 | 
						|
            if self.HasFlag(wx.SP_3DBORDER):
 | 
						|
                wx.RendererNative.Get().DrawSplitterBorder(
 | 
						|
                    self, dc, self.GetClientRect())
 | 
						|
 | 
						|
        # if there are no splits then we're done.
 | 
						|
        if len(self._windows) < 2:
 | 
						|
            return
 | 
						|
 | 
						|
        # if we are not supposed to use a sash then we're done.
 | 
						|
        if self.HasFlag(wx.SP_NOSASH):
 | 
						|
            return
 | 
						|
 | 
						|
        # Reverse the sense of the orientation, in this case it refers
 | 
						|
        # to the direction to draw the sash not the direction that
 | 
						|
        # windows are stacked.
 | 
						|
        orient = { wx.HORIZONTAL : wx.VERTICAL,
 | 
						|
                   wx.VERTICAL : wx.HORIZONTAL }[self._orient]
 | 
						|
 | 
						|
        flag = 0
 | 
						|
        if self._isHot:
 | 
						|
            flag = wx.CONTROL_CURRENT
 | 
						|
 | 
						|
        pos = 0
 | 
						|
        for sash in self._sashes[:-1]:
 | 
						|
            pos += sash
 | 
						|
            if wx.VERSION >= _RENDER_VER:
 | 
						|
                wx.RendererNative.Get().DrawSplitterSash(self, dc,
 | 
						|
                                                         self.GetClientSize(),
 | 
						|
                                                         pos, orient, flag)
 | 
						|
            else:
 | 
						|
                dc.SetPen(wx.TRANSPARENT_PEN)
 | 
						|
                dc.SetBrush(wx.Brush(self.GetBackgroundColour()))
 | 
						|
                sashsize = self._GetSashSize()
 | 
						|
                if orient == wx.VERTICAL:
 | 
						|
                    x = pos
 | 
						|
                    y = 0
 | 
						|
                    w = sashsize
 | 
						|
                    h = self.GetClientSize().height
 | 
						|
                else:
 | 
						|
                    x = 0
 | 
						|
                    y = pos
 | 
						|
                    w = self.GetClientSize().width
 | 
						|
                    h = sashsize
 | 
						|
                dc.DrawRectangle(x, y, w, h)
 | 
						|
 | 
						|
            pos += self._GetSashSize()
 | 
						|
 | 
						|
 | 
						|
    def _DrawSashTracker(self, x, y):
 | 
						|
        # Draw a line to represent the dragging sash, for when not
 | 
						|
        # doing live updates
 | 
						|
        w, h = self.GetClientSize()
 | 
						|
        dc = wx.ScreenDC()
 | 
						|
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            x1 = x
 | 
						|
            y1 = 2
 | 
						|
            x2 = x
 | 
						|
            y2 = h-2
 | 
						|
            if x1 > w:
 | 
						|
                x1 = w
 | 
						|
                x2 = w
 | 
						|
            elif x1 < 0:
 | 
						|
                x1 = 0
 | 
						|
                x2 = 0
 | 
						|
        else:
 | 
						|
            x1 = 2
 | 
						|
            y1 = y
 | 
						|
            x2 = w-2
 | 
						|
            y2 = y
 | 
						|
            if y1 > h:
 | 
						|
                y1 = h
 | 
						|
                y2 = h
 | 
						|
            elif y1 < 0:
 | 
						|
                y1 = 0
 | 
						|
                y2 = 0
 | 
						|
 | 
						|
        x1, y1 = self.ClientToScreenXY(x1, y1)
 | 
						|
        x2, y2 = self.ClientToScreenXY(x2, y2)
 | 
						|
     
 | 
						|
        dc.SetLogicalFunction(wx.INVERT)
 | 
						|
        dc.SetPen(self._sashTrackerPen)
 | 
						|
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
 | 
						|
        dc.DrawLine(x1, y1, x2, y2)
 | 
						|
        dc.SetLogicalFunction(wx.COPY)
 | 
						|
        
 | 
						|
 | 
						|
    def _SashHitTest(self, x, y, tolerance=5):
 | 
						|
        # if there are no splits then we're done.
 | 
						|
        if len(self._windows) < 2:
 | 
						|
            return -1
 | 
						|
 | 
						|
        if self._orient == wx.HORIZONTAL:
 | 
						|
            z = x
 | 
						|
        else:
 | 
						|
            z = y
 | 
						|
 | 
						|
        pos = 0
 | 
						|
        for idx, sash in enumerate(self._sashes[:-1]):
 | 
						|
            pos += sash
 | 
						|
            hitMin = pos - tolerance
 | 
						|
            hitMax = pos + self._GetSashSize() + tolerance
 | 
						|
            
 | 
						|
            if z >= hitMin and z <= hitMax:
 | 
						|
                return idx
 | 
						|
            
 | 
						|
            pos += self._GetSashSize() 
 | 
						|
 | 
						|
        return -1
 | 
						|
 | 
						|
 | 
						|
    def _SizeWindows(self):
 | 
						|
        # no windows yet?
 | 
						|
        if not self._windows:
 | 
						|
            return
 | 
						|
 | 
						|
        # are there any pending size settings?
 | 
						|
        for window, spos in self._pending.items():
 | 
						|
            idx = self._windows.index(window)
 | 
						|
            # TODO: this may need adjusted to make sure they all fit
 | 
						|
            # in the current client size
 | 
						|
            self._sashes[idx] = spos
 | 
						|
            del self._pending[window]
 | 
						|
 | 
						|
        # are there any that still have a -1?
 | 
						|
        for idx, spos in enumerate(self._sashes[:-1]):
 | 
						|
            if spos == -1:
 | 
						|
                # TODO: this should also be adjusted
 | 
						|
                self._sashes[idx] = 100
 | 
						|
        
 | 
						|
        cw, ch = self.GetClientSize()
 | 
						|
        border = self._GetBorderSize()
 | 
						|
        sash   = self._GetSashSize()
 | 
						|
        
 | 
						|
        if len(self._windows) == 1:
 | 
						|
            # there's only one, it's an easy layout
 | 
						|
            self._windows[0].SetDimensions(border, border,
 | 
						|
                                           cw - 2*border, ch - 2*border)
 | 
						|
        else:
 | 
						|
            if 'wxMSW' in wx.PlatformInfo:
 | 
						|
                self.Freeze()
 | 
						|
            if self._orient == wx.HORIZONTAL:
 | 
						|
                x = y = border
 | 
						|
                h = ch - 2*border
 | 
						|
                for idx, spos in enumerate(self._sashes[:-1]):
 | 
						|
                    self._windows[idx].SetDimensions(x, y, spos, h)
 | 
						|
                    x += spos + sash
 | 
						|
                # last one takes the rest of the space. TODO make this configurable
 | 
						|
                last = cw - 2*border - x
 | 
						|
                self._windows[idx+1].SetDimensions(x, y, last, h)
 | 
						|
                if last > 0:
 | 
						|
                    self._sashes[idx+1] = last
 | 
						|
            else:
 | 
						|
                x = y = border
 | 
						|
                w = cw - 2*border
 | 
						|
                for idx, spos in enumerate(self._sashes[:-1]):
 | 
						|
                    self._windows[idx].SetDimensions(x, y, w, spos)
 | 
						|
                    y += spos + sash
 | 
						|
                # last one takes the rest of the space. TODO make this configurable
 | 
						|
                last = ch - 2*border - y
 | 
						|
                self._windows[idx+1].SetDimensions(x, y, w, last)
 | 
						|
                if last > 0:
 | 
						|
                    self._sashes[idx+1] = last
 | 
						|
            if 'wxMSW' in wx.PlatformInfo:
 | 
						|
                self.Thaw()
 | 
						|
                
 | 
						|
        self._DrawSash(wx.ClientDC(self))
 | 
						|
        self._needUpdating = False
 | 
						|
 | 
						|
 | 
						|
    def _DoSendEvent(self, evt):
 | 
						|
        return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
 | 
						|
    
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
class MultiSplitterEvent(wx.PyCommandEvent):
 | 
						|
    """
 | 
						|
    This event class is almost the same as `wx.SplitterEvent` except
 | 
						|
    it adds an accessor for the sash index that is being changed.  The
 | 
						|
    same event type IDs and event binders are used as with
 | 
						|
    `wx.SplitterEvent`.
 | 
						|
    """
 | 
						|
    def __init__(self, type=wx.wxEVT_NULL, splitter=None):
 | 
						|
        wx.PyCommandEvent.__init__(self, type)
 | 
						|
        if splitter:
 | 
						|
            self.SetEventObject(splitter)
 | 
						|
            self.SetId(splitter.GetId())
 | 
						|
        self.sashIdx = -1
 | 
						|
        self.sashPos = -1
 | 
						|
        self.isAllowed = True
 | 
						|
 | 
						|
    def SetSashIdx(self, idx):
 | 
						|
        self.sashIdx = idx
 | 
						|
 | 
						|
    def SetSashPosition(self, pos):
 | 
						|
        self.sashPos = pos
 | 
						|
 | 
						|
    def GetSashIdx(self):
 | 
						|
        return self.sashIdx
 | 
						|
 | 
						|
    def GetSashPosition(self):
 | 
						|
        return self.sashPos
 | 
						|
 | 
						|
    # methods from wx.NotifyEvent
 | 
						|
    def Veto(self):
 | 
						|
        self.isAllowed = False
 | 
						|
    def Allow(self):
 | 
						|
        self.isAllowed = True
 | 
						|
    def IsAllowed(self):
 | 
						|
        return self.isAllowed
 | 
						|
        
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
 |