/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/dock.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/dock.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/thuban/Thuban/UI/dock.py revision 566 by jonathan, Wed Mar 26 11:10:41 2003 UTC branches/WIP-pyshapelib-bramz/Thuban/UI/dock.py revision 2734 by bramz, Thu Mar 1 12:42:59 2007 UTC
# Line 5  Line 5 
5  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
6  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
7    
8  """Classes for creating dockable windows"""  """Classes for creating dockable windows.
9    
10    The DockFrame is the main window that will be the parent for the
11    dockable windows.
12    
13    The DockPanel is a panel that all windows that wish to be dockable
14    should derive from.
15    """
16    
17  __version__ = "$Revision$"  __version__ = "$Revision$"
18    
19    import resource
20    
21  from Thuban import _  from Thuban import _
22    
23  from wxPython.wx import *  import wx
24    
25  from Thuban.Lib.connector import Publisher  from Thuban.Lib.connector import Publisher
26    
# Line 20  from dialogs import NonModalDialog Line 29  from dialogs import NonModalDialog
29  from messages import DOCKABLE_DOCKED, DOCKABLE_UNDOCKED, DOCKABLE_CLOSED  from messages import DOCKABLE_DOCKED, DOCKABLE_UNDOCKED, DOCKABLE_CLOSED
30    
31  ID_BUTTON_DOCK = 4001  ID_BUTTON_DOCK = 4001
32  ID_BUTTON_UNDOCK = 4002  ID_BUTTON_CLOSE = 4002
33    
34  class DockPanel(wxPanel):  PANEL_ID = 3141
35    
36    DOCK_BMP   = "dock_12"
37    UNDOCK_BMP = "undock_12"
38    CLOSE_BMP  = "close_12"
39    
40    class DockPanel(wx.Panel):
41        """A DockPanel is a panel that should be derived from to create panels
42        that can be docked in a DockFrame.
43        """
44    
45      def __init__(self, parent, id):      def __init__(self, parent, id):
46            """parent should be a DockableWindow created from
47            DockFrame.CreateDock().
48            """
49    
50            if not isinstance(parent, DockableWindow):
51                raise TypeError("")
52    
53          wxPanel.__init__(self, parent, id)          wx.Panel.__init__(self, parent.GetCurrentParent(), id)
54    
55          self.SetDockParent(None)          self.parent = parent
56    
57            #self.SetDockParent(None)
58            #parent.SetPanel(self)
59    
60        def Create(self):
61            self.parent.SetPanel(self)
62    
63      def SetDockParent(self, parent):      def SetDockParent(self, parent):
64          self.dockParent = parent          self.__dockParent = parent
65    
66      def GetDockParent(self):      def GetDockParent(self):
67          return self.dockParent          return self.__dockParent
68    
69        def SetDock(self, dock):
70            """Specifically set the docked state of the panel
71            to dock (True or False).
72            """
73            #if dock == self.IsDocked(): return
74    
75            if dock:
76                self.Dock()
77            else:
78                self.UnDock()
79    
80      def Dock(self):      def Dock(self):
81            """Dock the panel in the DockFrame."""
82          self.GetDockParent().Dock()          self.GetDockParent().Dock()
83    
84      def UnDock(self):      def UnDock(self):
85            """Undock the panel in the DockFrame."""
86          self.GetDockParent().UnDock()          self.GetDockParent().UnDock()
87    
88      def IsDocked(self):      def IsDocked(self):
89            """Return True if the panel is docked."""
90          return self.GetDockParent().IsDocked()          return self.GetDockParent().IsDocked()
91    
92  class DockableWindow(NonModalDialog, Publisher):  class DockableWindow(Publisher):
93    
94        def __getattr__(self, attr):
95            return getattr(self.__topWindow, attr)
96    
97        def __init__(self, parent, id, name, title, dockWindow, orient):
98            """Create the dockable window.
99    
100            Initially, the window is hidden, but in an undocked state.
101            """
102    
103      def __init__(self, parent, id, name,          if not isinstance(parent, DockFrame): raise TypeError("")
104                   title, dockWindow, panel, docked = False, show = True):  
105            self.__parent = parent
106            self.__id     = id
107            self.__name   = name
108    
109            self.__orientation = orient
110    
111            self.__dockWindow  = dockWindow
112            self.__floatWindow = wx.Frame(parent, id, title)
113    
114            self.__docked      = False
115            if self.__docked:
116                self.__topWindow = self.__dockWindow
117            else:
118                self.__topWindow = self.__floatWindow
119    
120          NonModalDialog.__init__(self, parent, name, title)          self.__floatSize     = None
121            self.__floatPosition = None
122    
123          self.id = id          self.__dockPanel  = None
124          self.dockWindow = dockWindow          self.__panel = None
         self.docked = docked  
