1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "notebook.h"
14 #include "wx/notebook.h"
17 #include "wx/imaglist.h"
20 #include "gdk/gdkkeysyms.h"
22 //-----------------------------------------------------------------------------
24 //-----------------------------------------------------------------------------
26 extern bool g_blockEventsOnDrag
;
28 //-----------------------------------------------------------------------------
30 //-----------------------------------------------------------------------------
32 class wxNotebookPage
: public wxObject
40 m_page
= (GtkNotebookPage
*) NULL
;
41 m_client
= (wxWindow
*) NULL
;
42 m_parent
= (GtkNotebook
*) NULL
;
43 m_box
= (GtkWidget
*) NULL
;
47 // mark page as "added' to the notebook, return FALSE if the page was
58 bool WasAdded() const { return m_added
; }
63 GtkNotebookPage
*m_page
;
66 GtkNotebook
*m_parent
;
67 GtkWidget
*m_box
; // in which the label and image are packed
73 //-----------------------------------------------------------------------------
75 //-----------------------------------------------------------------------------
77 static void gtk_notebook_page_change_callback(GtkNotebook
*WXUNUSED(widget
),
78 GtkNotebookPage
*WXUNUSED(page
),
82 wxNotebook
*notebook
= (wxNotebook
*)data
;
84 int old
= notebook
->GetSelection();
86 // TODO: emulate PAGE_CHANGING event
88 wxNotebookEvent
event( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
,
89 notebook
->GetId(), nPage
, old
);
90 event
.SetEventObject( notebook
);
91 notebook
->GetEventHandler()->ProcessEvent( event
);
94 //-----------------------------------------------------------------------------
96 //-----------------------------------------------------------------------------
98 static void gtk_page_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxWindow
*win
)
100 if ((win
->m_x
== alloc
->x
) &&
101 (win
->m_y
== alloc
->y
) &&
102 (win
->m_width
== alloc
->width
) &&
103 (win
->m_height
== alloc
->height
))
108 win
->SetSize( alloc
->x
, alloc
->y
, alloc
->width
, alloc
->height
);
110 if (win
->GetAutoLayout()) win
->Layout();
113 //-----------------------------------------------------------------------------
115 //-----------------------------------------------------------------------------
118 gtk_notebook_key_press_callback( GtkWidget
*widget
, GdkEventKey
*gdk_event
, wxNotebook
*notebook
)
120 if (g_blockEventsOnDrag
) return FALSE
;
122 if (!notebook
->HasVMT()) return FALSE
;
124 if (gdk_event
->keyval
!= GDK_Down
) return FALSE
;
126 if (notebook
!= notebook
->FindFocus()) return FALSE
;
128 if (notebook
->m_pages
.GetCount() == 0) return FALSE
;
130 wxNode
*node
= notebook
->m_pages
.Nth( notebook
->GetSelection() );
132 if (!node
) return FALSE
;
134 wxNotebookPage
*page
= (wxNotebookPage
*) node
->Data();
136 // don't let others the key event
137 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget
), "key_press_event" );
139 page
->m_client
->SetFocus();
144 //-----------------------------------------------------------------------------
145 // InsertChild callback for wxNotebook
146 //-----------------------------------------------------------------------------
148 static void wxInsertChildInNotebook( wxNotebook
* parent
, wxWindow
* child
)
150 wxNotebookPage
*page
= new wxNotebookPage();
152 page
->m_id
= parent
->GetPageCount();
154 page
->m_box
= gtk_hbox_new (FALSE
, 0);
155 gtk_container_border_width(GTK_CONTAINER(page
->m_box
), 2);
157 GtkNotebook
*notebook
= GTK_NOTEBOOK(parent
->m_widget
);
159 page
->m_client
= child
;
160 gtk_notebook_append_page( notebook
, child
->m_widget
, page
->m_box
);
162 page
->m_page
= (GtkNotebookPage
*) (g_list_last(notebook
->children
)->data
);
164 page
->m_parent
= notebook
;
166 gtk_signal_connect( GTK_OBJECT(child
->m_widget
), "size_allocate",
167 GTK_SIGNAL_FUNC(gtk_page_size_callback
), (gpointer
)child
);
169 wxASSERT_MSG( page
->m_page
, "Notebook page creation error" );
171 parent
->m_pages
.Append( page
);
174 //-----------------------------------------------------------------------------
176 //-----------------------------------------------------------------------------
178 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
,wxControl
)
180 void wxNotebook::Init()
182 m_imageList
= (wxImageList
*) NULL
;
183 m_pages
.DeleteContents( TRUE
);
187 wxNotebook::wxNotebook()
192 wxNotebook::wxNotebook( wxWindow
*parent
, wxWindowID id
,
193 const wxPoint
& pos
, const wxSize
& size
,
194 long style
, const wxString
& name
)
197 Create( parent
, id
, pos
, size
, style
, name
);
200 wxNotebook::~wxNotebook()
202 // don't generate change page events any more
203 if (m_idHandler
!= 0)
204 gtk_signal_disconnect(GTK_OBJECT(m_widget
), m_idHandler
);
209 bool wxNotebook::Create(wxWindow
*parent
, wxWindowID id
,
210 const wxPoint
& pos
, const wxSize
& size
,
211 long style
, const wxString
& name
)
214 m_acceptsFocus
= TRUE
;
215 m_insertCallback
= (wxInsertChildFunction
)wxInsertChildInNotebook
;
217 PreCreation( parent
, id
, pos
, size
, style
, name
);
219 m_widget
= gtk_notebook_new();
222 debug_focus_in( m_widget
, "wxNotebook::m_widget", name
);
225 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget
), 1 );
227 m_idHandler
= gtk_signal_connect (
228 GTK_OBJECT(m_widget
), "switch_page",
229 GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback
),
232 m_parent
->AddChild( this );
234 (m_parent
->m_insertCallback
)( m_parent
, this );
236 gtk_signal_connect( GTK_OBJECT(m_widget
), "key_press_event",
237 GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback
), (gpointer
)this );
246 int wxNotebook::GetSelection() const
248 wxCHECK_MSG( m_widget
!= NULL
, -1, "invalid notebook" );
250 if (m_pages
.Number() == 0) return -1;
252 GtkNotebookPage
*g_page
= GTK_NOTEBOOK(m_widget
)->cur_page
;
253 if (!g_page
) return -1;
255 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
257 wxNode
*node
= m_pages
.First();
260 page
= (wxNotebookPage
*)node
->Data();
262 if ((page
->m_page
== g_page
) || (page
->m_page
== (GtkNotebookPage
*)NULL
))
264 // page->m_page is NULL directly after gtk_notebook_append. gtk emits
265 // "switch_page" then and we ask for GetSelection() in the handler for
266 // "switch_page". otherwise m_page should never be NULL. all this
267 // might also be wrong.
273 wxCHECK_MSG( node
!= NULL
, -1, "wxNotebook: no selection?" );
278 int wxNotebook::GetPageCount() const
280 // count only the pages which were already added to the notebook for MSW
281 // compatibility (and, in fact, this behaviour makes more sense anyhow
282 // because only the added pages are shown)
285 for ( wxNode
*node
= m_pages
.First(); node
; node
= node
->Next() )
287 wxNotebookPage
*page
= (wxNotebookPage
*)node
->Data();
289 if (page
->WasAdded()) n
++;
295 int wxNotebook::GetRowCount() const
300 wxString
wxNotebook::GetPageText( int page
) const
302 wxCHECK_MSG( m_widget
!= NULL
, "", "invalid notebook" );
304 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
306 return nb_page
->m_text
;
311 int wxNotebook::GetPageImage( int page
) const
313 wxCHECK_MSG( m_widget
!= NULL
, 0, "invalid notebook" );
315 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
317 return nb_page
->m_image
;
322 wxNotebookPage
* wxNotebook::GetNotebookPage(int page
) const
324 wxCHECK_MSG( m_widget
!= NULL
, (wxNotebookPage
*)NULL
, "invalid notebook" );
326 wxNotebookPage
*nb_page
= (wxNotebookPage
*) NULL
;
328 wxNode
*node
= m_pages
.First();
331 nb_page
= (wxNotebookPage
*)node
->Data();
332 if (nb_page
->m_id
== page
)
337 wxFAIL_MSG( "Notebook page not found!" );
339 return (wxNotebookPage
*) NULL
;
342 int wxNotebook::SetSelection( int page
)
344 wxCHECK_MSG( m_widget
!= NULL
, -1, "invalid notebook" );
346 int selOld
= GetSelection();
347 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
349 if (!nb_page
) return -1;
352 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
355 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
360 if (!child
) return -1;
362 gtk_notebook_set_page( GTK_NOTEBOOK(m_widget
), page_num
);
367 void wxNotebook::AdvanceSelection( bool bForward
)
369 wxCHECK_RET( m_widget
!= NULL
, "invalid notebook" );
371 int sel
= GetSelection();
372 int max
= GetPageCount();
375 SetSelection( sel
== max
? 0 : sel
+ 1 );
377 SetSelection( sel
== 0 ? max
: sel
- 1 );
380 void wxNotebook::SetImageList( wxImageList
* imageList
)
382 m_imageList
= imageList
;
385 bool wxNotebook::SetPageText( int page
, const wxString
&text
)
387 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
389 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
391 if (!nb_page
) return FALSE
;
393 nb_page
->m_text
= text
;
398 bool wxNotebook::SetPageImage( int page
, int image
)
400 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
402 if (!nb_page
) return FALSE
;
404 nb_page
->m_image
= image
;
409 void wxNotebook::SetPageSize( const wxSize
&WXUNUSED(size
) )
411 wxFAIL_MSG( "wxNotebook::SetPageSize not implemented" );
414 void wxNotebook::SetPadding( const wxSize
&WXUNUSED(padding
) )
416 wxFAIL_MSG( "wxNotebook::SetPadding not implemented" );
419 bool wxNotebook::DeleteAllPages()
421 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
423 wxNode
*page_node
= m_pages
.First();
426 wxNotebookPage
*page
= (wxNotebookPage
*)page_node
->Data();
428 DeletePage( page
->m_id
);
430 page_node
= m_pages
.First();
436 bool wxNotebook::DeletePage( int page
)
438 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
439 if (!nb_page
) return FALSE
;
442 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
445 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
450 wxCHECK_MSG( child
!= NULL
, FALSE
, "illegal notebook index" );
452 delete nb_page
->m_client
;
454 m_pages
.DeleteObject( nb_page
);
459 bool wxNotebook::RemovePage( int page
)
461 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
462 if (!nb_page
) return FALSE
;
465 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
468 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
473 wxCHECK_MSG( child
!= NULL
, FALSE
, "illegal notebook index" );
475 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget
), page_num
);
477 m_pages
.DeleteObject( nb_page
);
482 bool wxNotebook::AddPage(wxWindow
* win
, const wxString
& text
,
483 bool select
, int imageId
)
485 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
487 // we've created the notebook page in AddChild(). Now we just have to set
488 // the caption for the page and set the others parameters.
490 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
492 wxNode
*node
= m_pages
.First();
495 page
= (wxNotebookPage
*)node
->Data();
496 if ( page
->m_client
== win
) break;
500 wxCHECK_MSG( page
!= NULL
, FALSE
,
501 "Can't add a page whose parent is not the notebook!" );
503 wxCHECK_MSG( page
->Add(), FALSE
,
504 "Can't add the same page twice to a notebook." );
508 wxASSERT( m_imageList
!= NULL
);
510 const wxBitmap
*bmp
= m_imageList
->GetBitmap(imageId
);
511 GdkPixmap
*pixmap
= bmp
->GetPixmap();
512 GdkBitmap
*mask
= (GdkBitmap
*) NULL
;
513 if ( bmp
->GetMask() )
515 mask
= bmp
->GetMask()->GetBitmap();
518 GtkWidget
*pixmapwid
= gtk_pixmap_new (pixmap
, mask
);
520 gtk_box_pack_start(GTK_BOX(page
->m_box
), pixmapwid
, FALSE
, FALSE
, 3);
522 gtk_widget_show(pixmapwid
);
525 // then set the attributes
527 if (page
->m_text
.IsEmpty()) page
->m_text
= "";
528 page
->m_image
= imageId
;
529 page
->m_label
= (GtkLabel
*)gtk_label_new(page
->m_text
);
530 gtk_box_pack_start( GTK_BOX(page
->m_box
), (GtkWidget
*)page
->m_label
, FALSE
, FALSE
, 3);
532 // @@@: what does this do? do we still need it?
533 // gtk_misc_set_alignment(GTK_MISC(page->m_label), 0.0, 0.5);
535 gtk_widget_show((GtkWidget
*)page
->m_label
);
537 if (select
) SetSelection( GetPageCount()-1 );
542 wxWindow
*wxNotebook::GetPage( int page
) const
544 wxCHECK_MSG( m_widget
!= NULL
, (wxWindow
*) NULL
, "invalid notebook" );
546 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
548 return (wxWindow
*) NULL
;
550 return nb_page
->m_client
;
553 // override these 2 functions to do nothing: everything is done in OnSize
554 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse
) )
556 // don't set the sizes of the pages - their correct size is not yet known
557 wxControl::SetConstraintSizes(FALSE
);
560 bool wxNotebook::DoPhase( int WXUNUSED(nPhase
) )
565 void wxNotebook::ApplyWidgetStyle()
568 gtk_widget_set_style( m_widget
, m_widgetStyle
);
571 //-----------------------------------------------------------------------------
573 //-----------------------------------------------------------------------------
575 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)