1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        wxMenuButton
3 // Purpose:     A button with a dropdown wxMenu
4 // Author:      John Labenski
5 // Modified by:
6 // Created:     11/05/2002
7 // RCS-ID:
8 // Copyright:   (c) John Labenki
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "menubtn.h"
14 #endif
15 
16 // For compilers that support precompilation, includes "wx/wx.h".
17 #include "wx/wxprec.h"
18 
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22 
23 #ifndef WX_PRECOMP
24 #include "wx/control.h"
25 #include "wx/menu.h"
26 #include "wx/settings.h"
27 #include "wx/bitmap.h"
28 #include "wx/pen.h"
29 #include "wx/dc.h"
30 #endif // WX_PRECOMP
31 
32 #include <wx/tglbtn.h>
33 #include <wx/dcclient.h>
34 #include <wx/timer.h>
35 #include <wx/image.h>
36 
37 #include "menubtn.h"
38 
39 
40 
41 // ==========================================================================
42 // wxCustomButton
43 // ==========================================================================
IMPLEMENT_DYNAMIC_CLASS(wxCustomButton,wxControl)44 IMPLEMENT_DYNAMIC_CLASS( wxCustomButton, wxControl )
45 
46 BEGIN_EVENT_TABLE(wxCustomButton,wxControl)
47 	EVT_MOUSE_EVENTS ( wxCustomButton::OnMouseEvents )
48 	EVT_PAINT        ( wxCustomButton::OnPaint )
49 	EVT_SIZE         ( wxCustomButton::OnSize )
50 END_EVENT_TABLE()
51 
52 wxCustomButton::~wxCustomButton()
53 {
54 	if (HasCapture()) ReleaseMouse();
55 	if (m_timer) delete m_timer;
56 }
57 
Init()58 void wxCustomButton::Init()
59 {
60 	m_focused = FALSE;
61 	m_labelMargin = wxSize(4,4);
62 	m_bitmapMargin = wxSize(2,2);
63 	m_down = 0;
64 	m_timer = NULL;
65 	m_eventType = 0;
66 	m_button_style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM;
67 }
68 
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)69 bool wxCustomButton::Create(wxWindow* parent, wxWindowID id,
70                             const wxString& label, const wxBitmap &bitmap,
71                             const wxPoint& pos, const wxSize& size,
72                             long style, const wxValidator& val,
73                             const wxString& name)
74 {
75 	if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name))
76 		return FALSE;
77 
78 	wxControl::SetLabel(label);
79 	wxControl::SetBackgroundColour(parent->GetBackgroundColour());
80 	wxControl::SetForegroundColour(parent->GetForegroundColour());
81 	wxControl::SetFont(parent->GetFont());
82 
83 	if (bitmap.Ok()) m_bmpLabel = bitmap;
84 
85 	if (!SetButtonStyle(style)) return FALSE;
86 
87 	wxSize bestSize = DoGetBestSize();
88 	SetSize(wxSize(size.x<0 ? bestSize.x:size.x, size.y<0 ? bestSize.y:size.y));
89 #if (wxMINOR_VERSION<8)
90 	SetBestSize(GetSize());
91 #else
92 	SetInitialSize(GetSize());
93 #endif
94 
95 	CalcLayout(TRUE);
96 	return TRUE;
97 }
98 
SetValue(bool depressed)99 void wxCustomButton::SetValue(bool depressed)
100 {
101 	wxCHECK_RET(!(m_button_style & wxCUSTBUT_NOTOGGLE), wxT("can't set button state"));
102 	m_down = depressed ? 1 : 0;
103 	Refresh(FALSE);
104 }
105 
SetButtonStyle(long style)106 bool wxCustomButton::SetButtonStyle(long style)
107 {
108 	int n_styles = 0;
109 	if ((style & wxCUSTBUT_LEFT) != 0)   n_styles++;
110 	if ((style & wxCUSTBUT_RIGHT) != 0)  n_styles++;
111 	if ((style & wxCUSTBUT_TOP) != 0)    n_styles++;
112 	if ((style & wxCUSTBUT_BOTTOM) != 0) n_styles++;
113 	wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton label position allowed"));
114 
115 	n_styles = 0;
116 	if ((style & wxCUSTBUT_NOTOGGLE) != 0)       n_styles++;
117 	if ((style & wxCUSTBUT_BUTTON) != 0)         n_styles++;
118 	if ((style & wxCUSTBUT_TOGGLE) != 0)         n_styles++;
119 	if ((style & wxCUSTBUT_BUT_DCLICK_TOG) != 0) n_styles++;
120 	if ((style & wxCUSTBUT_TOG_DCLICK_BUT) != 0) n_styles++;
121 	wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton style allowed"));
122 
123 	m_button_style = style;
124 
125 	if ((m_button_style & wxCUSTBUT_BUTTON) != 0)
126 		m_down = 0;
127 
128 	CalcLayout(TRUE);
129 	return TRUE;
130 }
131 
SetLabel(const wxString & label)132 void wxCustomButton::SetLabel( const wxString &label )
133 {
134 	wxControl::SetLabel(label);
135 	CalcLayout(TRUE);
136 }
137 
138 // sequence of events in GTK is up, dclick, up.
139 
OnMouseEvents(wxMouseEvent & event)140 void wxCustomButton::OnMouseEvents(wxMouseEvent& event)
141 {
142 	if (m_button_style & wxCUSTBUT_NOTOGGLE) return;
143 
144 	if (event.LeftDown() || event.RightDown())
145 	{
146 		if (!HasCapture())
147 			CaptureMouse(); // keep depressed until up
148 
149 		m_down++;
150 		Redraw();
151 	}
152 	else if (event.LeftDClick() || event.RightDClick())
153 	{
154 		m_down++; // GTK eats second down event
155 		Redraw();
156 	}
157 	else if (event.LeftUp())
158 	{
159 		if (HasCapture())
160 			ReleaseMouse();
161 
162 		m_eventType = wxEVT_LEFT_UP;
163 
164 #if (wxMINOR_VERSION<8)
165 		if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition()))
166 #else
167 		if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition()))
168 #endif
169 		{
170 			if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
171 			{
172 				m_down = 0;
173 				Redraw();
174 				SendEvent();
175 				return;
176 			}
177 			else
178 			{
179 				if (!m_timer)
180 				{
181 					m_timer = new wxTimer(this, m_down+1);
182 					m_timer->Start(200, TRUE);
183 				}
184 				else
185 				{
186 					m_eventType = wxEVT_LEFT_DCLICK;
187 				}
188 
189 				if ((m_button_style & wxCUSTBUT_TOGGLE) &&
190 				        (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)) m_down++;
191 			}
192 		}
193 
194 		Redraw();
195 	}
196 	else if (event.RightUp())
197 	{
198 		if (HasCapture())
199 			ReleaseMouse();
200 
201 		m_eventType = wxEVT_RIGHT_UP;
202 
203 #if (wxMINOR_VERSION<8)
204 		if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition()))
205 #else
206 		if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition()))
207 #endif
208 		{
209 			if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0))
210 			{
211 				m_down = 0;
212 				Redraw();
213 				SendEvent();
214 				return;
215 			}
216 			else
217 			{
218 				m_down++;
219 
220 				if (!m_timer)
221 				{
222 					m_timer = new wxTimer(this, m_down);
223 					m_timer->Start(250, TRUE);
224 				}
225 				else
226 				{
227 					m_eventType = wxEVT_RIGHT_DCLICK;
228 				}
229 			}
230 		}
231 
232 		Redraw();
233 	}
234 	else if (event.Entering())
235 	{
236 		m_focused = TRUE;
237 		if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
238 			m_down++;
239 
240 		Redraw();
241 	}
242 	else if (event.Leaving())
243 	{
244 		m_focused = FALSE;
245 		if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture())
246 			m_down--;
247 
248 		Redraw();
249 	}
250 }
251 
252 
253 
SendEvent()254 void wxCustomButton::SendEvent()
255 {
256 	if (((m_button_style & wxCUSTBUT_TOGGLE) && (m_eventType == wxEVT_LEFT_UP)) ||
257 	        ((m_button_style & wxCUSTBUT_BUT_DCLICK_TOG) && (m_eventType == wxEVT_LEFT_DCLICK)) ||
258 	        ((m_button_style & wxCUSTBUT_TOG_DCLICK_BUT) && (m_eventType == wxEVT_LEFT_UP)))
259 	{
260 		wxCommandEvent eventOut(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, GetId());
261 		eventOut.SetInt(m_down%2 ? 1 : 0);
262 		eventOut.SetExtraLong(m_eventType);
263 		eventOut.SetEventObject(this);
264 		GetEventHandler()->ProcessEvent(eventOut);
265 	}
266 	else
267 	{
268 		wxCommandEvent eventOut(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
269 		eventOut.SetInt(0);
270 		eventOut.SetExtraLong(m_eventType);
271 		eventOut.SetEventObject(this);
272 		GetEventHandler()->ProcessEvent(eventOut);
273 	}
274 }
275 
CreateBitmapDisabled(const wxBitmap & bitmap) const276 wxBitmap wxCustomButton::CreateBitmapDisabled(const wxBitmap &bitmap) const
277 {
278 	wxCHECK_MSG(bitmap.Ok(), wxNullBitmap, wxT("invalid bitmap"));
279 
280 	unsigned char br = GetBackgroundColour().Red();
281 	unsigned char bg = GetBackgroundColour().Green();
282 	unsigned char bb = GetBackgroundColour().Blue();
283 
284 	wxImage image = bitmap.ConvertToImage();
285 	int pos, width = image.GetWidth(), height = image.GetHeight();
286 	unsigned char *img_data = image.GetData();
287 
288 	for (int j=0; j<height; j++)
289 	{
290 		for (int i=j%2; i<width; i+=2)
291 		{
292 			pos = (j*width+i)*3;
293 			img_data[pos  ] = br;
294 			img_data[pos+1] = bg;
295 			img_data[pos+2] = bb;
296 		}
297 	}
298 
299 	return wxBitmap(image);
300 }
301 
SetBitmapLabel(const wxBitmap & bitmap)302 void wxCustomButton::SetBitmapLabel(const wxBitmap& bitmap)
303 {
304 	m_bmpLabel = bitmap;
305 	CalcLayout(TRUE);
306 }
307 
OnPaint(wxPaintEvent & WXUNUSED (event))308 void wxCustomButton::OnPaint(wxPaintEvent& WXUNUSED(event))
309 {
310 	wxPaintDC dc(this);
311 	Paint(dc);
312 }
313 
Redraw()314 void wxCustomButton::Redraw()
315 {
316 	wxClientDC dc(this);
317 	Paint(dc);
318 }
319 
Paint(wxDC & dc)320 void wxCustomButton::Paint( wxDC &dc )
321 {
322 #if (wxMINOR_VERSION<8)
323 	dc.BeginDrawing();
324 #endif
325 
326 	int w, h;
327 	GetSize(&w,&h);
328 
329 	wxColour foreColour = GetForegroundColour();
330 	wxColour backColour = GetBackgroundColour();
331 
332 	if (m_focused)
333 	{
334 		backColour.Set( wxMin(backColour.Red()   + 20, 255),
335 		                wxMin(backColour.Green() + 20, 255),
336 		                wxMin(backColour.Blue()  + 20, 255) );
337 	}
338 
339 	wxBitmap bitmap;
340 
341 	if (IsEnabled())
342 	{
343 		if (GetValue() && m_bmpSelected.Ok())
344 			bitmap = m_bmpSelected;
345 		else if (m_focused && m_bmpFocus.Ok())
346 			bitmap = m_bmpFocus;
347 		else if (m_bmpLabel.Ok())
348 			bitmap = m_bmpLabel;
349 	}
350 	else
351 	{
352 		// try to create disabled if it doesn't exist
353 		if (!m_bmpDisabled.Ok() && m_bmpLabel.Ok())
354 			m_bmpDisabled = CreateBitmapDisabled(m_bmpLabel);
355 
356 		if (m_bmpDisabled.Ok())
357 			bitmap = m_bmpDisabled;
358 		else if (m_bmpLabel.Ok())
359 			bitmap = m_bmpLabel;
360 
361 		foreColour = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
362 	}
363 
364 	wxBrush brush(backColour, wxSOLID);
365 	dc.SetBackground(brush);
366 	dc.SetBrush(brush);
367 	dc.SetPen(*wxTRANSPARENT_PEN);
368 
369 	dc.DrawRectangle(0, 0, w, h);
370 
371 	if (bitmap.Ok())
372 		dc.DrawBitmap(bitmap, m_bitmapPos.x, m_bitmapPos.y, TRUE );
373 
374 	if (!GetLabel().IsEmpty())
375 	{
376 		dc.SetFont(GetFont());
377 		dc.SetTextBackground(backColour);
378 		dc.SetTextForeground(foreColour);
379 		dc.DrawText(GetLabel(), m_labelPos.x, m_labelPos.y);
380 	}
381 
382 	if (GetValue())                                        // draw sunken border
383 	{
384 		dc.SetPen(*wxGREY_PEN);
385 		dc.DrawLine(0,h-1,0,0);
386 		dc.DrawLine(0,0,w,0);
387 		dc.SetPen(*wxWHITE_PEN);
388 		dc.DrawLine(w-1,1,w-1,h-1);
389 		dc.DrawLine(w-1,h-1,0,h-1);
390 		dc.SetPen(*wxBLACK_PEN);
391 		dc.DrawLine(1,h-2,1,1);
392 		dc.DrawLine(1,1,w-1,1);
393 	}
394 	else if (((m_button_style & wxCUSTBUT_FLAT) == 0) || m_focused) // draw raised border
395 	{
396 		dc.SetPen(*wxWHITE_PEN);
397 		dc.DrawLine(0,h-2,0,0);
398 		dc.DrawLine(0,0,w-1,0);
399 		dc.SetPen(*wxBLACK_PEN);
400 		dc.DrawLine(w-1,0,w-1,h-1);
401 		dc.DrawLine(w-1,h-1,-1,h-1);
402 		dc.SetPen(*wxGREY_PEN);
403 		dc.DrawLine(2,h-2,w-2,h-2);
404 		dc.DrawLine(w-2,h-2,w-2,1);
405 	}
406 
407 	dc.SetBackground(wxNullBrush);
408 	dc.SetBrush(wxNullBrush);
409 	dc.SetPen(wxNullPen);
410 #if (wxMINOR_VERSION<8)
411 	dc.EndDrawing();
412 #endif
413 }
414 
OnSize(wxSizeEvent & event)415 void wxCustomButton::OnSize( wxSizeEvent &event )
416 {
417 	CalcLayout(TRUE);
418 	event.Skip();
419 }
420 
SetMargins(const wxSize & margin,bool fit)421 void wxCustomButton::SetMargins(const wxSize &margin, bool fit)
422 {
423 	m_labelMargin = margin;
424 	m_bitmapMargin = margin;
425 	CalcLayout(TRUE);
426 	if (fit) SetSize(DoGetBestSize());
427 }
SetLabelMargin(const wxSize & margin,bool fit)428 void wxCustomButton::SetLabelMargin(const wxSize &margin, bool fit)
429 {
430 	m_labelMargin = margin;
431 	CalcLayout(TRUE);
432 	if (fit) SetSize(DoGetBestSize());
433 }
SetBitmapMargin(const wxSize & margin,bool fit)434 void wxCustomButton::SetBitmapMargin(const wxSize &margin, bool fit)
435 {
436 	m_bitmapMargin = margin;
437 	CalcLayout(TRUE);
438 	if (fit) SetSize(DoGetBestSize());
439 }
440 
DoGetBestSize() const441 wxSize wxCustomButton::DoGetBestSize() const
442 {
443 	int lw=0, lh=0;
444 	int bw=0, bh=0;
445 	bool has_bitmap = FALSE;
446 	bool has_label = FALSE;
447 
448 	if (!GetLabel().IsEmpty())
449 	{
450 		GetTextExtent(GetLabel(), &lw, &lh);
451 		lw += 2*m_labelMargin.x;
452 		lh += 2*m_labelMargin.y;
453 		has_label = TRUE;
454 	}
455 	if (m_bmpLabel.Ok())
456 	{
457 		bw = m_bmpLabel.GetWidth() + 2*m_bitmapMargin.x;
458 		bh = m_bmpLabel.GetHeight() + 2*m_bitmapMargin.y;
459 		has_bitmap = TRUE;
460 	}
461 
462 	if ((m_button_style & wxCUSTBUT_LEFT) || (m_button_style & wxCUSTBUT_RIGHT))
463 	{
464 		int h = bh > lh ? bh : lh;
465 		if (has_bitmap && has_label) lw -= wxMin(m_labelMargin.x, m_bitmapMargin.x);
466 		return wxSize(lw+bw, h);
467 	}
468 
469 	int w = bw > lw ? bw : lw;
470 	if (has_bitmap && has_label) lh -= wxMin(m_labelMargin.y, m_bitmapMargin.y);
471 	return wxSize(w, lh+bh);
472 }
473 
CalcLayout(bool refresh)474 void wxCustomButton::CalcLayout(bool refresh)
475 {
476 	int w, h;
477 	GetSize(&w,&h);
478 
479 	int bw = 0, bh = 0;
480 	int lw = 0, lh = 0;
481 
482 	if (m_bmpLabel.Ok()) // assume they're all the same size
483 	{
484 		bw = m_bmpLabel.GetWidth();
485 		bh = m_bmpLabel.GetHeight();
486 	}
487 	wxString label = GetLabel();
488 	if (!label.IsEmpty())
489 	{
490 		GetTextExtent(label, &lw, &lh);
491 	}
492 
493 	// Center the label or bitmap if only one or the other
494 	if (!m_bmpLabel.Ok())
495 	{
496 		m_bitmapPos = wxPoint(0,0);
497 		m_labelPos = wxPoint((w-lw)/2, (h-lh)/2);
498 	}
499 	else if (label.IsEmpty())
500 	{
501 		m_bitmapPos = wxPoint((w-bw)/2, (h-bh)/2);
502 		m_labelPos = wxPoint(0,0);
503 	}
504 	else if (m_button_style & wxCUSTBUT_LEFT)
505 	{
506 		int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
507 		m_labelPos  = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_labelMargin.x, (h - lh)/2);
508 		m_bitmapPos = wxPoint(m_labelPos.x + lw + mid_margin,         (h - bh)/2);
509 	}
510 	else if (m_button_style & wxCUSTBUT_RIGHT)
511 	{
512 		int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x);
513 		m_bitmapPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_bitmapMargin.x, (h - bh)/2);
514 		m_labelPos  = wxPoint(m_bitmapPos.x + bw + mid_margin,        (h - lh)/2);
515 	}
516 	else if (m_button_style & wxCUSTBUT_TOP)
517 	{
518 		int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
519 		m_labelPos  = wxPoint((w - lw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_labelMargin.y);
520 		m_bitmapPos = wxPoint((w - bw)/2, m_labelPos.y + lh + mid_margin);
521 	}
522 	else // if (m_button_style & wxCUSTBUT_BOTTOM)  DEFAULT
523 	{
524 		int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y);
525 		m_bitmapPos = wxPoint((w - bw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_bitmapMargin.y);
526 		m_labelPos  = wxPoint((w - lw)/2, m_bitmapPos.y + bh + mid_margin);
527 	}
528 
529 	if (refresh) Refresh(FALSE);
530 }
531 
532 
533 /* XPM */
534 static const char *down_arrow_xpm_data[] = {
535 	/* columns rows colors chars-per-pixel */
536 	"5 3 2 1",
537 	"  c None",
538 	"a c Black",
539 	/* pixels */
540 	"aaaaa",
541 	" aaa ",
542 	"  a  "
543 };
544 
545 static wxBitmap s_dropdownBitmap; // all buttons share the same bitmap
546 
547 enum
548 {
549 	IDD_DROPDOWN_BUTTON = 100
550 };
551 
552 //-----------------------------------------------------------------------------
553 // wxMenuButtonEvents
554 //-----------------------------------------------------------------------------
555 
556 DEFINE_LOCAL_EVENT_TYPE(wxEVT_MENUBUTTON_OPEN)
557 
558 // ==========================================================================
559 // MenuDropButton
560 // ==========================================================================
561 
562 class MenuDropButton : public wxCustomButton
563 {
564 public:
MenuDropButton(wxWindow * parent,wxWindowID id,long style)565 	MenuDropButton( wxWindow *parent, wxWindowID id, long style) : wxCustomButton()
566 	{
567 		if (!s_dropdownBitmap.Ok())
568 			s_dropdownBitmap = wxBitmap(down_arrow_xpm_data);
569 
570 		Create( parent, id, wxEmptyString, s_dropdownBitmap, wxDefaultPosition,
571 		        wxSize(wxMENUBUTTON_DROP_WIDTH, wxMENUBUTTON_DROP_HEIGHT), style);
572 	}
573 
Paint(wxDC & dc)574 	virtual void Paint( wxDC &dc )
575 	{
576 		wxCustomButton *labelBut = ((wxMenuButton*)GetParent())->GetLabelButton();
577 
578 		// pretend that both buttons have focus (for flat style)
579 		if (labelBut)
580 		{
581 			wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition());
582 
583 #if (wxMINOR_VERSION<8)
584 			if (GetRect().Inside(p) || labelBut->GetRect().Inside(p))
585 #else
586 			if (GetRect().Contains(p) || labelBut->GetRect().Contains(p))
587 #endif
588 			{
589 				m_focused = TRUE;
590 
591 				if (!labelBut->GetFocused())
592 					labelBut->SetFocused(TRUE);
593 			}
594 			else
595 			{
596 				m_focused = FALSE;
597 
598 				if (labelBut->GetFocused())
599 					labelBut->SetFocused(FALSE);
600 			}
601 		}
602 
603 		wxCustomButton::Paint(dc);
604 	}
605 };
606 
607 // ==========================================================================
608 // MenuLabelButton
609 // ==========================================================================
610 
611 class MenuLabelButton : public wxCustomButton
612 {
613 public:
MenuLabelButton(wxWindow * parent,wxWindowID id,const wxString & label,const wxBitmap & bitmap,long style)614 	MenuLabelButton( wxWindow* parent, wxWindowID id,
615 	                 const wxString &label,
616 	                 const wxBitmap &bitmap,
617 	                 long style ) : wxCustomButton()
618 	{
619 		Create(parent, id, label, bitmap, wxDefaultPosition, wxDefaultSize, style);
620 	}
621 
Paint(wxDC & dc)622 	virtual void Paint( wxDC &dc )
623 	{
624 		wxCustomButton *dropBut = ((wxMenuButton*)GetParent())->GetDropDownButton();
625 
626 		// pretend that both buttons have focus (for flat style)
627 		if (dropBut)
628 		{
629 			wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition());
630 
631 #if (wxMINOR_VERSION<8)
632 			if (GetRect().Inside(p) || dropBut->GetRect().Inside(p))
633 #else
634 			if (GetRect().Contains(p) || dropBut->GetRect().Contains(p))
635 #endif
636 			{
637 				m_focused = TRUE;
638 
639 				if (!dropBut->GetFocused())
640 					dropBut->SetFocused(TRUE);
641 			}
642 			else
643 			{
644 				m_focused = FALSE;
645 
646 				if (dropBut->GetFocused())
647 					dropBut->SetFocused(FALSE);
648 			}
649 		}
650 
651 		wxCustomButton::Paint(dc);
652 	}
653 };
654 
655 // ==========================================================================
656 // wxMenuButton
657 // ==========================================================================
658 
IMPLEMENT_DYNAMIC_CLASS(wxMenuButton,wxControl)659 IMPLEMENT_DYNAMIC_CLASS( wxMenuButton, wxControl )
660 
661 BEGIN_EVENT_TABLE(wxMenuButton,wxControl)
662 	EVT_BUTTON(wxID_ANY, wxMenuButton::OnButton)
663 
664 #ifdef __WXMSW__
665 	EVT_MENU(wxID_ANY, wxMenuButton::OnMenu)
666 #endif
667 END_EVENT_TABLE()
668 
669 wxMenuButton::~wxMenuButton()
670 {
671 	AssignMenu(NULL, TRUE);
672 }
673 
Init()674 void wxMenuButton::Init()
675 {
676 	m_labelButton = NULL;
677 	m_dropdownButton = NULL;
678 	m_menu = NULL;
679 	m_menu_static = FALSE;
680 	m_style = 0;
681 }
682 
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)683 bool wxMenuButton::Create( wxWindow* parent, wxWindowID id,
684                            const wxString &label,
685                            const wxBitmap &bitmap,
686                            const wxPoint& pos,
687                            const wxSize& size,
688                            long style,
689                            const wxValidator& val,
690                            const wxString& name)
691 {
692 	m_style = style;
693 
694 	long flat = style & wxMENUBUT_FLAT;
695 
696 	wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name);
697 	wxControl::SetLabel(label);
698 	SetBackgroundColour(parent->GetBackgroundColour());
699 	SetForegroundColour(parent->GetForegroundColour());
700 	SetFont(parent->GetFont());
701 
702 	m_labelButton = new MenuLabelButton(this, id, label, bitmap, wxCUSTBUT_BUTTON|flat);
703 	m_dropdownButton = new MenuDropButton(this, IDD_DROPDOWN_BUTTON, wxCUSTBUT_BUTTON|flat);
704 
705 	wxSize bestSize = DoGetBestSize();
706 	SetSize( wxSize(size.x < 0 ? bestSize.x : size.x,
707 	                size.y < 0 ? bestSize.y : size.y) );
708 
709 #if (wxMINOR_VERSION<8)
710 	SetBestSize(GetSize());
711 #else
712 	SetInitialSize(GetSize());
713 #endif
714 
715 	return TRUE;
716 }
717 
718 #ifdef __WXMSW__
719 // FIXME - I think there was a patch to fix this
OnMenu(wxCommandEvent & event)720 void wxMenuButton::OnMenu( wxCommandEvent &event )
721 {
722 	event.Skip();
723 	wxMenuItem *mi = m_menu->FindItem(event.GetId());
724 	if (mi && (mi->GetKind() == wxITEM_RADIO))
725 		m_menu->Check(event.GetId(), TRUE);
726 }
727 #endif // __WXMSW__
728 
OnButton(wxCommandEvent & event)729 void wxMenuButton::OnButton( wxCommandEvent &event)
730 {
731 	int win_id = event.GetId();
732 
733 	if (win_id == IDD_DROPDOWN_BUTTON)
734 	{
735 		wxNotifyEvent mevent(wxEVT_MENUBUTTON_OPEN, GetId());
736 		mevent.SetEventObject(this);
737 		if (GetEventHandler()->ProcessEvent(mevent) && !mevent.IsAllowed())
738 			return;
739 
740 		if (!m_menu)
741 			return;
742 
743 		PopupMenu(m_menu, wxPoint(0, GetSize().y));
744 
745 		m_labelButton->Refresh(FALSE);
746 		m_dropdownButton->Refresh(FALSE);
747 	}
748 	else if (win_id == m_labelButton->GetId())
749 	{
750 
751 		wxCommandEvent cevent(wxEVT_COMMAND_MENU_SELECTED, win_id);
752 		cevent.SetEventObject(this);
753 		cevent.SetId(win_id);
754 		GetParent()->GetEventHandler()->ProcessEvent(cevent);
755 
756 		if (!m_menu) return;
757 
758 		const wxMenuItemList &items = m_menu->GetMenuItems();
759 		int first_radio_id = -1;
760 		int checked_id = -1;
761 		bool check_next = FALSE;
762 
763 		// find the next available radio item to check
764 		for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext())
765 		{
766 			wxMenuItem *mi = (wxMenuItem*)node->GetData();
767 			if (mi && (mi->GetKind() == wxITEM_RADIO))
768 			{
769 				if (first_radio_id == -1)
770 					first_radio_id = mi->GetId();
771 
772 				if (check_next)
773 				{
774 					check_next = FALSE;
775 					checked_id = mi->GetId();
776 					break;
777 				}
778 				else if (mi->IsChecked())
779 					check_next = TRUE;
780 			}
781 		}
782 		// the last item was checked, go back to the first
783 		if (check_next && (first_radio_id != -1))
784 			checked_id = first_radio_id;
785 
786 		if (checked_id != -1)
787 		{
788 			m_menu->Check(checked_id, TRUE);
789 
790 			wxCommandEvent mevent( wxEVT_COMMAND_MENU_SELECTED, checked_id);
791 			mevent.SetEventObject( m_menu );
792 			mevent.SetInt(1);
793 			GetEventHandler()->ProcessEvent(mevent);
794 		}
795 	}
796 }
797 
GetSelection() const798 int wxMenuButton::GetSelection() const
799 {
800 	wxCHECK_MSG(m_menu != NULL, wxNOT_FOUND, wxT("No attached menu in wxMenuButton::GetSelection"));
801 
802 	const wxMenuItemList &items = m_menu->GetMenuItems();
803 
804 	for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext())
805 	{
806 		wxMenuItem *mi = (wxMenuItem*)node->GetData();
807 		if (mi && (mi->GetKind() == wxITEM_RADIO))
808 		{
809 			if (mi->IsChecked())
810 				return mi->GetId();
811 		}
812 	}
813 
814 	return wxNOT_FOUND;
815 }
816 
AssignMenu(wxMenu * menu,bool static_menu)817 void wxMenuButton::AssignMenu(wxMenu *menu, bool static_menu)
818 {
819 	if (!m_menu_static && m_menu)
820 		delete m_menu;
821 
822 	m_menu = menu;
823 	m_menu_static = static_menu;
824 }
825 
SetToolTip(const wxString & tip)826 void wxMenuButton::SetToolTip(const wxString &tip)
827 {
828 	wxWindow::SetToolTip(tip);
829 	((wxWindow*)m_labelButton)->SetToolTip(tip);
830 	((wxWindow*)m_dropdownButton)->SetToolTip(tip);
831 }
SetToolTip(wxToolTip * tip)832 void wxMenuButton::SetToolTip(wxToolTip *tip)
833 {
834 	wxWindow::SetToolTip(tip);
835 	((wxWindow*)m_labelButton)->SetToolTip(tip);
836 	((wxWindow*)m_dropdownButton)->SetToolTip(tip);
837 }
838 
DoSetSize(int x,int y,int width,int height,int sizeFlags)839 void wxMenuButton::DoSetSize(int x, int y, int width, int height, int sizeFlags)
840 {
841 	wxSize curSize( GetSize() );
842 	wxSize bestSize( DoGetBestSize() );
843 
844 	if (width == -1)
845 		width = curSize.GetWidth();
846 	if (width < 10)
847 		width = bestSize.GetWidth();
848 
849 	if (height == -1)
850 		height = curSize.GetHeight();
851 	if (height < 5)
852 		height = bestSize.GetHeight();
853 
854 	wxWindow::DoSetSize(x, y, width, height, sizeFlags);
855 
856 	if (m_labelButton)
857 		m_labelButton->SetSize(0, 0, width - wxMENUBUTTON_DROP_WIDTH, height);
858 	if (m_dropdownButton)
859 		m_dropdownButton->SetSize(width-wxMENUBUTTON_DROP_WIDTH, 0, wxMENUBUTTON_DROP_WIDTH, height);
860 }
861 
DoGetBestSize()862 wxSize wxMenuButton::DoGetBestSize()
863 {
864 	if (!m_labelButton || !m_dropdownButton)
865 		return wxSize(wxMENUBUTTON_DROP_WIDTH+wxMENUBUTTON_DROP_HEIGHT, wxMENUBUTTON_DROP_HEIGHT);
866 
867 	wxSize size = m_labelButton->GetBestSize();
868 	size.x += wxMENUBUTTON_DROP_WIDTH;
869 	return size;
870 }
871