]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/splitter.py
Better fix for 'int i' used twice in the same block.
[wxWidgets.git] / wxPython / wx / lib / splitter.py
1 #----------------------------------------------------------------------
2 # Name: wx.lib.splitter
3 # Purpose: A class similar to wx.SplitterWindow but that allows more
4 # a single split
5 #
6 # Author: Robin Dunn
7 #
8 # Created: 9-June-2005
9 # RCS-ID: $Id$
10 # Copyright: (c) 2005 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------
13 """
14 This module provides the `MultiSplitterWindow` class, which is very
15 similar to the standard `wx.SplitterWindow` except it can be split
16 more than once.
17 """
18
19 import wx
20 import sys
21
22 _RENDER_VER = (2,6,1,1)
23
24 #----------------------------------------------------------------------
25
26 class MultiSplitterWindow(wx.PyPanel):
27 """
28 This class is very similar to `wx.SplitterWindow` except that it
29 allows for more than two windows and more than one sash. Many of
30 the same styles, constants, and methods behave the same as in
31 wx.SplitterWindow. The key differences are seen in the methods
32 that deal with the child windows manage by the splitter, and also
33 those that deal with the sash positions. In most cases you will
34 need to pass an index value to tell the class which window or sash
35 you are refering to.
36
37 The concept of the sash position is also different than in
38 wx.SplitterWindow. Since the wx.Splitterwindow has only one sash
39 you can think of it's position as either relative to the whole
40 splitter window, or as relative to the first window pane managed
41 by the splitter. Once there are more than one sash then the
42 distinciton between the two concepts needs to be clairified. I've
43 chosen to use the second definition, and sash positions are the
44 distance (either horizontally or vertically) from the origin of
45 the window just before the sash in the splitter stack.
46
47 NOTE: These things are not yet supported:
48
49 * Using negative sash positions to indicate a position offset
50 from the end.
51
52 * User controlled unsplitting (with double clicks on the sash
53 or dragging a sash until the pane size is zero.)
54
55 * Sash gravity
56
57 """
58 def __init__(self, parent, id=-1,
59 pos = wx.DefaultPosition, size = wx.DefaultSize,
60 style = 0, name="multiSplitter"):
61
62 # always turn on tab traversal
63 style |= wx.TAB_TRAVERSAL
64
65 # and turn off any border styles
66 style &= ~wx.BORDER_MASK
67 style |= wx.BORDER_NONE
68
69 # initialize the base class
70 wx.PyPanel.__init__(self, parent, id, pos, size, style, name)
71 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
72
73 # initialize data members
74 self._windows = []
75 self._sashes = []
76 self._pending = {}
77 self._permitUnsplitAlways = self.HasFlag(wx.SP_PERMIT_UNSPLIT)
78 self._orient = wx.HORIZONTAL
79 self._dragMode = wx.SPLIT_DRAG_NONE
80 self._activeSash = -1
81 self._oldX = 0
82 self._oldY = 0
83 self._checkRequestedSashPosition = False
84 self._minimumPaneSize = 0
85 self._sashCursorWE = wx.StockCursor(wx.CURSOR_SIZEWE)
86 self._sashCursorNS = wx.StockCursor(wx.CURSOR_SIZENS)
87 self._sashTrackerPen = wx.Pen(wx.BLACK, 2, wx.SOLID)
88 self._needUpdating = False
89 self._isHot = False
90
91 # Bind event handlers
92 self.Bind(wx.EVT_PAINT, self._OnPaint)
93 self.Bind(wx.EVT_IDLE, self._OnIdle)
94 self.Bind(wx.EVT_SIZE, self._OnSize)
95 self.Bind(wx.EVT_MOUSE_EVENTS, self._OnMouse)
96
97
98
99 def SetOrientation(self, orient):
100 """
101 Set whether the windows managed by the splitter will be
102 stacked vertically or horizontally. The default is
103 horizontal.
104 """
105 assert orient in [ wx.VERTICAL, wx.HORIZONTAL ]
106 self._orient = orient
107
108 def GetOrientation(self):
109 """
110 Returns the current orientation of the splitter, either
111 wx.VERTICAL or wx.HORIZONTAL.
112 """
113 return self._orient
114
115
116 def SetMinimumPaneSize(self, minSize):
117 """
118 Set the smallest size that any pane will be allowed to be
119 resized to.
120 """
121 self._minimumPaneSize = minSize
122
123 def GetMinimumPaneSize(self):
124 """
125 Returns the smallest allowed size for a window pane.
126 """
127 return self._minimumPaneSize
128
129
130
131 def AppendWindow(self, window, sashPos=-1):
132 """
133 Add a new window to the splitter. If sashPos is given then it is the
134 """
135 self.InsertWindow(sys.maxint, window, sashPos)
136
137
138 def InsertWindow(self, idx, window, sashPos=-1):
139 """
140 """
141 assert window not in self._windows, "A window can only be in the splitter once!"
142 self._windows.insert(idx, window)
143 self._sashes.insert(idx, -1)
144 if not window.IsShown():
145 window.Show()
146 if sashPos != -1:
147 self._pending[window] = sashPos
148 self._checkRequestedSashPosition = False
149 self._SizeWindows()
150
151
152 def DetachWindow(self, window):
153 """
154 Removes the window from the stack of windows managed by the
155 splitter. The window will still exist so you should `Hide` or
156 `Destroy` it as needed.
157 """
158 assert window in self._windows, "Unknown window!"
159 idx = self._windows.index(window)
160 del self._windows[idx]
161 del self._sashes[idx]
162 self._SizeWindows()
163
164
165 def ReplaceWindow(self, oldWindow, newWindow):
166 """
167 Replaces oldWindow (which is currently being managed by the
168 splitter) with newWindow. The oldWindow window will still
169 exist so you should `Hide` or `Destroy` it as needed.
170 """
171 assert oldWindow in self._windows, "Unknown window!"
172 idx = self._windows.index(oldWindow)
173 self._windows[idx] = newWindow
174 if not newWindow.IsShown():
175 newWindow.Show()
176 self._SizeWindows()
177
178
179 def ExchangeWindows(self, window1, window2):
180 """
181 Trade the positions in the splitter of the two windows.
182 """
183 assert window1 in self._windows, "Unknown window!"
184 assert window2 in self._windows, "Unknown window!"
185 idx1 = self._windows.index(window1)
186 idx2 = self._windows.index(window2)
187 self._windows[idx1] = window2
188 self._windows[idx2] = window1
189 self._SizeWindows()
190
191
192 def GetWindow(self, idx):
193 """
194 Returns the idx'th window being managed by the splitter.
195 """
196 assert idx < len(self._windows)
197 return self._windows[idx]
198
199
200 def GetSashPosition(self, idx):
201 """
202 Returns the position of the idx'th sash, measured from the
203 left/top of the window preceding the sash.
204 """
205 assert idx < len(self._sashes)
206 return self._sashes[idx]
207
208
209 def SizeWindows(self):
210 """
211 Reposition and size the windows managed by the splitter.
212 Useful when windows have been added/removed or when styles
213 have been changed.
214 """
215 self._SizeWindows()
216
217
218 def DoGetBestSize(self):
219 """
220 Overridden base class virtual. Determines the best size of
221 the control based on the best sizes of the child windows.
222 """
223 best = wx.Size(0,0)
224 if not self._windows:
225 best = wx.Size(10,10)
226
227 sashsize = self._GetSashSize()
228 if self._orient == wx.HORIZONTAL:
229 for win in self._windows:
230 winbest = win.GetAdjustedBestSize()
231 best.width += max(self._minimumPaneSize, winbest.width)
232 best.height = max(best.height, winbest.height)
233 best.width += sashsize * (len(self._windows)-1)
234
235 else:
236 for win in self._windows:
237 winbest = win.GetAdjustedBestSize()
238 best.height += max(self._minimumPaneSize, winbest.height)
239 best.width = max(best.width, winbest.width)
240 best.height += sashsize * (len(self._windows)-1)
241
242 border = 2 * self._GetBorderSize()
243 best.width += border
244 best.height += border
245 return best
246
247 # -------------------------------------
248 # Event handlers
249
250 def _OnPaint(self, evt):
251 dc = wx.PaintDC(self)
252 self._DrawSash(dc)
253
254
255 def _OnSize(self, evt):
256 parent = wx.GetTopLevelParent(self)
257 if parent.IsIconized():
258 evt.Skip()
259 return
260 self._SizeWindows()
261
262
263 def _OnIdle(self, evt):
264 evt.Skip()
265 # if this is the first idle time after a sash position has
266 # potentially been set, allow _SizeWindows to check for a
267 # requested size.
268 if not self._checkRequestedSashPosition:
269 self._checkRequestedSashPosition = True
270 self._SizeWindows()
271
272 if self._needUpdating:
273 self._SizeWindows()
274
275
276
277 def _OnMouse(self, evt):
278 if self.HasFlag(wx.SP_NOSASH):
279 return
280
281 x, y = evt.GetPosition()
282 isLive = self.HasFlag(wx.SP_LIVE_UPDATE)
283 adjustNeighbor = evt.ShiftDown()
284
285 # LeftDown: set things up for dragging the sash
286 if evt.LeftDown() and self._SashHitTest(x, y) != -1:
287 self._activeSash = self._SashHitTest(x, y)
288 self._dragMode = wx.SPLIT_DRAG_DRAGGING
289
290 self.CaptureMouse()
291 self._SetResizeCursor()
292
293 if not isLive:
294 self._pendingPos = (self._sashes[self._activeSash],
295 self._sashes[self._activeSash+1])
296 self._DrawSashTracker(x, y)
297
298 self._oldX = x
299 self._oldY = y
300 return
301
302 # LeftUp: Finsish the drag
303 elif evt.LeftUp() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
304 self._dragMode = wx.SPLIT_DRAG_NONE
305 self.ReleaseMouse()
306 self.SetCursor(wx.STANDARD_CURSOR)
307
308 if not isLive:
309 # erase the old tracker
310 self._DrawSashTracker(self._oldX, self._oldY)
311
312 diff = self._GetMotionDiff(x, y)
313
314 # determine if we can change the position
315 if isLive:
316 oldPos1, oldPos2 = (self._sashes[self._activeSash],
317 self._sashes[self._activeSash+1])
318 else:
319 oldPos1, oldPos2 = self._pendingPos
320 newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
321 oldPos1 + diff,
322 oldPos2 - diff,
323 adjustNeighbor)
324 if newPos1 == -1:
325 # the change was not allowed
326 return
327
328 # TODO: check for unsplit?
329
330 self._SetSashPositionAndNotify(self._activeSash, newPos1, newPos2, adjustNeighbor)
331 self._activeSash = -1
332 self._pendingPos = (-1, -1)
333 self._SizeWindows()
334
335 # Entering or Leaving a sash: Change the cursor
336 elif (evt.Moving() or evt.Leaving() or evt.Entering()) and self._dragMode == wx.SPLIT_DRAG_NONE:
337 if evt.Leaving() or self._SashHitTest(x, y) == -1:
338 self._OnLeaveSash()
339 else:
340 self._OnEnterSash()
341
342 # Dragging the sash
343 elif evt.Dragging() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
344 diff = self._GetMotionDiff(x, y)
345 if not diff:
346 return # mouse didn't move far enough
347
348 # determine if we can change the position
349 if isLive:
350 oldPos1, oldPos2 = (self._sashes[self._activeSash],
351 self._sashes[self._activeSash+1])
352 else:
353 oldPos1, oldPos2 = self._pendingPos
354 newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
355 oldPos1 + diff,
356 oldPos2 - diff,
357 adjustNeighbor)
358 if newPos1 == -1:
359 # the change was not allowed
360 return
361
362 if newPos1 == self._sashes[self._activeSash]:
363 return # nothing was changed
364
365 if not isLive:
366 # erase the old tracker
367 self._DrawSashTracker(self._oldX, self._oldY)
368
369 if self._orient == wx.HORIZONTAL:
370 x = self._SashToCoord(self._activeSash, newPos1)
371 else:
372 y = self._SashToCoord(self._activeSash, newPos1)
373
374 # Remember old positions
375 self._oldX = x
376 self._oldY = y
377
378 if not isLive:
379 # draw a new tracker
380 self._pendingPos = (newPos1, newPos2)
381 self._DrawSashTracker(self._oldX, self._oldY)
382 else:
383 self._DoSetSashPosition(self._activeSash, newPos1, newPos2, adjustNeighbor)
384 self._needUpdating = True
385
386
387 # -------------------------------------
388 # Internal helpers
389
390 def _RedrawIfHotSensitive(self, isHot):
391 if not wx.VERSION >= _RENDER_VER:
392 return
393 if wx.RendererNative.Get().GetSplitterParams(self).isHotSensitive:
394 self._isHot = isHot
395 dc = wx.ClientDC(self)
396 self._DrawSash(dc)
397
398
399 def _OnEnterSash(self):
400 self._SetResizeCursor()
401 self._RedrawIfHotSensitive(True)
402
403
404 def _OnLeaveSash(self):
405 self.SetCursor(wx.STANDARD_CURSOR)
406 self._RedrawIfHotSensitive(False)
407
408
409 def _SetResizeCursor(self):
410 if self._orient == wx.HORIZONTAL:
411 self.SetCursor(self._sashCursorWE)
412 else:
413 self.SetCursor(self._sashCursorNS)
414
415
416 def _OnSashPositionChanging(self, idx, newPos1, newPos2, adjustNeighbor):
417 # TODO: check for possibility of unsplit (pane size becomes zero)
418
419 # make sure that minsizes are honored
420 newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
421
422 # sanity check
423 if newPos1 <= 0:
424 newPos2 += newPos1
425 newPos1 = 0
426
427 # send the events
428 evt = MultiSplitterEvent(
429 wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, self)
430 evt.SetSashIdx(idx)
431 evt.SetSashPosition(newPos1)
432 if not self._DoSendEvent(evt):
433 # the event handler vetoed the change
434 newPos1 = -1
435 else:
436 # or it might have changed the value
437 newPos1 = evt.GetSashPosition()
438
439 if adjustNeighbor and newPos1 != -1:
440 evt.SetSashIdx(idx+1)
441 evt.SetSashPosition(newPos2)
442 if not self._DoSendEvent(evt):
443 # the event handler vetoed the change
444 newPos2 = -1
445 else:
446 # or it might have changed the value
447 newPos2 = evt.GetSashPosition()
448 if newPos2 == -1:
449 newPos1 = -1
450
451 return (newPos1, newPos2)
452
453
454 def _AdjustSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
455 total = newPos1 + newPos2
456
457 # these are the windows on either side of the sash
458 win1 = self._windows[idx]
459 win2 = self._windows[idx+1]
460
461 # make adjustments for window min sizes
462 minSize = self._GetWindowMin(win1)
463 if minSize == -1 or self._minimumPaneSize > minSize:
464 minSize = self._minimumPaneSize
465 minSize += self._GetBorderSize()
466 if newPos1 < minSize:
467 newPos1 = minSize
468 newPos2 = total - newPos1
469
470 if adjustNeighbor:
471 minSize = self._GetWindowMin(win2)
472 if minSize == -1 or self._minimumPaneSize > minSize:
473 minSize = self._minimumPaneSize
474 minSize += self._GetBorderSize()
475 if newPos2 < minSize:
476 newPos2 = minSize
477 newPos1 = total - newPos2
478
479 return (newPos1, newPos2)
480
481
482 def _DoSetSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
483 newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
484 if newPos1 == self._sashes[idx]:
485 return False
486 self._sashes[idx] = newPos1
487 if adjustNeighbor:
488 self._sashes[idx+1] = newPos2
489 return True
490
491
492 def _SetSashPositionAndNotify(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
493 # TODO: what is the thing about _requestedSashPosition for?
494
495 self._DoSetSashPosition(idx, newPos1, newPos2, adjustNeighbor)
496
497 evt = MultiSplitterEvent(
498 wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, self)
499 evt.SetSashIdx(idx)
500 evt.SetSashPosition(newPos1)
501 self._DoSendEvent(evt)
502
503 if adjustNeighbor:
504 evt.SetSashIdx(idx+1)
505 evt.SetSashPosition(newPos2)
506 self._DoSendEvent(evt)
507
508
509 def _GetMotionDiff(self, x, y):
510 # find the diff from the old pos
511 if self._orient == wx.HORIZONTAL:
512 diff = x - self._oldX
513 else:
514 diff = y - self._oldY
515 return diff
516
517
518 def _SashToCoord(self, idx, sashPos):
519 coord = 0
520 for i in range(idx):
521 coord += self._sashes[i]
522 coord += self._GetSashSize()
523 coord += sashPos
524 return coord
525
526
527 def _GetWindowMin(self, window):
528 if self._orient == wx.HORIZONTAL:
529 return window.GetMinWidth()
530 else:
531 return window.GetMinHeight()
532
533
534 def _GetSashSize(self):
535 if self.HasFlag(wx.SP_NOSASH):
536 return 0
537 if wx.VERSION >= _RENDER_VER:
538 return wx.RendererNative.Get().GetSplitterParams(self).widthSash
539 else:
540 return 5
541
542
543 def _GetBorderSize(self):
544 if wx.VERSION >= _RENDER_VER:
545 return wx.RendererNative.Get().GetSplitterParams(self).border
546 else:
547 return 0
548
549
550 def _DrawSash(self, dc):
551 if wx.VERSION >= _RENDER_VER:
552 if self.HasFlag(wx.SP_3DBORDER):
553 wx.RendererNative.Get().DrawSplitterBorder(
554 self, dc, self.GetClientRect())
555
556 # if there are no splits then we're done.
557 if len(self._windows) < 2:
558 return
559
560 # if we are not supposed to use a sash then we're done.
561 if self.HasFlag(wx.SP_NOSASH):
562 return
563
564 # Reverse the sense of the orientation, in this case it refers
565 # to the direction to draw the sash not the direction that
566 # windows are stacked.
567 orient = { wx.HORIZONTAL : wx.VERTICAL,
568 wx.VERTICAL : wx.HORIZONTAL }[self._orient]
569
570 flag = 0
571 if self._isHot:
572 flag = wx.CONTROL_CURRENT
573
574 pos = 0
575 for sash in self._sashes[:-1]:
576 pos += sash
577 if wx.VERSION >= _RENDER_VER:
578 wx.RendererNative.Get().DrawSplitterSash(self, dc,
579 self.GetClientSize(),
580 pos, orient, flag)
581 else:
582 dc.SetPen(wx.TRANSPARENT_PEN)
583 dc.SetBrush(wx.Brush(self.GetBackgroundColour()))
584 sashsize = self._GetSashSize()
585 if orient == wx.VERTICAL:
586 x = pos
587 y = 0
588 w = sashsize
589 h = self.GetClientSize().height
590 else:
591 x = 0
592 y = pos
593 w = self.GetClientSize().width
594 h = sashsize
595 dc.DrawRectangle(x, y, w, h)
596
597 pos += self._GetSashSize()
598
599
600 def _DrawSashTracker(self, x, y):
601 # Draw a line to represent the dragging sash, for when not
602 # doing live updates
603 w, h = self.GetClientSize()
604 dc = wx.ScreenDC()
605
606 if self._orient == wx.HORIZONTAL:
607 x1 = x
608 y1 = 2
609 x2 = x
610 y2 = h-2
611 if x1 > w:
612 x1 = w
613 x2 = w
614 elif x1 < 0:
615 x1 = 0
616 x2 = 0
617 else:
618 x1 = 2
619 y1 = y
620 x2 = w-2
621 y2 = y
622 if y1 > h:
623 y1 = h
624 y2 = h
625 elif y1 < 0:
626 y1 = 0
627 y2 = 0
628
629 x1, y1 = self.ClientToScreenXY(x1, y1)
630 x2, y2 = self.ClientToScreenXY(x2, y2)
631
632 dc.SetLogicalFunction(wx.INVERT)
633 dc.SetPen(self._sashTrackerPen)
634 dc.SetBrush(wx.TRANSPARENT_BRUSH)
635 dc.DrawLine(x1, y1, x2, y2)
636 dc.SetLogicalFunction(wx.COPY)
637
638
639 def _SashHitTest(self, x, y, tolerance=5):
640 # if there are no splits then we're done.
641 if len(self._windows) < 2:
642 return -1
643
644 if self._orient == wx.HORIZONTAL:
645 z = x
646 else:
647 z = y
648
649 pos = 0
650 for idx, sash in enumerate(self._sashes[:-1]):
651 pos += sash
652 hitMin = pos - tolerance
653 hitMax = pos + self._GetSashSize() + tolerance
654
655 if z >= hitMin and z <= hitMax:
656 return idx
657
658 pos += self._GetSashSize()
659
660 return -1
661
662
663 def _SizeWindows(self):
664 # no windows yet?
665 if not self._windows:
666 return
667
668 # are there any pending size settings?
669 for window, spos in self._pending.items():
670 idx = self._windows.index(window)
671 # TODO: this may need adjusted to make sure they all fit
672 # in the current client size
673 self._sashes[idx] = spos
674 del self._pending[window]
675
676 # are there any that still have a -1?
677 for idx, spos in enumerate(self._sashes[:-1]):
678 if spos == -1:
679 # TODO: this should also be adjusted
680 self._sashes[idx] = 100
681
682 cw, ch = self.GetClientSize()
683 border = self._GetBorderSize()
684 sash = self._GetSashSize()
685
686 if len(self._windows) == 1:
687 # there's only one, it's an easy layout
688 self._windows[0].SetDimensions(border, border,
689 cw - 2*border, ch - 2*border)
690 else:
691 if 'wxMSW' in wx.PlatformInfo:
692 self.Freeze()
693 if self._orient == wx.HORIZONTAL:
694 x = y = border
695 h = ch - 2*border
696 for idx, spos in enumerate(self._sashes[:-1]):
697 self._windows[idx].SetDimensions(x, y, spos, h)
698 x += spos + sash
699 # last one takes the rest of the space. TODO make this configurable
700 last = cw - 2*border - x
701 self._windows[idx+1].SetDimensions(x, y, last, h)
702 if last > 0:
703 self._sashes[idx+1] = last
704 else:
705 x = y = border
706 w = cw - 2*border
707 for idx, spos in enumerate(self._sashes[:-1]):
708 self._windows[idx].SetDimensions(x, y, w, spos)
709 y += spos + sash
710 # last one takes the rest of the space. TODO make this configurable
711 last = ch - 2*border - y
712 self._windows[idx+1].SetDimensions(x, y, w, last)
713 if last > 0:
714 self._sashes[idx+1] = last
715 if 'wxMSW' in wx.PlatformInfo:
716 self.Thaw()
717
718 self._DrawSash(wx.ClientDC(self))
719 self._needUpdating = False
720
721
722 def _DoSendEvent(self, evt):
723 return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
724
725 #----------------------------------------------------------------------
726
727 class MultiSplitterEvent(wx.PyCommandEvent):
728 """
729 This event class is almost the same as `wx.SplitterEvent` except
730 it adds an accessor for the sash index that is being changed. The
731 same event type IDs and event binders are used as with
732 `wx.SplitterEvent`.
733 """
734 def __init__(self, type=wx.wxEVT_NULL, splitter=None):
735 wx.PyCommandEvent.__init__(self, type)
736 if splitter:
737 self.SetEventObject(splitter)
738 self.SetId(splitter.GetId())
739 self.sashIdx = -1
740 self.sashPos = -1
741 self.isAllowed = True
742
743 def SetSashIdx(self, idx):
744 self.sashIdx = idx
745
746 def SetSashPosition(self, pos):
747 self.sashPos = pos
748
749 def GetSashIdx(self):
750 return self.sashIdx
751
752 def GetSashPosition(self):
753 return self.sashPos
754
755 # methods from wx.NotifyEvent
756 def Veto(self):
757 self.isAllowed = False
758 def Allow(self):
759 self.isAllowed = True
760 def IsAllowed(self):
761 return self.isAllowed
762
763
764
765 #----------------------------------------------------------------------
766
767
768