Enhance wxRibbonToolBar functionality to wxRibbonButtonBar level.
[wxWidgets.git] / src / ribbon / toolbar.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/toolbar.cpp
3 // Purpose: Ribbon-style tool bar
4 // Author: Peter Cawley
5 // Modified by:
6 // Created: 2009-07-06
7 // RCS-ID: $Id$
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_RIBBON
19
20 #include "wx/ribbon/toolbar.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/ribbon/bar.h"
23 #include "wx/dcbuffer.h"
24
25 #ifndef WX_PRECOMP
26 #endif
27
28 #ifdef __WXMSW__
29 #include "wx/msw/private.h"
30 #endif
31
32 class wxRibbonToolBarToolBase
33 {
34 public:
35 wxString help_string;
36 wxBitmap bitmap;
37 wxBitmap bitmap_disabled;
38 wxRect dropdown;
39 wxPoint position;
40 wxSize size;
41 wxObject* client_data;
42 int id;
43 wxRibbonButtonKind kind;
44 long state;
45 };
46
47 WX_DEFINE_ARRAY_PTR(wxRibbonToolBarToolBase*, wxArrayRibbonToolBarToolBase);
48
49 class wxRibbonToolBarToolGroup
50 {
51 public:
52 // To identify the group as a wxRibbonToolBarToolBase*
53 wxRibbonToolBarToolBase dummy_tool;
54
55 wxArrayRibbonToolBarToolBase tools;
56 wxPoint position;
57 wxSize size;
58 };
59
60 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_CLICKED, wxRibbonToolBarEvent);
61 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED, wxRibbonToolBarEvent);
62
63 IMPLEMENT_DYNAMIC_CLASS(wxRibbonToolBarEvent, wxCommandEvent)
64 IMPLEMENT_CLASS(wxRibbonToolBar, wxRibbonControl)
65
66 BEGIN_EVENT_TABLE(wxRibbonToolBar, wxRibbonControl)
67 EVT_ENTER_WINDOW(wxRibbonToolBar::OnMouseEnter)
68 EVT_ERASE_BACKGROUND(wxRibbonToolBar::OnEraseBackground)
69 EVT_LEAVE_WINDOW(wxRibbonToolBar::OnMouseLeave)
70 EVT_LEFT_DOWN(wxRibbonToolBar::OnMouseDown)
71 EVT_LEFT_UP(wxRibbonToolBar::OnMouseUp)
72 EVT_MOTION(wxRibbonToolBar::OnMouseMove)
73 EVT_PAINT(wxRibbonToolBar::OnPaint)
74 EVT_SIZE(wxRibbonToolBar::OnSize)
75 END_EVENT_TABLE()
76
77 wxRibbonToolBar::wxRibbonToolBar()
78 {
79 }
80
81 wxRibbonToolBar::wxRibbonToolBar(wxWindow* parent,
82 wxWindowID id,
83 const wxPoint& pos,
84 const wxSize& size,
85 long style)
86 : wxRibbonControl(parent, id, pos, size, wxBORDER_NONE)
87 {
88 CommonInit(style);
89 }
90
91 bool wxRibbonToolBar::Create(wxWindow* parent,
92 wxWindowID id,
93 const wxPoint& pos,
94 const wxSize& size,
95 long style)
96 {
97 if(!wxRibbonControl::Create(parent, id, pos, size, wxBORDER_NONE))
98 {
99 return false;
100 }
101
102 CommonInit(style);
103 return true;
104 }
105
106 void wxRibbonToolBar::CommonInit(long WXUNUSED(style))
107 {
108 AppendGroup();
109 m_hover_tool = NULL;
110 m_active_tool = NULL;
111 m_nrows_min = 1;
112 m_nrows_max = 1;
113 m_sizes = new wxSize[1];
114 m_sizes[0] = wxSize(0, 0);
115 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
116 }
117
118 wxRibbonToolBar::~wxRibbonToolBar()
119 {
120 size_t count = m_groups.GetCount();
121 size_t i, t;
122 for(i = 0; i < count; ++i)
123 {
124 wxRibbonToolBarToolGroup* group = m_groups.Item(i);
125 size_t tool_count = group->tools.GetCount();
126 for(t = 0; t < tool_count; ++t)
127 {
128 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
129 delete tool;
130 }
131 delete group;
132 }
133 m_groups.Clear();
134 delete[] m_sizes;
135 }
136
137 wxRibbonToolBarToolBase* wxRibbonToolBar::AddTool(
138 int tool_id,
139 const wxBitmap& bitmap,
140 const wxString& help_string,
141 wxRibbonButtonKind kind)
142 {
143 return AddTool(tool_id, bitmap, wxNullBitmap, help_string, kind, NULL);
144 }
145
146 wxRibbonToolBarToolBase* wxRibbonToolBar::AddDropdownTool(
147 int tool_id,
148 const wxBitmap& bitmap,
149 const wxString& help_string)
150 {
151 return AddTool(tool_id, bitmap, wxNullBitmap, help_string,
152 wxRIBBON_BUTTON_DROPDOWN, NULL);
153 }
154
155 wxRibbonToolBarToolBase* wxRibbonToolBar::AddHybridTool(
156 int tool_id,
157 const wxBitmap& bitmap,
158 const wxString& help_string)
159 {
160 return AddTool(tool_id, bitmap, wxNullBitmap, help_string,
161 wxRIBBON_BUTTON_HYBRID, NULL);
162 }
163
164 wxRibbonToolBarToolBase* wxRibbonToolBar::AddToggleTool(
165 int tool_id,
166 const wxBitmap& bitmap,
167 const wxString& help_string)
168 {
169 return AddTool(tool_id, bitmap, wxNullBitmap, help_string,
170 wxRIBBON_BUTTON_TOGGLE, NULL);
171 }
172
173 wxRibbonToolBarToolBase* wxRibbonToolBar::AddTool(
174 int tool_id,
175 const wxBitmap& bitmap,
176 const wxBitmap& bitmap_disabled,
177 const wxString& help_string,
178 wxRibbonButtonKind kind,
179 wxObject* client_data)
180 {
181 return InsertTool(GetToolCount(), tool_id, bitmap, bitmap_disabled,
182 help_string, kind, client_data);
183 }
184
185 wxRibbonToolBarToolBase* wxRibbonToolBar::AddSeparator()
186 {
187 if(m_groups.Last()->tools.IsEmpty())
188 return NULL;
189
190 AppendGroup();
191 return &m_groups.Last()->dummy_tool;
192 }
193
194
195 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
196 size_t pos,
197 int tool_id,
198 const wxBitmap& bitmap,
199 const wxString& help_string,
200 wxRibbonButtonKind kind)
201 {
202 return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string, kind,
203 NULL);
204 }
205
206 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertDropdownTool(
207 size_t pos,
208 int tool_id,
209 const wxBitmap& bitmap,
210 const wxString& help_string)
211 {
212 return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
213 wxRIBBON_BUTTON_DROPDOWN, NULL);
214 }
215
216 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertHybridTool(
217 size_t pos,
218 int tool_id,
219 const wxBitmap& bitmap,
220 const wxString& help_string)
221 {
222 return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
223 wxRIBBON_BUTTON_HYBRID, NULL);
224 }
225
226 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertToggleTool(
227 size_t pos,
228 int tool_id,
229 const wxBitmap& bitmap,
230 const wxString& help_string)
231 {
232 return InsertTool(pos, tool_id, bitmap, wxNullBitmap, help_string,
233 wxRIBBON_BUTTON_TOGGLE, NULL);
234 }
235
236 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertTool(
237 size_t pos,
238 int tool_id,
239 const wxBitmap& bitmap,
240 const wxBitmap& bitmap_disabled,
241 const wxString& help_string,
242 wxRibbonButtonKind kind,
243 wxObject* client_data)
244 {
245 wxASSERT(bitmap.IsOk());
246
247 // Create the wxRibbonToolBarToolBase with parameters
248 wxRibbonToolBarToolBase* tool = new wxRibbonToolBarToolBase;
249 tool->id = tool_id;
250 tool->bitmap = bitmap;
251 if(bitmap_disabled.IsOk())
252 {
253 wxASSERT(bitmap.GetSize() == bitmap_disabled.GetSize());
254 tool->bitmap_disabled = bitmap_disabled;
255 }
256 else
257 tool->bitmap_disabled = MakeDisabledBitmap(bitmap);
258 tool->help_string = help_string;
259 tool->kind = kind;
260 tool->client_data = client_data;
261 tool->position = wxPoint(0, 0);
262 tool->size = wxSize(0, 0);
263 tool->state = 0;
264
265 // Find the position where insert tool
266 size_t group_count = m_groups.GetCount();
267 size_t g;
268 for(g = 0; g < group_count; ++g)
269 {
270 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
271 size_t tool_count = group->tools.GetCount();
272 if(pos <= tool_count)
273 {
274 group->tools.Insert(tool, pos);
275 return tool;
276 }
277 pos -= tool_count + 1;
278 }
279 wxFAIL_MSG("Tool position out of toolbar bounds.");
280 return NULL;
281 }
282
283 wxRibbonToolBarToolBase* wxRibbonToolBar::InsertSeparator(size_t pos)
284 {
285 size_t group_count = m_groups.GetCount();
286 size_t g;
287 for(g = 0; g < group_count; ++g)
288 {
289 if(pos==0) // Prepend group
290 return &InsertGroup(g)->dummy_tool;
291 if(pos==group_count) // Append group
292 return &InsertGroup(g+1)->dummy_tool;
293
294 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
295 size_t tool_count = group->tools.GetCount();
296 if(pos < tool_count)
297 {
298 wxRibbonToolBarToolGroup* new_group = InsertGroup(g+1);
299
300 for(size_t t = pos; t < tool_count; t++)
301 new_group->tools.Add(group->tools[t]);
302 group->tools.RemoveAt(pos, tool_count-pos);
303
304 return &group->dummy_tool;
305 }
306 pos -= tool_count + 1;
307 }
308 // Add an empty group at the end of the bar.
309 if(m_groups.Last()->tools.IsEmpty())
310 return NULL;
311 AppendGroup();
312 return &m_groups.Last()->dummy_tool;
313 }
314
315 wxRibbonToolBarToolGroup* wxRibbonToolBar::InsertGroup(size_t pos)
316 {
317 wxRibbonToolBarToolGroup* group = new wxRibbonToolBarToolGroup;
318 group->position = wxPoint(0, 0);
319 group->size = wxSize(0, 0);
320 m_groups.Insert(group, pos);
321 return group;
322 }
323
324 void wxRibbonToolBar::ClearTools()
325 {
326 size_t count = m_groups.GetCount();
327 size_t i, t;
328 for(i = 0; i < count; ++i)
329 {
330 wxRibbonToolBarToolGroup* group = m_groups.Item(i);
331 size_t tool_count = group->tools.GetCount();
332 for(t = 0; t < tool_count; ++t)
333 {
334 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
335 delete tool;
336 }
337 delete group;
338 }
339 m_groups.Clear();
340 }
341
342 bool wxRibbonToolBar::DeleteTool(int tool_id)
343 {
344 size_t group_count = m_groups.GetCount();
345 size_t g, t;
346 for(g = 0; g < group_count; ++g)
347 {
348 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
349 size_t tool_count = group->tools.GetCount();
350 for(t = 0; t < tool_count; ++t)
351 {
352 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
353 if(tool->id == tool_id)
354 {
355 group->tools.RemoveAt(t);
356 delete tool;
357 return true;
358 }
359 }
360 }
361 return false;
362 }
363
364 bool wxRibbonToolBar::DeleteToolByPos(size_t pos)
365 {
366 size_t group_count = m_groups.GetCount();
367 size_t g, t;
368 for(g = 0; g < group_count; ++g)
369 {
370 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
371 size_t tool_count = group->tools.GetCount();
372 if(pos<tool_count)
373 {
374 // Remove tool
375 wxRibbonToolBarToolBase* tool = group->tools.Item(pos);
376 group->tools.RemoveAt(pos);
377 delete tool;
378 return true;
379 }
380 else if(pos==tool_count)
381 {
382 // Remove separator
383 if(g < group_count - 1)
384 {
385 wxRibbonToolBarToolGroup* next_group = m_groups.Item(g+1);
386 for(t = 0; t < next_group->tools.GetCount(); ++t)
387 group->tools.Add(next_group->tools[t]);
388 m_groups.RemoveAt(g+1);
389 delete next_group;
390 }
391 return true;
392 }
393 }
394 return false;
395 }
396
397 wxRibbonToolBarToolBase* wxRibbonToolBar::FindById(int tool_id)const
398 {
399 size_t group_count = m_groups.GetCount();
400 size_t g, t;
401 for(g = 0; g < group_count; ++g)
402 {
403 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
404 size_t tool_count = group->tools.GetCount();
405 for(t = 0; t < tool_count; ++t)
406 {
407 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
408 if(tool->id == tool_id)
409 {
410 return tool;
411 }
412 }
413 }
414 return NULL;
415 }
416
417 wxRibbonToolBarToolBase* wxRibbonToolBar::GetToolByPos(size_t pos)const
418 {
419 size_t group_count = m_groups.GetCount();
420 size_t g;
421 for(g = 0; g < group_count; ++g)
422 {
423 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
424 size_t tool_count = group->tools.GetCount();
425 if(pos<tool_count)
426 {
427 return group->tools[pos];
428 }
429 else if(pos==tool_count)
430 {
431 return NULL;
432 }
433 }
434 return NULL;
435 }
436
437 size_t wxRibbonToolBar::GetToolCount() const
438 {
439 size_t count = 0;
440 for(size_t g = 0; g < m_groups.GetCount(); ++g)
441 {
442 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
443 count += group->tools.GetCount();
444 }
445 // There is a splitter in front of every group except for the first
446 // If only one group, no separator.
447 if(m_groups.GetCount()>1)
448 count += m_groups.GetCount() - 1;
449 return count;
450 }
451
452 int wxRibbonToolBar::GetToolId(const wxRibbonToolBarToolBase* tool)const
453 {
454 wxCHECK_MSG(tool != NULL , wxNOT_FOUND, "The tool pointer must not be NULL");
455 return tool->id;
456 }
457
458 wxObject* wxRibbonToolBar::GetToolClientData(int tool_id)const
459 {
460 wxRibbonToolBarToolBase* tool = FindById(tool_id);
461 wxCHECK_MSG(tool != NULL , NULL, "Invalid tool id");
462 return tool->client_data;
463 }
464
465 bool wxRibbonToolBar::GetToolEnabled(int tool_id)const
466 {
467 wxRibbonToolBarToolBase* tool = FindById(tool_id);
468 wxCHECK_MSG(tool != NULL , false, "Invalid tool id");
469 return (tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED) == 0;
470 }
471
472 wxString wxRibbonToolBar::GetToolHelpString(int tool_id)const
473 {
474 wxRibbonToolBarToolBase* tool = FindById(tool_id);
475 wxCHECK_MSG(tool != NULL , wxEmptyString, "Invalid tool id");
476 return tool->help_string;
477 }
478
479 wxRibbonButtonKind wxRibbonToolBar::GetToolKind(int tool_id)const
480 {
481 wxRibbonToolBarToolBase* tool = FindById(tool_id);
482 wxCHECK_MSG(tool != NULL , wxRIBBON_BUTTON_NORMAL, "Invalid tool id");
483 return tool->kind;
484 }
485
486 int wxRibbonToolBar::GetToolPos(int tool_id)const
487 {
488 size_t group_count = m_groups.GetCount();
489 size_t g, t;
490 int pos = 0;
491 for(g = 0; g < group_count; ++g)
492 {
493 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
494 size_t tool_count = group->tools.GetCount();
495 for(t = 0; t < tool_count; ++t)
496 {
497 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
498 if(tool->id == tool_id)
499 {
500 return pos;
501 }
502 ++pos;
503 }
504 ++pos; // Increment pos for group separator.
505 }
506 return wxNOT_FOUND;
507 }
508
509 bool wxRibbonToolBar::GetToolState(int tool_id)const
510 {
511 wxRibbonToolBarToolBase* tool = FindById(tool_id);
512 wxCHECK_MSG(tool != NULL , false, "Invalid tool id");
513 return (tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) != 0;
514 }
515
516 wxBitmap wxRibbonToolBar::MakeDisabledBitmap(const wxBitmap& original)
517 {
518 wxImage img(original.ConvertToImage());
519 return wxBitmap(img.ConvertToGreyscale());
520 }
521
522 void wxRibbonToolBar::AppendGroup()
523 {
524 wxRibbonToolBarToolGroup* group = new wxRibbonToolBarToolGroup;
525 group->position = wxPoint(0, 0);
526 group->size = wxSize(0, 0);
527 m_groups.Add(group);
528 }
529
530 bool wxRibbonToolBar::IsSizingContinuous() const
531 {
532 return false;
533 }
534
535 void wxRibbonToolBar::SetToolClientData(int tool_id, wxObject* clientData)
536 {
537 wxRibbonToolBarToolBase* tool = FindById(tool_id);
538 wxCHECK_RET(tool != NULL , "Invalid tool id");
539 tool->client_data = clientData;
540 }
541
542 void wxRibbonToolBar::SetToolDisabledBitmap(int tool_id, const wxBitmap &bitmap)
543 {
544 wxRibbonToolBarToolBase* tool = FindById(tool_id);
545 wxCHECK_RET(tool != NULL , "Invalid tool id");
546 tool->bitmap_disabled = bitmap;
547 }
548
549 void wxRibbonToolBar::SetToolHelpString(int tool_id, const wxString& helpString)
550 {
551 wxRibbonToolBarToolBase* tool = FindById(tool_id);
552 wxCHECK_RET(tool != NULL , "Invalid tool id");
553 tool->help_string = helpString;
554 }
555
556 void wxRibbonToolBar::SetToolNormalBitmap(int tool_id, const wxBitmap &bitmap)
557 {
558 wxRibbonToolBarToolBase* tool = FindById(tool_id);
559 wxCHECK_RET(tool != NULL , "Invalid tool id");
560 tool->bitmap = bitmap;
561 }
562
563 void wxRibbonToolBar::EnableTool(int tool_id, bool enable)
564 {
565 wxRibbonToolBarToolBase* tool = FindById(tool_id);
566 wxCHECK_RET(tool != NULL , "Invalid tool id");
567 if(enable)
568 {
569 if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
570 {
571 tool->state &= ~wxRIBBON_TOOLBAR_TOOL_DISABLED;
572 Refresh();
573 }
574 }
575 else
576 {
577 if((tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)==0)
578 {
579 tool->state |= wxRIBBON_TOOLBAR_TOOL_DISABLED;
580 Refresh();
581 }
582 }
583 }
584
585 void wxRibbonToolBar::ToggleTool(int tool_id, bool checked)
586 {
587 wxRibbonToolBarToolBase* tool = FindById(tool_id);
588 wxCHECK_RET(tool != NULL , "Invalid tool id");
589 if(checked)
590 {
591 if((tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED) == 0)
592 {
593 tool->state |= wxRIBBON_TOOLBAR_TOOL_TOGGLED;
594 Refresh();
595 }
596 }
597 else
598 {
599 if(tool->state & wxRIBBON_TOOLBAR_TOOL_TOGGLED)
600 {
601 tool->state &= ~wxRIBBON_TOOLBAR_TOOL_TOGGLED;
602 Refresh();
603 }
604 }
605 }
606
607 static int GetSizeInOrientation(wxSize size, wxOrientation orientation)
608 {
609 switch(orientation)
610 {
611 case wxHORIZONTAL: return size.GetWidth();
612 case wxVERTICAL: return size.GetHeight();
613 case wxBOTH: return size.GetWidth() * size.GetHeight();
614 default: return 0;
615 }
616 }
617
618 wxSize wxRibbonToolBar::DoGetNextSmallerSize(wxOrientation direction,
619 wxSize relative_to) const
620 {
621 wxSize result(relative_to);
622 int area = 0;
623 int nrows;
624 for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
625 {
626 wxSize size(m_sizes[nrows - m_nrows_min]);
627 wxSize original(size);
628 switch(direction)
629 {
630 case wxHORIZONTAL:
631 if(size.GetWidth() < relative_to.GetWidth()
632 && size.GetHeight() <= relative_to.GetHeight())
633 {
634 size.SetHeight(relative_to.GetHeight());
635 break;
636 }
637 continue;
638 case wxVERTICAL:
639 if(size.GetWidth() <= relative_to.GetWidth()
640 && size.GetHeight() < relative_to.GetHeight())
641 {
642 size.SetWidth(relative_to.GetWidth());
643 break;
644 }
645 continue;
646 case wxBOTH:
647 if(size.GetWidth() < relative_to.GetWidth()
648 && size.GetHeight() < relative_to.GetHeight())
649 {
650 break;
651 }
652 continue;
653 }
654 if(GetSizeInOrientation(original, direction) > area)
655 {
656 result = size;
657 area = GetSizeInOrientation(original, direction);
658 }
659 }
660 return result;
661 }
662
663 wxSize wxRibbonToolBar::DoGetNextLargerSize(wxOrientation direction,
664 wxSize relative_to) const
665 {
666 // Pick the smallest of our sizes which are larger than the given size
667 wxSize result(relative_to);
668 int area = INT_MAX;
669 int nrows;
670 for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
671 {
672 wxSize size(m_sizes[nrows - m_nrows_min]);
673 wxSize original(size);
674 switch(direction)
675 {
676 case wxHORIZONTAL:
677 if(size.GetWidth() > relative_to.GetWidth()
678 && size.GetHeight() <= relative_to.GetHeight())
679 {
680 size.SetHeight(relative_to.GetHeight());
681 break;
682 }
683 continue;
684 case wxVERTICAL:
685 if(size.GetWidth() <= relative_to.GetWidth()
686 && size.GetHeight() > relative_to.GetHeight())
687 {
688 size.SetWidth(relative_to.GetWidth());
689 break;
690 }
691 continue;
692 case wxBOTH:
693 if(size.GetWidth() > relative_to.GetWidth()
694 && size.GetHeight() > relative_to.GetHeight())
695 {
696 break;
697 }
698 continue;
699 }
700 if(GetSizeInOrientation(original, direction) < area)
701 {
702 result = size;
703 area = GetSizeInOrientation(original, direction);
704 }
705 }
706
707 return result;
708 }
709
710 void wxRibbonToolBar::SetRows(int nMin, int nMax)
711 {
712 if(nMax == -1)
713 nMax = nMin;
714
715 wxASSERT(1 <= nMin);
716 wxASSERT(nMin <= nMax);
717
718 m_nrows_min = nMin;
719 m_nrows_max = nMax;
720
721 delete[] m_sizes;
722 m_sizes = new wxSize[m_nrows_max - m_nrows_min + 1];
723 for(int i = m_nrows_min; i <= m_nrows_max; ++i)
724 m_sizes[i - m_nrows_min] = wxSize(0, 0);
725
726 Realize();
727 }
728
729 bool wxRibbonToolBar::Realize()
730 {
731 if(m_art == NULL)
732 return false;
733
734 // Calculate the size of each group and the position/size of each tool
735 wxMemoryDC temp_dc;
736 size_t group_count = m_groups.GetCount();
737 size_t g, t;
738 for(g = 0; g < group_count; ++g)
739 {
740 wxRibbonToolBarToolBase* prev = NULL;
741 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
742 size_t tool_count = group->tools.GetCount();
743 int tallest = 0;
744 for(t = 0; t < tool_count; ++t)
745 {
746 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
747 tool->size = m_art->GetToolSize(temp_dc, this,
748 tool->bitmap.GetSize(), tool->kind, t == 0,
749 t == (tool_count - 1), &tool->dropdown);
750 if(t == 0)
751 tool->state |= wxRIBBON_TOOLBAR_TOOL_FIRST;
752 if(t == tool_count - 1)
753 tool->state |= wxRIBBON_TOOLBAR_TOOL_LAST;
754 if(tool->size.GetHeight() > tallest)
755 tallest = tool->size.GetHeight();
756 if(prev)
757 {
758 tool->position = prev->position;
759 tool->position.x += prev->size.x;
760 }
761 else
762 {
763 tool->position = wxPoint(0, 0);
764 }
765 prev = tool;
766 }
767 if(tool_count == 0)
768 group->size = wxSize(0, 0);
769 else
770 {
771 group->size = wxSize(prev->position.x + prev->size.x, tallest);
772 for(t = 0; t < tool_count; ++t)
773 group->tools.Item(t)->size.SetHeight(tallest);
774 }
775 }
776
777 // Calculate the minimum size for each possible number of rows
778 int nrows, r;
779 int sep = m_art->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE);
780 int smallest_area = INT_MAX;
781 wxSize* row_sizes = new wxSize[m_nrows_max];
782 wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
783 wxVERTICAL : wxHORIZONTAL;
784 SetMinSize(wxSize(0, 0));
785 for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
786 {
787 for(r = 0; r < nrows; ++r)
788 row_sizes[r] = wxSize(0, 0);
789 for(g = 0; g < group_count; ++g)
790 {
791 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
792 int shortest_row = 0;
793 for(r = 1; r < nrows; ++r)
794 {
795 if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
796 shortest_row = r;
797 }
798 row_sizes[shortest_row].x += group->size.x + sep;
799 if(group->size.y > row_sizes[shortest_row].y)
800 row_sizes[shortest_row].y = group->size.y;
801 }
802 wxSize size(0, 0);
803 for(r = 0; r < nrows; ++r)
804 {
805 if(row_sizes[r].GetWidth() != 0)
806 row_sizes[r].DecBy(sep, 0);
807 if(row_sizes[r].GetWidth() > size.GetWidth())
808 size.SetWidth(row_sizes[r].GetWidth());
809 size.IncBy(0, row_sizes[r].y);
810 }
811 m_sizes[nrows - m_nrows_min] = size;
812 if(GetSizeInOrientation(size, major_axis) < smallest_area)
813 {
814 SetMinSize(size);
815 smallest_area = GetSizeInOrientation(size, major_axis);
816 }
817 }
818 delete[] row_sizes;
819
820 // Position the groups
821 wxSizeEvent dummy_event(GetSize());
822 OnSize(dummy_event);
823
824 return true;
825 }
826
827 void wxRibbonToolBar::OnSize(wxSizeEvent& evt)
828 {
829 if(m_art == NULL)
830 return;
831
832 // Choose row count with largest possible area
833 wxSize size = evt.GetSize();
834 int row_count = m_nrows_max;
835 wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
836 wxVERTICAL : wxHORIZONTAL;
837 if(m_nrows_max != m_nrows_min)
838 {
839 int area = 0;
840 for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
841 {
842 if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
843 GetSizeInOrientation(m_sizes[i], major_axis) > area)
844 {
845 area = GetSizeInOrientation(m_sizes[i], major_axis);
846 row_count = m_nrows_min + i;
847 }
848 }
849 }
850
851 // Assign groups to rows and calculate row widths
852 wxSize* row_sizes = new wxSize[row_count];
853 int sep = m_art->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE);
854
855 int r;
856 for(r = 0; r < row_count; ++r)
857 row_sizes[r] = wxSize(0, 0);
858 size_t g;
859 size_t group_count = m_groups.GetCount();
860 for(g = 0; g < group_count; ++g)
861 {
862 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
863 int shortest_row = 0;
864 for(r = 1; r < row_count; ++r)
865 {
866 if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
867 shortest_row = r;
868 }
869 group->position = wxPoint(row_sizes[shortest_row].x, shortest_row);
870 row_sizes[shortest_row].x += group->size.x + sep;
871 if(group->size.y > row_sizes[shortest_row].y)
872 row_sizes[shortest_row].y = group->size.y;
873 }
874
875 // Calculate row positions
876 int total_height = 0;
877 for(r = 0; r < row_count; ++r)
878 total_height += row_sizes[r].GetHeight();
879 int rowsep = (size.GetHeight() - total_height) / (row_count + 1);
880 int* rowypos = new int[row_count];
881 rowypos[0] = rowsep;
882 for(r = 1; r < row_count; ++r)
883 {
884 rowypos[r] = rowypos[r - 1] + row_sizes[r - 1].GetHeight() + rowsep;
885 }
886
887 // Set group y positions
888 for(g = 0; g < group_count; ++g)
889 {
890 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
891 group->position.y = rowypos[group->position.y];
892 }
893
894 delete[] rowypos;
895 delete[] row_sizes;
896 }
897
898 wxSize wxRibbonToolBar::DoGetBestSize() const
899 {
900 return GetMinSize();
901 }
902
903 void wxRibbonToolBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
904 {
905 // All painting done in main paint handler to minimise flicker
906 }
907
908 void wxRibbonToolBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
909 {
910 wxAutoBufferedPaintDC dc(this);
911 if(m_art == NULL)
912 return;
913
914 m_art->DrawToolBarBackground(dc, this, GetSize());
915
916 size_t group_count = m_groups.GetCount();
917 size_t g, t;
918 for(g = 0; g < group_count; ++g)
919 {
920 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
921 size_t tool_count = group->tools.GetCount();
922 if(tool_count != 0)
923 {
924 m_art->DrawToolGroupBackground(dc, this,
925 wxRect(group->position, group->size));
926 for(t = 0; t < tool_count; ++t)
927 {
928 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
929 wxRect rect(group->position + tool->position, tool->size);
930 if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
931 m_art->DrawTool(dc, this, rect, tool->bitmap_disabled,
932 tool->kind, tool->state);
933 else
934 m_art->DrawTool(dc, this, rect, tool->bitmap, tool->kind,
935 tool->state);
936 }
937 }
938 }
939 }
940
941 void wxRibbonToolBar::OnMouseMove(wxMouseEvent& evt)
942 {
943 wxPoint pos(evt.GetPosition());
944 wxRibbonToolBarToolBase *new_hover = NULL;
945
946 size_t group_count = m_groups.GetCount();
947 size_t g, t;
948 for(g = 0; g < group_count; ++g)
949 {
950 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
951 if(group->position.x <= pos.x && pos.x < group->position.x + group->size.x
952 && group->position.y <= pos.y && pos.y < group->position.y + group->size.y)
953 {
954 size_t tool_count = group->tools.GetCount();
955 pos -= group->position;
956 for(t = 0; t < tool_count; ++t)
957 {
958 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
959 if(tool->position.x <= pos.x && pos.x < tool->position.x + tool->size.x
960 && tool->position.y <= pos.y && pos.y < tool->position.y + tool->size.y)
961 {
962 pos -= tool->position;
963 new_hover = tool;
964 break;
965 }
966 }
967 break;
968 }
969 }
970
971 #if wxUSE_TOOLTIPS
972 if(new_hover)
973 {
974 SetToolTip(new_hover->help_string);
975 }
976 else if(GetToolTip())
977 {
978 UnsetToolTip();
979 }
980 #endif
981
982 if(new_hover && new_hover->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
983 {
984 new_hover = NULL; // A disabled tool can not be hilighted
985 }
986
987 if(new_hover != m_hover_tool)
988 {
989 if(m_hover_tool)
990 {
991 m_hover_tool->state &= ~(wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
992 | wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK);
993 }
994 m_hover_tool = new_hover;
995 if(new_hover)
996 {
997 long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
998 if(new_hover->dropdown.Contains(pos))
999 what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
1000
1001 new_hover->state |= what;
1002
1003 if(new_hover == m_active_tool)
1004 {
1005 new_hover->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1006 new_hover->state |= (what << 2);
1007 }
1008 }
1009 Refresh(false);
1010 }
1011 else if(m_hover_tool && m_hover_tool->kind == wxRIBBON_BUTTON_HYBRID)
1012 {
1013 long newstate = m_hover_tool->state &~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
1014 long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
1015 if(m_hover_tool->dropdown.Contains(pos))
1016 what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
1017 newstate |= what;
1018 if(newstate != m_hover_tool->state)
1019 {
1020 m_hover_tool->state = newstate;
1021 if(m_hover_tool == m_active_tool)
1022 {
1023 m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1024 m_hover_tool->state |= (what << 2);
1025 }
1026 Refresh(false);
1027 }
1028 }
1029 }
1030
1031 void wxRibbonToolBar::OnMouseDown(wxMouseEvent& evt)
1032 {
1033 OnMouseMove(evt);
1034 if(m_hover_tool)
1035 {
1036 m_active_tool = m_hover_tool;
1037 m_active_tool->state |=
1038 (m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_HOVER_MASK) << 2;
1039 Refresh(false);
1040 }
1041 }
1042
1043 void wxRibbonToolBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
1044 {
1045 if(m_hover_tool)
1046 {
1047 m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
1048 m_hover_tool = NULL;
1049 Refresh(false);
1050 }
1051 }
1052
1053 void wxRibbonToolBar::OnMouseUp(wxMouseEvent& WXUNUSED(evt))
1054 {
1055 if(m_active_tool)
1056 {
1057 if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK)
1058 {
1059 wxEventType evt_type = wxEVT_COMMAND_RIBBONTOOL_CLICKED;
1060 if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE)
1061 evt_type = wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED;
1062 wxRibbonToolBarEvent notification(evt_type, m_active_tool->id);
1063 if(m_active_tool->kind == wxRIBBON_BUTTON_TOGGLE)
1064 {
1065 m_active_tool->state ^=
1066 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
1067 notification.SetInt(m_active_tool->state &
1068 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
1069 }
1070 notification.SetEventObject(this);
1071 notification.SetBar(this);
1072 ProcessEvent(notification);
1073 }
1074
1075 // Notice that m_active_tool could have been reset by the event handler
1076 // above so we need to test it again.
1077 if (m_active_tool)
1078 {
1079 m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1080 m_active_tool = NULL;
1081 Refresh(false);
1082 }
1083 }
1084 }
1085
1086 void wxRibbonToolBar::OnMouseEnter(wxMouseEvent& evt)
1087 {
1088 if(m_active_tool && !evt.LeftIsDown())
1089 {
1090 m_active_tool = NULL;
1091 }
1092 }
1093
1094 void wxRibbonToolBar::UpdateWindowUI(long flags)
1095 {
1096 wxWindowBase::UpdateWindowUI(flags);
1097
1098 // don't waste time updating state of tools in a hidden toolbar
1099 if ( !IsShown() )
1100 return;
1101
1102 size_t group_count = m_groups.GetCount();
1103 size_t g, t;
1104 for(g = 0; g < group_count; ++g)
1105 {
1106 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
1107 size_t tool_count = group->tools.GetCount();
1108 for(t = 0; t < tool_count; ++t)
1109 {
1110 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1111 int id = tool->id;
1112
1113 wxUpdateUIEvent event(id);
1114 event.SetEventObject(this);
1115
1116 if ( ProcessWindowEvent(event) )
1117 {
1118 if ( event.GetSetEnabled() )
1119 EnableTool(id, event.GetEnabled());
1120 if ( event.GetSetChecked() )
1121 ToggleTool(id, event.GetChecked());
1122 }
1123 }
1124 }
1125 }
1126
1127 bool wxRibbonToolBarEvent::PopupMenu(wxMenu* menu)
1128 {
1129 wxPoint pos = wxDefaultPosition;
1130 if(m_bar->m_active_tool)
1131 {
1132 // Find the group which contains the tool
1133 size_t group_count = m_bar->m_groups.GetCount();
1134 size_t g, t;
1135 for(g = 0; g < group_count; ++g)
1136 {
1137 wxRibbonToolBarToolGroup* group = m_bar->m_groups.Item(g);
1138 size_t tool_count = group->tools.GetCount();
1139 for(t = 0; t < tool_count; ++t)
1140 {
1141 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1142 if(tool == m_bar->m_active_tool)
1143 {
1144 pos = group->position;
1145 pos += tool->position;
1146 pos.y += tool->size.GetHeight();
1147 g = group_count;
1148 break;
1149 }
1150 }
1151 }
1152 }
1153 return m_bar->PopupMenu(menu, pos);
1154 }
1155
1156 #endif // wxUSE_RIBBON