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