1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 __version__ = "$Revision: 1.7 $"
17 __author__ = "Gerrit van Dyk, Carlos, Karsten"
18
19
20 try:
21 import wxversion
22 import wx
23 except ImportError:
24 from wxPython import wx
25
26 MV_HOR = 0
27 MV_VER = not MV_HOR
28
29 SH_SIZE = 5
30 CR_SIZE = SH_SIZE * 3
31
32
34 """
35 Main multisash widget. Dynamically displays a stack of child widgets.
36 """
38 apply(wx.Window.__init__,(self,) + _args,_kwargs)
39
40 self.child = cMultiSashSplitter(self,self,wxPoint(0,0),self.GetSize())
41
42
43 self.focussed_leaf = self.child.leaf1
44 self.bottom_leaf = self.child.leaf1
45 self.displayed_leafs = []
46
47 wx.EVT_SIZE(self,self._on_size)
48
49
50
51
53 """
54 Retrieves the currently focused leaf. Typically, used to
55 process some action over the focused widget.
56 """
57 return self.focussed_leaf
58
60 """
61 Retrieves the currently displayed leafs.
62 """
63 return self.displayed_leafs
64
65 - def add_content(self, content):
66 """
67 Adds he supplied content widget to the multisash, setting it as
68 child of the bottom leaf.
69
70 @param content The new content widget to add.
71 @type content Any wx.Window derived object.
72 """
73 successful, errno = self.bottom_leaf.AddLeaf(direction = MV_VER, pos = 100)
74 if successful:
75 self.bottom_leaf.set_content(content)
76 return successful, errno
77
79 """
80 Clear all mulsisash leafs and restores initial values
81 """
82
83 old = self.child
84 self.child = cMultiSashSplitter(self,self,wxPoint(0,0),self.GetSize())
85 old.Destroy()
86 self.child.OnSize(None)
87
88
89 self.focussed_leaf = self.child.leaf1
90 self.bottom_leaf = self.child.leaf1
91 self.bottom_leaf.Select()
92 self.displayed_leafs = []
93
95 """
96 Updates the field that keeps track of the bottom leaf. It is required
97 to ensure new leafs are created under the bottom one.
98 If the bottom leaf is supplied as parameter, it is set. Typically,
99 after a new leaf has been added/created.
100 If the bottom leaf ins not supplied ad parameter, it will be dinamically
101 obtained. Typically, after leaf destruction.
102
103 @param bottom_leaf The leaf to be set as bottom one
104 @type bottom_leaf wx.MultiViewLeaf
105 """
106 if bottom_leaf is None:
107 self.bottom_leaf = self.__find_bottom_leaf(self.child)
108 else:
109 self.bottom_leaf = bottom_leaf
110 self.bottom_leaf.Select()
111
112
114 """
115 Recursively find all displayed leafs.
116 @param splitter The multisash splitter to traverse its leafs for.
117 @type splitter cMultiSashSplitter
118 """
119 if id(splitter) == id(self.child):
120 self.displayed_leafs = []
121
122
123
124
125 if isinstance(splitter.leaf1, cMultiSashSplitter):
126
127 self.refresh_displayed_leafs(splitter.leaf1)
128 if isinstance(splitter.leaf2, cMultiSashSplitter):
129
130 self.refresh_displayed_leafs(splitter.leaf2)
131
132 if not splitter.leaf2 is None and not isinstance(splitter.leaf2, cMultiSashSplitter):
133
134 self.displayed_leafs.append(splitter.leaf2)
135 if not splitter.leaf1 is None and not isinstance(splitter.leaf1, cMultiSashSplitter):
136
137 self.displayed_leafs.append(splitter.leaf1)
138
139
140
142 self.child.SetSize(self.GetSize())
143
144
145
147 """
148 Recursively find and return the bottom leaf.
149 """
150
151
152
153
154 if isinstance(splitter.leaf1, cMultiSashSplitter):
155
156 return self.__find_bottom_leaf(splitter.leaf1)
157 if isinstance(splitter.leaf2, cMultiSashSplitter):
158
159 return self.__find_bottom_leaf(splitter.leaf2)
160
161 if not splitter.leaf2 is None:
162
163 return splitter.leaf2
164 else:
165
166 return splitter.leaf1
167
169 self.child._unselect()
170
172 """
173 Basic split windows container of the multisash widget.
174 Has references to two leafs or splitted windows (typically, first leaf
175 is another cMultiSashSplitter and the second leaf is the displayed content
176 widget).
177 """
178 - def __init__(self, top_parent, parent, pos, size, leaf1 = None):
179 wx.Window.__init__ (
180 self,
181 id = -1,
182 parent = parent,
183 pos = pos,
184 size = size,
185 style = wx.CLIP_CHILDREN
186 )
187 self.top_parent = top_parent
188 self.leaf2 = None
189 if leaf1:
190 self.leaf1 = leaf1
191 self.leaf1.Reparent(self)
192 self.leaf1.MoveXY(0,0)
193 else:
194 self.leaf1 = cMultiSashLeaf (
195 self.top_parent,
196 self,
197 wxPoint(0,0),
198 self.GetSize()
199 )
200 self.direction = None
201
202 wx.EVT_SIZE(self,self.OnSize)
203
205 if self.leaf1:
206 self.leaf1._unselect()
207 if self.leaf2:
208 self.leaf2._unselect()
209
210
211 - def AddLeaf(self,direction,caller,pos):
212
213
214
215 if self.leaf2:
216
217
218 if caller == self.leaf1:
219
220 self.leaf1 = cMultiSashSplitter(self.top_parent,self,
221 caller.GetPosition(),
222 caller.GetSize(),
223 caller)
224 self.leaf1.AddLeaf(direction,caller,pos)
225 else:
226
227 self.leaf2 = cMultiSashSplitter(self.top_parent,self,
228 caller.GetPosition(),
229 caller.GetSize(),
230 caller)
231 self.leaf2.AddLeaf(direction,caller,pos)
232 else:
233
234
235
236 self.direction = direction
237 w,h = self.GetSizeTuple()
238 if direction == MV_HOR:
239
240 x,y = (pos,0)
241 w1,h1 = (w-pos,h)
242 w2,h2 = (pos,h)
243 else:
244
245 x,y = (0,pos)
246 w1,h1 = (w,h-pos)
247 w2,h2 = (w,pos)
248 self.leaf2 = cMultiSashLeaf(self.top_parent,self,
249 wxPoint(x,y),wx.Size(w1,h1))
250 self.leaf1.SetSize(wx.Size(w2,h2))
251 self.leaf2.OnSize(None)
252
253 self.top_parent.displayed_leafs.append(self.leaf2)
254
255 self.top_parent.refresh_bottom_leaf(self.leaf2)
256 self.leaf2.set_focus()
257
259
260 top_parent = self.top_parent
261 if not self.leaf2:
262 self.leaf1.set_content(cEmptyChild(self))
263 return
264 parent = self.GetParent()
265 if parent == self.top_parent:
266
267
268 if caller == self.leaf1:
269
270 old = self.leaf1
271 self.leaf1 = self.leaf2
272 self.leaf2 = None
273
274
275
276 old.Destroy()
277 else:
278
279
280
281
282 self.leaf2.Destroy()
283 self.leaf2 = None
284 self.leaf1.SetSize(self.GetSize())
285 self.leaf1.Move(self.GetPosition())
286 else:
287
288
289
290
291 self.top_parent.displayed_leafs.remove(caller)
292 w,h = self.GetSizeTuple()
293 x,y = self.GetPositionTuple()
294 if caller == self.leaf1:
295 if self == parent.leaf1:
296
297 parent.leaf1 = self.leaf2
298 else:
299
300 parent.leaf2 = self.leaf2
301
302 self.leaf2.Reparent(parent)
303 self.leaf2.SetDimensions(x,y,w,h)
304
305
306 else:
307 if self == parent.leaf1:
308
309 parent.leaf1 = self.leaf1
310 else:
311
312 parent.leaf2 = self.leaf1
313
314 self.leaf1.Reparent(parent)
315 self.leaf1.SetDimensions(x,y,w,h)
316
317 self.leaf1 = None
318 self.leaf2 = None
319 top_parent = self.top_parent
320 self.Destroy()
321
322
323
324
325
326
327
328
329
330 top_parent.refresh_bottom_leaf()
331 top_parent.refresh_displayed_leafs(top_parent.child)
332
333
334
335
336
337
342
344 if self.direction == side and self.leaf2 and view == self.leaf1:
345 return self
346 parent = self.GetParent()
347 if parent != self.top_parent:
348 return parent.SizeTarget(side,self)
349 return None
350
352 if self.direction != side:
353 return
354 if not (self.leaf1 and self.leaf2):
355 return
356 if pos < 10: return
357 w,h = self.GetSizeTuple()
358 if side == MV_HOR:
359 if pos > w - 10: return
360 else:
361 if pos > h - 10: return
362 if side == MV_HOR:
363 self.leaf1.SetDimensions(0,0,pos,h)
364 self.leaf2.SetDimensions(pos,0,w-pos,h)
365 else:
366 self.leaf1.SetDimensions(0,0,w,pos)
367 self.leaf2.SetDimensions(0,pos,w,h-pos)
368
370 if not self.leaf2:
371 self.leaf1.SetSize(self.GetSize())
372 self.leaf1.OnSize(None)
373 return
374 v1w,v1h = self.leaf1.GetSizeTuple()
375 v2w,v2h = self.leaf2.GetSizeTuple()
376 v1x,v1y = self.leaf1.GetPositionTuple()
377 v2x,v2y = self.leaf2.GetPositionTuple()
378 w,h = self.GetSizeTuple()
379
380 if v1x != v2x:
381 ratio = float(w) / float((v1w + v2w))
382 v1w *= ratio
383 v2w = w - v1w
384 v2x = v1w
385 else:
386 v1w = v2w = w
387
388 if v1y != v2y:
389 ratio = float(h) / float((v1h + v2h))
390 v1h *= ratio
391 v2h = h - v1h
392 v2y = v1h
393 else:
394 v1h = v2h = h
395
396 self.leaf1.SetDimensions(v1x,v1y,v1w,v1h)
397 self.leaf2.SetDimensions(v2x,v2y,v2w,v2h)
398 self.leaf1.OnSize(None)
399 self.leaf2.OnSize(None)
400
401
403 """
404 A leaf represent a split window, one instance of the displayed content
405 widget.
406 """
407 - def __init__(self,top_parent,parent,pos,size):
421
423 """
424 Set current leaf as focused leaf. Typically, the focused widget
425 will be required to further actions and processing.
426 """
427 self.top_parent.focussed_leaf = self
428
429
431 self.content._unselect()
432
433
434 - def set_content(self, content):
435 """
436 Sets the as content child of this leaf.
437
438 @param content The new content widget to set..
439 @type content Any wx.Window derived object.
440 """
441 self.content.set_new_content(content)
442
443
444 - def get_content(self):
445 """
446 Retrieves the content child of this leaf.
447 """
448 return self.content.child
449
450
452 """
453 Select the leaf
454 """
455 self.content.Select()
456
458 """Add a leaf.
459
460 returns (Status, error)
461 errors:
462 1: lacking space to add leaf
463 """
464
465 if pos < 10:
466 pos = 10
467 w,h = self.GetSizeTuple()
468 if direction == MV_VER:
469 if pos > h - 10:
470
471
472 return (False, 1)
473 else:
474 if pos > w - 10: return (False, 1)
475
476
477 if not isinstance(self.content.child, cEmptyChild):
478 self.GetParent().AddLeaf(direction,self,pos)
479 else:
480 self.set_focus()
481
482 self.GetParent().top_parent.displayed_leafs.append(self)
483 return (True, None)
484
485
489
490
493
495 return self.GetParent().CanSize(side,self)
496
498 self.sizerHor.OnSize(evt)
499 self.sizerVer.OnSize(evt)
500
501
502
503 self.content.OnSize(evt)
504 self.closer.OnSize(evt)
505
506
507 -class cMultiSashLeafContent(wx.Window):
508 """
509 Widget that encapsulate contents of a leaf or split window.
510 """
511 - def __init__(self,parent):
512 w,h = self.CalcSize(parent)
513 wx.Window.__init__ (
514 self,
515 id = -1,
516 parent = parent,
517 pos = wxPoint(0,0),
518 size = wx.Size(w,h),
519 style = wx.CLIP_CHILDREN | wx.SUNKEN_BORDER
520 )
521 self.child = cEmptyChild(self)
522 self.child.MoveXY(2,2)
523 self.__normal_colour = self.GetBackgroundColour()
524 self.selected = False
525
526 wx.EVT_SET_FOCUS(self, self._on_set_focus)
527 wx.EVT_CHILD_FOCUS(self, self._on_child_focus)
528
529 - def set_new_content(self,content):
530 """
531 Sets the as content child of this widget.
532
533 @param content The new content widget to set..
534 @type content Any wx.Window derived object.
535 """
536
537 self.SetBackgroundColour(self.__normal_colour)
538 if self.child:
539 self.child.Destroy()
540 content.Reparent(self)
541 self.child = content
542 self.child.MoveXY(2,2)
543
544 self.Select()
545 self.OnSize(None)
546
547
548
549 - def _unselect(self):
550 if self.selected:
551 self.selected = False
552 self.SetBackgroundColour(self.__normal_colour)
553 self.Refresh()
554
555 - def _on_set_focus(self,evt):
557
558 - def _on_child_focus(self,evt):
559 self._on_set_focus(evt)
560
561
562
563
564
566 """
567 May be invoked by user clicking on the leaf, or programmatically after
568 leaf creation. Highlight it and update focused leaf.
569 """
570
571 parent = self.GetParent()
572 parent.top_parent._unselect()
573 self.selected = True
574 if parent.top_parent.focussed_leaf != parent:
575 parent.set_focus()
576 self.SetBackgroundColour(wx.Colour(255,255,0))
577 self.Refresh()
578
579 - def CalcSize(self,parent):
580 w,h = parent.GetSizeTuple()
581 w -= SH_SIZE
582 h -= SH_SIZE
583 return (w,h)
584
585 - def OnSize(self,evt):
586 w,h = self.CalcSize(self.GetParent())
587 self.SetDimensions(0,0,w,h)
588 w,h = self.GetClientSizeTuple()
589 self.child.SetSize(wx.Size(w-4,h-4))
590
592 """
593 Leaf's sash bar
594 """
596 self.side = side
597 x,y,w,h = self.CalcSizePos(parent)
598 wx.Window.__init__(self,id = -1,parent = parent,
599 pos = wxPoint(x,y),
600 size = wx.Size(w,h),
601 style = wx.CLIP_CHILDREN)
602
603 self.px = None
604 self.py = None
605 self.isDrag = False
606 self.dragTarget = None
607
608 wx.EVT_LEAVE_WINDOW(self,self.OnLeave)
609 wx.EVT_ENTER_WINDOW(self,self.OnEnter)
610 wx.EVT_MOTION(self,self.OnMouseMove)
611 wx.EVT_LEFT_DOWN(self,self.OnPress)
612 wx.EVT_LEFT_UP(self,self.OnRelease)
613
627
629 x,y,w,h = self.CalcSizePos(self.GetParent())
630 self.SetDimensions(x,y,w,h)
631
633 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
634
636 if not self.GetParent().CanSize(not self.side):
637 return
638 if self.side == MV_HOR:
639 self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS))
640 else:
641 self.SetCursor(wx.StockCursor(wx.CURSOR_SIZEWE))
642
644 if self.isDrag:
645 DrawSash(self.dragTarget,self.px,self.py,self.side)
646 self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
647 self.px,self.py = self.dragTarget.ScreenToClientXY(self.px,self.py)
648 DrawSash(self.dragTarget,self.px,self.py,self.side)
649 else:
650 evt.Skip()
651
653 self.dragTarget = self.GetParent().SizeTarget(not self.side)
654 if self.dragTarget:
655 self.isDrag = True
656 self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
657 self.px,self.py = self.dragTarget.ScreenToClientXY(self.px,self.py)
658 DrawSash(self.dragTarget,self.px,self.py,self.side)
659 self.CaptureMouse()
660 else:
661 evt.Skip()
662
664 if self.isDrag:
665 DrawSash(self.dragTarget,self.px,self.py,self.side)
666 self.ReleaseMouse()
667 self.isDrag = False
668 if self.side == MV_HOR:
669 self.dragTarget.SizeLeaf(self.GetParent(),
670 self.py,not self.side)
671 else:
672 self.dragTarget.SizeLeaf(self.GetParent(),
673 self.px,not self.side)
674 self.dragTarget = None
675 else:
676 evt.Skip()
677
678
680 """
681 Sash bar's creator element
682 """
684 self.side = side
685 x,y,w,h = self.CalcSizePos(parent)
686 wx.Window.__init__(self,id = -1,parent = parent,
687 pos = wxPoint(x,y),
688 size = wx.Size(w,h),
689 style = wx.CLIP_CHILDREN)
690
691 self.px = None
692 self.py = None
693 self.isDrag = False
694
695 wx.EVT_LEAVE_WINDOW(self,self.OnLeave)
696 wx.EVT_ENTER_WINDOW(self,self.OnEnter)
697 wx.EVT_MOTION(self,self.OnMouseMove)
698 wx.EVT_LEFT_DOWN(self,self.OnPress)
699 wx.EVT_LEFT_UP(self,self.OnRelease)
700 wx.EVT_PAINT(self,self.OnPaint)
701
715
717 x,y,w,h = self.CalcSizePos(self.GetParent())
718 self.SetDimensions(x,y,w,h)
719
721 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
722
724 if self.side == MV_HOR:
725 self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
726 else:
727 self.SetCursor(wx.StockCursor(wx.CURSOR_POINT_LEFT))
728
730 if self.isDrag:
731 parent = self.GetParent()
732 DrawSash(parent,self.px,self.py,self.side)
733 self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
734 self.px,self.py = parent.ScreenToClientXY(self.px,self.py)
735 DrawSash(parent,self.px,self.py,self.side)
736 else:
737 evt.Skip()
738
740 self.isDrag = True
741 parent = self.GetParent()
742 self.px,self.py = self.ClientToScreenXY(evt.m_x,evt.m_y)
743 self.px,self.py = parent.ScreenToClientXY(self.px,self.py)
744 DrawSash(parent,self.px,self.py,self.side)
745 self.CaptureMouse()
746
748 if self.isDrag:
749 parent = self.GetParent()
750 DrawSash(parent,self.px,self.py,self.side)
751 self.ReleaseMouse()
752 self.isDrag = False
753
754 if self.side == MV_HOR:
755 parent.AddLeaf(MV_VER,self.py)
756 else:
757 parent.AddLeaf(MV_HOR,self.px)
758 else:
759 evt.Skip()
760
762 dc = wxPaintDC(self)
763 dc.SetBackground(wx.Brush(self.GetBackgroundColour(),wx.SOLID))
764 dc.Clear()
765
766 highlight = wxPen(wx.SystemSettings_GetSystemColour(
767 wx.SYS_COLOUR_BTNHIGHLIGHT),1,wx.SOLID)
768 shadow = wxPen(wx.SystemSettings_GetSystemColour(
769 wx.SYS_COLOUR_BTNSHADOW),1,wx.SOLID)
770 black = wxPen(wx.BLACK,1,wx.SOLID)
771 w,h = self.GetSizeTuple()
772 w -= 1
773 h -= 1
774
775
776 dc.SetPen(highlight)
777 dc.DrawLine(0,0,0,h)
778 dc.DrawLine(0,0,w,0)
779 dc.SetPen(black)
780 dc.DrawLine(0,h,w+1,h)
781 dc.DrawLine(w,0,w,h)
782 dc.SetPen(shadow)
783 dc.DrawLine(w-1,2,w-1,h)
784
785
787 """
788 Sash bar's destroyer element
789 """
791 x,y,w,h = self.CalcSizePos(parent)
792 wx.Window.__init__(self,id = -1,parent = parent,
793 pos = wxPoint(x,y),
794 size = wx.Size(w,h),
795 style = wx.CLIP_CHILDREN)
796
797 self.down = False
798 self.entered = False
799
800 wx.EVT_LEFT_DOWN(self,self.OnPress)
801 wx.EVT_LEFT_UP(self,self.OnRelease)
802 wx.EVT_PAINT(self,self.OnPaint)
803 wx.EVT_LEAVE_WINDOW(self,self.OnLeave)
804 wx.EVT_ENTER_WINDOW(self,self.OnEnter)
805
807 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
808 self.entered = False
809
811 self.SetCursor(wx.StockCursor(wx.CURSOR_BULLSEYE))
812 self.entered = True
813
815 self.down = True
816 evt.Skip()
817
819 if self.down and self.entered:
820 self.GetParent().DestroyLeaf()
821 else:
822 evt.Skip()
823 self.down = False
824
826 dc = wxPaintDC(self)
827 dc.SetBackground(wx.Brush(wx.RED,wx.SOLID))
828 dc.Clear()
829
831 pw,ph = parent.GetSizeTuple()
832 x = pw - SH_SIZE
833 w = SH_SIZE
834 h = SH_SIZE + 2
835 y = 1
836 return (x,y,w,h)
837
839 x,y,w,h = self.CalcSizePos(self.GetParent())
840 self.SetDimensions(x,y,w,h)
841
842
843
844
845
848 wx.Window.__init__(self,parent,-1, style = wx.CLIP_CHILDREN)
849
850
851
852
853
855 dc = wx.ScreenDC()
856 dc.StartDrawingOnTopWin(win)
857 bmp = wx.EmptyBitmap(8,8)
858 bdc = wx.MemoryDC()
859 bdc.SelectObject(bmp)
860 bdc.DrawRectangle(-1,-1,10,10)
861 for i in range(8):
862 for j in range(8):
863 if ((i + j) & 1):
864 bdc.DrawPoint(i,j)
865
866 brush = wx.Brush(wx.Colour(0,0,0))
867 brush.SetStipple(bmp)
868
869 dc.SetBrush(brush)
870 dc.SetLogicalFunction(wx.XOR)
871
872 body_w,body_h = win.GetClientSizeTuple()
873
874 if y < 0:
875 y = 0
876 if y > body_h:
877 y = body_h
878 if x < 0:
879 x = 0
880 if x > body_w:
881 x = body_w
882
883 if direction == MV_HOR:
884 x = 0
885 else:
886 y = 0
887
888 x,y = win.ClientToScreenXY(x,y)
889
890 w = body_w
891 h = body_h
892
893 if direction == MV_HOR:
894 dc.DrawRectangle(x,y-2,w,4)
895 else:
896 dc.DrawRectangle(x-2,y,4,h)
897
898 dc.EndDrawingOnTop()
899
900