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"
23 #include "wx/gtk/win_gtk.h"
24 #include "gdk/gdkkeysyms.h"
26 //-----------------------------------------------------------------------------
28 //-----------------------------------------------------------------------------
30 extern bool g_blockEventsOnDrag
;
32 //-----------------------------------------------------------------------------
34 //-----------------------------------------------------------------------------
36 class wxNotebookPage
: public wxObject
44 m_page
= (GtkNotebookPage
*) NULL
;
45 m_client
= (wxWindow
*) NULL
;
46 m_parent
= (GtkNotebook
*) NULL
;
47 m_box
= (GtkWidget
*) NULL
;
52 mark page as "added' to the notebook, return FALSE if the page was
65 bool WasAdded() const { return m_added
; }
70 GtkNotebookPage
*m_page
;
73 GtkNotebook
*m_parent
;
74 GtkWidget
*m_box
; // in which the label and image are packed
80 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 static void gtk_notebook_page_change_callback(GtkNotebook
*WXUNUSED(widget
),
85 GtkNotebookPage
*WXUNUSED(page
),
89 wxNotebook
*notebook
= (wxNotebook
*)data
;
91 int old
= notebook
->GetSelection();
93 // TODO: emulate PAGE_CHANGING event
95 wxNotebookEvent
event( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
,
96 notebook
->GetId(), nPage
, old
);
97 event
.SetEventObject( notebook
);
98 notebook
->GetEventHandler()->ProcessEvent( event
);
101 //-----------------------------------------------------------------------------
103 //-----------------------------------------------------------------------------
105 static void gtk_page_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxWindow
*win
)
107 if ((win
->m_x
== alloc
->x
) &&
108 (win
->m_y
== alloc
->y
) &&
109 (win
->m_width
== alloc
->width
) &&
110 (win
->m_height
== alloc
->height
))
115 win
->SetSize( alloc
->x
, alloc
->y
, alloc
->width
, alloc
->height
);
117 if (win
->GetAutoLayout()) win
->Layout();
120 //-----------------------------------------------------------------------------
122 //-----------------------------------------------------------------------------
125 gtk_notebook_key_press_callback( GtkWidget
*widget
, GdkEventKey
*gdk_event
, wxNotebook
*notebook
)
127 if (g_blockEventsOnDrag
) return FALSE
;
129 if (!notebook
->HasVMT()) return FALSE
;
131 if (gdk_event
->keyval
!= GDK_Down
) return FALSE
;
133 if (notebook
!= notebook
->FindFocus()) return FALSE
;
135 if (notebook
->m_pages
.GetCount() == 0) return FALSE
;
137 wxNode
*node
= notebook
->m_pages
.Nth( notebook
->GetSelection() );
139 if (!node
) return FALSE
;
141 wxNotebookPage
*page
= (wxNotebookPage
*) node
->Data();
143 // don't let others the key event
144 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget
), "key_press_event" );
146 page
->m_client
->SetFocus();
151 //-----------------------------------------------------------------------------
152 // InsertChild callback for wxNotebook
153 //-----------------------------------------------------------------------------
155 static void wxInsertChildInNotebook( wxNotebook
* parent
, wxWindow
* child
)
157 wxNotebookPage
*page
= new wxNotebookPage();
159 page
->m_id
= parent
->GetPageCount();
161 page
->m_box
= gtk_hbox_new (FALSE
, 0);
162 gtk_container_border_width(GTK_CONTAINER(page
->m_box
), 2);
164 GtkNotebook
*notebook
= GTK_NOTEBOOK(parent
->m_widget
);
166 page
->m_client
= child
;
167 gtk_notebook_append_page( notebook
, child
->m_widget
, page
->m_box
);
169 page
->m_page
= (GtkNotebookPage
*) (g_list_last(notebook
->children
)->data
);
171 page
->m_parent
= notebook
;
173 gtk_signal_connect( GTK_OBJECT(child
->m_widget
), "size_allocate",
174 GTK_SIGNAL_FUNC(gtk_page_size_callback
), (gpointer
)child
);
176 wxASSERT_MSG( page
->m_page
, "Notebook page creation error" );
178 parent
->m_pages
.Append( page
);
181 //-----------------------------------------------------------------------------
183 //-----------------------------------------------------------------------------
185 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
,wxControl
)
187 void wxNotebook::Init()
189 m_imageList
= (wxImageList
*) NULL
;
190 m_pages
.DeleteContents( TRUE
);
194 wxNotebook::wxNotebook()
199 wxNotebook::wxNotebook( wxWindow
*parent
, wxWindowID id
,
200 const wxPoint
& pos
, const wxSize
& size
,
201 long style
, const wxString
& name
)
204 Create( parent
, id
, pos
, size
, style
, name
);
207 wxNotebook::~wxNotebook()
209 // don't generate change page events any more
210 if (m_idHandler
!= 0)
211 gtk_signal_disconnect(GTK_OBJECT(m_widget
), m_idHandler
);
216 bool wxNotebook::Create(wxWindow
*parent
, wxWindowID id
,
217 const wxPoint
& pos
, const wxSize
& size
,
218 long style
, const wxString
& name
)
221 m_acceptsFocus
= TRUE
;
222 m_insertCallback
= (wxInsertChildFunction
)wxInsertChildInNotebook
;
224 PreCreation( parent
, id
, pos
, size
, style
, name
);
226 m_widget
= gtk_notebook_new();
229 debug_focus_in( m_widget
, "wxNotebook::m_widget", name
);
232 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget
), 1 );
234 m_idHandler
= gtk_signal_connect (
235 GTK_OBJECT(m_widget
), "switch_page",
236 GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback
),
239 m_parent
->AddChild( this );
241 (m_parent
->m_insertCallback
)( m_parent
, this );
243 gtk_signal_connect( GTK_OBJECT(m_widget
), "key_press_event",
244 GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback
), (gpointer
)this );
253 int wxNotebook::GetSelection() const
255 wxCHECK_MSG( m_widget
!= NULL
, -1, "invalid notebook" );
257 if (m_pages
.Number() == 0) return -1;
259 GtkNotebookPage
*g_page
= GTK_NOTEBOOK(m_widget
)->cur_page
;
260 if (!g_page
) return -1;
262 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
264 wxNode
*node
= m_pages
.First();
267 page
= (wxNotebookPage
*)node
->Data();
269 if ((page
->m_page
== g_page
) || (page
->m_page
== (GtkNotebookPage
*)NULL
))
271 // page->m_page is NULL directly after gtk_notebook_append. gtk emits
272 // "switch_page" then and we ask for GetSelection() in the handler for
273 // "switch_page". otherwise m_page should never be NULL. all this
274 // might also be wrong.
280 wxCHECK_MSG( node
!= NULL
, -1, "wxNotebook: no selection?" );
285 int wxNotebook::GetPageCount() const
287 // count only the pages which were already added to the notebook for MSW
288 // compatibility (and, in fact, this behaviour makes more sense anyhow
289 // because only the added pages are shown)
292 for ( wxNode
*node
= m_pages
.First(); node
; node
= node
->Next() )
294 wxNotebookPage
*page
= (wxNotebookPage
*)node
->Data();
296 if (page
->WasAdded()) n
++;
302 int wxNotebook::GetRowCount() const
307 wxString
wxNotebook::GetPageText( int page
) const
309 wxCHECK_MSG( m_widget
!= NULL
, "", "invalid notebook" );
311 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
313 return nb_page
->m_text
;
318 int wxNotebook::GetPageImage( int page
) const
320 wxCHECK_MSG( m_widget
!= NULL
, 0, "invalid notebook" );
322 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
324 return nb_page
->m_image
;
329 wxNotebookPage
* wxNotebook::GetNotebookPage(int page
) const
331 wxCHECK_MSG( m_widget
!= NULL
, (wxNotebookPage
*)NULL
, "invalid notebook" );
333 wxNotebookPage
*nb_page
= (wxNotebookPage
*) NULL
;
335 wxNode
*node
= m_pages
.First();
338 nb_page
= (wxNotebookPage
*)node
->Data();
339 if (nb_page
->m_id
== page
)
344 wxFAIL_MSG( "Notebook page not found!" );
346 return (wxNotebookPage
*) NULL
;
349 int wxNotebook::SetSelection( int page
)
351 wxCHECK_MSG( m_widget
!= NULL
, -1, "invalid notebook" );
353 int selOld
= GetSelection();
354 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
356 if (!nb_page
) return -1;
359 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
362 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
367 if (!child
) return -1;
369 gtk_notebook_set_page( GTK_NOTEBOOK(m_widget
), page_num
);
374 void wxNotebook::AdvanceSelection( bool bForward
)
376 wxCHECK_RET( m_widget
!= NULL
, "invalid notebook" );
378 int sel
= GetSelection();
379 int max
= GetPageCount();
382 SetSelection( sel
== max
? 0 : sel
+ 1 );
384 SetSelection( sel
== 0 ? max
: sel
- 1 );
387 void wxNotebook::SetImageList( wxImageList
* imageList
)
389 m_imageList
= imageList
;
392 bool wxNotebook::SetPageText( int page
, const wxString
&text
)
394 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
396 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
398 if (!nb_page
) return FALSE
;
400 nb_page
->m_text
= text
;
402 if (nb_page
->m_text
.IsEmpty()) nb_page
->m_text
= "";
404 gtk_label_set(nb_page
->m_label
, nb_page
->m_text
);
409 bool wxNotebook::SetPageImage( int page
, int image
)
411 /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
413 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
415 if (!nb_page
) return FALSE
;
417 /* Optimization posibility: return immediately if image unchanged.
418 * Not enabled because it may break existing (stupid) code that
419 * manipulates the imagelist to cycle images */
421 /* if (image == nb_page->m_image) return TRUE; */
423 /* For different cases:
424 1) no image -> no image
429 if (image
== -1 && nb_page
->m_image
== -1)
430 return TRUE
; /* Case 1): Nothing to do. */
432 GtkWidget
*pixmapwid
= (GtkWidget
*) NULL
;
434 if (nb_page
->m_image
!= -1)
436 /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
438 GList
*child
= gtk_container_children(GTK_CONTAINER(nb_page
->m_box
));
441 if (GTK_IS_PIXMAP(child
->data
))
443 pixmapwid
= GTK_WIDGET(child
->data
);
449 /* We should have the pixmap widget now */
450 wxASSERT(pixmapwid
!= NULL
);
454 /* If there's no new widget, just remove the old from the box */
455 gtk_container_remove(GTK_CONTAINER(nb_page
->m_box
), pixmapwid
);
456 nb_page
->m_image
= -1;
458 return TRUE
; /* Case 2) */
462 /* Only cases 3) and 4) left */
463 wxASSERT( m_imageList
!= NULL
); /* Just in case */
465 /* Construct the new pixmap */
466 const wxBitmap
*bmp
= m_imageList
->GetBitmap(image
);
467 GdkPixmap
*pixmap
= bmp
->GetPixmap();
468 GdkBitmap
*mask
= (GdkBitmap
*) NULL
;
469 if ( bmp
->GetMask() )
471 mask
= bmp
->GetMask()->GetBitmap();
474 if (pixmapwid
== NULL
)
476 /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
477 pixmapwid
= gtk_pixmap_new (pixmap
, mask
);
479 /* CHECKME: Are these pack flags okay? */
480 gtk_box_pack_start(GTK_BOX(nb_page
->m_box
), pixmapwid
, FALSE
, FALSE
, 3);
481 gtk_widget_show(pixmapwid
);
485 /* Case 4) Simply replace the pixmap */
486 gtk_pixmap_set(GTK_PIXMAP(pixmapwid
), pixmap
, mask
);
489 nb_page
->m_image
= image
;
494 void wxNotebook::SetPageSize( const wxSize
&WXUNUSED(size
) )
496 wxFAIL_MSG( "wxNotebook::SetPageSize not implemented" );
499 void wxNotebook::SetPadding( const wxSize
&WXUNUSED(padding
) )
501 wxFAIL_MSG( "wxNotebook::SetPadding not implemented" );
504 bool wxNotebook::DeleteAllPages()
506 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
508 wxNode
*page_node
= m_pages
.First();
511 wxNotebookPage
*page
= (wxNotebookPage
*)page_node
->Data();
513 DeletePage( page
->m_id
);
515 page_node
= m_pages
.First();
521 bool wxNotebook::DeletePage( int page
)
523 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
524 if (!nb_page
) return FALSE
;
527 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
530 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
535 wxCHECK_MSG( child
!= NULL
, FALSE
, "illegal notebook index" );
537 delete nb_page
->m_client
;
539 m_pages
.DeleteObject( nb_page
);
544 bool wxNotebook::RemovePage( int page
)
546 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
547 if (!nb_page
) return FALSE
;
550 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
553 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
558 wxCHECK_MSG( child
!= NULL
, FALSE
, "illegal notebook index" );
560 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget
), page_num
);
562 m_pages
.DeleteObject( nb_page
);
567 bool wxNotebook::AddPage(wxWindow
* win
, const wxString
& text
,
568 bool select
, int imageId
)
570 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, "invalid notebook" );
572 /* we've created the notebook page in AddChild(). Now we just have to set
573 the caption for the page and set the others parameters. */
575 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
577 wxNode
*node
= m_pages
.First();
580 page
= (wxNotebookPage
*)node
->Data();
581 if ( page
->m_client
== win
) break;
585 wxCHECK_MSG( page
!= NULL
, FALSE
,
586 "Can't add a page whose parent is not the notebook!" );
588 wxCHECK_MSG( page
->Add(), FALSE
,
589 "Can't add the same page twice to a notebook." );
593 wxASSERT( m_imageList
!= NULL
);
595 const wxBitmap
*bmp
= m_imageList
->GetBitmap(imageId
);
596 GdkPixmap
*pixmap
= bmp
->GetPixmap();
597 GdkBitmap
*mask
= (GdkBitmap
*) NULL
;
598 if ( bmp
->GetMask() )
600 mask
= bmp
->GetMask()->GetBitmap();
603 GtkWidget
*pixmapwid
= gtk_pixmap_new (pixmap
, mask
);
605 gtk_box_pack_start(GTK_BOX(page
->m_box
), pixmapwid
, FALSE
, FALSE
, 3);
607 gtk_widget_show(pixmapwid
);
610 /* then set the attributes */
612 if (page
->m_text
.IsEmpty()) page
->m_text
= "";
613 page
->m_image
= imageId
;
614 page
->m_label
= (GtkLabel
*)gtk_label_new(page
->m_text
);
615 gtk_box_pack_end( GTK_BOX(page
->m_box
), (GtkWidget
*)page
->m_label
, FALSE
, FALSE
, 3);
617 /* @@@: what does this do? do we still need it?
618 gtk_misc_set_alignment(GTK_MISC(page->m_label), 0.0, 0.5); */
620 gtk_widget_show((GtkWidget
*)page
->m_label
);
622 if (select
) SetSelection( GetPageCount()-1 );
627 wxWindow
*wxNotebook::GetPage( int page
) const
629 wxCHECK_MSG( m_widget
!= NULL
, (wxWindow
*) NULL
, "invalid notebook" );
631 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
633 return (wxWindow
*) NULL
;
635 return nb_page
->m_client
;
638 // override these 2 functions to do nothing: everything is done in OnSize
639 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse
) )
641 // don't set the sizes of the pages - their correct size is not yet known
642 wxControl::SetConstraintSizes(FALSE
);
645 bool wxNotebook::DoPhase( int WXUNUSED(nPhase
) )
650 void wxNotebook::ApplyWidgetStyle()
653 gtk_widget_set_style( m_widget
, m_widgetStyle
);
656 //-----------------------------------------------------------------------------
658 //-----------------------------------------------------------------------------
660 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)