1
/////////////////////////////////////////////////////////////////////////////
2
// Name: src/gtk/toolbar.cpp
3
// Purpose: GTK toolbar
4
// Author: Robert Roebling
5
// Modified: 13.12.99 by VZ to derive from wxToolBarBase
6
// Copyright: (c) Robert Roebling
7
// Licence: wxWindows licence
8
/////////////////////////////////////////////////////////////////////////////
10
// For compilers that support precompilation, includes "wx.h".
11
#include "wx/wxprec.h"
13
#if wxUSE_TOOLBAR_NATIVE
15
#include "wx/toolbar.h"
18
#include "wx/gtk/private.h"
19
#include "wx/gtk/private/gtk2-compat.h"
21
// ----------------------------------------------------------------------------
23
// ----------------------------------------------------------------------------
26
extern bool g_blockEventsOnDrag;
27
extern wxCursor g_globalCursor;
29
// ----------------------------------------------------------------------------
31
// ----------------------------------------------------------------------------
33
class wxToolBarTool : public wxToolBarToolBase
36
wxToolBarTool(wxToolBar *tbar,
38
const wxString& label,
39
const wxBitmap& bitmap1,
40
const wxBitmap& bitmap2,
43
const wxString& shortHelpString,
44
const wxString& longHelpString)
45
: wxToolBarToolBase(tbar, id, label, bitmap1, bitmap2, kind,
46
clientData, shortHelpString, longHelpString)
51
wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
52
: wxToolBarToolBase(tbar, control, label)
58
void CreateDropDown();
59
void ShowDropdown(GtkToggleButton* button);
64
// ----------------------------------------------------------------------------
66
// ----------------------------------------------------------------------------
68
IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
70
// ============================================================================
72
// ============================================================================
74
//-----------------------------------------------------------------------------
75
// "clicked" from m_item
76
//-----------------------------------------------------------------------------
79
static void item_clicked(GtkToolButton*, wxToolBarTool* tool)
81
if (g_blockEventsOnDrag) return;
83
tool->GetToolBar()->OnLeftClick(tool->GetId(), false);
87
//-----------------------------------------------------------------------------
88
// "toggled" from m_item
89
//-----------------------------------------------------------------------------
92
static void item_toggled(GtkToggleToolButton* button, wxToolBarTool* tool)
94
if (g_blockEventsOnDrag) return;
96
const bool active = gtk_toggle_tool_button_get_active(button) != 0;
98
if (!active && tool->GetKind() == wxITEM_RADIO)
101
if (!tool->GetToolBar()->OnLeftClick(tool->GetId(), active))
109
//-----------------------------------------------------------------------------
110
// "button_press_event" from m_item child
111
//-----------------------------------------------------------------------------
115
button_press_event(GtkWidget*, GdkEventButton* event, wxToolBarTool* tool)
117
if (event->button != 3)
120
if (g_blockEventsOnDrag) return TRUE;
122
tool->GetToolBar()->OnRightClick(
123
tool->GetId(), int(event->x), int(event->y));
129
//-----------------------------------------------------------------------------
130
// "child_detached" from m_widget
131
//-----------------------------------------------------------------------------
134
static void child_detached(GtkWidget*, GtkToolbar* toolbar, void*)
136
// disable showing overflow arrow when toolbar is detached,
137
// otherwise toolbar collapses to just an arrow
138
gtk_toolbar_set_show_arrow(toolbar, false);
142
//-----------------------------------------------------------------------------
143
// "child_attached" from m_widget
144
//-----------------------------------------------------------------------------
147
static void child_attached(GtkWidget*, GtkToolbar* toolbar, void*)
149
gtk_toolbar_set_show_arrow(toolbar, true);
153
//-----------------------------------------------------------------------------
154
// "enter_notify_event" / "leave_notify_event" from m_item
155
//-----------------------------------------------------------------------------
159
enter_notify_event(GtkWidget*, GdkEventCrossing* event, wxToolBarTool* tool)
161
if (g_blockEventsOnDrag) return TRUE;
164
if (event->type == GDK_ENTER_NOTIFY)
166
tool->GetToolBar()->OnMouseEnter(id);
172
//-----------------------------------------------------------------------------
173
// "expose_event" from GtkImage inside m_item
174
//-----------------------------------------------------------------------------
179
image_draw(GtkWidget* widget, cairo_t* cr, wxToolBarTool* tool)
181
image_expose_event(GtkWidget* widget, GdkEventExpose*, wxToolBarTool* tool)
184
const wxBitmap& bitmap = tool->GetDisabledBitmap();
185
if (tool->IsEnabled() || !bitmap.IsOk())
188
// draw disabled bitmap ourselves, GtkImage has no way to specify it
190
gtk_widget_get_allocation(widget, &alloc);
192
gtk_widget_get_requisition(widget, &req);
193
const int x = alloc.x + (alloc.width - req.width) / 2;
194
const int y = alloc.y + (alloc.height - req.height) / 2;
196
bitmap.Draw(cr, x, y);
199
gtk_widget_get_window(widget), gtk_widget_get_style(widget)->black_gc, bitmap.GetPixbuf(),
201
-1, -1, GDK_RGB_DITHER_NORMAL, 0, 0);
207
//-----------------------------------------------------------------------------
208
// "toggled" from dropdown menu button
209
//-----------------------------------------------------------------------------
212
static void arrow_toggled(GtkToggleButton* button, wxToolBarTool* tool)
214
if (gtk_toggle_button_get_active(button))
216
tool->ShowDropdown(button);
217
gtk_toggle_button_set_active(button, false);
222
//-----------------------------------------------------------------------------
223
// "button_press_event" from dropdown menu button
224
//-----------------------------------------------------------------------------
228
arrow_button_press_event(GtkToggleButton* button, GdkEventButton* event, wxToolBarTool* tool)
230
if (event->button == 1)
232
g_signal_handlers_block_by_func(button, (void*)arrow_toggled, tool);
233
gtk_toggle_button_set_active(button, true);
234
tool->ShowDropdown(button);
235
gtk_toggle_button_set_active(button, false);
236
g_signal_handlers_unblock_by_func(button, (void*)arrow_toggled, tool);
243
void wxToolBar::AddChildGTK(wxWindowGTK* child)
245
GtkWidget* align = gtk_alignment_new(0.5, 0.5, 0, 0);
246
gtk_widget_show(align);
247
gtk_container_add(GTK_CONTAINER(align), child->m_widget);
248
GtkToolItem* item = gtk_tool_item_new();
249
gtk_container_add(GTK_CONTAINER(item), align);
250
// position will be corrected in DoInsertTool if necessary
251
gtk_toolbar_insert(GTK_TOOLBAR(gtk_bin_get_child(GTK_BIN(m_widget))), item, -1);
254
// ----------------------------------------------------------------------------
256
// ----------------------------------------------------------------------------
258
void wxToolBarTool::SetImage()
260
const wxBitmap& bitmap = GetNormalBitmap();
261
wxCHECK_RET(bitmap.IsOk(), "invalid bitmap for wxToolBar icon");
263
GtkWidget* image = gtk_tool_button_get_icon_widget(GTK_TOOL_BUTTON(m_item));
264
// always use pixbuf, because pixmap mask does not
265
// work with disabled images in some themes
266
gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
269
// helper to create a dropdown menu item
270
void wxToolBarTool::CreateDropDown()
272
gtk_tool_item_set_homogeneous(m_item, false);
273
GtkOrientation orient = GTK_ORIENTATION_HORIZONTAL;
274
GtkArrowType arrowType = GTK_ARROW_DOWN;
275
if (GetToolBar()->HasFlag(wxTB_LEFT | wxTB_RIGHT))
277
orient = GTK_ORIENTATION_VERTICAL;
278
arrowType = GTK_ARROW_RIGHT;
280
GtkWidget* box = gtk_box_new(orient, 0);
281
GtkWidget* arrow = gtk_arrow_new(arrowType, GTK_SHADOW_NONE);
282
GtkWidget* tool_button = gtk_bin_get_child(GTK_BIN(m_item));
283
gtk_widget_reparent(tool_button, box);
284
GtkWidget* arrow_button = gtk_toggle_button_new();
285
gtk_button_set_relief(GTK_BUTTON(arrow_button),
286
gtk_tool_item_get_relief_style(GTK_TOOL_ITEM(m_item)));
287
gtk_container_add(GTK_CONTAINER(arrow_button), arrow);
288
gtk_container_add(GTK_CONTAINER(box), arrow_button);
289
gtk_widget_show_all(box);
290
gtk_container_add(GTK_CONTAINER(m_item), box);
292
g_signal_connect(arrow_button, "toggled", G_CALLBACK(arrow_toggled), this);
293
g_signal_connect(arrow_button, "button_press_event",
294
G_CALLBACK(arrow_button_press_event), this);
297
void wxToolBarTool::ShowDropdown(GtkToggleButton* button)
299
wxToolBarBase* toolbar = GetToolBar();
300
wxCommandEvent event(wxEVT_TOOL_DROPDOWN, GetId());
301
if (!toolbar->HandleWindowEvent(event))
303
wxMenu* menu = GetDropdownMenu();
307
gtk_widget_get_allocation(GTK_WIDGET(button), &alloc);
310
if (toolbar->HasFlag(wxTB_LEFT | wxTB_RIGHT))
314
toolbar->PopupMenu(menu, x, y);
319
wxToolBarToolBase *wxToolBar::CreateTool(int id,
320
const wxString& text,
321
const wxBitmap& bitmap1,
322
const wxBitmap& bitmap2,
324
wxObject *clientData,
325
const wxString& shortHelpString,
326
const wxString& longHelpString)
328
return new wxToolBarTool(this, id, text, bitmap1, bitmap2, kind,
329
clientData, shortHelpString, longHelpString);
333
wxToolBar::CreateTool(wxControl *control, const wxString& label)
335
return new wxToolBarTool(this, control, label);
338
//-----------------------------------------------------------------------------
339
// wxToolBar construction
340
//-----------------------------------------------------------------------------
342
void wxToolBar::Init()
348
wxToolBar::~wxToolBar()
351
if (m_tooltips) // always NULL if GTK >= 2.12
353
gtk_object_destroy(GTK_OBJECT(m_tooltips));
354
g_object_unref(m_tooltips);
359
bool wxToolBar::Create( wxWindow *parent,
364
const wxString& name )
366
if ( !PreCreation( parent, pos, size ) ||
367
!CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
369
wxFAIL_MSG( wxT("wxToolBar creation failed") );
376
m_toolbar = GTK_TOOLBAR( gtk_toolbar_new() );
378
if (gtk_check_version(2, 12, 0))
380
m_tooltips = gtk_tooltips_new();
381
g_object_ref(m_tooltips);
382
gtk_object_sink(GTK_OBJECT(m_tooltips));
387
if (style & wxTB_DOCKABLE)
389
m_widget = gtk_handle_box_new();
391
g_signal_connect(m_widget, "child_detached",
392
G_CALLBACK(child_detached), NULL);
393
g_signal_connect(m_widget, "child_attached",
394
G_CALLBACK(child_attached), NULL);
396
if (style & wxTB_FLAT)
397
gtk_handle_box_set_shadow_type( GTK_HANDLE_BOX(m_widget), GTK_SHADOW_NONE );
401
m_widget = gtk_event_box_new();
402
ConnectWidget( m_widget );
404
g_object_ref(m_widget);
405
gtk_container_add(GTK_CONTAINER(m_widget), GTK_WIDGET(m_toolbar));
406
gtk_widget_show(GTK_WIDGET(m_toolbar));
408
m_parent->DoAddChild( this );
415
GdkWindow *wxToolBar::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
417
return gtk_widget_get_window(GTK_WIDGET(m_toolbar));
420
void wxToolBar::GtkSetStyle()
422
GtkOrientation orient = GTK_ORIENTATION_HORIZONTAL;
423
if (HasFlag(wxTB_LEFT | wxTB_RIGHT))
424
orient = GTK_ORIENTATION_VERTICAL;
426
GtkToolbarStyle style = GTK_TOOLBAR_ICONS;
427
if (HasFlag(wxTB_NOICONS))
428
style = GTK_TOOLBAR_TEXT;
429
else if (HasFlag(wxTB_TEXT))
431
style = GTK_TOOLBAR_BOTH;
432
if (HasFlag(wxTB_HORZ_LAYOUT))
433
style = GTK_TOOLBAR_BOTH_HORIZ;
437
gtk_orientable_set_orientation(GTK_ORIENTABLE(m_toolbar), orient);
439
gtk_toolbar_set_orientation(m_toolbar, orient);
441
gtk_toolbar_set_style(m_toolbar, style);
444
void wxToolBar::SetWindowStyleFlag( long style )
446
wxToolBarBase::SetWindowStyleFlag(style);
452
bool wxToolBar::Realize()
454
if ( !wxToolBarBase::Realize() )
457
// bring the initial state of all the toolbar items in line with the
458
// internal state if the latter was changed by calling wxToolBarTool::
459
// Enable(): this works under MSW, where the toolbar items are only created
460
// in Realize() which uses the internal state to determine the initial
461
// button state, so make it work under GTK too
462
for ( wxToolBarToolsList::const_iterator i = m_tools.begin();
466
// by default the toolbar items are enabled and not toggled, so we only
467
// have to do something if their internal state doesn't correspond to
469
if ( !(*i)->IsEnabled() )
470
DoEnableTool(*i, false);
471
if ( (*i)->IsToggled() )
472
DoToggleTool(*i, true);
478
bool wxToolBar::DoInsertTool(size_t pos, wxToolBarToolBase *toolBase)
480
wxToolBarTool* tool = static_cast<wxToolBarTool*>(toolBase);
483
GtkWidget* bin_child;
484
switch ( tool->GetStyle() )
486
case wxTOOL_STYLE_BUTTON:
487
switch (tool->GetKind())
490
tool->m_item = gtk_toggle_tool_button_new();
491
g_signal_connect(tool->m_item, "toggled",
492
G_CALLBACK(item_toggled), tool);
495
radioGroup = GetRadioGroup(pos);
498
// this is the first button in the radio button group,
499
// it will be toggled automatically by GTK so bring the
500
// internal flag in sync
503
tool->m_item = gtk_radio_tool_button_new(radioGroup);
504
g_signal_connect(tool->m_item, "toggled",
505
G_CALLBACK(item_toggled), tool);
508
wxFAIL_MSG("unknown toolbar child type");
510
case wxITEM_DROPDOWN:
512
tool->m_item = gtk_tool_button_new(NULL, "");
513
g_signal_connect(tool->m_item, "clicked",
514
G_CALLBACK(item_clicked), tool);
517
if (!HasFlag(wxTB_NOICONS))
519
GtkWidget* image = gtk_image_new();
520
gtk_tool_button_set_icon_widget(
521
GTK_TOOL_BUTTON(tool->m_item), image);
523
gtk_widget_show(image);
525
g_signal_connect(image, "draw",
526
G_CALLBACK(image_draw), tool);
528
g_signal_connect(image, "expose_event",
529
G_CALLBACK(image_expose_event), tool);
532
if (!tool->GetLabel().empty())
534
gtk_tool_button_set_label(
535
GTK_TOOL_BUTTON(tool->m_item), wxGTK_CONV(tool->GetLabel()));
536
// needed for labels in horizontal toolbar with wxTB_HORZ_LAYOUT
537
gtk_tool_item_set_is_important(tool->m_item, true);
539
if (!HasFlag(wxTB_NO_TOOLTIPS) && !tool->GetShortHelp().empty())
541
#if GTK_CHECK_VERSION(2, 12, 0)
542
if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,12,0) == NULL)
544
gtk_tool_item_set_tooltip_text(tool->m_item,
545
wxGTK_CONV(tool->GetShortHelp()));
551
gtk_tool_item_set_tooltip(tool->m_item,
552
m_tooltips, wxGTK_CONV(tool->GetShortHelp()), "");
556
bin_child = gtk_bin_get_child(GTK_BIN(tool->m_item));
557
g_signal_connect(bin_child, "button_press_event",
558
G_CALLBACK(button_press_event), tool);
559
g_signal_connect(bin_child, "enter_notify_event",
560
G_CALLBACK(enter_notify_event), tool);
561
g_signal_connect(bin_child, "leave_notify_event",
562
G_CALLBACK(enter_notify_event), tool);
564
if (tool->GetKind() == wxITEM_DROPDOWN)
565
tool->CreateDropDown();
566
gtk_toolbar_insert(m_toolbar, tool->m_item, int(pos));
569
case wxTOOL_STYLE_SEPARATOR:
570
tool->m_item = gtk_separator_tool_item_new();
571
if ( tool->IsStretchable() )
573
gtk_separator_tool_item_set_draw
575
GTK_SEPARATOR_TOOL_ITEM(tool->m_item),
578
gtk_tool_item_set_expand(tool->m_item, TRUE);
580
gtk_toolbar_insert(m_toolbar, tool->m_item, int(pos));
583
case wxTOOL_STYLE_CONTROL:
584
wxWindow* control = tool->GetControl();
585
if (gtk_widget_get_parent(control->m_widget) == NULL)
586
AddChildGTK(control);
587
tool->m_item = GTK_TOOL_ITEM(gtk_widget_get_parent(gtk_widget_get_parent(control->m_widget)));
588
if (gtk_toolbar_get_item_index(m_toolbar, tool->m_item) != int(pos))
590
g_object_ref(tool->m_item);
591
gtk_container_remove(
592
GTK_CONTAINER(m_toolbar), GTK_WIDGET(tool->m_item));
593
gtk_toolbar_insert(m_toolbar, tool->m_item, int(pos));
594
g_object_unref(tool->m_item);
598
gtk_widget_show(GTK_WIDGET(tool->m_item));
600
InvalidateBestSize();
605
bool wxToolBar::DoDeleteTool(size_t /* pos */, wxToolBarToolBase* toolBase)
607
wxToolBarTool* tool = static_cast<wxToolBarTool*>(toolBase);
609
if (tool->GetStyle() == wxTOOL_STYLE_CONTROL)
611
// don't destroy the control here as we can be called from
612
// RemoveTool() and then we need to keep the control alive;
613
// while if we're called from DeleteTool() the control will
614
// be destroyed when wxToolBarToolBase itself is deleted
615
GtkWidget* widget = tool->GetControl()->m_widget;
616
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget);
618
gtk_widget_destroy(GTK_WIDGET(tool->m_item));
621
InvalidateBestSize();
625
GSList* wxToolBar::GetRadioGroup(size_t pos)
627
GSList* radioGroup = NULL;
628
GtkToolItem* item = NULL;
631
item = gtk_toolbar_get_nth_item(m_toolbar, int(pos) - 1);
632
if (!GTK_IS_RADIO_TOOL_BUTTON(item))
635
if (item == NULL && pos < m_tools.size())
637
item = gtk_toolbar_get_nth_item(m_toolbar, int(pos));
638
if (!GTK_IS_RADIO_TOOL_BUTTON(item))
642
radioGroup = gtk_radio_tool_button_get_group((GtkRadioToolButton*)item);
646
// ----------------------------------------------------------------------------
647
// wxToolBar tools state
648
// ----------------------------------------------------------------------------
650
void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
652
wxToolBarTool* tool = static_cast<wxToolBarTool*>(toolBase);
655
gtk_widget_set_sensitive(GTK_WIDGET(tool->m_item), enable);
658
void wxToolBar::DoToggleTool( wxToolBarToolBase *toolBase, bool toggle )
660
wxToolBarTool* tool = static_cast<wxToolBarTool*>(toolBase);
664
g_signal_handlers_block_by_func(tool->m_item, (void*)item_toggled, tool);
666
gtk_toggle_tool_button_set_active(
667
GTK_TOGGLE_TOOL_BUTTON(tool->m_item), toggle);
669
g_signal_handlers_unblock_by_func(tool->m_item, (void*)item_toggled, tool);
673
void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
674
bool WXUNUSED(toggle))
676
// VZ: absolutely no idea about how to do it
677
wxFAIL_MSG( wxT("not implemented") );
680
// ----------------------------------------------------------------------------
681
// wxToolBar geometry
682
// ----------------------------------------------------------------------------
684
wxSize wxToolBar::DoGetBestSize() const
686
// Unfortunately, if overflow arrow is enabled GtkToolbar only reports size
687
// of arrow. To get the real size, the arrow is temporarily disabled here.
688
// This is gross, since it will cause a queue_resize, and could potentially
689
// lead to an infinite loop. But there seems to be no alternative, short of
690
// disabling the arrow entirely.
691
gtk_toolbar_set_show_arrow(m_toolbar, false);
692
const wxSize size = wxToolBarBase::DoGetBestSize();
693
gtk_toolbar_set_show_arrow(m_toolbar, true);
697
wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord WXUNUSED(x),
698
wxCoord WXUNUSED(y)) const
700
// VZ: GTK+ doesn't seem to have such thing
701
wxFAIL_MSG( wxT("wxToolBar::FindToolForPosition() not implemented") );
706
void wxToolBar::SetToolShortHelp( int id, const wxString& helpString )
708
wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
712
(void)tool->SetShortHelp(helpString);
715
#if GTK_CHECK_VERSION(2, 12, 0)
716
if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,12,0) == NULL)
718
gtk_tool_item_set_tooltip_text(tool->m_item,
719
wxGTK_CONV(helpString));
725
gtk_tool_item_set_tooltip(tool->m_item,
726
m_tooltips, wxGTK_CONV(helpString), "");
733
void wxToolBar::SetToolNormalBitmap( int id, const wxBitmap& bitmap )
735
wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
738
wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));
740
tool->SetNormalBitmap(bitmap);
745
void wxToolBar::SetToolDisabledBitmap( int id, const wxBitmap& bitmap )
747
wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
750
wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));
752
tool->SetDisabledBitmap(bitmap);
756
// ----------------------------------------------------------------------------
760
wxToolBar::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
762
return GetDefaultAttributesFromGTKWidget(gtk_toolbar_new());
765
#endif // wxUSE_TOOLBAR_NATIVE