]> git.saurik.com Git - wxWidgets.git/blob - src/ribbon/toolbar.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[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
785 SetMinSize(wxSize(0, 0));
786 wxSize minSize(INT_MAX, INT_MAX);
787
788 // See if we're sizing flexibly (i.e. wrapping), and set min size differently
789 bool sizingFlexibly = false;
790 wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
791 if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
792 sizingFlexibly = true;
793
794 // Without this, there will be redundant horizontal space because SetMinSize will
795 // use the smallest possible height (and therefore largest width).
796 if (sizingFlexibly)
797 major_axis = wxHORIZONTAL;
798
799 for(nrows = m_nrows_min; nrows <= m_nrows_max; ++nrows)
800 {
801 for(r = 0; r < nrows; ++r)
802 row_sizes[r] = wxSize(0, 0);
803 for(g = 0; g < group_count; ++g)
804 {
805 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
806 int shortest_row = 0;
807 for(r = 1; r < nrows; ++r)
808 {
809 if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
810 shortest_row = r;
811 }
812 row_sizes[shortest_row].x += group->size.x + sep;
813 if(group->size.y > row_sizes[shortest_row].y)
814 row_sizes[shortest_row].y = group->size.y;
815 }
816 wxSize size(0, 0);
817 for(r = 0; r < nrows; ++r)
818 {
819 if(row_sizes[r].GetWidth() != 0)
820 row_sizes[r].DecBy(sep, 0);
821 if(row_sizes[r].GetWidth() > size.GetWidth())
822 size.SetWidth(row_sizes[r].GetWidth());
823 size.IncBy(0, row_sizes[r].y);
824 }
825 m_sizes[nrows - m_nrows_min] = size;
826
827 if(GetSizeInOrientation(size, major_axis) < smallest_area)
828 {
829 smallest_area = GetSizeInOrientation(size, major_axis);
830 SetMinSize(size);
831 }
832
833 if (sizingFlexibly)
834 {
835 if (size.x < minSize.x)
836 minSize.x = size.x;
837 if (size.y < minSize.y)
838 minSize.y = size.y;
839 }
840 }
841
842 if (sizingFlexibly)
843 {
844 // Give it the min size in either direction regardless of row,
845 // so that we're able to vary the size of the panel according to
846 // the space the toolbar takes up.
847 SetMinSize(minSize);
848 }
849 delete[] row_sizes;
850
851 // Position the groups
852 wxSizeEvent dummy_event(GetSize());
853 OnSize(dummy_event);
854
855 return true;
856 }
857
858 void wxRibbonToolBar::OnSize(wxSizeEvent& evt)
859 {
860 if(m_art == NULL)
861 return;
862
863 // Choose row count with largest possible area
864 wxSize size = evt.GetSize();
865 int row_count = m_nrows_max;
866 wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
867 wxVERTICAL : wxHORIZONTAL;
868
869 // See if we're sizing flexibly, and set min size differently
870 bool sizingFlexibly = false;
871 wxRibbonPanel* panel = wxDynamicCast(GetParent(), wxRibbonPanel);
872 if (panel && (panel->GetFlags() & wxRIBBON_PANEL_FLEXIBLE))
873 sizingFlexibly = true;
874
875 // Without this, there will be redundant horizontal space because SetMinSize will
876 // use the smallest possible height (and therefore largest width).
877 if (sizingFlexibly)
878 major_axis = wxHORIZONTAL;
879
880 wxSize bestSize = m_sizes[0];
881
882 if(m_nrows_max != m_nrows_min)
883 {
884 int area = 0;
885 for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
886 {
887 if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
888 GetSizeInOrientation(m_sizes[i], major_axis) > area)
889 {
890 area = GetSizeInOrientation(m_sizes[i], major_axis);
891 row_count = m_nrows_min + i;
892 bestSize = m_sizes[i];
893 }
894 }
895 }
896
897 // Assign groups to rows and calculate row widths
898 wxSize* row_sizes = new wxSize[row_count];
899 int sep = m_art->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE);
900
901 int r;
902 for(r = 0; r < row_count; ++r)
903 row_sizes[r] = wxSize(0, 0);
904 size_t g;
905 size_t group_count = m_groups.GetCount();
906 for(g = 0; g < group_count; ++g)
907 {
908 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
909 int shortest_row = 0;
910 for(r = 1; r < row_count; ++r)
911 {
912 if(row_sizes[r].GetWidth() < row_sizes[shortest_row].GetWidth())
913 shortest_row = r;
914 }
915 group->position = wxPoint(row_sizes[shortest_row].x, shortest_row);
916 row_sizes[shortest_row].x += group->size.x + sep;
917 if(group->size.y > row_sizes[shortest_row].y)
918 row_sizes[shortest_row].y = group->size.y;
919 }
920
921 // Calculate row positions
922 int total_height = 0;
923 for(r = 0; r < row_count; ++r)
924 total_height += row_sizes[r].GetHeight();
925 int rowsep = (size.GetHeight() - total_height) / (row_count + 1);
926 int* rowypos = new int[row_count];
927 rowypos[0] = rowsep;
928 for(r = 1; r < row_count; ++r)
929 {
930 rowypos[r] = rowypos[r - 1] + row_sizes[r - 1].GetHeight() + rowsep;
931 }
932
933 // Set group y positions
934 for(g = 0; g < group_count; ++g)
935 {
936 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
937 group->position.y = rowypos[group->position.y];
938 }
939
940 delete[] rowypos;
941 delete[] row_sizes;
942 }
943
944 // Finds the best width and height given the parents' width and height
945 wxSize wxRibbonToolBar::GetBestSizeForParentSize(const wxSize& parentSize) const
946 {
947 if (!m_sizes)
948 return GetMinSize();
949
950 // Choose row count with largest possible area
951 wxSize size = parentSize;
952 wxOrientation major_axis = m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL ?
953 wxVERTICAL : wxHORIZONTAL;
954
955 // A toolbar should maximize its width whether vertical or horizontal, so
956 // force the major axis to be horizontal. Without this, there will be
957 // redundant horizontal space.
958 major_axis = wxHORIZONTAL;
959 wxSize bestSize = m_sizes[0];
960
961 if(m_nrows_max != m_nrows_min)
962 {
963 int area = 0;
964 for(int i = 0; i <= m_nrows_max - m_nrows_min; ++i)
965 {
966 if(m_sizes[i].x <= size.x && m_sizes[i].y <= size.y &&
967 GetSizeInOrientation(m_sizes[i], major_axis) > area)
968 {
969 area = GetSizeInOrientation(m_sizes[i], major_axis);
970 bestSize = m_sizes[i];
971 }
972 }
973 }
974 return bestSize;
975 }
976
977 wxSize wxRibbonToolBar::DoGetBestSize() const
978 {
979 return GetMinSize();
980 }
981
982 void wxRibbonToolBar::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
983 {
984 // All painting done in main paint handler to minimise flicker
985 }
986
987 void wxRibbonToolBar::OnPaint(wxPaintEvent& WXUNUSED(evt))
988 {
989 wxAutoBufferedPaintDC dc(this);
990 if(m_art == NULL)
991 return;
992
993 m_art->DrawToolBarBackground(dc, this, GetSize());
994
995 size_t group_count = m_groups.GetCount();
996 size_t g, t;
997 for(g = 0; g < group_count; ++g)
998 {
999 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
1000 size_t tool_count = group->tools.GetCount();
1001 if(tool_count != 0)
1002 {
1003 m_art->DrawToolGroupBackground(dc, this,
1004 wxRect(group->position, group->size));
1005 for(t = 0; t < tool_count; ++t)
1006 {
1007 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1008 wxRect rect(group->position + tool->position, tool->size);
1009 if(tool->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
1010 m_art->DrawTool(dc, this, rect, tool->bitmap_disabled,
1011 tool->kind, tool->state);
1012 else
1013 m_art->DrawTool(dc, this, rect, tool->bitmap, tool->kind,
1014 tool->state);
1015 }
1016 }
1017 }
1018 }
1019
1020 void wxRibbonToolBar::OnMouseMove(wxMouseEvent& evt)
1021 {
1022 wxPoint pos(evt.GetPosition());
1023 wxRibbonToolBarToolBase *new_hover = NULL;
1024
1025 size_t group_count = m_groups.GetCount();
1026 size_t g, t;
1027 for(g = 0; g < group_count; ++g)
1028 {
1029 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
1030 if(group->position.x <= pos.x && pos.x < group->position.x + group->size.x
1031 && group->position.y <= pos.y && pos.y < group->position.y + group->size.y)
1032 {
1033 size_t tool_count = group->tools.GetCount();
1034 pos -= group->position;
1035 for(t = 0; t < tool_count; ++t)
1036 {
1037 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1038 if(tool->position.x <= pos.x && pos.x < tool->position.x + tool->size.x
1039 && tool->position.y <= pos.y && pos.y < tool->position.y + tool->size.y)
1040 {
1041 pos -= tool->position;
1042 new_hover = tool;
1043 break;
1044 }
1045 }
1046 break;
1047 }
1048 }
1049
1050 #if wxUSE_TOOLTIPS
1051 if(new_hover)
1052 {
1053 SetToolTip(new_hover->help_string);
1054 }
1055 else if(GetToolTip())
1056 {
1057 UnsetToolTip();
1058 }
1059 #endif
1060
1061 if(new_hover && new_hover->state & wxRIBBON_TOOLBAR_TOOL_DISABLED)
1062 {
1063 new_hover = NULL; // A disabled tool can not be hilighted
1064 }
1065
1066 if(new_hover != m_hover_tool)
1067 {
1068 if(m_hover_tool)
1069 {
1070 m_hover_tool->state &= ~(wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
1071 | wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK);
1072 }
1073 m_hover_tool = new_hover;
1074 if(new_hover)
1075 {
1076 long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
1077 if(new_hover->dropdown.Contains(pos))
1078 what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
1079
1080 new_hover->state |= what;
1081
1082 if(new_hover == m_active_tool)
1083 {
1084 new_hover->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1085 new_hover->state |= (what << 2);
1086 }
1087 }
1088 Refresh(false);
1089 }
1090 else if(m_hover_tool && m_hover_tool->kind == wxRIBBON_BUTTON_HYBRID)
1091 {
1092 long newstate = m_hover_tool->state &~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
1093 long what = wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED;
1094 if(m_hover_tool->dropdown.Contains(pos))
1095 what = wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED;
1096 newstate |= what;
1097 if(newstate != m_hover_tool->state)
1098 {
1099 m_hover_tool->state = newstate;
1100 if(m_hover_tool == m_active_tool)
1101 {
1102 m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1103 m_hover_tool->state |= (what << 2);
1104 }
1105 Refresh(false);
1106 }
1107 }
1108 }
1109
1110 void wxRibbonToolBar::OnMouseDown(wxMouseEvent& evt)
1111 {
1112 OnMouseMove(evt);
1113 if(m_hover_tool)
1114 {
1115 m_active_tool = m_hover_tool;
1116 m_active_tool->state |=
1117 (m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_HOVER_MASK) << 2;
1118 Refresh(false);
1119 }
1120 }
1121
1122 void wxRibbonToolBar::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
1123 {
1124 if(m_hover_tool)
1125 {
1126 m_hover_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK;
1127 m_hover_tool = NULL;
1128 Refresh(false);
1129 }
1130 }
1131
1132 void wxRibbonToolBar::OnMouseUp(wxMouseEvent& WXUNUSED(evt))
1133 {
1134 if(m_active_tool)
1135 {
1136 if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK)
1137 {
1138 wxEventType evt_type = wxEVT_COMMAND_RIBBONTOOL_CLICKED;
1139 if(m_active_tool->state & wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE)
1140 evt_type = wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED;
1141 wxRibbonToolBarEvent notification(evt_type, m_active_tool->id);
1142 if(m_active_tool->kind == wxRIBBON_BUTTON_TOGGLE)
1143 {
1144 m_active_tool->state ^=
1145 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED;
1146 notification.SetInt(m_active_tool->state &
1147 wxRIBBON_BUTTONBAR_BUTTON_TOGGLED);
1148 }
1149 notification.SetEventObject(this);
1150 notification.SetBar(this);
1151 ProcessEvent(notification);
1152
1153 wxStaticCast(m_parent, wxRibbonPanel)->HideIfExpanded();
1154 }
1155
1156 // Notice that m_active_tool could have been reset by the event handler
1157 // above so we need to test it again.
1158 if (m_active_tool)
1159 {
1160 m_active_tool->state &= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK;
1161 m_active_tool = NULL;
1162 Refresh(false);
1163 }
1164 }
1165 }
1166
1167 void wxRibbonToolBar::OnMouseEnter(wxMouseEvent& evt)
1168 {
1169 if(m_active_tool && !evt.LeftIsDown())
1170 {
1171 m_active_tool = NULL;
1172 }
1173 }
1174
1175 void wxRibbonToolBar::UpdateWindowUI(long flags)
1176 {
1177 wxWindowBase::UpdateWindowUI(flags);
1178
1179 // don't waste time updating state of tools in a hidden toolbar
1180 if ( !IsShown() )
1181 return;
1182
1183 size_t group_count = m_groups.GetCount();
1184 size_t g, t;
1185 for(g = 0; g < group_count; ++g)
1186 {
1187 wxRibbonToolBarToolGroup* group = m_groups.Item(g);
1188 size_t tool_count = group->tools.GetCount();
1189 for(t = 0; t < tool_count; ++t)
1190 {
1191 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1192 int id = tool->id;
1193
1194 wxUpdateUIEvent event(id);
1195 event.SetEventObject(this);
1196
1197 if ( ProcessWindowEvent(event) )
1198 {
1199 if ( event.GetSetEnabled() )
1200 EnableTool(id, event.GetEnabled());
1201 if ( event.GetSetChecked() )
1202 ToggleTool(id, event.GetChecked());
1203 }
1204 }
1205 }
1206 }
1207
1208 bool wxRibbonToolBarEvent::PopupMenu(wxMenu* menu)
1209 {
1210 wxPoint pos = wxDefaultPosition;
1211 if(m_bar->m_active_tool)
1212 {
1213 // Find the group which contains the tool
1214 size_t group_count = m_bar->m_groups.GetCount();
1215 size_t g, t;
1216 for(g = 0; g < group_count; ++g)
1217 {
1218 wxRibbonToolBarToolGroup* group = m_bar->m_groups.Item(g);
1219 size_t tool_count = group->tools.GetCount();
1220 for(t = 0; t < tool_count; ++t)
1221 {
1222 wxRibbonToolBarToolBase* tool = group->tools.Item(t);
1223 if(tool == m_bar->m_active_tool)
1224 {
1225 pos = group->position;
1226 pos += tool->position;
1227 pos.y += tool->size.GetHeight();
1228 g = group_count;
1229 break;
1230 }
1231 }
1232 }
1233 }
1234 return m_bar->PopupMenu(menu, pos);
1235 }
1236
1237 #endif // wxUSE_RIBBON