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