125    
126          self.dockBorder = None          self.__dockWindow.Hide()
127          self.dockBorderParent = self          self.__floatWindow.Hide()
128    
129          self.size = None          self.Bind(wx.EVT_CLOSE, self._OnClose)
         self.position = None  
130    
131          self.SetPanel(panel)      ##
132          self.SetDock(self.docked)      # Public methods
133        #
134    
135          self.Show(show)      def GetName(self):
136            return self.__name
137    
138      def SetPanel(self, panel, orient = wxVERTICAL):      def SetPanel(self, panel):
139    
140          if not isinstance(panel, DockPanel):          if not isinstance(panel, DockPanel):
141              raise TypeError("")              raise TypeError("")
142    
143          self.panel = panel          self.__panel = panel
144          self.panel.SetDockParent(self)          self.__panel.SetDockParent(self)
         self.orientation = orient  
145          self.__CreateBorder()          self.__CreateBorder()
146            
147            self.SetDock(self.__docked)
148    
149        def GetPanel(self):
150            return self.__panel
151    
152        def GetCurrentParent(self):
153            return self.__topWindow
154    
155      def SetDock(self, dock):      def SetDock(self, dock):
156    
157            self.__CheckAllGood()
158    
159          if dock:          if dock:
160              self.Dock()              self.Dock()
161          else:          else:
162              self.UnDock()              self.UnDock()
163    
164      def Dock(self):      def Dock(self):
165          #if self.IsDocked(): return          """Dock the window."""
166            self.__CheckAllGood()
167    
168          self.docked = True          wasVisible = self.IsShown()
169    
170          self.size = self.GetSize()          if wasVisible: self.Show(False)
         self.position = self.GetPosition()  
         #print self.position  
171    
172            self.__docked = True
173    
174            #
175            # save window information
176            #
177            self.__floatSize = self.GetSize()
178            self.__floatPosition = self.GetPosition()
179    
180            #
181            # reparent
182            #
183            self.__topWindow = self.__dockWindow
184            self.__dockPanel.Reparent(self.__topWindow)
185    
186          NonModalDialog.Hide(self)          self.__dockButton.SetBitmapLabel(self.__bmpUnDock)
187            self.__dockButton.SetBitmapFocus(self.__bmpUnDock)
188            self.__dockButton.SetBitmapSelected(self.__bmpUnDock)
189            self.__dockButton.SetBitmapDisabled(self.__bmpUnDock)
190            self.__dockButton.SetToolTip(wx.ToolTip(_("Undock")))
191    
192          self.dockBorderParent = self.dockWindow          self.SetDockSize(self.__dockWindow.GetSize())
         self.dockBorder.Reparent(self.dockBorderParent)  
