home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / lib-stdwin / Buttons.py < prev    next >
Text File  |  2000-08-10  |  10KB  |  412 lines

  1. # Module 'Buttons'
  2.  
  3.  
  4. # Import module 'rect' renamed as '_rect' to avoid exporting it on
  5. # 'from Buttons import *'
  6. #
  7. import rect
  8. _rect = rect
  9. del rect
  10.  
  11.  
  12. # Field indices in mouse event detail
  13. #
  14. _HV = 0
  15. _CLICKS = 1
  16. _BUTTON = 2
  17. _MASK = 3
  18.  
  19.  
  20. # LabelAppearance provides defaults for all appearance methods.
  21. # selected state not visible
  22. # disabled --> crossed out
  23. # hilited  --> inverted
  24. #
  25. class LabelAppearance:
  26.     #
  27.     # Initialization
  28.     #
  29.     def init_appearance(self):
  30.         self.bounds = _rect.empty
  31.         self.enabled = 1
  32.         self.hilited = 0
  33.         self.selected = 0
  34.         self.text = ''
  35.     #
  36.     # Size enquiry
  37.     #
  38.     def getminsize(self, m, (width, height)):
  39.         width = max(width, m.textwidth(self.text) + 6)
  40.         height = max(height, m.lineheight() + 6)
  41.         return width, height
  42.     #
  43.     def getbounds(self):
  44.         return self.bounds
  45.     #
  46.     # Changing the parameters
  47.     #
  48.     def settext(self, text):
  49.         self.text = text
  50.         if self.bounds <> _rect.empty:
  51.             self.recalctextpos()
  52.             self.redraw()
  53.     #
  54.     def setbounds(self, bounds):
  55.         self.bounds = bounds
  56.         if self.bounds <> _rect.empty:
  57.             self.recalc()
  58.     #
  59.     def realize(self):
  60.         pass
  61.     #
  62.     # Changing the state bits
  63.     #
  64.     def enable(self, flag):
  65.         if flag <> self.enabled:
  66.             self.enabled = flag
  67.             if self.bounds <> _rect.empty:
  68.                 self.flipenable(self.parent.begindrawing())
  69.     #
  70.     def hilite(self, flag):
  71.         if flag <> self.hilited:
  72.             self.hilited = flag
  73.             if self.bounds <> _rect.empty:
  74.                 self.fliphilite(self.parent.begindrawing())
  75.     #
  76.     def select(self, flag):
  77.         if flag <> self.selected:
  78.             self.selected = flag
  79.             if self.bounds <> _rect.empty:
  80.                 self.redraw()
  81.     #
  82.     # Recalculate the box bounds and text position.
  83.     # This can be overridden by buttons that draw different boxes
  84.     # or want their text in a different position.
  85.     #
  86.     def recalc(self):
  87.         if self.bounds <> _rect.empty:
  88.             self.recalcbounds()
  89.             self.recalctextpos()
  90.     #
  91.     def recalcbounds(self):
  92.         self.hilitebounds = _rect.inset(self.bounds, (3, 3))
  93.         self.crossbounds = self.bounds
  94.     #
  95.     def recalctextpos(self):
  96.         (left, top), (right, bottom) = self.bounds
  97.         m = self.parent.beginmeasuring()
  98.         h = (left + right - m.textwidth(self.text)) / 2
  99.         v = (top + bottom - m.lineheight()) / 2
  100.         self.textpos = h, v
  101.     #
  102.     # Generic drawing interface.
  103.     # Do not override redraw() or draw() methods; override drawit() c.s.
  104.     #
  105.     def redraw(self):
  106.         if self.bounds <> _rect.empty:
  107.             d = self.parent.begindrawing()
  108.             d.erase(self.bounds)
  109.             self.draw(d, self.bounds)
  110.     #
  111.     def draw(self, d, area):
  112.         area = _rect.intersect([area, self.bounds])
  113.         if area == _rect.empty:
  114.             return
  115.         d.cliprect(area)
  116.         self.drawit(d)
  117.         d.noclip()
  118.     #
  119.     # The drawit() method is fairly generic but may be overridden.
  120.     #
  121.     def drawit(self, d):
  122.         self.drawpict(d)
  123.         if self.text:
  124.             d.text(self.textpos, self.text)
  125.         if not self.enabled:
  126.             self.flipenable(d)
  127.         if self.hilited:
  128.             self.fliphilite(d)
  129.     #
  130.     # Default drawing detail functions.
  131.     # Overriding these is normally sufficient to get different
  132.     # appearances.
  133.     #
  134.     def drawpict(self, d):
  135.         pass
  136.     #
  137.     def flipenable(self, d):
  138.         _xorcross(d, self.crossbounds)
  139.     #
  140.     def fliphilite(self, d):
  141.         d.invert(self.hilitebounds)
  142.  
  143.  
  144. # A Strut is a label with no width of its own.
  145.  
  146. class StrutAppearance(LabelAppearance):
  147.     #
  148.     def getminsize(self, m, (width, height)):
  149.         height = max(height, m.lineheight() + 6)
  150.         return width, height
  151.     #
  152.  
  153.  
  154. # ButtonAppearance displays a centered string in a box.
  155. # selected --> bold border
  156. # disabled --> crossed out
  157. # hilited  --> inverted
  158. #
  159. class ButtonAppearance(LabelAppearance):
  160.     #
  161.     def drawpict(self, d):
  162.         d.box(_rect.inset(self.bounds, (1, 1)))
  163.         if self.selected:
  164.             # Make a thicker box
  165.             d.box(self.bounds)
  166.             d.box(_rect.inset(self.bounds, (2, 2)))
  167.             d.box(_rect.inset(self.bounds, (3, 3)))
  168.     #
  169.  
  170.  
  171. # CheckAppearance displays a small square box and a left-justified string.
  172. # selected --> a cross appears in the box
  173. # disabled --> whole button crossed out
  174. # hilited  --> box is inverted
  175. #
  176. class CheckAppearance(LabelAppearance):
  177.     #
  178.     def getminsize(self, m, (width, height)):
  179.         minwidth = m.textwidth(self.text) + 6
  180.         minheight = m.lineheight() + 6
  181.         width = max(width, minwidth + minheight + m.textwidth(' '))
  182.         height = max(height, minheight)
  183.         return width, height
  184.     #
  185.     def drawpict(self, d):
  186.         d.box(self.boxbounds)
  187.         if self.selected: _xorcross(d, self.boxbounds)
  188.     #
  189.     def recalcbounds(self):
  190.         LabelAppearance.recalcbounds(self)
  191.         (left, top), (right, bottom) = self.bounds
  192.         self.size = bottom - top - 4
  193.         self.boxbounds = (left+2, top+2), (left+2+self.size, bottom-2)
  194.         self.hilitebounds = self.boxbounds
  195.     #
  196.     def recalctextpos(self):
  197.         m = self.parent.beginmeasuring()
  198.         (left, top), (right, bottom) = self.boxbounds
  199.         h = right + m.textwidth(' ')
  200.         v = top + (self.size - m.lineheight()) / 2
  201.         self.textpos = h, v
  202.     #
  203.  
  204.  
  205. # RadioAppearance displays a round indicator and a left-justified string.
  206. # selected --> a dot appears in the indicator
  207. # disabled --> whole button crossed out
  208. # hilited  --> indicator is inverted
  209. #
  210. class RadioAppearance(CheckAppearance):
  211.     #
  212.     def drawpict(self, d):
  213.         (left, top), (right, bottom) = self.boxbounds
  214.         radius = self.size / 2
  215.         center = left + radius, top + radius
  216.         d.circle(center, radius)
  217.         if self.selected:
  218.             d.fillcircle(center, radius*3/5)
  219.     #
  220.  
  221.  
  222. # NoReactivity ignores mouse events.
  223. #
  224. class NoReactivity:
  225.     def init_reactivity(self): pass
  226.  
  227.  
  228. # BaseReactivity defines hooks and asks for mouse events,
  229. # but provides only dummy mouse event handlers.
  230. # The trigger methods call the corresponding hooks set by the user.
  231. # Hooks (and triggers) mean the following:
  232. # down_hook    called on some mouse-down events
  233. # move_hook    called on some mouse-move events
  234. # up_hook    called on mouse-up events
  235. # on_hook    called for buttons with on/off state, when it goes on
  236. # hook        called when a button 'fires' or a radiobutton goes on
  237. # There are usually extra conditions, e.g., hooks are only called
  238. # when the button is enabled, or active, or selected (on).
  239. #
  240. class BaseReactivity:
  241.     #
  242.     def init_reactivity(self):
  243.         self.down_hook = self.move_hook = self.up_hook = \
  244.             self.on_hook = self.off_hook = \
  245.             self.hook = self.active = 0
  246.         self.parent.need_mouse(self)
  247.     #
  248.     def mousetest(self, hv):
  249.         return _rect.pointinrect(hv, self.bounds)
  250.     #
  251.     def mouse_down(self, detail):
  252.         pass
  253.     #
  254.     def mouse_move(self, detail):
  255.         pass
  256.     #
  257.     def mouse_up(self, detail):
  258.         pass
  259.     #
  260.     def down_trigger(self):
  261.         if self.down_hook: self.down_hook(self)
  262.     #
  263.     def move_trigger(self):
  264.         if self.move_hook: self.move_hook(self)
  265.     #
  266.     def up_trigger(self):
  267.         if self.up_hook: self.up_hook(self)
  268.     #
  269.     def on_trigger(self):
  270.         if self.on_hook: self.on_hook(self)
  271.     #
  272.     def off_trigger(self):
  273.         if self.off_hook: self.off_hook(self)
  274.     #
  275.     def trigger(self):
  276.         if self.hook: self.hook(self)
  277.  
  278.  
  279. # ToggleReactivity acts like a simple pushbutton.
  280. # It toggles its hilite state on mouse down events.
  281. #
  282. class ToggleReactivity(BaseReactivity):
  283.     #
  284.     def mouse_down(self, detail):
  285.         if self.enabled and self.mousetest(detail[_HV]):
  286.             self.active = 1
  287.             self.hilite(not self.hilited)
  288.             self.down_trigger()
  289.     #
  290.     def mouse_move(self, detail):
  291.         if self.active:
  292.             self.move_trigger()
  293.     #
  294.     def mouse_up(self, detail):
  295.         if self.active:
  296.             self.up_trigger()
  297.             self.active = 0
  298.     #
  299.     def down_trigger(self):
  300.         if self.hilited:
  301.             self.on_trigger()
  302.         else:
  303.             self.off_trigger()
  304.         self.trigger()
  305.     #
  306.  
  307.  
  308. # TriggerReactivity acts like a fancy pushbutton.
  309. # It hilites itself while the mouse is down within its bounds.
  310. #
  311. class TriggerReactivity(BaseReactivity):
  312.     #
  313.     def mouse_down(self, detail):
  314.         if self.enabled and self.mousetest(detail[_HV]):
  315.             self.active = 1
  316.             self.hilite(1)
  317.             self.down_trigger()
  318.     #
  319.     def mouse_move(self, detail):
  320.         if self.active:
  321.             self.hilite(self.mousetest(detail[_HV]))
  322.             if self.hilited:
  323.                 self.move_trigger()
  324.     #
  325.     def mouse_up(self, detail):
  326.         if self.active:
  327.             self.hilite(self.mousetest(detail[_HV]))
  328.             if self.hilited:
  329.                 self.up_trigger()
  330.                 self.trigger()
  331.             self.active = 0
  332.             self.hilite(0)
  333.     #
  334.  
  335.  
  336. # CheckReactivity handles mouse events like TriggerReactivity,
  337. # It overrides the up_trigger method to flip its selected state.
  338. #
  339. class CheckReactivity(TriggerReactivity):
  340.     #
  341.     def up_trigger(self):
  342.         self.select(not self.selected)
  343.         if self.selected:
  344.             self.on_trigger()
  345.         else:
  346.             self.off_trigger()
  347.         self.trigger()
  348.  
  349.  
  350. # RadioReactivity turns itself on and the other buttons in its group
  351. # off when its up_trigger method is called.
  352. #
  353. class RadioReactivity(TriggerReactivity):
  354.     #
  355.     def init_reactivity(self):
  356.         TriggerReactivity.init_reactivity(self)
  357.         self.group = []
  358.     #
  359.     def up_trigger(self):
  360.         for b in self.group:
  361.             if b <> self:
  362.                 if b.selected:
  363.                     b.select(0)
  364.                     b.off_trigger()
  365.         self.select(1)
  366.         self.on_trigger()
  367.         self.trigger()
  368.  
  369.  
  370. # Auxiliary class for 'define' method.
  371. # Call the initializers in the right order.
  372. #
  373. class Define:
  374.     #
  375.     def define(self, parent):
  376.         self.parent = parent
  377.         parent.addchild(self)
  378.         self.init_appearance()
  379.         self.init_reactivity()
  380.         return self
  381.     #
  382.     def destroy(self):
  383.         self.parent = 0
  384.     #
  385.     def definetext(self, parent, text):
  386.         self = self.define(parent)
  387.         self.settext(text)
  388.         return self
  389.  
  390.  
  391. # Subroutine to cross out a rectangle.
  392. #
  393. def _xorcross(d, bounds):
  394.     ((left, top), (right, bottom)) = bounds
  395.     # This is s bit funny to make it look better
  396.     left = left + 2
  397.     right = right - 2
  398.     top = top + 2
  399.     bottom = bottom - 3
  400.     d.xorline(((left, top), (right, bottom)))
  401.     d.xorline((left, bottom), (right, top))
  402.  
  403.  
  404. # Ready-made button classes.
  405. #
  406. class Label(NoReactivity, LabelAppearance, Define): pass
  407. class Strut(NoReactivity, StrutAppearance, Define): pass
  408. class PushButton(TriggerReactivity, ButtonAppearance, Define): pass
  409. class CheckButton(CheckReactivity, CheckAppearance, Define): pass
  410. class RadioButton(RadioReactivity, RadioAppearance, Define): pass
  411. class ToggleButton(ToggleReactivity, ButtonAppearance, Define): pass
  412.