1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/checkbox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_CHECKBOX
13
14 #include "wx/checkbox.h"
15
16 #include "wx/gtk/private/wrapgtk.h"
17 #include "wx/gtk/private/eventsdisabler.h"
18
19 //-----------------------------------------------------------------------------
20 // data
21 //-----------------------------------------------------------------------------
22
23 extern bool g_blockEventsOnDrag;
24
25 //-----------------------------------------------------------------------------
26 // "clicked"
27 //-----------------------------------------------------------------------------
28
29 extern "C" {
gtk_checkbox_toggled_callback(GtkWidget * widget,wxCheckBox * cb)30 static void gtk_checkbox_toggled_callback(GtkWidget *widget, wxCheckBox *cb)
31 {
32 if (g_blockEventsOnDrag) return;
33
34 // Transitions for 3state checkbox must be done manually, GTK's checkbox
35 // is 2state with additional "undetermined state" flag which isn't
36 // changed automatically:
37 if (cb->Is3State())
38 {
39 GtkToggleButton *toggle = GTK_TOGGLE_BUTTON(widget);
40
41 if (cb->Is3rdStateAllowedForUser())
42 {
43 // The 3 states cycle like this when clicked:
44 // checked -> undetermined -> unchecked -> checked -> ...
45 bool active = gtk_toggle_button_get_active(toggle) != 0;
46 bool inconsistent = gtk_toggle_button_get_inconsistent(toggle) != 0;
47
48 wxGtkEventsDisabler<wxCheckBox> noEvents(cb);
49
50 if (!active && !inconsistent)
51 {
52 // checked -> undetermined
53 gtk_toggle_button_set_active(toggle, true);
54 gtk_toggle_button_set_inconsistent(toggle, true);
55 }
56 else if (!active && inconsistent)
57 {
58 // undetermined -> unchecked
59 gtk_toggle_button_set_inconsistent(toggle, false);
60 }
61 else if (active && !inconsistent)
62 {
63 // unchecked -> checked
64 // nothing to do
65 }
66 else
67 {
68 wxFAIL_MSG(wxT("3state wxCheckBox in unexpected state!"));
69 }
70 }
71 else
72 {
73 // user's action unsets undetermined state:
74 gtk_toggle_button_set_inconsistent(toggle, false);
75 }
76 }
77
78 wxCommandEvent event(wxEVT_CHECKBOX, cb->GetId());
79 event.SetInt(cb->Get3StateValue());
80 event.SetEventObject(cb);
81 cb->HandleWindowEvent(event);
82 }
83 }
84
85 //-----------------------------------------------------------------------------
86 // wxCheckBox
87 //-----------------------------------------------------------------------------
88
wxCheckBox()89 wxCheckBox::wxCheckBox()
90 {
91 m_widgetCheckbox = NULL;
92 }
93
~wxCheckBox()94 wxCheckBox::~wxCheckBox()
95 {
96 if (m_widgetCheckbox && m_widgetCheckbox != m_widget)
97 GTKDisconnect(m_widgetCheckbox);
98 }
99
Create(wxWindow * parent,wxWindowID id,const wxString & label,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)100 bool wxCheckBox::Create(wxWindow *parent,
101 wxWindowID id,
102 const wxString &label,
103 const wxPoint &pos,
104 const wxSize &size,
105 long style,
106 const wxValidator& validator,
107 const wxString &name )
108 {
109 WXValidateStyle( &style );
110 if (!PreCreation( parent, pos, size ) ||
111 !CreateBase( parent, id, pos, size, style, validator, name ))
112 {
113 wxFAIL_MSG( wxT("wxCheckBox creation failed") );
114 return false;
115 }
116
117 if ( style & wxALIGN_RIGHT )
118 {
119 // VZ: as I don't know a way to create a right aligned checkbox with
120 // GTK we will create a checkbox without label and a label at the
121 // left of it
122 m_widgetCheckbox = gtk_check_button_new();
123
124 m_widgetLabel = gtk_label_new("");
125 #ifdef __WXGTK4__
126 g_object_set(m_widgetLabel, "xalign", 0.0f, NULL);
127 #else
128 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
129 gtk_misc_set_alignment(GTK_MISC(m_widgetLabel), 0.0, 0.5);
130 wxGCC_WARNING_RESTORE()
131 #endif
132
133 m_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
134 gtk_box_pack_start(GTK_BOX(m_widget), m_widgetLabel, FALSE, FALSE, 3);
135 gtk_box_pack_start(GTK_BOX(m_widget), m_widgetCheckbox, FALSE, FALSE, 3);
136
137 gtk_widget_show( m_widgetLabel );
138 gtk_widget_show( m_widgetCheckbox );
139 }
140 else
141 {
142 m_widgetCheckbox = gtk_check_button_new_with_label("");
143 m_widgetLabel = gtk_bin_get_child(GTK_BIN(m_widgetCheckbox));
144 m_widget = m_widgetCheckbox;
145 }
146 g_object_ref(m_widget);
147 SetLabel( label );
148
149 if ( style & wxNO_BORDER )
150 {
151 gtk_container_set_border_width(GTK_CONTAINER(m_widgetCheckbox), 0);
152 }
153
154 g_signal_connect (m_widgetCheckbox, "toggled",
155 G_CALLBACK (gtk_checkbox_toggled_callback), this);
156
157 m_parent->DoAddChild( this );
158
159 #ifdef __WXGTK3__
160 // CSS added if the window has wxNO_BORDER inside base class PostCreation()
161 // makes checkbox look broken in the default GTK 3 theme, so avoid doing
162 // this by temporarily turning this flag off.
163 if ( style & wxNO_BORDER )
164 ToggleWindowStyle(wxNO_BORDER);
165 #endif
166
167 PostCreation(size);
168
169 #ifdef __WXGTK3__
170 // Turn it back on if necessary.
171 if ( style & wxNO_BORDER )
172 ToggleWindowStyle(wxNO_BORDER);
173 #endif
174
175 return true;
176 }
177
GTKDisableEvents()178 void wxCheckBox::GTKDisableEvents()
179 {
180 g_signal_handlers_block_by_func(m_widgetCheckbox,
181 (gpointer) gtk_checkbox_toggled_callback, this);
182 }
183
GTKEnableEvents()184 void wxCheckBox::GTKEnableEvents()
185 {
186 g_signal_handlers_unblock_by_func(m_widgetCheckbox,
187 (gpointer) gtk_checkbox_toggled_callback, this);
188 }
189
SetValue(bool state)190 void wxCheckBox::SetValue( bool state )
191 {
192 wxCHECK_RET( m_widgetCheckbox != NULL, wxT("invalid checkbox") );
193
194 if (state == GetValue())
195 return;
196
197 wxGtkEventsDisabler<wxCheckBox> noEvents(this);
198 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m_widgetCheckbox), state );
199 }
200
GetValue() const201 bool wxCheckBox::GetValue() const
202 {
203 wxCHECK_MSG( m_widgetCheckbox != NULL, false, wxT("invalid checkbox") );
204
205 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_widgetCheckbox)) != 0;
206 }
207
DoSet3StateValue(wxCheckBoxState state)208 void wxCheckBox::DoSet3StateValue(wxCheckBoxState state)
209 {
210 SetValue(state != wxCHK_UNCHECKED);
211 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(m_widgetCheckbox),
212 state == wxCHK_UNDETERMINED);
213 }
214
DoGet3StateValue() const215 wxCheckBoxState wxCheckBox::DoGet3StateValue() const
216 {
217 if (gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(m_widgetCheckbox)))
218 {
219 return wxCHK_UNDETERMINED;
220 }
221 else
222 {
223 return GetValue() ? wxCHK_CHECKED : wxCHK_UNCHECKED;
224 }
225 }
226
SetLabel(const wxString & label)227 void wxCheckBox::SetLabel( const wxString& label )
228 {
229 wxCHECK_RET( m_widgetLabel != NULL, wxT("invalid checkbox") );
230
231 // If we don't hide the empty label, in some themes a focus rectangle is
232 // still drawn around it and this looks out of place.
233 if ( label.empty() )
234 gtk_widget_hide(m_widgetLabel);
235 else
236 gtk_widget_show(m_widgetLabel);
237
238 // save the label inside m_label in case user calls GetLabel() later
239 wxControl::SetLabel(label);
240
241 GTKSetLabelForLabel(GTK_LABEL(m_widgetLabel), label);
242 }
243
DoEnable(bool enable)244 void wxCheckBox::DoEnable(bool enable)
245 {
246 if ( !m_widgetLabel )
247 return;
248
249 base_type::DoEnable(enable);
250
251 gtk_widget_set_sensitive( m_widgetLabel, enable );
252
253 if (enable)
254 GTKFixSensitivity();
255 }
256
DoApplyWidgetStyle(GtkRcStyle * style)257 void wxCheckBox::DoApplyWidgetStyle(GtkRcStyle *style)
258 {
259 GTKApplyStyle(m_widgetCheckbox, style);
260 GTKApplyStyle(m_widgetLabel, style);
261 }
262
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const263 GdkWindow *wxCheckBox::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
264 {
265 return gtk_button_get_event_window(GTK_BUTTON(m_widgetCheckbox));
266 }
267
268 // static
269 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))270 wxCheckBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
271 {
272 return GetDefaultAttributesFromGTKWidget(gtk_check_button_new());
273 }
274
275 #endif
276