193    
194          self.dockButton.SetLabel(_("Undock"))          if wasVisible: self.Show(True)
195    
196          self.issue(DOCKABLE_DOCKED, self.id, self)          self.issue(DOCKABLE_DOCKED, self.__id, self)
197    
198      def UnDock(self):      def UnDock(self):
199            """Undock the window."""
200            self.__CheckAllGood()
201    
202          #if not self.IsDocked(): return          wasVisible = self.IsShown()
203    
204          self.docked = False          if wasVisible: self.Show(False)
   
         self.dockBorderParent = self  
         self.dockBorder.Reparent(self.dockBorderParent)  
205    
206          self.dockButton.SetLabel(_("Dock"))          self.__docked = False
207    
208          if self.position is not None:          #
209              #print self.position          # reparent
210              self.SetPosition(self.position)          #
211            self.__topWindow = self.__floatWindow
212            self.__dockPanel.Reparent(self.__topWindow)
213    
214            self.__dockButton.SetBitmapLabel(self.__bmpDock)
215            self.__dockButton.SetBitmapFocus(self.__bmpDock)
216            self.__dockButton.SetBitmapSelected(self.__bmpDock)
217            self.__dockButton.SetBitmapDisabled(self.__bmpDock)
218            self.__dockButton.SetToolTip(wx.ToolTip(_("Dock")))
219    
220          NonModalDialog.Show(self) # this needs to come before SetSize()          if wasVisible: self.Show()
221    
222            #
223            # restore window information
224            #
225            if self.__floatPosition is not None:
226                self.SetPosition(self.__floatPosition)
227            if self.__floatSize is not None:
228                self.SetSize(self.__floatSize)
229    
230          if self.size is not None:          self.__dockPanel.SetSize(self.__topWindow.GetClientSize())
             self.SetSize(self.size)  
231    
232          self.issue(DOCKABLE_UNDOCKED, self.id, self)          self.issue(DOCKABLE_UNDOCKED, self.__id, self)
233    
234      def IsDocked(self):      def IsDocked(self):
235          return self.docked          self.__CheckAllGood()
236            return self.__docked
237    
238      def OnClose(self, event):      def Show(self, show = True):
239          self.issue(DOCKABLE_CLOSED, self.id, self)          """Show or hide the window."""
240          NonModalDialog.OnClose(self, event)          if show:
241                self.__DoShow()
242            else:
243                self.__DoHide()
244    
245        def SetDockSize(self, rect = None):
246            """Set the size of the dock window to rect."""
247    
248  #   def Show(self, show = True):          w0, h0 = self.__dockPanel.GetBestSize()
249            w, h = self.__panel.GetBestSize()
250    
251  #       if show:          if (w, h) < (w0, h0):
252                w = w0
253                h = h0
254    
255            if rect is not None:
256                rw = rect.width
257                rh = rect.height
258                if rw < w: rw = w
259                if rh < h: rh = h
260            else:
261                rw = w
262                rh = h
263    
264  #           if self.IsDocked():          # these are to account for the border?!!?
265  #               self.docked = False          rw += 8 # XXX: without this the sash isn't visible!?!?!?!
266  #               self.Dock()          rh += 8 # XXX: without this the sash isn't visible!?!?!?!
 #           else:  
 #               self.docked = True  
 #               self.UnDock()  
 #               #NonModalDialog.Show(self)  
 #       else:  
 #           self.docked = True  
 #           self.UnDock()  
 #           NonModalDialog.Hide(self)  
267    
268      def GetSize(self):          self.__dockWindow.SetDefaultSize(wx.Size(rw, rh))
         return self.dockBorder.GetSize()  
269    
     def GetBestSize(self):  
         return self.dockBorder.GetBestSize()  
