]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/buttons.py
Oops, DoLog and DoLogString are protected
[wxWidgets.git] / wxPython / wx / lib / buttons.py
1 #----------------------------------------------------------------------
2 # Name:        wx.lib.buttons
3 # Purpose:     Various kinds of generic buttons, (not native controls but
4 #              self-drawn.)
5 #
6 # Author:      Robin Dunn
7 #
8 # Created:     9-Dec-1999
9 # RCS-ID:      $Id$
10 # Copyright:   (c) 1999 by Total Control Software
11 # Licence:     wxWindows license
12 #----------------------------------------------------------------------
13 # 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net)
14 #
15 # o Updated for wx namespace
16 # o Tested with updated demo
17
18
19 """
20 This module implements various forms of generic buttons, meaning that
21 they are not built on native controls but are self-drawn.  They act
22 like normal buttons but you are able to better control how they look,
23 bevel width, colours, etc.
24 """
25
26 import wx
27 import imageutils
28
29
30 #----------------------------------------------------------------------
31
32 class GenButtonEvent(wx.PyCommandEvent):
33     """Event sent from the generic buttons when the button is activated. """
34     def __init__(self, eventType, ID):
35         wx.PyCommandEvent.__init__(self, eventType, ID)
36         self.isDown = False
37         self.theButton = None
38
39     def SetIsDown(self, isDown):
40         self.isDown = isDown
41
42     def GetIsDown(self):
43         return self.isDown
44
45     def SetButtonObj(self, btn):
46         self.theButton = btn
47
48     def GetButtonObj(self):
49         return self.theButton
50
51
52 #----------------------------------------------------------------------
53
54 class GenButton(wx.PyControl):
55     """A generic button, and base class for the other generic buttons."""
56
57     labelDelta = 1
58
59     def __init__(self, parent, ID, label,
60                  pos = wx.DefaultPosition, size = wx.DefaultSize,
61                  style = 0, validator = wx.DefaultValidator,
62                  name = "genbutton"):
63         cstyle = style
64         if cstyle == 0:
65             cstyle = wx.BORDER_NONE
66         wx.PyControl.__init__(self, parent, ID, pos, size, cstyle, validator, name)
67
68         self.up = True
69         self.hasFocus = False
70         self.style = style
71         if style & wx.BORDER_NONE:
72             self.bezelWidth = 0
73             self.useFocusInd = False
74         else:
75             self.bezelWidth = 2
76             self.useFocusInd = True
77
78         self.SetLabel(label)
79         self.InheritAttributes()
80         self.SetBestFittingSize(size)
81         self.InitColours()
82
83         self.Bind(wx.EVT_LEFT_DOWN,        self.OnLeftDown)
84         self.Bind(wx.EVT_LEFT_UP,          self.OnLeftUp)
85         if wx.Platform == '__WXMSW__':
86             self.Bind(wx.EVT_LEFT_DCLICK,  self.OnLeftDown)
87         self.Bind(wx.EVT_MOTION,           self.OnMotion)
88         self.Bind(wx.EVT_SET_FOCUS,        self.OnGainFocus)
89         self.Bind(wx.EVT_KILL_FOCUS,       self.OnLoseFocus)
90         self.Bind(wx.EVT_KEY_DOWN,         self.OnKeyDown)
91         self.Bind(wx.EVT_KEY_UP,           self.OnKeyUp)
92         self.Bind(wx.EVT_PAINT,            self.OnPaint)
93
94
95     def SetBestSize(self, size=None):
96         """
97         Given the current font and bezel width settings, calculate
98         and set a good size.
99         """
100         if size is None:
101             size = wx.DefaultSize            
102         wx.PyControl.SetBestFittingSize(self, size)
103
104
105     def DoGetBestSize(self):
106         """
107         Overridden base class virtual.  Determines the best size of the
108         button based on the label and bezel size.
109         """
110         w, h, useMin = self._GetLabelSize()
111         defSize = wx.Button.GetDefaultSize()
112         width = 12 + w
113         if useMin and width < defSize.width:
114            width = defSize.width
115         height = 11 + h
116         if useMin and height < defSize.height:
117             height = defSize.height
118         width = width + self.bezelWidth - 1
119         height = height + self.bezelWidth - 1
120         return (width, height)
121
122
123     def AcceptsFocus(self):
124         """Overridden base class virtual."""
125         return self.IsShown() and self.IsEnabled()
126
127
128     def GetDefaultAttributes(self):
129         """
130         Overridden base class virtual.  By default we should use
131         the same font/colour attributes as the native Button.
132         """
133         return wx.Button.GetClassDefaultAttributes()
134
135
136     def ShouldInheritColours(self):
137         """
138         Overridden base class virtual.  Buttons usually don't inherit
139         the parent's colours.
140         """
141         return False
142     
143
144     def Enable(self, enable=True):
145         wx.PyControl.Enable(self, enable)
146         self.Refresh()
147
148
149     def SetBezelWidth(self, width):
150         """Set the width of the 3D effect"""
151         self.bezelWidth = width
152
153     def GetBezelWidth(self):
154         """Return the width of the 3D effect"""
155         return self.bezelWidth
156
157     def SetUseFocusIndicator(self, flag):
158         """Specifiy if a focus indicator (dotted line) should be used"""
159         self.useFocusInd = flag
160
161     def GetUseFocusIndicator(self):
162         """Return focus indicator flag"""
163         return self.useFocusInd
164
165
166     def InitColours(self):
167         """
168         Calculate a new set of highlight and shadow colours based on
169         the background colour.  Works okay if the colour is dark...
170         """
171         faceClr = self.GetBackgroundColour()
172         r, g, b = faceClr.Get()
173         fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
174         self.faceDnClr = wx.Colour(fr, fg, fb)
175         sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
176         self.shadowPen = wx.Pen(wx.Colour(sr,sg,sb), 1, wx.SOLID)
177         hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
178         self.highlightPen = wx.Pen(wx.Colour(hr,hg,hb), 1, wx.SOLID)
179         self.focusClr = wx.Colour(hr, hg, hb)
180
181         textClr = self.GetForegroundColour()
182         if wx.Platform == "__WXMAC__":
183             self.focusIndPen = wx.Pen(textClr, 1, wx.SOLID)
184         else:
185             self.focusIndPen  = wx.Pen(textClr, 1, wx.USER_DASH)
186             self.focusIndPen.SetDashes([1,1])
187             self.focusIndPen.SetCap(wx.CAP_BUTT)
188         
189         
190     def SetBackgroundColour(self, colour):
191         wx.PyControl.SetBackgroundColour(self, colour)
192         self.InitColours()
193
194
195     def SetForegroundColour(self, colour):
196         wx.PyControl.SetForegroundColour(self, colour)
197         self.InitColours()
198
199     def SetDefault(self):
200         self.GetParent().SetDefaultItem(self)
201         
202     def _GetLabelSize(self):
203         """ used internally """
204         w, h = self.GetTextExtent(self.GetLabel())
205         return w, h, True
206
207
208     def Notify(self):
209         evt = GenButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
210         evt.SetIsDown(not self.up)
211         evt.SetButtonObj(self)
212         evt.SetEventObject(self)
213         self.GetEventHandler().ProcessEvent(evt)
214
215
216     def DrawBezel(self, dc, x1, y1, x2, y2):
217         # draw the upper left sides
218         if self.up:
219             dc.SetPen(self.highlightPen)
220         else:
221             dc.SetPen(self.shadowPen)
222         for i in range(self.bezelWidth):
223             dc.DrawLine(x1+i, y1, x1+i, y2-i)
224             dc.DrawLine(x1, y1+i, x2-i, y1+i)
225
226         # draw the lower right sides
227         if self.up:
228             dc.SetPen(self.shadowPen)
229         else:
230             dc.SetPen(self.highlightPen)
231         for i in range(self.bezelWidth):
232             dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
233             dc.DrawLine(x2-i, y1+i, x2-i, y2)
234
235
236     def DrawLabel(self, dc, width, height, dw=0, dy=0):
237         dc.SetFont(self.GetFont())
238         if self.IsEnabled():
239             dc.SetTextForeground(self.GetForegroundColour())
240         else:
241             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
242         label = self.GetLabel()
243         tw, th = dc.GetTextExtent(label)
244         if not self.up:
245             dw = dy = self.labelDelta
246         dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy)
247
248
249     def DrawFocusIndicator(self, dc, w, h):
250         bw = self.bezelWidth
251         self.focusIndPen.SetColour(self.focusClr)
252         dc.SetLogicalFunction(wx.INVERT)
253         dc.SetPen(self.focusIndPen)
254         dc.SetBrush(wx.TRANSPARENT_BRUSH)
255         dc.DrawRectangle(bw+2,bw+2,  w-bw*2-4, h-bw*2-4)
256         dc.SetLogicalFunction(wx.COPY)
257
258
259     def OnPaint(self, event):
260         (width, height) = self.GetClientSizeTuple()
261         x1 = y1 = 0
262         x2 = width-1
263         y2 = height-1
264         
265         dc = wx.BufferedPaintDC(self)
266         brush = None
267         
268         if self.up:
269             colBg = self.GetBackgroundColour()
270             brush = wx.Brush(colBg, wx.SOLID)
271             if self.style & wx.BORDER_NONE:
272                 myAttr = self.GetDefaultAttributes()
273                 parAttr = self.GetParent().GetDefaultAttributes()
274                 myDef = colBg == myAttr.colBg
275                 parDef = self.GetParent().GetBackgroundColour() == parAttr.colBg
276                 if myDef and parDef:
277                     if wx.Platform == "__WXMAC__":
278                         brush.MacSetTheme(1) # 1 == kThemeBrushDialogBackgroundActive
279                     elif wx.Platform == "__WXMSW__":
280                         if self.DoEraseBackground(dc):
281                             brush = None
282                 elif myDef and not parDef:
283                     colBg = self.GetParent().GetBackgroundColour()
284                     brush = wx.Brush(colBg, wx.SOLID)
285         else:
286             brush = wx.Brush(self.faceDnClr, wx.SOLID)
287         if brush is not None:
288             dc.SetBackground(brush)
289             dc.Clear()
290                     
291         self.DrawBezel(dc, x1, y1, x2, y2)
292         self.DrawLabel(dc, width, height)
293         if self.hasFocus and self.useFocusInd:
294             self.DrawFocusIndicator(dc, width, height)
295
296
297     def OnLeftDown(self, event):
298         if not self.IsEnabled():
299             return
300         self.up = False
301         self.CaptureMouse()
302         self.SetFocus()
303         self.Refresh()
304         event.Skip()
305
306
307     def OnLeftUp(self, event):
308         if not self.IsEnabled() or not self.HasCapture():
309             return
310         if self.HasCapture():
311             self.ReleaseMouse()
312             if not self.up:    # if the button was down when the mouse was released...
313                 self.Notify()
314             self.up = True
315             self.Refresh()
316             event.Skip()
317
318
319     def OnMotion(self, event):
320         if not self.IsEnabled() or not self.HasCapture():
321             return
322         if event.LeftIsDown() and self.HasCapture():
323             x,y = event.GetPositionTuple()
324             w,h = self.GetClientSizeTuple()
325             if self.up and x<w and x>=0 and y<h and y>=0:
326                 self.up = False
327                 self.Refresh()
328                 return
329             if not self.up and (x<0 or y<0 or x>=w or y>=h):
330                 self.up = True
331                 self.Refresh()
332                 return
333         event.Skip()
334
335
336     def OnGainFocus(self, event):
337         self.hasFocus = True
338         dc = wx.ClientDC(self)
339         w, h = self.GetClientSizeTuple()
340         if self.useFocusInd:
341             self.DrawFocusIndicator(dc, w, h)
342
343
344     def OnLoseFocus(self, event):
345         self.hasFocus = False
346         dc = wx.ClientDC(self)
347         w, h = self.GetClientSizeTuple()
348         if self.useFocusInd:
349             self.DrawFocusIndicator(dc, w, h)
350
351
352     def OnKeyDown(self, event):
353         if self.hasFocus and event.KeyCode() == ord(" "):
354             self.up = False
355             self.Refresh()
356         event.Skip()
357
358
359     def OnKeyUp(self, event):
360         if self.hasFocus and event.KeyCode() == ord(" "):
361             self.up = True
362             self.Notify()
363             self.Refresh()
364         event.Skip()
365
366
367 #----------------------------------------------------------------------
368
369 class GenBitmapButton(GenButton):
370     """A generic bitmap button."""
371
372     def __init__(self, parent, ID, bitmap,
373                  pos = wx.DefaultPosition, size = wx.DefaultSize,
374                  style = 0, validator = wx.DefaultValidator,
375                  name = "genbutton"):
376         self.bmpDisabled = None
377         self.bmpFocus = None
378         self.bmpSelected = None
379         self.SetBitmapLabel(bitmap)
380         GenButton.__init__(self, parent, ID, "", pos, size, style, validator, name)
381
382
383     def GetBitmapLabel(self):
384         return self.bmpLabel
385     def GetBitmapDisabled(self):
386         return self.bmpDisabled
387     def GetBitmapFocus(self):
388         return self.bmpFocus
389     def GetBitmapSelected(self):
390         return self.bmpSelected
391
392
393     def SetBitmapDisabled(self, bitmap):
394         """Set bitmap to display when the button is disabled"""
395         self.bmpDisabled = bitmap
396
397     def SetBitmapFocus(self, bitmap):
398         """Set bitmap to display when the button has the focus"""
399         self.bmpFocus = bitmap
400         self.SetUseFocusIndicator(False)
401
402     def SetBitmapSelected(self, bitmap):
403         """Set bitmap to display when the button is selected (pressed down)"""
404         self.bmpSelected = bitmap
405
406     def SetBitmapLabel(self, bitmap, createOthers=True):
407         """
408         Set the bitmap to display normally.
409         This is the only one that is required. If
410         createOthers is True, then the other bitmaps
411         will be generated on the fly.  Currently,
412         only the disabled bitmap is generated.
413         """
414         self.bmpLabel = bitmap
415         if bitmap is not None and createOthers:
416             image = wx.ImageFromBitmap(bitmap)
417             imageutils.grayOut(image)
418             self.SetBitmapDisabled(wx.BitmapFromImage(image))
419
420
421     def _GetLabelSize(self):
422         """ used internally """
423         if not self.bmpLabel:
424             return -1, -1, False
425         return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False
426
427     def DrawLabel(self, dc, width, height, dw=0, dy=0):
428         bmp = self.bmpLabel
429         if self.bmpDisabled and not self.IsEnabled():
430             bmp = self.bmpDisabled
431         if self.bmpFocus and self.hasFocus:
432             bmp = self.bmpFocus
433         if self.bmpSelected and not self.up:
434             bmp = self.bmpSelected
435         bw,bh = bmp.GetWidth(), bmp.GetHeight()
436         if not self.up:
437             dw = dy = self.labelDelta
438         hasMask = bmp.GetMask() != None
439         dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask)
440
441
442 #----------------------------------------------------------------------
443
444
445 class GenBitmapTextButton(GenBitmapButton):
446     """A generic bitmapped button with text label"""
447     def __init__(self, parent, ID, bitmap, label,
448                  pos = wx.DefaultPosition, size = wx.DefaultSize,
449                  style = 0, validator = wx.DefaultValidator,
450                  name = "genbutton"):
451         GenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name)
452         self.SetLabel(label)
453
454
455     def _GetLabelSize(self):
456         """ used internally """
457         w, h = self.GetTextExtent(self.GetLabel())
458         if not self.bmpLabel:
459             return w, h, True       # if there isn't a bitmap use the size of the text
460
461         w_bmp = self.bmpLabel.GetWidth()+2
462         h_bmp = self.bmpLabel.GetHeight()+2
463         width = w + w_bmp
464         if h_bmp > h:
465             height = h_bmp
466         else:
467             height = h
468         return width, height, True
469
470
471     def DrawLabel(self, dc, width, height, dw=0, dy=0):
472         bmp = self.bmpLabel
473         if bmp != None:     # if the bitmap is used
474             if self.bmpDisabled and not self.IsEnabled():
475                 bmp = self.bmpDisabled
476             if self.bmpFocus and self.hasFocus:
477                 bmp = self.bmpFocus
478             if self.bmpSelected and not self.up:
479                 bmp = self.bmpSelected
480             bw,bh = bmp.GetWidth(), bmp.GetHeight()
481             if not self.up:
482                 dw = dy = self.labelDelta
483             hasMask = bmp.GetMask() != None
484         else:
485             bw = bh = 0     # no bitmap -> size is zero
486
487         dc.SetFont(self.GetFont())
488         if self.IsEnabled():
489             dc.SetTextForeground(self.GetForegroundColour())
490         else:
491             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
492
493         label = self.GetLabel()
494         tw, th = dc.GetTextExtent(label)        # size of text
495         if not self.up:
496             dw = dy = self.labelDelta
497
498         pos_x = (width-bw-tw)/2+dw      # adjust for bitmap and text to centre
499         if bmp !=None:
500             dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
501             pos_x = pos_x + 2   # extra spacing from bitmap
502
503         dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy)      # draw the text
504
505
506 #----------------------------------------------------------------------
507
508
509 class __ToggleMixin:
510     def SetToggle(self, flag):
511         self.up = not flag
512         self.Refresh()
513     SetValue = SetToggle
514
515     def GetToggle(self):
516         return not self.up
517     GetValue = GetToggle
518
519     def OnLeftDown(self, event):
520         if not self.IsEnabled():
521             return
522         self.saveUp = self.up
523         self.up = not self.up
524         self.CaptureMouse()
525         self.SetFocus()
526         self.Refresh()
527
528     def OnLeftUp(self, event):
529         if not self.IsEnabled() or not self.HasCapture():
530             return
531         if self.HasCapture():
532             self.ReleaseMouse()
533             self.Refresh()
534             if self.up != self.saveUp:
535                 self.Notify()
536
537     def OnKeyDown(self, event):
538         event.Skip()
539
540     def OnMotion(self, event):
541         if not self.IsEnabled():
542             return
543         if event.LeftIsDown() and self.HasCapture():
544             x,y = event.GetPositionTuple()
545             w,h = self.GetClientSizeTuple()
546             if x<w and x>=0 and y<h and y>=0:
547                 self.up = not self.saveUp
548                 self.Refresh()
549                 return
550             if (x<0 or y<0 or x>=w or y>=h):
551                 self.up = self.saveUp
552                 self.Refresh()
553                 return
554         event.Skip()
555
556     def OnKeyUp(self, event):
557         if self.hasFocus and event.KeyCode() == ord(" "):
558             self.up = not self.up
559             self.Notify()
560             self.Refresh()
561         event.Skip()
562
563
564
565
566 class GenToggleButton(__ToggleMixin, GenButton):
567     """A generic toggle button"""
568     pass
569
570 class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
571     """A generic toggle bitmap button"""
572     pass
573
574 class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
575     """A generic toggle bitmap button with text label"""
576     pass
577
578 #----------------------------------------------------------------------
579
580