1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk1/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 /////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #if wxUSE_TOOLBAR_NATIVE
22 
23 #include "wx/toolbar.h"
24 
25 #ifndef WX_PRECOMP
26     #include "wx/frame.h"
27 #endif
28 
29 #include <glib.h>
30 #include "wx/gtk1/private.h"
31 
32 // ----------------------------------------------------------------------------
33 // globals
34 // ----------------------------------------------------------------------------
35 
36 // idle system
37 extern void wxapp_install_idle_handler();
38 extern bool g_isIdle;
39 
40 // data
41 extern bool       g_blockEventsOnDrag;
42 extern wxCursor   g_globalCursor;
43 
44 // ----------------------------------------------------------------------------
45 // private functions
46 // ----------------------------------------------------------------------------
47 
48 // translate wxWidgets toolbar style flags to GTK orientation and style
GetGtkStyle(long style,GtkOrientation * orient,GtkToolbarStyle * gtkStyle)49 static void GetGtkStyle(long style,
50                         GtkOrientation *orient, GtkToolbarStyle *gtkStyle)
51 {
52     *orient = style & wxTB_VERTICAL ? GTK_ORIENTATION_VERTICAL
53                                     : GTK_ORIENTATION_HORIZONTAL;
54 
55 
56     if ( style & wxTB_TEXT )
57     {
58         *gtkStyle = style & wxTB_NOICONS
59                         ? GTK_TOOLBAR_TEXT
60                         : GTK_TOOLBAR_BOTH;
61     }
62     else // no text, hence we must have the icons or what would we show?
63     {
64         *gtkStyle = GTK_TOOLBAR_ICONS;
65     }
66 }
67 
68 // ----------------------------------------------------------------------------
69 // wxToolBarTool
70 // ----------------------------------------------------------------------------
71 
72 class wxToolBarTool : public wxToolBarToolBase
73 {
74 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)75     wxToolBarTool(wxToolBar *tbar,
76                   int id,
77                   const wxString& label,
78                   const wxBitmap& bitmap1,
79                   const wxBitmap& bitmap2,
80                   wxItemKind kind,
81                   wxObject *clientData,
82                   const wxString& shortHelpString,
83                   const wxString& longHelpString)
84         : wxToolBarToolBase(tbar, id, label, bitmap1, bitmap2, kind,
85                             clientData, shortHelpString, longHelpString)
86     {
87         Init();
88     }
89 
wxToolBarTool(wxToolBar * tbar,wxControl * control,const wxString & label)90     wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
91         : wxToolBarToolBase(tbar, control, label)
92     {
93         Init();
94     }
95 
96     // is this a radio button?
97     //
98     // unlike GetKind(), can be called for any kind of tools, not just buttons
IsRadio() const99     bool IsRadio() const { return IsButton() && GetKind() == wxITEM_RADIO; }
100 
101     // this is only called for the normal buttons, i.e. not separators nor
102     // controls
GetGtkChildType() const103     GtkToolbarChildType GetGtkChildType() const
104     {
105         switch ( GetKind() )
106         {
107             case wxITEM_CHECK:
108                 return GTK_TOOLBAR_CHILD_TOGGLEBUTTON;
109 
110             case wxITEM_RADIO:
111                 return GTK_TOOLBAR_CHILD_RADIOBUTTON;
112 
113             default:
114                 wxFAIL_MSG( wxT("unknown toolbar child type") );
115                 // fall through
116 
117             case wxITEM_NORMAL:
118                 return GTK_TOOLBAR_CHILD_BUTTON;
119         }
120     }
121 
SetPixmap(const wxBitmap & bitmap)122     void SetPixmap(const wxBitmap& bitmap)
123     {
124         if (bitmap.IsOk())
125         {
126             GdkBitmap *mask = bitmap.GetMask() ? bitmap.GetMask()->GetBitmap()
127                                                : NULL;
128             gtk_pixmap_set( GTK_PIXMAP(m_pixmap), bitmap.GetPixmap(), mask );
129         }
130     }
131 
132     GtkWidget            *m_item;
133     GtkWidget            *m_pixmap;
134 
135 protected:
136     void Init();
137 };
138 
139 // ----------------------------------------------------------------------------
140 // wxWin macros
141 // ----------------------------------------------------------------------------
142 
IMPLEMENT_DYNAMIC_CLASS(wxToolBar,wxControl)143 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
144 
145 // ============================================================================
146 // implementation
147 // ============================================================================
148 
149 //-----------------------------------------------------------------------------
150 // "clicked" (internal from gtk_toolbar)
151 //-----------------------------------------------------------------------------
152 
153 extern "C" {
154 static void gtk_toolbar_callback( GtkWidget *WXUNUSED(widget),
155                                   wxToolBarTool *tool )
156 {
157     if (g_isIdle)
158         wxapp_install_idle_handler();
159 
160     wxToolBar *tbar = (wxToolBar *)tool->GetToolBar();
161 
162     if (tbar->m_blockEvent) return;
163 
164     if (g_blockEventsOnDrag) return;
165     if (!tool->IsEnabled()) return;
166 
167     if (tool->CanBeToggled())
168     {
169         tool->Toggle();
170 
171         tool->SetPixmap(tool->GetBitmap());
172 
173         if ( tool->IsRadio() && !tool->IsToggled() )
174         {
175             // radio button went up, don't report this as a wxWin event
176             return;
177         }
178     }
179 
180     if( !tbar->OnLeftClick( tool->GetId(), tool->IsToggled() ) && tool->CanBeToggled() )
181     {
182         // revert back
183         tool->Toggle();
184 
185         tool->SetPixmap(tool->GetBitmap());
186     }
187 }
188 }
189 
190 //-----------------------------------------------------------------------------
191 // "enter_notify_event" / "leave_notify_event"
192 //-----------------------------------------------------------------------------
193 
194 extern "C" {
gtk_toolbar_tool_callback(GtkWidget * WXUNUSED (widget),GdkEventCrossing * gdk_event,wxToolBarTool * tool)195 static gint gtk_toolbar_tool_callback( GtkWidget *WXUNUSED(widget),
196                                        GdkEventCrossing *gdk_event,
197                                        wxToolBarTool *tool )
198 {
199     if (g_isIdle) wxapp_install_idle_handler();
200 
201     if (g_blockEventsOnDrag) return TRUE;
202 
203     wxToolBar *tb = (wxToolBar *)tool->GetToolBar();
204 
205     // emit the event
206     if( gdk_event->type == GDK_ENTER_NOTIFY )
207         tb->OnMouseEnter( tool->GetId() );
208     else
209         tb->OnMouseEnter( -1 );
210 
211     return FALSE;
212 }
213 }
214 
215 //-----------------------------------------------------------------------------
216 // InsertChild callback for wxToolBar
217 //-----------------------------------------------------------------------------
218 
wxInsertChildInToolBar(wxToolBar * WXUNUSED (parent),wxWindow * WXUNUSED (child))219 static void wxInsertChildInToolBar( wxToolBar* WXUNUSED(parent),
220                                     wxWindow* WXUNUSED(child) )
221 {
222     // we don't do anything here
223 }
224 
225 // ----------------------------------------------------------------------------
226 // wxToolBarTool
227 // ----------------------------------------------------------------------------
228 
Init()229 void wxToolBarTool::Init()
230 {
231     m_item =
232     m_pixmap = NULL;
233 }
234 
CreateTool(int id,const wxString & text,const wxBitmap & bitmap1,const wxBitmap & bitmap2,wxItemKind kind,wxObject * clientData,const wxString & shortHelpString,const wxString & longHelpString)235 wxToolBarToolBase *wxToolBar::CreateTool(int id,
236                                          const wxString& text,
237                                          const wxBitmap& bitmap1,
238                                          const wxBitmap& bitmap2,
239                                          wxItemKind kind,
240                                          wxObject *clientData,
241                                          const wxString& shortHelpString,
242                                          const wxString& longHelpString)
243 {
244     return new wxToolBarTool(this, id, text, bitmap1, bitmap2, kind,
245                              clientData, shortHelpString, longHelpString);
246 }
247 
248 wxToolBarToolBase *
CreateTool(wxControl * control,const wxString & label)249 wxToolBar::CreateTool(wxControl *control, const wxString& label)
250 {
251     return new wxToolBarTool(this, control, label);
252 }
253 
254 //-----------------------------------------------------------------------------
255 // wxToolBar construction
256 //-----------------------------------------------------------------------------
257 
Init()258 void wxToolBar::Init()
259 {
260     m_toolbar = 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.IsOk(), 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 = NULL;
369 
370             GdkPixmap *pixmap = bitmap.GetPixmap();
371 
372             GdkBitmap *mask = 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( wxT("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 WXUNUSED (pos),wxToolBarToolBase * toolBase)484 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolBase)
485 {
486     wxToolBarTool *tool = (wxToolBarTool *)toolBase;
487 
488     switch ( tool->GetStyle() )
489     {
490         case wxTOOL_STYLE_CONTROL:
491             // don't destroy the control here as we can be called from
492             // RemoveTool() and then we need to keep the control alive;
493             // while if we're called from DeleteTool() the control will
494             // be destroyed when wxToolBarToolBase itself is deleted
495             break;
496 
497         case wxTOOL_STYLE_BUTTON:
498             gtk_widget_destroy( tool->m_item );
499             break;
500 
501         default:
502             wxFAIL_MSG( "unknown tool style" );
503             return false;
504     }
505 
506     InvalidateBestSize();
507     return true;
508 }
509 
510 // ----------------------------------------------------------------------------
511 // wxToolBar tools state
512 // ----------------------------------------------------------------------------
513 
DoEnableTool(wxToolBarToolBase * toolBase,bool enable)514 void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
515 {
516     wxToolBarTool *tool = (wxToolBarTool *)toolBase;
517 
518     if (tool->m_item)
519     {
520         gtk_widget_set_sensitive( tool->m_item, enable );
521     }
522 }
523 
DoToggleTool(wxToolBarToolBase * toolBase,bool toggle)524 void wxToolBar::DoToggleTool( wxToolBarToolBase *toolBase, bool toggle )
525 {
526     wxToolBarTool *tool = (wxToolBarTool *)toolBase;
527 
528     GtkWidget *item = tool->m_item;
529     if ( item && GTK_IS_TOGGLE_BUTTON(item) )
530     {
531         tool->SetPixmap(tool->GetBitmap());
532 
533         m_blockEvent = true;
534 
535         gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(item), toggle );
536 
537         m_blockEvent = false;
538     }
539 }
540 
DoSetToggle(wxToolBarToolBase * WXUNUSED (tool),bool WXUNUSED (toggle))541 void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
542                             bool WXUNUSED(toggle))
543 {
544     // VZ: absolutely no idea about how to do it
545     wxFAIL_MSG( wxT("not implemented") );
546 }
547 
548 // ----------------------------------------------------------------------------
549 // wxToolBar geometry
550 // ----------------------------------------------------------------------------
551 
FindToolForPosition(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y)) const552 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord WXUNUSED(x),
553                                                   wxCoord WXUNUSED(y)) const
554 {
555     // VZ: GTK+ doesn't seem to have such thing
556     wxFAIL_MSG( wxT("wxToolBar::FindToolForPosition() not implemented") );
557 
558     return NULL;
559 }
560 
SetMargins(int x,int y)561 void wxToolBar::SetMargins( int x, int y )
562 {
563     wxCHECK_RET( GetToolsCount() == 0,
564                  wxT("wxToolBar::SetMargins must be called before adding tools.") );
565 
566     if (x > 1)
567         gtk_toolbar_append_space( m_toolbar );  // oh well
568 
569     m_xMargin = x;
570     m_yMargin = y;
571 }
572 
SetToolSeparation(int separation)573 void wxToolBar::SetToolSeparation( int separation )
574 {
575     gtk_toolbar_set_space_size( m_toolbar, separation );
576 
577     m_toolSeparation = separation;
578 }
579 
SetToolShortHelp(int id,const wxString & helpString)580 void wxToolBar::SetToolShortHelp( int id, const wxString& helpString )
581 {
582     wxToolBarTool *tool = (wxToolBarTool *)FindById(id);
583 
584     if ( tool )
585     {
586         (void)tool->SetShortHelp(helpString);
587         gtk_tooltips_set_tip(m_toolbar->tooltips, tool->m_item,
588                              wxGTK_CONV( helpString ), "");
589     }
590 }
591 
592 // ----------------------------------------------------------------------------
593 // wxToolBar idle handling
594 // ----------------------------------------------------------------------------
595 
OnInternalIdle()596 void wxToolBar::OnInternalIdle()
597 {
598     wxCursor cursor = m_cursor;
599     if (g_globalCursor.IsOk()) cursor = g_globalCursor;
600 
601     if (cursor.IsOk())
602     {
603         /* I now set the cursor the anew in every OnInternalIdle call
604            as setting the cursor in a parent window also effects the
605            windows above so that checking for the current cursor is
606            not possible. */
607 
608         if (HasFlag(wxTB_DOCKABLE) && (m_widget->window))
609         {
610             /* if the toolbar is dockable, then m_widget stands for the
611                GtkHandleBox widget, which uses its own window so that we
612                can set the cursor for it. if the toolbar is not dockable,
613                m_widget comes from m_toolbar which uses its parent's
614                window ("windowless windows") and thus we cannot set the
615                cursor. */
616             gdk_window_set_cursor( m_widget->window, cursor.GetCursor() );
617         }
618 
619         wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
620         while ( node )
621         {
622             wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
623             node = node->GetNext();
624 
625             GtkWidget *item = tool->m_item;
626             if ( item )
627             {
628                 GdkWindow *window = item->window;
629 
630                 if ( window )
631                 {
632                     gdk_window_set_cursor( window, cursor.GetCursor() );
633                 }
634             }
635         }
636     }
637 
638     if (wxUpdateUIEvent::CanUpdate(this))
639         UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
640 }
641 
642 
643 // ----------------------------------------------------------------------------
644 
645 // static
646 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))647 wxToolBar::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
648 {
649     wxVisualAttributes attr;
650     GtkWidget* widget = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
651     attr = GetDefaultAttributesFromGTKWidget(widget);
652     gtk_widget_destroy(widget);
653     return attr;
654 }
655 
656 #endif // wxUSE_TOOLBAR_NATIVE
657