1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/toolbar.cpp
3 // Purpose: Ribbon-style tool bar
4 // Author: Peter Cawley
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
18 #include "wx/ribbon/toolbar.h"
22 #include "wx/ribbon/art.h"
23 #include "wx/ribbon/bar.h"
24 #include "wx/dcbuffer.h"
30 #include "wx/msw/private.h"
33 class wxRibbonToolBarToolBase
38 wxBitmap bitmap_disabled
;
42 wxObject
* client_data
;
44 wxRibbonButtonKind kind
;
48 WX_DEFINE_ARRAY(wxRibbonToolBarToolBase
*, wxArrayRibbonToolBarToolBase
);
50 class wxRibbonToolBarToolGroup
53 // To identify the group as a wxRibbonToolBarToolBase*
54 wxRibbonToolBarToolBase dummy_tool
;
56 wxArrayRibbonToolBarToolBase tools
;
61 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_CLICKED
, wxRibbonToolBarEvent
);
62 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED
, wxRibbonToolBarEvent
);
64 IMPLEMENT_DYNAMIC_CLASS(wxRibbonToolBarEvent
, wxCommandEvent
)
65 IMPLEMENT_CLASS(wxRibbonToolBar
, wxRibbonControl
)
67 BEGIN_EVENT_TABLE(wxRibbonToolBar
, wxRibbonControl
)
68 EVT_ENTER_WINDOW(wxRibbonToolBar::OnMouseEnter
)
69 EVT_ERASE_BACKGROUND(wxRibbonToolBar::OnEraseBackground
)
70 EVT_LEAVE_WINDOW(wxRibbonToolBar::OnMouseLeave
)
71 EVT_LEFT_DOWN(wxRibbonToolBar::OnMouseDown
)
72 EVT_LEFT_UP(wxRibbonToolBar::OnMouseUp
)
73 EVT_MOTION(wxRibbonToolBar::OnMouseMove
)
74 EVT_PAINT(wxRibbonToolBar::OnPaint
)
75 EVT_SIZE(wxRibbonToolBar::OnSize
)
78 wxRibbonToolBar::wxRibbonToolBar()
82 wxRibbonToolBar::wxRibbonToolBar(wxWindow
* parent
,
87 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
92 bool wxRibbonToolBar::Create(wxWindow
* parent
,
98 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
107 void wxRibbonToolBar::CommonInit(long WXUNUSED(style
))
111 m_active_tool
= NULL
;
114 m_sizes
= new wxSize
[1];
115 m_sizes
[0] = wxSize(0, 0);
116 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
119 wxRibbonToolBar::~wxRibbonToolBar()
121 size_t count
= m_groups
.GetCount();
123 for(i
= 0; i
< count
; ++i
)
125 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(i
);
126 size_t tool_count
= group
->tools
.GetCount();
127 for(t
= 0; t
< tool_count
; ++t
)
129 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
138 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddTool(
140 const wxBitmap
& bitmap
,
141 const wxString
& help_string
,
142 wxRibbonButtonKind kind
)
144 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
, kind
, NULL
);
147 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddDropdownTool(
149 const wxBitmap
& bitmap
,
150 const wxString
& help_string
)
152 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
,
153 wxRIBBON_BUTTON_DROPDOWN
, NULL
);
156 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddHybridTool(
158 const wxBitmap
& bitmap
,
159 const wxString
& help_string
)
161 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
,
162 wxRIBBON_BUTTON_HYBRID
, NULL
);
165 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddTool(
167 const wxBitmap
& bitmap
,
168 const wxBitmap
& bitmap_disabled
,
169 const wxString
& help_string
,
170 wxRibbonButtonKind kind
,
171 wxObject
* client_data
)
173 wxASSERT(bitmap
.IsOk());
175 wxRibbonToolBarToolBase
* tool
= new wxRibbonToolBarToolBase
;
177 tool
->bitmap
= bitmap
;
178 if(bitmap_disabled
.IsOk())
180 wxASSERT(bitmap
.GetSize() == bitmap_disabled
.GetSize());
181 tool
->bitmap_disabled
= bitmap_disabled
;
184 tool
->bitmap_disabled
= MakeDisabledBitmap(bitmap
);
185 tool
->help_string
= help_string
;
187 tool
->client_data
= client_data
;
188 tool
->position
= wxPoint(0, 0);
189 tool
->size
= wxSize(0, 0);
192 m_groups
.Last()->tools
.Add(tool
);
196 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddSeparator()
198 if(m_groups
.Last()->tools
.IsEmpty())
202 return &m_groups
.Last()->dummy_tool
;
205 wxBitmap
wxRibbonToolBar::MakeDisabledBitmap(const wxBitmap
& original
)
207 wxImage
img(original
.ConvertToImage());
208 return wxBitmap(img
.ConvertToGreyscale());
211 void wxRibbonToolBar::AppendGroup()
213 wxRibbonToolBarToolGroup
* group
= new wxRibbonToolBarToolGroup
;
214 group
->position
= wxPoint(0, 0);
215 group
->size
= wxSize(0, 0);
219 bool wxRibbonToolBar::IsSizingContinuous() const
224 static int GetSizeInOrientation(wxSize size
, wxOrientation orientation
)
228 case wxHORIZONTAL
: return size
.GetWidth();
229 case wxVERTICAL
: return size
.GetHeight();
230 case wxBOTH
: return size
.GetWidth() * size
.GetHeight();
235 wxSize
wxRibbonToolBar::DoGetNextSmallerSize(wxOrientation direction
,
236 wxSize relative_to
) const
238 wxSize
result(relative_to
);
241 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
243 wxSize
size(m_sizes
[nrows
- m_nrows_min
]);
244 wxSize
original(size
);
248 if(size
.GetWidth() < relative_to
.GetWidth()
249 && size
.GetHeight() <= relative_to
.GetHeight())
251 size
.SetHeight(relative_to
.GetHeight());
256 if(size
.GetWidth() <= relative_to
.GetWidth()
257 && size
.GetHeight() < relative_to
.GetHeight())
259 size
.SetWidth(relative_to
.GetWidth());
264 if(size
.GetWidth() < relative_to
.GetWidth()
265 && size
.GetHeight() < relative_to
.GetHeight())
271 if(GetSizeInOrientation(original
, direction
) > area
)
274 area
= GetSizeInOrientation(original
, direction
);
280 wxSize
wxRibbonToolBar::DoGetNextLargerSize(wxOrientation direction
,
281 wxSize relative_to
) const
283 // Pick the smallest of our sizes which are larger than the given size
284 wxSize
result(relative_to
);
287 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
289 wxSize
size(m_sizes
[nrows
- m_nrows_min
]);
290 wxSize
original(size
);
294 if(size
.GetWidth() > relative_to
.GetWidth()
295 && size
.GetHeight() <= relative_to
.GetHeight())
297 size
.SetHeight(relative_to
.GetHeight());
302 if(size
.GetWidth() <= relative_to
.GetWidth()
303 && size
.GetHeight() > relative_to
.GetHeight())
305 size
.SetWidth(relative_to
.GetWidth());
310 if(size
.GetWidth() > relative_to
.GetWidth()
311 && size
.GetHeight() > relative_to
.GetHeight())
317 if(GetSizeInOrientation(original
, direction
) < area
)
320 area
= GetSizeInOrientation(original
, direction
);
327 void wxRibbonToolBar::SetRows(int nMin
, int nMax
)
333 wxASSERT(nMin
<= nMax
);
339 m_sizes
= new wxSize
[m_nrows_max
- m_nrows_min
+ 1];
340 for(int i
= m_nrows_min
; i
<= m_nrows_max
; ++i
)
341 m_sizes
[i
- m_nrows_min
] = wxSize(0, 0);
346 bool wxRibbonToolBar::Realize()
351 // Calculate the size of each group and the position/size of each tool
353 size_t group_count
= m_groups
.GetCount();
355 for(g
= 0; g
< group_count
; ++g
)
357 wxRibbonToolBarToolBase
* prev
= NULL
;
358 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
359 size_t tool_count
= group
->tools
.GetCount();
361 for(t
= 0; t
< tool_count
; ++t
)
363 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
364 tool
->size
= m_art
->GetToolSize(temp_dc
, this,
365 tool
->bitmap
.GetSize(), tool
->kind
, t
== 0,
366 t
== (tool_count
- 1), &tool
->dropdown
);
367 tool
->state
= tool
->state
& ~wxRIBBON_TOOLBAR_TOOL_DISABLED
;
369 tool
->state
|= wxRIBBON_TOOLBAR_TOOL_FIRST
;
370 if(t
== tool_count
- 1)
371 tool
->state
|= wxRIBBON_TOOLBAR_TOOL_LAST
;
372 if(tool
->size
.GetHeight() > tallest
)
373 tallest
= tool
->size
.GetHeight();
376 tool
->position
= prev
->position
;
377 tool
->position
.x
+= prev
->size
.x
;
381 tool
->position
= wxPoint(0, 0);
386 group
->size
= wxSize(0, 0);
389 group
->size
= wxSize(prev
->position
.x
+ prev
->size
.x
, tallest
);
390 for(t
= 0; t
< tool_count
; ++t
)
391 group
->tools
.Item(t
)->size
.SetHeight(tallest
);
395 // Calculate the minimum size for each possible number of rows
397 int sep
= m_art
->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE
);
398 int smallest_area
= INT_MAX
;
399 wxSize
* row_sizes
= new wxSize
[m_nrows_max
];
400 wxOrientation major_axis
= m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
?
401 wxVERTICAL
: wxHORIZONTAL
;
402 SetMinSize(wxSize(0, 0));
403 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
405 for(r
= 0; r
< nrows
; ++r
)
406 row_sizes
[r
] = wxSize(0, 0);
407 for(g
= 0; g
< group_count
; ++g
)
409 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
410 int shortest_row
= 0;
411 for(r
= 1; r
< nrows
; ++r
)
413 if(row_sizes
[r
].GetWidth() < row_sizes
[shortest_row
].GetWidth())
416 row_sizes
[shortest_row
].x
+= group
->size
.x
+ sep
;
417 if(group
->size
.y
> row_sizes
[shortest_row
].y
)
418 row_sizes
[shortest_row
].y
= group
->size
.y
;
421 for(r
= 0; r
< nrows
; ++r
)
423 if(row_sizes
[r
].GetWidth() != 0)
424 row_sizes
[r
].DecBy(sep
, 0);
425 if(row_sizes
[r
].GetWidth() > size
.GetWidth())
426 size
.SetWidth(row_sizes
[r
].GetWidth());
427 size
.IncBy(0, row_sizes
[r
].y
);
429 m_sizes
[nrows
- m_nrows_min
] = size
;
430 if(GetSizeInOrientation(size
, major_axis
) < smallest_area
)
433 smallest_area
= GetSizeInOrientation(size
, major_axis
);
438 // Position the groups
439 wxSizeEvent
dummy_event(GetSize());
445 void wxRibbonToolBar::OnSize(wxSizeEvent
& evt
)
450 // Choose row count with largest possible area
451 wxSize size
= evt
.GetSize();
452 int row_count
= m_nrows_max
;
453 wxOrientation major_axis
= m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
?
454 wxVERTICAL
: wxHORIZONTAL
;
455 if(m_nrows_max
!= m_nrows_min
)
458 for(int i
= 0; i
<= m_nrows_max
- m_nrows_min
; ++i
)
460 if(m_sizes
[i
].x
<= size
.x
&& m_sizes
[i
].y
<= size
.y
&&
461 GetSizeInOrientation(m_sizes
[i
], major_axis
) > area
)
463 area
= GetSizeInOrientation(m_sizes
[i
], major_axis
);
464 row_count
= m_nrows_min
+ i
;
469 // Assign groups to rows and calculate row widths
470 wxSize
* row_sizes
= new wxSize
[row_count
];
471 int sep
= m_art
->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE
);
474 for(r
= 0; r
< row_count
; ++r
)
475 row_sizes
[r
] = wxSize(0, 0);
477 size_t group_count
= m_groups
.GetCount();
478 for(g
= 0; g
< group_count
; ++g
)
480 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
481 int shortest_row
= 0;
482 for(r
= 1; r
< row_count
; ++r
)
484 if(row_sizes
[r
].GetWidth() < row_sizes
[shortest_row
].GetWidth())
487 group
->position
= wxPoint(row_sizes
[shortest_row
].x
, shortest_row
);
488 row_sizes
[shortest_row
].x
+= group
->size
.x
+ sep
;
489 if(group
->size
.y
> row_sizes
[shortest_row
].y
)
490 row_sizes
[shortest_row
].y
= group
->size
.y
;
493 // Calculate row positions
494 int total_height
= 0;
495 for(r
= 0; r
< row_count
; ++r
)
496 total_height
+= row_sizes
[r
].GetHeight();
497 int rowsep
= (size
.GetHeight() - total_height
) / (row_count
+ 1);
498 int* rowypos
= new int[row_count
];
500 for(r
= 1; r
< row_count
; ++r
)
502 rowypos
[r
] = rowypos
[r
- 1] + row_sizes
[r
- 1].GetHeight() + rowsep
;
505 // Set group y positions
506 for(g
= 0; g
< group_count
; ++g
)
508 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
509 group
->position
.y
= rowypos
[group
->position
.y
];
516 wxSize
wxRibbonToolBar::DoGetBestSize() const
521 void wxRibbonToolBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
523 // All painting done in main paint handler to minimise flicker
526 void wxRibbonToolBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
528 wxAutoBufferedPaintDC
dc(this);
532 m_art
->DrawToolBarBackground(dc
, this, GetSize());
534 size_t group_count
= m_groups
.GetCount();
536 for(g
= 0; g
< group_count
; ++g
)
538 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
539 size_t tool_count
= group
->tools
.GetCount();
542 m_art
->DrawToolGroupBackground(dc
, this,
543 wxRect(group
->position
, group
->size
));
544 for(t
= 0; t
< tool_count
; ++t
)
546 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
547 wxRect
rect(group
->position
+ tool
->position
, tool
->size
);
548 m_art
->DrawTool(dc
, this, rect
, tool
->bitmap
, tool
->kind
,
555 void wxRibbonToolBar::OnMouseMove(wxMouseEvent
& evt
)
557 wxPoint
pos(evt
.GetPosition());
558 wxRibbonToolBarToolBase
*new_hover
= NULL
;
560 size_t group_count
= m_groups
.GetCount();
562 for(g
= 0; g
< group_count
; ++g
)
564 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
565 if(group
->position
.x
<= pos
.x
&& pos
.x
< group
->position
.x
+ group
->size
.x
566 && group
->position
.y
<= pos
.y
&& pos
.y
< group
->position
.y
+ group
->size
.y
)
568 size_t tool_count
= group
->tools
.GetCount();
569 pos
-= group
->position
;
570 for(t
= 0; t
< tool_count
; ++t
)
572 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
573 if(tool
->position
.x
<= pos
.x
&& pos
.x
< tool
->position
.x
+ tool
->size
.x
574 && tool
->position
.y
<= pos
.y
&& pos
.y
< tool
->position
.y
+ tool
->size
.y
)
576 pos
-= tool
->position
;
585 if(new_hover
!= m_hover_tool
)
589 m_hover_tool
->state
&= ~(wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
590 | wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
);
592 m_hover_tool
= new_hover
;
595 long what
= wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED
;
596 if(new_hover
->dropdown
.Contains(pos
))
597 what
= wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED
;
599 new_hover
->state
|= what
;
601 if(new_hover
== m_active_tool
)
603 new_hover
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
604 new_hover
->state
|= (what
<< 2);
609 else if(m_hover_tool
&& m_hover_tool
->kind
== wxRIBBON_BUTTON_HYBRID
)
611 long newstate
= m_hover_tool
->state
&~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
;
612 long what
= wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED
;
613 if(m_hover_tool
->dropdown
.Contains(pos
))
614 what
= wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED
;
616 if(newstate
!= m_hover_tool
->state
)
618 m_hover_tool
->state
= newstate
;
619 if(m_hover_tool
== m_active_tool
)
621 m_hover_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
622 m_hover_tool
->state
|= (what
<< 2);
629 void wxRibbonToolBar::OnMouseDown(wxMouseEvent
& evt
)
634 m_active_tool
= m_hover_tool
;
635 m_active_tool
->state
|=
636 (m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
) << 2;
641 void wxRibbonToolBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
645 m_hover_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
;
651 void wxRibbonToolBar::OnMouseUp(wxMouseEvent
& WXUNUSED(evt
))
655 if(m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
)
657 wxEventType evt_type
= wxEVT_COMMAND_RIBBONTOOL_CLICKED
;
658 if(m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE
)
659 evt_type
= wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED
;
660 wxRibbonToolBarEvent
notification(evt_type
, m_active_tool
->id
);
661 notification
.SetEventObject(this);
662 notification
.SetBar(this);
663 ProcessEvent(notification
);
665 m_active_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
666 m_active_tool
= NULL
;
671 void wxRibbonToolBar::OnMouseEnter(wxMouseEvent
& evt
)
673 if(m_active_tool
&& !evt
.LeftIsDown())
675 m_active_tool
= NULL
;
679 bool wxRibbonToolBarEvent::PopupMenu(wxMenu
* menu
)
681 wxPoint pos
= wxDefaultPosition
;
682 if(m_bar
->m_active_tool
)
684 // Find the group which contains the tool
685 size_t group_count
= m_bar
->m_groups
.GetCount();
687 for(g
= 0; g
< group_count
; ++g
)
689 wxRibbonToolBarToolGroup
* group
= m_bar
->m_groups
.Item(g
);
690 size_t tool_count
= group
->tools
.GetCount();
691 for(t
= 0; t
< tool_count
; ++t
)
693 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
694 if(tool
== m_bar
->m_active_tool
)
696 pos
= group
->position
;
697 pos
+= tool
->position
;
698 pos
.y
+= tool
->size
.GetHeight();
705 return m_bar
->PopupMenu(menu
, pos
);
708 #endif // wxUSE_RIBBON