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
);
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
);
664 m_active_tool
->state
&= ~wxRIBBON_TOOLBAR_TOOL_ACTIVE_MASK
;
665 m_active_tool
= NULL
;
670 void wxRibbonToolBar::OnMouseEnter(wxMouseEvent
& evt
)
672 if(m_active_tool
&& !evt
.LeftIsDown())
674 m_active_tool
= NULL
;
678 bool wxRibbonToolBarEvent::PopupMenu(wxMenu
* menu
)
680 wxPoint pos
= wxDefaultPosition
;
681 if(m_bar
->m_active_tool
)
683 // Find the group which contains the tool
684 size_t group_count
= m_bar
->m_groups
.GetCount();
686 for(g
= 0; g
< group_count
; ++g
)
688 wxRibbonToolBarToolGroup
* group
= m_bar
->m_groups
.Item(g
);
689 size_t tool_count
= group
->tools
.GetCount();
690 for(t
= 0; t
< tool_count
; ++t
)
692 wxRibbonToolBarToolBase
* tool
= group
->tools
.Item(t
);
693 if(tool
== m_bar
->m_active_tool
)
695 pos
= group
->position
;
696 pos
+= tool
->position
;
697 pos
.y
+= tool
->size
.GetHeight();
704 return m_bar
->PopupMenu(menu
, pos
);
707 #endif // wxUSE_RIBBON