270    
271      def GetClientSize(self):      def Destroy(self):
272          return self.dockBorder.GetClientSize()          self.__panel.Destroy()
273            self.__floatWindow.Destroy()
274            self.__dockWindow.Destroy()
275            self.__parent.OnDockDestroy(self)
276    
277      def GetAdjustedBestSize(self):      ##
278          return self.dockBorder.GetAdjustedBestSize()      # Event handlers
279        #
280    
281      def GetSizeTuple(self):      def _OnButtonClose(self, event):
282          return self.dockBorder.GetSizeTuple()          #self.Close()
283            self.Show(False)
284    
285      def _OnDock(self, event):      def _OnClose(self, force = False):
286            self.Show(False)
287    
288          self.SetDock(not self.docked)      def _OnToggleDock(self, event):
289            self.__CheckAllGood()
290    
291  #       if self.IsDocked():          self.SetDock(not self.IsDocked())
 #           win.SetLabel(_("Undock"))  
 #           self.UnDock()  
 #       else:  
 #           win.SetLabel(_("Dock"))  
 #           self.Dock()  
292    
293      #def _OnUnDock(self, event):      ##
294          #self.UnDock()      # Private methods
295        #
296    
297        def __CheckAllGood(self):
298            if self.__panel is None:
299                raise TypeError("")
300    
301        def __DoShow(self):
302            if self.IsShown(): return
303    
304            self.__topWindow.Show()
305    
306            if self.__topWindow is self.__dockWindow:
307                self.__parent._UpdateDocks()
308    
309        def __DoHide(self):
310            if not self.IsShown(): return
311    
312            self.__topWindow.Show(False)
313    
314            if self.__topWindow is self.__dockWindow:
315                self.__parent._UpdateDocks()
316    
317      def __CreateBorder(self):      def __CreateBorder(self):
318    
319          self.panel.Reparent(self) # Make sure we hang on to the panel          self.__dockPanel = wx.Panel(self.__topWindow, -1, style=wx.SUNKEN_BORDER)
320    
321            self.__panel.Reparent(self.__dockPanel)
322            self.__panel.SetId(PANEL_ID)
323    
324          sizer = wxBoxSizer(self.orientation)          if self.__orientation == wx.LAYOUT_VERTICAL:
325                sizer = wx.BoxSizer(wx.VERTICAL)
326                headerBoxOrient = wx.HORIZONTAL
327            else:
328                sizer = wx.BoxSizer(wx.HORIZONTAL)
329                headerBoxOrient = wx.VERTICAL
330    
331    
332            headerBox = wx.StaticBoxSizer(
333                            wx.StaticBox(self.__dockPanel, -1, ""), headerBoxOrient)
334    
335          self.dockBorder = wxPanel(self.dockBorderParent, -1)          #
336          #self.dockBorder.SetBackgroundColour(wxColour(255, 0, 0))          # ideally, we should be able to rotate this text depending on
337            # our orientation
338            #
339            text = wx.StaticText(self.__dockPanel, -1, self.GetTitle(),
340                                 style = wx.ALIGN_CENTRE)
341            font = text.GetFont()
342            font.SetPointSize(10)
343            text.SetFont(font)
344    
345          if self.orientation == wxVERTICAL:          #
346              buttonBoxOrient = wxHORIZONTAL          # load the dock/undock/close bitmaps
347            # and create the buttons
348            #
349            self.__bmpDock   = \
350                resource.GetBitmapResource(DOCK_BMP, wx.BITMAP_TYPE_XPM)
351            self.__bmpUnDock = \
352                resource.GetBitmapResource(UNDOCK_BMP, wx.BITMAP_TYPE_XPM)
353    
354            if self.__docked:
355                bmp = self.__bmpDock
356          else:          else:
357              buttonBoxOrient = wxVERTICAL              bmp = self.__bmpUnDock
358    
359            self.__dockButton = wx.BitmapButton(
360                self.__dockPanel, ID_BUTTON_DOCK,
361                bmp,
362                size = wx.Size(bmp.GetWidth() + 4, bmp.GetHeight() + 4),
363                style = wx.BU_EXACTFIT | wx.ADJUST_MINSIZE)
364    
365            bmp = resource.GetBitmapResource(CLOSE_BMP, wx.BITMAP_TYPE_XPM)
366    
367            closeX = wx.BitmapButton(self.__dockPanel, ID_BUTTON_CLOSE, bmp,
368                              size = wx.Size(bmp.GetWidth() + 4,
369                                            bmp.GetHeight() + 4),
370                              style = wx.BU_EXACTFIT | wx.ADJUST_MINSIZE)
371            closeX.SetToolTip(wx.ToolTip(_("Close")))
372    
373            #
374            # fill in the sizer in an order appropriate to the orientation
375            #
376            if self.__orientation == wx.LAYOUT_VERTICAL:
377                headerBox.Add(text, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL, 0)
378                headerBox.Add((1, 5), 1, wx.GROW)
379                headerBox.Add(self.__dockButton, 0, wx.ALIGN_RIGHT, 0)
380                headerBox.Add(closeX, 0, wx.ALIGN_RIGHT | wx.LEFT, 4)
381            else:
382                headerBox.Add(closeX, 0, wx.ALIGN_RIGHT | wx.BOTTOM, 4)
383                headerBox.Add(self.__dockButton, 0, wx.ALIGN_RIGHT, 0)
384                headerBox.Add(text, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL, 0)
385    
386            sizer.Add(headerBox, 0, wx.GROW, 0)
387            sizer.Add(self.__panel, 1, wx.GROW, 0)
388    
389    
390            self.__dockPanel.SetAutoLayout(True)
391            self.__dockPanel.SetSizer(sizer)
392            sizer.SetSizeHints(self.__dockPanel)
393            sizer.SetSizeHints(self.__floatWindow)
394    
395            self.Bind(wx.EVT_BUTTON, self._OnToggleDock, self.__dockPanel, id=ID_BUTTON_DOCK)
396            self.Bind(wx.EVT_BUTTON, self._OnButtonClose, self.__dockPanel, id=ID_BUTTON_CLOSE)
397    
         buttonBox = wxStaticBoxSizer(  
                         wxStaticBox(self.dockBorder, -1, ""),  
                         buttonBoxOrient)  
