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