fix for infinite sizing loop (partial patch 1907189)
[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
30 #include <gdk/gdkkeysyms.h>
31
32 // ----------------------------------------------------------------------------
33 // events
34 // ----------------------------------------------------------------------------
35
36 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
37 DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
38
39 //-----------------------------------------------------------------------------
40 // data
41 //-----------------------------------------------------------------------------
42
43 extern bool g_blockEventsOnDrag;
44
45 //-----------------------------------------------------------------------------
46 // wxGtkNotebookPage
47 //-----------------------------------------------------------------------------
48
49 // VZ: this is rather ugly as we keep the pages themselves in an array (it
50 // allows us to have quite a few functions implemented in the base class)
51 // but the page data is kept in a separate list, so we must maintain them
52 // in sync manually... of course, the list had been there before the base
53 // class which explains it but it still would be nice to do something
54 // about this one day
55
56 class wxGtkNotebookPage: public wxObject
57 {
58 public:
59 wxGtkNotebookPage()
60 {
61 m_image = -1;
62 m_page = (GtkNotebookPage *) NULL;
63 m_box = (GtkWidget *) NULL;
64 }
65
66 wxString m_text;
67 int m_image;
68 GtkNotebookPage *m_page;
69 GtkLabel *m_label;
70 GtkWidget *m_box; // in which the label and image are packed
71 };
72
73
74 #include "wx/listimpl.cpp"
75 WX_DEFINE_LIST(wxGtkNotebookPagesList)
76
77
78 //-----------------------------------------------------------------------------
79 // "switch_page"
80 //-----------------------------------------------------------------------------
81
82 extern "C" {
83 static void gtk_notebook_page_changing_callback( GtkNotebook *widget,
84 GtkNotebookPage *WXUNUSED(gpage),
85 guint page,
86 wxNotebook *notebook )
87 {
88 int old = gtk_notebook_get_current_page( widget );
89
90 if ( !notebook->SendPageChangingEvent(page) )
91 {
92 // program doesn't allow the page change
93 g_signal_stop_emission_by_name(notebook->m_widget, "switch_page");
94 }
95 else
96 {
97 // the page change event also reports the old page
98 notebook->m_oldSelection = old;
99 }
100 }
101 }
102
103 extern "C" {
104 static void gtk_notebook_page_changed_callback( GtkNotebook * WXUNUSED(widget),
105 GtkNotebookPage *WXUNUSED(gpage),
106 guint WXUNUSED(page),
107 wxNotebook *notebook )
108 {
109 int old = notebook->m_oldSelection;
110 notebook->SendPageChangedEvent( old );
111 }
112 }
113
114 //-----------------------------------------------------------------------------
115 // "realize" from m_widget
116 //-----------------------------------------------------------------------------
117
118 extern "C" {
119 static void
120 gtk_notebook_realized_callback( GtkWidget * WXUNUSED(widget), wxWindow *win )
121 {
122 /* GTK 1.2 up to version 1.2.5 is broken so that we have to call a queue_resize
123 here in order to make repositioning before showing to take effect. */
124 gtk_widget_queue_resize( win->m_widget );
125 }
126 }
127
128 //-----------------------------------------------------------------------------
129 // InsertChild callback for wxNotebook
130 //-----------------------------------------------------------------------------
131
132 static void wxInsertChildInNotebook(wxWindow* parent, wxWindow* child)
133 {
134 // Hack Alert! (Part I): This sets the notebook as the parent of the child
135 // widget, and takes care of some details such as updating the state and
136 // style of the child to reflect its new location. We do this early
137 // because without it GetBestSize (which is used to set the initial size
138 // of controls if an explicit size is not given) will often report
139 // incorrect sizes since the widget's style context is not fully known.
140 // See bug #901694 for details
141 // (http://sourceforge.net/tracker/?func=detail&aid=901694&group_id=9863&atid=109863)
142 gtk_widget_set_parent(child->m_widget, parent->m_widget);
143
144 // NOTE: This should be considered a temporary workaround until we can
145 // work out the details and implement delaying the setting of the initial
146 // size of widgets until the size is really needed.
147 }
148
149 //-----------------------------------------------------------------------------
150 // wxNotebook
151 //-----------------------------------------------------------------------------
152
153 IMPLEMENT_DYNAMIC_CLASS(wxNotebook,wxBookCtrlBase)
154
155 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
156 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
157 END_EVENT_TABLE()
158
159 void wxNotebook::Init()
160 {
161 m_padding = 0;
162
163 m_imageList = (wxImageList *) NULL;
164 m_oldSelection = -1;
165 m_themeEnabled = true;
166 }
167
168 wxNotebook::wxNotebook()
169 {
170 Init();
171 }
172
173 wxNotebook::wxNotebook( wxWindow *parent, wxWindowID id,
174 const wxPoint& pos, const wxSize& size,
175 long style, const wxString& name )
176 {
177 Init();
178 Create( parent, id, pos, size, style, name );
179 }
180
181 wxNotebook::~wxNotebook()
182 {
183 DeleteAllPages();
184 }
185
186 bool wxNotebook::Create(wxWindow *parent, wxWindowID id,
187 const wxPoint& pos, const wxSize& size,
188 long style, const wxString& name )
189 {
190 m_insertCallback = wxInsertChildInNotebook;
191
192 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
193 style |= wxBK_TOP;
194
195 if (!PreCreation( parent, pos, size ) ||
196 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
197 {
198 wxFAIL_MSG( wxT("wxNoteBook creation failed") );
199 return false;
200 }
201
202
203 m_widget = gtk_notebook_new();
204
205 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
206
207 g_signal_connect (m_widget, "switch_page",
208 G_CALLBACK (gtk_notebook_page_changing_callback), this);
209
210 g_signal_connect_after (m_widget, "switch_page",
211 G_CALLBACK (gtk_notebook_page_changed_callback), this);
212
213 m_parent->DoAddChild( this );
214
215 if (m_windowStyle & wxBK_RIGHT)
216 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_RIGHT );
217 if (m_windowStyle & wxBK_LEFT)
218 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_LEFT );
219 if (m_windowStyle & wxBK_BOTTOM)
220 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_BOTTOM );
221
222 PostCreation(size);
223
224 g_signal_connect (m_widget, "realize",
225 G_CALLBACK (gtk_notebook_realized_callback), this);
226
227 return true;
228 }
229
230 int wxNotebook::GetSelection() const
231 {
232 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
233
234 return gtk_notebook_get_current_page( GTK_NOTEBOOK(m_widget) );
235 }
236
237 wxString wxNotebook::GetPageText( size_t page ) const
238 {
239 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid notebook") );
240
241 wxGtkNotebookPage* nb_page = GetNotebookPage(page);
242 if (nb_page)
243 return nb_page->m_text;
244 else
245 return wxEmptyString;
246 }
247
248 int wxNotebook::GetPageImage( size_t page ) const
249 {
250 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
251
252 wxGtkNotebookPage* nb_page = GetNotebookPage(page);
253 if (nb_page)
254 return nb_page->m_image;
255 else
256 return -1;
257 }
258
259 wxGtkNotebookPage* wxNotebook::GetNotebookPage( int page ) const
260 {
261 wxCHECK_MSG( m_widget != NULL, (wxGtkNotebookPage*) NULL, wxT("invalid notebook") );
262
263 wxCHECK_MSG( page < (int)m_pagesData.GetCount(), (wxGtkNotebookPage*) NULL, wxT("invalid notebook index") );
264
265 return m_pagesData.Item(page)->GetData();
266 }
267
268 int wxNotebook::DoSetSelection( size_t page, int flags )
269 {
270 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
271
272 wxCHECK_MSG( page < m_pagesData.GetCount(), -1, wxT("invalid notebook index") );
273
274 int selOld = GetSelection();
275
276 if ( !(flags & SetSelection_SendEvent) )
277 {
278 g_signal_handlers_block_by_func(m_widget,
279 (gpointer)gtk_notebook_page_changing_callback, this);
280
281 g_signal_handlers_block_by_func(m_widget,
282 (gpointer)gtk_notebook_page_changed_callback, this);
283 }
284
285 gtk_notebook_set_current_page( GTK_NOTEBOOK(m_widget), page );
286
287 if ( !(flags & SetSelection_SendEvent) )
288 {
289 g_signal_handlers_unblock_by_func(m_widget,
290 (gpointer)gtk_notebook_page_changing_callback, this);
291
292 g_signal_handlers_unblock_by_func(m_widget,
293 (gpointer)gtk_notebook_page_changed_callback, this);
294 }
295
296 wxNotebookPage *client = GetPage(page);
297 if ( client )
298 client->SetFocus();
299
300 return selOld;
301 }
302
303 bool wxNotebook::SetPageText( size_t page, const wxString &text )
304 {
305 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid notebook") );
306
307 wxGtkNotebookPage* nb_page = GetNotebookPage(page);
308
309 wxCHECK_MSG( nb_page, false, wxT("SetPageText: invalid page index") );
310
311 nb_page->m_text = text;
312
313 gtk_label_set_text( nb_page->m_label, wxGTK_CONV( nb_page->m_text ) );
314
315 return true;
316 }
317
318 bool wxNotebook::SetPageImage( size_t page, int image )
319 {
320 /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
321
322 wxGtkNotebookPage* nb_page = GetNotebookPage(page);
323
324 if (!nb_page) return false;
325
326 /* Optimization posibility: return immediately if image unchanged.
327 * Not enabled because it may break existing (stupid) code that
328 * manipulates the imagelist to cycle images */
329
330 /* if (image == nb_page->m_image) return true; */
331
332 /* For different cases:
333 1) no image -> no image
334 2) image -> no image
335 3) no image -> image
336 4) image -> image */
337
338 if (image == -1 && nb_page->m_image == -1)
339 return true; /* Case 1): Nothing to do. */
340
341 GtkWidget *pixmapwid = (GtkWidget*) NULL;
342
343 if (nb_page->m_image != -1)
344 {
345 /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
346
347 GList *child = gtk_container_get_children(GTK_CONTAINER(nb_page->m_box));
348 while (child)
349 {
350 if (GTK_IS_IMAGE(child->data))
351 {
352 pixmapwid = GTK_WIDGET(child->data);
353 break;
354 }
355 child = child->next;
356 }
357
358 /* We should have the pixmap widget now */
359 wxASSERT(pixmapwid != NULL);
360
361 if (image == -1)
362 {
363 /* If there's no new widget, just remove the old from the box */
364 gtk_container_remove(GTK_CONTAINER(nb_page->m_box), pixmapwid);
365 nb_page->m_image = -1;
366
367 return true; /* Case 2) */
368 }
369 }
370
371 /* Only cases 3) and 4) left */
372 wxASSERT( m_imageList != NULL ); /* Just in case */
373
374 /* Construct the new pixmap */
375 const wxBitmap *bmp = m_imageList->GetBitmapPtr(image);
376
377 if (pixmapwid == NULL)
378 {
379 /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
380 pixmapwid = gtk_image_new_from_pixbuf(bmp->GetPixbuf());
381
382 /* CHECKME: Are these pack flags okay? */
383 gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
384 gtk_widget_show(pixmapwid);
385 }
386 else
387 {
388 /* Case 4) Simply replace the pixmap */
389 gtk_image_set_from_pixbuf((GtkImage*)pixmapwid, bmp->GetPixbuf());
390 }
391
392 nb_page->m_image = image;
393
394 return true;
395 }
396
397 void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
398 {
399 wxFAIL_MSG( wxT("wxNotebook::SetPageSize not implemented") );
400 }
401
402 void wxNotebook::SetPadding( const wxSize &padding )
403 {
404 wxCHECK_RET( m_widget != NULL, wxT("invalid notebook") );
405
406 m_padding = padding.GetWidth();
407
408 int i;
409 for (i=0; i<int(GetPageCount()); i++)
410 {
411 wxGtkNotebookPage* nb_page = GetNotebookPage(i);
412 wxASSERT(nb_page != NULL);
413
414 if (nb_page->m_image != -1)
415 {
416 // gtk_box_set_child_packing sets padding on BOTH sides
417 // icon provides left padding, label provides center and right
418 int image = nb_page->m_image;
419 SetPageImage(i,-1);
420 SetPageImage(i,image);
421 }
422 wxASSERT(nb_page->m_label);
423 gtk_box_set_child_packing(GTK_BOX(nb_page->m_box),
424 GTK_WIDGET(nb_page->m_label),
425 FALSE, FALSE, m_padding, GTK_PACK_END);
426 }
427 }
428
429 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
430 {
431 wxFAIL_MSG( wxT("wxNotebook::SetTabSize not implemented") );
432 }
433
434 bool wxNotebook::DeleteAllPages()
435 {
436 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid notebook") );
437
438 while (m_pagesData.GetCount() > 0)
439 DeletePage( m_pagesData.GetCount()-1 );
440
441 wxASSERT_MSG( GetPageCount() == 0, _T("all pages must have been deleted") );
442
443 InvalidateBestSize();
444 return wxNotebookBase::DeleteAllPages();
445 }
446
447 wxNotebookPage *wxNotebook::DoRemovePage( size_t page )
448 {
449 // We cannot remove the page yet, as GTK sends the "switch_page"
450 // signal before it has removed the notebook-page from its
451 // corresponding list. Thus, if we were to remove the page from
452 // m_pages at this point, the two lists of pages would be out
453 // of sync during the PAGE_CHANGING/PAGE_CHANGED events.
454 wxNotebookPage *client = GetPage(page);
455 if ( !client )
456 return NULL;
457
458 gtk_widget_ref( client->m_widget );
459 gtk_widget_unrealize( client->m_widget );
460
461 // we don't need to unparent the client->m_widget; GTK+ will do
462 // that for us (and will throw a warning if we do it!)
463 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page );
464
465 // It's safe to remove the page now.
466 wxASSERT_MSG(GetPage(page) == client, wxT("pages changed during delete"));
467 wxNotebookBase::DoRemovePage(page);
468
469 wxGtkNotebookPage* p = GetNotebookPage(page);
470 m_pagesData.DeleteObject(p);
471 delete p;
472
473 return client;
474 }
475
476 bool wxNotebook::InsertPage( size_t position,
477 wxNotebookPage* win,
478 const wxString& text,
479 bool select,
480 int imageId )
481 {
482 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid notebook") );
483
484 wxCHECK_MSG( win->GetParent() == this, false,
485 wxT("Can't add a page whose parent is not the notebook!") );
486
487 wxCHECK_MSG( position <= GetPageCount(), false,
488 _T("invalid page index in wxNotebookPage::InsertPage()") );
489
490 // Hack Alert! (Part II): See above in wxInsertChildInNotebook callback
491 // why this has to be done. NOTE: using gtk_widget_unparent here does not
492 // work as it seems to undo too much and will cause errors in the
493 // gtk_notebook_insert_page below, so instead just clear the parent by
494 // hand here.
495 win->m_widget->parent = NULL;
496
497 if (m_themeEnabled)
498 win->SetThemeEnabled(true);
499
500 GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
501
502 wxGtkNotebookPage *nb_page = new wxGtkNotebookPage();
503
504 if ( position == GetPageCount() )
505 m_pagesData.Append( nb_page );
506 else
507 m_pagesData.Insert( position, nb_page );
508
509 m_pages.Insert(win, position);
510
511 // set the label image and text
512 // this must be done before adding the page, as GetPageText
513 // and GetPageImage will otherwise return wrong values in
514 // the page-changed event that results from inserting the
515 // first page.
516 nb_page->m_image = imageId;
517 nb_page->m_text = wxStripMenuCodes(text);
518
519 nb_page->m_box = gtk_hbox_new( FALSE, 1 );
520 gtk_container_set_border_width((GtkContainer*)nb_page->m_box, 2);
521
522 gint idx = gtk_notebook_insert_page(notebook, win->m_widget,
523 nb_page->m_box, position);
524
525 nb_page->m_page = (GtkNotebookPage *)gtk_notebook_get_nth_page(notebook, idx);
526
527 if (imageId != -1)
528 {
529 wxASSERT( m_imageList != NULL );
530
531 const wxBitmap *bmp = m_imageList->GetBitmapPtr(imageId);
532 GtkWidget* pixmapwid = gtk_image_new_from_pixbuf(bmp->GetPixbuf());
533 gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
534 gtk_widget_show(pixmapwid);
535 }
536
537 /* set the label text */
538 nb_page->m_label = GTK_LABEL( gtk_label_new(wxGTK_CONV(nb_page->m_text)) );
539 gtk_box_pack_end( GTK_BOX(nb_page->m_box), GTK_WIDGET(nb_page->m_label), FALSE, FALSE, m_padding );
540
541 /* apply current style */
542 GtkRcStyle *style = CreateWidgetStyle();
543 if ( style )
544 {
545 gtk_widget_modify_style(GTK_WIDGET(nb_page->m_label), style);
546 gtk_rc_style_unref(style);
547 }
548
549 /* show the label */
550 gtk_widget_show( GTK_WIDGET(nb_page->m_label) );
551
552 if (select && (m_pagesData.GetCount() > 1))
553 {
554 SetSelection( position );
555 }
556
557 InvalidateBestSize();
558 return true;
559 }
560
561 // helper for HitTest(): check if the point lies inside the given widget which
562 // is the child of the notebook whose position and border size are passed as
563 // parameters
564 static bool
565 IsPointInsideWidget(const wxPoint& pt, GtkWidget *w,
566 gint x, gint y, gint border = 0)
567 {
568 return
569 (pt.x >= w->allocation.x - x - border) &&
570 (pt.x <= w->allocation.x - x + border + w->allocation.width) &&
571 (pt.y >= w->allocation.y - y - border) &&
572 (pt.y <= w->allocation.y - y + border + w->allocation.height);
573 }
574
575 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
576 {
577 const gint x = m_widget->allocation.x;
578 const gint y = m_widget->allocation.y;
579
580 const size_t count = GetPageCount();
581 size_t i = 0;
582
583 GtkNotebook * notebook = GTK_NOTEBOOK(m_widget);
584 if (gtk_notebook_get_scrollable(notebook))
585 i = g_list_position( notebook->children, notebook->first_tab );
586
587 for ( ; i < count; i++ )
588 {
589 wxGtkNotebookPage* nb_page = GetNotebookPage(i);
590 GtkWidget *box = nb_page->m_box;
591
592 const gint border = gtk_container_get_border_width(GTK_CONTAINER(box));
593
594 if ( IsPointInsideWidget(pt, box, x, y, border) )
595 {
596 // ok, we're inside this tab -- now find out where, if needed
597 if ( flags )
598 {
599 GtkWidget *pixmap = NULL;
600
601 GList *children = gtk_container_get_children(GTK_CONTAINER(box));
602 for ( GList *child = children; child; child = child->next )
603 {
604 if (GTK_IS_IMAGE(child->data))
605 {
606 pixmap = GTK_WIDGET(child->data);
607 break;
608 }
609 }
610
611 if ( children )
612 g_list_free(children);
613
614 if ( pixmap && IsPointInsideWidget(pt, pixmap, x, y) )
615 {
616 *flags = wxBK_HITTEST_ONICON;
617 }
618 else if ( IsPointInsideWidget(pt, GTK_WIDGET(nb_page->m_label), x, y) )
619 {
620 *flags = wxBK_HITTEST_ONLABEL;
621 }
622 else
623 {
624 *flags = wxBK_HITTEST_ONITEM;
625 }
626 }
627
628 return i;
629 }
630 }
631
632 if ( flags )
633 {
634 *flags = wxBK_HITTEST_NOWHERE;
635 wxWindowBase * page = GetCurrentPage();
636 if ( page )
637 {
638 // rect origin is in notebook's parent coordinates
639 wxRect rect = page->GetRect();
640
641 // adjust it to the notebook's coordinates
642 wxPoint pos = GetPosition();
643 rect.x -= pos.x;
644 rect.y -= pos.y;
645 if ( rect.Contains( pt ) )
646 *flags |= wxBK_HITTEST_ONPAGE;
647 }
648 }
649
650 return wxNOT_FOUND;
651 }
652
653 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
654 {
655 if (event.IsWindowChange())
656 AdvanceSelection( event.GetDirection() );
657 else
658 event.Skip();
659 }
660
661 #if wxUSE_CONSTRAINTS
662
663 // override these 2 functions to do nothing: everything is done in OnSize
664 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
665 {
666 // don't set the sizes of the pages - their correct size is not yet known
667 wxControl::SetConstraintSizes(false);
668 }
669
670 bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
671 {
672 return true;
673 }
674
675 #endif
676
677 void wxNotebook::DoApplyWidgetStyle(GtkRcStyle *style)
678 {
679 gtk_widget_modify_style(m_widget, style);
680 size_t cnt = m_pagesData.GetCount();
681 for (size_t i = 0; i < cnt; i++)
682 gtk_widget_modify_style(GTK_WIDGET(GetNotebookPage(i)->m_label), style);
683 }
684
685 GdkWindow *wxNotebook::GTKGetWindow(wxArrayGdkWindows& windows) const
686 {
687 windows.push_back(m_widget->window);
688 windows.push_back(GTK_NOTEBOOK(m_widget)->event_window);
689
690 return NULL;
691 }
692
693 // static
694 wxVisualAttributes
695 wxNotebook::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
696 {
697 return GetDefaultAttributesFromGTKWidget(gtk_notebook_new);
698 }
699
700 //-----------------------------------------------------------------------------
701 // wxNotebookEvent
702 //-----------------------------------------------------------------------------
703
704 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
705
706 #endif