1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        wxCustomButton based on wxCustomToggleCtrl.cpp
3 // Purpose:     a toggle button
4 // Author:      Bruce Phillips
5 // Modified by: John Labenski
6 // Created:     11/05/2002
7 // RCS-ID:
8 // Copyright:   (c) Bruce Phillips, John Labenki
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 #include "precomp.h"
13 
14 // For compilers that support precompilation, includes "wx/wx.h".
15 #include <wx/wxprec.h>
16 
17 #ifdef __BORLANDC__
18     #pragma hdrstop
19 #endif
20 
21 #ifndef WX_PRECOMP
22     #include <wx/control.h>
23     #include <wx/settings.h>
24     #include <wx/bitmap.h>
25     #include <wx/timer.h>
26     #include <wx/dc.h>
27     #include <wx/dcclient.h>
28 #endif // WX_PRECOMP
29 
30 #include <wx/tglbtn.h>
31 #include <wx/image.h>
32 #include <wx/renderer.h>
33 #include "wx/things/toggle.h"
34 
35 // ==========================================================================
36 // wxCustomButton
37 // ==========================================================================
IMPLEMENT_DYNAMIC_CLASS(wxCustomButton,wxControl)38 IMPLEMENT_DYNAMIC_CLASS( wxCustomButton, wxControl )
39 
40 BEGIN_EVENT_TABLE(wxCustomButton,wxControl)
41     EVT_MOUSE_EVENTS ( wxCustomButton::OnMouseEvents )
42     EVT_PAINT        ( wxCustomButton::OnPaint )
43     EVT_TIMER        ( wxID_ANY, wxCustomButton::OnTimer)
44     EVT_SIZE         ( wxCustomButton::OnSize )
45 END_EVENT_TABLE()
46 
47 wxCustomButton::~wxCustomButton()
48 {
49     if (HasCapture()) ReleaseMouse();
50     if (m_timer) delete m_timer;
51 }
52 
Init()53 void wxCustomButton::Init()
54 {
55     m_focused      = false;
56     m_labelMargin  = wxSize(4,4);
57     m_bitmapMargin = wxSize(2,2);
58     m_down         = 0;
59     m_timer        = NULL;
60     m_eventType    = 0;
61     m_button_style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM;
62 }
63 
Create(wxWindow * parent,wxWindowID id,const wxString & label,const wxBitmap & bitmap,const wxPoint & pos,const wxSize & size_,long style,const wxValidator & val,const wxString & name)64 bool wxCustomButton::Create(wxWindow* parent, wxWindowID id,
65                             const wxString& label, const wxBitmap &bitmap,
66                             const wxPoint& pos, const wxSize& size_,
67                             long style, const wxValidator& val,
68                             const wxString& name)
69 {
70     m_labelString = label;
71     if (bitmap.Ok()) m_bmpLabel = bitmap;
72     wxSize bestSize = DoGetBestSize_(parent);
73     wxSize size(size_.x<0 ? bestSize.x:size_.x, size_.y<0 ? bestSize.y:size_.y);
74 
75     //SetInitialSize(size);
76 
77     if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name))
78         return false;
79 
80     wxControl::SetBackgroundColour(parent->GetBackgroundColour());
81     wxControl::SetForegroundColour(parent->GetForegroundColour());
82     wxControl::SetFont(parent->GetFont());
83 
84     if (!SetButtonStyle(style)) return false;
85 
86     //SetBestSize(size);
87 
88     CalcLayout(true);
89     return true;
90 }
91 
SetValue(bool depressed)92 void wxCustomButton::SetValue(bool depressed)
93 {
94     wxCHECK_RET(!(m_button_style & wxCUSTBUT_NOTOGGLE), wxT("can't set button state"));
95     m_down = depressed ? 1 : 0;
96     Refresh(false);
97 }
98 
SetButtonStyle(long style)99 bool wxCustomButton::SetButtonStyle(long style)
100 {
101     int n_styles = 0;
102     if ((style & wxCUSTBUT_LEFT) != 0)   n_styles++;
103     if ((style & wxCUSTBUT_RIGHT) != 0)  n_styles++;
104     if ((style & wxCUSTBUT_TOP) != 0)    n_styles++;
105     if ((style & wxCUSTBUT_BOTTOM) != 0) n_styles++;
106     wxCHECK_MSG(n_styles < 2, false, wxT("Only one wxCustomButton label position allowed"));
107 
108     n_styles = 0;
109     if ((style & wxCUSTBUT_NOTOGGLE) != 0)       n_styles++;
110     if ((style & wxCUSTBUT_BUTTON) != 0)         n_styles++;
111     if ((style & wxCUSTBUT_TOGGLE) != 0)         n_styles++;
112     if ((style & wxCUSTBUT_BUT_DCLICK_TOG) != 0) n_styles++;
113     if ((style & wxCUSTBUT_TOG_DCLICK_BUT) != 0) n_styles++;
114     wxCHECK_MSG(n_styles < 2, false, wxT("Only one wxCustomButton style allowed"));
115 
116     m_button_style = style;
117 
118     if ((m_button_style & wxCUSTBUT_BUTTON) != 0)
119         m_down = 0;
120 
121     CalcLayout(true);
122     return true;
123 }
124 
SetLabel(const wxString & label)125 void wxCustomButton::SetLabel( const wxString &label )
126 {
127     m_labelString = label;
128     InvalidateBestSize();
129     CalcLayout(true);
130 }
131 
132 // sequence of events in GTK is up, dclick, up.
133 
OnMouseEvents(wxMouseEvent & event)134 void wxCustomButton::OnMouseEvents(wxMouseEvent& event)
135 {
136     if (m_button_style & wxCUSTBUT_NOTOGGLE) return;
137 
138     if (event.LeftDown() || event.RightDown())
139     {
140         if (!HasCapture())
141             CaptureMouse(); // keep depressed until up
142 
143         m_down++;
144         Redraw();
145     }
146     else if (event.LeftDClick() || event.RightDClick())
147     {
148         m_down++; // GTK eats second down event
149         Redraw();
150     }
151     else if (event.LeftUp())
152     {
153         if (HasCapture())
154             ReleaseMouse();
155 
156         m_eventType = wxEVT_LEFT_UP;
157 
158         if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition()))
159         {
160             if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
161             {
162                 m_down = 0;
163                 Redraw();
164                 SendEvent();
165                 return;
166             }
167             else
168             {
169                 if (!m_timer)
170                 {
171                     m_timer = new wxTimer(this, m_down+1);
172                     m_timer->Start(200, true);
173                 }
174                 else
175                 {
176                     m_eventType = wxEVT_LEFT_DCLICK;
177                 }
178 
179                 if ((m_button_style & wxCUSTBUT_TOGGLE) &&
180                     (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)) m_down++;
181             }
182         }
183 
184         Redraw();
185     }
186     else if (event.RightUp())
187     {
188         if (HasCapture())
189             ReleaseMouse();
190 
191         m_eventType = wxEVT_RIGHT_UP;
192 
193         if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition()))
194         {
195             if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
196             {
197                 m_down = 0;
198                 Redraw();
199                 SendEvent();
200                 return;
201             }
202             else
203             {
204                 m_down++;
205 
206                 if (!m_timer)
207                 {
208                     m_timer = new wxTimer(this, m_down);
209                     m_timer->Start(250, true);
210                 }
211                 else
212                 {
213                     m_eventType = wxEVT_RIGHT_DCLICK;
214                 }
215             }
216         }
217 
218         Redraw();
219     }
220     else if (event.Entering())
221     {
222         m_focused = true;
223         if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
224             m_down++;
225 
226         Redraw();
227     }
228     else if (event.Leaving())
229     {
230         m_focused = false;
231         if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
232             m_down--;
233 
234         Redraw();
235     }
236 }
237 
OnTimer(wxTimerEvent & event)238 void wxCustomButton::OnTimer( wxTimerEvent &event )
239 {
240     m_timer->Stop();
241     delete m_timer;
242     m_timer = NULL;
243 
244     // Clean up the button presses
245     // FIXME - GTK eats second left down for a DClick, who know about the others?
246     if (m_button_style & wxCUSTBUT_BUTTON)
247     {
248         m_down = 0;
249     }
250     else if (m_button_style & wxCUSTBUT_TOGGLE)
251     {
252         if (m_eventType == wxEVT_LEFT_UP)
253             m_down = event.GetId()%2 ? 0 : 1;
254         else
255             m_down = event.GetId()%2 ? 1 : 0;
256     }
257     else if (m_button_style & wxCUSTBUT_BUT_DCLICK_TOG)
258     {
259         if (m_eventType == wxEVT_LEFT_DCLICK)
260             m_down = event.GetId()%2 ? 0 : 1;
261         else
262             m_down = event.GetId()%2 ? 1 : 0;
263     }
264     else if (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)
265     {
266         if (m_eventType == wxEVT_LEFT_UP)
267             m_down = event.GetId()%2 ? 0 : 1;
268         else
269             m_down = event.GetId()%2 ? 1 : 0;
270     }
271 
272     Refresh(false);
273     SendEvent();
274 }
275 
SendEvent()276 void wxCustomButton::SendEvent()
277 {
278     if (((m_button_style & wxCUSTBUT_TOGGLE) && (m_eventType == wxEVT_LEFT_UP)) ||
279         ((m_button_style & wxCUSTBUT_BUT_DCLICK_TOG) && (m_eventType == wxEVT_LEFT_DCLICK)) ||
280         ((m_button_style & wxCUSTBUT_TOG_DCLICK_BUT) && (m_eventType == wxEVT_LEFT_UP)))
281     {
282         wxCommandEvent eventOut(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, GetId());
283         eventOut.SetInt(m_down%2 ? 1 : 0);
284         eventOut.SetExtraLong(m_eventType);
285         eventOut.SetEventObject(this);
286         GetEventHandler()->ProcessEvent(eventOut);
287     }
288     else
289     {
290         wxCommandEvent eventOut(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
291         eventOut.SetInt(0);
292         eventOut.SetExtraLong(m_eventType);
293         eventOut.SetEventObject(this);
294         GetEventHandler()->ProcessEvent(eventOut);
295     }
296 }
297 
CreateBitmapDisabled(const wxBitmap & bitmap) const298 wxBitmap wxCustomButton::CreateBitmapDisabled(const wxBitmap &bitmap) const
299 {
300     wxCHECK_MSG(bitmap.Ok(), wxNullBitmap, wxT("invalid bitmap"));
301 
302     unsigned char br = GetBackgroundColour().Red();
303     unsigned char bg = GetBackgroundColour().Green();
304     unsigned char bb = GetBackgroundColour().Blue();
305 
306     wxImage image = bitmap.ConvertToImage();
307     int pos, width = image.GetWidth(), height = image.GetHeight();
308     unsigned char *img_data = image.GetData();
309 
310     for (int j=0; j<height; j++)
311     {
312         for (int i=j%2; i<width; i+=2)
313         {
314             pos = (j*width+i)*3;
315             img_data[pos  ] = br;
316             img_data[pos+1] = bg;
317             img_data[pos+2] = bb;
318         }
319     }
320 
321     return wxBitmap(image);
322 
323 /*      // FIXME why bother creating focused wxCustomButton's bitmap
324         wxImage imgFoc = bitmap.ConvertToImage();
325 
326         bool mask = false;
327         unsigned char mr=0, mg=0, mb=0;
328         if (img.HasMask())
329         {
330             mask = true;
331             mr = imgDis.GetMaskRed();
332             mg = imgDis.GetMaskGreen();
333             mb = imgDis.GetMaskBlue();
334         }
335         unsigned char *r, *g, *b;
336         unsigned char *focData = imgFoc.GetData();
337         r = imgFoc.GetData();
338         g = imgFoc.GetData() + 1;
339         b = imgFoc.GetData() + 2;
340         for (int j=0; j<h; j++)
341         {
342             for (int i=0; i<w; i++)
343             {
344                 if ((!mask || ((*r!=mr)&&(*b!=mb)&&(*g!=mg))) &&
345                     ((*r<236)&&(*b<236)&&(*g<236)))
346                 {
347                     *r += 20; *g += 20; *b += 20;
348                 }
349                 r += 3; g += 3; b += 3;
350             }
351         }
352         m_bmpFocus = wxBitmap(imgFoc);
353 */
354 
355 }
356 
SetBitmapLabel(const wxBitmap & bitmap)357 void wxCustomButton::SetBitmapLabel(const wxBitmap& bitmap)
358 {
359     m_bmpLabel = bitmap;
360     InvalidateBestSize();
361     CalcLayout(true);
362 }
363 
OnPaint(wxPaintEvent & WXUNUSED (event))364 void wxCustomButton::OnPaint(wxPaintEvent& WXUNUSED(event))
365 {
366     wxPaintDC dc(this);
367     Paint(dc);
368 }
369 
Redraw()370 void wxCustomButton::Redraw()
371 {
372     wxClientDC dc(this);
373     Paint(dc);
374 }
375 
Paint(wxDC & dc)376 void wxCustomButton::Paint( wxDC &dc )
377 {
378     int w, h;
379     GetSize(&w,&h);
380 
381     wxColour foreColour = GetForegroundColour();
382     wxColour backColour = GetBackgroundColour();
383 
384     if (m_focused)
385     {
386         backColour.Set( wxMin(backColour.Red()   + 20, 255),
387                         wxMin(backColour.Green() + 20, 255),
388                         wxMin(backColour.Blue()  + 20, 255) );
389     }
390 
391     wxBitmap bitmap;
392 
393     if (IsEnabled())
394     {
395         if (GetValue() && m_bmpSelected.Ok())
396             bitmap = m_bmpSelected;
397         else if (m_focused && m_bmpFocus.Ok())
398             bitmap = m_bmpFocus;
399         else if (m_bmpLabel.Ok())
400             bitmap = m_bmpLabel;
401     }
402     else
403     {
404         // try to create disabled if it doesn't exist
405         if (!m_bmpDisabled.Ok() && m_bmpLabel.Ok())
406             m_bmpDisabled = CreateBitmapDisabled(m_bmpLabel);
407 
408         if (m_bmpDisabled.Ok())
409             bitmap = m_bmpDisabled;
410         else if (m_bmpLabel.Ok())
411             bitmap = m_bmpLabel;
412 
413         foreColour = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
414     }
415 
416     // wxCONTROL_DISABLED
417     //flags may have the wxCONTROL_PRESSED, wxCONTROL_CURRENT or wxCONTROL_ISDEFAULT
418 
419     int ren_flags = 0;
420     if (GetValue())
421         ren_flags |= wxCONTROL_PRESSED;
422     if (m_focused)
423         ren_flags |= wxCONTROL_CURRENT;
424     if (!IsEnabled())
425         ren_flags |= wxCONTROL_DISABLED;
426 
427     wxRendererNative::Get().DrawPushButton(this, dc, wxRect(0, 0, w, h), ren_flags);
428 
429     if (bitmap.Ok())
430         dc.DrawBitmap(bitmap, m_bitmapPos.x, m_bitmapPos.y, true );
431 
432     if (!GetLabel().IsEmpty())
433     {
434         dc.SetFont(GetFont());
435         dc.SetTextBackground(backColour);
436         dc.SetTextForeground(foreColour);
437         dc.DrawText(GetLabel(), m_labelPos.x, m_labelPos.y);
438     }
439 
440     dc.SetBackground(wxNullBrush);
441     dc.SetBrush(wxNullBrush);
442     dc.SetPen(wxNullPen);
443 }
444 
OnSize(wxSizeEvent & event)445 void wxCustomButton::OnSize( wxSizeEvent &event )
446 {
447     CalcLayout(true);
448     event.Skip();
449 }
450 
SetMargins(const wxSize & margin,bool fit)451 void wxCustomButton::SetMargins(const wxSize &margin, bool fit)
452 {
453     m_labelMargin  = margin;
454     m_bitmapMargin = margin;
455     if (fit) SetSize(DoGetBestSize());
456     CalcLayout(true);
457 }
SetLabelMargin(const wxSize & margin,bool fit)458 void wxCustomButton::SetLabelMargin(const wxSize &margin, bool fit)
459 {
460     m_labelMargin = margin;
461     CalcLayout(true);
462     if (fit) SetSize(DoGetBestSize());
463 }
SetBitmapMargin(const wxSize & margin,bool fit)464 void wxCustomButton::SetBitmapMargin(const wxSize &margin, bool fit)
465 {
466     m_bitmapMargin = margin;
467     CalcLayout(true);
468     if (fit) SetSize(DoGetBestSize());
469 }
470 
DoGetBestSize() const471 wxSize wxCustomButton::DoGetBestSize() const
472 {
473     return DoGetBestSize_((wxWindow*)this);
474 }
475 
DoGetBestSize_(wxWindow * win) const476 wxSize wxCustomButton::DoGetBestSize_(wxWindow* win) const
477 {
478     //((wxWindow*)this)->InvalidateBestSize();
479 
480     int lw = 0, lh = 0;
481     int bw = 0, bh = 0;
482     bool has_bitmap = m_bmpLabel.Ok();
483     bool has_label  = !m_labelString.IsEmpty();
484 
485     if (has_label)
486     {
487         win->GetTextExtent(m_labelString, &lw, &lh);
488         lw += 2*m_labelMargin.x;
489         lh += 2*m_labelMargin.y;
490     }
491     if (has_bitmap)
492     {
493         bw = m_bmpLabel.GetWidth()  + 2*m_bitmapMargin.x;
494         bh = m_bmpLabel.GetHeight() + 2*m_bitmapMargin.y;
495     }
496 
497     if (((m_button_style & wxCUSTBUT_LEFT) != 0) || ((m_button_style & wxCUSTBUT_RIGHT) != 0))
498     {
499         int h = (bh > lh) ? bh : lh;
500         if (has_bitmap && has_label)
501             lw -= wxMin(m_labelMargin.x, m_bitmapMargin.x);
502 
503         return wxSize(lw+bw, h);
504     }
505 
506     int w = (bw > lw) ? bw : lw;
507     if (has_bitmap && has_label)
508         lh -= wxMin(m_labelMargin.y, m_bitmapMargin.y);
509 
510     return wxSize(w, lh+bh);
511 }
512 
CalcLayout(bool refresh)513 void wxCustomButton::CalcLayout(bool refresh)
514 {
515     int w, h;
516     GetSize(&w,&h);
517 
518     int bw = 0, bh = 0;
519     int lw = 0, lh = 0;
520     bool has_bitmap = m_bmpLabel.Ok();
521     bool has_label  = !GetLabel().IsEmpty();
522 
523     if (has_bitmap) // assume they're all the same size
524     {
525         bw = m_bmpLabel.GetWidth();
526         bh = m_bmpLabel.GetHeight();
527     }
528 
529     if (has_label)
530     {
531         GetTextExtent(GetLabel(), &lw, &lh);
532     }
533 
534     // Center the label or bitmap if only one or the other
535     if (!has_bitmap)
536     {
537         m_bitmapPos = wxPoint(0,0);
538         m_labelPos  = wxPoint((w-lw)/2, (h-lh)/2);
539     }
540     else if (!has_label)
541     {
542         m_bitmapPos = wxPoint((w-bw)/2, (h-bh)/2);
543         m_labelPos  = wxPoint(0,0);
544     }
545     else if ((m_button_style & wxCUSTBUT_LEFT) != 0)
546     {
547         int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
548         m_labelPos  = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_labelMargin.x, (h - lh)/2);
549         m_bitmapPos = wxPoint(m_labelPos.x + lw + mid_margin,         (h - bh)/2);
550     }
551     else if ((m_button_style & wxCUSTBUT_RIGHT) != 0)
552     {
553         int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
554         m_bitmapPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_bitmapMargin.x, (h - bh)/2);
555         m_labelPos  = wxPoint(m_bitmapPos.x + bw + mid_margin,        (h - lh)/2);
556     }
557     else if ((m_button_style & wxCUSTBUT_TOP) != 0)
558     {
559         int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
560         m_labelPos  = wxPoint((w - lw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_labelMargin.y);
561         m_bitmapPos = wxPoint((w - bw)/2, m_labelPos.y + lh + mid_margin);
562     }
563     else // if ((m_button_style & wxCUSTBUT_BOTTOM) != 0)  DEFAULT
564     {
565         int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
566         m_bitmapPos = wxPoint((w - bw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_bitmapMargin.y);
567         m_labelPos  = wxPoint((w - lw)/2, m_bitmapPos.y + bh + mid_margin);
568     }
569 
570     if (refresh) Refresh(false);
571 }
572