]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/tbargtk.cpp
really fix binary backwards compatibility for wxMotif
[wxWidgets.git] / src / gtk / tbargtk.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: tbargtk.cpp
3 // Purpose: GTK toolbar
4 // Author: Robert Roebling
5 // Modified: 13.12.99 by VZ to derive from wxToolBarBase
6 // RCS-ID: $Id$
7 // Copyright: (c) Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
20 #pragma implementation "tbargtk.h"
21 #endif
22
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
25
26 #include "wx/toolbar.h"
27
28 #if wxUSE_TOOLBAR_NATIVE
29
30 #include "wx/frame.h"
31
32 #include <glib.h>
33 #include "wx/gtk/private.h"
34
35 // ----------------------------------------------------------------------------
36 // globals
37 // ----------------------------------------------------------------------------
38
39 // idle system
40 extern void wxapp_install_idle_handler();
41 extern bool g_isIdle;
42
43 // data
44 extern bool g_blockEventsOnDrag;
45 extern wxCursor g_globalCursor;
46
47 // ----------------------------------------------------------------------------
48 // private functions
49 // ----------------------------------------------------------------------------
50
51 // translate wxWidgets toolbar style flags to GTK orientation and style
52 static void GetGtkStyle(long style,
53 GtkOrientation *orient, GtkToolbarStyle *gtkStyle)
54 {
55 *orient = style & wxTB_VERTICAL ? GTK_ORIENTATION_VERTICAL
56 : GTK_ORIENTATION_HORIZONTAL;
57
58
59 if ( style & wxTB_TEXT )
60 {
61 *gtkStyle = style & wxTB_NOICONS
62 ? GTK_TOOLBAR_TEXT
63 : (
64 #ifdef __WXGTK20__
65 style & wxTB_HORZ_LAYOUT ? GTK_TOOLBAR_BOTH_HORIZ :
66 #endif // __WXGTK20__
67 GTK_TOOLBAR_BOTH);
68 }
69 else // no text, hence we must have the icons or what would we show?
70 {
71 *gtkStyle = GTK_TOOLBAR_ICONS;
72 }
73 }
74
75 // ----------------------------------------------------------------------------
76 // wxToolBarTool
77 // ----------------------------------------------------------------------------
78
79 class wxToolBarTool : public wxToolBarToolBase
80 {
81 public:
82 wxToolBarTool(wxToolBar *tbar,
83 int id,
84 const wxString& label,
85 const wxBitmap& bitmap1,
86 const wxBitmap& bitmap2,
87 wxItemKind kind,
88 wxObject *clientData,
89 const wxString& shortHelpString,
90 const wxString& longHelpString)
91 : wxToolBarToolBase(tbar, id, label, bitmap1, bitmap2, kind,
92 clientData, shortHelpString, longHelpString)
93 {
94 Init();
95 }
96
97 wxToolBarTool(wxToolBar *tbar, wxControl *control)
98 : wxToolBarToolBase(tbar, control)
99 {
100 Init();
101 }
102
103 // is this a radio button?
104 //
105 // unlike GetKind(), can be called for any kind of tools, not just buttons
106 bool IsRadio() const { return IsButton() && GetKind() == wxITEM_RADIO; }
107
108 // this is only called for the normal buttons, i.e. not separators nor
109 // controls
110 GtkToolbarChildType GetGtkChildType() const
111 {
112 switch ( GetKind() )
113 {
114 case wxITEM_CHECK:
115 return GTK_TOOLBAR_CHILD_TOGGLEBUTTON;
116
117 case wxITEM_RADIO:
118 return GTK_TOOLBAR_CHILD_RADIOBUTTON;
119
120 default:
121 wxFAIL_MSG( _T("unknown toolbar child type") );
122 // fall through
123
124 case wxITEM_NORMAL:
125 return GTK_TOOLBAR_CHILD_BUTTON;
126 }
127 }
128
129 void SetPixmap(const wxBitmap& bitmap)
130 {
131 if (bitmap.Ok())
132 {
133 GdkBitmap *mask = bitmap.GetMask() ? bitmap.GetMask()->GetBitmap()
134 : (GdkBitmap *)NULL;
135 #ifdef __WXGTK20__
136 if (bitmap.HasPixbuf())
137 gtk_image_set_from_pixbuf( GTK_IMAGE(m_pixmap), bitmap.GetPixbuf() );
138 else
139 #endif // !__WXGTK20__
140 gtk_pixmap_set( GTK_PIXMAP(m_pixmap), bitmap.GetPixmap(), mask );
141 }
142 }
143
144 GtkWidget *m_item;
145 GtkWidget *m_pixmap;
146
147 protected:
148 void Init();
149 };
150
151 // ----------------------------------------------------------------------------
152 // wxWin macros
153 // ----------------------------------------------------------------------------
154
155 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
156
157 // ============================================================================
158 // implementation
159 // ============================================================================
160
161 //-----------------------------------------------------------------------------
162 // "clicked" (internal from gtk_toolbar)
163 //-----------------------------------------------------------------------------
164
165 extern "C" {
166 static void gtk_toolbar_callback( GtkWidget *WXUNUSED(widget),
167 wxToolBarTool *tool )
168 {
169 if (g_isIdle)
170 wxapp_install_idle_handler();
171
172 wxToolBar *tbar = (wxToolBar *)tool->GetToolBar();
173
174 if (tbar->m_blockEvent) return;
175
176 if (g_blockEventsOnDrag) return;
177 if (!tool->IsEnabled()) return;
178
179 if (tool->CanBeToggled())
180 {
181 tool->Toggle();
182
183 tool->SetPixmap(tool->GetBitmap());
184
185 if ( tool->IsRadio() && !tool->IsToggled() )
186 {
187 // radio button went up, don't report this as a wxWin event
188 return;
189 }
190 }
191
192 if( !tbar->OnLeftClick( tool->GetId(), tool->IsToggled() ) && tool->CanBeToggled() )
193 {
194 // revert back
195 tool->Toggle();
196
197 tool->SetPixmap(tool->GetBitmap());
198 }
199 }
200 }
201
202 //-----------------------------------------------------------------------------
203 // "enter_notify_event" / "leave_notify_event"
204 //-----------------------------------------------------------------------------
205
206 extern "C" {
207 static gint gtk_toolbar_tool_callback( GtkWidget *WXUNUSED(widget),
208 GdkEventCrossing *gdk_event,
209 wxToolBarTool *tool )
210 {
211 if (g_isIdle) wxapp_install_idle_handler();
212
213 if (g_blockEventsOnDrag) return TRUE;
214
215 wxToolBar *tb = (wxToolBar *)tool->GetToolBar();
216
217 // emit the event
218 if( gdk_event->type == GDK_ENTER_NOTIFY )
219 tb->OnMouseEnter( tool->GetId() );
220 else
221 tb->OnMouseEnter( -1 );
222
223 return FALSE;
224 }
225 }
226
227 //-----------------------------------------------------------------------------
228 // InsertChild callback for wxToolBar
229 //-----------------------------------------------------------------------------
230
231 static void wxInsertChildInToolBar( wxToolBar* WXUNUSED(parent),
232 wxWindow* WXUNUSED(child) )
233 {
234 // we don't do anything here
235 }
236
237 // ----------------------------------------------------------------------------
238 // wxToolBarTool
239 // ----------------------------------------------------------------------------
240
241 void wxToolBarTool::Init()
242 {
243 m_item =
244 m_pixmap = (GtkWidget *)NULL;
245 }
246
247 wxToolBarToolBase *wxToolBar::CreateTool(int id,
248 const wxString& text,
249 const wxBitmap& bitmap1,
250 const wxBitmap& bitmap2,
251 wxItemKind kind,
252 wxObject *clientData,
253 const wxString& shortHelpString,
254 const wxString& longHelpString)
255 {
256 return new wxToolBarTool(this, id, text, bitmap1, bitmap2, kind,
257 clientData, shortHelpString, longHelpString);
258 }
259
260 wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
261 {
262 return new wxToolBarTool(this, control);
263 }
264
265 //-----------------------------------------------------------------------------
266 // wxToolBar construction
267 //-----------------------------------------------------------------------------
268
269 void wxToolBar::Init()
270 {
271 m_toolbar = (GtkToolbar *)NULL;
272 m_blockEvent = false;
273 m_defaultWidth = 32;
274 m_defaultHeight = 32;
275 }
276
277 wxToolBar::~wxToolBar()
278 {
279 }
280
281 bool wxToolBar::Create( wxWindow *parent,
282 wxWindowID id,
283 const wxPoint& pos,
284 const wxSize& size,
285 long style,
286 const wxString& name )
287 {
288 m_needParent = true;
289 m_insertCallback = (wxInsertChildFunction)wxInsertChildInToolBar;
290
291 if ( !PreCreation( parent, pos, size ) ||
292 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
293 {
294 wxFAIL_MSG( wxT("wxToolBar creation failed") );
295
296 return false;
297 }
298
299 #ifdef __WXGTK20__
300 m_toolbar = GTK_TOOLBAR( gtk_toolbar_new() );
301 GtkSetStyle();
302
303 // Doesn't work this way.
304 // GtkToolbarSpaceStyle space_style = GTK_TOOLBAR_SPACE_EMPTY;
305 // gtk_widget_style_set (GTK_WIDGET (m_toolbar), "space_style", &space_style, NULL);
306 #else
307 GtkOrientation orient;
308 GtkToolbarStyle gtkStyle;
309 GetGtkStyle(style, &orient, &gtkStyle);
310
311 m_toolbar = GTK_TOOLBAR( gtk_toolbar_new(orient, gtkStyle) );
312 #endif
313
314 SetToolSeparation(7);
315
316 if (style & wxTB_DOCKABLE)
317 {
318 m_widget = gtk_handle_box_new();
319 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_toolbar) );
320 gtk_widget_show( GTK_WIDGET(m_toolbar) );
321
322 if (style & wxTB_FLAT)
323 gtk_handle_box_set_shadow_type( GTK_HANDLE_BOX(m_widget), GTK_SHADOW_NONE );
324 }
325 else
326 {
327 m_widget = gtk_event_box_new();
328 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_toolbar) );
329 ConnectWidget( m_widget );
330 gtk_widget_show(GTK_WIDGET(m_toolbar));
331 }
332
333 gtk_toolbar_set_tooltips( GTK_TOOLBAR(m_toolbar), TRUE );
334
335 // FIXME: there is no such function for toolbars in 2.0
336 #ifndef __WXGTK20__
337 if (style & wxTB_FLAT)
338 gtk_toolbar_set_button_relief( GTK_TOOLBAR(m_toolbar), GTK_RELIEF_NONE );
339 #endif
340
341 m_parent->DoAddChild( this );
342
343 PostCreation(size);
344
345 return true;
346 }
347
348 void wxToolBar::GtkSetStyle()
349 {
350 GtkOrientation orient;
351 GtkToolbarStyle style;
352 GetGtkStyle(GetWindowStyle(), &orient, &style);
353
354 gtk_toolbar_set_orientation(m_toolbar, orient);
355 gtk_toolbar_set_style(m_toolbar, style);
356 }
357
358 void wxToolBar::SetWindowStyleFlag( long style )
359 {
360 wxToolBarBase::SetWindowStyleFlag(style);
361
362 if ( m_toolbar )
363 GtkSetStyle();
364 }
365
366 bool wxToolBar::DoInsertTool(size_t pos, wxToolBarToolBase *toolBase)
367 {
368 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
369
370 #ifndef __WXGTK20__
371 // if we have inserted a space before all the tools we must change the GTK
372 // index by 1
373 size_t posGtk = m_xMargin > 1 ? pos + 1 : pos;
374 #else
375 size_t posGtk = pos;
376 #endif
377
378 if ( tool->IsButton() )
379 {
380 if ( !HasFlag(wxTB_NOICONS) )
381 {
382 wxBitmap bitmap = tool->GetNormalBitmap();
383
384 wxCHECK_MSG( bitmap.Ok(), false,
385 wxT("invalid bitmap for wxToolBar icon") );
386
387 wxCHECK_MSG( bitmap.GetBitmap() == NULL, false,
388 wxT("wxToolBar doesn't support GdkBitmap") );
389
390 wxCHECK_MSG( bitmap.GetPixmap() != NULL, false,
391 wxT("wxToolBar::Add needs a wxBitmap") );
392
393 GtkWidget *tool_pixmap = (GtkWidget *)NULL;
394
395
396 #ifdef __WXGTK20__
397 if (bitmap.HasPixbuf())
398 {
399 tool_pixmap = gtk_image_new();
400 tool->m_pixmap = tool_pixmap;
401 tool->SetPixmap(bitmap);
402 }
403 else
404 #endif
405 {
406 GdkPixmap *pixmap = bitmap.GetPixmap();
407
408 GdkBitmap *mask = (GdkBitmap *)NULL;
409 if ( bitmap.GetMask() )
410 mask = bitmap.GetMask()->GetBitmap();
411
412 tool_pixmap = gtk_pixmap_new( pixmap, mask );
413 gtk_pixmap_set_build_insensitive( GTK_PIXMAP(tool_pixmap), TRUE );
414 }
415
416 gtk_misc_set_alignment( GTK_MISC(tool_pixmap), 0.5, 0.5 );
417
418 tool->m_pixmap = tool_pixmap;
419 }
420 }
421
422 switch ( tool->GetStyle() )
423 {
424 case wxTOOL_STYLE_BUTTON:
425 // for a radio button we need the widget which starts the radio
426 // group it belongs to, i.e. the first radio button immediately
427 // preceding this one
428 {
429 GtkWidget *widget = NULL;
430
431 if ( tool->IsRadio() )
432 {
433 wxToolBarToolsList::compatibility_iterator node
434 = wxToolBarToolsList::compatibility_iterator();
435 if ( pos ) node = m_tools.Item(pos - 1);
436
437 while ( node )
438 {
439 wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
440 if ( !tool->IsRadio() )
441 break;
442
443 widget = tool->m_item;
444
445 node = node->GetPrevious();
446 }
447
448 if ( !widget )
449 {
450 // this is the first button in the radio button group,
451 // it will be toggled automatically by GTK so bring the
452 // internal flag in sync
453 tool->Toggle(true);
454 }
455 }
456
457 tool->m_item = gtk_toolbar_insert_element
458 (
459 m_toolbar,
460 tool->GetGtkChildType(),
461 widget,
462 tool->GetLabel().empty()
463 ? NULL
464 : (const char*) wxGTK_CONV( tool->GetLabel() ),
465 tool->GetShortHelp().empty()
466 ? NULL
467 : (const char*) wxGTK_CONV( tool->GetShortHelp() ),
468 "", // tooltip_private_text (?)
469 tool->m_pixmap,
470 (GtkSignalFunc)gtk_toolbar_callback,
471 (gpointer)tool,
472 posGtk
473 );
474
475 if ( !tool->m_item )
476 {
477 wxFAIL_MSG( _T("gtk_toolbar_insert_element() failed") );
478
479 return false;
480 }
481
482 gtk_signal_connect( GTK_OBJECT(tool->m_item),
483 "enter_notify_event",
484 GTK_SIGNAL_FUNC(gtk_toolbar_tool_callback),
485 (gpointer)tool );
486 gtk_signal_connect( GTK_OBJECT(tool->m_item),
487 "leave_notify_event",
488 GTK_SIGNAL_FUNC(gtk_toolbar_tool_callback),
489 (gpointer)tool );
490 }
491 break;
492
493 case wxTOOL_STYLE_SEPARATOR:
494 gtk_toolbar_insert_space( m_toolbar, posGtk );
495
496 // skip the rest
497 return true;
498
499 case wxTOOL_STYLE_CONTROL:
500 gtk_toolbar_insert_widget(
501 m_toolbar,
502 tool->GetControl()->m_widget,
503 (const char *) NULL,
504 (const char *) NULL,
505 posGtk
506 );
507 break;
508 }
509
510 GtkRequisition req;
511 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_widget) )->size_request )
512 (m_widget, &req );
513 m_width = req.width + m_xMargin;
514 m_height = req.height + 2*m_yMargin;
515 InvalidateBestSize();
516
517 return true;
518 }
519
520 bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *toolBase)
521 {
522 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
523
524 switch ( tool->GetStyle() )
525 {
526 case wxTOOL_STYLE_CONTROL:
527 tool->GetControl()->Destroy();
528 break;
529
530 case wxTOOL_STYLE_BUTTON:
531 gtk_widget_destroy( tool->m_item );
532 break;
533
534 #ifdef __WXGTK20__
535 case wxTOOL_STYLE_SEPARATOR:
536 gtk_toolbar_remove_space( m_toolbar, pos );
537 break;
538 #endif
539 }
540
541 InvalidateBestSize();
542 return true;
543 }
544
545 // ----------------------------------------------------------------------------
546 // wxToolBar tools state
547 // ----------------------------------------------------------------------------
548
549 void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
550 {
551 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
552
553 if (tool->m_item)
554 {
555 gtk_widget_set_sensitive( tool->m_item, enable );
556 }
557 }
558
559 void wxToolBar::DoToggleTool( wxToolBarToolBase *toolBase, bool toggle )
560 {
561 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
562
563 GtkWidget *item = tool->m_item;
564 if ( item && GTK_IS_TOGGLE_BUTTON(item) )
565 {
566 tool->SetPixmap(tool->GetBitmap());
567
568 m_blockEvent = true;
569
570 gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(item), toggle );
571
572 m_blockEvent = false;
573 }
574 }
575
576 void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
577 bool WXUNUSED(toggle))
578 {
579 // VZ: absolutely no idea about how to do it
580 wxFAIL_MSG( _T("not implemented") );
581 }
582
583 // ----------------------------------------------------------------------------
584 // wxToolBar geometry
585 // ----------------------------------------------------------------------------
586
587 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord WXUNUSED(x),
588 wxCoord WXUNUSED(y)) const
589 {
590 // VZ: GTK+ doesn't seem to have such thing
591 wxFAIL_MSG( _T("wxToolBar::FindToolForPosition() not implemented") );
592
593 return (wxToolBarToolBase *)NULL;
594 }
595
596 void wxToolBar::SetMargins( int x, int y )
597 {
598 wxCHECK_RET( GetToolsCount() == 0,
599 wxT("wxToolBar::SetMargins must be called before adding tools.") );
600
601 #ifndef __WXGTK20__
602 if (x > 1)
603 gtk_toolbar_append_space( m_toolbar ); // oh well
604 #endif
605
606 m_xMargin = x;
607 m_yMargin = y;
608 }
609
610 void wxToolBar::SetToolSeparation( int separation )
611 {
612 // FIXME: this function disappeared
613 #ifndef __WXGTK20__
614 gtk_toolbar_set_space_size( m_toolbar, separation );
615 #endif
616
617 m_toolSeparation = separation;
618 }
619
620 void wxToolBar::SetToolShortHelp( int id, const wxString& helpString )
621 {
622 wxToolBarTool *tool = (wxToolBarTool *)FindById(id);
623
624 if ( tool )
625 {
626 (void)tool->SetShortHelp(helpString);
627 gtk_tooltips_set_tip(m_toolbar->tooltips, tool->m_item,
628 wxGTK_CONV( helpString ), "");
629 }
630 }
631
632 // ----------------------------------------------------------------------------
633 // wxToolBar idle handling
634 // ----------------------------------------------------------------------------
635
636 void wxToolBar::OnInternalIdle()
637 {
638 wxCursor cursor = m_cursor;
639 if (g_globalCursor.Ok()) cursor = g_globalCursor;
640
641 if (cursor.Ok())
642 {
643 /* I now set the cursor the anew in every OnInternalIdle call
644 as setting the cursor in a parent window also effects the
645 windows above so that checking for the current cursor is
646 not possible. */
647
648 if (HasFlag(wxTB_DOCKABLE) && (m_widget->window))
649 {
650 /* if the toolbar is dockable, then m_widget stands for the
651 GtkHandleBox widget, which uses its own window so that we
652 can set the cursor for it. if the toolbar is not dockable,
653 m_widget comes from m_toolbar which uses its parent's
654 window ("windowless windows") and thus we cannot set the
655 cursor. */
656 gdk_window_set_cursor( m_widget->window, cursor.GetCursor() );
657 }
658
659 wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
660 while ( node )
661 {
662 wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
663 node = node->GetNext();
664
665 GtkWidget *item = tool->m_item;
666 if ( item )
667 {
668 GdkWindow *window = item->window;
669
670 if ( window )
671 {
672 gdk_window_set_cursor( window, cursor.GetCursor() );
673 }
674 }
675 }
676 }
677
678 if (wxUpdateUIEvent::CanUpdate(this))
679 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
680 }
681
682
683 // ----------------------------------------------------------------------------
684
685 // static
686 wxVisualAttributes
687 wxToolBar::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
688 {
689 #ifdef __WXGTK20__
690 return GetDefaultAttributesFromGTKWidget(gtk_toolbar_new);
691 #else
692 wxVisualAttributes attr;
693 GtkWidget* widget = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
694 attr = GetDefaultAttributesFromGTKWidget(widget);
695 gtk_widget_destroy(widget);
696 return attr;
697 #endif
698 }
699
700 #endif // wxUSE_TOOLBAR_NATIVE