1 /*! \file <gtk-pcb-coord-entry.c>
2 * \brief Implementation of GHidCoordEntry widget
4 * This widget is a modified spinbox for the user to enter
5 * pcb coords. It is assigned a default unit (for display),
6 * but this can be changed by the user by typing a new one
7 * or right-clicking on the box.
9 * Internally, it keeps track of its value in pcb coords.
10 * From the user's perspective, it uses natural human units.
14 #include <glib-object.h>
19 #include "pcb-printf.h"
21 #include "ghid-coord-entry.h"
28 static guint ghid_coord_entry_signals
[LAST_SIGNAL
] = { 0 };
30 struct _GHidCoordEntry
38 enum ce_step_size step_size
;
42 struct _GHidCoordEntryClass
44 GtkSpinButtonClass parent_class
;
46 void (* change_unit
) (GHidCoordEntry
*, const Unit
*);
50 /*! \brief Callback for "Change Unit" menu click */
52 menu_item_activate_cb (GtkMenuItem
*item
, GHidCoordEntry
*ce
)
54 const char *text
= gtk_menu_item_get_label (item
);
55 const Unit
*unit
= get_unit_struct (text
);
57 g_signal_emit (ce
, ghid_coord_entry_signals
[UNIT_CHANGE_SIGNAL
], 0, unit
);
60 /*! \brief Callback for context menu creation */
62 ghid_coord_entry_popup_cb (GHidCoordEntry
*ce
, GtkMenu
*menu
, gpointer data
)
65 const Unit
*unit_list
;
66 GtkWidget
*menu_item
, *submenu
;
70 unit_list
= get_unit_list ();
72 submenu
= gtk_menu_new ();
73 for (i
= 0; i
< n
; ++i
)
75 menu_item
= gtk_menu_item_new_with_label (unit_list
[i
].suffix
);
76 g_signal_connect (G_OBJECT (menu_item
), "activate",
77 G_CALLBACK (menu_item_activate_cb
), ce
);
78 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), menu_item
);
79 gtk_widget_show (menu_item
);
82 /* Add submenu to menu */
83 menu_item
= gtk_separator_menu_item_new ();
84 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
85 gtk_widget_show (menu_item
);
87 menu_item
= gtk_menu_item_new_with_label (_("Change Units"));
88 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item
), submenu
);
89 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
90 gtk_widget_show (menu_item
);
93 /*! \brief Callback for user output */
95 ghid_coord_entry_output_cb (GHidCoordEntry
*ce
, gpointer data
)
97 GtkAdjustment
*adj
= gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ce
));
98 double value
= gtk_adjustment_get_value (adj
);
101 text
= pcb_g_strdup_printf ("%.*f %s", ce
->unit
->default_prec
, value
, ce
->unit
->suffix
);
102 gtk_entry_set_text (GTK_ENTRY (ce
), text
);
108 /*! \brief Callback for user input */
110 ghid_coord_text_changed_cb (GHidCoordEntry
*entry
, gpointer data
)
114 const Unit
*new_unit
;
117 /* Check if units have changed */
118 text
= gtk_entry_get_text (GTK_ENTRY (entry
));
119 value
= strtod (text
, &suffix
);
120 new_unit
= get_unit_struct (suffix
);
121 if (new_unit
&& new_unit
!= entry
->unit
)
123 entry
->value
= unit_to_coord (new_unit
, value
);
124 g_signal_emit (entry
, ghid_coord_entry_signals
[UNIT_CHANGE_SIGNAL
], 0, new_unit
);
130 /*! \brief Callback for change in value (input or ^v clicks) */
132 ghid_coord_value_changed_cb (GHidCoordEntry
*ce
, gpointer data
)
134 GtkAdjustment
*adj
= gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ce
));
136 /* Re-calculate internal value */
137 double value
= gtk_adjustment_get_value (adj
);
138 ce
->value
= unit_to_coord (ce
->unit
, value
);
139 /* Handle potential unit changes */
140 ghid_coord_text_changed_cb (ce
, data
);
145 /*! \brief Change the unit used by a coord entry
147 * \param [in] ce The entry to be acted on
148 * \parin [in] new_unit The new unit to be used
151 ghid_coord_entry_change_unit (GHidCoordEntry
*ce
, const Unit
*new_unit
)
153 double climb_rate
= 0.0;
154 GtkAdjustment
*adj
= gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ce
));
157 /* Re-calculate min/max values for spinbox */
158 gtk_adjustment_configure (adj
, coord_to_unit (new_unit
, ce
->value
),
159 coord_to_unit (new_unit
, ce
->min_value
),
160 coord_to_unit (new_unit
, ce
->max_value
),
161 ce
->unit
->step_small
,
162 ce
->unit
->step_medium
,
165 switch (ce
->step_size
)
167 case CE_TINY
: climb_rate
= new_unit
->step_tiny
; break;
168 case CE_SMALL
: climb_rate
= new_unit
->step_small
; break;
169 case CE_MEDIUM
: climb_rate
= new_unit
->step_medium
; break;
170 case CE_LARGE
: climb_rate
= new_unit
->step_large
; break;
172 gtk_spin_button_configure (GTK_SPIN_BUTTON (ce
), adj
, climb_rate
,
173 new_unit
->default_prec
+ strlen (new_unit
->suffix
));
178 ghid_coord_entry_init (GHidCoordEntry
*ce
)
180 /* Hookup signal handlers */
181 g_signal_connect (G_OBJECT (ce
), "focus_out_event",
182 G_CALLBACK (ghid_coord_text_changed_cb
), NULL
);
183 g_signal_connect (G_OBJECT (ce
), "value_changed",
184 G_CALLBACK (ghid_coord_value_changed_cb
), NULL
);
185 g_signal_connect (G_OBJECT (ce
), "populate_popup",
186 G_CALLBACK (ghid_coord_entry_popup_cb
), NULL
);
187 g_signal_connect (G_OBJECT (ce
), "output",
188 G_CALLBACK (ghid_coord_entry_output_cb
), NULL
);
192 ghid_coord_entry_class_init (GHidCoordEntryClass
*klass
)
194 klass
->change_unit
= ghid_coord_entry_change_unit
;
196 /* GtkAutoComplete *ce : the object acted on */
197 /* const Unit *new_unit: the new unit that was set */
198 ghid_coord_entry_signals
[UNIT_CHANGE_SIGNAL
] =
199 g_signal_new ("change-unit",
200 G_TYPE_FROM_CLASS (klass
),
201 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
202 G_STRUCT_OFFSET (GHidCoordEntryClass
, change_unit
),
204 g_cclosure_marshal_VOID__POINTER
, G_TYPE_NONE
,
209 /* PUBLIC FUNCTIONS */
211 ghid_coord_entry_get_type (void)
213 static GType ce_type
= 0;
217 const GTypeInfo ce_info
=
219 sizeof (GHidCoordEntryClass
),
220 NULL
, /* base_init */
221 NULL
, /* base_finalize */
222 (GClassInitFunc
) ghid_coord_entry_class_init
,
223 NULL
, /* class_finalize */
224 NULL
, /* class_data */
225 sizeof (GHidCoordEntry
),
227 (GInstanceInitFunc
) ghid_coord_entry_init
,
230 ce_type
= g_type_register_static (GTK_TYPE_SPIN_BUTTON
,
239 /*! \brief Create a new GHidCoordEntry
241 * \param [in] min_val The minimum allowed value, in pcb coords
242 * \param [in] max_val The maximum allowed value, in pcb coords
243 * \param [in] value The default value, in pcb coords
244 * \param [in] unit The default unit
245 * \param [in] step_size How large the default increments should be
247 * \return a freshly-allocated GHidCoordEntry
250 ghid_coord_entry_new (Coord min_val
, Coord max_val
, Coord value
,
251 const Unit
*unit
, enum ce_step_size step_size
)
253 /* Setup spinbox min/max values */
254 double small_step
, big_step
;
256 GHidCoordEntry
*ce
= g_object_new (GHID_COORD_ENTRY_TYPE
, NULL
);
259 ce
->min_value
= min_val
;
260 ce
->max_value
= max_val
;
263 ce
->step_size
= step_size
;
267 small_step
= unit
->step_tiny
;
268 big_step
= unit
->step_small
;
271 small_step
= unit
->step_small
;
272 big_step
= unit
->step_medium
;
275 small_step
= unit
->step_medium
;
276 big_step
= unit
->step_large
;
279 small_step
= unit
->step_large
;
280 big_step
= unit
->step_huge
;
283 small_step
= big_step
= 0;
287 adj
= GTK_ADJUSTMENT (gtk_adjustment_new (coord_to_unit (unit
, value
),
288 coord_to_unit (unit
, min_val
),
289 coord_to_unit (unit
, max_val
),
290 small_step
, big_step
, 0.0));
291 gtk_spin_button_configure (GTK_SPIN_BUTTON (ce
), adj
, small_step
,
292 unit
->default_prec
+ strlen (unit
->suffix
));
293 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (ce
), FALSE
);
295 return GTK_WIDGET (ce
);
298 /*! \brief Gets a GHidCoordEntry's value, in pcb coords */
300 ghid_coord_entry_get_value (GHidCoordEntry
*ce
)
305 /*! \brief Sets a GHidCoordEntry's value, in pcb coords */
307 ghid_coord_entry_set_value (GHidCoordEntry
*ce
, Coord val
)
309 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ce
),
310 coord_to_unit (ce
->unit
, val
));