]> Pileus Git - ~andy/gtk/blob - gtk/gtktogglebutton.c
Make PLT-reduction work with gcc4, and don't include everything in
[~andy/gtk] / gtk / gtktogglebutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28 #include "gtklabel.h"
29 #include "gtkmain.h"
30 #include "gtkmarshalers.h"
31 #include "gtktogglebutton.h"
32 #include "gtkintl.h"
33 #include "gtkalias.h"
34
35 #define DEFAULT_LEFT_POS  4
36 #define DEFAULT_TOP_POS   4
37 #define DEFAULT_SPACING   7
38
39 enum {
40   TOGGLED,
41   LAST_SIGNAL
42 };
43
44 enum {
45   PROP_0,
46   PROP_ACTIVE,
47   PROP_INCONSISTENT,
48   PROP_DRAW_INDICATOR
49 };
50
51
52 static void gtk_toggle_button_class_init    (GtkToggleButtonClass *klass);
53 static void gtk_toggle_button_init          (GtkToggleButton      *toggle_button);
54 static gint gtk_toggle_button_expose        (GtkWidget            *widget,
55                                              GdkEventExpose       *event);
56 static gboolean gtk_toggle_button_mnemonic_activate  (GtkWidget            *widget,
57                                                       gboolean              group_cycling);
58 static void gtk_toggle_button_pressed       (GtkButton            *button);
59 static void gtk_toggle_button_released      (GtkButton            *button);
60 static void gtk_toggle_button_clicked       (GtkButton            *button);
61 static void gtk_toggle_button_set_property  (GObject              *object,
62                                              guint                 prop_id,
63                                              const GValue         *value,
64                                              GParamSpec           *pspec);
65 static void gtk_toggle_button_get_property  (GObject              *object,
66                                              guint                 prop_id,
67                                              GValue               *value,
68                                              GParamSpec           *pspec);
69 static void gtk_toggle_button_update_state  (GtkButton            *button);
70
71 static guint toggle_button_signals[LAST_SIGNAL] = { 0 };
72 static GtkContainerClass *parent_class = NULL;
73
74 GType
75 gtk_toggle_button_get_type (void)
76 {
77   static GType toggle_button_type = 0;
78
79   if (!toggle_button_type)
80     {
81       static const GTypeInfo toggle_button_info =
82       {
83         sizeof (GtkToggleButtonClass),
84         NULL,           /* base_init */
85         NULL,           /* base_finalize */
86         (GClassInitFunc) gtk_toggle_button_class_init,
87         NULL,           /* class_finalize */
88         NULL,           /* class_data */
89         sizeof (GtkToggleButton),
90         0,              /* n_preallocs */
91         (GInstanceInitFunc) gtk_toggle_button_init,
92       };
93
94       toggle_button_type =
95         g_type_register_static (GTK_TYPE_BUTTON, "GtkToggleButton",
96                                 &toggle_button_info, 0);
97     }
98
99   return toggle_button_type;
100 }
101
102 static void
103 gtk_toggle_button_class_init (GtkToggleButtonClass *class)
104 {
105   GObjectClass *gobject_class;
106   GtkWidgetClass *widget_class;
107   GtkContainerClass *container_class;
108   GtkButtonClass *button_class;
109
110   gobject_class = G_OBJECT_CLASS (class);
111   widget_class = (GtkWidgetClass*) class;
112   container_class = (GtkContainerClass*) class;
113   button_class = (GtkButtonClass*) class;
114
115   parent_class = g_type_class_peek_parent (class);
116
117   gobject_class->set_property = gtk_toggle_button_set_property;
118   gobject_class->get_property = gtk_toggle_button_get_property;
119
120   widget_class->expose_event = gtk_toggle_button_expose;
121   widget_class->mnemonic_activate = gtk_toggle_button_mnemonic_activate;
122
123   button_class->pressed = gtk_toggle_button_pressed;
124   button_class->released = gtk_toggle_button_released;
125   button_class->clicked = gtk_toggle_button_clicked;
126   button_class->enter = gtk_toggle_button_update_state;
127   button_class->leave = gtk_toggle_button_update_state;
128
129   class->toggled = NULL;
130
131   g_object_class_install_property (gobject_class,
132                                    PROP_ACTIVE,
133                                    g_param_spec_boolean ("active",
134                                                          P_("Active"),
135                                                          P_("If the toggle button should be pressed in or not"),
136                                                          FALSE,
137                                                          G_PARAM_READWRITE));
138
139   g_object_class_install_property (gobject_class,
140                                    PROP_INCONSISTENT,
141                                    g_param_spec_boolean ("inconsistent",
142                                                          P_("Inconsistent"),
143                                                          P_("If the toggle button is in an \"in between\" state"),
144                                                          FALSE,
145                                                          G_PARAM_READWRITE));
146
147   g_object_class_install_property (gobject_class,
148                                    PROP_DRAW_INDICATOR,
149                                    g_param_spec_boolean ("draw-indicator",
150                                                          P_("Draw Indicator"),
151                                                          P_("If the toggle part of the button is displayed"),
152                                                          FALSE,
153                                                          G_PARAM_READWRITE));
154
155   toggle_button_signals[TOGGLED] =
156     g_signal_new ("toggled",
157                   G_OBJECT_CLASS_TYPE (gobject_class),
158                   G_SIGNAL_RUN_FIRST,
159                   G_STRUCT_OFFSET (GtkToggleButtonClass, toggled),
160                   NULL, NULL,
161                   _gtk_marshal_VOID__VOID,
162                   G_TYPE_NONE, 0);
163 }
164
165 static void
166 gtk_toggle_button_init (GtkToggleButton *toggle_button)
167 {
168   toggle_button->active = FALSE;
169   toggle_button->draw_indicator = FALSE;
170   GTK_BUTTON (toggle_button)->depress_on_activate = TRUE;
171 }
172
173
174 GtkWidget*
175 gtk_toggle_button_new (void)
176 {
177   return g_object_new (GTK_TYPE_TOGGLE_BUTTON, NULL);
178 }
179
180 GtkWidget*
181 gtk_toggle_button_new_with_label (const gchar *label)
182 {
183   return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "label", label, NULL);
184 }
185
186 /**
187  * gtk_toggle_button_new_with_mnemonic:
188  * @label: the text of the button, with an underscore in front of the
189  *         mnemonic character
190  * @returns: a new #GtkToggleButton
191  *
192  * Creates a new #GtkToggleButton containing a label. The label
193  * will be created using gtk_label_new_with_mnemonic(), so underscores
194  * in @label indicate the mnemonic for the button.
195  **/
196 GtkWidget*
197 gtk_toggle_button_new_with_mnemonic (const gchar *label)
198 {
199   return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "label", label, "use_underline", TRUE, NULL);
200 }
201
202 static void
203 gtk_toggle_button_set_property (GObject      *object,
204                                 guint         prop_id,
205                                 const GValue *value,
206                                 GParamSpec   *pspec)
207 {
208   GtkToggleButton *tb;
209
210   tb = GTK_TOGGLE_BUTTON (object);
211
212   switch (prop_id)
213     {
214     case PROP_ACTIVE:
215       gtk_toggle_button_set_active (tb, g_value_get_boolean (value));
216       break;
217     case PROP_INCONSISTENT:
218       gtk_toggle_button_set_inconsistent (tb, g_value_get_boolean (value));
219       break;
220     case PROP_DRAW_INDICATOR:
221       gtk_toggle_button_set_mode (tb, g_value_get_boolean (value));
222       break;
223     default:
224       break;
225     }
226 }
227
228 static void
229 gtk_toggle_button_get_property (GObject      *object,
230                                 guint         prop_id,
231                                 GValue       *value,
232                                 GParamSpec   *pspec)
233 {
234   GtkToggleButton *tb;
235
236   tb = GTK_TOGGLE_BUTTON (object);
237
238   switch (prop_id)
239     {
240     case PROP_ACTIVE:
241       g_value_set_boolean (value, tb->active);
242       break;
243     case PROP_INCONSISTENT:
244       g_value_set_boolean (value, tb->inconsistent);
245       break;
246     case PROP_DRAW_INDICATOR:
247       g_value_set_boolean (value, tb->draw_indicator);
248       break;
249     default:
250       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251       break;
252     }
253 }
254
255 /**
256  * gtk_toggle_button_set_mode:
257  * @toggle_button: a #GtkToggleButton
258  * @draw_indicator: if %TRUE, draw the button as a separate indicator
259  * and label; if %FALSE, draw the button like a normal button
260  *
261  * Sets whether the button is displayed as a separate indicator and label.
262  * You can call this function on a checkbutton or a radiobutton with
263  * @draw_indicator = %FALSE to make the button look like a normal button
264  *
265  * This function only affects instances of classes like #GtkCheckButton
266  * and #GtkRadioButton that derive from #GtkToggleButton,
267  * not instances of #GtkToggleButton itself.
268  */
269 void
270 gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
271                             gboolean         draw_indicator)
272 {
273   GtkWidget *widget;
274
275   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
276
277   widget = GTK_WIDGET (toggle_button);
278
279   draw_indicator = draw_indicator ? TRUE : FALSE;
280
281   if (toggle_button->draw_indicator != draw_indicator)
282     {
283       toggle_button->draw_indicator = draw_indicator;
284       GTK_BUTTON (toggle_button)->depress_on_activate = !draw_indicator;
285       
286       if (GTK_WIDGET_VISIBLE (toggle_button))
287         gtk_widget_queue_resize (GTK_WIDGET (toggle_button));
288
289       g_object_notify (G_OBJECT (toggle_button), "draw_indicator");
290     }
291 }
292
293 /**
294  * gtk_toggle_button_get_mode:
295  * @toggle_button: a #GtkToggleButton
296  *
297  * Retrieves whether the button is displayed as a separate indicator
298  * and label. See gtk_toggle_button_set_mode().
299  *
300  * Return value: %TRUE if the togglebutton is drawn as a separate indicator
301  *   and label.
302  **/
303 gboolean
304 gtk_toggle_button_get_mode (GtkToggleButton *toggle_button)
305 {
306   g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), FALSE);
307
308   return toggle_button->draw_indicator;
309 }
310
311 void
312 gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
313                               gboolean         is_active)
314 {
315   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
316
317   is_active = is_active != FALSE;
318
319   if (toggle_button->active != is_active)
320     gtk_button_clicked (GTK_BUTTON (toggle_button));
321 }
322
323
324 gboolean
325 gtk_toggle_button_get_active (GtkToggleButton *toggle_button)
326 {
327   g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), FALSE);
328
329   return (toggle_button->active) ? TRUE : FALSE;
330 }
331
332
333 void
334 gtk_toggle_button_toggled (GtkToggleButton *toggle_button)
335 {
336   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
337
338   g_signal_emit (toggle_button, toggle_button_signals[TOGGLED], 0);
339 }
340
341 /**
342  * gtk_toggle_button_set_inconsistent:
343  * @toggle_button: a #GtkToggleButton
344  * @setting: %TRUE if state is inconsistent
345  *
346  * If the user has selected a range of elements (such as some text or
347  * spreadsheet cells) that are affected by a toggle button, and the
348  * current values in that range are inconsistent, you may want to
349  * display the toggle in an "in between" state. This function turns on
350  * "in between" display.  Normally you would turn off the inconsistent
351  * state again if the user toggles the toggle button. This has to be
352  * done manually, gtk_toggle_button_set_inconsistent() only affects
353  * visual appearance, it doesn't affect the semantics of the button.
354  * 
355  **/
356 void
357 gtk_toggle_button_set_inconsistent (GtkToggleButton *toggle_button,
358                                     gboolean         setting)
359 {
360   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
361   
362   setting = setting != FALSE;
363
364   if (setting != toggle_button->inconsistent)
365     {
366       toggle_button->inconsistent = setting;
367       
368       gtk_toggle_button_update_state (GTK_BUTTON (toggle_button));
369       gtk_widget_queue_draw (GTK_WIDGET (toggle_button));
370
371       g_object_notify (G_OBJECT (toggle_button), "inconsistent");      
372     }
373 }
374
375 /**
376  * gtk_toggle_button_get_inconsistent:
377  * @toggle_button: a #GtkToggleButton
378  * 
379  * Gets the value set by gtk_toggle_button_set_inconsistent().
380  * 
381  * Return value: %TRUE if the button is displayed as inconsistent, %FALSE otherwise
382  **/
383 gboolean
384 gtk_toggle_button_get_inconsistent (GtkToggleButton *toggle_button)
385 {
386   g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), FALSE);
387
388   return toggle_button->inconsistent;
389 }
390
391 static gint
392 gtk_toggle_button_expose (GtkWidget      *widget,
393                           GdkEventExpose *event)
394 {
395   if (GTK_WIDGET_DRAWABLE (widget))
396     {
397       GtkWidget *child = GTK_BIN (widget)->child;
398       GtkButton *button = GTK_BUTTON (widget);
399       GtkStateType state_type;
400       GtkShadowType shadow_type;
401
402       state_type = GTK_WIDGET_STATE (widget);
403       
404       if (GTK_TOGGLE_BUTTON (widget)->inconsistent)
405         {
406           if (state_type == GTK_STATE_ACTIVE)
407             state_type = GTK_STATE_NORMAL;
408           shadow_type = GTK_SHADOW_ETCHED_IN;
409         }
410       else
411         shadow_type = button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
412
413       _gtk_button_paint (button, &event->area, state_type, shadow_type,
414                          "togglebutton", "togglebuttondefault");
415
416       if (child)
417         gtk_container_propagate_expose (GTK_CONTAINER (widget), child, event);
418     }
419   
420   return FALSE;
421 }
422
423 static gboolean
424 gtk_toggle_button_mnemonic_activate (GtkWidget *widget,
425                                      gboolean   group_cycling)
426 {
427   /*
428    * We override the standard implementation in 
429    * gtk_widget_real_mnemonic_activate() in order to focus the widget even
430    * if there is no mnemonic conflict.
431    */
432   if (GTK_WIDGET_CAN_FOCUS (widget))
433     gtk_widget_grab_focus (widget);
434
435   if (!group_cycling)
436     gtk_widget_activate (widget);
437
438   return TRUE;
439 }
440
441 static void
442 gtk_toggle_button_pressed (GtkButton *button)
443 {
444   button->button_down = TRUE;
445
446   gtk_toggle_button_update_state (button);
447   gtk_widget_queue_draw (GTK_WIDGET (button));
448 }
449
450 static void
451 gtk_toggle_button_released (GtkButton *button)
452 {
453   if (button->button_down)
454     {
455       button->button_down = FALSE;
456
457       if (button->in_button)
458         gtk_button_clicked (button);
459
460       gtk_toggle_button_update_state (button);
461       gtk_widget_queue_draw (GTK_WIDGET (button));
462     }
463 }
464
465 static void
466 gtk_toggle_button_clicked (GtkButton *button)
467 {
468   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
469   toggle_button->active = !toggle_button->active;
470
471   gtk_toggle_button_toggled (toggle_button);
472
473   gtk_toggle_button_update_state (button);
474
475   g_object_notify (G_OBJECT (toggle_button), "active");
476 }
477
478 static void
479 gtk_toggle_button_update_state (GtkButton *button)
480 {
481   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
482   gboolean depressed;
483   GtkStateType new_state;
484
485   if (toggle_button->inconsistent)
486     depressed = FALSE;
487   else if (button->in_button && button->button_down)
488     depressed = TRUE;
489   else
490     depressed = toggle_button->active;
491       
492   if (button->in_button && (!button->button_down || toggle_button->draw_indicator))
493     new_state = GTK_STATE_PRELIGHT;
494   else
495     new_state = depressed ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL;
496
497   _gtk_button_set_depressed (button, depressed); 
498   gtk_widget_set_state (GTK_WIDGET (toggle_button), new_state);
499 }
500
501 #define __GTK_TOGGLE_BUTTON_C__
502 #include "gtkaliasdef.c"