398    
399          button = wxStaticText(self.dockBorder, -1,  class DockFrame(wx.Frame):
400                                self.GetTitle(),      """A DockFrame is a frame that will contain dockable panels."""
                               style = wxSIMPLE_BORDER | wxALIGN_CENTRE)  
         buttonBox.Add(button, 0,  
                       wxSHAPED | wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL, 0)  
401    
402          buttonBox.Add(60, 20, 1, wxGROW)      def __init__(self, parent, id, title, position, size):
403            wx.Frame.__init__(self, parent, id, title, position, size)
404    
405            self.openWindows = {}
406    
407            self.__update_lock = 0
408    
409            self.SetMainWindow(None)
410    
411    
412            self.Bind(wx.EVT_SIZE, self._OnSashSize)
413            self.Bind(wx.EVT_CLOSE, self.OnClose)
414    
415        layout2oppSash = {
416                wx.LAYOUT_NONE   : wx.SASH_NONE,
417                wx.LAYOUT_TOP    : wx.SASH_BOTTOM,
418                wx.LAYOUT_LEFT   : wx.SASH_RIGHT,
419                wx.LAYOUT_RIGHT  : wx.SASH_LEFT,
420                wx.LAYOUT_BOTTOM : wx.SASH_TOP }
421    
422    
423        def OnClose(self, event):
424    
425            self.__update_lock += 1
426    
427          #          #
428          # Perhaps using wxToggleButton would be better, but it's only          # child windows are not notified when the parent is destroyed
429          # supported under wxMSW and wxGTK as of v2.4.0.3          # as of v2.4.0.3 so we need to interate over our children
430            # and tell them to go away.
431          #          #
432          self.dockButton = wxButton(self.dockBorder, ID_BUTTON_DOCK,          for key in self.openWindows.keys():
433              "WWWWWW", style = wxGROW | wxBU_EXACTFIT | wxADJUST_MINSIZE)              win = self.openWindows[key]
434          buttonBox.Add(self.dockButton, 0, wxALIGN_RIGHT, 0)              win.Destroy()
435    
436            self.__update_lock -= 1
437    
438            # should really call _UpdateDocks() here but we don't need to
439            # since we're going away
440    
441        def CreateDock(self, name, id, title, align):
442            """Create a new dock. align specifies where the dock will
443            be placed. It can be one of wxLAYOUT_NONE, wxLAYOUT_LEFT,
444            wxLAYOUT_RIGHT.
445            """
446    
447            if align in (wx.LAYOUT_NONE, wx.LAYOUT_LEFT, wx.LAYOUT_RIGHT):
448                orient = wx.LAYOUT_VERTICAL
449            else:
450                orient = wx.LAYOUT_HORIZONTAL
451    
452            sash = wx.SashLayoutWindow(self, id, style=wx.NO_BORDER|wx.SW_3D)
453            sash.SetOrientation(orient)
454            sash.SetAlignment(align)
455            sash.SetSashVisible(DockFrame.layout2oppSash[align], True)
456            sash.SetSashBorder(DockFrame.layout2oppSash[align], True)
457    
458            win = DockableWindow(self, id, name, title, sash, orient)
459    
460            self.__RegisterDock(name, win)
461            self.Bind(wx.EVT_SASH_DRAGGED, self._OnSashDragged, id=id)
462    
463            return win
464    
465        def FindRegisteredDock(self, name):
466            """Return a reference to a dock that has name."""
467            return self.openWindows.get(name)
468    
469        def OnDockDestroy(self, win):
470            del self.openWindows[win.GetName()]
471            self._UpdateDocks()
472    
473        def SetMainWindow(self, main):
474            self.__mainWindow = main
475            self._UpdateDocks()
476    
477        def _UpdateDocks(self):
478            if self.__update_lock == 0:
479                wx.LayoutAlgorithm().LayoutWindow(self, self.__mainWindow)
480    
481        def _OnSashDragged(self, event):
482            if event.GetDragStatus() == wx.SASH_STATUS_OUT_OF_RANGE:
483                return
484    
485            id = event.GetId()
486            sash = self.FindWindowById(id)
487            #assert(isinstance(win, wxPanel))
488            dockPanel = sash.GetChildren()[0]
489            panel = dockPanel.FindWindowById(PANEL_ID)
490            assert isinstance(panel, DockPanel)
491            win = panel.GetDockParent()
492            assert isinstance(win, DockableWindow)
493    
494            assert win.IsDocked()
495    
496          #button = wxButton(self.dockBorder, ID_BUTTON_UNDOCK,          rect = event.GetDragRect()
         #                      _("Undock"),  
         #                      style = wxSIMPLE_BORDER | wxBU_EXACTFIT)  
         #buttonBox.Add(button, 0, wxALIGN_RIGHT, 0)  
497    
498          sizer.Add(buttonBox, 0, wxGROW, 0)          win.SetDockSize(rect)
499    
500          self.panel.Reparent(self.dockBorder)          self._UpdateDocks()
501    
502          sizer.Add(self.panel, 1, wxGROW | wxALL, 0)      def _OnSashSize(self, event):
503          self.dockBorder.SetSizer(sizer)          self._UpdateDocks()
         self.dockBorder.SetAutoLayout(True)  
504    
505          #self.dockBorderParent.SetAutoLayout(True)      def __RegisterDock(self, name, win):
506            if self.FindRegisteredDock(name) is not None:
507                raise ValueError(
508                    "A dockable window is already registered under the name '%s'" % name)
509    
510          EVT_BUTTON(self.dockBorder, ID_BUTTON_DOCK, self._OnDock)          self.openWindows[name] = win
         #EVT_BUTTON(self.dockBorder, ID_BUTTON_UNDOCK, self._OnUnDock)  
511    

Legend:
Removed from v.566  
changed lines
  Added in v.2734

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26