unused parameter warnings suppressed
[wxWidgets.git] / src / gtk / notebook.cpp
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
30 extern void wxapp_install_idle_handler();
31 extern bool g_isIdle;
32
33 //-----------------------------------------------------------------------------
34 // data
35 //-----------------------------------------------------------------------------
36
37 extern bool g_blockEventsOnDrag;
38
39 //-----------------------------------------------------------------------------
40 // debug
41 //-----------------------------------------------------------------------------
42
43 #ifdef __WXDEBUG__
44
45 extern void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *window );
46
47 #endif
48
49 //-----------------------------------------------------------------------------
50 // wxNotebookPage
51 //-----------------------------------------------------------------------------
52
53 class wxNotebookPage: public wxObject
54 {
55 public:
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
93 private:
94 bool m_added;
95 };
96
97 //-----------------------------------------------------------------------------
98 // "switch_page"
99 //-----------------------------------------------------------------------------
100
101 static 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
124 static 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
145 static gint
146 gtk_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->m_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
182 static 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
212 IMPLEMENT_DYNAMIC_CLASS(wxNotebook,wxControl)
213
214 BEGIN_EVENT_TABLE(wxNotebook, wxControl)
215 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
216 END_EVENT_TABLE()
217
218 void wxNotebook::Init()
219 {
220 m_imageList = (wxImageList *) NULL;
221 m_pages.DeleteContents( TRUE );
222 m_idHandler = 0;
223 }
224
225 wxNotebook::wxNotebook()
226 {
227 Init();
228 }
229
230 wxNotebook::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
238 wxNotebook::~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
247 bool 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->DoAddChild( this );
271
272 gtk_signal_connect( GTK_OBJECT(m_widget), "key_press_event",
273 GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback), (gpointer)this );
274
275 PostCreation();
276
277 Show( TRUE );
278
279 return TRUE;
280 }
281
282 int wxNotebook::GetSelection() const
283 {
284 wxCHECK_MSG( m_widget != NULL, -1, _T("invalid notebook") );
285
286 if (m_pages.Number() == 0) return -1;
287
288 GtkNotebookPage *g_page = GTK_NOTEBOOK(m_widget)->cur_page;
289 if (!g_page) return -1;
290
291 wxNotebookPage *page = (wxNotebookPage *) NULL;
292
293 wxNode *node = m_pages.First();
294 while (node)
295 {
296 page = (wxNotebookPage*)node->Data();
297
298 if ((page->m_page == g_page) || (page->m_page == (GtkNotebookPage*)NULL))
299 {
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.
304 break;
305 }
306 node = node->Next();
307 }
308
309 wxCHECK_MSG( node != NULL, -1, _T("wxNotebook: no selection?") );
310
311 return page->m_id;
312 }
313
314 int wxNotebook::GetPageCount() const
315 {
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)
319
320 int n = 0;
321 for ( wxNode *node = m_pages.First(); node; node = node->Next() )
322 {
323 wxNotebookPage *page = (wxNotebookPage*)node->Data();
324
325 if (page->WasAdded()) n++;
326 }
327
328 return n;
329 }
330
331 int wxNotebook::GetRowCount() const
332 {
333 return 1;
334 }
335
336 wxString wxNotebook::GetPageText( int page ) const
337 {
338 wxCHECK_MSG( m_widget != NULL, _T(""), _T("invalid notebook") );
339
340 wxNotebookPage* nb_page = GetNotebookPage(page);
341 if (nb_page)
342 return nb_page->m_text;
343 else
344 return "";
345 }
346
347 int wxNotebook::GetPageImage( int page ) const
348 {
349 wxCHECK_MSG( m_widget != NULL, 0, _T("invalid notebook") );
350
351 wxNotebookPage* nb_page = GetNotebookPage(page);
352 if (nb_page)
353 return nb_page->m_image;
354 else
355 return 0;
356 }
357
358 wxNotebookPage* wxNotebook::GetNotebookPage(int page) const
359 {
360 wxCHECK_MSG( m_widget != NULL, (wxNotebookPage*)NULL, _T("invalid notebook") );
361
362 wxNotebookPage *nb_page = (wxNotebookPage *) NULL;
363
364 wxNode *node = m_pages.First();
365 while (node)
366 {
367 nb_page = (wxNotebookPage*)node->Data();
368 if (nb_page->m_id == page)
369 return nb_page;
370 node = node->Next();
371 }
372
373 wxFAIL_MSG( _T("Notebook page not found!") );
374
375 return (wxNotebookPage *) NULL;
376 }
377
378 int wxNotebook::SetSelection( int page )
379 {
380 wxCHECK_MSG( m_widget != NULL, -1, _T("invalid notebook") );
381
382 int selOld = GetSelection();
383 wxNotebookPage* nb_page = GetNotebookPage(page);
384
385 if (!nb_page) return -1;
386
387 int page_num = 0;
388 GList *child = GTK_NOTEBOOK(m_widget)->children;
389 while (child)
390 {
391 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
392 page_num++;
393 child = child->next;
394 }
395
396 if (!child) return -1;
397
398 gtk_notebook_set_page( GTK_NOTEBOOK(m_widget), page_num );
399
400 return selOld;
401 }
402
403 void wxNotebook::AdvanceSelection( bool bForward )
404 {
405 wxCHECK_RET( m_widget != NULL, _T("invalid notebook") );
406
407 int sel = GetSelection();
408 int max = GetPageCount();
409
410 if (bForward)
411 SetSelection( sel == max ? 0 : sel + 1 );
412 else
413 SetSelection( sel == 0 ? max-1 : sel - 1 );
414 }
415
416 void wxNotebook::SetImageList( wxImageList* imageList )
417 {
418 m_imageList = imageList;
419 }
420
421 bool wxNotebook::SetPageText( int page, const wxString &text )
422 {
423 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
424
425 wxNotebookPage* nb_page = GetNotebookPage(page);
426
427 wxCHECK_MSG( nb_page, FALSE, _T("SetPageText: invalid page index") );
428
429 nb_page->m_text = text;
430
431 gtk_label_set(nb_page->m_label, nb_page->m_text.mbc_str());
432
433 return TRUE;
434 }
435
436 bool wxNotebook::SetPageImage( int page, int image )
437 {
438 /* HvdH 28-12-98: now it works, but it's a bit of a kludge */
439
440 wxNotebookPage* nb_page = GetNotebookPage(page);
441
442 if (!nb_page) return FALSE;
443
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 */
447
448 /* if (image == nb_page->m_image) return TRUE; */
449
450 /* For different cases:
451 1) no image -> no image
452 2) image -> no image
453 3) no image -> image
454 4) image -> image */
455
456 if (image == -1 && nb_page->m_image == -1)
457 return TRUE; /* Case 1): Nothing to do. */
458
459 GtkWidget *pixmapwid = (GtkWidget*) NULL;
460
461 if (nb_page->m_image != -1)
462 {
463 /* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
464
465 GList *child = gtk_container_children(GTK_CONTAINER(nb_page->m_box));
466 while (child)
467 {
468 if (GTK_IS_PIXMAP(child->data))
469 {
470 pixmapwid = GTK_WIDGET(child->data);
471 break;
472 }
473 child = child->next;
474 }
475
476 /* We should have the pixmap widget now */
477 wxASSERT(pixmapwid != NULL);
478
479 if (image == -1)
480 {
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;
484
485 return TRUE; /* Case 2) */
486 }
487 }
488
489 /* Only cases 3) and 4) left */
490 wxASSERT( m_imageList != NULL ); /* Just in case */
491
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() )
497 {
498 mask = bmp->GetMask()->GetBitmap();
499 }
500
501 if (pixmapwid == NULL)
502 {
503 /* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
504 pixmapwid = gtk_pixmap_new (pixmap, mask );
505
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);
509 }
510 else
511 {
512 /* Case 4) Simply replace the pixmap */
513 gtk_pixmap_set(GTK_PIXMAP(pixmapwid), pixmap, mask);
514 }
515
516 nb_page->m_image = image;
517
518 return TRUE;
519 }
520
521 void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
522 {
523 wxFAIL_MSG( _T("wxNotebook::SetPageSize not implemented") );
524 }
525
526 void wxNotebook::SetPadding( const wxSize &WXUNUSED(padding) )
527 {
528 wxFAIL_MSG( _T("wxNotebook::SetPadding not implemented") );
529 }
530
531 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
532 {
533 wxFAIL_MSG( _T("wxNotebook::SetTabSize not implemented") );
534 }
535
536 bool wxNotebook::DeleteAllPages()
537 {
538 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
539
540 wxNode *page_node = m_pages.First();
541 while (page_node)
542 {
543 wxNotebookPage *page = (wxNotebookPage*)page_node->Data();
544
545 DeletePage( page->m_id );
546
547 page_node = m_pages.First();
548 }
549
550 return TRUE;
551 }
552
553 bool wxNotebook::DeletePage( int page )
554 {
555 wxNotebookPage* nb_page = GetNotebookPage(page);
556 if (!nb_page) return FALSE;
557
558 int page_num = 0;
559 GList *child = GTK_NOTEBOOK(m_widget)->children;
560 while (child)
561 {
562 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
563 page_num++;
564 child = child->next;
565 }
566
567 wxCHECK_MSG( child != NULL, FALSE, _T("illegal notebook index") );
568
569 delete nb_page->m_client;
570
571 m_pages.DeleteObject( nb_page );
572
573 /* adjust the notebook page numbers so that
574 m_id reflects the current position, Daniel Paull */
575 int count = 0;
576 wxNode *node = m_pages.First();
577 wxNotebookPage *pagePtr = (wxNotebookPage *) NULL;
578 while (node)
579 {
580 pagePtr = (wxNotebookPage*)node->Data();
581 pagePtr->m_id = count++;
582 node = node->Next();
583 }
584
585 return TRUE;
586 }
587
588 bool wxNotebook::RemovePage( int page )
589 {
590 wxNotebookPage* nb_page = GetNotebookPage(page);
591 if (!nb_page) return FALSE;
592
593 int page_num = 0;
594 GList *child = GTK_NOTEBOOK(m_widget)->children;
595 while (child)
596 {
597 if (nb_page->m_page == (GtkNotebookPage*)child->data) break;
598 page_num++;
599 child = child->next;
600 }
601
602 wxCHECK_MSG( child != NULL, FALSE, _T("illegal notebook index") );
603
604 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page_num );
605
606 m_pages.DeleteObject( nb_page );
607
608 return TRUE;
609 }
610
611 bool wxNotebook::AddPage(wxWindow* win, const wxString& text,
612 bool select, int imageId)
613 {
614 wxCHECK_MSG( m_widget != NULL, FALSE, _T("invalid notebook") );
615
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. */
618
619 wxNotebookPage *page = (wxNotebookPage *) NULL;
620
621 wxNode *node = m_pages.First();
622 while (node)
623 {
624 page = (wxNotebookPage*)node->Data();
625 if ( page->m_client == win ) break;
626 node = node->Next();
627 }
628
629 wxCHECK_MSG( page != NULL, FALSE,
630 _T("Can't add a page whose parent is not the notebook!") );
631
632 wxCHECK_MSG( page->Add(), FALSE,
633 _T("Can't add the same page twice to a notebook.") );
634
635 if (imageId != -1)
636 {
637 wxASSERT( m_imageList != NULL );
638
639 const wxBitmap *bmp = m_imageList->GetBitmap(imageId);
640 GdkPixmap *pixmap = bmp->GetPixmap();
641 GdkBitmap *mask = (GdkBitmap*) NULL;
642 if ( bmp->GetMask() )
643 {
644 mask = bmp->GetMask()->GetBitmap();
645 }
646
647 GtkWidget *pixmapwid = gtk_pixmap_new (pixmap, mask );
648
649 gtk_box_pack_start(GTK_BOX(page->m_box), pixmapwid, FALSE, FALSE, 3);
650
651 gtk_widget_show(pixmapwid);
652 }
653
654 /* then set the attributes */
655 page->m_text = text;
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);
660
661 /* @@@: what does this do? do we still need it?
662 gtk_misc_set_alignment(GTK_MISC(page->m_label), 0.0, 0.5); */
663
664 gtk_widget_show((GtkWidget *)page->m_label);
665
666 if (select) SetSelection( GetPageCount()-1 );
667
668 return TRUE;
669 }
670
671 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
672 {
673 if (event.IsWindowChange())
674 AdvanceSelection( event.GetDirection() );
675 else
676 event.Skip();
677 }
678
679 wxWindow *wxNotebook::GetPage( int page ) const
680 {
681 wxCHECK_MSG( m_widget != NULL, (wxWindow*) NULL, _T("invalid notebook") );
682
683 wxNotebookPage* nb_page = GetNotebookPage(page);
684 if (!nb_page)
685 return (wxWindow *) NULL;
686 else
687 return nb_page->m_client;
688 }
689
690 // override these 2 functions to do nothing: everything is done in OnSize
691 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
692 {
693 // don't set the sizes of the pages - their correct size is not yet known
694 wxControl::SetConstraintSizes(FALSE);
695 }
696
697 bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
698 {
699 return TRUE;
700 }
701
702 void wxNotebook::ApplyWidgetStyle()
703 {
704 SetWidgetStyle();
705 gtk_widget_set_style( m_widget, m_widgetStyle );
706 }
707
708 bool wxNotebook::IsOwnGtkWindow( GdkWindow *window )
709 {
710 return ((m_widget->window == window) ||
711 (GTK_NOTEBOOK(m_widget)->panel == window));
712 }
713
714 //-----------------------------------------------------------------------------
715 // wxNotebookEvent
716 //-----------------------------------------------------------------------------
717
718 IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxNotifyEvent)
719