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