]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk1/notebook.cpp
Misc. fixes
[wxWidgets.git] / src / gtk1 / notebook.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: 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#ifdef __GNUG__
11#pragma implementation "notebook.h"
12#endif
13
14#include "wx/notebook.h"
15#include "wx/panel.h"
16#include "wx/utils.h"
17#include "wx/imaglist.h"
18#include "wx/intl.h"
19#include "wx/log.h"
20
21#include "gdk/gdk.h"
22#include "gtk/gtk.h"
23#include "wx/gtk/win_gtk.h"
24#include "gdk/gdkkeysyms.h"
25
26//-----------------------------------------------------------------------------
27// idle system
28//-----------------------------------------------------------------------------
29
30extern void wxapp_install_idle_handler();
31extern bool g_isIdle;
32
33//-----------------------------------------------------------------------------
34// data
35//-----------------------------------------------------------------------------
36
37extern bool g_blockEventsOnDrag;
38
39//-----------------------------------------------------------------------------
40// debug
41//-----------------------------------------------------------------------------
42
43#ifdef __WXDEBUG__
44
45extern void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *window );
46
47#endif
48
49//-----------------------------------------------------------------------------
50// wxNotebookPage
51//-----------------------------------------------------------------------------
52
53class wxNotebookPage: public wxObject
54{
55public:
56 wxNotebookPage()
57 {
58 m_id = -1;
59 m_text = "";
60 m_image = -1;
61 m_page = (GtkNotebookPage *) NULL;
62 m_client = (wxWindow *) NULL;
63 m_parent = (GtkNotebook *) NULL;
64 m_box = (GtkWidget *) NULL;
65 m_added = FALSE;
66 }
67
68 /*
69 mark page as "added' to the notebook, return FALSE if the page was
70 already added
71 */
72
73 bool Add()
74 {
75 if ( WasAdded() )
76 return FALSE;
77
78 m_added = TRUE;
79 return TRUE;
80 }
81
82 bool WasAdded() const { return m_added; }
83
84 int m_id;
85 wxString m_text;
86 int m_image;
87 GtkNotebookPage *m_page;
88 GtkLabel *m_label;
89 wxWindow *m_client;
90 GtkNotebook *m_parent;
91 GtkWidget *m_box; // in which the label and image are packed
92
93private:
94 bool m_added;
95};
96
97//-----------------------------------------------------------------------------
98// "switch_page"
99//-----------------------------------------------------------------------------
100
101static void gtk_notebook_page_change_callback(GtkNotebook *WXUNUSED(widget),
102 GtkNotebookPage *WXUNUSED(page),
103 gint nPage,
104 gpointer data)
105{
106 if (g_isIdle) wxapp_install_idle_handler();
107
108 wxNotebook *notebook = (wxNotebook *)data;
109
110 int old = notebook->GetSelection();
111
112 // TODO: emulate PAGE_CHANGING event
113
114 wxNotebookEvent event( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,
115 notebook->GetId(), nPage, old );
116 event.SetEventObject( notebook );
117 notebook->GetEventHandler()->ProcessEvent( event );
118}
119
120//-----------------------------------------------------------------------------
121// "size_allocate"
122//-----------------------------------------------------------------------------
123
124static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
125{
126 if (g_isIdle) wxapp_install_idle_handler();
127
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))
132 {
133 return;
134 }
135
136 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
137
138 if (win->GetAutoLayout()) win->Layout();
139}
140
141//-----------------------------------------------------------------------------
142// "key_press_event"
143//-----------------------------------------------------------------------------
144
145static gint
146gtk_notebook_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxNotebook *notebook )
147{
148 if (g_isIdle) wxapp_install_idle_handler();
149
150 if (g_blockEventsOnDrag) return FALSE;
151
152 if (!notebook->HasVMT()) return FALSE;
153
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 */
157
158 if (gdk_event->keyval != GDK_Down) return FALSE;
159
160 if (notebook != notebook->FindFocus()) return FALSE;
161
162 if (notebook->m_pages.GetCount() == 0) return FALSE;
163
164 wxNode *node = notebook->m_pages.Nth( notebook->GetSelection() );
165
166 if (!node) return FALSE;
167
168 wxNotebookPage *page = (wxNotebookPage*) node->Data();
169
170 // don't let others the key event
171 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
172
173 page->m_client->SetFocus();
174
175 return TRUE;
176}
177
178//-----------------------------------------------------------------------------
179// InsertChild callback for wxNotebook
180//-----------------------------------------------------------------------------
181
182static void wxInsertChildInNotebook( wxNotebook* parent, wxWindow* child )
183{
184 wxNotebookPage *page = new wxNotebookPage();
185
186 page->m_id = parent->GetPageCount();
187
188 page->m_box = gtk_hbox_new (FALSE, 0);
189 gtk_container_border_width(GTK_CONTAINER(page->m_box), 2);
190
191 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
192
193 page->m_client = child;
194 gtk_notebook_append_page( notebook, child->m_widget, page->m_box );
195
196 page->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
197
198 page->m_parent = notebook;
199
200 gtk_signal_connect( GTK_OBJECT(child->m_widget), "size_allocate",
201 GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)child );
202
203 wxASSERT_MSG( page->m_page, _T("Notebook page creation error") );
204
205 parent->m_pages.Append( page );
206}
207
208//-----------------------------------------------------------------------------
209// wxNotebook
210//-----------------------------------------------------------------------------
211
212IMPLEMENT_DYNAMIC_CLASS(wxNotebook,wxControl)
213
214BEGIN_EVENT_TABLE(wxNotebook, wxControl)
215 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
216END_EVENT_TABLE()
217
218void wxNotebook::Init()
219{
220 m_imageList = (wxImageList *) NULL;
221 m_pages.DeleteContents( TRUE );
222 m_idHandler = 0;
223}
224
225wxNotebook::wxNotebook()
226{
227 Init();
228}
229
230wxNotebook::wxNotebook( wxWindow *parent, wxWindowID id,
231 const wxPoint& pos, const wxSize& size,
232 long style, const wxString& name )
233{
234 Init();
235 Create( parent, id, pos, size, style, name );
236}
237
238wxNotebook::~wxNotebook()
239{
240 // don't generate change page events any more
241 if (m_idHandler != 0)
242 gtk_signal_disconnect(GTK_OBJECT(m_widget), m_idHandler);
243
244 DeleteAllPages();
245}
246
247bool wxNotebook::Create(wxWindow *parent, wxWindowID id,
248 const wxPoint& pos, const wxSize& size,
249 long style, const wxString& name )
250{
251 m_needParent = TRUE;
252 m_acceptsFocus = TRUE;
253 m_insertCallback = (wxInsertChildFunction)wxInsertChildInNotebook;
254
255 PreCreation( parent, id, pos, size, style, name );
256
257 m_widget = gtk_notebook_new();
258
259#ifdef __WXDEBUG__
260 debug_focus_in( m_widget, _T("wxNotebook::m_widget"), name );
261#endif
262
263 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
264
265 m_idHandler = gtk_signal_connect (
266 GTK_OBJECT(m_widget), "switch_page",
267 GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback),
268 (gpointer)this );
269
270 m_parent->AddChild( this );
271
272 (m_parent->m_insertCallback)( m_parent, this );
273
274 gtk_signal_connect( GTK_OBJECT(m_widget), "key_press_event",
275 GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback), (gpointer)this );
276
277 PostCreation();
278
279 Show( TRUE );
280
281 return TRUE;
282}
283
284int wxNotebook::GetSelection() const
285{
286 wxCHECK_MSG( m_widget != NULL, -1, _T("invalid notebook") );
287
288 if (m_pages.Number() == 0) return -1;
289
290 GtkNotebookPage *g_page = GTK_NOTEBOOK(m_widget)->cur_page;
291 if (!g_page) return -1;
292
293 wxNotebookPage *page = (wxNotebookPage *) NULL;
294
295 wxNode *node = m_pages.First();
296 while (node)
297 {
298 page = (wxNotebookPage*)node->Data();
299
300 if ((page->m_page == g_page) || (page->m_page == (GtkNotebookPage*)NULL))
301 {
302 // page->m_page is NULL directly after gtk_notebook_append. gtk emits
303 // "switch_page" then and we ask for GetSelection() in the handler for
304 // "switch_page". otherwise m_page should never be NULL. all this
305 // might also be wrong.
306 break;
307 }
308 node = node->Next();
309 }
310
311 wxCHECK_MSG( node != NULL, -1, _T("wxNotebook: no selection?") );
312
313 return page->m_id;
314}
315
316int wxNotebook::GetPageCount() const
317{
318 // count only the pages which were already added to the notebook for MSW
319 // compatibility (and, in fact, this behaviour makes more sense anyhow
320 // because only the added pages are shown)
321
322 int n = 0;
323 for ( wxNode *node = m_pages.First(); node; node = node->Next() )
324 {
325 wxNotebookPage *page = (wxNotebookPage*)node->Data();
326
327 if (page->WasAdded()) n++;
328 }
329
330 return n;
331}
332
333int wxNotebook::GetRowCount() const
334{
335 return 1;
336}
337
338wxString wxNotebook::GetPageText( int page ) const
339{
340 wxCHECK_MSG( m_widget != NULL, _T(""), _T("invalid notebook") );
341
342 wxNotebookPage* nb_page = GetNotebookPage(page);
343 if (nb_page)
344 return nb_page->m_text;
345 else
346 return "";
347}
348
349int wxNotebook::GetPageImage( int page ) const
350{
351 wxCHECK_MSG( m_widget != NULL, 0, _T("invalid notebook") );
352
353 wxNotebookPage* nb_page = GetNotebookPage(page);
354 if (nb_page)
355 return nb_page->m_image;
356 else
357 return 0;
358}
359
360wxNotebookPage* wxNotebook::GetNotebookPage(int page) const
361{
362 wxCHECK_MSG( m_widget != NULL, (wxNotebookPage*)NULL, _T("invalid notebook") );
363
364 wxNotebookPage *nb_page = (wxNotebookPage *) NULL;
365
366 wxNode *node = m_pages.First();
367 while (node)
368 {
369 nb_page = (wxNotebookPage*)node->Data();
370 if (nb_page->m_id == page)
371 return nb_page;
372 node = node->Next();
373 }
374
375 wxFAIL_MSG( _T("Notebook page not found!") );
376
377 return (wxNotebookPage *) NULL;
378}
379
380int wxNotebook::SetSelection( int page )
381{
382 wxCHECK_MSG( m_widget != NULL, -1, _T("invalid notebook") );
383
384 int selOld = GetSelection();
385 wxNotebookPage* nb_page = GetNotebookPage(page);
386
387 if (!nb_page) return -1;
388
389 int page_num = 0;
390 GList *child = GTK_NOTEBOOK(m_widget)->children;
391 while (child)
392 {
393 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
394 page_num++;
395 child = child->next;
396 }
397
398 if (!child) return -1;
399
400 gtk_notebook_set_page( GTK_NOTEBOOK(m_widget), page_num );
401
402 return selOld;
403}
404
405void wxNotebook::AdvanceSelection( bool bForward )
406{
407 wxCHECK_RET( m_widget != NULL, _T("invalid notebook") );
408
409 int sel = GetSelection();
410 int max = GetPageCount();
411
412 if (bForward)
413 SetSelection( sel == max ? 0 : sel + 1 );
414 else
415 SetSelection( sel == 0 ? max-1 : sel - 1 );
416}
417
418void wxNotebook::SetImageList( wxImageList* imageList )
419{
420 m_imageList = imageList;
421}
422
423bool wxNotebook::SetPageText( int page, const wxString &text )
424{
425 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
426
427 wxNotebookPage* nb_page = GetNotebookPage(page);
428
429 wxCHECK_MSG( nb_page, FALSE, _T("SetPageText: invalid page index") );
430
431 nb_page->m_text = text;
432
433 gtk_label_set(nb_page->m_label, nb_page->m_text.mbc_str());
434
435 return TRUE;
436}
437
438bool wxNotebook::SetPageImage( int page, int image )
439{
440 /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
441
442 wxNotebookPage* nb_page = GetNotebookPage(page);
443
444 if (!nb_page) return FALSE;
445
446 /* Optimization posibility: return immediately if image unchanged.
447 * Not enabled because it may break existing (stupid) code that
448 * manipulates the imagelist to cycle images */
449
450 /* if (image == nb_page->m_image) return TRUE; */
451
452 /* For different cases:
453 1) no image -> no image
454 2) image -> no image
455 3) no image -> image
456 4) image -> image */
457
458 if (image == -1 && nb_page->m_image == -1)
459 return TRUE; /* Case 1): Nothing to do. */
460
461 GtkWidget *pixmapwid = (GtkWidget*) NULL;
462
463 if (nb_page->m_image != -1)
464 {
465 /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
466
467 GList *child = gtk_container_children(GTK_CONTAINER(nb_page->m_box));
468 while (child)
469 {
470 if (GTK_IS_PIXMAP(child->data))
471 {
472 pixmapwid = GTK_WIDGET(child->data);
473 break;
474 }
475 child = child->next;
476 }
477
478 /* We should have the pixmap widget now */
479 wxASSERT(pixmapwid != NULL);
480
481 if (image == -1)
482 {
483 /* If there's no new widget, just remove the old from the box */
484 gtk_container_remove(GTK_CONTAINER(nb_page->m_box), pixmapwid);
485 nb_page->m_image = -1;
486
487 return TRUE; /* Case 2) */
488 }
489 }
490
491 /* Only cases 3) and 4) left */
492 wxASSERT( m_imageList != NULL ); /* Just in case */
493
494 /* Construct the new pixmap */
495 const wxBitmap *bmp = m_imageList->GetBitmap(image);
496 GdkPixmap *pixmap = bmp->GetPixmap();
497 GdkBitmap *mask = (GdkBitmap*) NULL;
498 if ( bmp->GetMask() )
499 {
500 mask = bmp->GetMask()->GetBitmap();
501 }
502
503 if (pixmapwid == NULL)
504 {
505 /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
506 pixmapwid = gtk_pixmap_new (pixmap, mask );
507
508 /* CHECKME: Are these pack flags okay? */
509 gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, 3);
510 gtk_widget_show(pixmapwid);
511 }
512 else
513 {
514 /* Case 4) Simply replace the pixmap */
515 gtk_pixmap_set(GTK_PIXMAP(pixmapwid), pixmap, mask);
516 }
517
518 nb_page->m_image = image;
519
520 return TRUE;
521}
522
523void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
524{
525 wxFAIL_MSG( _T("wxNotebook::SetPageSize not implemented") );
526}
527
528void wxNotebook::SetPadding( const wxSize &WXUNUSED(padding) )
529{
530 wxFAIL_MSG( _T("wxNotebook::SetPadding not implemented") );
531}
532
533void wxNotebook::SetTabSize(const wxSize& sz)
534{
535 wxFAIL_MSG( _T("wxNotebook::SetTabSize not implemented") );
536}
537
538bool wxNotebook::DeleteAllPages()
539{
540 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
541
542 wxNode *page_node = m_pages.First();
543 while (page_node)
544 {
545 wxNotebookPage *page = (wxNotebookPage*)page_node->Data();
546
547 DeletePage( page->m_id );
548
549 page_node = m_pages.First();
550 }
551
552 return TRUE;
553}
554
555bool wxNotebook::DeletePage( int page )
556{
557 wxNotebookPage* nb_page = GetNotebookPage(page);
558 if (!nb_page) return FALSE;
559
560 int page_num = 0;
561 GList *child = GTK_NOTEBOOK(m_widget)->children;
562 while (child)
563 {
564 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
565 page_num++;
566 child = child->next;
567 }
568
569 wxCHECK_MSG( child != NULL, FALSE, _T("illegal notebook index") );
570
571 delete nb_page->m_client;
572
573 m_pages.DeleteObject( nb_page );
574
575 /* adjust the notebook page numbers so that
576 m_id reflects the current position, Daniel Paull */
577 int count = 0;
578 wxNode *node = m_pages.First();
579 wxNotebookPage *pagePtr = (wxNotebookPage *) NULL;
580 while (node)
581 {
582 pagePtr = (wxNotebookPage*)node->Data();
583 pagePtr->m_id = count++;
584 node = node->Next();
585 }
586
587 return TRUE;
588}
589
590bool wxNotebook::RemovePage( int page )
591{
592 wxNotebookPage* nb_page = GetNotebookPage(page);
593 if (!nb_page) return FALSE;
594
595 int page_num = 0;
596 GList *child = GTK_NOTEBOOK(m_widget)->children;
597 while (child)
598 {
599 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
600 page_num++;
601 child = child->next;
602 }
603
604 wxCHECK_MSG( child != NULL, FALSE, _T("illegal notebook index") );
605
606 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page_num );
607
608 m_pages.DeleteObject( nb_page );
609
610 return TRUE;
611}
612
613bool wxNotebook::AddPage(wxWindow* win, const wxString& text,
614 bool select, int imageId)
615{
616 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
617
618 /* we've created the notebook page in AddChild(). Now we just have to set
619 the caption for the page and set the others parameters. */
620
621 wxNotebookPage *page = (wxNotebookPage *) NULL;
622
623 wxNode *node = m_pages.First();
624 while (node)
625 {
626 page = (wxNotebookPage*)node->Data();
627 if ( page->m_client == win ) break;
628 node = node->Next();
629 }
630
631 wxCHECK_MSG( page != NULL, FALSE,
632 _T("Can't add a page whose parent is not the notebook!") );
633
634 wxCHECK_MSG( page->Add(), FALSE,
635 _T("Can't add the same page twice to a notebook.") );
636
637 if (imageId != -1)
638 {
639 wxASSERT( m_imageList != NULL );
640
641 const wxBitmap *bmp = m_imageList->GetBitmap(imageId);
642 GdkPixmap *pixmap = bmp->GetPixmap();
643 GdkBitmap *mask = (GdkBitmap*) NULL;
644 if ( bmp->GetMask() )
645 {
646 mask = bmp->GetMask()->GetBitmap();
647 }
648
649 GtkWidget *pixmapwid = gtk_pixmap_new (pixmap, mask );
650
651 gtk_box_pack_start(GTK_BOX(page->m_box), pixmapwid, FALSE, FALSE, 3);
652
653 gtk_widget_show(pixmapwid);
654 }
655
656 /* then set the attributes */
657 page->m_text = text;
658 if (page->m_text.IsEmpty()) page->m_text = _T("");
659 page->m_image = imageId;
660 page->m_label = (GtkLabel *)gtk_label_new(page->m_text.mbc_str());
661 gtk_box_pack_end( GTK_BOX(page->m_box), (GtkWidget *)page->m_label, FALSE, FALSE, 3);
662
663 /* @@@: what does this do? do we still need it?
664 gtk_misc_set_alignment(GTK_MISC(page->m_label), 0.0, 0.5); */
665
666 gtk_widget_show((GtkWidget *)page->m_label);
667
668 if (select) SetSelection( GetPageCount()-1 );
669
670 return TRUE;
671}
672
673void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
674{
675 if (event.IsWindowChange())
676 AdvanceSelection( event.GetDirection() );
677 else
678 event.Skip();
679}
680
681wxWindow *wxNotebook::GetPage( int page ) const
682{
683 wxCHECK_MSG( m_widget != NULL, (wxWindow*) NULL, _T("invalid notebook") );
684
685 wxNotebookPage* nb_page = GetNotebookPage(page);
686 if (!nb_page)
687 return (wxWindow *) NULL;
688 else
689 return nb_page->m_client;
690}
691
692// override these 2 functions to do nothing: everything is done in OnSize
693void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
694{
695 // don't set the sizes of the pages - their correct size is not yet known
696 wxControl::SetConstraintSizes(FALSE);
697}
698
699bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
700{
701 return TRUE;
702}
703
704void wxNotebook::ApplyWidgetStyle()
705{
706 SetWidgetStyle();
707 gtk_widget_set_style( m_widget, m_widgetStyle );
708}
709
710bool wxNotebook::IsOwnGtkWindow( GdkWindow *window )
711{
712 return ((m_widget->window == window) ||
713 (GTK_NOTEBOOK(m_widget)->panel == window));
714}
715
716//-----------------------------------------------------------------------------
717// wxNotebookEvent
718//-----------------------------------------------------------------------------
719
720IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
721