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