GTK+ callbacks must have C linkage (patch 1157384)
[wxWidgets.git] / src / gtk1 / radiobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: radiobox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "radiobox.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #if wxUSE_RADIOBOX
18
19 #include "wx/radiobox.h"
20
21 #include "wx/dialog.h"
22 #include "wx/frame.h"
23 #include "wx/log.h"
24
25 #include "wx/gtk/private.h"
26 #include <gdk/gdkkeysyms.h>
27
28 #include "wx/gtk/win_gtk.h"
29
30 //-----------------------------------------------------------------------------
31 // idle system
32 //-----------------------------------------------------------------------------
33
34 extern void wxapp_install_idle_handler();
35 extern bool g_isIdle;
36
37 //-----------------------------------------------------------------------------
38 // data
39 //-----------------------------------------------------------------------------
40
41 extern bool g_blockEventsOnDrag;
42 extern wxWindowGTK *g_delayedFocus;
43
44 //-----------------------------------------------------------------------------
45 // "clicked"
46 //-----------------------------------------------------------------------------
47
48 extern "C" {
49 static void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioBox *rb )
50 {
51 if (g_isIdle) wxapp_install_idle_handler();
52
53 if (!rb->m_hasVMT) return;
54 if (g_blockEventsOnDrag) return;
55
56 if (!button->active) return;
57
58 wxCommandEvent event( wxEVT_COMMAND_RADIOBOX_SELECTED, rb->GetId() );
59 event.SetInt( rb->GetSelection() );
60 event.SetString( rb->GetStringSelection() );
61 event.SetEventObject( rb );
62 rb->GetEventHandler()->ProcessEvent(event);
63 }
64 }
65
66 //-----------------------------------------------------------------------------
67 // "key_press_event"
68 //-----------------------------------------------------------------------------
69
70 extern "C" {
71 static gint gtk_radiobox_keypress_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxRadioBox *rb )
72 {
73 if (g_isIdle)
74 wxapp_install_idle_handler();
75
76 if (!rb->m_hasVMT) return FALSE;
77 if (g_blockEventsOnDrag) return FALSE;
78
79 if ((gdk_event->keyval != GDK_Up) &&
80 (gdk_event->keyval != GDK_Down) &&
81 (gdk_event->keyval != GDK_Left) &&
82 (gdk_event->keyval != GDK_Right))
83 {
84 return FALSE;
85 }
86
87 wxList::compatibility_iterator node = rb->m_boxes.Find( (wxObject*) widget );
88 if (!node)
89 {
90 return FALSE;
91 }
92
93 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
94
95 if ((gdk_event->keyval == GDK_Up) ||
96 (gdk_event->keyval == GDK_Left))
97 {
98 if (node == rb->m_boxes.GetFirst())
99 node = rb->m_boxes.GetLast();
100 else
101 node = node->GetPrevious();
102 }
103 else
104 {
105 if (node == rb->m_boxes.GetLast())
106 node = rb->m_boxes.GetFirst();
107 else
108 node = node->GetNext();
109 }
110
111 GtkWidget *button = (GtkWidget*) node->GetData();
112
113 gtk_widget_grab_focus( button );
114
115 return TRUE;
116 }
117 }
118
119 extern "C" {
120 static gint gtk_radiobutton_focus_in( GtkWidget *widget,
121 GdkEvent *WXUNUSED(event),
122 wxRadioBox *win )
123 {
124 if ( win->m_lostFocus )
125 {
126 // no, we didn't really lose it
127 win->m_lostFocus = FALSE;
128 }
129 else if ( !win->m_hasFocus )
130 {
131 win->m_hasFocus = true;
132
133 wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() );
134 event.SetEventObject( win );
135
136 // never stop the signal emission, it seems to break the kbd handling
137 // inside the radiobox
138 (void)win->GetEventHandler()->ProcessEvent( event );
139 }
140
141 return FALSE;
142 }
143 }
144
145 extern "C" {
146 static gint gtk_radiobutton_focus_out( GtkWidget *widget,
147 GdkEvent *WXUNUSED(event),
148 wxRadioBox *win )
149 {
150 // wxASSERT_MSG( win->m_hasFocus, _T("got focus out without any focus in?") );
151 // Replace with a warning, else we dump core a lot!
152 // if (!win->m_hasFocus)
153 // wxLogWarning(_T("Radiobox got focus out without any focus in.") );
154
155 // we might have lost the focus, but may be not - it may have just gone to
156 // another button in the same radiobox, so we'll check for it in the next
157 // idle iteration (leave m_hasFocus == true for now)
158 win->m_lostFocus = true;
159
160 return FALSE;
161 }
162 }
163
164 //-----------------------------------------------------------------------------
165 // wxRadioBox
166 //-----------------------------------------------------------------------------
167
168 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox,wxControl)
169
170 void wxRadioBox::Init()
171 {
172 m_needParent = true;
173 m_acceptsFocus = true;
174
175 m_hasFocus =
176 m_lostFocus = false;
177 }
178
179 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id,
180 const wxString& title,
181 const wxPoint &pos, const wxSize &size,
182 const wxArrayString& choices, int majorDim,
183 long style, const wxValidator& validator,
184 const wxString &name )
185 {
186 wxCArrayString chs(choices);
187
188 return Create( parent, id, title, pos, size, chs.GetCount(),
189 chs.GetStrings(), majorDim, style, validator, name );
190 }
191
192 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title,
193 const wxPoint &pos, const wxSize &size,
194 int n, const wxString choices[], int majorDim,
195 long style, const wxValidator& validator,
196 const wxString &name )
197 {
198 if (!PreCreation( parent, pos, size ) ||
199 !CreateBase( parent, id, pos, size, style, validator, name ))
200 {
201 wxFAIL_MSG( wxT("wxRadioBox creation failed") );
202 return false;
203 }
204
205 m_widget = gtk_frame_new( wxGTK_CONV( title ) );
206
207 // majorDim may be 0 if all trailing parameters were omitted, so don't
208 // assert here but just use the correct value for it
209 m_majorDim = majorDim == 0 ? n : majorDim;
210
211 int num_per_major = (n - 1) / m_majorDim +1;
212
213 int num_of_cols = 0;
214 int num_of_rows = 0;
215 if (HasFlag(wxRA_SPECIFY_COLS))
216 {
217 num_of_cols = m_majorDim;
218 num_of_rows = num_per_major;
219 }
220 else
221 {
222 num_of_cols = num_per_major;
223 num_of_rows = m_majorDim;
224 }
225
226 GtkRadioButton *m_radio = (GtkRadioButton*) NULL;
227
228 GtkWidget *table = gtk_table_new( num_of_rows, num_of_cols, FALSE );
229 gtk_table_set_col_spacings( GTK_TABLE(table), 1 );
230 gtk_table_set_row_spacings( GTK_TABLE(table), 1 );
231 gtk_widget_show( table );
232 gtk_container_add( GTK_CONTAINER(m_widget), table );
233
234 wxString label;
235 GSList *radio_button_group = (GSList *) NULL;
236 for (int i = 0; i < n; i++)
237 {
238 if ( i != 0 )
239 radio_button_group = gtk_radio_button_group( GTK_RADIO_BUTTON(m_radio) );
240
241 label.Empty();
242 for ( const wxChar *pc = choices[i]; *pc; pc++ )
243 {
244 if ( *pc != wxT('&') )
245 label += *pc;
246 }
247
248 m_radio = GTK_RADIO_BUTTON( gtk_radio_button_new_with_label( radio_button_group, wxGTK_CONV( label ) ) );
249 gtk_widget_show( GTK_WIDGET(m_radio) );
250
251 gtk_signal_connect( GTK_OBJECT(m_radio), "key_press_event",
252 GTK_SIGNAL_FUNC(gtk_radiobox_keypress_callback), (gpointer)this );
253
254 m_boxes.Append( (wxObject*) m_radio );
255
256 if (HasFlag(wxRA_SPECIFY_COLS))
257 {
258 int left = i%num_of_cols;
259 int right = (i%num_of_cols) + 1;
260 int top = i/num_of_cols;
261 int bottom = (i/num_of_cols)+1;
262 gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(m_radio), left, right, top, bottom,
263 GTK_FILL, GTK_FILL, 1, 1 );
264 }
265 else
266 {
267 int left = i/num_of_rows;
268 int right = (i/num_of_rows) + 1;
269 int top = i%num_of_rows;
270 int bottom = (i%num_of_rows)+1;
271 gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(m_radio), left, right, top, bottom,
272 GTK_FILL, GTK_FILL, 1, 1 );
273 }
274
275 ConnectWidget( GTK_WIDGET(m_radio) );
276
277 if (!i) gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(m_radio), TRUE );
278
279 gtk_signal_connect( GTK_OBJECT(m_radio), "clicked",
280 GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
281
282 gtk_signal_connect( GTK_OBJECT(m_radio), "focus_in_event",
283 GTK_SIGNAL_FUNC(gtk_radiobutton_focus_in), (gpointer)this );
284
285 gtk_signal_connect( GTK_OBJECT(m_radio), "focus_out_event",
286 GTK_SIGNAL_FUNC(gtk_radiobutton_focus_out), (gpointer)this );
287 }
288
289 m_parent->DoAddChild( this );
290
291 SetLabel( title );
292
293 PostCreation(size);
294
295 return true;
296 }
297
298 wxRadioBox::~wxRadioBox()
299 {
300 wxList::compatibility_iterator node = m_boxes.GetFirst();
301 while (node)
302 {
303 GtkWidget *button = GTK_WIDGET( node->GetData() );
304 gtk_widget_destroy( button );
305 node = node->GetNext();
306 }
307 }
308
309 bool wxRadioBox::Show( bool show )
310 {
311 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
312
313 if (!wxControl::Show(show))
314 {
315 // nothing to do
316 return false;
317 }
318
319 if ( HasFlag(wxNO_BORDER) )
320 gtk_widget_hide( m_widget );
321
322 wxList::compatibility_iterator node = m_boxes.GetFirst();
323 while (node)
324 {
325 GtkWidget *button = GTK_WIDGET( node->GetData() );
326
327 if (show) gtk_widget_show( button ); else gtk_widget_hide( button );
328
329 node = node->GetNext();
330 }
331
332 return true;
333 }
334
335 int wxRadioBox::FindString( const wxString &find ) const
336 {
337 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid radiobox") );
338
339 int count = 0;
340
341 wxList::compatibility_iterator node = m_boxes.GetFirst();
342 while (node)
343 {
344 GtkLabel *label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
345 #ifdef __WXGTK20__
346 wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
347 #else
348 wxString str( label->label );
349 #endif
350 if (find == str)
351 return count;
352
353 count++;
354
355 node = node->GetNext();
356 }
357
358 return wxNOT_FOUND;
359 }
360
361 void wxRadioBox::SetFocus()
362 {
363 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
364
365 if (m_boxes.GetCount() == 0) return;
366
367 wxList::compatibility_iterator node = m_boxes.GetFirst();
368 while (node)
369 {
370 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
371 if (button->active)
372 {
373 gtk_widget_grab_focus( GTK_WIDGET(button) );
374 return;
375 }
376 node = node->GetNext();
377 }
378 }
379
380 void wxRadioBox::SetSelection( int n )
381 {
382 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
383
384 wxList::compatibility_iterator node = m_boxes.Item( n );
385
386 wxCHECK_RET( node, wxT("radiobox wrong index") );
387
388 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
389
390 GtkDisableEvents();
391
392 gtk_toggle_button_set_active( button, 1 );
393
394 GtkEnableEvents();
395 }
396
397 int wxRadioBox::GetSelection(void) const
398 {
399 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid radiobox") );
400
401 int count = 0;
402
403 wxList::compatibility_iterator node = m_boxes.GetFirst();
404 while (node)
405 {
406 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
407 if (button->active) return count;
408 count++;
409 node = node->GetNext();
410 }
411
412 wxFAIL_MSG( wxT("wxRadioBox none selected") );
413
414 return wxNOT_FOUND;
415 }
416
417 wxString wxRadioBox::GetString( int n ) const
418 {
419 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid radiobox") );
420
421 wxList::compatibility_iterator node = m_boxes.Item( n );
422
423 wxCHECK_MSG( node, wxEmptyString, wxT("radiobox wrong index") );
424
425 GtkLabel *label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
426
427 #ifdef __WXGTK20__
428 wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
429 #else
430 wxString str( label->label );
431 #endif
432
433 return str;
434 }
435
436 void wxRadioBox::SetLabel( const wxString& label )
437 {
438 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
439
440 wxControl::SetLabel( label );
441
442 gtk_frame_set_label( GTK_FRAME(m_widget), wxGTK_CONV( wxControl::GetLabel() ) );
443 }
444
445 void wxRadioBox::SetString( int item, const wxString& label )
446 {
447 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
448
449 wxList::compatibility_iterator node = m_boxes.Item( item );
450
451 wxCHECK_RET( node, wxT("radiobox wrong index") );
452
453 GtkLabel *g_label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
454
455 gtk_label_set( g_label, wxGTK_CONV( label ) );
456 }
457
458 bool wxRadioBox::Enable( bool enable )
459 {
460 if ( !wxControl::Enable( enable ) )
461 return false;
462
463 wxList::compatibility_iterator node = m_boxes.GetFirst();
464 while (node)
465 {
466 GtkButton *button = GTK_BUTTON( node->GetData() );
467 GtkLabel *label = GTK_LABEL( BUTTON_CHILD(button) );
468
469 gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
470 gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
471 node = node->GetNext();
472 }
473
474 return true;
475 }
476
477 bool wxRadioBox::Enable( int item, bool enable )
478 {
479 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
480
481 wxList::compatibility_iterator node = m_boxes.Item( item );
482
483 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
484
485 GtkButton *button = GTK_BUTTON( node->GetData() );
486 GtkLabel *label = GTK_LABEL( BUTTON_CHILD(button) );
487
488 gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
489 gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
490
491 return true;
492 }
493
494 bool wxRadioBox::Show( int item, bool show )
495 {
496 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
497
498 wxList::compatibility_iterator node = m_boxes.Item( item );
499
500 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
501
502 GtkWidget *button = GTK_WIDGET( node->GetData() );
503
504 if (show)
505 gtk_widget_show( button );
506 else
507 gtk_widget_hide( button );
508
509 return true;
510 }
511
512 wxString wxRadioBox::GetStringSelection() const
513 {
514 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid radiobox") );
515
516 wxList::compatibility_iterator node = m_boxes.GetFirst();
517 while (node)
518 {
519 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
520 if (button->active)
521 {
522 GtkLabel *label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
523
524 #ifdef __WXGTK20__
525 wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
526 #else
527 wxString str( label->label );
528 #endif
529 return str;
530 }
531 node = node->GetNext();
532 }
533
534 wxFAIL_MSG( wxT("wxRadioBox none selected") );
535 return wxEmptyString;
536 }
537
538 bool wxRadioBox::SetStringSelection( const wxString &s )
539 {
540 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
541
542 int res = FindString( s );
543 if (res == wxNOT_FOUND) return false;
544 SetSelection( res );
545
546 return true;
547 }
548
549 int wxRadioBox::GetCount() const
550 {
551 return m_boxes.GetCount();
552 }
553
554 void wxRadioBox::GtkDisableEvents()
555 {
556 wxList::compatibility_iterator node = m_boxes.GetFirst();
557 while (node)
558 {
559 gtk_signal_disconnect_by_func( GTK_OBJECT(node->GetData()),
560 GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
561
562 node = node->GetNext();
563 }
564 }
565
566 void wxRadioBox::GtkEnableEvents()
567 {
568 wxList::compatibility_iterator node = m_boxes.GetFirst();
569 while (node)
570 {
571 gtk_signal_connect( GTK_OBJECT(node->GetData()), "clicked",
572 GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
573
574 node = node->GetNext();
575 }
576 }
577
578 void wxRadioBox::DoApplyWidgetStyle(GtkRcStyle *style)
579 {
580 gtk_widget_modify_style( m_widget, style );
581
582 #ifdef __WXGTK20__
583 gtk_widget_modify_style(GTK_FRAME(m_widget)->label_widget, style);
584 #endif
585
586 wxList::compatibility_iterator node = m_boxes.GetFirst();
587 while (node)
588 {
589 GtkWidget *widget = GTK_WIDGET( node->GetData() );
590
591 gtk_widget_modify_style( widget, style );
592 gtk_widget_modify_style( BUTTON_CHILD(node->GetData()), style );
593
594 node = node->GetNext();
595 }
596 }
597
598 #if wxUSE_TOOLTIPS
599 void wxRadioBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
600 {
601 wxList::compatibility_iterator node = m_boxes.GetFirst();
602 while (node)
603 {
604 GtkWidget *widget = GTK_WIDGET( node->GetData() );
605 gtk_tooltips_set_tip( tips, widget, wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
606 node = node->GetNext();
607 }
608 }
609 #endif // wxUSE_TOOLTIPS
610
611 bool wxRadioBox::IsOwnGtkWindow( GdkWindow *window )
612 {
613 if (window == m_widget->window) return true;
614
615 wxList::compatibility_iterator node = m_boxes.GetFirst();
616 while (node)
617 {
618 GtkWidget *button = GTK_WIDGET( node->GetData() );
619
620 if (window == button->window) return true;
621
622 node = node->GetNext();
623 }
624
625 return false;
626 }
627
628 void wxRadioBox::OnInternalIdle()
629 {
630 if ( m_lostFocus )
631 {
632 m_hasFocus = false;
633 m_lostFocus = false;
634
635 wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
636 event.SetEventObject( this );
637
638 (void)GetEventHandler()->ProcessEvent( event );
639 }
640
641 if (g_delayedFocus == this)
642 {
643 if (GTK_WIDGET_REALIZED(m_widget))
644 {
645 g_delayedFocus = NULL;
646 SetFocus();
647 }
648 }
649 }
650
651 // static
652 wxVisualAttributes
653 wxRadioBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
654 {
655 wxVisualAttributes attr;
656 // NB: we need toplevel window so that GTK+ can find the right style
657 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
658 GtkWidget* widget = gtk_radio_button_new_with_label(NULL, "");
659 gtk_container_add(GTK_CONTAINER(wnd), widget);
660 attr = GetDefaultAttributesFromGTKWidget(widget);
661 gtk_widget_destroy(wnd);
662 return attr;
663 }
664
665 #if WXWIN_COMPATIBILITY_2_2
666
667 int wxRadioBox::Number() const
668 {
669 return GetCount();
670 }
671
672 wxString wxRadioBox::GetLabel(int n) const
673 {
674 return GetString(n);
675 }
676
677 void wxRadioBox::SetLabel( int item, const wxString& label )
678 {
679 SetString(item, label);
680 }
681
682 #endif // WXWIN_COMPATIBILITY_2_2
683
684 #endif // wxUSE_RADIOBOX
685