]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/notebook.cpp
Minor correction
[wxWidgets.git] / src / gtk / notebook.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/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 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_NOTEBOOK
14
15 #include "wx/notebook.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/intl.h"
19 #include "wx/log.h"
20 #include "wx/utils.h"
21 #include "wx/msgdlg.h"
22 #include "wx/bitmap.h"
23 #endif
24
25 #include "wx/imaglist.h"
26 #include "wx/fontutil.h"
27
28 #include "wx/gtk/private.h"
29
30 //-----------------------------------------------------------------------------
31 // wxGtkNotebookPage
32 //-----------------------------------------------------------------------------
33
34 // VZ: this is rather ugly as we keep the pages themselves in an array (it
35 // allows us to have quite a few functions implemented in the base class)
36 // but the page data is kept in a separate list, so we must maintain them
37 // in sync manually... of course, the list had been there before the base
38 // class which explains it but it still would be nice to do something
39 // about this one day
40
41 class wxGtkNotebookPage: public wxObject
42 {
43 public:
44 GtkWidget* m_box;
45 GtkWidget* m_label;
46 GtkWidget* m_image;
47 int m_imageIndex;
48 };
49
50
51 #include "wx/listimpl.cpp"
52 WX_DEFINE_LIST(wxGtkNotebookPagesList)
53
54 extern "C" {
55 static void event_after(GtkNotebook*, GdkEvent*, wxNotebook*);
56 }
57
58 //-----------------------------------------------------------------------------
59 // "switch_page"
60 //-----------------------------------------------------------------------------
61
62 extern "C" {
63 static void
64 switch_page_after(GtkWidget* widget, GtkNotebookPage*, guint, wxNotebook* win)
65 {
66 g_signal_handlers_block_by_func(widget, (void*)switch_page_after, win);
67 win->SendPageChangedEvent(win->m_oldSelection);
68 }
69 }
70
71 extern "C" {
72 static void
73 switch_page(GtkNotebook* widget, GtkNotebookPage*, int page, wxNotebook* win)
74 {
75 win->m_oldSelection = gtk_notebook_get_current_page(widget);
76
77 if (win->SendPageChangingEvent(page))
78 // allow change, unblock handler for changed event
79 g_signal_handlers_unblock_by_func(widget, (void*)switch_page_after, win);
80 else
81 // change vetoed, unblock handler to set selection back
82 g_signal_handlers_unblock_by_func(widget, (void*)event_after, win);
83 }
84 }
85
86 //-----------------------------------------------------------------------------
87 // "event_after" from m_widget
88 //-----------------------------------------------------------------------------
89
90 extern "C" {
91 static void event_after(GtkNotebook* widget, GdkEvent*, wxNotebook* win)
92 {
93 g_signal_handlers_block_by_func(widget, (void*)event_after, win);
94 g_signal_handlers_block_by_func(widget, (void*)switch_page, win);
95
96 // restore previous selection
97 gtk_notebook_set_current_page(widget, win->m_oldSelection);
98
99 g_signal_handlers_unblock_by_func(widget, (void*)switch_page, win);
100 }
101 }
102
103 //-----------------------------------------------------------------------------
104 // InsertChild callback for wxNotebook
105 //-----------------------------------------------------------------------------
106
107 static void wxInsertChildInNotebook(wxWindow* parent, wxWindow* child)
108 {
109 // Hack Alert! (Part I): This sets the notebook as the parent of the child
110 // widget, and takes care of some details such as updating the state and
111 // style of the child to reflect its new location. We do this early
112 // because without it GetBestSize (which is used to set the initial size
113 // of controls if an explicit size is not given) will often report
114 // incorrect sizes since the widget's style context is not fully known.
115 // See bug #901694 for details
116 // (http://sourceforge.net/tracker/?func=detail&aid=901694&group_id=9863&atid=109863)
117 gtk_widget_set_parent(child->m_widget, parent->m_widget);
118
119 // NOTE: This should be considered a temporary workaround until we can
120 // work out the details and implement delaying the setting of the initial
121 // size of widgets until the size is really needed.
122 }
123
124 //-----------------------------------------------------------------------------
125 // wxNotebook
126 //-----------------------------------------------------------------------------
127
128 IMPLEMENT_DYNAMIC_CLASS(wxNotebook,wxBookCtrlBase)
129
130 BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
131 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
132 END_EVENT_TABLE()
133
134 void wxNotebook::Init()
135 {
136 m_padding = 0;
137 m_oldSelection = -1;
138 m_themeEnabled = true;
139 }
140
141 wxNotebook::wxNotebook()
142 {
143 Init();
144 }
145
146 wxNotebook::wxNotebook( wxWindow *parent, wxWindowID id,
147 const wxPoint& pos, const wxSize& size,
148 long style, const wxString& name )
149 {
150 Init();
151 Create( parent, id, pos, size, style, name );
152 }
153
154 wxNotebook::~wxNotebook()
155 {
156 DeleteAllPages();
157 }
158
159 bool wxNotebook::Create(wxWindow *parent, wxWindowID id,
160 const wxPoint& pos, const wxSize& size,
161 long style, const wxString& name )
162 {
163 m_insertCallback = wxInsertChildInNotebook;
164
165 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
166 style |= wxBK_TOP;
167
168 if (!PreCreation( parent, pos, size ) ||
169 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
170 {
171 wxFAIL_MSG( wxT("wxNoteBook creation failed") );
172 return false;
173 }
174
175
176 m_widget = gtk_notebook_new();
177 g_object_ref(m_widget);
178
179 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
180
181 g_signal_connect (m_widget, "switch_page",
182 G_CALLBACK(switch_page), this);
183
184 g_signal_connect_after (m_widget, "switch_page",
185 G_CALLBACK(switch_page_after), this);
186 g_signal_handlers_block_by_func(m_widget, (void*)switch_page_after, this);
187
188 g_signal_connect(m_widget, "event_after", G_CALLBACK(event_after), this);
189 g_signal_handlers_block_by_func(m_widget, (void*)event_after, this);
190
191 m_parent->DoAddChild( this );
192
193 if (m_windowStyle & wxBK_RIGHT)
194 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_RIGHT );
195 if (m_windowStyle & wxBK_LEFT)
196 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_LEFT );
197 if (m_windowStyle & wxBK_BOTTOM)
198 gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_BOTTOM );
199
200 PostCreation(size);
201
202 return true;
203 }
204
205 int wxNotebook::GetSelection() const
206 {
207 wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
208
209 return gtk_notebook_get_current_page( GTK_NOTEBOOK(m_widget) );
210 }
211
212 wxString wxNotebook::GetPageText( size_t page ) const
213 {
214 wxCHECK_MSG(page < GetPageCount(), wxEmptyString, "invalid notebook index");
215
216 GtkLabel* label = GTK_LABEL(GetNotebookPage(page)->m_label);
217 return wxGTK_CONV_BACK(gtk_label_get_text(label));
218 }
219
220 int wxNotebook::GetPageImage( size_t page ) const
221 {
222 wxCHECK_MSG(page < GetPageCount(), -1, "invalid notebook index");
223
224 return GetNotebookPage(page)->m_imageIndex;
225 }
226
227 wxGtkNotebookPage* wxNotebook::GetNotebookPage( int page ) const
228 {
229 return m_pagesData.Item(page)->GetData();
230 }
231
232 int wxNotebook::DoSetSelection( size_t page, int flags )
233 {
234 wxCHECK_MSG(page < GetPageCount(), -1, "invalid notebook index");
235
236 int selOld = GetSelection();
237
238 if ( !(flags & SetSelection_SendEvent) )
239 {
240 g_signal_handlers_block_by_func(m_widget, (void*)switch_page, this);
241 }
242
243 gtk_notebook_set_current_page( GTK_NOTEBOOK(m_widget), page );
244
245 if ( !(flags & SetSelection_SendEvent) )
246 {
247 g_signal_handlers_unblock_by_func(m_widget, (void*)switch_page, this);
248 }
249
250 wxNotebookPage *client = GetPage(page);
251 if ( client )
252 client->SetFocus();
253
254 return selOld;
255 }
256
257 bool wxNotebook::SetPageText( size_t page, const wxString &text )
258 {
259 wxCHECK_MSG(page < GetPageCount(), false, "invalid notebook index");
260
261 GtkLabel* label = GTK_LABEL(GetNotebookPage(page)->m_label);
262 gtk_label_set_text(label, wxGTK_CONV(text));
263
264 return true;
265 }
266
267 bool wxNotebook::SetPageImage( size_t page, int image )
268 {
269 wxCHECK_MSG(page < GetPageCount(), false, "invalid notebook index");
270
271 wxGtkNotebookPage* pageData = GetNotebookPage(page);
272 if (image >= 0)
273 {
274 wxCHECK_MSG(m_imageList, false, "invalid notebook imagelist");
275 const wxBitmap* bitmap = m_imageList->GetBitmapPtr(image);
276 if (bitmap == NULL)
277 return false;
278 if (pageData->m_image)
279 {
280 gtk_image_set_from_pixbuf(
281 GTK_IMAGE(pageData->m_image), bitmap->GetPixbuf());
282 }
283 else
284 {
285 pageData->m_image = gtk_image_new_from_pixbuf(bitmap->GetPixbuf());
286 gtk_widget_show(pageData->m_image);
287 gtk_box_pack_start(GTK_BOX(pageData->m_box),
288 pageData->m_image, false, false, m_padding);
289 }
290 }
291 else if (pageData->m_image)
292 {
293 gtk_widget_destroy(pageData->m_image);
294 pageData->m_image = NULL;
295 }
296 pageData->m_imageIndex = image;
297
298 return true;
299 }
300
301 void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
302 {
303 wxFAIL_MSG( wxT("wxNotebook::SetPageSize not implemented") );
304 }
305
306 void wxNotebook::SetPadding( const wxSize &padding )
307 {
308 wxCHECK_RET( m_widget != NULL, wxT("invalid notebook") );
309
310 m_padding = padding.GetWidth();
311
312 for (size_t i = GetPageCount(); i--;)
313 {
314 wxGtkNotebookPage* pageData = GetNotebookPage(i);
315 if (pageData->m_image)
316 {
317 gtk_box_set_child_packing(GTK_BOX(pageData->m_box),
318 pageData->m_image, false, false, m_padding, GTK_PACK_START);
319 }
320 gtk_box_set_child_packing(GTK_BOX(pageData->m_box),
321 pageData->m_label, false, false, m_padding, GTK_PACK_END);
322 }
323 }
324
325 void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
326 {
327 wxFAIL_MSG( wxT("wxNotebook::SetTabSize not implemented") );
328 }
329
330 bool wxNotebook::DeleteAllPages()
331 {
332 for (size_t i = GetPageCount(); i--;)
333 DeletePage(i);
334
335 return wxNotebookBase::DeleteAllPages();
336 }
337
338 wxNotebookPage *wxNotebook::DoRemovePage( size_t page )
339 {
340 // We cannot remove the page yet, as GTK sends the "switch_page"
341 // signal before it has removed the notebook-page from its
342 // corresponding list. Thus, if we were to remove the page from
343 // m_pages at this point, the two lists of pages would be out
344 // of sync during the PAGE_CHANGING/PAGE_CHANGED events.
345 wxNotebookPage *client = GetPage(page);
346 if ( !client )
347 return NULL;
348
349 gtk_widget_unrealize( client->m_widget );
350
351 // we don't need to unparent the client->m_widget; GTK+ will do
352 // that for us (and will throw a warning if we do it!)
353 gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page );
354
355 // It's safe to remove the page now.
356 wxASSERT_MSG(GetPage(page) == client, wxT("pages changed during delete"));
357 wxNotebookBase::DoRemovePage(page);
358
359 wxGtkNotebookPage* p = GetNotebookPage(page);
360 m_pagesData.DeleteObject(p);
361 delete p;
362
363 return client;
364 }
365
366 bool wxNotebook::InsertPage( size_t position,
367 wxNotebookPage* win,
368 const wxString& text,
369 bool select,
370 int imageId )
371 {
372 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid notebook") );
373
374 wxCHECK_MSG( win->GetParent() == this, false,
375 wxT("Can't add a page whose parent is not the notebook!") );
376
377 wxCHECK_MSG( position <= GetPageCount(), false,
378 _T("invalid page index in wxNotebookPage::InsertPage()") );
379
380 // Hack Alert! (Part II): See above in wxInsertChildInNotebook callback
381 // why this has to be done.
382 gtk_widget_unparent(win->m_widget);
383
384 if (m_themeEnabled)
385 win->SetThemeEnabled(true);
386
387 GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
388
389 wxGtkNotebookPage* pageData = new wxGtkNotebookPage;
390
391 m_pages.Insert(win, position);
392 m_pagesData.Insert(position, pageData);
393
394 // set the label image and text
395 // this must be done before adding the page, as GetPageText
396 // and GetPageImage will otherwise return wrong values in
397 // the page-changed event that results from inserting the
398 // first page.
399 pageData->m_imageIndex = imageId;
400
401 pageData->m_box = gtk_hbox_new(false, 1);
402 gtk_container_set_border_width(GTK_CONTAINER(pageData->m_box), 2);
403
404 pageData->m_image = NULL;
405 if (imageId != -1)
406 {
407 if (m_imageList)
408 {
409 const wxBitmap* bitmap = m_imageList->GetBitmapPtr(imageId);
410 pageData->m_image = gtk_image_new_from_pixbuf(bitmap->GetPixbuf());
411 gtk_box_pack_start(GTK_BOX(pageData->m_box),
412 pageData->m_image, false, false, m_padding);
413 }
414 else
415 wxFAIL_MSG("invalid notebook imagelist");
416 }
417
418 /* set the label text */
419 pageData->m_label = gtk_label_new(wxGTK_CONV(wxStripMenuCodes(text)));
420 gtk_box_pack_end(GTK_BOX(pageData->m_box),
421 pageData->m_label, false, false, m_padding);
422
423 gtk_widget_show_all(pageData->m_box);
424 gtk_notebook_insert_page(notebook, win->m_widget, pageData->m_box, position);
425
426 /* apply current style */
427 GtkRcStyle *style = CreateWidgetStyle();
428 if ( style )
429 {
430 gtk_widget_modify_style(pageData->m_label, style);
431 gtk_rc_style_unref(style);
432 }
433
434 if (select && GetPageCount() > 1)
435 {
436 SetSelection( position );
437 }
438
439 InvalidateBestSize();
440 return true;
441 }
442
443 // helper for HitTest(): check if the point lies inside the given widget which
444 // is the child of the notebook whose position and border size are passed as
445 // parameters
446 static bool
447 IsPointInsideWidget(const wxPoint& pt, GtkWidget *w,
448 gint x, gint y, gint border = 0)
449 {
450 return
451 (pt.x >= w->allocation.x - x - border) &&
452 (pt.x <= w->allocation.x - x + border + w->allocation.width) &&
453 (pt.y >= w->allocation.y - y - border) &&
454 (pt.y <= w->allocation.y - y + border + w->allocation.height);
455 }
456
457 int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
458 {
459 const gint x = m_widget->allocation.x;
460 const gint y = m_widget->allocation.y;
461
462 const size_t count = GetPageCount();
463 size_t i = 0;
464
465 GtkNotebook * notebook = GTK_NOTEBOOK(m_widget);
466 if (gtk_notebook_get_scrollable(notebook))
467 i = g_list_position( notebook->children, notebook->first_tab );
468
469 for ( ; i < count; i++ )
470 {
471 wxGtkNotebookPage* pageData = GetNotebookPage(i);
472 GtkWidget* box = pageData->m_box;
473
474 const gint border = gtk_container_get_border_width(GTK_CONTAINER(box));
475
476 if ( IsPointInsideWidget(pt, box, x, y, border) )
477 {
478 // ok, we're inside this tab -- now find out where, if needed
479 if ( flags )
480 {
481 if (pageData->m_image && IsPointInsideWidget(pt, pageData->m_image, x, y))
482 {
483 *flags = wxBK_HITTEST_ONICON;
484 }
485 else if (IsPointInsideWidget(pt, pageData->m_label, x, y))
486 {
487 *flags = wxBK_HITTEST_ONLABEL;
488 }
489 else
490 {
491 *flags = wxBK_HITTEST_ONITEM;
492 }
493 }
494
495 return i;
496 }
497 }
498
499 if ( flags )
500 {
501 *flags = wxBK_HITTEST_NOWHERE;
502 wxWindowBase * page = GetCurrentPage();
503 if ( page )
504 {
505 // rect origin is in notebook's parent coordinates
506 wxRect rect = page->GetRect();
507
508 // adjust it to the notebook's coordinates
509 wxPoint pos = GetPosition();
510 rect.x -= pos.x;
511 rect.y -= pos.y;
512 if ( rect.Contains( pt ) )
513 *flags |= wxBK_HITTEST_ONPAGE;
514 }
515 }
516
517 return wxNOT_FOUND;
518 }
519
520 void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
521 {
522 if (event.IsWindowChange())
523 AdvanceSelection( event.GetDirection() );
524 else
525 event.Skip();
526 }
527
528 #if wxUSE_CONSTRAINTS
529
530 // override these 2 functions to do nothing: everything is done in OnSize
531 void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
532 {
533 // don't set the sizes of the pages - their correct size is not yet known
534 wxControl::SetConstraintSizes(false);
535 }
536
537 bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
538 {
539 return true;
540 }
541
542 #endif
543
544 void wxNotebook::DoApplyWidgetStyle(GtkRcStyle *style)
545 {
546 gtk_widget_modify_style(m_widget, style);
547 for (size_t i = GetPageCount(); i--;)
548 gtk_widget_modify_style(GetNotebookPage(i)->m_label, style);
549 }
550
551 GdkWindow *wxNotebook::GTKGetWindow(wxArrayGdkWindows& windows) const
552 {
553 windows.push_back(m_widget->window);
554 windows.push_back(GTK_NOTEBOOK(m_widget)->event_window);
555
556 return NULL;
557 }
558
559 // static
560 wxVisualAttributes
561 wxNotebook::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
562 {
563 return GetDefaultAttributesFromGTKWidget(gtk_notebook_new);
564 }
565
566 #endif