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"
20 #include "wx/ribbon/toolbar.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/ribbon/bar.h"
23 #include "wx/dcbuffer.h"
29 #include "wx/msw/private.h"
32 class wxRibbonToolBarToolBase
37 wxBitmap bitmap_disabled
;
41 wxObject
* client_data
;
43 wxRibbonButtonKind kind
;
47 WX_DEFINE_ARRAY_PTR(wxRibbonToolBarToolBase
*, wxArrayRibbonToolBarToolBase
);
49 class wxRibbonToolBarToolGroup
52 // To identify the group as a wxRibbonToolBarToolBase*
53 wxRibbonToolBarToolBase dummy_tool
;
55 wxArrayRibbonToolBarToolBase tools
;
60 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_CLICKED
, wxRibbonToolBarEvent
);
61 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED
, wxRibbonToolBarEvent
);
63 IMPLEMENT_DYNAMIC_CLASS(wxRibbonToolBarEvent
, wxCommandEvent
)
64 IMPLEMENT_CLASS(wxRibbonToolBar
, wxRibbonControl
)
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
)
77 wxRibbonToolBar::wxRibbonToolBar()
81 wxRibbonToolBar::wxRibbonToolBar(wxWindow
* parent
,
86 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
91 bool wxRibbonToolBar::Create(wxWindow
* parent
,
97 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
106 void wxRibbonToolBar::CommonInit(long WXUNUSED(style
))
110 m_active_tool
= NULL
;
113 m_sizes
= new wxSize
[1];
114 m_sizes
[0] = wxSize(0, 0);
115 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
118 wxRibbonToolBar::~wxRibbonToolBar()
120 size_t count
= m_groups
.GetCount();
122 for(i
= 0; i
< count
; ++i
)
124 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(i
);
125 size_t tool_count
= group
->tools
.GetCount();
126 for(t
= 0; t
< tool_count
; ++t
)
128 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
137 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddTool(
139 const wxBitmap
& bitmap
,
140 const wxString
& help_string
,
141 wxRibbonButtonKind kind
)
143 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
, kind
, NULL
);
146 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddDropdownTool(
148 const wxBitmap
& bitmap
,
149 const wxString
& help_string
)
151 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
,
152 wxRIBBON_BUTTON_DROPDOWN
, NULL
);
155 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddHybridTool(
157 const wxBitmap
& bitmap
,
158 const wxString
& help_string
)
160 return AddTool(tool_id
, bitmap
, wxNullBitmap
, help_string
,
161 wxRIBBON_BUTTON_HYBRID
, NULL
);
164 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddTool(
166 const wxBitmap
& bitmap
,
167 const wxBitmap
& bitmap_disabled
,
168 const wxString
& help_string
,
169 wxRibbonButtonKind kind
,
170 wxObject
* client_data
)
172 wxASSERT(bitmap
.IsOk());
174 wxRibbonToolBarToolBase
* tool
= new wxRibbonToolBarToolBase
;
176 tool
->bitmap
= bitmap
;
177 if(bitmap_disabled
.IsOk())
179 wxASSERT(bitmap
.GetSize() == bitmap_disabled
.GetSize());
180 tool
->bitmap_disabled
= bitmap_disabled
;
183 tool
->bitmap_disabled
= MakeDisabledBitmap(bitmap
);
184 tool
->help_string
= help_string
;
186 tool
->client_data
= client_data
;
187 tool
->position
= wxPoint(0, 0);
188 tool
->size
= wxSize(0, 0);
191 m_groups
.Last()->tools
.Add(tool
);
195 wxRibbonToolBarToolBase
* wxRibbonToolBar::AddSeparator()
197 if(m_groups
.Last()->tools
.IsEmpty())
201 return &m_groups
.Last()->dummy_tool
;
204 wxBitmap
wxRibbonToolBar::MakeDisabledBitmap(const wxBitmap
& original
)
206 wxImage
img(original
.ConvertToImage());
207 return wxBitmap(img
.ConvertToGreyscale());
210 void wxRibbonToolBar::AppendGroup()
212 wxRibbonToolBarToolGroup
* group
= new wxRibbonToolBarToolGroup
;
213 group
->position
= wxPoint(0, 0);
214 group
->size
= wxSize(0, 0);
218 bool wxRibbonToolBar::IsSizingContinuous() const
223 static int GetSizeInOrientation(wxSize size
, wxOrientation orientation
)
227 case wxHORIZONTAL
: return size
.GetWidth();
228 case wxVERTICAL
: return size
.GetHeight();
229 case wxBOTH
: return size
.GetWidth() * size
.GetHeight();
234 wxSize
wxRibbonToolBar::DoGetNextSmallerSize(wxOrientation direction
,
235 wxSize relative_to
) const
237 wxSize
result(relative_to
);
240 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
242 wxSize
size(m_sizes
[nrows
- m_nrows_min
]);
243 wxSize
original(size
);
247 if(size
.GetWidth() < relative_to
.GetWidth()
248 && size
.GetHeight() <= relative_to
.GetHeight())
250 size
.SetHeight(relative_to
.GetHeight());
255 if(size
.GetWidth() <= relative_to
.GetWidth()
256 && size
.GetHeight() < relative_to
.GetHeight())
258 size
.SetWidth(relative_to
.GetWidth());
263 if(size
.GetWidth() < relative_to
.GetWidth()
264 && size
.GetHeight() < relative_to
.GetHeight())
270 if(GetSizeInOrientation(original
, direction
) > area
)
273 area
= GetSizeInOrientation(original
, direction
);
279 wxSize
wxRibbonToolBar::DoGetNextLargerSize(wxOrientation direction
,
280 wxSize relative_to
) const
282 // Pick the smallest of our sizes which are larger than the given size
283 wxSize
result(relative_to
);
286 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
288 wxSize
size(m_sizes
[nrows
- m_nrows_min
]);
289 wxSize
original(size
);
293 if(size
.GetWidth() > relative_to
.GetWidth()
294 && size
.GetHeight() <= relative_to
.GetHeight())
296 size
.SetHeight(relative_to
.GetHeight());
301 if(size
.GetWidth() <= relative_to
.GetWidth()
302 && size
.GetHeight() > relative_to
.GetHeight())
304 size
.SetWidth(relative_to
.GetWidth());
309 if(size
.GetWidth() > relative_to
.GetWidth()
310 && size
.GetHeight() > relative_to
.GetHeight())
316 if(GetSizeInOrientation(original
, direction
) < area
)
319 area
= GetSizeInOrientation(original
, direction
);
326 void wxRibbonToolBar::SetRows(int nMin
, int nMax
)
332 wxASSERT(nMin
<= nMax
);
338 m_sizes
= new wxSize
[m_nrows_max
- m_nrows_min
+ 1];
339 for(int i
= m_nrows_min
; i
<= m_nrows_max
; ++i
)
340 m_sizes
[i
- m_nrows_min
] = wxSize(0, 0);
345 bool wxRibbonToolBar::Realize()
350 // Calculate the size of each group and the position/size of each tool
352 size_t group_count
= m_groups
.GetCount();
354 for(g
= 0; g
< group_count
; ++g
)
356 wxRibbonToolBarToolBase
* prev
= NULL
;
357 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
358 size_t tool_count
= group
->tools
.GetCount();
360 for(t
= 0; t
< tool_count
; ++t
)
362 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
363 tool
->size
= m_art
->GetToolSize(temp_dc
, this,
364 tool
->bitmap
.GetSize(), tool
->kind
, t
== 0,
365 t
== (tool_count
- 1), &tool
->dropdown
);
366 tool
->state
= tool
->state
& ~wxRIBBON_TOOLBAR_TOOL_DISABLED
;
368 tool
->state
|= wxRIBBON_TOOLBAR_TOOL_FIRST
;
369 if(t
== tool_count
- 1)
370 tool
->state
|= wxRIBBON_TOOLBAR_TOOL_LAST
;
371 if(tool
->size
.GetHeight() > tallest
)
372 tallest
= tool
->size
.GetHeight();
375 tool
->position
= prev
->position
;
376 tool
->position
.x
+= prev
->size
.x
;
380 tool
->position
= wxPoint(0, 0);
385 group
->size
= wxSize(0, 0);
388 group
->size
= wxSize(prev
->position
.x
+ prev
->size
.x
, tallest
);
389 for(t
= 0; t
< tool_count
; ++t
)
390 group
->tools
.Item(t
)->size
.SetHeight(tallest
);
394 // Calculate the minimum size for each possible number of rows
396 int sep
= m_art
->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE
);
397 int smallest_area
= INT_MAX
;
398 wxSize
* row_sizes
= new wxSize
[m_nrows_max
];
399 wxOrientation major_axis
= m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
?
400 wxVERTICAL
: wxHORIZONTAL
;
401 SetMinSize(wxSize(0, 0));
402 for(nrows
= m_nrows_min
; nrows
<= m_nrows_max
; ++nrows
)
404 for(r
= 0; r
< nrows
; ++r
)
405 row_sizes
[r
] = wxSize(0, 0);
406 for(g
= 0; g
< group_count
; ++g
)
408 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
409 int shortest_row
= 0;
410 for(r
= 1; r
< nrows
; ++r
)
412 if(row_sizes
[r
].GetWidth() < row_sizes
[shortest_row
].GetWidth())
415 row_sizes
[shortest_row
].x
+= group
->size
.x
+ sep
;
416 if(group
->size
.y
> row_sizes
[shortest_row
].y
)
417 row_sizes
[shortest_row
].y
= group
->size
.y
;
420 for(r
= 0; r
< nrows
; ++r
)
422 if(row_sizes
[r
].GetWidth() != 0)
423 row_sizes
[r
].DecBy(sep
, 0);
424 if(row_sizes
[r
].GetWidth() > size
.GetWidth())
425 size
.SetWidth(row_sizes
[r
].GetWidth());
426 size
.IncBy(0, row_sizes
[r
].y
);
428 m_sizes
[nrows
- m_nrows_min
] = size
;
429 if(GetSizeInOrientation(size
, major_axis
) < smallest_area
)
432 smallest_area
= GetSizeInOrientation(size
, major_axis
);
437 // Position the groups
438 wxSizeEvent
dummy_event(GetSize());
444 void wxRibbonToolBar::OnSize(wxSizeEvent
& evt
)
449 // Choose row count with largest possible area
450 wxSize size
= evt
.GetSize();
451 int row_count
= m_nrows_max
;
452 wxOrientation major_axis
= m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
?
453 wxVERTICAL
: wxHORIZONTAL
;
454 if(m_nrows_max
!= m_nrows_min
)
457 for(int i
= 0; i
<= m_nrows_max
- m_nrows_min
; ++i
)
459 if(m_sizes
[i
].x
<= size
.x
&& m_sizes
[i
].y
<= size
.y
&&
460 GetSizeInOrientation(m_sizes
[i
], major_axis
) > area
)
462 area
= GetSizeInOrientation(m_sizes
[i
], major_axis
);
463 row_count
= m_nrows_min
+ i
;
468 // Assign groups to rows and calculate row widths
469 wxSize
* row_sizes
= new wxSize
[row_count
];
470 int sep
= m_art
->GetMetric(wxRIBBON_ART_TOOL_GROUP_SEPARATION_SIZE
);
473 for(r
= 0; r
< row_count
; ++r
)
474 row_sizes
[r
] = wxSize(0, 0);
476 size_t group_count
= m_groups
.GetCount();
477 for(g
= 0; g
< group_count
; ++g
)
479 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
480 int shortest_row
= 0;
481 for(r
= 1; r
< row_count
; ++r
)
483 if(row_sizes
[r
].GetWidth() < row_sizes
[shortest_row
].GetWidth())
486 group
->position
= wxPoint(row_sizes
[shortest_row
].x
, shortest_row
);
487 row_sizes
[shortest_row
].x
+= group
->size
.x
+ sep
;
488 if(group
->size
.y
> row_sizes
[shortest_row
].y
)
489 row_sizes
[shortest_row
].y
= group
->size
.y
;
492 // Calculate row positions
493 int total_height
= 0;
494 for(r
= 0; r
< row_count
; ++r
)
495 total_height
+= row_sizes
[r
].GetHeight();
496 int rowsep
= (size
.GetHeight() - total_height
) / (row_count
+ 1);
497 int* rowypos
= new int[row_count
];
499 for(r
= 1; r
< row_count
; ++r
)
501 rowypos
[r
] = rowypos
[r
- 1] + row_sizes
[r
- 1].GetHeight() + rowsep
;
504 // Set group y positions
505 for(g
= 0; g
< group_count
; ++g
)
507 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
508 group
->position
.y
= rowypos
[group
->position
.y
];
515 wxSize
wxRibbonToolBar::DoGetBestSize() const
520 void wxRibbonToolBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
522 // All painting done in main paint handler to minimise flicker
525 void wxRibbonToolBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
527 wxAutoBufferedPaintDC
dc(this);
531 m_art
->DrawToolBarBackground(dc
, this, GetSize());
533 size_t group_count
= m_groups
.GetCount();
535 for(g
= 0; g
< group_count
; ++g
)
537 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
538 size_t tool_count
= group
->tools
.GetCount();
541 m_art
->DrawToolGroupBackground(dc
, this,
542 wxRect(group
->position
, group
->size
));
543 for(t
= 0; t
< tool_count
; ++t
)
545 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
546 wxRect
rect(group
->position
+ tool
->position
, tool
->size
);
547 m_art
->DrawTool(dc
, this, rect
, tool
->bitmap
, tool
->kind
,
554 void wxRibbonToolBar::OnMouseMove(wxMouseEvent
& evt
)
556 wxPoint
pos(evt
.GetPosition());
557 wxRibbonToolBarToolBase
*new_hover
= NULL
;
559 size_t group_count
= m_groups
.GetCount();
561 for(g
= 0; g
< group_count
; ++g
)
563 wxRibbonToolBarToolGroup
* group
= m_groups
.Item(g
);
564 if(group
->position
.x
<= pos
.x
&& pos
.x
< group
->position
.x
+ group
->size
.x
565 && group
->position
.y
<= pos
.y
&& pos
.y
< group
->position
.y
+ group
->size
.y
)
567 size_t tool_count
= group
->tools
.GetCount();
568 pos
-= group
->position
;
569 for(t
= 0; t
< tool_count
; ++t
)
571 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
572 if(tool
->position
.x
<= pos
.x
&& pos
.x
< tool
->position
.x
+ tool
->size
.x
573 && tool
->position
.y
<= pos
.y
&& pos
.y
< tool
->position
.y
+ tool
->size
.y
)
575 pos
-= tool
->position
;
584 if(new_hover
!= m_hover_tool
)
588 m_hover_tool
->state
&= ~(wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
589 | wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
);
591 m_hover_tool
= new_hover
;
594 long what
= wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED
;
595 if(new_hover
->dropdown
.Contains(pos
))
596 what
= wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED
;
598 new_hover
->state
|= what
;
600 if(new_hover
== m_active_tool
)
602 new_hover
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
603 new_hover
->state
|= (what
<< 2);
608 else if(m_hover_tool
&& m_hover_tool
->kind
== wxRIBBON_BUTTON_HYBRID
)
610 long newstate
= m_hover_tool
->state
&~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
;
611 long what
= wxRIBBON_TOOLBAR_TOOL_NORMAL_HOVERED
;
612 if(m_hover_tool
->dropdown
.Contains(pos
))
613 what
= wxRIBBON_TOOLBAR_TOOL_DROPDOWN_HOVERED
;
615 if(newstate
!= m_hover_tool
->state
)
617 m_hover_tool
->state
= newstate
;
618 if(m_hover_tool
== m_active_tool
)
620 m_hover_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
621 m_hover_tool
->state
|= (what
<< 2);
628 void wxRibbonToolBar::OnMouseDown(wxMouseEvent
& evt
)
633 m_active_tool
= m_hover_tool
;
634 m_active_tool
->state
|=
635 (m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
) << 2;
640 void wxRibbonToolBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
644 m_hover_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_HOVER_MASK
;
650 void wxRibbonToolBar::OnMouseUp(wxMouseEvent
& WXUNUSED(evt
))
654 if(m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
)
656 wxEventType evt_type
= wxEVT_COMMAND_RIBBONTOOL_CLICKED
;
657 if(m_active_tool
->state
& wxRIBBON_TOOLBAR_TOOL_DROPDOWN_ACTIVE
)
658 evt_type
= wxEVT_COMMAND_RIBBONTOOL_DROPDOWN_CLICKED
;
659 wxRibbonToolBarEvent
notification(evt_type
, m_active_tool
->id
);
660 notification
.SetEventObject(this);
661 notification
.SetBar(this);
662 ProcessEvent(notification
);
665 // Notice that m_active_tool could have been reset by the event handler
666 // above so we need to test it again.
669 m_active_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
670 m_active_tool
= NULL
;
676 void wxRibbonToolBar::OnMouseEnter(wxMouseEvent
& evt
)
678 if(m_active_tool
&& !evt
.LeftIsDown())
680 m_active_tool
= NULL
;
684 bool wxRibbonToolBarEvent::PopupMenu(wxMenu
* menu
)
686 wxPoint pos
= wxDefaultPosition
;
687 if(m_bar
->m_active_tool
)
689 // Find the group which contains the tool
690 size_t group_count
= m_bar
->m_groups
.GetCount();
692 for(g
= 0; g
< group_count
; ++g
)
694 wxRibbonToolBarToolGroup
* group
= m_bar
->m_groups
.Item(g
);
695 size_t tool_count
= group
->tools
.GetCount();
696 for(t
= 0; t
< tool_count
; ++t
)
698 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
699 if(tool
== m_bar
->m_active_tool
)
701 pos
= group
->position
;
702 pos
+= tool
->position
;
703 pos
.y
+= tool
->size
.GetHeight();
710 return m_bar
->PopupMenu(menu
, pos
);
713 #endif // wxUSE_RIBBON