Update Spanish translation
[gnumeric.git] / src / sheet-object-widget.c
blob7a5def31a725abba5740f755b5c6d613c95b7632
2 /*
3 * sheet-object-widget.c: SheetObject wrappers for simple gtk widgets.
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <application.h>
26 #include <sheet-object-widget-impl.h>
27 #include <widgets/gnm-radiobutton.h>
28 #include <gnm-pane.h>
29 #include <gnumeric-simple-canvas.h>
30 #include <gui-util.h>
31 #include <gutils.h>
32 #include <dependent.h>
33 #include <sheet-control-gui.h>
34 #include <sheet-object-impl.h>
35 #include <expr.h>
36 #include <parse-util.h>
37 #include <value.h>
38 #include <ranges.h>
39 #include <selection.h>
40 #include <wbc-gtk.h>
41 #include <workbook.h>
42 #include <sheet.h>
43 #include <cell.h>
44 #include <mathfunc.h>
45 #include <widgets/gnm-expr-entry.h>
46 #include <dialogs/dialogs.h>
47 #include <dialogs/help.h>
48 #include <xml-sax.h>
49 #include <commands.h>
50 #include <gnm-format.h>
51 #include <number-match.h>
53 #include <goffice/goffice.h>
55 #include <gsf/gsf-impl-utils.h>
56 #include <libxml/globals.h>
57 #include <gdk/gdkkeysyms.h>
58 #include <math.h>
59 #include <string.h>
61 #define CXML2C(s) ((char const *)(s))
62 #define CC2XML(s) ((xmlChar const *)(s))
64 static inline gboolean
65 attr_eq (const xmlChar *a, const char *s)
67 return !strcmp (CXML2C (a), s);
70 /****************************************************************************/
72 static void
73 cb_so_get_ref (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, gpointer user)
75 GnmDependent **pdep = user;
76 *pdep = dep;
79 static GnmCellRef *
80 so_get_ref (SheetObject const *so, GnmCellRef *res, gboolean force_sheet)
82 GnmValue *target;
83 GnmDependent *dep = NULL;
85 g_return_val_if_fail (so != NULL, NULL);
87 /* Let's hope there's just one. */
88 sheet_object_foreach_dep ((SheetObject*)so, cb_so_get_ref, &dep);
89 g_return_val_if_fail (dep, NULL);
91 if (dep->texpr == NULL)
92 return NULL;
94 target = gnm_expr_top_get_range (dep->texpr);
95 if (target == NULL)
96 return NULL;
98 *res = target->v_range.cell.a;
99 value_release (target);
101 if (force_sheet && res->sheet == NULL)
102 res->sheet = sheet_object_get_sheet (so);
103 return res;
106 static void
107 cb_so_clear_sheet (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, G_GNUC_UNUSED gpointer user)
109 if (dependent_is_linked (dep))
110 dependent_unlink (dep);
111 dep->sheet = NULL;
114 static gboolean
115 so_clear_sheet (SheetObject *so)
117 /* Note: This implements sheet_object_clear_sheet. */
118 sheet_object_foreach_dep (so, cb_so_clear_sheet, NULL);
119 return FALSE;
122 static GocWidget *
123 get_goc_widget (SheetObjectView *view)
125 GocGroup *group = GOC_GROUP (view);
127 if (group == NULL || group->children == NULL)
128 return NULL;
130 return GOC_WIDGET (group->children->data);
133 static void
134 so_widget_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
136 GocItem *view = GOC_ITEM (sov);
137 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
138 double left = MIN (coords [0], coords [2]) / scale;
139 double top = MIN (coords [1], coords [3]) / scale;
140 double width = (fabs (coords [2] - coords [0]) + 1.) / scale;
141 double height = (fabs (coords [3] - coords [1]) + 1.) / scale;
143 /* We only need the next check for frames, but it doesn't hurt otherwise. */
144 if (width < 8.)
145 width = 8.;
147 if (visible) {
148 /* NOTE : far point is EXCLUDED so we add 1 */
149 goc_widget_set_bounds (get_goc_widget (sov),
150 left, top, width, height);
151 goc_item_show (view);
152 } else
153 goc_item_hide (view);
156 static GdkWindow *
157 so_widget_view_get_window (GocItem *item)
159 GocGroup *group = GOC_GROUP (item);
160 return goc_item_get_window (GOC_ITEM (group->children->data));
163 static void
164 so_widget_view_class_init (SheetObjectViewClass *sov_klass)
166 GocItemClass *item_klass = (GocItemClass *) sov_klass;
167 sov_klass->set_bounds = so_widget_view_set_bounds;
168 item_klass->get_window = so_widget_view_get_window;
171 static GSF_CLASS (SOWidgetView, so_widget_view,
172 so_widget_view_class_init, NULL,
173 GNM_SO_VIEW_TYPE)
175 /****************************************************************************/
177 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-config-dialog"
179 #define GNM_SOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_TYPE, SheetObjectWidgetClass))
180 #define SOW_CLASS(so) (GNM_SOW_CLASS (G_OBJECT_GET_CLASS(so)))
182 #define SOW_MAKE_TYPE(n1, n2, fn_config, fn_set_sheet, fn_clear_sheet, fn_foreach_dep, \
183 fn_copy, fn_write_sax, fn_prep_sax_parser, \
184 fn_get_property, fn_set_property, \
185 fn_draw_cairo, class_init_code) \
187 static void \
188 sheet_widget_ ## n1 ## _class_init (GObjectClass *object_class) \
190 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class); \
191 SheetObjectClass *so_class = GNM_SO_CLASS (object_class); \
192 object_class->finalize = &sheet_widget_ ## n1 ## _finalize; \
193 object_class->set_property = fn_set_property; \
194 object_class->get_property = fn_get_property; \
195 so_class->user_config = fn_config; \
196 so_class->interactive = TRUE; \
197 so_class->assign_to_sheet = fn_set_sheet; \
198 so_class->remove_from_sheet = fn_clear_sheet; \
199 so_class->foreach_dep = fn_foreach_dep; \
200 so_class->copy = fn_copy; \
201 so_class->write_xml_sax = fn_write_sax; \
202 so_class->prep_sax_parser = fn_prep_sax_parser; \
203 so_class->draw_cairo = fn_draw_cairo; \
204 sow_class->create_widget = &sheet_widget_ ## n1 ## _create_widget; \
205 { class_init_code; } \
208 GSF_CLASS (SheetWidget ## n2, sheet_widget_ ## n1, \
209 &sheet_widget_ ## n1 ## _class_init, \
210 &sheet_widget_ ## n1 ## _init, \
211 GNM_SOW_TYPE)
213 typedef struct {
214 SheetObject so;
215 } SheetObjectWidget;
217 typedef struct {
218 SheetObjectClass parent_class;
219 GtkWidget *(*create_widget)(SheetObjectWidget *);
220 } SheetObjectWidgetClass;
222 static GObjectClass *sheet_object_widget_class = NULL;
224 static GtkWidget *
225 sow_create_widget (SheetObjectWidget *sow)
227 GtkWidget *w = SOW_CLASS(sow)->create_widget (sow);
228 GtkStyleContext *context = gtk_widget_get_style_context (w);
229 gtk_style_context_add_class (context, "sheet-object");
230 return w;
233 static void
234 sheet_widget_draw_cairo (SheetObject const *so, cairo_t *cr,
235 double width, double height)
237 /* This is the default for so widgets without their own method */
238 /* See bugs #705638 and #705640 */
239 if (NULL != gdk_screen_get_default ()) {
240 GtkWidget *win = gtk_offscreen_window_new ();
241 GtkWidget *w = sow_create_widget (GNM_SOW (so));
243 gtk_container_add (GTK_CONTAINER (win), w);
244 gtk_widget_set_size_request (w, width, height);
245 gtk_widget_show_all (win);
246 gtk_container_propagate_draw (GTK_CONTAINER (win), w, cr);
247 gtk_widget_destroy (win);
248 } else
249 g_warning (_("Because of GTK bug #705640, a sheet object widget is not being printed."));
252 static void
253 sax_write_dep (GsfXMLOut *output, GnmDependent const *dep, char const *id,
254 GnmConventions const *convs)
256 if (dep->texpr != NULL) {
257 GnmParsePos pos;
258 char *val;
260 parse_pos_init_dep (&pos, dep);
261 val = gnm_expr_top_as_string (dep->texpr, &pos, convs);
262 gsf_xml_out_add_cstr (output, id, val);
263 g_free (val);
267 static gboolean
268 sax_read_dep (xmlChar const * const *attrs, char const *name,
269 GnmDependent *dep, GsfXMLIn *xin, GnmConventions const *convs)
271 g_return_val_if_fail (attrs != NULL, FALSE);
272 g_return_val_if_fail (attrs[0] != NULL, FALSE);
273 g_return_val_if_fail (attrs[1] != NULL, FALSE);
275 if (!attr_eq (attrs[0], name))
276 return FALSE;
278 dep->sheet = NULL;
279 if (attrs[1] != NULL && *attrs[1] != '\0') {
280 GnmParsePos pp;
282 parse_pos_init_sheet (&pp, gnm_xml_in_cur_sheet (xin));
283 dep->texpr = gnm_expr_parse_str (CXML2C (attrs[1]), &pp,
284 GNM_EXPR_PARSE_DEFAULT,
285 convs, NULL);
286 } else
287 dep->texpr = NULL;
289 return TRUE;
292 static SheetObjectView *
293 sheet_object_widget_new_view (SheetObject *so, SheetObjectViewContainer *container)
295 GtkWidget *view_widget = sow_create_widget (GNM_SOW (so));
296 GocItem *view_item = goc_item_new (
297 gnm_pane_object_group (GNM_PANE (container)),
298 so_widget_view_get_type (),
299 NULL);
300 goc_item_new (GOC_GROUP (view_item),
301 GOC_TYPE_WIDGET,
302 "widget", view_widget,
303 NULL);
304 /* g_warning ("%p is widget for so %p", (void *)view_widget, (void *)so);*/
305 gtk_widget_show_all (view_widget);
306 goc_item_hide (view_item);
307 gnm_pane_widget_register (so, view_widget, view_item);
308 return gnm_pane_object_register (so, view_item, TRUE);
311 static void
312 sheet_object_widget_class_init (GObjectClass *object_class)
314 SheetObjectClass *so_class = GNM_SO_CLASS (object_class);
315 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class);
317 sheet_object_widget_class = G_OBJECT_CLASS (object_class);
319 /* SheetObject class method overrides */
320 so_class->new_view = sheet_object_widget_new_view;
321 so_class->rubber_band_directly = TRUE;
322 so_class->draw_cairo = sheet_widget_draw_cairo;
324 sow_class->create_widget = NULL;
327 static void
328 sheet_object_widget_init (SheetObjectWidget *sow)
330 SheetObject *so = GNM_SO (sow);
331 so->flags |= SHEET_OBJECT_CAN_PRESS;
334 GSF_CLASS (SheetObjectWidget, sheet_object_widget,
335 sheet_object_widget_class_init,
336 sheet_object_widget_init,
337 GNM_SO_TYPE)
339 static WorkbookControl *
340 widget_wbc (GtkWidget *widget)
342 return scg_wbc (GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (widget, GNM_SIMPLE_CANVAS_TYPE))->scg);
346 /****************************************************************************/
347 #define GNM_SOW_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_FRAME_TYPE, SheetWidgetFrame))
348 typedef struct {
349 SheetObjectWidget sow;
350 char *label;
351 } SheetWidgetFrame;
352 typedef SheetObjectWidgetClass SheetWidgetFrameClass;
354 enum {
355 SOF_PROP_0 = 0,
356 SOF_PROP_TEXT
359 static void
360 sheet_widget_frame_get_property (GObject *obj, guint param_id,
361 GValue *value, GParamSpec *pspec)
363 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
365 switch (param_id) {
366 case SOF_PROP_TEXT:
367 g_value_set_string (value, swf->label);
368 break;
369 default:
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
371 break;
375 static void
376 sheet_widget_frame_set_property (GObject *obj, guint param_id,
377 GValue const *value, GParamSpec *pspec)
379 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
381 switch (param_id) {
382 case SOF_PROP_TEXT:
383 sheet_widget_frame_set_label (GNM_SO (swf),
384 g_value_get_string (value));
385 break;
386 default:
387 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
388 return;
393 static void
394 sheet_widget_frame_init_full (SheetWidgetFrame *swf, char const *text)
396 swf->label = g_strdup (text);
399 static void
400 sheet_widget_frame_init (SheetWidgetFrame *swf)
402 sheet_widget_frame_init_full (swf, _("Frame"));
405 static void
406 sheet_widget_frame_finalize (GObject *obj)
408 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
410 g_free (swf->label);
411 swf->label = NULL;
413 sheet_object_widget_class->finalize (obj);
416 static GtkWidget *
417 sheet_widget_frame_create_widget (SheetObjectWidget *sow)
419 GtkWidget *widget = gtk_event_box_new (),
420 *frame = gtk_frame_new (GNM_SOW_FRAME (sow)->label);
421 gtk_container_add (GTK_CONTAINER (widget), frame);
422 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
423 return widget;
426 static void
427 sheet_widget_frame_copy (SheetObject *dst, SheetObject const *src)
429 sheet_widget_frame_init_full (GNM_SOW_FRAME (dst),
430 GNM_SOW_FRAME (src)->label);
433 static void
434 sheet_widget_frame_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
435 G_GNUC_UNUSED GnmConventions const *convs)
437 SheetWidgetFrame const *swf = GNM_SOW_FRAME (so);
438 gsf_xml_out_add_cstr (output, "Label", swf->label);
441 static void
442 sheet_widget_frame_prep_sax_parser (SheetObject *so, G_GNUC_UNUSED GsfXMLIn *xin,
443 xmlChar const **attrs,
444 G_GNUC_UNUSED GnmConventions const *convs)
446 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
447 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
448 if (attr_eq (attrs[0], "Label")) {
449 g_free (swf->label);
450 swf->label = g_strdup (CXML2C (attrs[1]));
454 typedef struct {
455 GtkWidget *dialog;
456 GtkWidget *label;
458 char *old_label;
459 GtkWidget *old_focus;
461 WBCGtk *wbcg;
462 SheetWidgetFrame *swf;
463 Sheet *sheet;
464 } FrameConfigState;
466 static void
467 cb_frame_config_destroy (FrameConfigState *state)
469 g_return_if_fail (state != NULL);
471 g_free (state->old_label);
472 state->old_label = NULL;
473 state->dialog = NULL;
474 g_free (state);
477 static void
478 cb_frame_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
480 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
482 cmd_so_set_frame_label (GNM_WBC (state->wbcg),
483 GNM_SO (state->swf),
484 g_strdup (state->old_label), g_strdup (text));
485 gtk_widget_destroy (state->dialog);
488 void
489 sheet_widget_frame_set_label (SheetObject *so, char const* str)
491 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
492 GList *ptr;
494 str = str ? str : "";
496 if (go_str_compare (str, swf->label) == 0)
497 return;
499 g_free (swf->label);
500 swf->label = g_strdup (str);
502 for (ptr = swf->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
503 SheetObjectView *view = ptr->data;
504 GocWidget *item = get_goc_widget (view);
505 GList *children = gtk_container_get_children (GTK_CONTAINER (item->widget));
506 gtk_frame_set_label (GTK_FRAME (children->data), str);
507 g_list_free (children);
511 static void
512 cb_frame_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
514 sheet_widget_frame_set_label (GNM_SO (state->swf), state->old_label);
516 gtk_widget_destroy (state->dialog);
519 static void
520 cb_frame_label_changed (GtkWidget *entry, FrameConfigState *state)
522 gchar const *text;
524 text = gtk_entry_get_text(GTK_ENTRY(entry));
525 sheet_widget_frame_set_label (GNM_SO (state->swf), text);
528 static void
529 sheet_widget_frame_user_config (SheetObject *so, SheetControl *sc)
531 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
532 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
533 FrameConfigState *state;
534 GtkBuilder *gui;
536 g_return_if_fail (swf != NULL);
538 /* Only pop up one copy per workbook */
539 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
540 return;
542 gui = gnm_gtk_builder_load ("res:ui/so-frame.ui", NULL, GO_CMD_CONTEXT (wbcg));
543 if (!gui)
544 return;
545 state = g_new (FrameConfigState, 1);
546 state->swf = swf;
547 state->wbcg = wbcg;
548 state->sheet = sc_sheet (sc);
549 state->old_focus = NULL;
550 state->old_label = g_strdup(swf->label);
551 state->dialog = go_gtk_builder_get_widget (gui, "so_frame");
553 state->label = go_gtk_builder_get_widget (gui, "entry");
554 gtk_entry_set_text (GTK_ENTRY(state->label), swf->label);
555 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
556 gnm_editable_enters (GTK_WINDOW (state->dialog),
557 GTK_WIDGET (state->label));
559 g_signal_connect (G_OBJECT(state->label),
560 "changed",
561 G_CALLBACK (cb_frame_label_changed), state);
562 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
563 "ok_button")),
564 "clicked",
565 G_CALLBACK (cb_frame_config_ok_clicked), state);
566 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
567 "cancel_button")),
568 "clicked",
569 G_CALLBACK (cb_frame_config_cancel_clicked), state);
571 gnm_init_help_button (
572 go_gtk_builder_get_widget (gui, "help_button"),
573 GNUMERIC_HELP_LINK_SO_FRAME);
576 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
577 SHEET_OBJECT_CONFIG_KEY);
579 wbc_gtk_attach_guru (state->wbcg, state->dialog);
580 g_object_set_data_full (G_OBJECT (state->dialog),
581 "state", state, (GDestroyNotify) cb_frame_config_destroy);
582 g_object_unref (gui);
584 gtk_widget_show (state->dialog);
587 static PangoFontDescription *
588 get_font (void)
590 // Note: Under gnumeric, we get a proper font using GtkStyleContext.
591 // Under ssconvert, we try GSettings.
592 // The 'sans 10' is just insurance
594 PangoFontDescription *desc;
595 PangoFontMask mask;
596 int size = 0;
598 if (gdk_screen_get_default ()) {
599 // Without a default screen, the following will crash
600 // with newer gtk+.
601 GtkStyleContext *style = gtk_style_context_new ();
602 GtkWidgetPath *path = gtk_widget_path_new ();
604 gtk_style_context_set_path (style, path);
605 gtk_widget_path_unref (path);
607 gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL,
608 GTK_STYLE_PROPERTY_FONT, &desc, NULL);
609 g_object_unref (style);
610 } else
611 desc = pango_font_description_new ();
613 mask = pango_font_description_get_set_fields (desc);
614 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
615 size = pango_font_description_get_size (desc);
617 if (gnm_debug_flag ("so-font")) {
618 char *s = pango_font_description_to_string (desc);
619 g_printerr ("from GtkStyleContext font=\"%s\", family set = %i,"
620 " size set = %i, size = %i\n",
621 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
622 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
623 g_free (s);
626 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
627 /* Trying gsettings */
628 GSettings *set = g_settings_new ("org.gnome.desktop.interface");
629 char *font_name = g_settings_get_string (set, "font-name");
630 if (font_name != NULL) {
631 pango_font_description_free (desc);
632 desc = pango_font_description_from_string (font_name);
633 g_free (font_name);
634 mask = pango_font_description_get_set_fields (desc);
635 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
636 size = pango_font_description_get_size (desc);
637 else
638 size = 0;
639 if (gnm_debug_flag ("so-font")) {
640 char *s = pango_font_description_to_string (desc);
641 g_printerr ("from GSettings: font=\"%s\", family set = %i,"
642 " size set = %i, size = %i\n",
643 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
644 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
645 g_free (s);
650 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
651 pango_font_description_free (desc);
652 desc = pango_font_description_from_string ("sans 10");
653 if (gnm_debug_flag ("so-font"))
654 g_printerr ("Using \"sans 10\" instead.\n");
657 return desc;
660 static void
661 draw_cairo_text (cairo_t *cr, char const *text, int *pwidth, int *pheight,
662 gboolean centered_v, gboolean centered_h, gboolean single, gint highlight_n, gboolean scale)
664 PangoLayout *layout = pango_cairo_create_layout (cr);
665 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
666 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
667 PangoFontDescription *desc = get_font ();
668 int width, height;
670 pango_context_set_font_description
671 (pango_layout_get_context (layout), desc);
672 pango_layout_set_spacing (layout, 3 * PANGO_SCALE);
673 pango_layout_set_single_paragraph_mode (layout, single);
674 pango_layout_set_text (layout, text, -1);
675 pango_layout_get_pixel_size (layout, &width, &height);
677 cairo_scale (cr, scale_h, scale_v);
679 if (scale && pwidth != NULL && pheight != NULL) {
680 double sc_x = ((double) *pwidth)/(width * scale_h);
681 double sc_y = ((double) *pheight)/(height * scale_v);
682 double sc = MIN(sc_x, sc_y);
684 if (sc < 1.)
685 cairo_scale (cr, sc, sc);
688 if (centered_v)
689 cairo_rel_move_to (cr, 0., 0.5 - ((double)height)/2.);
690 if (centered_h)
691 cairo_rel_move_to (cr, 0.5 - ((double)width)/2., 0.);
692 if (highlight_n > 0 && pheight != NULL && pwidth != NULL) {
693 PangoLayoutIter *pliter;
694 gboolean got_line = TRUE;
695 int i;
696 pliter = pango_layout_get_iter (layout);
697 for (i = 1; i < highlight_n; i++)
698 got_line = pango_layout_iter_next_line (pliter);
700 if (got_line) {
701 int y0, y1;
702 double dy0 = 0, dy1 = 0;
703 pango_layout_iter_get_line_yrange (pliter, &y0, &y1);
704 dy0 = y0 / (double)PANGO_SCALE;
705 dy1 = y1 / (double)PANGO_SCALE;
707 if (dy1 > (*pheight - 4)/scale_v)
708 cairo_translate (cr, 0, (*pheight - 4)/scale_v - dy1);
710 cairo_new_path (cr);
711 cairo_rectangle (cr, -4/scale_h, dy0,
712 *pwidth/scale_h, dy1 - dy0);
713 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
714 cairo_fill (cr);
716 pango_layout_iter_free (pliter);
717 cairo_set_source_rgb(cr, 0, 0, 0);
719 pango_cairo_show_layout (cr, layout);
720 pango_font_description_free (desc);
721 g_object_unref (layout);
723 if (pwidth)
724 *pwidth = width * scale_h;
725 if (pheight)
726 *pheight = height * scale_v;
729 static void
730 sheet_widget_frame_draw_cairo (SheetObject const *so, cairo_t *cr,
731 double width, double height)
733 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
735 int theight = 0, twidth = 0;
736 cairo_save (cr);
737 cairo_move_to (cr, 10, 0);
739 cairo_save (cr);
740 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
741 draw_cairo_text (cr, swf->label, &twidth, &theight, FALSE, FALSE, TRUE, 0, FALSE);
742 cairo_restore (cr);
744 cairo_set_line_width (cr, 1);
745 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
746 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
747 cairo_new_path (cr);
748 cairo_move_to (cr, 6, theight/2);
749 cairo_line_to (cr, 0, theight/2);
750 cairo_line_to (cr, 0, height);
751 cairo_line_to (cr, width, height);
752 cairo_line_to (cr, width, theight/2);
753 cairo_line_to (cr, 14 + twidth, theight/2);
754 cairo_stroke (cr);
756 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
757 cairo_new_path (cr);
758 cairo_move_to (cr, 6, theight/2 + 1);
759 cairo_line_to (cr, 1, theight/2 + 1);
760 cairo_line_to (cr, 1, height - 1);
761 cairo_line_to (cr, width - 1, height - 1);
762 cairo_line_to (cr, width - 1, theight/2 + 1);
763 cairo_line_to (cr, 14 + twidth, theight/2 + 1);
764 cairo_stroke (cr);
766 cairo_new_path (cr);
767 cairo_restore (cr);
770 SOW_MAKE_TYPE (frame, Frame,
771 sheet_widget_frame_user_config,
772 NULL,
773 NULL,
774 NULL,
775 sheet_widget_frame_copy,
776 sheet_widget_frame_write_xml_sax,
777 sheet_widget_frame_prep_sax_parser,
778 sheet_widget_frame_get_property,
779 sheet_widget_frame_set_property,
780 sheet_widget_frame_draw_cairo,
782 g_object_class_install_property
783 (object_class, SOF_PROP_TEXT,
784 g_param_spec_string ("text", NULL, NULL, NULL,
785 GSF_PARAM_STATIC | G_PARAM_READWRITE));
788 /****************************************************************************/
789 #define GNM_SOW_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_BUTTON_TYPE, SheetWidgetButton))
790 #define DEP_TO_BUTTON(d_ptr) (SheetWidgetButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetButton, dep))
791 typedef struct {
792 SheetObjectWidget sow;
794 GnmDependent dep;
795 char *label;
796 PangoAttrList *markup;
797 gboolean value;
798 } SheetWidgetButton;
799 typedef SheetObjectWidgetClass SheetWidgetButtonClass;
801 enum {
802 SOB_PROP_0 = 0,
803 SOB_PROP_TEXT,
804 SOB_PROP_MARKUP
807 static void
808 sheet_widget_button_get_property (GObject *obj, guint param_id,
809 GValue *value, GParamSpec *pspec)
811 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
813 switch (param_id) {
814 case SOB_PROP_TEXT:
815 g_value_set_string (value, swb->label);
816 break;
817 case SOB_PROP_MARKUP:
818 g_value_set_boxed (value, NULL); /* swb->markup */
819 break;
820 default:
821 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
822 break;
826 static void
827 sheet_widget_button_set_property (GObject *obj, guint param_id,
828 GValue const *value, GParamSpec *pspec)
830 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
832 switch (param_id) {
833 case SOB_PROP_TEXT:
834 sheet_widget_button_set_label (GNM_SO (swb),
835 g_value_get_string (value));
836 break;
837 case SOB_PROP_MARKUP:
838 #if 0
839 sheet_widget_button_set_markup (GNM_SO (swb),
840 g_value_peek_pointer (value));
841 #endif
842 break;
843 default:
844 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
845 return;
849 static void
850 button_eval (GnmDependent *dep)
852 GnmValue *v;
853 GnmEvalPos pos;
854 gboolean err, result;
856 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
857 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
858 result = value_get_as_bool (v, &err);
859 value_release (v);
860 if (!err) {
861 SheetWidgetButton *swb = DEP_TO_BUTTON(dep);
863 swb->value = result;
867 static void
868 button_debug_name (GnmDependent const *dep, GString *target)
870 g_string_append_printf (target, "Button%p", (void *)dep);
873 static DEPENDENT_MAKE_TYPE (button, NULL)
875 static void
876 sheet_widget_button_init_full (SheetWidgetButton *swb,
877 GnmCellRef const *ref,
878 char const *text,
879 PangoAttrList *markup)
881 SheetObject *so = GNM_SO (swb);
883 so->flags &= ~SHEET_OBJECT_PRINT;
884 swb->label = g_strdup (text);
885 swb->markup = markup;
886 swb->value = FALSE;
887 swb->dep.sheet = NULL;
888 swb->dep.flags = button_get_dep_type ();
889 swb->dep.texpr = (ref != NULL)
890 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
891 : NULL;
892 if (markup) pango_attr_list_ref (markup);
895 static void
896 sheet_widget_button_init (SheetWidgetButton *swb)
898 sheet_widget_button_init_full (swb, NULL, _("Button"), NULL);
901 static void
902 sheet_widget_button_finalize (GObject *obj)
904 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
906 g_free (swb->label);
907 swb->label = NULL;
909 if (swb->markup) {
910 pango_attr_list_unref (swb->markup);
911 swb->markup = NULL;
914 dependent_set_expr (&swb->dep, NULL);
916 sheet_object_widget_class->finalize (obj);
919 static void
920 cb_button_pressed (GtkToggleButton *button, SheetWidgetButton *swb)
922 GnmCellRef ref;
924 swb->value = TRUE;
926 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
927 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
928 _("Pressed Button"),
929 &ref, value_new_bool (TRUE),
930 sheet_object_get_sheet (GNM_SO (swb)));
934 static void
935 cb_button_released (GtkToggleButton *button, SheetWidgetButton *swb)
937 GnmCellRef ref;
939 swb->value = FALSE;
941 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
942 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
943 _("Released Button"),
944 &ref, value_new_bool (FALSE),
945 sheet_object_get_sheet (GNM_SO (swb)));
949 static GtkWidget *
950 sheet_widget_button_create_widget (SheetObjectWidget *sow)
952 SheetWidgetButton *swb = GNM_SOW_BUTTON (sow);
953 GtkWidget *w = gtk_button_new_with_label (swb->label);
954 gtk_widget_set_can_focus (w, FALSE);
955 gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))),
956 swb->markup);
957 g_signal_connect (G_OBJECT (w),
958 "pressed",
959 G_CALLBACK (cb_button_pressed), swb);
960 g_signal_connect (G_OBJECT (w),
961 "released",
962 G_CALLBACK (cb_button_released), swb);
963 return w;
966 static void
967 sheet_widget_button_copy (SheetObject *dst, SheetObject const *src)
969 SheetWidgetButton const *src_swb = GNM_SOW_BUTTON (src);
970 SheetWidgetButton *dst_swb = GNM_SOW_BUTTON (dst);
971 GnmCellRef ref;
972 sheet_widget_button_init_full (dst_swb,
973 so_get_ref (src, &ref, FALSE),
974 src_swb->label,
975 src_swb->markup);
976 dst_swb->value = src_swb->value;
979 typedef struct {
980 GtkWidget *dialog;
981 GnmExprEntry *expression;
982 GtkWidget *label;
984 char *old_label;
985 GtkWidget *old_focus;
987 WBCGtk *wbcg;
988 SheetWidgetButton *swb;
989 Sheet *sheet;
990 } ButtonConfigState;
992 static void
993 cb_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
994 ButtonConfigState *state)
996 /* Note: half of the set-focus action is handle by the default
997 * callback installed by wbc_gtk_attach_guru */
999 /* Force an update of the content in case it needs tweaking (eg make it
1000 * absolute) */
1001 if (state->old_focus != NULL &&
1002 GNM_EXPR_ENTRY_IS (gtk_widget_get_parent (state->old_focus))) {
1003 GnmParsePos pp;
1004 GnmExprTop const *texpr = gnm_expr_entry_parse
1005 (GNM_EXPR_ENTRY (gtk_widget_get_parent (state->old_focus)),
1006 parse_pos_init_sheet (&pp, state->sheet),
1007 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1008 if (texpr != NULL)
1009 gnm_expr_top_unref (texpr);
1011 state->old_focus = focus_widget;
1014 static void
1015 cb_button_config_destroy (ButtonConfigState *state)
1017 g_return_if_fail (state != NULL);
1019 g_free (state->old_label);
1020 state->old_label = NULL;
1021 state->dialog = NULL;
1022 g_free (state);
1025 static void
1026 cb_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1028 SheetObject *so = GNM_SO (state->swb);
1029 GnmParsePos pp;
1030 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1031 parse_pos_init_sheet (&pp, so->sheet),
1032 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1033 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
1035 cmd_so_set_button (GNM_WBC (state->wbcg), so,
1036 texpr, g_strdup (state->old_label), g_strdup (text));
1038 gtk_widget_destroy (state->dialog);
1041 static void
1042 cb_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1044 sheet_widget_button_set_label (GNM_SO (state->swb),
1045 state->old_label);
1046 gtk_widget_destroy (state->dialog);
1049 static void
1050 cb_button_label_changed (GtkEntry *entry, ButtonConfigState *state)
1052 sheet_widget_button_set_label (GNM_SO (state->swb),
1053 gtk_entry_get_text (entry));
1056 static void
1057 sheet_widget_button_user_config (SheetObject *so, SheetControl *sc)
1059 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1060 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1061 ButtonConfigState *state;
1062 GtkWidget *grid;
1063 GtkBuilder *gui;
1065 g_return_if_fail (swb != NULL);
1067 /* Only pop up one copy per workbook */
1068 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1069 return;
1071 gui = gnm_gtk_builder_load ("res:ui/so-button.ui", NULL, GO_CMD_CONTEXT (wbcg));
1072 if (!gui)
1073 return;
1074 state = g_new (ButtonConfigState, 1);
1075 state->swb = swb;
1076 state->wbcg = wbcg;
1077 state->sheet = sc_sheet (sc);
1078 state->old_focus = NULL;
1079 state->old_label = g_strdup (swb->label);
1080 state->dialog = go_gtk_builder_get_widget (gui, "SO-Button");
1082 grid = go_gtk_builder_get_widget (gui, "main-grid");
1084 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1085 gnm_expr_entry_set_flags (state->expression,
1086 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1087 GNM_EE_MASK);
1088 gnm_expr_entry_load_from_dep (state->expression, &swb->dep);
1089 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1090 GTK_WIDGET (state->expression));
1091 gtk_grid_attach (GTK_GRID (grid),
1092 GTK_WIDGET (state->expression), 1, 0, 1, 1);
1093 gtk_widget_show (GTK_WIDGET (state->expression));
1095 state->label = go_gtk_builder_get_widget (gui, "label_entry");
1096 gtk_entry_set_text (GTK_ENTRY (state->label), swb->label);
1097 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
1098 gnm_editable_enters (GTK_WINDOW (state->dialog),
1099 GTK_WIDGET (state->expression));
1100 gnm_editable_enters (GTK_WINDOW (state->dialog),
1101 GTK_WIDGET (state->label));
1103 g_signal_connect (G_OBJECT (state->label),
1104 "changed",
1105 G_CALLBACK (cb_button_label_changed), state);
1106 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1107 "clicked",
1108 G_CALLBACK (cb_button_config_ok_clicked), state);
1109 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1110 "clicked",
1111 G_CALLBACK (cb_button_config_cancel_clicked), state);
1113 gnm_init_help_button (
1114 go_gtk_builder_get_widget (gui, "help_button"),
1115 GNUMERIC_HELP_LINK_SO_BUTTON);
1117 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1118 SHEET_OBJECT_CONFIG_KEY);
1120 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1121 g_object_set_data_full (G_OBJECT (state->dialog),
1122 "state", state, (GDestroyNotify) cb_button_config_destroy);
1124 /* Note: half of the set-focus action is handle by the default */
1125 /* callback installed by wbc_gtk_attach_guru */
1126 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1127 G_CALLBACK (cb_button_set_focus), state);
1128 g_object_unref (gui);
1130 gtk_widget_show (state->dialog);
1133 static gboolean
1134 sheet_widget_button_set_sheet (SheetObject *so, Sheet *sheet)
1136 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1138 dependent_set_sheet (&swb->dep, sheet);
1140 return FALSE;
1143 static void
1144 sheet_widget_button_foreach_dep (SheetObject *so,
1145 SheetObjectForeachDepFunc func,
1146 gpointer user)
1148 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1149 func (&swb->dep, so, user);
1152 static void
1153 sheet_widget_button_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1154 GnmConventions const *convs)
1156 /* FIXME: markup */
1157 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1158 gsf_xml_out_add_cstr (output, "Label", swb->label);
1159 gsf_xml_out_add_int (output, "Value", swb->value);
1160 sax_write_dep (output, &swb->dep, "Input", convs);
1163 static void
1164 sheet_widget_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1165 xmlChar const **attrs,
1166 GnmConventions const *convs)
1168 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1169 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1170 if (attr_eq (attrs[0], "Label"))
1171 g_object_set (G_OBJECT (swb), "text", attrs[1], NULL);
1172 else if (gnm_xml_attr_int (attrs, "Value", &swb->value))
1174 else if (sax_read_dep (attrs, "Input", &swb->dep, xin, convs))
1178 void
1179 sheet_widget_button_set_link (SheetObject *so, GnmExprTop const *texpr)
1181 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1182 dependent_set_expr (&swb->dep, texpr);
1183 if (texpr && swb->dep.sheet)
1184 dependent_link (&swb->dep);
1187 GnmExprTop const *
1188 sheet_widget_button_get_link (SheetObject *so)
1190 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1191 GnmExprTop const *texpr = swb->dep.texpr;
1193 if (texpr)
1194 gnm_expr_top_ref (texpr);
1196 return texpr;
1200 void
1201 sheet_widget_button_set_label (SheetObject *so, char const *str)
1203 GList *ptr;
1204 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1205 char *new_label;
1207 if (go_str_compare (str, swb->label) == 0)
1208 return;
1210 new_label = g_strdup (str);
1211 g_free (swb->label);
1212 swb->label = new_label;
1214 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1215 SheetObjectView *view = ptr->data;
1216 GocWidget *item = get_goc_widget (view);
1217 gtk_button_set_label (GTK_BUTTON (item->widget), swb->label);
1221 void
1222 sheet_widget_button_set_markup (SheetObject *so, PangoAttrList *markup)
1224 GList *ptr;
1225 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1227 if (markup == swb->markup)
1228 return;
1230 if (swb->markup) pango_attr_list_unref (swb->markup);
1231 swb->markup = markup;
1232 if (markup) pango_attr_list_ref (markup);
1234 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1235 SheetObjectView *view = ptr->data;
1236 GocWidget *item = get_goc_widget (view);
1237 GtkLabel *lab =
1238 GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->widget)));
1239 gtk_label_set_attributes (lab, swb->markup);
1243 static void
1244 sheet_widget_button_draw_cairo (SheetObject const *so, cairo_t *cr,
1245 double width, double height)
1247 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1248 int twidth, theight;
1249 double half_line;
1250 double radius = 10;
1252 if (height < 3 * radius)
1253 radius = height / 3.;
1254 if (width < 3 * radius)
1255 radius = width / 3.;
1256 if (radius < 1)
1257 radius = 1;
1258 half_line = radius * 0.15;
1260 cairo_save (cr);
1261 cairo_set_line_width (cr, 2 * half_line);
1262 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1264 cairo_new_path (cr);
1265 cairo_arc (cr, radius + half_line, radius + half_line, radius, M_PI, - M_PI/2);
1266 cairo_arc (cr, width - (radius + half_line), radius + half_line,
1267 radius, - M_PI/2, 0);
1268 cairo_arc (cr, width - (radius + half_line), height - (radius + half_line),
1269 radius, 0, M_PI/2);
1270 cairo_arc (cr, (radius + half_line), height - (radius + half_line),
1271 radius, M_PI/2, M_PI);
1272 cairo_close_path (cr);
1273 cairo_stroke (cr);
1275 cairo_set_source_rgb(cr, 0, 0, 0);
1277 cairo_move_to (cr, width/2., height/2.);
1279 twidth = 0.8 * width;
1280 theight = 0.8 * height;
1281 draw_cairo_text (cr, swb->label, &twidth, &theight, TRUE, TRUE, TRUE, 0, TRUE);
1283 cairo_new_path (cr);
1284 cairo_restore (cr);
1287 SOW_MAKE_TYPE (button, Button,
1288 sheet_widget_button_user_config,
1289 sheet_widget_button_set_sheet,
1290 so_clear_sheet,
1291 sheet_widget_button_foreach_dep,
1292 sheet_widget_button_copy,
1293 sheet_widget_button_write_xml_sax,
1294 sheet_widget_button_prep_sax_parser,
1295 sheet_widget_button_get_property,
1296 sheet_widget_button_set_property,
1297 sheet_widget_button_draw_cairo,
1299 g_object_class_install_property
1300 (object_class, SOB_PROP_TEXT,
1301 g_param_spec_string ("text", NULL, NULL, NULL,
1302 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1303 g_object_class_install_property
1304 (object_class, SOB_PROP_MARKUP,
1305 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
1306 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1309 /****************************************************************************/
1311 #define DEP_TO_ADJUSTMENT(d_ptr) (SheetWidgetAdjustment *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetAdjustment, dep))
1312 #define GNM_SOW_ADJUSTMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_ADJUSTMENT_TYPE, SheetWidgetAdjustmentClass))
1313 #define SWA_CLASS(so) (GNM_SOW_ADJUSTMENT_CLASS (G_OBJECT_GET_CLASS(so)))
1315 typedef struct {
1316 SheetObjectWidget sow;
1318 gboolean being_updated;
1319 GnmDependent dep;
1320 GtkAdjustment *adjustment;
1322 gboolean horizontal;
1323 } SheetWidgetAdjustment;
1325 typedef struct {
1326 SheetObjectWidgetClass parent_class;
1327 GType type;
1328 gboolean has_orientation;
1329 } SheetWidgetAdjustmentClass;
1331 enum {
1332 SWA_PROP_0 = 0,
1333 SWA_PROP_HORIZONTAL
1336 #ifndef g_signal_handlers_disconnect_by_data
1337 #define g_signal_handlers_disconnect_by_data(instance, data) \
1338 g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data))
1339 #endif
1340 static void
1341 cb_range_destroyed (GtkWidget *w, SheetWidgetAdjustment *swa)
1343 GObject *accessible = G_OBJECT (gtk_widget_get_accessible (w));
1344 if (accessible)
1345 g_signal_handlers_disconnect_by_data (swa->adjustment, accessible);
1348 static void
1349 sheet_widget_adjustment_set_value (SheetWidgetAdjustment *swa, double new_val)
1351 if (swa->being_updated)
1352 return;
1353 swa->being_updated = TRUE;
1354 gtk_adjustment_set_value (swa->adjustment, new_val);
1355 swa->being_updated = FALSE;
1359 * sheet_widget_adjustment_get_adjustment:
1360 * @so: #SheetObject
1362 * Returns: (transfer none): the associated #GtkAdjustment.
1364 GtkAdjustment *
1365 sheet_widget_adjustment_get_adjustment (SheetObject *so)
1367 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), NULL);
1368 return (GNM_SOW_ADJUSTMENT (so)->adjustment);
1371 gboolean
1372 sheet_widget_adjustment_get_horizontal (SheetObject *so)
1374 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), TRUE);
1375 return (GNM_SOW_ADJUSTMENT (so)->horizontal);
1378 void
1379 sheet_widget_adjustment_set_link (SheetObject *so, GnmExprTop const *texpr)
1381 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1382 dependent_set_expr (&swa->dep, texpr);
1383 if (texpr && swa->dep.sheet)
1384 dependent_link (&swa->dep);
1387 GnmExprTop const *
1388 sheet_widget_adjustment_get_link (SheetObject *so)
1390 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1391 GnmExprTop const *texpr = swa->dep.texpr;
1393 if (texpr)
1394 gnm_expr_top_ref (texpr);
1396 return texpr;
1400 static void
1401 adjustment_eval (GnmDependent *dep)
1403 GnmValue *v;
1404 GnmEvalPos pos;
1406 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
1407 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
1408 sheet_widget_adjustment_set_value (DEP_TO_ADJUSTMENT(dep),
1409 value_get_as_float (v));
1410 value_release (v);
1413 static void
1414 adjustment_debug_name (GnmDependent const *dep, GString *target)
1416 g_string_append_printf (target, "Adjustment%p", (void *)dep);
1419 static DEPENDENT_MAKE_TYPE (adjustment, NULL)
1421 static void
1422 cb_adjustment_widget_value_changed (GtkWidget *widget,
1423 SheetWidgetAdjustment *swa)
1425 GnmCellRef ref;
1427 if (swa->being_updated)
1428 return;
1430 if (so_get_ref (GNM_SO (swa), &ref, TRUE) != NULL) {
1431 GnmCell *cell = sheet_cell_fetch (ref.sheet, ref.col, ref.row);
1432 /* TODO : add more control for precision, XL is stupid */
1433 int new_val = gnm_fake_round (gtk_adjustment_get_value (swa->adjustment));
1434 if (cell->value != NULL &&
1435 VALUE_IS_FLOAT (cell->value) &&
1436 value_get_as_float (cell->value) == new_val)
1437 return;
1439 swa->being_updated = TRUE;
1440 cmd_so_set_value (widget_wbc (widget),
1441 /* FIXME: This text sucks: */
1442 _("Change widget"),
1443 &ref, value_new_int (new_val),
1444 sheet_object_get_sheet (GNM_SO (swa)));
1445 swa->being_updated = FALSE;
1449 void
1450 sheet_widget_adjustment_set_horizontal (SheetObject *so,
1451 gboolean horizontal)
1453 SheetWidgetAdjustment *swa = (SheetWidgetAdjustment *)so;
1454 GList *ptr;
1455 GtkOrientation o;
1457 if (!SWA_CLASS (swa)->has_orientation)
1458 return;
1459 horizontal = !!horizontal;
1460 if (horizontal == swa->horizontal)
1461 return;
1462 swa->horizontal = horizontal;
1463 o = horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1465 /* Change direction for all realized widgets. */
1466 for (ptr = swa->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1467 SheetObjectView *view = ptr->data;
1468 GocWidget *item = get_goc_widget (view);
1469 gtk_orientable_set_orientation (GTK_ORIENTABLE (item->widget), o);
1474 static void
1475 sheet_widget_adjustment_get_property (GObject *obj, guint param_id,
1476 GValue *value, GParamSpec *pspec)
1478 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1480 switch (param_id) {
1481 case SWA_PROP_HORIZONTAL:
1482 g_value_set_boolean (value, swa->horizontal);
1483 break;
1484 default:
1485 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1486 break;
1490 static void
1491 sheet_widget_adjustment_set_property (GObject *obj, guint param_id,
1492 GValue const *value, GParamSpec *pspec)
1494 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1496 switch (param_id) {
1497 case SWA_PROP_HORIZONTAL:
1498 sheet_widget_adjustment_set_horizontal (GNM_SO (swa), g_value_get_boolean (value));
1499 break;
1500 default:
1501 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1502 return;
1506 static void
1507 sheet_widget_adjustment_init_full (SheetWidgetAdjustment *swa,
1508 GnmCellRef const *ref,
1509 gboolean horizontal)
1511 SheetObject *so;
1512 g_return_if_fail (swa != NULL);
1514 so = GNM_SO (swa);
1515 so->flags &= ~SHEET_OBJECT_PRINT;
1517 swa->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0., 0., 100., 1., 10., 0.));
1518 g_object_ref_sink (swa->adjustment);
1520 swa->horizontal = horizontal;
1521 swa->being_updated = FALSE;
1522 swa->dep.sheet = NULL;
1523 swa->dep.flags = adjustment_get_dep_type ();
1524 swa->dep.texpr = (ref != NULL)
1525 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
1526 : NULL;
1529 static void
1530 sheet_widget_adjustment_init (SheetWidgetAdjustment *swa)
1532 sheet_widget_adjustment_init_full (swa, NULL, FALSE);
1535 static void
1536 sheet_widget_adjustment_finalize (GObject *obj)
1538 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1540 g_return_if_fail (swa != NULL);
1542 dependent_set_expr (&swa->dep, NULL);
1543 if (swa->adjustment != NULL) {
1544 g_object_unref (swa->adjustment);
1545 swa->adjustment = NULL;
1548 sheet_object_widget_class->finalize (obj);
1551 static void
1552 sheet_widget_adjustment_copy (SheetObject *dst, SheetObject const *src)
1554 SheetWidgetAdjustment const *src_swa = GNM_SOW_ADJUSTMENT (src);
1555 SheetWidgetAdjustment *dst_swa = GNM_SOW_ADJUSTMENT (dst);
1556 GtkAdjustment *dst_adjust, *src_adjust;
1557 GnmCellRef ref;
1559 sheet_widget_adjustment_init_full (dst_swa,
1560 so_get_ref (src, &ref, FALSE),
1561 src_swa->horizontal);
1562 dst_adjust = dst_swa->adjustment;
1563 src_adjust = src_swa->adjustment;
1565 gtk_adjustment_configure
1566 (dst_adjust,
1567 gtk_adjustment_get_value (src_adjust),
1568 gtk_adjustment_get_lower (src_adjust),
1569 gtk_adjustment_get_upper (src_adjust),
1570 gtk_adjustment_get_step_increment (src_adjust),
1571 gtk_adjustment_get_page_increment (src_adjust),
1572 gtk_adjustment_get_page_size (src_adjust));
1575 typedef struct {
1576 GtkWidget *dialog;
1577 GnmExprEntry *expression;
1578 GtkWidget *min;
1579 GtkWidget *max;
1580 GtkWidget *inc;
1581 GtkWidget *page;
1582 GtkWidget *direction_h;
1583 GtkWidget *direction_v;
1585 char *undo_label;
1586 GtkWidget *old_focus;
1588 WBCGtk *wbcg;
1589 SheetWidgetAdjustment *swa;
1590 Sheet *sheet;
1591 } AdjustmentConfigState;
1593 static void
1594 cb_adjustment_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
1595 AdjustmentConfigState *state)
1597 GtkWidget *ofp;
1599 /* Note: half of the set-focus action is handle by the default
1600 * callback installed by wbc_gtk_attach_guru. */
1602 ofp = state->old_focus
1603 ? gtk_widget_get_parent (state->old_focus)
1604 : NULL;
1605 /* Force an update of the content in case it needs tweaking (eg make it
1606 * absolute) */
1607 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
1608 GnmParsePos pp;
1609 GnmExprTop const *texpr = gnm_expr_entry_parse (
1610 GNM_EXPR_ENTRY (ofp),
1611 parse_pos_init_sheet (&pp, state->sheet),
1612 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1613 if (texpr != NULL)
1614 gnm_expr_top_unref (texpr);
1616 state->old_focus = focus_widget;
1619 static void
1620 cb_adjustment_config_destroy (AdjustmentConfigState *state)
1622 g_return_if_fail (state != NULL);
1624 g_free (state->undo_label);
1626 state->dialog = NULL;
1627 g_free (state);
1630 static void
1631 cb_adjustment_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1633 SheetObject *so = GNM_SO (state->swa);
1634 GnmParsePos pp;
1635 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1636 parse_pos_init_sheet (&pp, so->sheet),
1637 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1638 gboolean horizontal;
1640 horizontal = state->direction_h
1641 ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state->direction_h))
1642 : state->swa->horizontal;
1644 cmd_so_set_adjustment (GNM_WBC (state->wbcg), so,
1645 texpr,
1646 horizontal,
1647 gtk_spin_button_get_value_as_int (
1648 GTK_SPIN_BUTTON (state->min)),
1649 gtk_spin_button_get_value_as_int (
1650 GTK_SPIN_BUTTON (state->max)),
1651 gtk_spin_button_get_value_as_int (
1652 GTK_SPIN_BUTTON (state->inc)),
1653 gtk_spin_button_get_value_as_int (
1654 GTK_SPIN_BUTTON (state->page)),
1655 state->undo_label);
1657 gtk_widget_destroy (state->dialog);
1660 static void
1661 cb_adjustment_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1663 gtk_widget_destroy (state->dialog);
1666 static void
1667 sheet_widget_adjustment_user_config_impl (SheetObject *so, SheetControl *sc, char const *undo_label, char const *dialog_label)
1669 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1670 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (swa);
1671 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1672 AdjustmentConfigState *state;
1673 GtkWidget *grid;
1674 GtkBuilder *gui;
1675 gboolean has_directions = swa_class->has_orientation;
1677 /* Only pop up one copy per workbook */
1678 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1679 return;
1681 gui = gnm_gtk_builder_load ("res:ui/so-scrollbar.ui", NULL, GO_CMD_CONTEXT (wbcg));
1682 if (!gui)
1683 return;
1684 state = g_new (AdjustmentConfigState, 1);
1685 state->swa = swa;
1686 state->wbcg = wbcg;
1687 state->sheet = sc_sheet (sc);
1688 state->old_focus = NULL;
1689 state->undo_label = (undo_label == NULL) ? NULL : g_strdup (undo_label);
1690 state->dialog = go_gtk_builder_get_widget (gui, "SO-Scrollbar");
1692 if (dialog_label != NULL)
1693 gtk_window_set_title (GTK_WINDOW (state->dialog), dialog_label);
1695 grid = go_gtk_builder_get_widget (gui, "main-grid");
1697 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1698 gnm_expr_entry_set_flags (state->expression,
1699 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1700 GNM_EE_MASK);
1701 gnm_expr_entry_load_from_dep (state->expression, &swa->dep);
1702 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1703 GTK_WIDGET (state->expression));
1704 gtk_grid_attach (GTK_GRID (grid),
1705 GTK_WIDGET (state->expression), 1, 0, 2, 1);
1706 gtk_widget_show (GTK_WIDGET (state->expression));
1708 if (has_directions) {
1709 state->direction_h = go_gtk_builder_get_widget (gui, "direction_h");
1710 state->direction_v = go_gtk_builder_get_widget (gui, "direction_v");
1711 gtk_toggle_button_set_active
1712 (GTK_TOGGLE_BUTTON (swa->horizontal
1713 ? state->direction_h
1714 : state->direction_v),
1715 TRUE);
1716 } else {
1717 state->direction_h = NULL;
1718 state->direction_v = NULL;
1719 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_label"));
1720 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_h"));
1721 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_v"));
1724 /* TODO : This is silly, no need to be similar to XL here. */
1725 state->min = go_gtk_builder_get_widget (gui, "spin_min");
1726 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->min),
1727 gtk_adjustment_get_lower (swa->adjustment));
1728 state->max = go_gtk_builder_get_widget (gui, "spin_max");
1729 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max),
1730 gtk_adjustment_get_upper (swa->adjustment));
1731 state->inc = go_gtk_builder_get_widget (gui, "spin_increment");
1732 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->inc),
1733 gtk_adjustment_get_step_increment (swa->adjustment));
1734 state->page = go_gtk_builder_get_widget (gui, "spin_page");
1735 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->page),
1736 gtk_adjustment_get_page_increment (swa->adjustment));
1738 gnm_editable_enters (GTK_WINDOW (state->dialog),
1739 GTK_WIDGET (state->expression));
1740 gnm_editable_enters (GTK_WINDOW (state->dialog),
1741 GTK_WIDGET (state->min));
1742 gnm_editable_enters (GTK_WINDOW (state->dialog),
1743 GTK_WIDGET (state->max));
1744 gnm_editable_enters (GTK_WINDOW (state->dialog),
1745 GTK_WIDGET (state->inc));
1746 gnm_editable_enters (GTK_WINDOW (state->dialog),
1747 GTK_WIDGET (state->page));
1748 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1749 "clicked",
1750 G_CALLBACK (cb_adjustment_config_ok_clicked), state);
1751 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1752 "clicked",
1753 G_CALLBACK (cb_adjustment_config_cancel_clicked), state);
1755 gnm_init_help_button (
1756 go_gtk_builder_get_widget (gui, "help_button"),
1757 GNUMERIC_HELP_LINK_SO_ADJUSTMENT);
1759 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1760 SHEET_OBJECT_CONFIG_KEY);
1762 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1763 g_object_set_data_full (G_OBJECT (state->dialog),
1764 "state", state, (GDestroyNotify) cb_adjustment_config_destroy);
1766 /* Note: half of the set-focus action is handle by the default */
1767 /* callback installed by wbc_gtk_attach_guru */
1768 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1769 G_CALLBACK (cb_adjustment_set_focus), state);
1770 g_object_unref (gui);
1772 gtk_widget_show (state->dialog);
1775 static void
1776 sheet_widget_adjustment_user_config (SheetObject *so, SheetControl *sc)
1778 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Adjustment"),
1779 N_("Adjustment Properties"));
1782 static gboolean
1783 sheet_widget_adjustment_set_sheet (SheetObject *so, Sheet *sheet)
1785 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1787 dependent_set_sheet (&swa->dep, sheet);
1789 return FALSE;
1792 static void
1793 sheet_widget_adjustment_foreach_dep (SheetObject *so,
1794 SheetObjectForeachDepFunc func,
1795 gpointer user)
1797 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1798 func (&swa->dep, so, user);
1801 static void
1802 sheet_widget_adjustment_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1803 GnmConventions const *convs)
1805 SheetWidgetAdjustment const *swa = GNM_SOW_ADJUSTMENT (so);
1806 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1808 go_xml_out_add_double (output, "Min", gtk_adjustment_get_lower (swa->adjustment));
1809 go_xml_out_add_double (output, "Max", gtk_adjustment_get_upper (swa->adjustment));
1810 go_xml_out_add_double (output, "Inc", gtk_adjustment_get_step_increment (swa->adjustment));
1811 go_xml_out_add_double (output, "Page", gtk_adjustment_get_page_increment (swa->adjustment));
1812 go_xml_out_add_double (output, "Value", gtk_adjustment_get_value (swa->adjustment));
1814 if (swa_class->has_orientation)
1815 gsf_xml_out_add_bool (output, "Horizontal", swa->horizontal);
1817 sax_write_dep (output, &swa->dep, "Input", convs);
1820 static void
1821 sheet_widget_adjustment_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1822 xmlChar const **attrs,
1823 GnmConventions const *convs)
1825 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1826 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1827 swa->horizontal = FALSE;
1829 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1830 double tmp;
1831 gboolean b;
1833 if (gnm_xml_attr_double (attrs, "Min", &tmp))
1834 gtk_adjustment_set_lower (swa->adjustment, tmp);
1835 else if (gnm_xml_attr_double (attrs, "Max", &tmp))
1836 gtk_adjustment_set_upper (swa->adjustment, tmp); /* allow scrolling to max */
1837 else if (gnm_xml_attr_double (attrs, "Inc", &tmp))
1838 gtk_adjustment_set_step_increment (swa->adjustment, tmp);
1839 else if (gnm_xml_attr_double (attrs, "Page", &tmp))
1840 gtk_adjustment_set_page_increment (swa->adjustment, tmp);
1841 else if (gnm_xml_attr_double (attrs, "Value", &tmp))
1842 gtk_adjustment_set_value (swa->adjustment, tmp);
1843 else if (sax_read_dep (attrs, "Input", &swa->dep, xin, convs))
1845 else if (swa_class->has_orientation &&
1846 gnm_xml_attr_bool (attrs, "Horizontal", &b))
1847 swa->horizontal = b;
1850 swa->dep.flags = adjustment_get_dep_type ();
1853 void
1854 sheet_widget_adjustment_set_details (SheetObject *so, GnmExprTop const *tlink,
1855 int value, int min, int max,
1856 int inc, int page)
1858 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1859 double page_size;
1861 g_return_if_fail (swa != NULL);
1863 dependent_set_expr (&swa->dep, tlink);
1864 if (tlink && swa->dep.sheet)
1865 dependent_link (&swa->dep);
1867 page_size = gtk_adjustment_get_page_size (swa->adjustment); /* ??? */
1868 gtk_adjustment_configure (swa->adjustment,
1869 value, min, max, inc, page, page_size);
1872 static GtkWidget *
1873 sheet_widget_adjustment_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
1875 g_assert_not_reached ();
1876 return NULL;
1879 SOW_MAKE_TYPE (adjustment, Adjustment,
1880 sheet_widget_adjustment_user_config,
1881 sheet_widget_adjustment_set_sheet,
1882 so_clear_sheet,
1883 sheet_widget_adjustment_foreach_dep,
1884 sheet_widget_adjustment_copy,
1885 sheet_widget_adjustment_write_xml_sax,
1886 sheet_widget_adjustment_prep_sax_parser,
1887 sheet_widget_adjustment_get_property,
1888 sheet_widget_adjustment_set_property,
1889 sheet_widget_draw_cairo,
1891 ((SheetWidgetAdjustmentClass *) object_class)->has_orientation = TRUE;
1892 g_object_class_install_property
1893 (object_class, SWA_PROP_HORIZONTAL,
1894 g_param_spec_boolean ("horizontal", NULL, NULL,
1895 FALSE,
1896 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1899 /****************************************************************************/
1901 #define GNM_SOW_SCROLLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SCROLLBAR_TYPE, SheetWidgetScrollbar))
1902 #define DEP_TO_SCROLLBAR(d_ptr) (SheetWidgetScrollbar *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetScrollbar, dep))
1904 typedef SheetWidgetAdjustment SheetWidgetScrollbar;
1905 typedef SheetWidgetAdjustmentClass SheetWidgetScrollbarClass;
1907 static GtkWidget *
1908 sheet_widget_scrollbar_create_widget (SheetObjectWidget *sow)
1910 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
1911 GtkWidget *bar;
1913 swa->being_updated = TRUE;
1914 bar = gtk_scrollbar_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
1915 gtk_widget_set_can_focus (bar, FALSE);
1916 g_signal_connect (G_OBJECT (bar),
1917 "value_changed",
1918 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
1919 g_signal_connect (G_OBJECT (bar), "destroy",
1920 G_CALLBACK (cb_range_destroyed), swa);
1921 swa->being_updated = FALSE;
1923 return bar;
1926 static void
1927 sheet_widget_scrollbar_user_config (SheetObject *so, SheetControl *sc)
1929 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Scrollbar"),
1930 N_("Scrollbar Properties"));
1933 static void sheet_widget_slider_horizontal_draw_cairo
1934 (SheetObject const *so, cairo_t *cr, double width, double height);
1936 static void
1937 sheet_widget_scrollbar_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
1938 double width, double height)
1940 cairo_save (cr);
1941 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1943 cairo_new_path (cr);
1944 cairo_move_to (cr, 0., height/2);
1945 cairo_rel_line_to (cr, 15., 7.5);
1946 cairo_rel_line_to (cr, 0, -15);
1947 cairo_close_path (cr);
1948 cairo_fill (cr);
1950 cairo_new_path (cr);
1951 cairo_move_to (cr, width, height/2);
1952 cairo_rel_line_to (cr, -15., 7.5);
1953 cairo_rel_line_to (cr, 0, -15);
1954 cairo_close_path (cr);
1955 cairo_fill (cr);
1957 cairo_new_path (cr);
1958 cairo_translate (cr, 15., 0.);
1959 sheet_widget_slider_horizontal_draw_cairo (so, cr, width - 30, height);
1960 cairo_restore (cr);
1963 static void
1964 sheet_widget_scrollbar_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
1965 double width, double height)
1967 cairo_save (cr);
1968 cairo_rotate (cr, M_PI/2);
1969 cairo_translate (cr, 0., -width);
1970 sheet_widget_scrollbar_horizontal_draw_cairo (so, cr, height, width);
1971 cairo_restore (cr);
1974 static void
1975 sheet_widget_scrollbar_draw_cairo (SheetObject const *so, cairo_t *cr,
1976 double width, double height)
1978 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1979 if (swa->horizontal)
1980 sheet_widget_scrollbar_horizontal_draw_cairo
1981 (so, cr, width, height);
1982 else
1983 sheet_widget_scrollbar_vertical_draw_cairo
1984 (so, cr, width, height);
1987 static void
1988 sheet_widget_scrollbar_class_init (SheetObjectWidgetClass *sow_class)
1990 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
1991 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
1993 sow_class->create_widget = &sheet_widget_scrollbar_create_widget;
1994 so_class->user_config = &sheet_widget_scrollbar_user_config;
1995 so_class->draw_cairo = &sheet_widget_scrollbar_draw_cairo;
1996 swa_class->type = GTK_TYPE_SCROLLBAR;
1999 GSF_CLASS (SheetWidgetScrollbar, sheet_widget_scrollbar,
2000 &sheet_widget_scrollbar_class_init, NULL,
2001 GNM_SOW_ADJUSTMENT_TYPE)
2003 /****************************************************************************/
2005 #define GNM_SOW_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SPIN_BUTTON_TYPE, SheetWidgetSpinbutton))
2006 #define DEP_TO_SPINBUTTON(d_ptr) (SheetWidgetSpinbutton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSpinbutton, dep))
2008 typedef SheetWidgetAdjustment SheetWidgetSpinbutton;
2009 typedef SheetWidgetAdjustmentClass SheetWidgetSpinbuttonClass;
2011 static GtkWidget *
2012 sheet_widget_spinbutton_create_widget (SheetObjectWidget *sow)
2014 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2015 GtkWidget *spinbutton;
2017 swa->being_updated = TRUE;
2018 spinbutton = gtk_spin_button_new
2019 (swa->adjustment,
2020 gtk_adjustment_get_step_increment (swa->adjustment),
2022 gtk_widget_set_can_focus (spinbutton, FALSE);
2023 g_signal_connect (G_OBJECT (spinbutton),
2024 "value_changed",
2025 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2026 g_signal_connect (G_OBJECT (spinbutton), "destroy",
2027 G_CALLBACK (cb_range_destroyed), swa);
2028 swa->being_updated = FALSE;
2029 return spinbutton;
2032 static void
2033 sheet_widget_spinbutton_user_config (SheetObject *so, SheetControl *sc)
2035 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Spinbutton"),
2036 N_("Spinbutton Properties"));
2039 static void
2040 sheet_widget_spinbutton_draw_cairo (SheetObject const *so, cairo_t *cr,
2041 double width, double height)
2043 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2044 GtkAdjustment *adjustment = swa->adjustment;
2045 double value = gtk_adjustment_get_value (adjustment);
2046 int ivalue = (int) value;
2047 double halfheight = height/2;
2048 char *str;
2050 cairo_save (cr);
2051 cairo_set_line_width (cr, 0.5);
2052 cairo_set_source_rgb(cr, 0, 0, 0);
2054 cairo_new_path (cr);
2055 cairo_move_to (cr, 0, 0);
2056 cairo_line_to (cr, width, 0);
2057 cairo_line_to (cr, width, height);
2058 cairo_line_to (cr, 0, height);
2059 cairo_close_path (cr);
2060 cairo_stroke (cr);
2062 cairo_new_path (cr);
2063 cairo_move_to (cr, width - 10, 0);
2064 cairo_rel_line_to (cr, 0, height);
2065 cairo_stroke (cr);
2067 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2069 cairo_new_path (cr);
2070 cairo_move_to (cr, width - 5, 3);
2071 cairo_rel_line_to (cr, 3, 3);
2072 cairo_rel_line_to (cr, -6, 0);
2073 cairo_close_path (cr);
2074 cairo_fill (cr);
2076 cairo_new_path (cr);
2077 cairo_move_to (cr, width - 5, height - 3);
2078 cairo_rel_line_to (cr, 3, -3);
2079 cairo_rel_line_to (cr, -6, 0);
2080 cairo_close_path (cr);
2081 cairo_fill (cr);
2083 str = g_strdup_printf ("%i", ivalue);
2084 cairo_set_source_rgb(cr, 0, 0, 0);
2085 cairo_move_to (cr, 4., halfheight);
2086 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
2087 g_free (str);
2089 cairo_new_path (cr);
2090 cairo_restore (cr);
2093 static void
2094 sheet_widget_spinbutton_class_init (SheetObjectWidgetClass *sow_class)
2096 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2097 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2099 sow_class->create_widget = &sheet_widget_spinbutton_create_widget;
2100 so_class->user_config = &sheet_widget_spinbutton_user_config;
2101 so_class->draw_cairo = &sheet_widget_spinbutton_draw_cairo;
2103 swa_class->type = GTK_TYPE_SPIN_BUTTON;
2104 swa_class->has_orientation = FALSE;
2107 GSF_CLASS (SheetWidgetSpinbutton, sheet_widget_spinbutton,
2108 &sheet_widget_spinbutton_class_init, NULL,
2109 GNM_SOW_ADJUSTMENT_TYPE)
2111 /****************************************************************************/
2113 #define GNM_SOW_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SLIDER_TYPE, SheetWidgetSlider))
2114 #define DEP_TO_SLIDER(d_ptr) (SheetWidgetSlider *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSlider, dep))
2116 typedef SheetWidgetAdjustment SheetWidgetSlider;
2117 typedef SheetWidgetAdjustmentClass SheetWidgetSliderClass;
2119 static GtkWidget *
2120 sheet_widget_slider_create_widget (SheetObjectWidget *sow)
2122 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2123 GtkWidget *slider;
2125 swa->being_updated = TRUE;
2126 slider = gtk_scale_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
2127 gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
2128 gtk_widget_set_can_focus (slider, FALSE);
2129 g_signal_connect (G_OBJECT (slider),
2130 "value_changed",
2131 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2132 g_signal_connect (G_OBJECT (slider), "destroy",
2133 G_CALLBACK (cb_range_destroyed), swa);
2134 swa->being_updated = FALSE;
2136 return slider;
2139 static void
2140 sheet_widget_slider_user_config (SheetObject *so, SheetControl *sc)
2142 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Slider"),
2143 N_("Slider Properties"));
2146 static void
2147 sheet_widget_slider_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
2148 double width, double height)
2150 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2151 GtkAdjustment *adjustment = swa->adjustment;
2152 double value = gtk_adjustment_get_value (adjustment);
2153 double upper = gtk_adjustment_get_upper (adjustment);
2154 double lower = gtk_adjustment_get_lower (adjustment);
2155 double fraction = (upper == lower) ? 0.0 : (value - lower)/(upper- lower);
2157 cairo_save (cr);
2158 cairo_set_line_width (cr, 5);
2159 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
2160 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2162 cairo_new_path (cr);
2163 cairo_move_to (cr, 4, height/2);
2164 cairo_rel_line_to (cr, width - 8., 0);
2165 cairo_stroke (cr);
2167 cairo_set_line_width (cr, 15);
2168 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2169 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2171 cairo_new_path (cr);
2172 cairo_move_to (cr, fraction * (width - 8. - 1. - 5. - 5. + 2.5 + 2.5)
2173 - 10. + 10. + 4. + 5. - 2.5, height/2);
2174 cairo_rel_line_to (cr, 1, 0);
2175 cairo_stroke (cr);
2177 cairo_new_path (cr);
2178 cairo_restore (cr);
2181 static void
2182 sheet_widget_slider_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
2183 double width, double height)
2185 cairo_save (cr);
2186 cairo_rotate (cr, M_PI/2);
2187 cairo_translate (cr, 0., -width);
2188 sheet_widget_slider_horizontal_draw_cairo (so, cr, height, width);
2189 cairo_restore (cr);
2192 static void
2193 sheet_widget_slider_draw_cairo (SheetObject const *so, cairo_t *cr,
2194 double width, double height)
2196 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2197 if (swa->horizontal)
2198 sheet_widget_slider_horizontal_draw_cairo (so, cr, width, height);
2199 else
2200 sheet_widget_slider_vertical_draw_cairo (so, cr, width, height);
2203 static void
2204 sheet_widget_slider_class_init (SheetObjectWidgetClass *sow_class)
2206 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2207 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2209 sow_class->create_widget = &sheet_widget_slider_create_widget;
2210 so_class->user_config = &sheet_widget_slider_user_config;
2211 so_class->draw_cairo = &sheet_widget_slider_draw_cairo;
2213 swa_class->type = GTK_TYPE_SCALE;
2216 GSF_CLASS (SheetWidgetSlider, sheet_widget_slider,
2217 &sheet_widget_slider_class_init, NULL,
2218 GNM_SOW_ADJUSTMENT_TYPE)
2220 /****************************************************************************/
2222 #define GNM_SOW_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_CHECKBOX_TYPE, SheetWidgetCheckbox))
2223 #define DEP_TO_CHECKBOX(d_ptr) (SheetWidgetCheckbox *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetCheckbox, dep))
2225 typedef struct {
2226 SheetObjectWidget sow;
2228 GnmDependent dep;
2229 char *label;
2230 gboolean value;
2231 gboolean being_updated;
2232 } SheetWidgetCheckbox;
2233 typedef SheetObjectWidgetClass SheetWidgetCheckboxClass;
2235 enum {
2236 SOC_PROP_0 = 0,
2237 SOC_PROP_ACTIVE,
2238 SOC_PROP_TEXT,
2239 SOC_PROP_MARKUP
2242 static void
2243 sheet_widget_checkbox_set_active (SheetWidgetCheckbox *swc)
2245 GList *ptr;
2247 swc->being_updated = TRUE;
2249 for (ptr = swc->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2250 SheetObjectView *view = ptr->data;
2251 GocWidget *item = get_goc_widget (view);
2252 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2253 swc->value);
2256 g_object_notify (G_OBJECT (swc), "active");
2258 swc->being_updated = FALSE;
2261 static void
2262 sheet_widget_checkbox_get_property (GObject *obj, guint param_id,
2263 GValue *value, GParamSpec *pspec)
2265 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2267 switch (param_id) {
2268 case SOC_PROP_ACTIVE:
2269 g_value_set_boolean (value, swc->value);
2270 break;
2271 case SOC_PROP_TEXT:
2272 g_value_set_string (value, swc->label);
2273 break;
2274 case SOC_PROP_MARKUP:
2275 g_value_set_boxed (value, NULL); /* swc->markup */
2276 break;
2277 default:
2278 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2279 break;
2283 static void
2284 sheet_widget_checkbox_set_property (GObject *obj, guint param_id,
2285 GValue const *value, GParamSpec *pspec)
2287 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2289 switch (param_id) {
2290 case SOC_PROP_ACTIVE:
2291 swc->value = g_value_get_boolean (value);
2292 sheet_widget_checkbox_set_active (swc);
2293 break;
2294 case SOC_PROP_TEXT:
2295 sheet_widget_checkbox_set_label (GNM_SO (swc),
2296 g_value_get_string (value));
2297 break;
2298 case SOC_PROP_MARKUP:
2299 #if 0
2300 sheet_widget_checkbox_set_markup (GNM_SO (swc),
2301 g_value_peek_pointer (value));
2302 #endif
2303 break;
2304 default:
2305 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2306 return;
2310 static void
2311 checkbox_eval (GnmDependent *dep)
2313 GnmValue *v;
2314 GnmEvalPos pos;
2315 gboolean err, result;
2317 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2318 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2319 result = value_get_as_bool (v, &err);
2320 value_release (v);
2321 if (!err) {
2322 SheetWidgetCheckbox *swc = DEP_TO_CHECKBOX(dep);
2324 swc->value = result;
2325 sheet_widget_checkbox_set_active (swc);
2329 static void
2330 checkbox_debug_name (GnmDependent const *dep, GString *target)
2332 g_string_append_printf (target, "Checkbox%p", (void *)dep);
2335 static DEPENDENT_MAKE_TYPE (checkbox, NULL)
2337 static void
2338 sheet_widget_checkbox_init_full (SheetWidgetCheckbox *swc,
2339 GnmCellRef const *ref, char const *label)
2341 static int counter = 0;
2343 g_return_if_fail (swc != NULL);
2345 swc->label = label ? g_strdup (label) : g_strdup_printf (_("CheckBox %d"), ++counter);
2346 swc->being_updated = FALSE;
2347 swc->value = FALSE;
2348 swc->dep.sheet = NULL;
2349 swc->dep.flags = checkbox_get_dep_type ();
2350 swc->dep.texpr = (ref != NULL)
2351 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2352 : NULL;
2355 static void
2356 sheet_widget_checkbox_init (SheetWidgetCheckbox *swc)
2358 sheet_widget_checkbox_init_full (swc, NULL, NULL);
2361 static void
2362 sheet_widget_checkbox_finalize (GObject *obj)
2364 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2366 g_return_if_fail (swc != NULL);
2368 g_free (swc->label);
2369 swc->label = NULL;
2371 dependent_set_expr (&swc->dep, NULL);
2373 sheet_object_widget_class->finalize (obj);
2376 static void
2377 cb_checkbox_toggled (GtkToggleButton *button, SheetWidgetCheckbox *swc)
2379 GnmCellRef ref;
2381 if (swc->being_updated)
2382 return;
2383 swc->value = gtk_toggle_button_get_active (button);
2384 sheet_widget_checkbox_set_active (swc);
2386 if (so_get_ref (GNM_SO (swc), &ref, TRUE) != NULL) {
2387 gboolean new_val = gtk_toggle_button_get_active (button);
2388 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2389 /* FIXME: This text sucks: */
2390 _("Clicking checkbox"),
2391 &ref, value_new_bool (new_val),
2392 sheet_object_get_sheet (GNM_SO (swc)));
2396 static GtkWidget *
2397 sheet_widget_checkbox_create_widget (SheetObjectWidget *sow)
2399 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2400 GtkWidget *button;
2402 g_return_val_if_fail (swc != NULL, NULL);
2404 button = gtk_check_button_new_with_label (swc->label);
2405 gtk_widget_set_can_focus (button, FALSE);
2406 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2407 g_signal_connect (G_OBJECT (button),
2408 "toggled",
2409 G_CALLBACK (cb_checkbox_toggled), swc);
2411 return button;
2414 static void
2415 sheet_widget_checkbox_copy (SheetObject *dst, SheetObject const *src)
2417 SheetWidgetCheckbox const *src_swc = GNM_SOW_CHECKBOX (src);
2418 SheetWidgetCheckbox *dst_swc = GNM_SOW_CHECKBOX (dst);
2419 GnmCellRef ref;
2420 sheet_widget_checkbox_init_full (dst_swc,
2421 so_get_ref (src, &ref, FALSE),
2422 src_swc->label);
2423 dst_swc->value = src_swc->value;
2426 typedef struct {
2427 GtkWidget *dialog;
2428 GnmExprEntry *expression;
2429 GtkWidget *label;
2431 char *old_label;
2432 GtkWidget *old_focus;
2434 WBCGtk *wbcg;
2435 SheetWidgetCheckbox *swc;
2436 Sheet *sheet;
2437 } CheckboxConfigState;
2439 static void
2440 cb_checkbox_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
2441 CheckboxConfigState *state)
2443 GtkWidget *ofp;
2445 /* Note: half of the set-focus action is handle by the default
2446 * callback installed by wbc_gtk_attach_guru. */
2448 ofp = state->old_focus
2449 ? gtk_widget_get_parent (state->old_focus)
2450 : NULL;
2452 /* Force an update of the content in case it needs tweaking (eg make it
2453 * absolute) */
2454 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
2455 GnmParsePos pp;
2456 GnmExprTop const *texpr = gnm_expr_entry_parse (
2457 GNM_EXPR_ENTRY (ofp),
2458 parse_pos_init_sheet (&pp, state->sheet),
2459 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2460 if (texpr != NULL)
2461 gnm_expr_top_unref (texpr);
2463 state->old_focus = focus_widget;
2466 static void
2467 cb_checkbox_config_destroy (CheckboxConfigState *state)
2469 g_return_if_fail (state != NULL);
2471 g_free (state->old_label);
2472 state->old_label = NULL;
2473 state->dialog = NULL;
2474 g_free (state);
2477 static void
2478 cb_checkbox_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2480 SheetObject *so = GNM_SO (state->swc);
2481 GnmParsePos pp;
2482 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
2483 parse_pos_init_sheet (&pp, so->sheet),
2484 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2485 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
2487 cmd_so_set_checkbox (GNM_WBC (state->wbcg), so,
2488 texpr, g_strdup (state->old_label), g_strdup (text));
2490 gtk_widget_destroy (state->dialog);
2493 static void
2494 cb_checkbox_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2496 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2497 state->old_label);
2498 gtk_widget_destroy (state->dialog);
2501 static void
2502 cb_checkbox_label_changed (GtkEntry *entry, CheckboxConfigState *state)
2504 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2505 gtk_entry_get_text (entry));
2508 static void
2509 sheet_widget_checkbox_user_config (SheetObject *so, SheetControl *sc)
2511 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2512 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
2513 CheckboxConfigState *state;
2514 GtkWidget *grid;
2515 GtkBuilder *gui;
2517 g_return_if_fail (swc != NULL);
2519 /* Only pop up one copy per workbook */
2520 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
2521 return;
2523 gui = gnm_gtk_builder_load ("res:ui/so-checkbox.ui", NULL, GO_CMD_CONTEXT (wbcg));
2524 if (!gui)
2525 return;
2526 state = g_new (CheckboxConfigState, 1);
2527 state->swc = swc;
2528 state->wbcg = wbcg;
2529 state->sheet = sc_sheet (sc);
2530 state->old_focus = NULL;
2531 state->old_label = g_strdup (swc->label);
2532 state->dialog = go_gtk_builder_get_widget (gui, "SO-Checkbox");
2534 grid = go_gtk_builder_get_widget (gui, "main-grid");
2536 state->expression = gnm_expr_entry_new (wbcg, TRUE);
2537 gnm_expr_entry_set_flags (state->expression,
2538 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
2539 GNM_EE_MASK);
2540 gnm_expr_entry_load_from_dep (state->expression, &swc->dep);
2541 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
2542 GTK_WIDGET (state->expression));
2543 gtk_grid_attach (GTK_GRID (grid),
2544 GTK_WIDGET (state->expression), 1, 0, 1, 1);
2545 gtk_widget_show (GTK_WIDGET (state->expression));
2547 state->label = go_gtk_builder_get_widget (gui, "label_entry");
2548 gtk_entry_set_text (GTK_ENTRY (state->label), swc->label);
2549 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
2550 gnm_editable_enters (GTK_WINDOW (state->dialog),
2551 GTK_WIDGET (state->expression));
2552 gnm_editable_enters (GTK_WINDOW (state->dialog),
2553 GTK_WIDGET (state->label));
2555 g_signal_connect (G_OBJECT (state->label),
2556 "changed",
2557 G_CALLBACK (cb_checkbox_label_changed), state);
2558 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
2559 "clicked",
2560 G_CALLBACK (cb_checkbox_config_ok_clicked), state);
2561 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
2562 "clicked",
2563 G_CALLBACK (cb_checkbox_config_cancel_clicked), state);
2565 gnm_init_help_button (
2566 go_gtk_builder_get_widget (gui, "help_button"),
2567 GNUMERIC_HELP_LINK_SO_CHECKBOX);
2569 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
2570 SHEET_OBJECT_CONFIG_KEY);
2572 wbc_gtk_attach_guru (state->wbcg, state->dialog);
2573 g_object_set_data_full (G_OBJECT (state->dialog),
2574 "state", state, (GDestroyNotify) cb_checkbox_config_destroy);
2576 /* Note: half of the set-focus action is handle by the default */
2577 /* callback installed by wbc_gtk_attach_guru */
2578 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
2579 G_CALLBACK (cb_checkbox_set_focus), state);
2580 g_object_unref (gui);
2582 gtk_widget_show (state->dialog);
2585 static gboolean
2586 sheet_widget_checkbox_set_sheet (SheetObject *so, Sheet *sheet)
2588 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2590 dependent_set_sheet (&swc->dep, sheet);
2591 sheet_widget_checkbox_set_active (swc);
2593 return FALSE;
2596 static void
2597 sheet_widget_checkbox_foreach_dep (SheetObject *so,
2598 SheetObjectForeachDepFunc func,
2599 gpointer user)
2601 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2602 func (&swc->dep, so, user);
2605 static void
2606 sheet_widget_checkbox_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
2607 GnmConventions const *convs)
2609 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2610 gsf_xml_out_add_cstr (output, "Label", swc->label);
2611 gsf_xml_out_add_int (output, "Value", swc->value);
2612 sax_write_dep (output, &swc->dep, "Input", convs);
2615 static void
2616 sheet_widget_checkbox_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
2617 xmlChar const **attrs,
2618 GnmConventions const *convs)
2620 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2622 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2623 if (attr_eq (attrs[0], "Label")) {
2624 g_free (swc->label);
2625 swc->label = g_strdup (CXML2C (attrs[1]));
2626 } else if (gnm_xml_attr_int (attrs, "Value", &swc->value))
2627 ; /* ??? */
2628 else if (sax_read_dep (attrs, "Input", &swc->dep, xin, convs))
2629 ; /* ??? */
2632 void
2633 sheet_widget_checkbox_set_link (SheetObject *so, GnmExprTop const *texpr)
2635 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2636 dependent_set_expr (&swc->dep, texpr);
2637 if (texpr && swc->dep.sheet)
2638 dependent_link (&swc->dep);
2641 GnmExprTop const *
2642 sheet_widget_checkbox_get_link (SheetObject *so)
2644 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2645 GnmExprTop const *texpr = swc->dep.texpr;
2647 if (texpr)
2648 gnm_expr_top_ref (texpr);
2650 return texpr;
2654 void
2655 sheet_widget_checkbox_set_label (SheetObject *so, char const *str)
2657 GList *list;
2658 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2659 char *new_label;
2661 if (go_str_compare (str, swc->label) == 0)
2662 return;
2664 new_label = g_strdup (str);
2665 g_free (swc->label);
2666 swc->label = new_label;
2668 for (list = swc->sow.so.realized_list; list; list = list->next) {
2669 SheetObjectView *view = list->data;
2670 GocWidget *item = get_goc_widget (view);
2671 gtk_button_set_label (GTK_BUTTON (item->widget), swc->label);
2675 static void
2676 sheet_widget_checkbox_draw_cairo (SheetObject const *so, cairo_t *cr,
2677 double width, double height)
2679 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2680 double halfheight = height/2;
2681 double dx = 8., dxh, pm;
2682 int pw, ph;
2684 pm = MIN (height - 2, width - 12);
2685 if (dx > pm)
2686 dx = MAX (pm, 3);
2687 dxh = dx/2;
2689 cairo_save (cr);
2690 cairo_set_line_width (cr, 0.5);
2691 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
2693 cairo_new_path (cr);
2694 cairo_move_to (cr, dxh, halfheight - dxh);
2695 cairo_rel_line_to (cr, 0, dx);
2696 cairo_rel_line_to (cr, dx, 0);
2697 cairo_rel_line_to (cr, 0., -dx);
2698 cairo_rel_line_to (cr, -dx, 0.);
2699 cairo_close_path (cr);
2700 cairo_fill_preserve (cr);
2701 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
2702 cairo_stroke (cr);
2704 if (swc->value) {
2705 cairo_new_path (cr);
2706 cairo_move_to (cr, dxh, halfheight - dxh);
2707 cairo_rel_line_to (cr, dx, dx);
2708 cairo_rel_line_to (cr, -dx, 0.);
2709 cairo_rel_line_to (cr, dx, -dx);
2710 cairo_rel_line_to (cr, -dx, 0.);
2711 cairo_close_path (cr);
2712 cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
2713 cairo_stroke (cr);
2716 cairo_move_to (cr, 2 * dx, halfheight);
2718 pw = width - 2 * dx;
2719 ph = height;
2721 draw_cairo_text (cr, swc->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
2723 cairo_new_path (cr);
2724 cairo_restore (cr);
2728 SOW_MAKE_TYPE (checkbox, Checkbox,
2729 sheet_widget_checkbox_user_config,
2730 sheet_widget_checkbox_set_sheet,
2731 so_clear_sheet,
2732 sheet_widget_checkbox_foreach_dep,
2733 sheet_widget_checkbox_copy,
2734 sheet_widget_checkbox_write_xml_sax,
2735 sheet_widget_checkbox_prep_sax_parser,
2736 sheet_widget_checkbox_get_property,
2737 sheet_widget_checkbox_set_property,
2738 sheet_widget_checkbox_draw_cairo,
2740 g_object_class_install_property
2741 (object_class, SOC_PROP_ACTIVE,
2742 g_param_spec_boolean ("active", NULL, NULL,
2743 FALSE,
2744 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2745 g_object_class_install_property
2746 (object_class, SOC_PROP_TEXT,
2747 g_param_spec_string ("text", NULL, NULL, NULL,
2748 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2749 g_object_class_install_property
2750 (object_class, SOC_PROP_MARKUP,
2751 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
2752 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2755 /****************************************************************************/
2756 typedef SheetWidgetCheckbox SheetWidgetToggleButton;
2757 typedef SheetWidgetCheckboxClass SheetWidgetToggleButtonClass;
2758 static GtkWidget *
2759 sheet_widget_toggle_button_create_widget (SheetObjectWidget *sow)
2761 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2762 GtkWidget *button = gtk_toggle_button_new_with_label (swc->label);
2763 gtk_widget_set_can_focus (button, FALSE);
2764 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2765 g_signal_connect (G_OBJECT (button),
2766 "toggled",
2767 G_CALLBACK (cb_checkbox_toggled), swc);
2768 return button;
2770 static void
2771 sheet_widget_toggle_button_class_init (SheetObjectWidgetClass *sow_class)
2773 sow_class->create_widget = &sheet_widget_toggle_button_create_widget;
2776 GSF_CLASS (SheetWidgetToggleButton, sheet_widget_toggle_button,
2777 &sheet_widget_toggle_button_class_init, NULL,
2778 GNM_SOW_CHECKBOX_TYPE)
2780 /****************************************************************************/
2782 #define GNM_SOW_RADIO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_RADIO_BUTTON_TYPE, SheetWidgetRadioButton))
2783 #define DEP_TO_RADIO_BUTTON(d_ptr) (SheetWidgetRadioButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetRadioButton, dep))
2785 typedef struct {
2786 SheetObjectWidget sow;
2788 gboolean being_updated;
2789 char *label;
2790 GnmValue *value;
2791 gboolean active;
2792 GnmDependent dep;
2793 } SheetWidgetRadioButton;
2794 typedef SheetObjectWidgetClass SheetWidgetRadioButtonClass;
2796 enum {
2797 SOR_PROP_0 = 0,
2798 SOR_PROP_ACTIVE,
2799 SOR_PROP_TEXT,
2800 SOR_PROP_MARKUP,
2801 SOR_PROP_VALUE
2804 static void
2805 sheet_widget_radio_button_set_active (SheetWidgetRadioButton *swrb,
2806 gboolean active)
2808 GList *ptr;
2810 if (swrb->active == active)
2811 return;
2812 swrb->active = active;
2814 swrb->being_updated = TRUE;
2816 for (ptr = swrb->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2817 SheetObjectView *view = ptr->data;
2818 GocWidget *item = get_goc_widget (view);
2819 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2820 active);
2823 g_object_notify (G_OBJECT (swrb), "active");
2825 swrb->being_updated = FALSE;
2829 static void
2830 sheet_widget_radio_button_get_property (GObject *obj, guint param_id,
2831 GValue *value, GParamSpec *pspec)
2833 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2835 switch (param_id) {
2836 case SOR_PROP_ACTIVE:
2837 g_value_set_boolean (value, swrb->active);
2838 break;
2839 case SOR_PROP_TEXT:
2840 g_value_set_string (value, swrb->label);
2841 break;
2842 case SOR_PROP_MARKUP:
2843 g_value_set_boxed (value, NULL); /* swrb->markup */
2844 break;
2845 case SOR_PROP_VALUE:
2846 g_value_set_boxed (value, swrb->value);
2847 break;
2848 default:
2849 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2850 break;
2854 static void
2855 sheet_widget_radio_button_set_property (GObject *obj, guint param_id,
2856 GValue const *value, GParamSpec *pspec)
2858 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2860 switch (param_id) {
2861 case SOR_PROP_ACTIVE:
2862 sheet_widget_radio_button_set_active (swrb,
2863 g_value_get_boolean (value));
2864 break;
2865 case SOR_PROP_TEXT:
2866 sheet_widget_radio_button_set_label (GNM_SO (swrb),
2867 g_value_get_string (value));
2868 break;
2869 case SOR_PROP_MARKUP:
2870 #if 0
2871 sheet_widget_radio_button_set_markup (GNM_SO (swrb),
2872 g_value_peek_pointer (value));
2873 #endif
2874 break;
2875 case SOR_PROP_VALUE:
2876 sheet_widget_radio_button_set_value (GNM_SO (swrb),
2877 g_value_get_boxed (value));
2878 break;
2879 default:
2880 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2881 return;
2885 GnmValue const *
2886 sheet_widget_radio_button_get_value (SheetObject *so)
2888 SheetWidgetRadioButton *swrb;
2890 g_return_val_if_fail (GNM_IS_SOW_RADIO_BUTTON (so), NULL);
2892 swrb = GNM_SOW_RADIO_BUTTON (so);
2893 return swrb->value;
2896 void
2897 sheet_widget_radio_button_set_value (SheetObject *so, GnmValue const *val)
2899 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
2901 value_release (swrb->value);
2902 swrb->value = value_dup (val);
2905 static void
2906 radio_button_eval (GnmDependent *dep)
2908 GnmValue *v;
2909 GnmEvalPos pos;
2910 SheetWidgetRadioButton *swrb = DEP_TO_RADIO_BUTTON (dep);
2912 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2913 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2914 if (v && swrb->value) {
2915 gboolean active = value_equal (swrb->value, v);
2916 sheet_widget_radio_button_set_active (swrb, active);
2918 value_release (v);
2921 static void
2922 radio_button_debug_name (GnmDependent const *dep, GString *target)
2924 g_string_append_printf (target, "RadioButton%p", (void *)dep);
2927 static DEPENDENT_MAKE_TYPE (radio_button, NULL)
2929 static void
2930 sheet_widget_radio_button_init_full (SheetWidgetRadioButton *swrb,
2931 GnmCellRef const *ref,
2932 char const *label,
2933 GnmValue const *value,
2934 gboolean active)
2936 g_return_if_fail (swrb != NULL);
2938 swrb->being_updated = FALSE;
2939 swrb->label = g_strdup (label ? label : _("RadioButton"));
2940 swrb->value = value ? value_dup (value) : value_new_empty ();
2941 swrb->active = active;
2943 swrb->dep.sheet = NULL;
2944 swrb->dep.flags = radio_button_get_dep_type ();
2945 swrb->dep.texpr = (ref != NULL)
2946 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2947 : NULL;
2950 static void
2951 sheet_widget_radio_button_init (SheetWidgetRadioButton *swrb)
2953 sheet_widget_radio_button_init_full (swrb, NULL, NULL, NULL, TRUE);
2956 static void
2957 sheet_widget_radio_button_finalize (GObject *obj)
2959 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2961 g_return_if_fail (swrb != NULL);
2963 g_free (swrb->label);
2964 swrb->label = NULL;
2965 value_release (swrb->value);
2966 swrb->value = NULL;
2968 dependent_set_expr (&swrb->dep, NULL);
2970 sheet_object_widget_class->finalize (obj);
2973 static void
2974 sheet_widget_radio_button_toggled (GtkToggleButton *button,
2975 SheetWidgetRadioButton *swrb)
2977 GnmCellRef ref;
2979 if (swrb->being_updated)
2980 return;
2981 swrb->active = gtk_toggle_button_get_active (button);
2983 if (so_get_ref (GNM_SO (swrb), &ref, TRUE) != NULL) {
2984 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2985 /* FIXME: This text sucks: */
2986 _("Clicking radiobutton"),
2987 &ref, value_dup (swrb->value),
2988 sheet_object_get_sheet (GNM_SO (swrb)));
2992 static GtkWidget *
2993 sheet_widget_radio_button_create_widget (SheetObjectWidget *sow)
2995 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (sow);
2996 GtkWidget *w = g_object_new (GNM_TYPE_RADIO_BUTTON,
2997 "label", swrb->label,
2998 NULL) ;
3000 gtk_widget_set_can_focus (w, FALSE);
3002 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), swrb->active);
3004 g_signal_connect (G_OBJECT (w),
3005 "toggled",
3006 G_CALLBACK (sheet_widget_radio_button_toggled), sow);
3007 return w;
3010 static void
3011 sheet_widget_radio_button_copy (SheetObject *dst, SheetObject const *src)
3013 SheetWidgetRadioButton const *src_swrb = GNM_SOW_RADIO_BUTTON (src);
3014 SheetWidgetRadioButton *dst_swrb = GNM_SOW_RADIO_BUTTON (dst);
3015 GnmCellRef ref;
3017 sheet_widget_radio_button_init_full (dst_swrb,
3018 so_get_ref (src, &ref, FALSE),
3019 src_swrb->label,
3020 src_swrb->value,
3021 src_swrb->active);
3024 static gboolean
3025 sheet_widget_radio_button_set_sheet (SheetObject *so, Sheet *sheet)
3027 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3029 dependent_set_sheet (&swrb->dep, sheet);
3031 return FALSE;
3034 static void
3035 sheet_widget_radio_button_foreach_dep (SheetObject *so,
3036 SheetObjectForeachDepFunc func,
3037 gpointer user)
3039 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3040 func (&swrb->dep, so, user);
3043 static void
3044 sheet_widget_radio_button_write_xml_sax (SheetObject const *so,
3045 GsfXMLOut *output,
3046 GnmConventions const *convs)
3048 SheetWidgetRadioButton const *swrb = GNM_SOW_RADIO_BUTTON (so);
3049 GString *valstr = g_string_new (NULL);
3051 value_get_as_gstring (swrb->value, valstr, convs);
3053 gsf_xml_out_add_cstr (output, "Label", swrb->label);
3054 gsf_xml_out_add_cstr (output, "Value", valstr->str);
3055 gsf_xml_out_add_int (output, "ValueType", swrb->value->v_any.type);
3056 gsf_xml_out_add_int (output, "Active", swrb->active);
3057 sax_write_dep (output, &swrb->dep, "Input", convs);
3059 g_string_free (valstr, TRUE);
3062 static void
3063 sheet_widget_radio_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3064 xmlChar const **attrs,
3065 GnmConventions const *convs)
3067 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3068 const char *valstr = NULL;
3069 int value_type = 0;
3071 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
3072 if (attr_eq (attrs[0], "Label")) {
3073 g_free (swrb->label);
3074 swrb->label = g_strdup (CXML2C (attrs[1]));
3075 } else if (attr_eq (attrs[0], "Value")) {
3076 valstr = CXML2C (attrs[1]);
3077 } else if (gnm_xml_attr_bool (attrs, "Active", &swrb->active) ||
3078 gnm_xml_attr_int (attrs, "ValueType", &value_type) ||
3079 sax_read_dep (attrs, "Input", &swrb->dep, xin, convs))
3080 ; /* Nothing */
3083 value_release (swrb->value);
3084 swrb->value = NULL;
3085 if (valstr) {
3086 swrb->value = value_type
3087 ? value_new_from_string (value_type, valstr, NULL, FALSE)
3088 : format_match (valstr, NULL, NULL);
3090 if (!swrb->value)
3091 swrb->value = value_new_empty ();
3094 void
3095 sheet_widget_radio_button_set_link (SheetObject *so, GnmExprTop const *texpr)
3097 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3098 dependent_set_expr (&swrb->dep, texpr);
3099 if (texpr && swrb->dep.sheet)
3100 dependent_link (&swrb->dep);
3103 GnmExprTop const *
3104 sheet_widget_radio_button_get_link (SheetObject *so)
3106 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3107 GnmExprTop const *texpr = swrb->dep.texpr;
3109 if (texpr)
3110 gnm_expr_top_ref (texpr);
3112 return texpr;
3115 void
3116 sheet_widget_radio_button_set_label (SheetObject *so, char const *str)
3118 GList *list;
3119 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3120 char *new_label;
3122 if (go_str_compare (str, swrb->label) == 0)
3123 return;
3125 new_label = g_strdup (str);
3126 g_free (swrb->label);
3127 swrb->label = new_label;
3129 for (list = swrb->sow.so.realized_list; list; list = list->next) {
3130 SheetObjectView *view = list->data;
3131 GocWidget *item = get_goc_widget (view);
3132 gtk_button_set_label (GTK_BUTTON (item->widget), swrb->label);
3137 typedef struct {
3138 GtkWidget *dialog;
3139 GnmExprEntry *expression;
3140 GtkWidget *label, *value;
3142 char *old_label;
3143 GnmValue *old_value;
3144 GtkWidget *old_focus;
3146 WBCGtk *wbcg;
3147 SheetWidgetRadioButton *swrb;
3148 Sheet *sheet;
3149 } RadioButtonConfigState;
3151 static void
3152 cb_radio_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
3153 RadioButtonConfigState *state)
3155 GtkWidget *ofp;
3157 /* Note: half of the set-focus action is handle by the default
3158 * callback installed by wbc_gtk_attach_guru */
3160 ofp = state->old_focus
3161 ? gtk_widget_get_parent (state->old_focus)
3162 : NULL;
3164 /* Force an update of the content in case it needs tweaking (eg make it
3165 * absolute) */
3166 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
3167 GnmParsePos pp;
3168 GnmExprTop const *texpr = gnm_expr_entry_parse (
3169 GNM_EXPR_ENTRY (ofp),
3170 parse_pos_init_sheet (&pp, state->sheet),
3171 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3172 if (texpr != NULL)
3173 gnm_expr_top_unref (texpr);
3175 state->old_focus = focus_widget;
3178 static void
3179 cb_radio_button_config_destroy (RadioButtonConfigState *state)
3181 g_return_if_fail (state != NULL);
3183 g_free (state->old_label);
3184 state->old_label = NULL;
3186 value_release (state->old_value);
3187 state->old_value = NULL;
3189 state->dialog = NULL;
3191 g_free (state);
3194 static GnmValue *
3195 so_parse_value (SheetObject *so, const char *s)
3197 Sheet *sheet = so->sheet;
3198 return format_match (s, NULL, sheet_date_conv (sheet));
3201 static void
3202 cb_radio_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3204 SheetObject *so = GNM_SO (state->swrb);
3205 GnmParsePos pp;
3206 GnmExprTop const *texpr = gnm_expr_entry_parse
3207 (state->expression,
3208 parse_pos_init_sheet (&pp, so->sheet),
3209 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3210 gchar const *text = gtk_entry_get_text (GTK_ENTRY (state->label));
3211 gchar const *val = gtk_entry_get_text (GTK_ENTRY (state->value));
3212 GnmValue *new_val = so_parse_value (so, val);
3214 cmd_so_set_radio_button (GNM_WBC (state->wbcg), so,
3215 texpr,
3216 g_strdup (state->old_label), g_strdup (text),
3217 value_dup (state->old_value), new_val);
3219 gtk_widget_destroy (state->dialog);
3222 static void
3223 cb_radio_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3225 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3226 state->old_label);
3227 sheet_widget_radio_button_set_value (GNM_SO (state->swrb),
3228 state->old_value);
3229 gtk_widget_destroy (state->dialog);
3232 static void
3233 cb_radio_button_label_changed (GtkEntry *entry, RadioButtonConfigState *state)
3235 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3236 gtk_entry_get_text (entry));
3239 static void
3240 cb_radio_button_value_changed (GtkEntry *entry, RadioButtonConfigState *state)
3242 const char *text = gtk_entry_get_text (entry);
3243 SheetObject *so = GNM_SO (state->swrb);
3244 GnmValue *val = so_parse_value (so, text);
3246 sheet_widget_radio_button_set_value (so, val);
3247 value_release (val);
3250 static void
3251 sheet_widget_radio_button_user_config (SheetObject *so, SheetControl *sc)
3253 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3254 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
3255 RadioButtonConfigState *state;
3256 GtkWidget *grid;
3257 GString *valstr;
3258 GtkBuilder *gui;
3260 g_return_if_fail (swrb != NULL);
3262 /* Only pop up one copy per workbook */
3263 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
3264 return;
3266 gui = gnm_gtk_builder_load ("res:ui/so-radiobutton.ui", NULL, GO_CMD_CONTEXT (wbcg));
3267 if (!gui)
3268 return;
3269 state = g_new (RadioButtonConfigState, 1);
3270 state->swrb = swrb;
3271 state->wbcg = wbcg;
3272 state->sheet = sc_sheet (sc);
3273 state->old_focus = NULL;
3274 state->old_label = g_strdup (swrb->label);
3275 state->old_value = value_dup (swrb->value);
3276 state->dialog = go_gtk_builder_get_widget (gui, "SO-Radiobutton");
3278 grid = go_gtk_builder_get_widget (gui, "main-grid");
3280 state->expression = gnm_expr_entry_new (wbcg, TRUE);
3281 gnm_expr_entry_set_flags (state->expression,
3282 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
3283 GNM_EE_MASK);
3284 gnm_expr_entry_load_from_dep (state->expression, &swrb->dep);
3285 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
3286 GTK_WIDGET (state->expression));
3287 gtk_grid_attach (GTK_GRID (grid),
3288 GTK_WIDGET (state->expression), 1, 0, 1, 1);
3289 gtk_widget_show (GTK_WIDGET (state->expression));
3291 state->label = go_gtk_builder_get_widget (gui, "label_entry");
3292 gtk_entry_set_text (GTK_ENTRY (state->label), swrb->label);
3293 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
3294 state->value = go_gtk_builder_get_widget (gui, "value_entry");
3296 valstr = g_string_new (NULL);
3297 value_get_as_gstring (swrb->value, valstr, so->sheet->convs);
3298 gtk_entry_set_text (GTK_ENTRY (state->value), valstr->str);
3299 g_string_free (valstr, TRUE);
3301 gnm_editable_enters (GTK_WINDOW (state->dialog),
3302 GTK_WIDGET (state->expression));
3303 gnm_editable_enters (GTK_WINDOW (state->dialog),
3304 GTK_WIDGET (state->label));
3305 gnm_editable_enters (GTK_WINDOW (state->dialog),
3306 GTK_WIDGET (state->value));
3308 g_signal_connect (G_OBJECT (state->label),
3309 "changed",
3310 G_CALLBACK (cb_radio_button_label_changed), state);
3311 g_signal_connect (G_OBJECT (state->value),
3312 "changed",
3313 G_CALLBACK (cb_radio_button_value_changed), state);
3314 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
3315 "clicked",
3316 G_CALLBACK (cb_radio_button_config_ok_clicked), state);
3317 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
3318 "clicked",
3319 G_CALLBACK (cb_radio_button_config_cancel_clicked), state);
3321 gnm_init_help_button (
3322 go_gtk_builder_get_widget (gui, "help_button"),
3323 GNUMERIC_HELP_LINK_SO_RADIO_BUTTON);
3325 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
3326 SHEET_OBJECT_CONFIG_KEY);
3328 wbc_gtk_attach_guru (state->wbcg, state->dialog);
3329 g_object_set_data_full (G_OBJECT (state->dialog),
3330 "state", state, (GDestroyNotify) cb_radio_button_config_destroy);
3331 g_object_unref (gui);
3333 /* Note: half of the set-focus action is handle by the default */
3334 /* callback installed by wbc_gtk_attach_guru */
3335 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
3336 G_CALLBACK (cb_radio_button_set_focus), state);
3338 gtk_widget_show (state->dialog);
3341 static void
3342 sheet_widget_radio_button_draw_cairo (SheetObject const *so, cairo_t *cr,
3343 G_GNUC_UNUSED double width, double height)
3345 SheetWidgetRadioButton const *swr = GNM_SOW_RADIO_BUTTON (so);
3346 double halfheight = height/2;
3347 double dx = 8., dxh, pm;
3348 int pw, ph;
3350 pm = MIN (height - 2, width - 12);
3351 if (dx > pm)
3352 dx = MAX (pm, 3);
3353 dxh = dx/2;
3355 cairo_save (cr);
3356 cairo_set_line_width (cr, 0.5);
3357 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
3359 cairo_new_path (cr);
3360 cairo_move_to (cr, dxh + dx, halfheight);
3361 cairo_arc (cr, dx, halfheight, dxh, 0., 2*M_PI);
3362 cairo_close_path (cr);
3363 cairo_fill_preserve (cr);
3364 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
3365 cairo_stroke (cr);
3367 if (swr->active) {
3368 cairo_new_path (cr);
3369 cairo_move_to (cr, dx + dxh/2 + 0.5, halfheight);
3370 cairo_arc (cr, dx, halfheight, dxh/2 + 0.5, 0., 2*M_PI);
3371 cairo_close_path (cr);
3372 cairo_fill (cr);
3375 cairo_move_to (cr, 2 * dx, halfheight);
3377 pw = width - 2 * dx;
3378 ph = height;
3380 draw_cairo_text (cr, swr->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
3382 cairo_new_path (cr);
3383 cairo_restore (cr);
3386 SOW_MAKE_TYPE (radio_button, RadioButton,
3387 sheet_widget_radio_button_user_config,
3388 sheet_widget_radio_button_set_sheet,
3389 so_clear_sheet,
3390 sheet_widget_radio_button_foreach_dep,
3391 sheet_widget_radio_button_copy,
3392 sheet_widget_radio_button_write_xml_sax,
3393 sheet_widget_radio_button_prep_sax_parser,
3394 sheet_widget_radio_button_get_property,
3395 sheet_widget_radio_button_set_property,
3396 sheet_widget_radio_button_draw_cairo,
3398 g_object_class_install_property
3399 (object_class, SOR_PROP_ACTIVE,
3400 g_param_spec_boolean ("active", NULL, NULL,
3401 FALSE,
3402 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3403 g_object_class_install_property
3404 (object_class, SOR_PROP_TEXT,
3405 g_param_spec_string ("text", NULL, NULL, NULL,
3406 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3407 g_object_class_install_property
3408 (object_class, SOR_PROP_MARKUP,
3409 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
3410 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3411 g_object_class_install_property
3412 (object_class, SOR_PROP_VALUE,
3413 g_param_spec_boxed ("value", NULL, NULL,
3414 gnm_value_get_type (),
3415 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3418 /****************************************************************************/
3420 #define GNM_SOW_LIST_BASE_TYPE (sheet_widget_list_base_get_type ())
3421 #define GNM_SOW_LIST_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_LIST_BASE_TYPE, SheetWidgetListBase))
3422 #define DEP_TO_LIST_BASE_CONTENT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, content_dep))
3423 #define DEP_TO_LIST_BASE_OUTPUT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, output_dep))
3425 typedef struct {
3426 SheetObjectWidget sow;
3428 GnmDependent content_dep; /* content of the list */
3429 GnmDependent output_dep; /* selected element */
3431 GtkTreeModel *model;
3432 int selection;
3433 gboolean result_as_index;
3434 } SheetWidgetListBase;
3435 typedef struct {
3436 SheetObjectWidgetClass base;
3438 void (*model_changed) (SheetWidgetListBase *list);
3439 void (*selection_changed) (SheetWidgetListBase *list);
3440 } SheetWidgetListBaseClass;
3442 enum {
3443 LIST_BASE_MODEL_CHANGED,
3444 LIST_BASE_SELECTION_CHANGED,
3445 LIST_BASE_LAST_SIGNAL
3448 static guint list_base_signals [LIST_BASE_LAST_SIGNAL] = { 0 };
3449 static GType sheet_widget_list_base_get_type (void);
3451 static void
3452 sheet_widget_list_base_set_selection (SheetWidgetListBase *swl, int selection,
3453 WorkbookControl *wbc)
3455 GnmCellRef ref;
3457 if (selection >= 0 && swl->model != NULL) {
3458 int n = gtk_tree_model_iter_n_children (swl->model, NULL);
3459 if (selection > n)
3460 selection = n;
3461 } else
3462 selection = 0;
3464 if (swl->selection != selection) {
3465 swl->selection = selection;
3466 if (NULL!= wbc &&
3467 so_get_ref (GNM_SO (swl), &ref, TRUE) != NULL) {
3468 GnmValue *v;
3469 if (swl->result_as_index)
3470 v = value_new_int (swl->selection);
3471 else if (selection != 0) {
3472 GtkTreeIter iter;
3473 char *content;
3474 gtk_tree_model_iter_nth_child
3475 (swl->model, &iter, NULL, selection - 1);
3476 gtk_tree_model_get (swl->model, &iter,
3477 0, &content, -1);
3478 v = value_new_string_nocopy (content);
3479 } else
3480 v = value_new_string ("");
3481 cmd_so_set_value (wbc, _("Clicking in list"), &ref, v,
3482 sheet_object_get_sheet (GNM_SO (swl)));
3484 g_signal_emit (G_OBJECT (swl),
3485 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3489 static void
3490 sheet_widget_list_base_set_selection_value (SheetWidgetListBase *swl, GnmValue *v)
3492 GtkTreeIter iter;
3493 int selection = 0, i = 1;
3495 if (swl->model != NULL && gtk_tree_model_get_iter_first (swl->model, &iter)) {
3496 char *str = value_get_as_string (v);
3497 do {
3498 char *content;
3499 gboolean match;
3500 gtk_tree_model_get (swl->model, &iter,
3501 0, &content, -1);
3502 match = 0 == g_ascii_strcasecmp (str, content);
3503 g_free (content);
3504 if (match) {
3505 selection = i;
3506 break;
3508 i++;
3509 } while (gtk_tree_model_iter_next (swl->model, &iter));
3510 g_free (str);
3513 if (swl->selection != selection) {
3514 swl->selection = selection;
3515 g_signal_emit (G_OBJECT (swl),
3516 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3520 static void
3521 list_output_eval (GnmDependent *dep)
3523 GnmEvalPos pos;
3524 GnmValue *v = gnm_expr_top_eval (dep->texpr,
3525 eval_pos_init_dep (&pos, dep),
3526 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
3527 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_OUTPUT (dep);
3529 if (swl->result_as_index)
3530 sheet_widget_list_base_set_selection
3531 (swl, floor (value_get_as_float (v)), NULL);
3532 else
3533 sheet_widget_list_base_set_selection_value (swl, v);
3534 value_release (v);
3537 static void
3538 list_output_debug_name (GnmDependent const *dep, GString *target)
3540 g_string_append_printf (target, "ListOutput%p", (void *)dep);
3543 static DEPENDENT_MAKE_TYPE (list_output, NULL)
3545 /*-----------*/
3546 static GnmValue *
3547 cb_collect (GnmValueIter const *iter, GtkListStore *model)
3549 GtkTreeIter list_iter;
3551 gtk_list_store_append (model, &list_iter);
3552 if (NULL != iter->v) {
3553 GOFormat const *fmt = (NULL != iter->cell_iter)
3554 ? gnm_cell_get_format (iter->cell_iter->cell) : NULL;
3555 char *label = format_value (fmt, iter->v, -1, NULL);
3556 gtk_list_store_set (model, &list_iter, 0, label, -1);
3557 g_free (label);
3558 } else
3559 gtk_list_store_set (model, &list_iter, 0, "", -1);
3561 return NULL;
3563 static void
3564 list_content_eval (GnmDependent *dep)
3566 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_CONTENT (dep);
3567 GnmEvalPos ep;
3568 GnmValue *v = NULL;
3569 GtkListStore *model;
3571 if (dep->texpr != NULL) {
3572 v = gnm_expr_top_eval (dep->texpr,
3573 eval_pos_init_dep (&ep, dep),
3574 GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
3575 GNM_EXPR_EVAL_PERMIT_EMPTY);
3577 model = gtk_list_store_new (1, G_TYPE_STRING);
3578 if (v) {
3579 value_area_foreach (v, &ep, CELL_ITER_ALL,
3580 (GnmValueIterFunc) cb_collect, model);
3581 value_release (v);
3584 if (NULL != swl->model)
3585 g_object_unref (swl->model);
3586 swl->model = GTK_TREE_MODEL (model);
3587 g_signal_emit (G_OBJECT (swl), list_base_signals [LIST_BASE_MODEL_CHANGED], 0);
3590 static void
3591 list_content_debug_name (GnmDependent const *dep, GString *target)
3593 g_string_append_printf (target, "ListContent%p", (void *)dep);
3596 static DEPENDENT_MAKE_TYPE (list_content, NULL)
3598 /*-----------*/
3600 static void
3601 sheet_widget_list_base_init (SheetObjectWidget *sow)
3603 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3604 SheetObject *so = GNM_SO (sow);
3606 so->flags &= ~SHEET_OBJECT_PRINT;
3608 swl->content_dep.sheet = NULL;
3609 swl->content_dep.flags = list_content_get_dep_type ();
3610 swl->content_dep.texpr = NULL;
3612 swl->output_dep.sheet = NULL;
3613 swl->output_dep.flags = list_output_get_dep_type ();
3614 swl->output_dep.texpr = NULL;
3616 swl->model = NULL;
3617 swl->selection = 0;
3618 swl->result_as_index = TRUE;
3621 static void
3622 sheet_widget_list_base_finalize (GObject *obj)
3624 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (obj);
3625 dependent_set_expr (&swl->content_dep, NULL);
3626 dependent_set_expr (&swl->output_dep, NULL);
3627 if (swl->model != NULL) {
3628 g_object_unref (swl->model);
3629 swl->model = NULL;
3631 sheet_object_widget_class->finalize (obj);
3634 static void
3635 sheet_widget_list_base_user_config (SheetObject *so, SheetControl *sc)
3637 dialog_so_list (scg_wbcg (GNM_SCG (sc)), G_OBJECT (so));
3639 static gboolean
3640 sheet_widget_list_base_set_sheet (SheetObject *so, Sheet *sheet)
3642 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3644 g_return_val_if_fail (swl != NULL, TRUE);
3645 g_return_val_if_fail (swl->content_dep.sheet == NULL, TRUE);
3646 g_return_val_if_fail (swl->output_dep.sheet == NULL, TRUE);
3648 dependent_set_sheet (&swl->content_dep, sheet);
3649 dependent_set_sheet (&swl->output_dep, sheet);
3651 list_content_eval (&swl->content_dep); /* populate the list */
3653 return FALSE;
3656 static void
3657 sheet_widget_list_base_foreach_dep (SheetObject *so,
3658 SheetObjectForeachDepFunc func,
3659 gpointer user)
3661 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3662 func (&swl->content_dep, so, user);
3663 func (&swl->output_dep, so, user);
3666 static void
3667 sheet_widget_list_base_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
3668 GnmConventions const *convs)
3670 SheetWidgetListBase const *swl = GNM_SOW_LIST_BASE (so);
3671 sax_write_dep (output, &swl->content_dep, "Content", convs);
3672 sax_write_dep (output, &swl->output_dep, "Output", convs);
3673 gsf_xml_out_add_int (output, "OutputAsIndex", swl->result_as_index ? 1 : 0);
3676 static void
3677 sheet_widget_list_base_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3678 xmlChar const **attrs,
3679 GnmConventions const *convs)
3681 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3683 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
3684 if (sax_read_dep (attrs, "Content", &swl->content_dep, xin, convs))
3686 else if (sax_read_dep (attrs, "Output", &swl->output_dep, xin, convs))
3688 else if (gnm_xml_attr_bool (attrs, "OutputAsIndex", &swl->result_as_index))
3692 static GtkWidget *
3693 sheet_widget_list_base_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
3695 g_warning("ERROR: sheet_widget_list_base_create_widget SHOULD NEVER BE CALLED (but it has been)!\n");
3696 return gtk_frame_new ("invisiwidget(WARNING: I AM A BUG!)");
3699 SOW_MAKE_TYPE (list_base, ListBase,
3700 sheet_widget_list_base_user_config,
3701 sheet_widget_list_base_set_sheet,
3702 so_clear_sheet,
3703 sheet_widget_list_base_foreach_dep,
3704 NULL,
3705 sheet_widget_list_base_write_xml_sax,
3706 sheet_widget_list_base_prep_sax_parser,
3707 NULL,
3708 NULL,
3709 sheet_widget_draw_cairo,
3711 list_base_signals[LIST_BASE_MODEL_CHANGED] = g_signal_new ("model-changed",
3712 GNM_SOW_LIST_BASE_TYPE,
3713 G_SIGNAL_RUN_LAST,
3714 G_STRUCT_OFFSET (SheetWidgetListBaseClass, model_changed),
3715 NULL, NULL,
3716 g_cclosure_marshal_VOID__VOID,
3717 G_TYPE_NONE, 0);
3718 list_base_signals[LIST_BASE_SELECTION_CHANGED] = g_signal_new ("selection-changed",
3719 GNM_SOW_LIST_BASE_TYPE,
3720 G_SIGNAL_RUN_LAST,
3721 G_STRUCT_OFFSET (SheetWidgetListBaseClass, selection_changed),
3722 NULL, NULL,
3723 g_cclosure_marshal_VOID__VOID,
3724 G_TYPE_NONE, 0);
3727 void
3728 sheet_widget_list_base_set_links (SheetObject *so,
3729 GnmExprTop const *output,
3730 GnmExprTop const *content)
3732 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3733 dependent_set_expr (&swl->output_dep, output);
3734 if (output && swl->output_dep.sheet)
3735 dependent_link (&swl->output_dep);
3736 dependent_set_expr (&swl->content_dep, content);
3737 if (content && swl->content_dep.sheet) {
3738 dependent_link (&swl->content_dep);
3739 list_content_eval (&swl->content_dep); /* populate the list */
3743 GnmExprTop const *
3744 sheet_widget_list_base_get_result_link (SheetObject const *so)
3746 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3747 GnmExprTop const *texpr = swl->output_dep.texpr;
3749 if (texpr)
3750 gnm_expr_top_ref (texpr);
3752 return texpr;
3755 GnmExprTop const *
3756 sheet_widget_list_base_get_content_link (SheetObject const *so)
3758 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3759 GnmExprTop const *texpr = swl->content_dep.texpr;
3761 if (texpr)
3762 gnm_expr_top_ref (texpr);
3764 return texpr;
3767 gboolean
3768 sheet_widget_list_base_result_type_is_index (SheetObject const *so)
3770 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3772 return swl->result_as_index;
3775 void
3776 sheet_widget_list_base_set_result_type (SheetObject *so, gboolean as_index)
3778 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3780 if (swl->result_as_index == as_index)
3781 return;
3783 swl->result_as_index = as_index;
3788 * sheet_widget_list_base_get_adjustment:
3789 * @so: #SheetObject
3791 * Note: allocates a new adjustment.
3792 * Returns: (transfer full): the newly created #GtkAdjustment.
3794 GtkAdjustment *
3795 sheet_widget_list_base_get_adjustment (SheetObject *so)
3797 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3798 GtkAdjustment *adj;
3800 g_return_val_if_fail (swl, NULL);
3802 adj = (GtkAdjustment*)gtk_adjustment_new
3803 (swl->selection,
3805 1 + gtk_tree_model_iter_n_children (swl->model, NULL),
3809 g_object_ref_sink (adj);
3811 return adj;
3814 /****************************************************************************/
3816 #define GNM_SOW_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_LIST_TYPE, SheetWidgetList))
3818 typedef SheetWidgetListBase SheetWidgetList;
3819 typedef SheetWidgetListBaseClass SheetWidgetListClass;
3821 static void
3822 cb_list_selection_changed (SheetWidgetListBase *swl,
3823 GtkTreeSelection *selection)
3825 if (swl->selection > 0) {
3826 GtkTreePath *path = gtk_tree_path_new_from_indices (swl->selection-1, -1);
3827 gtk_tree_selection_select_path (selection, path);
3828 gtk_tree_path_free (path);
3829 } else
3830 gtk_tree_selection_unselect_all (selection);
3833 static void
3834 cb_list_model_changed (SheetWidgetListBase *swl, GtkTreeView *list)
3836 int old_selection = swl->selection;
3837 swl->selection = -1;
3838 gtk_tree_view_set_model (GTK_TREE_VIEW (list), swl->model);
3839 sheet_widget_list_base_set_selection (swl, old_selection, NULL);
3841 static void
3842 cb_selection_changed (GtkTreeSelection *selection,
3843 SheetWidgetListBase *swl)
3845 GtkWidget *view = (GtkWidget *)gtk_tree_selection_get_tree_view (selection);
3846 GnmSimpleCanvas *scanvas = GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (view, GNM_SIMPLE_CANVAS_TYPE));
3847 GtkTreeModel *model;
3848 GtkTreeIter iter;
3849 int pos = 0;
3850 if (swl->selection != -1) {
3851 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3852 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
3853 if (NULL != path) {
3854 pos = *gtk_tree_path_get_indices (path) + 1;
3855 gtk_tree_path_free (path);
3858 sheet_widget_list_base_set_selection
3859 (swl, pos, scg_wbc (scanvas->scg));
3863 static GtkWidget *
3864 sheet_widget_list_create_widget (SheetObjectWidget *sow)
3866 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3867 GtkTreeSelection *selection;
3868 GtkTreeIter iter;
3869 GtkWidget *list = gtk_tree_view_new_with_model (swl->model);
3870 GtkWidget *sw = gtk_scrolled_window_new (
3871 gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (list)),
3872 gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (list)));
3873 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
3874 GTK_POLICY_AUTOMATIC,
3875 GTK_POLICY_ALWAYS);
3876 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
3877 gtk_tree_view_append_column (GTK_TREE_VIEW (list),
3878 gtk_tree_view_column_new_with_attributes ("ID",
3879 gtk_cell_renderer_text_new (), "text", 0,
3880 NULL));
3882 gtk_container_add (GTK_CONTAINER (sw), list);
3884 g_signal_connect_object (G_OBJECT (swl), "model-changed",
3885 G_CALLBACK (cb_list_model_changed), list, 0);
3887 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
3888 if ((swl->model != NULL) && (swl->selection > 0) &&
3889 gtk_tree_model_iter_nth_child (swl->model, &iter, NULL, swl->selection - 1))
3890 gtk_tree_selection_select_iter (selection, &iter);
3891 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
3892 G_CALLBACK (cb_list_selection_changed), selection, 0);
3893 g_signal_connect (selection, "changed",
3894 G_CALLBACK (cb_selection_changed), swl);
3895 return sw;
3898 static void
3899 sheet_widget_list_draw_cairo (SheetObject const *so, cairo_t *cr,
3900 double width, double height)
3902 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3904 cairo_save (cr);
3905 cairo_set_line_width (cr, 0.5);
3906 cairo_set_source_rgb(cr, 0, 0, 0);
3908 cairo_new_path (cr);
3909 cairo_move_to (cr, 0, 0);
3910 cairo_line_to (cr, width, 0);
3911 cairo_line_to (cr, width, height);
3912 cairo_line_to (cr, 0, height);
3913 cairo_close_path (cr);
3914 cairo_stroke (cr);
3916 cairo_new_path (cr);
3917 cairo_move_to (cr, width - 10, 0);
3918 cairo_rel_line_to (cr, 0, height);
3919 cairo_stroke (cr);
3921 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
3923 cairo_new_path (cr);
3924 cairo_move_to (cr, width - 5 -3, height - 12);
3925 cairo_rel_line_to (cr, 6, 0);
3926 cairo_rel_line_to (cr, -3, 8);
3927 cairo_close_path (cr);
3928 cairo_fill (cr);
3930 cairo_new_path (cr);
3931 cairo_move_to (cr, width - 5 -3, 12);
3932 cairo_rel_line_to (cr, 6, 0);
3933 cairo_rel_line_to (cr, -3, -8);
3934 cairo_close_path (cr);
3935 cairo_fill (cr);
3937 if (swl->model != NULL) {
3938 GtkTreeIter iter;
3939 GString*str = g_string_new (NULL);
3940 int twidth = width, theight = height;
3943 cairo_new_path (cr);
3944 cairo_rectangle (cr, 2, 1, width - 2 - 12, height - 2);
3945 cairo_clip (cr);
3946 if (gtk_tree_model_get_iter_first (swl->model, &iter))
3947 do {
3948 char *astr = NULL, *newline;
3949 gtk_tree_model_get (swl->model, &iter, 0, &astr, -1);
3950 while (NULL != (newline = strchr (astr, '\n')))
3951 *newline = ' ';
3952 g_string_append (str, astr);
3953 g_string_append_c (str, '\n');
3954 g_free (astr);
3955 } while (gtk_tree_model_iter_next (swl->model, &iter));
3957 cairo_translate (cr, 4., 2.);
3959 draw_cairo_text (cr, str->str, &twidth, &theight, FALSE, FALSE, FALSE,
3960 swl->selection, FALSE);
3962 g_string_free (str, TRUE);
3965 cairo_new_path (cr);
3966 cairo_restore (cr);
3969 static void
3970 sheet_widget_list_class_init (SheetObjectWidgetClass *sow_class)
3972 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
3974 so_class->draw_cairo = &sheet_widget_list_draw_cairo;
3975 sow_class->create_widget = &sheet_widget_list_create_widget;
3978 GSF_CLASS (SheetWidgetList, sheet_widget_list,
3979 &sheet_widget_list_class_init, NULL,
3980 GNM_SOW_LIST_BASE_TYPE)
3982 /****************************************************************************/
3984 #define GNM_SOW_COMBO(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_COMBO_TYPE, SheetWidgetCombo))
3986 typedef SheetWidgetListBase SheetWidgetCombo;
3987 typedef SheetWidgetListBaseClass SheetWidgetComboClass;
3989 static void
3990 cb_combo_selection_changed (SheetWidgetListBase *swl,
3991 GtkComboBox *combo)
3993 int pos = swl->selection - 1;
3994 if (pos < 0) {
3995 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo))), "");
3996 pos = -1;
3998 gtk_combo_box_set_active (combo, pos);
4001 static void
4002 cb_combo_model_changed (SheetWidgetListBase *swl, GtkComboBox *combo)
4004 gtk_combo_box_set_model (GTK_COMBO_BOX (combo), swl->model);
4006 /* we can not set this until we have a model,
4007 * but after that we can not reset it */
4008 if (gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo)) < 0)
4009 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), 0);
4011 /* force entry to reload */
4012 cb_combo_selection_changed (swl, combo);
4015 static void
4016 cb_combo_changed (GtkComboBox *combo, SheetWidgetListBase *swl)
4018 int pos = gtk_combo_box_get_active (combo) + 1;
4019 sheet_widget_list_base_set_selection (swl, pos,
4020 widget_wbc (GTK_WIDGET (combo)));
4023 static GtkWidget *
4024 sheet_widget_combo_create_widget (SheetObjectWidget *sow)
4026 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
4027 GtkWidget *widget = gtk_event_box_new (), *combo;
4029 combo = gtk_combo_box_new_with_entry ();
4030 gtk_widget_set_can_focus (gtk_bin_get_child (GTK_BIN (combo)),
4031 FALSE);
4032 if (swl->model != NULL)
4033 g_object_set (G_OBJECT (combo),
4034 "model", swl->model,
4035 "entry-text-column", 0,
4036 "active", swl->selection - 1,
4037 NULL);
4039 g_signal_connect_object (G_OBJECT (swl), "model-changed",
4040 G_CALLBACK (cb_combo_model_changed), combo, 0);
4041 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
4042 G_CALLBACK (cb_combo_selection_changed), combo, 0);
4043 g_signal_connect (G_OBJECT (combo), "changed",
4044 G_CALLBACK (cb_combo_changed), swl);
4046 gtk_container_add (GTK_CONTAINER (widget), combo);
4047 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
4048 return widget;
4051 static void
4052 sheet_widget_combo_draw_cairo (SheetObject const *so, cairo_t *cr,
4053 double width, double height)
4055 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
4056 double halfheight = height/2;
4058 cairo_save (cr);
4059 cairo_set_line_width (cr, 0.5);
4060 cairo_set_source_rgb(cr, 0, 0, 0);
4062 cairo_new_path (cr);
4063 cairo_move_to (cr, 0, 0);
4064 cairo_line_to (cr, width, 0);
4065 cairo_line_to (cr, width, height);
4066 cairo_line_to (cr, 0, height);
4067 cairo_close_path (cr);
4068 cairo_stroke (cr);
4070 cairo_new_path (cr);
4071 cairo_move_to (cr, width - 10, 0);
4072 cairo_rel_line_to (cr, 0, height);
4073 cairo_stroke (cr);
4075 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
4077 cairo_new_path (cr);
4078 cairo_move_to (cr, width - 5 -3, halfheight - 4);
4079 cairo_rel_line_to (cr, 6, 0);
4080 cairo_rel_line_to (cr, -3, 8);
4081 cairo_close_path (cr);
4082 cairo_fill (cr);
4084 cairo_set_source_rgb(cr, 0, 0, 0);
4085 cairo_move_to (cr, 4., halfheight);
4087 if (swl->model != NULL) {
4088 GtkTreeIter iter;
4089 if (gtk_tree_model_iter_nth_child (swl->model, &iter, NULL,
4090 swl->selection - 1)) {
4091 char *str = NULL;
4092 gtk_tree_model_get (swl->model, &iter, 0, &str, -1);
4093 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
4094 g_free (str);
4098 cairo_new_path (cr);
4099 cairo_restore (cr);
4102 static void
4103 sheet_widget_combo_class_init (SheetObjectWidgetClass *sow_class)
4105 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
4107 so_class->draw_cairo = &sheet_widget_combo_draw_cairo;
4108 sow_class->create_widget = &sheet_widget_combo_create_widget;
4111 GSF_CLASS (SheetWidgetCombo, sheet_widget_combo,
4112 &sheet_widget_combo_class_init, NULL,
4113 GNM_SOW_LIST_BASE_TYPE)
4119 /**************************************************************************/
4122 * sheet_widget_init_clases:
4123 * @void:
4125 * Initilize the classes for the sheet-object-widgets. We need to initalize
4126 * them before we try loading a sheet that might contain sheet-object-widgets
4128 void
4129 sheet_object_widget_register (void)
4131 GNM_SOW_FRAME_TYPE;
4132 GNM_SOW_BUTTON_TYPE;
4133 GNM_SOW_SCROLLBAR_TYPE;
4134 GNM_SOW_CHECKBOX_TYPE;
4135 GNM_SOW_RADIO_BUTTON_TYPE;
4136 GNM_SOW_LIST_TYPE;
4137 GNM_SOW_COMBO_TYPE;
4138 GNM_SOW_SPIN_BUTTON_TYPE;
4139 GNM_SOW_SLIDER_TYPE;