1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk1/notebook.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling, Vadim Zeitlin
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_NOTEBOOK
13 
14 #include "wx/notebook.h"
15 
16 #ifndef WX_PRECOMP
17     #include "wx/intl.h"
18     #include "wx/log.h"
19     #include "wx/utils.h"
20     #include "wx/panel.h"
21     #include "wx/msgdlg.h"
22     #include "wx/bitmap.h"
23 #endif
24 
25 #include "wx/imaglist.h"
26 #include "wx/fontutil.h"
27 
28 #include "wx/gtk1/private.h"
29 #include "wx/gtk1/win_gtk.h"
30 
31 #include <gdk/gdkkeysyms.h>
32 
33 //-----------------------------------------------------------------------------
34 // idle system
35 //-----------------------------------------------------------------------------
36 
37 extern void wxapp_install_idle_handler();
38 extern bool g_isIdle;
39 
40 //-----------------------------------------------------------------------------
41 // data
42 //-----------------------------------------------------------------------------
43 
44 extern bool g_blockEventsOnDrag;
45 
46 //-----------------------------------------------------------------------------
47 // wxGtkNotebookPage
48 //-----------------------------------------------------------------------------
49 
50 // VZ: this is rather ugly as we keep the pages themselves in an array (it
51 //     allows us to have quite a few functions implemented in the base class)
52 //     but the page data is kept in a separate list, so we must maintain them
53 //     in sync manually... of course, the list had been there before the base
54 //     class which explains it but it still would be nice to do something
55 //     about this one day
56 
57 class wxGtkNotebookPage: public wxObject
58 {
59 public:
wxGtkNotebookPage()60     wxGtkNotebookPage()
61     {
62         m_image = -1;
63         m_page = NULL;
64         m_box = NULL;
65     }
66 
67     wxString           m_text;
68     int                m_image;
69     GtkNotebookPage   *m_page;
70     GtkLabel          *m_label;
71     GtkWidget         *m_box;     // in which the label and image are packed
72 };
73 
74 
75 #include "wx/listimpl.cpp"
WX_DEFINE_LIST(wxGtkNotebookPagesList)76 WX_DEFINE_LIST(wxGtkNotebookPagesList)
77 
78 
79 //-----------------------------------------------------------------------------
80 // "switch_page"
81 //-----------------------------------------------------------------------------
82 
83 extern "C" {
84 static void gtk_notebook_page_change_callback(GtkNotebook *WXUNUSED(widget),
85                                               GtkNotebookPage *WXUNUSED(page),
86                                               gint page,
87                                               wxNotebook *notebook )
88 {
89     // are you trying to call SetSelection() from a notebook event handler?
90     // you shouldn't!
91     wxCHECK_RET( !notebook->m_inSwitchPage,
92                  wxT("gtk_notebook_page_change_callback reentered") );
93 
94     notebook->m_inSwitchPage = true;
95     if (g_isIdle)
96         wxapp_install_idle_handler();
97 
98     int old = notebook->GetSelection();
99 
100     if (notebook->m_skipNextPageChangeEvent)
101     {
102         // this event was programmatically generated by ChangeSelection() and thus must
103         // be skipped
104         notebook->m_skipNextPageChangeEvent = false;
105 
106         // make wxNotebook::GetSelection() return the correct (i.e. consistent
107         // with wxBookCtrlEvent::GetSelection()) value even though the page is
108         // not really changed in GTK+
109         notebook->SetSelection(page);
110     }
111     else
112     {
113         if ( !notebook->SendPageChangingEvent(page) )
114         {
115             // program doesn't allow the page change
116             gtk_signal_emit_stop_by_name(GTK_OBJECT(notebook->m_widget), "switch_page");
117         }
118         else // change allowed
119         {
120             // make wxNotebook::GetSelection() return the correct (i.e. consistent
121             // with wxBookCtrlEvent::GetSelection()) value even though the page is
122             // not really changed in GTK+
123             notebook->SetSelection(page);
124 
125             notebook->SendPageChangedEvent(old);
126         }
127     }
128 
129     notebook->m_inSwitchPage = FALSE;
130 }
131 }
132 
133 //-----------------------------------------------------------------------------
134 // "size_allocate"
135 //-----------------------------------------------------------------------------
136 
137 extern "C" {
gtk_page_size_callback(GtkWidget * WXUNUSED (widget),GtkAllocation * alloc,wxWindow * win)138 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
139 {
140     if (g_isIdle)
141         wxapp_install_idle_handler();
142 
143     if ((win->m_x == alloc->x) &&
144         (win->m_y == alloc->y) &&
145         (win->m_width == alloc->width) &&
146         (win->m_height == alloc->height))
147     {
148         return;
149     }
150 
151     win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
152 
153     /* GTK 1.2 up to version 1.2.5 is broken so that we have to call allocate
154        here in order to make repositioning after resizing to take effect. */
155     if ((gtk_major_version == 1) &&
156         (gtk_minor_version == 2) &&
157         (gtk_micro_version < 6) &&
158         (win->m_wxwindow) &&
159         (GTK_WIDGET_REALIZED(win->m_wxwindow)))
160     {
161         gtk_widget_size_allocate( win->m_wxwindow, alloc );
162     }
163 }
164 }
165 
166 //-----------------------------------------------------------------------------
167 // "realize" from m_widget
168 //-----------------------------------------------------------------------------
169 
170 extern "C" {
171 static gint
gtk_notebook_realized_callback(GtkWidget * WXUNUSED (widget),wxWindow * win)172 gtk_notebook_realized_callback( GtkWidget * WXUNUSED(widget), wxWindow *win )
173 {
174     if (g_isIdle)
175         wxapp_install_idle_handler();
176 
177     /* GTK 1.2 up to version 1.2.5 is broken so that we have to call a queue_resize
178        here in order to make repositioning before showing to take effect. */
179     gtk_widget_queue_resize( win->m_widget );
180 
181     return FALSE;
182 }
183 }
184 
185 //-----------------------------------------------------------------------------
186 // "key_press_event"
187 //-----------------------------------------------------------------------------
188 
189 extern "C" {
gtk_notebook_key_press_callback(GtkWidget * widget,GdkEventKey * gdk_event,wxNotebook * notebook)190 static gint gtk_notebook_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxNotebook *notebook )
191 {
192     if (g_isIdle)
193         wxapp_install_idle_handler();
194 
195     if (!notebook->m_hasVMT) return FALSE;
196     if (g_blockEventsOnDrag) return FALSE;
197 
198     /* win is a control: tab can be propagated up */
199     if ((gdk_event->keyval == GDK_Left) || (gdk_event->keyval == GDK_Right))
200     {
201         int page;
202         int nMax = notebook->GetPageCount();
203         if ( nMax-- ) // decrement it to get the last valid index
204         {
205             int nSel = notebook->GetSelection();
206 
207             // change selection wrapping if it becomes invalid
208             page = (gdk_event->keyval != GDK_Left) ? nSel == nMax ? 0
209                                        : nSel + 1
210                         : nSel == 0 ? nMax
211                                     : nSel - 1;
212         }
213         else // notebook is empty, no next page
214         {
215             return FALSE;
216         }
217 
218         // m_selection = page;
219         gtk_notebook_set_page( GTK_NOTEBOOK(widget), page );
220 
221         gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
222         return TRUE;
223     }
224 
225     /* win is a control: tab can be propagated up */
226     if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
227     {
228         int sel = notebook->GetSelection();
229         if (sel == wxNOT_FOUND)
230             return TRUE;
231         wxGtkNotebookPage *nb_page = notebook->GetNotebookPage(sel);
232         wxCHECK_MSG( nb_page, FALSE, wxT("invalid selection in wxNotebook") );
233 
234         wxNavigationKeyEvent event;
235         event.SetEventObject( notebook );
236         /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
237         event.SetDirection( (gdk_event->keyval == GDK_Tab) );
238         /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
239         event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) ||
240                                (gdk_event->keyval == GDK_Left) || (gdk_event->keyval == GDK_Right) );
241         event.SetCurrentFocus( notebook );
242 
243         wxNotebookPage *client = notebook->GetPage(sel);
244         if ( !client->HandleWindowEvent( event ) )
245         {
246              client->SetFocus();
247         }
248 
249         gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
250         return TRUE;
251     }
252 
253     return FALSE;
254 }
255 }
256 
257 //-----------------------------------------------------------------------------
258 // InsertChild callback for wxNotebook
259 //-----------------------------------------------------------------------------
260 
wxInsertChildInNotebook(wxNotebook * parent,wxWindow * child)261 static void wxInsertChildInNotebook( wxNotebook* parent, wxWindow* child )
262 {
263     // Hack Alert! (Part I): This sets the notebook as the parent of the child
264     // widget, and takes care of some details such as updating the state and
265     // style of the child to reflect its new location.  We do this early
266     // because without it GetBestSize (which is used to set the initial size
267     // of controls if an explicit size is not given) will often report
268     // incorrect sizes since the widget's style context is not fully known.
269     // See bug #901694 for details
270     // (http://sourceforge.net/tracker/?func=detail&aid=901694&group_id=9863&atid=109863)
271     gtk_widget_set_parent(child->m_widget, parent->m_widget);
272 
273     // NOTE: This should be considered a temporary workaround until we can
274     // work out the details and implement delaying the setting of the initial
275     // size of widgets until the size is really needed.
276 }
277 
278 //-----------------------------------------------------------------------------
279 // wxNotebook
280 //-----------------------------------------------------------------------------
281 
BEGIN_EVENT_TABLE(wxNotebook,wxBookCtrlBase)282 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
283     EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
284 END_EVENT_TABLE()
285 
286 void wxNotebook::Init()
287 {
288     m_padding = 0;
289     m_inSwitchPage = false;
290 
291     m_themeEnabled = true;
292 }
293 
wxNotebook()294 wxNotebook::wxNotebook()
295 {
296     Init();
297 }
298 
wxNotebook(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)299 wxNotebook::wxNotebook( wxWindow *parent, wxWindowID id,
300       const wxPoint& pos, const wxSize& size,
301       long style, const wxString& name )
302 {
303     Init();
304     Create( parent, id, pos, size, style, name );
305 }
306 
~wxNotebook()307 wxNotebook::~wxNotebook()
308 {
309     DeleteAllPages();
310 }
311 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)312 bool wxNotebook::Create(wxWindow *parent, wxWindowID id,
313                         const wxPoint& pos, const wxSize& size,
314                         long style, const wxString& name )
315 {
316     m_needParent = true;
317     m_acceptsFocus = true;
318     m_insertCallback = (wxInsertChildFunction)wxInsertChildInNotebook;
319 
320     if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
321         style |= wxBK_TOP;
322 
323     if (!PreCreation( parent, pos, size ) ||
324         !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
325     {
326         wxFAIL_MSG( wxT("wxNoteBook creation failed") );
327         return false;
328     }
329 
330 
331     m_widget = gtk_notebook_new();
332 
333     gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
334 
335     gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
336       GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
337 
338     m_parent->DoAddChild( this );
339 
340     if (m_windowStyle & wxBK_RIGHT)
341         gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_RIGHT );
342     if (m_windowStyle & wxBK_LEFT)
343         gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_LEFT );
344     if (m_windowStyle & wxBK_BOTTOM)
345         gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_BOTTOM );
346 
347     gtk_signal_connect( GTK_OBJECT(m_widget), "key_press_event",
348       GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback), (gpointer)this );
349 
350     PostCreation(size);
351 
352     gtk_signal_connect( GTK_OBJECT(m_widget), "realize",
353                             GTK_SIGNAL_FUNC(gtk_notebook_realized_callback), (gpointer) this );
354 
355     return true;
356 }
357 
GetSelection() const358 int wxNotebook::GetSelection() const
359 {
360     wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid notebook") );
361 
362     if ( m_selection == wxNOT_FOUND )
363     {
364         GList *nb_pages = GTK_NOTEBOOK(m_widget)->children;
365 
366         if (g_list_length(nb_pages) != 0)
367         {
368             GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
369 
370             gpointer cur = notebook->cur_page;
371             if ( cur != NULL )
372             {
373                 const_cast<wxNotebook *>(this)->
374                     SetSelection(g_list_index( nb_pages, cur ));
375             }
376         }
377     }
378 
379     return m_selection;
380 }
381 
GetPageText(size_t page) const382 wxString wxNotebook::GetPageText( size_t page ) const
383 {
384     wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid notebook") );
385 
386     wxGtkNotebookPage* nb_page = GetNotebookPage(page);
387     if (nb_page)
388         return nb_page->m_text;
389     else
390         return wxEmptyString;
391 }
392 
GetPageImage(size_t page) const393 int wxNotebook::GetPageImage( size_t page ) const
394 {
395     wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
396 
397     wxGtkNotebookPage* nb_page = GetNotebookPage(page);
398     if (nb_page)
399         return nb_page->m_image;
400     else
401         return -1;
402 }
403 
GetNotebookPage(int page) const404 wxGtkNotebookPage* wxNotebook::GetNotebookPage( int page ) const
405 {
406     wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid notebook") );
407 
408     wxCHECK_MSG( page < (int)m_pagesData.GetCount(), NULL, wxT("invalid notebook index") );
409 
410     return m_pagesData.Item(page)->GetData();
411 }
412 
DoSetSelection(size_t page,int flags)413 int wxNotebook::DoSetSelection( size_t page, int flags )
414 {
415     wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid notebook") );
416 
417     wxCHECK_MSG( page < m_pagesData.GetCount(), -1, wxT("invalid notebook index") );
418 
419     int selOld = GetSelection();
420 
421     if ( !(flags & SetSelection_SendEvent) )
422         m_skipNextPageChangeEvent = true;
423 
424     // cache the selection
425     m_selection = page;
426     gtk_notebook_set_page( GTK_NOTEBOOK(m_widget), page );
427 
428     // gtk_notebook_set_current_page is supposed to emit the switch-page signal
429     // which should be caught by our gtk_notebook_page_change_callback which
430     // should have reset the flag to false, check it:
431     wxASSERT_LEVEL_2_MSG(
432         (flags & SetSelection_SendEvent) || !m_skipNextPageChangeEvent,
433         "internal error in selection events generation"
434     );
435 
436     wxNotebookPage *client = GetPage(page);
437     if ( client )
438         client->SetFocus();
439 
440     return selOld;
441 }
442 
SetPageText(size_t page,const wxString & text)443 bool wxNotebook::SetPageText( size_t page, const wxString &text )
444 {
445     wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
446 
447     wxGtkNotebookPage* nb_page = GetNotebookPage(page);
448 
449     wxCHECK_MSG( nb_page, FALSE, wxT("SetPageText: invalid page index") );
450 
451     nb_page->m_text = text;
452 
453     gtk_label_set( nb_page->m_label, wxGTK_CONV( nb_page->m_text ) );
454 
455     return true;
456 }
457 
SetPageImage(size_t page,int image)458 bool wxNotebook::SetPageImage( size_t page, int image )
459 {
460     /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
461 
462     wxGtkNotebookPage* nb_page = GetNotebookPage(page);
463 
464     if (!nb_page) return FALSE;
465 
466     /* Optimization posibility: return immediately if image unchanged.
467      * Not enabled because it may break existing (stupid) code that
468      * manipulates the imagelist to cycle images */
469 
470     /* if (image == nb_page->m_image) return true; */
471 
472     /* For different cases:
473        1) no image -> no image
474        2) image -> no image
475        3) no image -> image
476        4) image -> image */
477 
478     if (image == -1 && nb_page->m_image == -1)
479         return true; /* Case 1): Nothing to do. */
480 
481     GtkWidget *pixmapwid = NULL;
482 
483     if (nb_page->m_image != -1)
484     {
485         /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
486 
487         GList *child = gtk_container_children(GTK_CONTAINER(nb_page->m_box));
488         while (child)
489         {
490             if (GTK_IS_PIXMAP(child->data))
491             {
492                 pixmapwid = GTK_WIDGET(child->data);
493                 break;
494             }
495             child = child->next;
496         }
497 
498         /* We should have the pixmap widget now */
499         wxASSERT(pixmapwid != NULL);
500 
501         if (image == -1)
502         {
503             /* If there's no new widget, just remove the old from the box */
504             gtk_container_remove(GTK_CONTAINER(nb_page->m_box), pixmapwid);
505             nb_page->m_image = -1;
506 
507             return true; /* Case 2) */
508         }
509     }
510 
511     /* Only cases 3) and 4) left */
512     wxASSERT( HasImageList() ); /* Just in case */
513 
514     /* Construct the new pixmap */
515     const wxBitmap *bmp = GetImageList()->GetBitmapPtr(image);
516     GdkPixmap *pixmap = bmp->GetPixmap();
517     GdkBitmap *mask = NULL;
518     if ( bmp->GetMask() )
519     {
520         mask = bmp->GetMask()->GetBitmap();
521     }
522 
523     if (pixmapwid == NULL)
524     {
525         /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
526         pixmapwid = gtk_pixmap_new (pixmap, mask );
527 
528         /* CHECKME: Are these pack flags okay? */
529         gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
530         gtk_widget_show(pixmapwid);
531     }
532     else
533     {
534         /* Case 4) Simply replace the pixmap */
535         gtk_pixmap_set(GTK_PIXMAP(pixmapwid), pixmap, mask);
536     }
537 
538     nb_page->m_image = image;
539 
540     return true;
541 }
542 
SetPageSize(const wxSize & WXUNUSED (size))543 void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
544 {
545     wxFAIL_MSG( wxT("wxNotebook::SetPageSize not implemented") );
546 }
547 
SetPadding(const wxSize & padding)548 void wxNotebook::SetPadding( const wxSize &padding )
549 {
550     wxCHECK_RET( m_widget != NULL, wxT("invalid notebook") );
551 
552     m_padding = padding.GetWidth();
553 
554     int i;
555     for (i=0; i<int(GetPageCount()); i++)
556     {
557         wxGtkNotebookPage* nb_page = GetNotebookPage(i);
558         wxASSERT(nb_page != NULL);
559 
560         if (nb_page->m_image != -1)
561         {
562             // gtk_box_set_child_packing sets padding on BOTH sides
563             // icon provides left padding, label provides center and right
564             int image = nb_page->m_image;
565             SetPageImage(i,-1);
566             SetPageImage(i,image);
567         }
568         wxASSERT(nb_page->m_label);
569         gtk_box_set_child_packing(GTK_BOX(nb_page->m_box),
570                                   GTK_WIDGET(nb_page->m_label),
571                                   FALSE, FALSE, m_padding, GTK_PACK_END);
572     }
573 }
574 
SetTabSize(const wxSize & WXUNUSED (sz))575 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
576 {
577     wxFAIL_MSG( wxT("wxNotebook::SetTabSize not implemented") );
578 }
579 
DeleteAllPages()580 bool wxNotebook::DeleteAllPages()
581 {
582     wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
583 
584     while (m_pagesData.GetCount() > 0)
585         DeletePage( m_pagesData.GetCount()-1 );
586 
587     wxASSERT_MSG( GetPageCount() == 0, wxT("all pages must have been deleted") );
588 
589     InvalidateBestSize();
590     return wxNotebookBase::DeleteAllPages();
591 }
592 
DoRemovePage(size_t page)593 wxNotebookPage *wxNotebook::DoRemovePage( size_t page )
594 {
595     if ( m_selection != wxNOT_FOUND && (size_t)m_selection >= page )
596     {
597         // the index will become invalid after the page is deleted
598         m_selection = wxNOT_FOUND;
599     }
600 
601     wxNotebookPage *client = wxNotebookBase::DoRemovePage(page);
602     if ( !client )
603         return NULL;
604 
605     gtk_widget_ref( client->m_widget );
606     gtk_widget_unrealize( client->m_widget );
607     gtk_widget_unparent( client->m_widget );
608 
609     // gtk_notebook_remove_page() sends "switch_page" signal with some strange
610     // new page index (when deleting selected page 0, new page is 1 although,
611     // clearly, the selection should stay 0), so suppress this
612     gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
613       GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer) this );
614 
615     gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page );
616 
617     gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
618       GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
619 
620     wxGtkNotebookPage* p = GetNotebookPage(page);
621     m_pagesData.DeleteObject(p);
622     delete p;
623 
624     return client;
625 }
626 
InsertPage(size_t position,wxNotebookPage * win,const wxString & text,bool select,int imageId)627 bool wxNotebook::InsertPage( size_t position,
628                              wxNotebookPage* win,
629                              const wxString& text,
630                              bool select,
631                              int imageId )
632 {
633     wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
634 
635     wxCHECK_MSG( win->GetParent() == this, FALSE,
636                wxT("Can't add a page whose parent is not the notebook!") );
637 
638     wxCHECK_MSG( position <= GetPageCount(), FALSE,
639                  wxT("invalid page index in wxNotebookPage::InsertPage()") );
640 
641     // Hack Alert! (Part II): See above in wxInsertChildInNotebook callback
642     // why this has to be done.  NOTE: using gtk_widget_unparent here does not
643     // work as it seems to undo too much and will cause errors in the
644     // gtk_notebook_insert_page below, so instead just clear the parent by
645     // hand here.
646     win->m_widget->parent = NULL;
647 
648     // don't receive switch page during addition
649     gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
650       GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer) this );
651 
652     if (m_themeEnabled)
653         win->SetThemeEnabled(true);
654 
655     GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
656 
657     wxGtkNotebookPage *nb_page = new wxGtkNotebookPage();
658 
659     if ( position == GetPageCount() )
660         m_pagesData.Append( nb_page );
661     else
662         m_pagesData.Insert( position, nb_page );
663 
664     m_pages.Insert(win, position);
665 
666     nb_page->m_box = gtk_hbox_new( FALSE, 1 );
667     gtk_container_border_width( GTK_CONTAINER(nb_page->m_box), 2 );
668 
669     gtk_signal_connect( GTK_OBJECT(win->m_widget), "size_allocate",
670       GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)win );
671 
672     gtk_notebook_insert_page( notebook, win->m_widget, nb_page->m_box, position );
673 
674     nb_page->m_page = (GtkNotebookPage*) g_list_last(notebook->children)->data;
675 
676     /* set the label image */
677     nb_page->m_image = imageId;
678 
679     if (imageId != -1)
680     {
681         wxASSERT( HasImageList() );
682 
683         const wxBitmap *bmp = GetImageList()->GetBitmapPtr(imageId);
684         GdkPixmap *pixmap = bmp->GetPixmap();
685         GdkBitmap *mask = NULL;
686         if ( bmp->GetMask() )
687         {
688             mask = bmp->GetMask()->GetBitmap();
689         }
690 
691         GtkWidget *pixmapwid = gtk_pixmap_new (pixmap, mask );
692 
693         gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
694 
695         gtk_widget_show(pixmapwid);
696     }
697 
698     /* set the label text */
699 
700     nb_page->m_text = text;
701     if (nb_page->m_text.empty()) nb_page->m_text = wxEmptyString;
702 
703     nb_page->m_label = GTK_LABEL( gtk_label_new(wxGTK_CONV(nb_page->m_text)) );
704     gtk_box_pack_end( GTK_BOX(nb_page->m_box), GTK_WIDGET(nb_page->m_label), FALSE, FALSE, m_padding );
705 
706     /* apply current style */
707     GtkRcStyle *style = CreateWidgetStyle();
708     if ( style )
709     {
710         gtk_widget_modify_style(GTK_WIDGET(nb_page->m_label), style);
711         gtk_rc_style_unref(style);
712     }
713 
714     /* show the label */
715     gtk_widget_show( GTK_WIDGET(nb_page->m_label) );
716     if (select && (m_pagesData.GetCount() > 1))
717     {
718       SetSelection( position );
719     }
720 
721     gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
722       GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
723 
724     InvalidateBestSize();
725     return true;
726 }
727 
728 // helper for HitTest(): check if the point lies inside the given widget which
729 // is the child of the notebook whose position and border size are passed as
730 // parameters
731 static bool
IsPointInsideWidget(const wxPoint & pt,GtkWidget * w,gint x,gint y,gint border=0)732 IsPointInsideWidget(const wxPoint& pt, GtkWidget *w,
733                     gint x, gint y, gint border = 0)
734 {
735     return
736         (pt.x >= w->allocation.x - x - border) &&
737         (pt.x <= w->allocation.x - x + border + w->allocation.width) &&
738         (pt.y >= w->allocation.y - y - border) &&
739         (pt.y <= w->allocation.y - y + border + w->allocation.height);
740 }
741 
HitTest(const wxPoint & pt,long * flags) const742 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
743 {
744     const gint x = m_widget->allocation.x;
745     const gint y = m_widget->allocation.y;
746 
747     const size_t count = GetPageCount();
748     size_t i = 0;
749 
750     // MR: Code to fix HitTest index return when tabs are scrolled.
751     // No idea if it would work for GTK1
752 #if 0
753     GtkNotebook * notebook = GTK_NOTEBOOK(m_widget);
754     if (gtk_notebook_get_scrollable(notebook));
755         i = g_list_position( notebook->children, notebook->first_tab );
756 #endif
757 
758     for ( ; i < count; i++ )
759     {
760         wxGtkNotebookPage* nb_page = GetNotebookPage(i);
761         GtkWidget *box = nb_page->m_box;
762 
763         // VZ: don't know how to find the border width in GTK+ 1.2
764         const gint border = 0;
765         if ( IsPointInsideWidget(pt, box, x, y, border) )
766         {
767             // ok, we're inside this tab -- now find out where, if needed
768             if ( flags )
769             {
770                 GtkWidget *pixmap = NULL;
771 
772                 GList *children = gtk_container_children(GTK_CONTAINER(box));
773                 for ( GList *child = children; child; child = child->next )
774                 {
775                     if ( GTK_IS_PIXMAP(child->data) )
776                     {
777                         pixmap = GTK_WIDGET(child->data);
778                         break;
779                     }
780                 }
781 
782                 if ( children )
783                     g_list_free(children);
784 
785                 if ( pixmap && IsPointInsideWidget(pt, pixmap, x, y) )
786                 {
787                     *flags = wxBK_HITTEST_ONICON;
788                 }
789                 else if ( IsPointInsideWidget(pt, GTK_WIDGET(nb_page->m_label), x, y) )
790                 {
791                     *flags = wxBK_HITTEST_ONLABEL;
792                 }
793                 else
794                 {
795                     *flags = wxBK_HITTEST_ONITEM;
796                 }
797             }
798 
799             return i;
800         }
801     }
802 
803     if ( flags )
804         *flags = wxBK_HITTEST_NOWHERE;
805 
806     return wxNOT_FOUND;
807 }
808 
OnNavigationKey(wxNavigationKeyEvent & event)809 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
810 {
811     if (event.IsWindowChange())
812         AdvanceSelection( event.GetDirection() );
813     else
814         event.Skip();
815 }
816 
817 #if wxUSE_CONSTRAINTS
818 
819 // override these 2 functions to do nothing: everything is done in OnSize
SetConstraintSizes(bool WXUNUSED (recurse))820 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
821 {
822     // don't set the sizes of the pages - their correct size is not yet known
823     wxControl::SetConstraintSizes(FALSE);
824 }
825 
DoPhase(int WXUNUSED (nPhase))826 bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
827 {
828     return true;
829 }
830 
831 #endif
832 
DoApplyWidgetStyle(GtkRcStyle * style)833 void wxNotebook::DoApplyWidgetStyle(GtkRcStyle *style)
834 {
835     gtk_widget_modify_style(m_widget, style);
836     size_t cnt = m_pagesData.GetCount();
837     for (size_t i = 0; i < cnt; i++)
838         gtk_widget_modify_style(GTK_WIDGET(GetNotebookPage(i)->m_label), style);
839 }
840 
IsOwnGtkWindow(GdkWindow * window)841 bool wxNotebook::IsOwnGtkWindow( GdkWindow *window )
842 {
843     return ((m_widget->window == window) ||
844             (NOTEBOOK_PANEL(m_widget) == window));
845 }
846 
847 // static
848 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))849 wxNotebook::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
850 {
851     return GetDefaultAttributesFromGTKWidget(gtk_notebook_new);
852 }
853 
854 #endif
855