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