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 void wxapp_install_idle_handler();
33 //-----------------------------------------------------------------------------
35 //-----------------------------------------------------------------------------
37 extern bool g_blockEventsOnDrag
;
39 //-----------------------------------------------------------------------------
41 //-----------------------------------------------------------------------------
45 extern void debug_focus_in( GtkWidget
* widget
, const wxChar
* name
, const wxChar
*window
);
49 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
53 class wxNotebookPage
: public wxObject
61 m_page
= (GtkNotebookPage
*) NULL
;
62 m_client
= (wxWindow
*) NULL
;
63 m_parent
= (GtkNotebook
*) NULL
;
64 m_box
= (GtkWidget
*) NULL
;
69 mark page as "added' to the notebook, return FALSE if the page was
82 bool WasAdded() const { return m_added
; }
87 GtkNotebookPage
*m_page
;
90 GtkNotebook
*m_parent
;
91 GtkWidget
*m_box
; // in which the label and image are packed
97 //-----------------------------------------------------------------------------
99 //-----------------------------------------------------------------------------
101 static void gtk_notebook_page_change_callback(GtkNotebook
*WXUNUSED(widget
),
102 GtkNotebookPage
*WXUNUSED(page
),
106 if (g_isIdle
) wxapp_install_idle_handler();
108 wxNotebook
*notebook
= (wxNotebook
*)data
;
110 int old
= notebook
->GetSelection();
112 // TODO: emulate PAGE_CHANGING event
114 wxNotebookEvent
event( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
,
115 notebook
->GetId(), nPage
, old
);
116 event
.SetEventObject( notebook
);
117 notebook
->GetEventHandler()->ProcessEvent( event
);
120 //-----------------------------------------------------------------------------
122 //-----------------------------------------------------------------------------
124 static void gtk_page_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxWindow
*win
)
126 if (g_isIdle
) wxapp_install_idle_handler();
128 if ((win
->m_x
== alloc
->x
) &&
129 (win
->m_y
== alloc
->y
) &&
130 (win
->m_width
== alloc
->width
) &&
131 (win
->m_height
== alloc
->height
))
136 win
->SetSize( alloc
->x
, alloc
->y
, alloc
->width
, alloc
->height
);
138 if (win
->GetAutoLayout()) win
->Layout();
141 //-----------------------------------------------------------------------------
143 //-----------------------------------------------------------------------------
146 gtk_notebook_key_press_callback( GtkWidget
*widget
, GdkEventKey
*gdk_event
, wxNotebook
*notebook
)
148 if (g_isIdle
) wxapp_install_idle_handler();
150 if (g_blockEventsOnDrag
) return FALSE
;
152 if (!notebook
->m_hasVMT
) return FALSE
;
154 /* this code makes jumping down from the handles of the notebooks
155 to the actual items in the visible notebook page possible with
156 the down-arrow key */
158 if (gdk_event
->keyval
!= GDK_Down
) return FALSE
;
160 if (notebook
!= notebook
->FindFocus()) return FALSE
;
162 if (notebook
->m_pages
.GetCount() == 0) return FALSE
;
164 wxNode
*node
= notebook
->m_pages
.Nth( notebook
->GetSelection() );
166 if (!node
) return FALSE
;
168 wxNotebookPage
*page
= (wxNotebookPage
*) node
->Data();
170 // don't let others the key event
171 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget
), "key_press_event" );
173 page
->m_client
->SetFocus();
178 //-----------------------------------------------------------------------------
179 // InsertChild callback for wxNotebook
180 //-----------------------------------------------------------------------------
182 static void wxInsertChildInNotebook( wxNotebook
* parent
, wxWindow
* child
)
184 wxNotebookPage
*page
= new wxNotebookPage();
186 page
->m_id
= parent
->GetPageCount();
188 page
->m_box
= gtk_hbox_new (FALSE
, 0);
189 gtk_container_border_width(GTK_CONTAINER(page
->m_box
), 2);
191 GtkNotebook
*notebook
= GTK_NOTEBOOK(parent
->m_widget
);
193 page
->m_client
= child
;
194 gtk_notebook_append_page( notebook
, child
->m_widget
, page
->m_box
);
196 page
->m_page
= (GtkNotebookPage
*) (g_list_last(notebook
->children
)->data
);
198 page
->m_parent
= notebook
;
200 gtk_signal_connect( GTK_OBJECT(child
->m_widget
), "size_allocate",
201 GTK_SIGNAL_FUNC(gtk_page_size_callback
), (gpointer
)child
);
203 wxASSERT_MSG( page
->m_page
, _T("Notebook page creation error") );
205 parent
->m_pages
.Append( page
);
208 //-----------------------------------------------------------------------------
210 //-----------------------------------------------------------------------------
212 IMPLEMENT_DYNAMIC_CLASS(wxNotebook
,wxControl
)
214 BEGIN_EVENT_TABLE(wxNotebook
, wxControl
)
215 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey
)
218 void wxNotebook::Init()
220 m_imageList
= (wxImageList
*) NULL
;
221 m_pages
.DeleteContents( TRUE
);
225 wxNotebook::wxNotebook()
230 wxNotebook::wxNotebook( wxWindow
*parent
, wxWindowID id
,
231 const wxPoint
& pos
, const wxSize
& size
,
232 long style
, const wxString
& name
)
235 Create( parent
, id
, pos
, size
, style
, name
);
238 wxNotebook::~wxNotebook()
240 // don't generate change page events any more
241 if (m_idHandler
!= 0)
242 gtk_signal_disconnect(GTK_OBJECT(m_widget
), m_idHandler
);
247 bool wxNotebook::Create(wxWindow
*parent
, wxWindowID id
,
248 const wxPoint
& pos
, const wxSize
& size
,
249 long style
, const wxString
& name
)
252 m_acceptsFocus
= TRUE
;
253 m_insertCallback
= (wxInsertChildFunction
)wxInsertChildInNotebook
;
255 PreCreation( parent
, id
, pos
, size
, style
, name
);
257 m_widget
= gtk_notebook_new();
260 debug_focus_in( m_widget
, _T("wxNotebook::m_widget"), name
);
263 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget
), 1 );
265 m_idHandler
= gtk_signal_connect (
266 GTK_OBJECT(m_widget
), "switch_page",
267 GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback
),
270 m_parent
->DoAddChild( this );
272 gtk_signal_connect( GTK_OBJECT(m_widget
), "key_press_event",
273 GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback
), (gpointer
)this );
282 int wxNotebook::GetSelection() const
284 wxCHECK_MSG( m_widget
!= NULL
, -1, _T("invalid notebook") );
286 if (m_pages
.Number() == 0) return -1;
288 GtkNotebookPage
*g_page
= GTK_NOTEBOOK(m_widget
)->cur_page
;
289 if (!g_page
) return -1;
291 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
293 wxNode
*node
= m_pages
.First();
296 page
= (wxNotebookPage
*)node
->Data();
298 if ((page
->m_page
== g_page
) || (page
->m_page
== (GtkNotebookPage
*)NULL
))
300 // page->m_page is NULL directly after gtk_notebook_append. gtk emits
301 // "switch_page" then and we ask for GetSelection() in the handler for
302 // "switch_page". otherwise m_page should never be NULL. all this
303 // might also be wrong.
309 wxCHECK_MSG( node
!= NULL
, -1, _T("wxNotebook: no selection?") );
314 int wxNotebook::GetPageCount() const
316 // count only the pages which were already added to the notebook for MSW
317 // compatibility (and, in fact, this behaviour makes more sense anyhow
318 // because only the added pages are shown)
321 for ( wxNode
*node
= m_pages
.First(); node
; node
= node
->Next() )
323 wxNotebookPage
*page
= (wxNotebookPage
*)node
->Data();
325 if (page
->WasAdded()) n
++;
331 int wxNotebook::GetRowCount() const
336 wxString
wxNotebook::GetPageText( int page
) const
338 wxCHECK_MSG( m_widget
!= NULL
, _T(""), _T("invalid notebook") );
340 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
342 return nb_page
->m_text
;
347 int wxNotebook::GetPageImage( int page
) const
349 wxCHECK_MSG( m_widget
!= NULL
, 0, _T("invalid notebook") );
351 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
353 return nb_page
->m_image
;
358 wxNotebookPage
* wxNotebook::GetNotebookPage(int page
) const
360 wxCHECK_MSG( m_widget
!= NULL
, (wxNotebookPage
*)NULL
, _T("invalid notebook") );
362 wxNotebookPage
*nb_page
= (wxNotebookPage
*) NULL
;
364 wxNode
*node
= m_pages
.First();
367 nb_page
= (wxNotebookPage
*)node
->Data();
368 if (nb_page
->m_id
== page
)
373 wxFAIL_MSG( _T("Notebook page not found!") );
375 return (wxNotebookPage
*) NULL
;
378 int wxNotebook::SetSelection( int page
)
380 wxCHECK_MSG( m_widget
!= NULL
, -1, _T("invalid notebook") );
382 int selOld
= GetSelection();
383 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
385 if (!nb_page
) return -1;
388 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
391 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
396 if (!child
) return -1;
398 gtk_notebook_set_page( GTK_NOTEBOOK(m_widget
), page_num
);
403 void wxNotebook::AdvanceSelection( bool bForward
)
405 wxCHECK_RET( m_widget
!= NULL
, _T("invalid notebook") );
407 int sel
= GetSelection();
408 int max
= GetPageCount();
411 SetSelection( sel
== max
? 0 : sel
+ 1 );
413 SetSelection( sel
== 0 ? max
-1 : sel
- 1 );
416 void wxNotebook::SetImageList( wxImageList
* imageList
)
418 m_imageList
= imageList
;
421 bool wxNotebook::SetPageText( int page
, const wxString
&text
)
423 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, _T("invalid notebook") );
425 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
427 wxCHECK_MSG( nb_page
, FALSE
, _T("SetPageText: invalid page index") );
429 nb_page
->m_text
= text
;
431 gtk_label_set(nb_page
->m_label
, nb_page
->m_text
.mbc_str());
436 bool wxNotebook::SetPageImage( int page
, int image
)
438 /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
440 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
442 if (!nb_page
) return FALSE
;
444 /* Optimization posibility: return immediately if image unchanged.
445 * Not enabled because it may break existing (stupid) code that
446 * manipulates the imagelist to cycle images */
448 /* if (image == nb_page->m_image) return TRUE; */
450 /* For different cases:
451 1) no image -> no image
456 if (image
== -1 && nb_page
->m_image
== -1)
457 return TRUE
; /* Case 1): Nothing to do. */
459 GtkWidget
*pixmapwid
= (GtkWidget
*) NULL
;
461 if (nb_page
->m_image
!= -1)
463 /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
465 GList
*child
= gtk_container_children(GTK_CONTAINER(nb_page
->m_box
));
468 if (GTK_IS_PIXMAP(child
->data
))
470 pixmapwid
= GTK_WIDGET(child
->data
);
476 /* We should have the pixmap widget now */
477 wxASSERT(pixmapwid
!= NULL
);
481 /* If there's no new widget, just remove the old from the box */
482 gtk_container_remove(GTK_CONTAINER(nb_page
->m_box
), pixmapwid
);
483 nb_page
->m_image
= -1;
485 return TRUE
; /* Case 2) */
489 /* Only cases 3) and 4) left */
490 wxASSERT( m_imageList
!= NULL
); /* Just in case */
492 /* Construct the new pixmap */
493 const wxBitmap
*bmp
= m_imageList
->GetBitmap(image
);
494 GdkPixmap
*pixmap
= bmp
->GetPixmap();
495 GdkBitmap
*mask
= (GdkBitmap
*) NULL
;
496 if ( bmp
->GetMask() )
498 mask
= bmp
->GetMask()->GetBitmap();
501 if (pixmapwid
== NULL
)
503 /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
504 pixmapwid
= gtk_pixmap_new (pixmap
, mask
);
506 /* CHECKME: Are these pack flags okay? */
507 gtk_box_pack_start(GTK_BOX(nb_page
->m_box
), pixmapwid
, FALSE
, FALSE
, 3);
508 gtk_widget_show(pixmapwid
);
512 /* Case 4) Simply replace the pixmap */
513 gtk_pixmap_set(GTK_PIXMAP(pixmapwid
), pixmap
, mask
);
516 nb_page
->m_image
= image
;
521 void wxNotebook::SetPageSize( const wxSize
&WXUNUSED(size
) )
523 wxFAIL_MSG( _T("wxNotebook::SetPageSize not implemented") );
526 void wxNotebook::SetPadding( const wxSize
&WXUNUSED(padding
) )
528 wxFAIL_MSG( _T("wxNotebook::SetPadding not implemented") );
531 void wxNotebook::SetTabSize(const wxSize
& WXUNUSED(sz
))
533 wxFAIL_MSG( _T("wxNotebook::SetTabSize not implemented") );
536 bool wxNotebook::DeleteAllPages()
538 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, _T("invalid notebook") );
540 wxNode
*page_node
= m_pages
.First();
543 wxNotebookPage
*page
= (wxNotebookPage
*)page_node
->Data();
545 DeletePage( page
->m_id
);
547 page_node
= m_pages
.First();
553 bool wxNotebook::DeletePage( int page
)
555 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
556 if (!nb_page
) return FALSE
;
559 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
562 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
567 wxCHECK_MSG( child
!= NULL
, FALSE
, _T("illegal notebook index") );
569 delete nb_page
->m_client
;
571 m_pages
.DeleteObject( nb_page
);
573 /* adjust the notebook page numbers so that
574 m_id reflects the current position, Daniel Paull */
576 wxNode
*node
= m_pages
.First();
577 wxNotebookPage
*pagePtr
= (wxNotebookPage
*) NULL
;
580 pagePtr
= (wxNotebookPage
*)node
->Data();
581 pagePtr
->m_id
= count
++;
588 bool wxNotebook::RemovePage( int page
)
590 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
591 if (!nb_page
) return FALSE
;
594 GList
*child
= GTK_NOTEBOOK(m_widget
)->children
;
597 if (nb_page
->m_page
== (GtkNotebookPage
*)child
->data
) break;
602 wxCHECK_MSG( child
!= NULL
, FALSE
, _T("illegal notebook index") );
604 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget
), page_num
);
606 m_pages
.DeleteObject( nb_page
);
611 bool wxNotebook::AddPage(wxWindow
* win
, const wxString
& text
,
612 bool select
, int imageId
)
614 wxCHECK_MSG( m_widget
!= NULL
, FALSE
, _T("invalid notebook") );
616 /* we've created the notebook page in AddChild(). Now we just have to set
617 the caption for the page and set the others parameters. */
619 wxNotebookPage
*page
= (wxNotebookPage
*) NULL
;
621 wxNode
*node
= m_pages
.First();
624 page
= (wxNotebookPage
*)node
->Data();
625 if ( page
->m_client
== win
) break;
629 wxCHECK_MSG( page
!= NULL
, FALSE
,
630 _T("Can't add a page whose parent is not the notebook!") );
632 wxCHECK_MSG( page
->Add(), FALSE
,
633 _T("Can't add the same page twice to a notebook.") );
637 wxASSERT( m_imageList
!= NULL
);
639 const wxBitmap
*bmp
= m_imageList
->GetBitmap(imageId
);
640 GdkPixmap
*pixmap
= bmp
->GetPixmap();
641 GdkBitmap
*mask
= (GdkBitmap
*) NULL
;
642 if ( bmp
->GetMask() )
644 mask
= bmp
->GetMask()->GetBitmap();
647 GtkWidget
*pixmapwid
= gtk_pixmap_new (pixmap
, mask
);
649 gtk_box_pack_start(GTK_BOX(page
->m_box
), pixmapwid
, FALSE
, FALSE
, 3);
651 gtk_widget_show(pixmapwid
);
654 /* then set the attributes */
656 if (page
->m_text
.IsEmpty()) page
->m_text
= _T("");
657 page
->m_image
= imageId
;
658 page
->m_label
= (GtkLabel
*)gtk_label_new(page
->m_text
.mbc_str());
659 gtk_box_pack_end( GTK_BOX(page
->m_box
), (GtkWidget
*)page
->m_label
, FALSE
, FALSE
, 3);
661 /* @@@: what does this do? do we still need it?
662 gtk_misc_set_alignment(GTK_MISC(page->m_label), 0.0, 0.5); */
664 gtk_widget_show((GtkWidget
*)page
->m_label
);
666 if (select
) SetSelection( GetPageCount()-1 );
671 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent
& event
)
673 if (event
.IsWindowChange())
674 AdvanceSelection( event
.GetDirection() );
679 wxWindow
*wxNotebook::GetPage( int page
) const
681 wxCHECK_MSG( m_widget
!= NULL
, (wxWindow
*) NULL
, _T("invalid notebook") );
683 wxNotebookPage
* nb_page
= GetNotebookPage(page
);
685 return (wxWindow
*) NULL
;
687 return nb_page
->m_client
;
690 // override these 2 functions to do nothing: everything is done in OnSize
691 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse
) )
693 // don't set the sizes of the pages - their correct size is not yet known
694 wxControl::SetConstraintSizes(FALSE
);
697 bool wxNotebook::DoPhase( int WXUNUSED(nPhase
) )
702 void wxNotebook::ApplyWidgetStyle()
705 gtk_widget_set_style( m_widget
, m_widgetStyle
);
708 bool wxNotebook::IsOwnGtkWindow( GdkWindow
*window
)
710 return ((m_widget
->window
== window
) ||
711 (GTK_NOTEBOOK(m_widget
)->panel
== window
));
714 //-----------------------------------------------------------------------------
716 //-----------------------------------------------------------------------------
718 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent
, wxNotifyEvent
)