Whitespace.
[gnumeric.git] / src / sheet-object-widget.c
blobc6501c91f0e90f575f36b414d6fdf62ceaa472d0
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * sheet-object-widget.c: SheetObject wrappers for simple gtk widgets.
6 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include "gnumeric.h"
26 #include "application.h"
27 #include "sheet-object-widget-impl.h"
28 #include "widgets/gnm-radiobutton.h"
29 #include "gnm-pane.h"
30 #include "gnumeric-simple-canvas.h"
31 #include "gui-util.h"
32 #include "gutils.h"
33 #include "dependent.h"
34 #include "sheet-control-gui.h"
35 #include "sheet-object-impl.h"
36 #include "expr.h"
37 #include "parse-util.h"
38 #include "value.h"
39 #include "ranges.h"
40 #include "selection.h"
41 #include "wbc-gtk.h"
42 #include "workbook.h"
43 #include "sheet.h"
44 #include "cell.h"
45 #include "mathfunc.h"
46 #include "gnumeric-expr-entry.h"
47 #include "dialogs.h"
48 #include "dialogs/help.h"
49 #include "xml-sax.h"
50 #include "commands.h"
51 #include "gnm-format.h"
52 #include "number-match.h"
54 #include <goffice/goffice.h>
56 #include <gsf/gsf-impl-utils.h>
57 #include <libxml/globals.h>
58 #include <gdk/gdkkeysyms.h>
59 #include <gtk/gtk.h>
60 #include <math.h>
61 #include <string.h>
63 #define CXML2C(s) ((char const *)(s))
64 #define CC2XML(s) ((xmlChar const *)(s))
66 static inline gboolean
67 attr_eq (const xmlChar *a, const char *s)
69 return !strcmp (CXML2C (a), s);
72 /****************************************************************************/
74 static void
75 cb_so_get_ref (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, gpointer user)
77 GnmDependent **pdep = user;
78 *pdep = dep;
81 static GnmCellRef *
82 so_get_ref (SheetObject const *so, GnmCellRef *res, gboolean force_sheet)
84 GnmValue *target;
85 GnmDependent *dep = NULL;
87 g_return_val_if_fail (so != NULL, NULL);
89 /* Let's hope there's just one. */
90 sheet_object_foreach_dep ((SheetObject*)so, cb_so_get_ref, &dep);
91 g_return_val_if_fail (dep, NULL);
93 if (dep->texpr == NULL)
94 return NULL;
96 target = gnm_expr_top_get_range (dep->texpr);
97 if (target == NULL)
98 return NULL;
100 *res = target->v_range.cell.a;
101 value_release (target);
103 if (force_sheet && res->sheet == NULL)
104 res->sheet = sheet_object_get_sheet (so);
105 return res;
108 static void
109 cb_so_clear_sheet (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, G_GNUC_UNUSED gpointer user)
111 if (dependent_is_linked (dep))
112 dependent_unlink (dep);
113 dep->sheet = NULL;
116 static gboolean
117 so_clear_sheet (SheetObject *so)
119 /* Note: This implements sheet_object_clear_sheet. */
120 sheet_object_foreach_dep (so, cb_so_clear_sheet, NULL);
121 return FALSE;
124 static GocWidget *
125 get_goc_widget (SheetObjectView *view)
127 GocGroup *group = GOC_GROUP (view);
129 if (group == NULL || group->children == NULL)
130 return NULL;
132 return GOC_WIDGET (group->children->data);
135 static void
136 so_widget_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
138 GocItem *view = GOC_ITEM (sov);
139 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
140 double left = MIN (coords [0], coords [2]) / scale;
141 double top = MIN (coords [1], coords [3]) / scale;
142 double width = (fabs (coords [2] - coords [0]) + 1.) / scale;
143 double height = (fabs (coords [3] - coords [1]) + 1.) / scale;
145 /* We only need the next check for frames, but it doesn't hurt otherwise. */
146 if (width < 8.)
147 width = 8.;
149 if (visible) {
150 /* NOTE : far point is EXCLUDED so we add 1 */
151 goc_widget_set_bounds (get_goc_widget (sov),
152 left, top, width, height);
153 goc_item_show (view);
154 } else
155 goc_item_hide (view);
158 static GdkWindow *
159 so_widget_view_get_window (GocItem *item)
161 GocGroup *group = GOC_GROUP (item);
162 return goc_item_get_window (GOC_ITEM (group->children->data));
165 static void
166 so_widget_view_class_init (SheetObjectViewClass *sov_klass)
168 GocItemClass *item_klass = (GocItemClass *) sov_klass;
169 sov_klass->set_bounds = so_widget_view_set_bounds;
170 item_klass->get_window = so_widget_view_get_window;
173 static GSF_CLASS (SOWidgetView, so_widget_view,
174 so_widget_view_class_init, NULL,
175 GNM_SO_VIEW_TYPE)
177 /****************************************************************************/
179 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-config-dialog"
181 #define GNM_SOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_TYPE, SheetObjectWidgetClass))
182 #define SOW_CLASS(so) (GNM_SOW_CLASS (G_OBJECT_GET_CLASS(so)))
184 #define SOW_MAKE_TYPE(n1, n2, fn_config, fn_set_sheet, fn_clear_sheet, fn_foreach_dep, \
185 fn_copy, fn_write_sax, fn_prep_sax_parser, \
186 fn_get_property, fn_set_property, \
187 fn_draw_cairo, class_init_code) \
189 static void \
190 sheet_widget_ ## n1 ## _class_init (GObjectClass *object_class) \
192 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class); \
193 SheetObjectClass *so_class = GNM_SO_CLASS (object_class); \
194 object_class->finalize = &sheet_widget_ ## n1 ## _finalize; \
195 object_class->set_property = fn_set_property; \
196 object_class->get_property = fn_get_property; \
197 so_class->user_config = fn_config; \
198 so_class->interactive = TRUE; \
199 so_class->assign_to_sheet = fn_set_sheet; \
200 so_class->remove_from_sheet = fn_clear_sheet; \
201 so_class->foreach_dep = fn_foreach_dep; \
202 so_class->copy = fn_copy; \
203 so_class->write_xml_sax = fn_write_sax; \
204 so_class->prep_sax_parser = fn_prep_sax_parser; \
205 so_class->draw_cairo = fn_draw_cairo; \
206 sow_class->create_widget = &sheet_widget_ ## n1 ## _create_widget; \
207 { class_init_code; } \
210 GSF_CLASS (SheetWidget ## n2, sheet_widget_ ## n1, \
211 &sheet_widget_ ## n1 ## _class_init, \
212 &sheet_widget_ ## n1 ## _init, \
213 GNM_SOW_TYPE)
215 typedef struct {
216 SheetObject so;
217 } SheetObjectWidget;
219 typedef struct {
220 SheetObjectClass parent_class;
221 GtkWidget *(*create_widget)(SheetObjectWidget *);
222 } SheetObjectWidgetClass;
224 static GObjectClass *sheet_object_widget_class = NULL;
226 static GtkWidget *
227 sow_create_widget (SheetObjectWidget *sow)
229 GtkWidget *w = SOW_CLASS(sow)->create_widget (sow);
230 GtkStyleContext *context = gtk_widget_get_style_context (w);
231 gtk_style_context_add_class (context, "sheet-object");
232 return w;
235 static void
236 sheet_widget_draw_cairo (SheetObject const *so, cairo_t *cr,
237 double width, double height)
239 /* This is the default for so widgets without their own method */
240 /* See bugs #705638 and #705640 */
241 if (NULL != gdk_screen_get_default ()) {
242 GtkWidget *win = gtk_offscreen_window_new ();
243 GtkWidget *w = sow_create_widget (GNM_SOW (so));
245 gtk_container_add (GTK_CONTAINER (win), w);
246 gtk_widget_set_size_request (w, width, height);
247 gtk_widget_show_all (win);
248 gtk_container_propagate_draw (GTK_CONTAINER (win), w, cr);
249 gtk_widget_destroy (win);
250 } else
251 g_warning (_("Because of GTK bug #705640, a sheet object widget is not being printed."));
254 static void
255 sax_write_dep (GsfXMLOut *output, GnmDependent const *dep, char const *id,
256 GnmConventions const *convs)
258 if (dep->texpr != NULL) {
259 GnmParsePos pos;
260 char *val;
262 parse_pos_init_dep (&pos, dep);
263 val = gnm_expr_top_as_string (dep->texpr, &pos, convs);
264 gsf_xml_out_add_cstr (output, id, val);
265 g_free (val);
269 static gboolean
270 sax_read_dep (xmlChar const * const *attrs, char const *name,
271 GnmDependent *dep, GsfXMLIn *xin, GnmConventions const *convs)
273 g_return_val_if_fail (attrs != NULL, FALSE);
274 g_return_val_if_fail (attrs[0] != NULL, FALSE);
275 g_return_val_if_fail (attrs[1] != NULL, FALSE);
277 if (!attr_eq (attrs[0], name))
278 return FALSE;
280 dep->sheet = NULL;
281 if (attrs[1] != NULL && *attrs[1] != '\0') {
282 GnmParsePos pp;
284 parse_pos_init_sheet (&pp, gnm_xml_in_cur_sheet (xin));
285 dep->texpr = gnm_expr_parse_str (CXML2C (attrs[1]), &pp,
286 GNM_EXPR_PARSE_DEFAULT,
287 convs, NULL);
288 } else
289 dep->texpr = NULL;
291 return TRUE;
294 static SheetObjectView *
295 sheet_object_widget_new_view (SheetObject *so, SheetObjectViewContainer *container)
297 GtkWidget *view_widget = sow_create_widget (GNM_SOW (so));
298 GocItem *view_item = goc_item_new (
299 gnm_pane_object_group (GNM_PANE (container)),
300 so_widget_view_get_type (),
301 NULL);
302 goc_item_new (GOC_GROUP (view_item),
303 GOC_TYPE_WIDGET,
304 "widget", view_widget,
305 NULL);
306 /* g_warning ("%p is widget for so %p", (void *)view_widget, (void *)so);*/
307 gtk_widget_show_all (view_widget);
308 goc_item_hide (view_item);
309 gnm_pane_widget_register (so, view_widget, view_item);
310 return gnm_pane_object_register (so, view_item, TRUE);
313 static void
314 sheet_object_widget_class_init (GObjectClass *object_class)
316 SheetObjectClass *so_class = GNM_SO_CLASS (object_class);
317 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class);
319 sheet_object_widget_class = G_OBJECT_CLASS (object_class);
321 /* SheetObject class method overrides */
322 so_class->new_view = sheet_object_widget_new_view;
323 so_class->rubber_band_directly = TRUE;
324 so_class->draw_cairo = sheet_widget_draw_cairo;
326 sow_class->create_widget = NULL;
329 static void
330 sheet_object_widget_init (SheetObjectWidget *sow)
332 SheetObject *so = GNM_SO (sow);
333 so->flags |= SHEET_OBJECT_CAN_PRESS;
336 GSF_CLASS (SheetObjectWidget, sheet_object_widget,
337 sheet_object_widget_class_init,
338 sheet_object_widget_init,
339 GNM_SO_TYPE)
341 static WorkbookControl *
342 widget_wbc (GtkWidget *widget)
344 return scg_wbc (GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (widget, GNM_SIMPLE_CANVAS_TYPE))->scg);
348 /****************************************************************************/
349 #define GNM_SOW_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_FRAME_TYPE, SheetWidgetFrame))
350 typedef struct {
351 SheetObjectWidget sow;
352 char *label;
353 } SheetWidgetFrame;
354 typedef SheetObjectWidgetClass SheetWidgetFrameClass;
356 enum {
357 SOF_PROP_0 = 0,
358 SOF_PROP_TEXT
361 static void
362 sheet_widget_frame_get_property (GObject *obj, guint param_id,
363 GValue *value, GParamSpec *pspec)
365 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
367 switch (param_id) {
368 case SOF_PROP_TEXT:
369 g_value_set_string (value, swf->label);
370 break;
371 default:
372 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
373 break;
377 static void
378 sheet_widget_frame_set_property (GObject *obj, guint param_id,
379 GValue const *value, GParamSpec *pspec)
381 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
383 switch (param_id) {
384 case SOF_PROP_TEXT:
385 sheet_widget_frame_set_label (GNM_SO (swf),
386 g_value_get_string (value));
387 break;
388 default:
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
390 return;
395 static void
396 sheet_widget_frame_init_full (SheetWidgetFrame *swf, char const *text)
398 swf->label = g_strdup (text);
401 static void
402 sheet_widget_frame_init (SheetWidgetFrame *swf)
404 sheet_widget_frame_init_full (swf, _("Frame"));
407 static void
408 sheet_widget_frame_finalize (GObject *obj)
410 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
412 g_free (swf->label);
413 swf->label = NULL;
415 sheet_object_widget_class->finalize (obj);
418 static GtkWidget *
419 sheet_widget_frame_create_widget (SheetObjectWidget *sow)
421 GtkWidget *widget = gtk_event_box_new (),
422 *frame = gtk_frame_new (GNM_SOW_FRAME (sow)->label);
423 gtk_container_add (GTK_CONTAINER (widget), frame);
424 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
425 return widget;
428 static void
429 sheet_widget_frame_copy (SheetObject *dst, SheetObject const *src)
431 sheet_widget_frame_init_full (GNM_SOW_FRAME (dst),
432 GNM_SOW_FRAME (src)->label);
435 static void
436 sheet_widget_frame_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
437 G_GNUC_UNUSED GnmConventions const *convs)
439 SheetWidgetFrame const *swf = GNM_SOW_FRAME (so);
440 gsf_xml_out_add_cstr (output, "Label", swf->label);
443 static void
444 sheet_widget_frame_prep_sax_parser (SheetObject *so, G_GNUC_UNUSED GsfXMLIn *xin,
445 xmlChar const **attrs,
446 G_GNUC_UNUSED GnmConventions const *convs)
448 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
449 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
450 if (attr_eq (attrs[0], "Label")) {
451 g_free (swf->label);
452 swf->label = g_strdup (CXML2C (attrs[1]));
456 typedef struct {
457 GtkWidget *dialog;
458 GtkWidget *label;
460 char *old_label;
461 GtkWidget *old_focus;
463 WBCGtk *wbcg;
464 SheetWidgetFrame *swf;
465 Sheet *sheet;
466 } FrameConfigState;
468 static void
469 cb_frame_config_destroy (FrameConfigState *state)
471 g_return_if_fail (state != NULL);
473 g_free (state->old_label);
474 state->old_label = NULL;
475 state->dialog = NULL;
476 g_free (state);
479 static void
480 cb_frame_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
482 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
484 cmd_so_set_frame_label (GNM_WBC (state->wbcg),
485 GNM_SO (state->swf),
486 g_strdup (state->old_label), g_strdup (text));
487 gtk_widget_destroy (state->dialog);
490 void
491 sheet_widget_frame_set_label (SheetObject *so, char const* str)
493 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
494 GList *ptr;
496 str = str ? str : "";
498 if (go_str_compare (str, swf->label) == 0)
499 return;
501 g_free (swf->label);
502 swf->label = g_strdup (str);
504 for (ptr = swf->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
505 SheetObjectView *view = ptr->data;
506 GocWidget *item = get_goc_widget (view);
507 GList *children = gtk_container_get_children (GTK_CONTAINER (item->widget));
508 gtk_frame_set_label (GTK_FRAME (children->data), str);
509 g_list_free (children);
513 static void
514 cb_frame_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
516 sheet_widget_frame_set_label (GNM_SO (state->swf), state->old_label);
518 gtk_widget_destroy (state->dialog);
521 static void
522 cb_frame_label_changed (GtkWidget *entry, FrameConfigState *state)
524 gchar const *text;
526 text = gtk_entry_get_text(GTK_ENTRY(entry));
527 sheet_widget_frame_set_label (GNM_SO (state->swf), text);
530 static void
531 sheet_widget_frame_user_config (SheetObject *so, SheetControl *sc)
533 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
534 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
535 FrameConfigState *state;
536 GtkBuilder *gui;
538 g_return_if_fail (swf != NULL);
540 /* Only pop up one copy per workbook */
541 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
542 return;
544 gui = gnm_gtk_builder_load ("so-frame.ui", NULL, GO_CMD_CONTEXT (wbcg));
545 if (!gui)
546 return;
547 state = g_new (FrameConfigState, 1);
548 state->swf = swf;
549 state->wbcg = wbcg;
550 state->sheet = sc_sheet (sc);
551 state->old_focus = NULL;
552 state->old_label = g_strdup(swf->label);
553 state->dialog = go_gtk_builder_get_widget (gui, "so_frame");
555 state->label = go_gtk_builder_get_widget (gui, "entry");
556 gtk_entry_set_text (GTK_ENTRY(state->label), swf->label);
557 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
558 gnm_editable_enters (GTK_WINDOW (state->dialog),
559 GTK_WIDGET (state->label));
561 g_signal_connect (G_OBJECT(state->label),
562 "changed",
563 G_CALLBACK (cb_frame_label_changed), state);
564 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
565 "ok_button")),
566 "clicked",
567 G_CALLBACK (cb_frame_config_ok_clicked), state);
568 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
569 "cancel_button")),
570 "clicked",
571 G_CALLBACK (cb_frame_config_cancel_clicked), state);
573 gnm_init_help_button (
574 go_gtk_builder_get_widget (gui, "help_button"),
575 GNUMERIC_HELP_LINK_SO_FRAME);
578 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
579 SHEET_OBJECT_CONFIG_KEY);
581 wbc_gtk_attach_guru (state->wbcg, state->dialog);
582 g_object_set_data_full (G_OBJECT (state->dialog),
583 "state", state, (GDestroyNotify) cb_frame_config_destroy);
584 g_object_unref (gui);
586 gtk_widget_show (state->dialog);
589 static PangoFontDescription *
590 get_font (void)
592 // Note: Under gnumeric, we get a proper font using GtkStyleContext.
593 // Under ssconvert, we try GSettings.
594 // The 'sans 10' is just insurance
596 PangoFontDescription *desc;
597 PangoFontMask mask;
598 int size = 0;
600 if (gdk_screen_get_default ()) {
601 // Without a default screen, the following will crash
602 // with newer gtk+.
603 GtkStyleContext *style = gtk_style_context_new ();
604 GtkWidgetPath *path = gtk_widget_path_new ();
606 gtk_style_context_set_path (style, path);
607 gtk_widget_path_unref (path);
609 gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL,
610 GTK_STYLE_PROPERTY_FONT, &desc, NULL);
611 g_object_unref (style);
612 } else
613 desc = pango_font_description_new ();
615 mask = pango_font_description_get_set_fields (desc);
616 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
617 size = pango_font_description_get_size (desc);
619 if (gnm_debug_flag ("so-font")) {
620 char *s = pango_font_description_to_string (desc);
621 g_printerr ("from GtkStyleContext font=\"%s\", family set = %i,"
622 " size set = %i, size = %i\n",
623 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
624 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
625 g_free (s);
628 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
629 /* Trying gsettings */
630 GSettings *set = g_settings_new ("org.gnome.desktop.interface");
631 char *font_name = g_settings_get_string (set, "font-name");
632 if (font_name != NULL) {
633 pango_font_description_free (desc);
634 desc = pango_font_description_from_string (font_name);
635 g_free (font_name);
636 mask = pango_font_description_get_set_fields (desc);
637 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
638 size = pango_font_description_get_size (desc);
639 else
640 size = 0;
641 if (gnm_debug_flag ("so-font")) {
642 char *s = pango_font_description_to_string (desc);
643 g_printerr ("from GSettings: font=\"%s\", family set = %i,"
644 " size set = %i, size = %i\n",
645 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
646 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
647 g_free (s);
652 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
653 pango_font_description_free (desc);
654 desc = pango_font_description_from_string ("sans 10");
655 if (gnm_debug_flag ("so-font"))
656 g_printerr ("Using \"sans 10\" instead.\n");
659 return desc;
662 static void
663 draw_cairo_text (cairo_t *cr, char const *text, int *pwidth, int *pheight,
664 gboolean centered_v, gboolean centered_h, gboolean single, gint highlight_n, gboolean scale)
666 PangoLayout *layout = pango_cairo_create_layout (cr);
667 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
668 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
669 PangoFontDescription *desc = get_font ();
670 int width, height;
672 pango_context_set_font_description
673 (pango_layout_get_context (layout), desc);
674 pango_layout_set_spacing (layout, 3 * PANGO_SCALE);
675 pango_layout_set_single_paragraph_mode (layout, single);
676 pango_layout_set_text (layout, text, -1);
677 pango_layout_get_pixel_size (layout, &width, &height);
679 cairo_scale (cr, scale_h, scale_v);
681 if (scale && pwidth != NULL && pheight != NULL) {
682 double sc_x = ((double) *pwidth)/(width * scale_h);
683 double sc_y = ((double) *pheight)/(height * scale_v);
684 double sc = MIN(sc_x, sc_y);
686 if (sc < 1.)
687 cairo_scale (cr, sc, sc);
690 if (centered_v)
691 cairo_rel_move_to (cr, 0., 0.5 - ((double)height)/2.);
692 if (centered_h)
693 cairo_rel_move_to (cr, 0.5 - ((double)width)/2., 0.);
694 if (highlight_n > 0 && pheight != NULL && pwidth != NULL) {
695 PangoLayoutIter *pliter;
696 gboolean got_line = TRUE;
697 int i;
698 pliter = pango_layout_get_iter (layout);
699 for (i = 1; i < highlight_n; i++)
700 got_line = pango_layout_iter_next_line (pliter);
702 if (got_line) {
703 int y0, y1;
704 double dy0 = 0, dy1 = 0;
705 pango_layout_iter_get_line_yrange (pliter, &y0, &y1);
706 dy0 = y0 / (double)PANGO_SCALE;
707 dy1 = y1 / (double)PANGO_SCALE;
709 if (dy1 > (*pheight - 4)/scale_v)
710 cairo_translate (cr, 0, (*pheight - 4)/scale_v - dy1);
712 cairo_new_path (cr);
713 cairo_rectangle (cr, -4/scale_h, dy0,
714 *pwidth/scale_h, dy1 - dy0);
715 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
716 cairo_fill (cr);
718 pango_layout_iter_free (pliter);
719 cairo_set_source_rgb(cr, 0, 0, 0);
721 pango_cairo_show_layout (cr, layout);
722 pango_font_description_free (desc);
723 g_object_unref (layout);
725 if (pwidth)
726 *pwidth = width * scale_h;
727 if (pheight)
728 *pheight = height * scale_v;
731 static void
732 sheet_widget_frame_draw_cairo (SheetObject const *so, cairo_t *cr,
733 double width, double height)
735 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
737 int theight = 0, twidth = 0;
738 cairo_save (cr);
739 cairo_move_to (cr, 10, 0);
741 cairo_save (cr);
742 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
743 draw_cairo_text (cr, swf->label, &twidth, &theight, FALSE, FALSE, TRUE, 0, FALSE);
744 cairo_restore (cr);
746 cairo_set_line_width (cr, 1);
747 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
748 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
749 cairo_new_path (cr);
750 cairo_move_to (cr, 6, theight/2);
751 cairo_line_to (cr, 0, theight/2);
752 cairo_line_to (cr, 0, height);
753 cairo_line_to (cr, width, height);
754 cairo_line_to (cr, width, theight/2);
755 cairo_line_to (cr, 14 + twidth, theight/2);
756 cairo_stroke (cr);
758 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
759 cairo_new_path (cr);
760 cairo_move_to (cr, 6, theight/2 + 1);
761 cairo_line_to (cr, 1, theight/2 + 1);
762 cairo_line_to (cr, 1, height - 1);
763 cairo_line_to (cr, width - 1, height - 1);
764 cairo_line_to (cr, width - 1, theight/2 + 1);
765 cairo_line_to (cr, 14 + twidth, theight/2 + 1);
766 cairo_stroke (cr);
768 cairo_new_path (cr);
769 cairo_restore (cr);
772 SOW_MAKE_TYPE (frame, Frame,
773 sheet_widget_frame_user_config,
774 NULL,
775 NULL,
776 NULL,
777 sheet_widget_frame_copy,
778 sheet_widget_frame_write_xml_sax,
779 sheet_widget_frame_prep_sax_parser,
780 sheet_widget_frame_get_property,
781 sheet_widget_frame_set_property,
782 sheet_widget_frame_draw_cairo,
784 g_object_class_install_property
785 (object_class, SOF_PROP_TEXT,
786 g_param_spec_string ("text", NULL, NULL, NULL,
787 GSF_PARAM_STATIC | G_PARAM_READWRITE));
790 /****************************************************************************/
791 #define GNM_SOW_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_BUTTON_TYPE, SheetWidgetButton))
792 #define DEP_TO_BUTTON(d_ptr) (SheetWidgetButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetButton, dep))
793 typedef struct {
794 SheetObjectWidget sow;
796 GnmDependent dep;
797 char *label;
798 PangoAttrList *markup;
799 gboolean value;
800 } SheetWidgetButton;
801 typedef SheetObjectWidgetClass SheetWidgetButtonClass;
803 enum {
804 SOB_PROP_0 = 0,
805 SOB_PROP_TEXT,
806 SOB_PROP_MARKUP
809 static void
810 sheet_widget_button_get_property (GObject *obj, guint param_id,
811 GValue *value, GParamSpec *pspec)
813 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
815 switch (param_id) {
816 case SOB_PROP_TEXT:
817 g_value_set_string (value, swb->label);
818 break;
819 case SOB_PROP_MARKUP:
820 g_value_set_boxed (value, NULL); /* swb->markup */
821 break;
822 default:
823 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
824 break;
828 static void
829 sheet_widget_button_set_property (GObject *obj, guint param_id,
830 GValue const *value, GParamSpec *pspec)
832 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
834 switch (param_id) {
835 case SOB_PROP_TEXT:
836 sheet_widget_button_set_label (GNM_SO (swb),
837 g_value_get_string (value));
838 break;
839 case SOB_PROP_MARKUP:
840 #if 0
841 sheet_widget_button_set_markup (GNM_SO (swb),
842 g_value_peek_pointer (value));
843 #endif
844 break;
845 default:
846 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
847 return;
851 static void
852 button_eval (GnmDependent *dep)
854 GnmValue *v;
855 GnmEvalPos pos;
856 gboolean err, result;
858 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
859 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
860 result = value_get_as_bool (v, &err);
861 value_release (v);
862 if (!err) {
863 SheetWidgetButton *swb = DEP_TO_BUTTON(dep);
865 swb->value = result;
869 static void
870 button_debug_name (GnmDependent const *dep, GString *target)
872 g_string_append_printf (target, "Button%p", (void *)dep);
875 static DEPENDENT_MAKE_TYPE (button, NULL)
877 static void
878 sheet_widget_button_init_full (SheetWidgetButton *swb,
879 GnmCellRef const *ref,
880 char const *text,
881 PangoAttrList *markup)
883 SheetObject *so = GNM_SO (swb);
885 so->flags &= ~SHEET_OBJECT_PRINT;
886 swb->label = g_strdup (text);
887 swb->markup = markup;
888 swb->value = FALSE;
889 swb->dep.sheet = NULL;
890 swb->dep.flags = button_get_dep_type ();
891 swb->dep.texpr = (ref != NULL)
892 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
893 : NULL;
894 if (markup) pango_attr_list_ref (markup);
897 static void
898 sheet_widget_button_init (SheetWidgetButton *swb)
900 sheet_widget_button_init_full (swb, NULL, _("Button"), NULL);
903 static void
904 sheet_widget_button_finalize (GObject *obj)
906 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
908 g_free (swb->label);
909 swb->label = NULL;
911 if (swb->markup) {
912 pango_attr_list_unref (swb->markup);
913 swb->markup = NULL;
916 dependent_set_expr (&swb->dep, NULL);
918 sheet_object_widget_class->finalize (obj);
921 static void
922 cb_button_pressed (GtkToggleButton *button, SheetWidgetButton *swb)
924 GnmCellRef ref;
926 swb->value = TRUE;
928 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
929 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
930 _("Pressed Button"),
931 &ref, value_new_bool (TRUE),
932 sheet_object_get_sheet (GNM_SO (swb)));
936 static void
937 cb_button_released (GtkToggleButton *button, SheetWidgetButton *swb)
939 GnmCellRef ref;
941 swb->value = FALSE;
943 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
944 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
945 _("Released Button"),
946 &ref, value_new_bool (FALSE),
947 sheet_object_get_sheet (GNM_SO (swb)));
951 static GtkWidget *
952 sheet_widget_button_create_widget (SheetObjectWidget *sow)
954 SheetWidgetButton *swb = GNM_SOW_BUTTON (sow);
955 GtkWidget *w = gtk_button_new_with_label (swb->label);
956 gtk_widget_set_can_focus (w, FALSE);
957 gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))),
958 swb->markup);
959 g_signal_connect (G_OBJECT (w),
960 "pressed",
961 G_CALLBACK (cb_button_pressed), swb);
962 g_signal_connect (G_OBJECT (w),
963 "released",
964 G_CALLBACK (cb_button_released), swb);
965 return w;
968 static void
969 sheet_widget_button_copy (SheetObject *dst, SheetObject const *src)
971 SheetWidgetButton const *src_swb = GNM_SOW_BUTTON (src);
972 SheetWidgetButton *dst_swb = GNM_SOW_BUTTON (dst);
973 GnmCellRef ref;
974 sheet_widget_button_init_full (dst_swb,
975 so_get_ref (src, &ref, FALSE),
976 src_swb->label,
977 src_swb->markup);
978 dst_swb->value = src_swb->value;
981 typedef struct {
982 GtkWidget *dialog;
983 GnmExprEntry *expression;
984 GtkWidget *label;
986 char *old_label;
987 GtkWidget *old_focus;
989 WBCGtk *wbcg;
990 SheetWidgetButton *swb;
991 Sheet *sheet;
992 } ButtonConfigState;
994 static void
995 cb_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
996 ButtonConfigState *state)
998 /* Note: half of the set-focus action is handle by the default
999 * callback installed by wbc_gtk_attach_guru */
1001 /* Force an update of the content in case it needs tweaking (eg make it
1002 * absolute) */
1003 if (state->old_focus != NULL &&
1004 GNM_EXPR_ENTRY_IS (gtk_widget_get_parent (state->old_focus))) {
1005 GnmParsePos pp;
1006 GnmExprTop const *texpr = gnm_expr_entry_parse
1007 (GNM_EXPR_ENTRY (gtk_widget_get_parent (state->old_focus)),
1008 parse_pos_init_sheet (&pp, state->sheet),
1009 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1010 if (texpr != NULL)
1011 gnm_expr_top_unref (texpr);
1013 state->old_focus = focus_widget;
1016 static void
1017 cb_button_config_destroy (ButtonConfigState *state)
1019 g_return_if_fail (state != NULL);
1021 g_free (state->old_label);
1022 state->old_label = NULL;
1023 state->dialog = NULL;
1024 g_free (state);
1027 static void
1028 cb_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1030 SheetObject *so = GNM_SO (state->swb);
1031 GnmParsePos pp;
1032 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1033 parse_pos_init_sheet (&pp, so->sheet),
1034 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1035 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
1037 cmd_so_set_button (GNM_WBC (state->wbcg), so,
1038 texpr, g_strdup (state->old_label), g_strdup (text));
1040 gtk_widget_destroy (state->dialog);
1043 static void
1044 cb_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1046 sheet_widget_button_set_label (GNM_SO (state->swb),
1047 state->old_label);
1048 gtk_widget_destroy (state->dialog);
1051 static void
1052 cb_button_label_changed (GtkEntry *entry, ButtonConfigState *state)
1054 sheet_widget_button_set_label (GNM_SO (state->swb),
1055 gtk_entry_get_text (entry));
1058 static void
1059 sheet_widget_button_user_config (SheetObject *so, SheetControl *sc)
1061 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1062 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1063 ButtonConfigState *state;
1064 GtkWidget *grid;
1065 GtkBuilder *gui;
1067 g_return_if_fail (swb != NULL);
1069 /* Only pop up one copy per workbook */
1070 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1071 return;
1073 gui = gnm_gtk_builder_load ("so-button.ui", NULL, GO_CMD_CONTEXT (wbcg));
1074 if (!gui)
1075 return;
1076 state = g_new (ButtonConfigState, 1);
1077 state->swb = swb;
1078 state->wbcg = wbcg;
1079 state->sheet = sc_sheet (sc);
1080 state->old_focus = NULL;
1081 state->old_label = g_strdup (swb->label);
1082 state->dialog = go_gtk_builder_get_widget (gui, "SO-Button");
1084 grid = go_gtk_builder_get_widget (gui, "main-grid");
1086 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1087 gnm_expr_entry_set_flags (state->expression,
1088 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1089 GNM_EE_MASK);
1090 gnm_expr_entry_load_from_dep (state->expression, &swb->dep);
1091 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1092 GTK_WIDGET (state->expression));
1093 gtk_grid_attach (GTK_GRID (grid),
1094 GTK_WIDGET (state->expression), 1, 0, 1, 1);
1095 gtk_widget_show (GTK_WIDGET (state->expression));
1097 state->label = go_gtk_builder_get_widget (gui, "label_entry");
1098 gtk_entry_set_text (GTK_ENTRY (state->label), swb->label);
1099 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
1100 gnm_editable_enters (GTK_WINDOW (state->dialog),
1101 GTK_WIDGET (state->expression));
1102 gnm_editable_enters (GTK_WINDOW (state->dialog),
1103 GTK_WIDGET (state->label));
1105 g_signal_connect (G_OBJECT (state->label),
1106 "changed",
1107 G_CALLBACK (cb_button_label_changed), state);
1108 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1109 "clicked",
1110 G_CALLBACK (cb_button_config_ok_clicked), state);
1111 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1112 "clicked",
1113 G_CALLBACK (cb_button_config_cancel_clicked), state);
1115 gnm_init_help_button (
1116 go_gtk_builder_get_widget (gui, "help_button"),
1117 GNUMERIC_HELP_LINK_SO_BUTTON);
1119 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1120 SHEET_OBJECT_CONFIG_KEY);
1122 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1123 g_object_set_data_full (G_OBJECT (state->dialog),
1124 "state", state, (GDestroyNotify) cb_button_config_destroy);
1126 /* Note: half of the set-focus action is handle by the default */
1127 /* callback installed by wbc_gtk_attach_guru */
1128 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1129 G_CALLBACK (cb_button_set_focus), state);
1130 g_object_unref (gui);
1132 gtk_widget_show (state->dialog);
1135 static gboolean
1136 sheet_widget_button_set_sheet (SheetObject *so, Sheet *sheet)
1138 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1140 dependent_set_sheet (&swb->dep, sheet);
1142 return FALSE;
1145 static void
1146 sheet_widget_button_foreach_dep (SheetObject *so,
1147 SheetObjectForeachDepFunc func,
1148 gpointer user)
1150 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1151 func (&swb->dep, so, user);
1154 static void
1155 sheet_widget_button_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1156 GnmConventions const *convs)
1158 /* FIXME: markup */
1159 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1160 gsf_xml_out_add_cstr (output, "Label", swb->label);
1161 gsf_xml_out_add_int (output, "Value", swb->value);
1162 sax_write_dep (output, &swb->dep, "Input", convs);
1165 static void
1166 sheet_widget_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1167 xmlChar const **attrs,
1168 GnmConventions const *convs)
1170 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1171 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1172 if (attr_eq (attrs[0], "Label"))
1173 g_object_set (G_OBJECT (swb), "text", attrs[1], NULL);
1174 else if (gnm_xml_attr_int (attrs, "Value", &swb->value))
1176 else if (sax_read_dep (attrs, "Input", &swb->dep, xin, convs))
1180 void
1181 sheet_widget_button_set_link (SheetObject *so, GnmExprTop const *texpr)
1183 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1184 dependent_set_expr (&swb->dep, texpr);
1185 if (texpr && swb->dep.sheet)
1186 dependent_link (&swb->dep);
1189 GnmExprTop const *
1190 sheet_widget_button_get_link (SheetObject *so)
1192 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1193 GnmExprTop const *texpr = swb->dep.texpr;
1195 if (texpr)
1196 gnm_expr_top_ref (texpr);
1198 return texpr;
1202 void
1203 sheet_widget_button_set_label (SheetObject *so, char const *str)
1205 GList *ptr;
1206 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1207 char *new_label;
1209 if (go_str_compare (str, swb->label) == 0)
1210 return;
1212 new_label = g_strdup (str);
1213 g_free (swb->label);
1214 swb->label = new_label;
1216 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1217 SheetObjectView *view = ptr->data;
1218 GocWidget *item = get_goc_widget (view);
1219 gtk_button_set_label (GTK_BUTTON (item->widget), swb->label);
1223 void
1224 sheet_widget_button_set_markup (SheetObject *so, PangoAttrList *markup)
1226 GList *ptr;
1227 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1229 if (markup == swb->markup)
1230 return;
1232 if (swb->markup) pango_attr_list_unref (swb->markup);
1233 swb->markup = markup;
1234 if (markup) pango_attr_list_ref (markup);
1236 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1237 SheetObjectView *view = ptr->data;
1238 GocWidget *item = get_goc_widget (view);
1239 GtkLabel *lab =
1240 GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->widget)));
1241 gtk_label_set_attributes (lab, swb->markup);
1245 static void
1246 sheet_widget_button_draw_cairo (SheetObject const *so, cairo_t *cr,
1247 double width, double height)
1249 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1250 int twidth, theight;
1251 double half_line;
1252 double radius = 10;
1254 if (height < 3 * radius)
1255 radius = height / 3.;
1256 if (width < 3 * radius)
1257 radius = width / 3.;
1258 if (radius < 1)
1259 radius = 1;
1260 half_line = radius * 0.15;
1262 cairo_save (cr);
1263 cairo_set_line_width (cr, 2 * half_line);
1264 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1266 cairo_new_path (cr);
1267 cairo_arc (cr, radius + half_line, radius + half_line, radius, M_PI, - M_PI/2);
1268 cairo_arc (cr, width - (radius + half_line), radius + half_line,
1269 radius, - M_PI/2, 0);
1270 cairo_arc (cr, width - (radius + half_line), height - (radius + half_line),
1271 radius, 0, M_PI/2);
1272 cairo_arc (cr, (radius + half_line), height - (radius + half_line),
1273 radius, M_PI/2, M_PI);
1274 cairo_close_path (cr);
1275 cairo_stroke (cr);
1277 cairo_set_source_rgb(cr, 0, 0, 0);
1279 cairo_move_to (cr, width/2., height/2.);
1281 twidth = 0.8 * width;
1282 theight = 0.8 * height;
1283 draw_cairo_text (cr, swb->label, &twidth, &theight, TRUE, TRUE, TRUE, 0, TRUE);
1285 cairo_new_path (cr);
1286 cairo_restore (cr);
1289 SOW_MAKE_TYPE (button, Button,
1290 sheet_widget_button_user_config,
1291 sheet_widget_button_set_sheet,
1292 so_clear_sheet,
1293 sheet_widget_button_foreach_dep,
1294 sheet_widget_button_copy,
1295 sheet_widget_button_write_xml_sax,
1296 sheet_widget_button_prep_sax_parser,
1297 sheet_widget_button_get_property,
1298 sheet_widget_button_set_property,
1299 sheet_widget_button_draw_cairo,
1301 g_object_class_install_property
1302 (object_class, SOB_PROP_TEXT,
1303 g_param_spec_string ("text", NULL, NULL, NULL,
1304 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1305 g_object_class_install_property
1306 (object_class, SOB_PROP_MARKUP,
1307 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
1308 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1311 /****************************************************************************/
1313 #define DEP_TO_ADJUSTMENT(d_ptr) (SheetWidgetAdjustment *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetAdjustment, dep))
1314 #define GNM_SOW_ADJUSTMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_ADJUSTMENT_TYPE, SheetWidgetAdjustmentClass))
1315 #define SWA_CLASS(so) (GNM_SOW_ADJUSTMENT_CLASS (G_OBJECT_GET_CLASS(so)))
1317 typedef struct {
1318 SheetObjectWidget sow;
1320 gboolean being_updated;
1321 GnmDependent dep;
1322 GtkAdjustment *adjustment;
1324 gboolean horizontal;
1325 } SheetWidgetAdjustment;
1327 typedef struct {
1328 SheetObjectWidgetClass parent_class;
1329 GType type;
1330 gboolean has_orientation;
1331 } SheetWidgetAdjustmentClass;
1333 enum {
1334 SWA_PROP_0 = 0,
1335 SWA_PROP_HORIZONTAL
1338 #ifndef g_signal_handlers_disconnect_by_data
1339 #define g_signal_handlers_disconnect_by_data(instance, data) \
1340 g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data))
1341 #endif
1342 static void
1343 cb_range_destroyed (GtkWidget *w, SheetWidgetAdjustment *swa)
1345 GObject *accessible = G_OBJECT (gtk_widget_get_accessible (w));
1346 if (accessible)
1347 g_signal_handlers_disconnect_by_data (swa->adjustment, accessible);
1350 static void
1351 sheet_widget_adjustment_set_value (SheetWidgetAdjustment *swa, double new_val)
1353 if (swa->being_updated)
1354 return;
1355 swa->being_updated = TRUE;
1356 gtk_adjustment_set_value (swa->adjustment, new_val);
1357 swa->being_updated = FALSE;
1361 * sheet_widget_adjustment_get_adjustment:
1362 * @so: #SheetObject
1364 * Returns: (transfer none): the associated #GtkAdjustment.
1366 GtkAdjustment *
1367 sheet_widget_adjustment_get_adjustment (SheetObject *so)
1369 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), NULL);
1370 return (GNM_SOW_ADJUSTMENT (so)->adjustment);
1373 gboolean
1374 sheet_widget_adjustment_get_horizontal (SheetObject *so)
1376 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), TRUE);
1377 return (GNM_SOW_ADJUSTMENT (so)->horizontal);
1380 void
1381 sheet_widget_adjustment_set_link (SheetObject *so, GnmExprTop const *texpr)
1383 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1384 dependent_set_expr (&swa->dep, texpr);
1385 if (texpr && swa->dep.sheet)
1386 dependent_link (&swa->dep);
1389 GnmExprTop const *
1390 sheet_widget_adjustment_get_link (SheetObject *so)
1392 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1393 GnmExprTop const *texpr = swa->dep.texpr;
1395 if (texpr)
1396 gnm_expr_top_ref (texpr);
1398 return texpr;
1402 static void
1403 adjustment_eval (GnmDependent *dep)
1405 GnmValue *v;
1406 GnmEvalPos pos;
1408 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
1409 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
1410 sheet_widget_adjustment_set_value (DEP_TO_ADJUSTMENT(dep),
1411 value_get_as_float (v));
1412 value_release (v);
1415 static void
1416 adjustment_debug_name (GnmDependent const *dep, GString *target)
1418 g_string_append_printf (target, "Adjustment%p", (void *)dep);
1421 static DEPENDENT_MAKE_TYPE (adjustment, NULL)
1423 static void
1424 cb_adjustment_widget_value_changed (GtkWidget *widget,
1425 SheetWidgetAdjustment *swa)
1427 GnmCellRef ref;
1429 if (swa->being_updated)
1430 return;
1432 if (so_get_ref (GNM_SO (swa), &ref, TRUE) != NULL) {
1433 GnmCell *cell = sheet_cell_fetch (ref.sheet, ref.col, ref.row);
1434 /* TODO : add more control for precision, XL is stupid */
1435 int new_val = gnm_fake_round (gtk_adjustment_get_value (swa->adjustment));
1436 if (cell->value != NULL &&
1437 VALUE_IS_FLOAT (cell->value) &&
1438 value_get_as_float (cell->value) == new_val)
1439 return;
1441 swa->being_updated = TRUE;
1442 cmd_so_set_value (widget_wbc (widget),
1443 /* FIXME: This text sucks: */
1444 _("Change widget"),
1445 &ref, value_new_int (new_val),
1446 sheet_object_get_sheet (GNM_SO (swa)));
1447 swa->being_updated = FALSE;
1451 void
1452 sheet_widget_adjustment_set_horizontal (SheetObject *so,
1453 gboolean horizontal)
1455 SheetWidgetAdjustment *swa = (SheetWidgetAdjustment *)so;
1456 GList *ptr;
1457 GtkOrientation o;
1459 if (!SWA_CLASS (swa)->has_orientation)
1460 return;
1461 horizontal = !!horizontal;
1462 if (horizontal == swa->horizontal)
1463 return;
1464 swa->horizontal = horizontal;
1465 o = horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1467 /* Change direction for all realized widgets. */
1468 for (ptr = swa->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1469 SheetObjectView *view = ptr->data;
1470 GocWidget *item = get_goc_widget (view);
1471 gtk_orientable_set_orientation (GTK_ORIENTABLE (item->widget), o);
1476 static void
1477 sheet_widget_adjustment_get_property (GObject *obj, guint param_id,
1478 GValue *value, GParamSpec *pspec)
1480 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1482 switch (param_id) {
1483 case SWA_PROP_HORIZONTAL:
1484 g_value_set_boolean (value, swa->horizontal);
1485 break;
1486 default:
1487 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1488 break;
1492 static void
1493 sheet_widget_adjustment_set_property (GObject *obj, guint param_id,
1494 GValue const *value, GParamSpec *pspec)
1496 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1498 switch (param_id) {
1499 case SWA_PROP_HORIZONTAL:
1500 sheet_widget_adjustment_set_horizontal (GNM_SO (swa), g_value_get_boolean (value));
1501 break;
1502 default:
1503 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1504 return;
1508 static void
1509 sheet_widget_adjustment_init_full (SheetWidgetAdjustment *swa,
1510 GnmCellRef const *ref,
1511 gboolean horizontal)
1513 SheetObject *so;
1514 g_return_if_fail (swa != NULL);
1516 so = GNM_SO (swa);
1517 so->flags &= ~SHEET_OBJECT_PRINT;
1519 swa->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0., 0., 100., 1., 10., 0.));
1520 g_object_ref_sink (swa->adjustment);
1522 swa->horizontal = horizontal;
1523 swa->being_updated = FALSE;
1524 swa->dep.sheet = NULL;
1525 swa->dep.flags = adjustment_get_dep_type ();
1526 swa->dep.texpr = (ref != NULL)
1527 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
1528 : NULL;
1531 static void
1532 sheet_widget_adjustment_init (SheetWidgetAdjustment *swa)
1534 sheet_widget_adjustment_init_full (swa, NULL, FALSE);
1537 static void
1538 sheet_widget_adjustment_finalize (GObject *obj)
1540 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1542 g_return_if_fail (swa != NULL);
1544 dependent_set_expr (&swa->dep, NULL);
1545 if (swa->adjustment != NULL) {
1546 g_object_unref (swa->adjustment);
1547 swa->adjustment = NULL;
1550 sheet_object_widget_class->finalize (obj);
1553 static void
1554 sheet_widget_adjustment_copy (SheetObject *dst, SheetObject const *src)
1556 SheetWidgetAdjustment const *src_swa = GNM_SOW_ADJUSTMENT (src);
1557 SheetWidgetAdjustment *dst_swa = GNM_SOW_ADJUSTMENT (dst);
1558 GtkAdjustment *dst_adjust, *src_adjust;
1559 GnmCellRef ref;
1561 sheet_widget_adjustment_init_full (dst_swa,
1562 so_get_ref (src, &ref, FALSE),
1563 src_swa->horizontal);
1564 dst_adjust = dst_swa->adjustment;
1565 src_adjust = src_swa->adjustment;
1567 gtk_adjustment_configure
1568 (dst_adjust,
1569 gtk_adjustment_get_value (src_adjust),
1570 gtk_adjustment_get_lower (src_adjust),
1571 gtk_adjustment_get_upper (src_adjust),
1572 gtk_adjustment_get_step_increment (src_adjust),
1573 gtk_adjustment_get_page_increment (src_adjust),
1574 gtk_adjustment_get_page_size (src_adjust));
1577 typedef struct {
1578 GtkWidget *dialog;
1579 GnmExprEntry *expression;
1580 GtkWidget *min;
1581 GtkWidget *max;
1582 GtkWidget *inc;
1583 GtkWidget *page;
1584 GtkWidget *direction_h;
1585 GtkWidget *direction_v;
1587 char *undo_label;
1588 GtkWidget *old_focus;
1590 WBCGtk *wbcg;
1591 SheetWidgetAdjustment *swa;
1592 Sheet *sheet;
1593 } AdjustmentConfigState;
1595 static void
1596 cb_adjustment_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
1597 AdjustmentConfigState *state)
1599 GtkWidget *ofp;
1601 /* Note: half of the set-focus action is handle by the default
1602 * callback installed by wbc_gtk_attach_guru. */
1604 ofp = state->old_focus
1605 ? gtk_widget_get_parent (state->old_focus)
1606 : NULL;
1607 /* Force an update of the content in case it needs tweaking (eg make it
1608 * absolute) */
1609 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
1610 GnmParsePos pp;
1611 GnmExprTop const *texpr = gnm_expr_entry_parse (
1612 GNM_EXPR_ENTRY (ofp),
1613 parse_pos_init_sheet (&pp, state->sheet),
1614 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1615 if (texpr != NULL)
1616 gnm_expr_top_unref (texpr);
1618 state->old_focus = focus_widget;
1621 static void
1622 cb_adjustment_config_destroy (AdjustmentConfigState *state)
1624 g_return_if_fail (state != NULL);
1626 g_free (state->undo_label);
1628 state->dialog = NULL;
1629 g_free (state);
1632 static void
1633 cb_adjustment_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1635 SheetObject *so = GNM_SO (state->swa);
1636 GnmParsePos pp;
1637 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1638 parse_pos_init_sheet (&pp, so->sheet),
1639 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1640 gboolean horizontal;
1642 horizontal = state->direction_h
1643 ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state->direction_h))
1644 : state->swa->horizontal;
1646 cmd_so_set_adjustment (GNM_WBC (state->wbcg), so,
1647 texpr,
1648 horizontal,
1649 gtk_spin_button_get_value_as_int (
1650 GTK_SPIN_BUTTON (state->min)),
1651 gtk_spin_button_get_value_as_int (
1652 GTK_SPIN_BUTTON (state->max)),
1653 gtk_spin_button_get_value_as_int (
1654 GTK_SPIN_BUTTON (state->inc)),
1655 gtk_spin_button_get_value_as_int (
1656 GTK_SPIN_BUTTON (state->page)),
1657 state->undo_label);
1659 gtk_widget_destroy (state->dialog);
1662 static void
1663 cb_adjustment_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1665 gtk_widget_destroy (state->dialog);
1668 static void
1669 sheet_widget_adjustment_user_config_impl (SheetObject *so, SheetControl *sc, char const *undo_label, char const *dialog_label)
1671 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1672 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (swa);
1673 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1674 AdjustmentConfigState *state;
1675 GtkWidget *grid;
1676 GtkBuilder *gui;
1677 gboolean has_directions = swa_class->has_orientation;
1679 /* Only pop up one copy per workbook */
1680 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1681 return;
1683 gui = gnm_gtk_builder_load ("so-scrollbar.ui", NULL, GO_CMD_CONTEXT (wbcg));
1684 if (!gui)
1685 return;
1686 state = g_new (AdjustmentConfigState, 1);
1687 state->swa = swa;
1688 state->wbcg = wbcg;
1689 state->sheet = sc_sheet (sc);
1690 state->old_focus = NULL;
1691 state->undo_label = (undo_label == NULL) ? NULL : g_strdup (undo_label);
1692 state->dialog = go_gtk_builder_get_widget (gui, "SO-Scrollbar");
1694 if (dialog_label != NULL)
1695 gtk_window_set_title (GTK_WINDOW (state->dialog), dialog_label);
1697 grid = go_gtk_builder_get_widget (gui, "main-grid");
1699 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1700 gnm_expr_entry_set_flags (state->expression,
1701 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1702 GNM_EE_MASK);
1703 gnm_expr_entry_load_from_dep (state->expression, &swa->dep);
1704 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1705 GTK_WIDGET (state->expression));
1706 gtk_grid_attach (GTK_GRID (grid),
1707 GTK_WIDGET (state->expression), 1, 0, 2, 1);
1708 gtk_widget_show (GTK_WIDGET (state->expression));
1710 if (has_directions) {
1711 state->direction_h = go_gtk_builder_get_widget (gui, "direction_h");
1712 state->direction_v = go_gtk_builder_get_widget (gui, "direction_v");
1713 gtk_toggle_button_set_active
1714 (GTK_TOGGLE_BUTTON (swa->horizontal
1715 ? state->direction_h
1716 : state->direction_v),
1717 TRUE);
1718 } else {
1719 state->direction_h = NULL;
1720 state->direction_v = NULL;
1721 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_label"));
1722 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_h"));
1723 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_v"));
1726 /* TODO : This is silly, no need to be similar to XL here. */
1727 state->min = go_gtk_builder_get_widget (gui, "spin_min");
1728 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->min),
1729 gtk_adjustment_get_lower (swa->adjustment));
1730 state->max = go_gtk_builder_get_widget (gui, "spin_max");
1731 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max),
1732 gtk_adjustment_get_upper (swa->adjustment));
1733 state->inc = go_gtk_builder_get_widget (gui, "spin_increment");
1734 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->inc),
1735 gtk_adjustment_get_step_increment (swa->adjustment));
1736 state->page = go_gtk_builder_get_widget (gui, "spin_page");
1737 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->page),
1738 gtk_adjustment_get_page_increment (swa->adjustment));
1740 gnm_editable_enters (GTK_WINDOW (state->dialog),
1741 GTK_WIDGET (state->expression));
1742 gnm_editable_enters (GTK_WINDOW (state->dialog),
1743 GTK_WIDGET (state->min));
1744 gnm_editable_enters (GTK_WINDOW (state->dialog),
1745 GTK_WIDGET (state->max));
1746 gnm_editable_enters (GTK_WINDOW (state->dialog),
1747 GTK_WIDGET (state->inc));
1748 gnm_editable_enters (GTK_WINDOW (state->dialog),
1749 GTK_WIDGET (state->page));
1750 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1751 "clicked",
1752 G_CALLBACK (cb_adjustment_config_ok_clicked), state);
1753 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1754 "clicked",
1755 G_CALLBACK (cb_adjustment_config_cancel_clicked), state);
1757 gnm_init_help_button (
1758 go_gtk_builder_get_widget (gui, "help_button"),
1759 GNUMERIC_HELP_LINK_SO_ADJUSTMENT);
1761 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1762 SHEET_OBJECT_CONFIG_KEY);
1764 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1765 g_object_set_data_full (G_OBJECT (state->dialog),
1766 "state", state, (GDestroyNotify) cb_adjustment_config_destroy);
1768 /* Note: half of the set-focus action is handle by the default */
1769 /* callback installed by wbc_gtk_attach_guru */
1770 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1771 G_CALLBACK (cb_adjustment_set_focus), state);
1772 g_object_unref (gui);
1774 gtk_widget_show (state->dialog);
1777 static void
1778 sheet_widget_adjustment_user_config (SheetObject *so, SheetControl *sc)
1780 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Adjustment"),
1781 N_("Adjustment Properties"));
1784 static gboolean
1785 sheet_widget_adjustment_set_sheet (SheetObject *so, Sheet *sheet)
1787 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1789 dependent_set_sheet (&swa->dep, sheet);
1791 return FALSE;
1794 static void
1795 sheet_widget_adjustment_foreach_dep (SheetObject *so,
1796 SheetObjectForeachDepFunc func,
1797 gpointer user)
1799 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1800 func (&swa->dep, so, user);
1803 static void
1804 sheet_widget_adjustment_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1805 GnmConventions const *convs)
1807 SheetWidgetAdjustment const *swa = GNM_SOW_ADJUSTMENT (so);
1808 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1810 go_xml_out_add_double (output, "Min", gtk_adjustment_get_lower (swa->adjustment));
1811 go_xml_out_add_double (output, "Max", gtk_adjustment_get_upper (swa->adjustment));
1812 go_xml_out_add_double (output, "Inc", gtk_adjustment_get_step_increment (swa->adjustment));
1813 go_xml_out_add_double (output, "Page", gtk_adjustment_get_page_increment (swa->adjustment));
1814 go_xml_out_add_double (output, "Value", gtk_adjustment_get_value (swa->adjustment));
1816 if (swa_class->has_orientation)
1817 gsf_xml_out_add_bool (output, "Horizontal", swa->horizontal);
1819 sax_write_dep (output, &swa->dep, "Input", convs);
1822 static void
1823 sheet_widget_adjustment_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1824 xmlChar const **attrs,
1825 GnmConventions const *convs)
1827 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1828 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1829 swa->horizontal = FALSE;
1831 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1832 double tmp;
1833 gboolean b;
1835 if (gnm_xml_attr_double (attrs, "Min", &tmp))
1836 gtk_adjustment_set_lower (swa->adjustment, tmp);
1837 else if (gnm_xml_attr_double (attrs, "Max", &tmp))
1838 gtk_adjustment_set_upper (swa->adjustment, tmp); /* allow scrolling to max */
1839 else if (gnm_xml_attr_double (attrs, "Inc", &tmp))
1840 gtk_adjustment_set_step_increment (swa->adjustment, tmp);
1841 else if (gnm_xml_attr_double (attrs, "Page", &tmp))
1842 gtk_adjustment_set_page_increment (swa->adjustment, tmp);
1843 else if (gnm_xml_attr_double (attrs, "Value", &tmp))
1844 gtk_adjustment_set_value (swa->adjustment, tmp);
1845 else if (sax_read_dep (attrs, "Input", &swa->dep, xin, convs))
1847 else if (swa_class->has_orientation &&
1848 gnm_xml_attr_bool (attrs, "Horizontal", &b))
1849 swa->horizontal = b;
1852 swa->dep.flags = adjustment_get_dep_type ();
1855 void
1856 sheet_widget_adjustment_set_details (SheetObject *so, GnmExprTop const *tlink,
1857 int value, int min, int max,
1858 int inc, int page)
1860 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1861 double page_size;
1863 g_return_if_fail (swa != NULL);
1865 dependent_set_expr (&swa->dep, tlink);
1866 if (tlink && swa->dep.sheet)
1867 dependent_link (&swa->dep);
1869 page_size = gtk_adjustment_get_page_size (swa->adjustment); /* ??? */
1870 gtk_adjustment_configure (swa->adjustment,
1871 value, min, max, inc, page, page_size);
1874 static GtkWidget *
1875 sheet_widget_adjustment_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
1877 g_assert_not_reached ();
1878 return NULL;
1881 SOW_MAKE_TYPE (adjustment, Adjustment,
1882 sheet_widget_adjustment_user_config,
1883 sheet_widget_adjustment_set_sheet,
1884 so_clear_sheet,
1885 sheet_widget_adjustment_foreach_dep,
1886 sheet_widget_adjustment_copy,
1887 sheet_widget_adjustment_write_xml_sax,
1888 sheet_widget_adjustment_prep_sax_parser,
1889 sheet_widget_adjustment_get_property,
1890 sheet_widget_adjustment_set_property,
1891 sheet_widget_draw_cairo,
1893 ((SheetWidgetAdjustmentClass *) object_class)->has_orientation = TRUE;
1894 g_object_class_install_property
1895 (object_class, SWA_PROP_HORIZONTAL,
1896 g_param_spec_boolean ("horizontal", NULL, NULL,
1897 FALSE,
1898 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1901 /****************************************************************************/
1903 #define GNM_SOW_SCROLLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SCROLLBAR_TYPE, SheetWidgetScrollbar))
1904 #define DEP_TO_SCROLLBAR(d_ptr) (SheetWidgetScrollbar *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetScrollbar, dep))
1906 typedef SheetWidgetAdjustment SheetWidgetScrollbar;
1907 typedef SheetWidgetAdjustmentClass SheetWidgetScrollbarClass;
1909 static GtkWidget *
1910 sheet_widget_scrollbar_create_widget (SheetObjectWidget *sow)
1912 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
1913 GtkWidget *bar;
1915 swa->being_updated = TRUE;
1916 bar = gtk_scrollbar_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
1917 gtk_widget_set_can_focus (bar, FALSE);
1918 g_signal_connect (G_OBJECT (bar),
1919 "value_changed",
1920 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
1921 g_signal_connect (G_OBJECT (bar), "destroy",
1922 G_CALLBACK (cb_range_destroyed), swa);
1923 swa->being_updated = FALSE;
1925 return bar;
1928 static void
1929 sheet_widget_scrollbar_user_config (SheetObject *so, SheetControl *sc)
1931 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Scrollbar"),
1932 N_("Scrollbar Properties"));
1935 static void sheet_widget_slider_horizontal_draw_cairo
1936 (SheetObject const *so, cairo_t *cr, double width, double height);
1938 static void
1939 sheet_widget_scrollbar_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
1940 double width, double height)
1942 cairo_save (cr);
1943 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1945 cairo_new_path (cr);
1946 cairo_move_to (cr, 0., height/2);
1947 cairo_rel_line_to (cr, 15., 7.5);
1948 cairo_rel_line_to (cr, 0, -15);
1949 cairo_close_path (cr);
1950 cairo_fill (cr);
1952 cairo_new_path (cr);
1953 cairo_move_to (cr, width, height/2);
1954 cairo_rel_line_to (cr, -15., 7.5);
1955 cairo_rel_line_to (cr, 0, -15);
1956 cairo_close_path (cr);
1957 cairo_fill (cr);
1959 cairo_new_path (cr);
1960 cairo_translate (cr, 15., 0.);
1961 sheet_widget_slider_horizontal_draw_cairo (so, cr, width - 30, height);
1962 cairo_restore (cr);
1965 static void
1966 sheet_widget_scrollbar_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
1967 double width, double height)
1969 cairo_save (cr);
1970 cairo_rotate (cr, M_PI/2);
1971 cairo_translate (cr, 0., -width);
1972 sheet_widget_scrollbar_horizontal_draw_cairo (so, cr, height, width);
1973 cairo_restore (cr);
1976 static void
1977 sheet_widget_scrollbar_draw_cairo (SheetObject const *so, cairo_t *cr,
1978 double width, double height)
1980 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1981 if (swa->horizontal)
1982 sheet_widget_scrollbar_horizontal_draw_cairo
1983 (so, cr, width, height);
1984 else
1985 sheet_widget_scrollbar_vertical_draw_cairo
1986 (so, cr, width, height);
1989 static void
1990 sheet_widget_scrollbar_class_init (SheetObjectWidgetClass *sow_class)
1992 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
1993 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
1995 sow_class->create_widget = &sheet_widget_scrollbar_create_widget;
1996 so_class->user_config = &sheet_widget_scrollbar_user_config;
1997 so_class->draw_cairo = &sheet_widget_scrollbar_draw_cairo;
1998 swa_class->type = GTK_TYPE_SCROLLBAR;
2001 GSF_CLASS (SheetWidgetScrollbar, sheet_widget_scrollbar,
2002 &sheet_widget_scrollbar_class_init, NULL,
2003 GNM_SOW_ADJUSTMENT_TYPE)
2005 /****************************************************************************/
2007 #define GNM_SOW_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SPIN_BUTTON_TYPE, SheetWidgetSpinbutton))
2008 #define DEP_TO_SPINBUTTON(d_ptr) (SheetWidgetSpinbutton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSpinbutton, dep))
2010 typedef SheetWidgetAdjustment SheetWidgetSpinbutton;
2011 typedef SheetWidgetAdjustmentClass SheetWidgetSpinbuttonClass;
2013 static GtkWidget *
2014 sheet_widget_spinbutton_create_widget (SheetObjectWidget *sow)
2016 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2017 GtkWidget *spinbutton;
2019 swa->being_updated = TRUE;
2020 spinbutton = gtk_spin_button_new
2021 (swa->adjustment,
2022 gtk_adjustment_get_step_increment (swa->adjustment),
2024 gtk_widget_set_can_focus (spinbutton, FALSE);
2025 g_signal_connect (G_OBJECT (spinbutton),
2026 "value_changed",
2027 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2028 g_signal_connect (G_OBJECT (spinbutton), "destroy",
2029 G_CALLBACK (cb_range_destroyed), swa);
2030 swa->being_updated = FALSE;
2031 return spinbutton;
2034 static void
2035 sheet_widget_spinbutton_user_config (SheetObject *so, SheetControl *sc)
2037 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Spinbutton"),
2038 N_("Spinbutton Properties"));
2041 static void
2042 sheet_widget_spinbutton_draw_cairo (SheetObject const *so, cairo_t *cr,
2043 double width, double height)
2045 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2046 GtkAdjustment *adjustment = swa->adjustment;
2047 double value = gtk_adjustment_get_value (adjustment);
2048 int ivalue = (int) value;
2049 double halfheight = height/2;
2050 char *str;
2052 cairo_save (cr);
2053 cairo_set_line_width (cr, 0.5);
2054 cairo_set_source_rgb(cr, 0, 0, 0);
2056 cairo_new_path (cr);
2057 cairo_move_to (cr, 0, 0);
2058 cairo_line_to (cr, width, 0);
2059 cairo_line_to (cr, width, height);
2060 cairo_line_to (cr, 0, height);
2061 cairo_close_path (cr);
2062 cairo_stroke (cr);
2064 cairo_new_path (cr);
2065 cairo_move_to (cr, width - 10, 0);
2066 cairo_rel_line_to (cr, 0, height);
2067 cairo_stroke (cr);
2069 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2071 cairo_new_path (cr);
2072 cairo_move_to (cr, width - 5, 3);
2073 cairo_rel_line_to (cr, 3, 3);
2074 cairo_rel_line_to (cr, -6, 0);
2075 cairo_close_path (cr);
2076 cairo_fill (cr);
2078 cairo_new_path (cr);
2079 cairo_move_to (cr, width - 5, height - 3);
2080 cairo_rel_line_to (cr, 3, -3);
2081 cairo_rel_line_to (cr, -6, 0);
2082 cairo_close_path (cr);
2083 cairo_fill (cr);
2085 str = g_strdup_printf ("%i", ivalue);
2086 cairo_set_source_rgb(cr, 0, 0, 0);
2087 cairo_move_to (cr, 4., halfheight);
2088 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
2089 g_free (str);
2091 cairo_new_path (cr);
2092 cairo_restore (cr);
2095 static void
2096 sheet_widget_spinbutton_class_init (SheetObjectWidgetClass *sow_class)
2098 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2099 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2101 sow_class->create_widget = &sheet_widget_spinbutton_create_widget;
2102 so_class->user_config = &sheet_widget_spinbutton_user_config;
2103 so_class->draw_cairo = &sheet_widget_spinbutton_draw_cairo;
2105 swa_class->type = GTK_TYPE_SPIN_BUTTON;
2106 swa_class->has_orientation = FALSE;
2109 GSF_CLASS (SheetWidgetSpinbutton, sheet_widget_spinbutton,
2110 &sheet_widget_spinbutton_class_init, NULL,
2111 GNM_SOW_ADJUSTMENT_TYPE)
2113 /****************************************************************************/
2115 #define GNM_SOW_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SLIDER_TYPE, SheetWidgetSlider))
2116 #define DEP_TO_SLIDER(d_ptr) (SheetWidgetSlider *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSlider, dep))
2118 typedef SheetWidgetAdjustment SheetWidgetSlider;
2119 typedef SheetWidgetAdjustmentClass SheetWidgetSliderClass;
2121 static GtkWidget *
2122 sheet_widget_slider_create_widget (SheetObjectWidget *sow)
2124 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2125 GtkWidget *slider;
2127 swa->being_updated = TRUE;
2128 slider = gtk_scale_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
2129 gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
2130 gtk_widget_set_can_focus (slider, FALSE);
2131 g_signal_connect (G_OBJECT (slider),
2132 "value_changed",
2133 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2134 g_signal_connect (G_OBJECT (slider), "destroy",
2135 G_CALLBACK (cb_range_destroyed), swa);
2136 swa->being_updated = FALSE;
2138 return slider;
2141 static void
2142 sheet_widget_slider_user_config (SheetObject *so, SheetControl *sc)
2144 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Slider"),
2145 N_("Slider Properties"));
2148 static void
2149 sheet_widget_slider_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
2150 double width, double height)
2152 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2153 GtkAdjustment *adjustment = swa->adjustment;
2154 double value = gtk_adjustment_get_value (adjustment);
2155 double upper = gtk_adjustment_get_upper (adjustment);
2156 double lower = gtk_adjustment_get_lower (adjustment);
2157 double fraction = (upper == lower) ? 0.0 : (value - lower)/(upper- lower);
2159 cairo_save (cr);
2160 cairo_set_line_width (cr, 5);
2161 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
2162 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2164 cairo_new_path (cr);
2165 cairo_move_to (cr, 4, height/2);
2166 cairo_rel_line_to (cr, width - 8., 0);
2167 cairo_stroke (cr);
2169 cairo_set_line_width (cr, 15);
2170 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2171 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2173 cairo_new_path (cr);
2174 cairo_move_to (cr, fraction * (width - 8. - 1. - 5. - 5. + 2.5 + 2.5)
2175 - 10. + 10. + 4. + 5. - 2.5, height/2);
2176 cairo_rel_line_to (cr, 1, 0);
2177 cairo_stroke (cr);
2179 cairo_new_path (cr);
2180 cairo_restore (cr);
2183 static void
2184 sheet_widget_slider_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
2185 double width, double height)
2187 cairo_save (cr);
2188 cairo_rotate (cr, M_PI/2);
2189 cairo_translate (cr, 0., -width);
2190 sheet_widget_slider_horizontal_draw_cairo (so, cr, height, width);
2191 cairo_restore (cr);
2194 static void
2195 sheet_widget_slider_draw_cairo (SheetObject const *so, cairo_t *cr,
2196 double width, double height)
2198 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2199 if (swa->horizontal)
2200 sheet_widget_slider_horizontal_draw_cairo (so, cr, width, height);
2201 else
2202 sheet_widget_slider_vertical_draw_cairo (so, cr, width, height);
2205 static void
2206 sheet_widget_slider_class_init (SheetObjectWidgetClass *sow_class)
2208 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2209 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2211 sow_class->create_widget = &sheet_widget_slider_create_widget;
2212 so_class->user_config = &sheet_widget_slider_user_config;
2213 so_class->draw_cairo = &sheet_widget_slider_draw_cairo;
2215 swa_class->type = GTK_TYPE_SCALE;
2218 GSF_CLASS (SheetWidgetSlider, sheet_widget_slider,
2219 &sheet_widget_slider_class_init, NULL,
2220 GNM_SOW_ADJUSTMENT_TYPE)
2222 /****************************************************************************/
2224 #define GNM_SOW_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_CHECKBOX_TYPE, SheetWidgetCheckbox))
2225 #define DEP_TO_CHECKBOX(d_ptr) (SheetWidgetCheckbox *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetCheckbox, dep))
2227 typedef struct {
2228 SheetObjectWidget sow;
2230 GnmDependent dep;
2231 char *label;
2232 gboolean value;
2233 gboolean being_updated;
2234 } SheetWidgetCheckbox;
2235 typedef SheetObjectWidgetClass SheetWidgetCheckboxClass;
2237 enum {
2238 SOC_PROP_0 = 0,
2239 SOC_PROP_ACTIVE,
2240 SOC_PROP_TEXT,
2241 SOC_PROP_MARKUP
2244 static void
2245 sheet_widget_checkbox_set_active (SheetWidgetCheckbox *swc)
2247 GList *ptr;
2249 swc->being_updated = TRUE;
2251 for (ptr = swc->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2252 SheetObjectView *view = ptr->data;
2253 GocWidget *item = get_goc_widget (view);
2254 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2255 swc->value);
2258 g_object_notify (G_OBJECT (swc), "active");
2260 swc->being_updated = FALSE;
2263 static void
2264 sheet_widget_checkbox_get_property (GObject *obj, guint param_id,
2265 GValue *value, GParamSpec *pspec)
2267 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2269 switch (param_id) {
2270 case SOC_PROP_ACTIVE:
2271 g_value_set_boolean (value, swc->value);
2272 break;
2273 case SOC_PROP_TEXT:
2274 g_value_set_string (value, swc->label);
2275 break;
2276 case SOC_PROP_MARKUP:
2277 g_value_set_boxed (value, NULL); /* swc->markup */
2278 break;
2279 default:
2280 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2281 break;
2285 static void
2286 sheet_widget_checkbox_set_property (GObject *obj, guint param_id,
2287 GValue const *value, GParamSpec *pspec)
2289 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2291 switch (param_id) {
2292 case SOC_PROP_ACTIVE:
2293 swc->value = g_value_get_boolean (value);
2294 sheet_widget_checkbox_set_active (swc);
2295 break;
2296 case SOC_PROP_TEXT:
2297 sheet_widget_checkbox_set_label (GNM_SO (swc),
2298 g_value_get_string (value));
2299 break;
2300 case SOC_PROP_MARKUP:
2301 #if 0
2302 sheet_widget_checkbox_set_markup (GNM_SO (swc),
2303 g_value_peek_pointer (value));
2304 #endif
2305 break;
2306 default:
2307 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2308 return;
2312 static void
2313 checkbox_eval (GnmDependent *dep)
2315 GnmValue *v;
2316 GnmEvalPos pos;
2317 gboolean err, result;
2319 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2320 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2321 result = value_get_as_bool (v, &err);
2322 value_release (v);
2323 if (!err) {
2324 SheetWidgetCheckbox *swc = DEP_TO_CHECKBOX(dep);
2326 swc->value = result;
2327 sheet_widget_checkbox_set_active (swc);
2331 static void
2332 checkbox_debug_name (GnmDependent const *dep, GString *target)
2334 g_string_append_printf (target, "Checkbox%p", (void *)dep);
2337 static DEPENDENT_MAKE_TYPE (checkbox, NULL)
2339 static void
2340 sheet_widget_checkbox_init_full (SheetWidgetCheckbox *swc,
2341 GnmCellRef const *ref, char const *label)
2343 static int counter = 0;
2345 g_return_if_fail (swc != NULL);
2347 swc->label = label ? g_strdup (label) : g_strdup_printf (_("CheckBox %d"), ++counter);
2348 swc->being_updated = FALSE;
2349 swc->value = FALSE;
2350 swc->dep.sheet = NULL;
2351 swc->dep.flags = checkbox_get_dep_type ();
2352 swc->dep.texpr = (ref != NULL)
2353 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2354 : NULL;
2357 static void
2358 sheet_widget_checkbox_init (SheetWidgetCheckbox *swc)
2360 sheet_widget_checkbox_init_full (swc, NULL, NULL);
2363 static void
2364 sheet_widget_checkbox_finalize (GObject *obj)
2366 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2368 g_return_if_fail (swc != NULL);
2370 g_free (swc->label);
2371 swc->label = NULL;
2373 dependent_set_expr (&swc->dep, NULL);
2375 sheet_object_widget_class->finalize (obj);
2378 static void
2379 cb_checkbox_toggled (GtkToggleButton *button, SheetWidgetCheckbox *swc)
2381 GnmCellRef ref;
2383 if (swc->being_updated)
2384 return;
2385 swc->value = gtk_toggle_button_get_active (button);
2386 sheet_widget_checkbox_set_active (swc);
2388 if (so_get_ref (GNM_SO (swc), &ref, TRUE) != NULL) {
2389 gboolean new_val = gtk_toggle_button_get_active (button);
2390 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2391 /* FIXME: This text sucks: */
2392 _("Clicking checkbox"),
2393 &ref, value_new_bool (new_val),
2394 sheet_object_get_sheet (GNM_SO (swc)));
2398 static GtkWidget *
2399 sheet_widget_checkbox_create_widget (SheetObjectWidget *sow)
2401 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2402 GtkWidget *button;
2404 g_return_val_if_fail (swc != NULL, NULL);
2406 button = gtk_check_button_new_with_label (swc->label);
2407 gtk_widget_set_can_focus (button, FALSE);
2408 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2409 g_signal_connect (G_OBJECT (button),
2410 "toggled",
2411 G_CALLBACK (cb_checkbox_toggled), swc);
2413 return button;
2416 static void
2417 sheet_widget_checkbox_copy (SheetObject *dst, SheetObject const *src)
2419 SheetWidgetCheckbox const *src_swc = GNM_SOW_CHECKBOX (src);
2420 SheetWidgetCheckbox *dst_swc = GNM_SOW_CHECKBOX (dst);
2421 GnmCellRef ref;
2422 sheet_widget_checkbox_init_full (dst_swc,
2423 so_get_ref (src, &ref, FALSE),
2424 src_swc->label);
2425 dst_swc->value = src_swc->value;
2428 typedef struct {
2429 GtkWidget *dialog;
2430 GnmExprEntry *expression;
2431 GtkWidget *label;
2433 char *old_label;
2434 GtkWidget *old_focus;
2436 WBCGtk *wbcg;
2437 SheetWidgetCheckbox *swc;
2438 Sheet *sheet;
2439 } CheckboxConfigState;
2441 static void
2442 cb_checkbox_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
2443 CheckboxConfigState *state)
2445 GtkWidget *ofp;
2447 /* Note: half of the set-focus action is handle by the default
2448 * callback installed by wbc_gtk_attach_guru. */
2450 ofp = state->old_focus
2451 ? gtk_widget_get_parent (state->old_focus)
2452 : NULL;
2454 /* Force an update of the content in case it needs tweaking (eg make it
2455 * absolute) */
2456 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
2457 GnmParsePos pp;
2458 GnmExprTop const *texpr = gnm_expr_entry_parse (
2459 GNM_EXPR_ENTRY (ofp),
2460 parse_pos_init_sheet (&pp, state->sheet),
2461 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2462 if (texpr != NULL)
2463 gnm_expr_top_unref (texpr);
2465 state->old_focus = focus_widget;
2468 static void
2469 cb_checkbox_config_destroy (CheckboxConfigState *state)
2471 g_return_if_fail (state != NULL);
2473 g_free (state->old_label);
2474 state->old_label = NULL;
2475 state->dialog = NULL;
2476 g_free (state);
2479 static void
2480 cb_checkbox_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2482 SheetObject *so = GNM_SO (state->swc);
2483 GnmParsePos pp;
2484 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
2485 parse_pos_init_sheet (&pp, so->sheet),
2486 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2487 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
2489 cmd_so_set_checkbox (GNM_WBC (state->wbcg), so,
2490 texpr, g_strdup (state->old_label), g_strdup (text));
2492 gtk_widget_destroy (state->dialog);
2495 static void
2496 cb_checkbox_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2498 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2499 state->old_label);
2500 gtk_widget_destroy (state->dialog);
2503 static void
2504 cb_checkbox_label_changed (GtkEntry *entry, CheckboxConfigState *state)
2506 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2507 gtk_entry_get_text (entry));
2510 static void
2511 sheet_widget_checkbox_user_config (SheetObject *so, SheetControl *sc)
2513 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2514 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
2515 CheckboxConfigState *state;
2516 GtkWidget *grid;
2517 GtkBuilder *gui;
2519 g_return_if_fail (swc != NULL);
2521 /* Only pop up one copy per workbook */
2522 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
2523 return;
2525 gui = gnm_gtk_builder_load ("so-checkbox.ui", NULL, GO_CMD_CONTEXT (wbcg));
2526 if (!gui)
2527 return;
2528 state = g_new (CheckboxConfigState, 1);
2529 state->swc = swc;
2530 state->wbcg = wbcg;
2531 state->sheet = sc_sheet (sc);
2532 state->old_focus = NULL;
2533 state->old_label = g_strdup (swc->label);
2534 state->dialog = go_gtk_builder_get_widget (gui, "SO-Checkbox");
2536 grid = go_gtk_builder_get_widget (gui, "main-grid");
2538 state->expression = gnm_expr_entry_new (wbcg, TRUE);
2539 gnm_expr_entry_set_flags (state->expression,
2540 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
2541 GNM_EE_MASK);
2542 gnm_expr_entry_load_from_dep (state->expression, &swc->dep);
2543 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
2544 GTK_WIDGET (state->expression));
2545 gtk_grid_attach (GTK_GRID (grid),
2546 GTK_WIDGET (state->expression), 1, 0, 1, 1);
2547 gtk_widget_show (GTK_WIDGET (state->expression));
2549 state->label = go_gtk_builder_get_widget (gui, "label_entry");
2550 gtk_entry_set_text (GTK_ENTRY (state->label), swc->label);
2551 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
2552 gnm_editable_enters (GTK_WINDOW (state->dialog),
2553 GTK_WIDGET (state->expression));
2554 gnm_editable_enters (GTK_WINDOW (state->dialog),
2555 GTK_WIDGET (state->label));
2557 g_signal_connect (G_OBJECT (state->label),
2558 "changed",
2559 G_CALLBACK (cb_checkbox_label_changed), state);
2560 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
2561 "clicked",
2562 G_CALLBACK (cb_checkbox_config_ok_clicked), state);
2563 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
2564 "clicked",
2565 G_CALLBACK (cb_checkbox_config_cancel_clicked), state);
2567 gnm_init_help_button (
2568 go_gtk_builder_get_widget (gui, "help_button"),
2569 GNUMERIC_HELP_LINK_SO_CHECKBOX);
2571 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
2572 SHEET_OBJECT_CONFIG_KEY);
2574 wbc_gtk_attach_guru (state->wbcg, state->dialog);
2575 g_object_set_data_full (G_OBJECT (state->dialog),
2576 "state", state, (GDestroyNotify) cb_checkbox_config_destroy);
2578 /* Note: half of the set-focus action is handle by the default */
2579 /* callback installed by wbc_gtk_attach_guru */
2580 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
2581 G_CALLBACK (cb_checkbox_set_focus), state);
2582 g_object_unref (gui);
2584 gtk_widget_show (state->dialog);
2587 static gboolean
2588 sheet_widget_checkbox_set_sheet (SheetObject *so, Sheet *sheet)
2590 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2592 dependent_set_sheet (&swc->dep, sheet);
2593 sheet_widget_checkbox_set_active (swc);
2595 return FALSE;
2598 static void
2599 sheet_widget_checkbox_foreach_dep (SheetObject *so,
2600 SheetObjectForeachDepFunc func,
2601 gpointer user)
2603 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2604 func (&swc->dep, so, user);
2607 static void
2608 sheet_widget_checkbox_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
2609 GnmConventions const *convs)
2611 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2612 gsf_xml_out_add_cstr (output, "Label", swc->label);
2613 gsf_xml_out_add_int (output, "Value", swc->value);
2614 sax_write_dep (output, &swc->dep, "Input", convs);
2617 static void
2618 sheet_widget_checkbox_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
2619 xmlChar const **attrs,
2620 GnmConventions const *convs)
2622 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2624 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2625 if (attr_eq (attrs[0], "Label")) {
2626 g_free (swc->label);
2627 swc->label = g_strdup (CXML2C (attrs[1]));
2628 } else if (gnm_xml_attr_int (attrs, "Value", &swc->value))
2629 ; /* ??? */
2630 else if (sax_read_dep (attrs, "Input", &swc->dep, xin, convs))
2631 ; /* ??? */
2634 void
2635 sheet_widget_checkbox_set_link (SheetObject *so, GnmExprTop const *texpr)
2637 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2638 dependent_set_expr (&swc->dep, texpr);
2639 if (texpr && swc->dep.sheet)
2640 dependent_link (&swc->dep);
2643 GnmExprTop const *
2644 sheet_widget_checkbox_get_link (SheetObject *so)
2646 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2647 GnmExprTop const *texpr = swc->dep.texpr;
2649 if (texpr)
2650 gnm_expr_top_ref (texpr);
2652 return texpr;
2656 void
2657 sheet_widget_checkbox_set_label (SheetObject *so, char const *str)
2659 GList *list;
2660 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2661 char *new_label;
2663 if (go_str_compare (str, swc->label) == 0)
2664 return;
2666 new_label = g_strdup (str);
2667 g_free (swc->label);
2668 swc->label = new_label;
2670 for (list = swc->sow.so.realized_list; list; list = list->next) {
2671 SheetObjectView *view = list->data;
2672 GocWidget *item = get_goc_widget (view);
2673 gtk_button_set_label (GTK_BUTTON (item->widget), swc->label);
2677 static void
2678 sheet_widget_checkbox_draw_cairo (SheetObject const *so, cairo_t *cr,
2679 double width, double height)
2681 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2682 double halfheight = height/2;
2683 double dx = 8., dxh, pm;
2684 int pw, ph;
2686 pm = MIN (height - 2, width - 12);
2687 if (dx > pm)
2688 dx = MAX (pm, 3);
2689 dxh = dx/2;
2691 cairo_save (cr);
2692 cairo_set_line_width (cr, 0.5);
2693 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
2695 cairo_new_path (cr);
2696 cairo_move_to (cr, dxh, halfheight - dxh);
2697 cairo_rel_line_to (cr, 0, dx);
2698 cairo_rel_line_to (cr, dx, 0);
2699 cairo_rel_line_to (cr, 0., -dx);
2700 cairo_rel_line_to (cr, -dx, 0.);
2701 cairo_close_path (cr);
2702 cairo_fill_preserve (cr);
2703 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
2704 cairo_stroke (cr);
2706 if (swc->value) {
2707 cairo_new_path (cr);
2708 cairo_move_to (cr, dxh, halfheight - dxh);
2709 cairo_rel_line_to (cr, dx, dx);
2710 cairo_rel_line_to (cr, -dx, 0.);
2711 cairo_rel_line_to (cr, dx, -dx);
2712 cairo_rel_line_to (cr, -dx, 0.);
2713 cairo_close_path (cr);
2714 cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
2715 cairo_stroke (cr);
2718 cairo_move_to (cr, 2 * dx, halfheight);
2720 pw = width - 2 * dx;
2721 ph = height;
2723 draw_cairo_text (cr, swc->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
2725 cairo_new_path (cr);
2726 cairo_restore (cr);
2730 SOW_MAKE_TYPE (checkbox, Checkbox,
2731 sheet_widget_checkbox_user_config,
2732 sheet_widget_checkbox_set_sheet,
2733 so_clear_sheet,
2734 sheet_widget_checkbox_foreach_dep,
2735 sheet_widget_checkbox_copy,
2736 sheet_widget_checkbox_write_xml_sax,
2737 sheet_widget_checkbox_prep_sax_parser,
2738 sheet_widget_checkbox_get_property,
2739 sheet_widget_checkbox_set_property,
2740 sheet_widget_checkbox_draw_cairo,
2742 g_object_class_install_property
2743 (object_class, SOC_PROP_ACTIVE,
2744 g_param_spec_boolean ("active", NULL, NULL,
2745 FALSE,
2746 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2747 g_object_class_install_property
2748 (object_class, SOC_PROP_TEXT,
2749 g_param_spec_string ("text", NULL, NULL, NULL,
2750 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2751 g_object_class_install_property
2752 (object_class, SOC_PROP_MARKUP,
2753 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
2754 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2757 /****************************************************************************/
2758 typedef SheetWidgetCheckbox SheetWidgetToggleButton;
2759 typedef SheetWidgetCheckboxClass SheetWidgetToggleButtonClass;
2760 static GtkWidget *
2761 sheet_widget_toggle_button_create_widget (SheetObjectWidget *sow)
2763 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2764 GtkWidget *button = gtk_toggle_button_new_with_label (swc->label);
2765 gtk_widget_set_can_focus (button, FALSE);
2766 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2767 g_signal_connect (G_OBJECT (button),
2768 "toggled",
2769 G_CALLBACK (cb_checkbox_toggled), swc);
2770 return button;
2772 static void
2773 sheet_widget_toggle_button_class_init (SheetObjectWidgetClass *sow_class)
2775 sow_class->create_widget = &sheet_widget_toggle_button_create_widget;
2778 GSF_CLASS (SheetWidgetToggleButton, sheet_widget_toggle_button,
2779 &sheet_widget_toggle_button_class_init, NULL,
2780 GNM_SOW_CHECKBOX_TYPE)
2782 /****************************************************************************/
2784 #define GNM_SOW_RADIO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_RADIO_BUTTON_TYPE, SheetWidgetRadioButton))
2785 #define DEP_TO_RADIO_BUTTON(d_ptr) (SheetWidgetRadioButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetRadioButton, dep))
2787 typedef struct {
2788 SheetObjectWidget sow;
2790 gboolean being_updated;
2791 char *label;
2792 GnmValue *value;
2793 gboolean active;
2794 GnmDependent dep;
2795 } SheetWidgetRadioButton;
2796 typedef SheetObjectWidgetClass SheetWidgetRadioButtonClass;
2798 enum {
2799 SOR_PROP_0 = 0,
2800 SOR_PROP_ACTIVE,
2801 SOR_PROP_TEXT,
2802 SOR_PROP_MARKUP,
2803 SOR_PROP_VALUE
2806 static void
2807 sheet_widget_radio_button_set_active (SheetWidgetRadioButton *swrb,
2808 gboolean active)
2810 GList *ptr;
2812 if (swrb->active == active)
2813 return;
2814 swrb->active = active;
2816 swrb->being_updated = TRUE;
2818 for (ptr = swrb->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2819 SheetObjectView *view = ptr->data;
2820 GocWidget *item = get_goc_widget (view);
2821 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2822 active);
2825 g_object_notify (G_OBJECT (swrb), "active");
2827 swrb->being_updated = FALSE;
2831 static void
2832 sheet_widget_radio_button_get_property (GObject *obj, guint param_id,
2833 GValue *value, GParamSpec *pspec)
2835 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2837 switch (param_id) {
2838 case SOR_PROP_ACTIVE:
2839 g_value_set_boolean (value, swrb->active);
2840 break;
2841 case SOR_PROP_TEXT:
2842 g_value_set_string (value, swrb->label);
2843 break;
2844 case SOR_PROP_MARKUP:
2845 g_value_set_boxed (value, NULL); /* swrb->markup */
2846 break;
2847 case SOR_PROP_VALUE:
2848 g_value_set_boxed (value, swrb->value);
2849 break;
2850 default:
2851 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2852 break;
2856 static void
2857 sheet_widget_radio_button_set_property (GObject *obj, guint param_id,
2858 GValue const *value, GParamSpec *pspec)
2860 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2862 switch (param_id) {
2863 case SOR_PROP_ACTIVE:
2864 sheet_widget_radio_button_set_active (swrb,
2865 g_value_get_boolean (value));
2866 break;
2867 case SOR_PROP_TEXT:
2868 sheet_widget_radio_button_set_label (GNM_SO (swrb),
2869 g_value_get_string (value));
2870 break;
2871 case SOR_PROP_MARKUP:
2872 #if 0
2873 sheet_widget_radio_button_set_markup (GNM_SO (swrb),
2874 g_value_peek_pointer (value));
2875 #endif
2876 break;
2877 case SOR_PROP_VALUE:
2878 sheet_widget_radio_button_set_value (GNM_SO (swrb),
2879 g_value_get_boxed (value));
2880 break;
2881 default:
2882 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2883 return;
2887 GnmValue const *
2888 sheet_widget_radio_button_get_value (SheetObject *so)
2890 SheetWidgetRadioButton *swrb;
2892 g_return_val_if_fail (GNM_IS_SOW_RADIO_BUTTON (so), NULL);
2894 swrb = GNM_SOW_RADIO_BUTTON (so);
2895 return swrb->value;
2898 void
2899 sheet_widget_radio_button_set_value (SheetObject *so, GnmValue const *val)
2901 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
2903 value_release (swrb->value);
2904 swrb->value = value_dup (val);
2907 static void
2908 radio_button_eval (GnmDependent *dep)
2910 GnmValue *v;
2911 GnmEvalPos pos;
2912 SheetWidgetRadioButton *swrb = DEP_TO_RADIO_BUTTON (dep);
2914 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2915 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2916 if (v && swrb->value) {
2917 gboolean active = value_equal (swrb->value, v);
2918 sheet_widget_radio_button_set_active (swrb, active);
2920 value_release (v);
2923 static void
2924 radio_button_debug_name (GnmDependent const *dep, GString *target)
2926 g_string_append_printf (target, "RadioButton%p", (void *)dep);
2929 static DEPENDENT_MAKE_TYPE (radio_button, NULL)
2931 static void
2932 sheet_widget_radio_button_init_full (SheetWidgetRadioButton *swrb,
2933 GnmCellRef const *ref,
2934 char const *label,
2935 GnmValue const *value,
2936 gboolean active)
2938 g_return_if_fail (swrb != NULL);
2940 swrb->being_updated = FALSE;
2941 swrb->label = g_strdup (label ? label : _("RadioButton"));
2942 swrb->value = value ? value_dup (value) : value_new_empty ();
2943 swrb->active = active;
2945 swrb->dep.sheet = NULL;
2946 swrb->dep.flags = radio_button_get_dep_type ();
2947 swrb->dep.texpr = (ref != NULL)
2948 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2949 : NULL;
2952 static void
2953 sheet_widget_radio_button_init (SheetWidgetRadioButton *swrb)
2955 sheet_widget_radio_button_init_full (swrb, NULL, NULL, NULL, TRUE);
2958 static void
2959 sheet_widget_radio_button_finalize (GObject *obj)
2961 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2963 g_return_if_fail (swrb != NULL);
2965 g_free (swrb->label);
2966 swrb->label = NULL;
2967 value_release (swrb->value);
2968 swrb->value = NULL;
2970 dependent_set_expr (&swrb->dep, NULL);
2972 sheet_object_widget_class->finalize (obj);
2975 static void
2976 sheet_widget_radio_button_toggled (GtkToggleButton *button,
2977 SheetWidgetRadioButton *swrb)
2979 GnmCellRef ref;
2981 if (swrb->being_updated)
2982 return;
2983 swrb->active = gtk_toggle_button_get_active (button);
2985 if (so_get_ref (GNM_SO (swrb), &ref, TRUE) != NULL) {
2986 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2987 /* FIXME: This text sucks: */
2988 _("Clicking radiobutton"),
2989 &ref, value_dup (swrb->value),
2990 sheet_object_get_sheet (GNM_SO (swrb)));
2994 static GtkWidget *
2995 sheet_widget_radio_button_create_widget (SheetObjectWidget *sow)
2997 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (sow);
2998 GtkWidget *w = g_object_new (GNM_TYPE_RADIO_BUTTON,
2999 "label", swrb->label,
3000 NULL) ;
3002 gtk_widget_set_can_focus (w, FALSE);
3004 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), swrb->active);
3006 g_signal_connect (G_OBJECT (w),
3007 "toggled",
3008 G_CALLBACK (sheet_widget_radio_button_toggled), sow);
3009 return w;
3012 static void
3013 sheet_widget_radio_button_copy (SheetObject *dst, SheetObject const *src)
3015 SheetWidgetRadioButton const *src_swrb = GNM_SOW_RADIO_BUTTON (src);
3016 SheetWidgetRadioButton *dst_swrb = GNM_SOW_RADIO_BUTTON (dst);
3017 GnmCellRef ref;
3019 sheet_widget_radio_button_init_full (dst_swrb,
3020 so_get_ref (src, &ref, FALSE),
3021 src_swrb->label,
3022 src_swrb->value,
3023 src_swrb->active);
3026 static gboolean
3027 sheet_widget_radio_button_set_sheet (SheetObject *so, Sheet *sheet)
3029 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3031 dependent_set_sheet (&swrb->dep, sheet);
3033 return FALSE;
3036 static void
3037 sheet_widget_radio_button_foreach_dep (SheetObject *so,
3038 SheetObjectForeachDepFunc func,
3039 gpointer user)
3041 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3042 func (&swrb->dep, so, user);
3045 static void
3046 sheet_widget_radio_button_write_xml_sax (SheetObject const *so,
3047 GsfXMLOut *output,
3048 GnmConventions const *convs)
3050 SheetWidgetRadioButton const *swrb = GNM_SOW_RADIO_BUTTON (so);
3051 GString *valstr = g_string_new (NULL);
3053 value_get_as_gstring (swrb->value, valstr, convs);
3055 gsf_xml_out_add_cstr (output, "Label", swrb->label);
3056 gsf_xml_out_add_cstr (output, "Value", valstr->str);
3057 gsf_xml_out_add_int (output, "ValueType", swrb->value->v_any.type);
3058 gsf_xml_out_add_int (output, "Active", swrb->active);
3059 sax_write_dep (output, &swrb->dep, "Input", convs);
3061 g_string_free (valstr, TRUE);
3064 static void
3065 sheet_widget_radio_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3066 xmlChar const **attrs,
3067 GnmConventions const *convs)
3069 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3070 const char *valstr = NULL;
3071 int value_type = 0;
3073 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
3074 if (attr_eq (attrs[0], "Label")) {
3075 g_free (swrb->label);
3076 swrb->label = g_strdup (CXML2C (attrs[1]));
3077 } else if (attr_eq (attrs[0], "Value")) {
3078 valstr = CXML2C (attrs[1]);
3079 } else if (gnm_xml_attr_bool (attrs, "Active", &swrb->active) ||
3080 gnm_xml_attr_int (attrs, "ValueType", &value_type) ||
3081 sax_read_dep (attrs, "Input", &swrb->dep, xin, convs))
3082 ; /* Nothing */
3085 value_release (swrb->value);
3086 swrb->value = NULL;
3087 if (valstr) {
3088 swrb->value = value_type
3089 ? value_new_from_string (value_type, valstr, NULL, FALSE)
3090 : format_match (valstr, NULL, NULL);
3092 if (!swrb->value)
3093 swrb->value = value_new_empty ();
3096 void
3097 sheet_widget_radio_button_set_link (SheetObject *so, GnmExprTop const *texpr)
3099 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3100 dependent_set_expr (&swrb->dep, texpr);
3101 if (texpr && swrb->dep.sheet)
3102 dependent_link (&swrb->dep);
3105 GnmExprTop const *
3106 sheet_widget_radio_button_get_link (SheetObject *so)
3108 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3109 GnmExprTop const *texpr = swrb->dep.texpr;
3111 if (texpr)
3112 gnm_expr_top_ref (texpr);
3114 return texpr;
3117 void
3118 sheet_widget_radio_button_set_label (SheetObject *so, char const *str)
3120 GList *list;
3121 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3122 char *new_label;
3124 if (go_str_compare (str, swrb->label) == 0)
3125 return;
3127 new_label = g_strdup (str);
3128 g_free (swrb->label);
3129 swrb->label = new_label;
3131 for (list = swrb->sow.so.realized_list; list; list = list->next) {
3132 SheetObjectView *view = list->data;
3133 GocWidget *item = get_goc_widget (view);
3134 gtk_button_set_label (GTK_BUTTON (item->widget), swrb->label);
3139 typedef struct {
3140 GtkWidget *dialog;
3141 GnmExprEntry *expression;
3142 GtkWidget *label, *value;
3144 char *old_label;
3145 GnmValue *old_value;
3146 GtkWidget *old_focus;
3148 WBCGtk *wbcg;
3149 SheetWidgetRadioButton *swrb;
3150 Sheet *sheet;
3151 } RadioButtonConfigState;
3153 static void
3154 cb_radio_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
3155 RadioButtonConfigState *state)
3157 GtkWidget *ofp;
3159 /* Note: half of the set-focus action is handle by the default
3160 * callback installed by wbc_gtk_attach_guru */
3162 ofp = state->old_focus
3163 ? gtk_widget_get_parent (state->old_focus)
3164 : NULL;
3166 /* Force an update of the content in case it needs tweaking (eg make it
3167 * absolute) */
3168 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
3169 GnmParsePos pp;
3170 GnmExprTop const *texpr = gnm_expr_entry_parse (
3171 GNM_EXPR_ENTRY (ofp),
3172 parse_pos_init_sheet (&pp, state->sheet),
3173 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3174 if (texpr != NULL)
3175 gnm_expr_top_unref (texpr);
3177 state->old_focus = focus_widget;
3180 static void
3181 cb_radio_button_config_destroy (RadioButtonConfigState *state)
3183 g_return_if_fail (state != NULL);
3185 g_free (state->old_label);
3186 state->old_label = NULL;
3188 value_release (state->old_value);
3189 state->old_value = NULL;
3191 state->dialog = NULL;
3193 g_free (state);
3196 static GnmValue *
3197 so_parse_value (SheetObject *so, const char *s)
3199 Sheet *sheet = so->sheet;
3200 return format_match (s, NULL, workbook_date_conv (sheet->workbook));
3203 static void
3204 cb_radio_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3206 SheetObject *so = GNM_SO (state->swrb);
3207 GnmParsePos pp;
3208 GnmExprTop const *texpr = gnm_expr_entry_parse
3209 (state->expression,
3210 parse_pos_init_sheet (&pp, so->sheet),
3211 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3212 gchar const *text = gtk_entry_get_text (GTK_ENTRY (state->label));
3213 gchar const *val = gtk_entry_get_text (GTK_ENTRY (state->value));
3214 GnmValue *new_val = so_parse_value (so, val);
3216 cmd_so_set_radio_button (GNM_WBC (state->wbcg), so,
3217 texpr,
3218 g_strdup (state->old_label), g_strdup (text),
3219 value_dup (state->old_value), new_val);
3221 gtk_widget_destroy (state->dialog);
3224 static void
3225 cb_radio_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3227 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3228 state->old_label);
3229 sheet_widget_radio_button_set_value (GNM_SO (state->swrb),
3230 state->old_value);
3231 gtk_widget_destroy (state->dialog);
3234 static void
3235 cb_radio_button_label_changed (GtkEntry *entry, RadioButtonConfigState *state)
3237 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3238 gtk_entry_get_text (entry));
3241 static void
3242 cb_radio_button_value_changed (GtkEntry *entry, RadioButtonConfigState *state)
3244 const char *text = gtk_entry_get_text (entry);
3245 SheetObject *so = GNM_SO (state->swrb);
3246 GnmValue *val = so_parse_value (so, text);
3248 sheet_widget_radio_button_set_value (so, val);
3249 value_release (val);
3252 static void
3253 sheet_widget_radio_button_user_config (SheetObject *so, SheetControl *sc)
3255 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3256 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
3257 RadioButtonConfigState *state;
3258 GtkWidget *grid;
3259 GString *valstr;
3260 GtkBuilder *gui;
3262 g_return_if_fail (swrb != NULL);
3264 /* Only pop up one copy per workbook */
3265 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
3266 return;
3268 gui = gnm_gtk_builder_load ("so-radiobutton.ui", NULL, GO_CMD_CONTEXT (wbcg));
3269 if (!gui)
3270 return;
3271 state = g_new (RadioButtonConfigState, 1);
3272 state->swrb = swrb;
3273 state->wbcg = wbcg;
3274 state->sheet = sc_sheet (sc);
3275 state->old_focus = NULL;
3276 state->old_label = g_strdup (swrb->label);
3277 state->old_value = value_dup (swrb->value);
3278 state->dialog = go_gtk_builder_get_widget (gui, "SO-Radiobutton");
3280 grid = go_gtk_builder_get_widget (gui, "main-grid");
3282 state->expression = gnm_expr_entry_new (wbcg, TRUE);
3283 gnm_expr_entry_set_flags (state->expression,
3284 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
3285 GNM_EE_MASK);
3286 gnm_expr_entry_load_from_dep (state->expression, &swrb->dep);
3287 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
3288 GTK_WIDGET (state->expression));
3289 gtk_grid_attach (GTK_GRID (grid),
3290 GTK_WIDGET (state->expression), 1, 0, 1, 1);
3291 gtk_widget_show (GTK_WIDGET (state->expression));
3293 state->label = go_gtk_builder_get_widget (gui, "label_entry");
3294 gtk_entry_set_text (GTK_ENTRY (state->label), swrb->label);
3295 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
3296 state->value = go_gtk_builder_get_widget (gui, "value_entry");
3298 valstr = g_string_new (NULL);
3299 value_get_as_gstring (swrb->value, valstr, so->sheet->convs);
3300 gtk_entry_set_text (GTK_ENTRY (state->value), valstr->str);
3301 g_string_free (valstr, TRUE);
3303 gnm_editable_enters (GTK_WINDOW (state->dialog),
3304 GTK_WIDGET (state->expression));
3305 gnm_editable_enters (GTK_WINDOW (state->dialog),
3306 GTK_WIDGET (state->label));
3307 gnm_editable_enters (GTK_WINDOW (state->dialog),
3308 GTK_WIDGET (state->value));
3310 g_signal_connect (G_OBJECT (state->label),
3311 "changed",
3312 G_CALLBACK (cb_radio_button_label_changed), state);
3313 g_signal_connect (G_OBJECT (state->value),
3314 "changed",
3315 G_CALLBACK (cb_radio_button_value_changed), state);
3316 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
3317 "clicked",
3318 G_CALLBACK (cb_radio_button_config_ok_clicked), state);
3319 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
3320 "clicked",
3321 G_CALLBACK (cb_radio_button_config_cancel_clicked), state);
3323 gnm_init_help_button (
3324 go_gtk_builder_get_widget (gui, "help_button"),
3325 GNUMERIC_HELP_LINK_SO_RADIO_BUTTON);
3327 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
3328 SHEET_OBJECT_CONFIG_KEY);
3330 wbc_gtk_attach_guru (state->wbcg, state->dialog);
3331 g_object_set_data_full (G_OBJECT (state->dialog),
3332 "state", state, (GDestroyNotify) cb_radio_button_config_destroy);
3333 g_object_unref (gui);
3335 /* Note: half of the set-focus action is handle by the default */
3336 /* callback installed by wbc_gtk_attach_guru */
3337 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
3338 G_CALLBACK (cb_radio_button_set_focus), state);
3340 gtk_widget_show (state->dialog);
3343 static void
3344 sheet_widget_radio_button_draw_cairo (SheetObject const *so, cairo_t *cr,
3345 G_GNUC_UNUSED double width, double height)
3347 SheetWidgetRadioButton const *swr = GNM_SOW_RADIO_BUTTON (so);
3348 double halfheight = height/2;
3349 double dx = 8., dxh, pm;
3350 int pw, ph;
3352 pm = MIN (height - 2, width - 12);
3353 if (dx > pm)
3354 dx = MAX (pm, 3);
3355 dxh = dx/2;
3357 cairo_save (cr);
3358 cairo_set_line_width (cr, 0.5);
3359 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
3361 cairo_new_path (cr);
3362 cairo_move_to (cr, dxh + dx, halfheight);
3363 cairo_arc (cr, dx, halfheight, dxh, 0., 2*M_PI);
3364 cairo_close_path (cr);
3365 cairo_fill_preserve (cr);
3366 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
3367 cairo_stroke (cr);
3369 if (swr->active) {
3370 cairo_new_path (cr);
3371 cairo_move_to (cr, dx + dxh/2 + 0.5, halfheight);
3372 cairo_arc (cr, dx, halfheight, dxh/2 + 0.5, 0., 2*M_PI);
3373 cairo_close_path (cr);
3374 cairo_fill (cr);
3377 cairo_move_to (cr, 2 * dx, halfheight);
3379 pw = width - 2 * dx;
3380 ph = height;
3382 draw_cairo_text (cr, swr->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
3384 cairo_new_path (cr);
3385 cairo_restore (cr);
3388 SOW_MAKE_TYPE (radio_button, RadioButton,
3389 sheet_widget_radio_button_user_config,
3390 sheet_widget_radio_button_set_sheet,
3391 so_clear_sheet,
3392 sheet_widget_radio_button_foreach_dep,
3393 sheet_widget_radio_button_copy,
3394 sheet_widget_radio_button_write_xml_sax,
3395 sheet_widget_radio_button_prep_sax_parser,
3396 sheet_widget_radio_button_get_property,
3397 sheet_widget_radio_button_set_property,
3398 sheet_widget_radio_button_draw_cairo,
3400 g_object_class_install_property
3401 (object_class, SOR_PROP_ACTIVE,
3402 g_param_spec_boolean ("active", NULL, NULL,
3403 FALSE,
3404 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3405 g_object_class_install_property
3406 (object_class, SOR_PROP_TEXT,
3407 g_param_spec_string ("text", NULL, NULL, NULL,
3408 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3409 g_object_class_install_property
3410 (object_class, SOR_PROP_MARKUP,
3411 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
3412 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3413 g_object_class_install_property
3414 (object_class, SOR_PROP_VALUE,
3415 g_param_spec_boxed ("value", NULL, NULL,
3416 gnm_value_get_type (),
3417 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3420 /****************************************************************************/
3422 #define GNM_SOW_LIST_BASE_TYPE (sheet_widget_list_base_get_type ())
3423 #define GNM_SOW_LIST_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_LIST_BASE_TYPE, SheetWidgetListBase))
3424 #define DEP_TO_LIST_BASE_CONTENT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, content_dep))
3425 #define DEP_TO_LIST_BASE_OUTPUT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, output_dep))
3427 typedef struct {
3428 SheetObjectWidget sow;
3430 GnmDependent content_dep; /* content of the list */
3431 GnmDependent output_dep; /* selected element */
3433 GtkTreeModel *model;
3434 int selection;
3435 gboolean result_as_index;
3436 } SheetWidgetListBase;
3437 typedef struct {
3438 SheetObjectWidgetClass base;
3440 void (*model_changed) (SheetWidgetListBase *list);
3441 void (*selection_changed) (SheetWidgetListBase *list);
3442 } SheetWidgetListBaseClass;
3444 enum {
3445 LIST_BASE_MODEL_CHANGED,
3446 LIST_BASE_SELECTION_CHANGED,
3447 LIST_BASE_LAST_SIGNAL
3450 static guint list_base_signals [LIST_BASE_LAST_SIGNAL] = { 0 };
3451 static GType sheet_widget_list_base_get_type (void);
3453 static void
3454 sheet_widget_list_base_set_selection (SheetWidgetListBase *swl, int selection,
3455 WorkbookControl *wbc)
3457 GnmCellRef ref;
3459 if (selection >= 0 && swl->model != NULL) {
3460 int n = gtk_tree_model_iter_n_children (swl->model, NULL);
3461 if (selection > n)
3462 selection = n;
3463 } else
3464 selection = 0;
3466 if (swl->selection != selection) {
3467 swl->selection = selection;
3468 if (NULL!= wbc &&
3469 so_get_ref (GNM_SO (swl), &ref, TRUE) != NULL) {
3470 GnmValue *v;
3471 if (swl->result_as_index)
3472 v = value_new_int (swl->selection);
3473 else if (selection != 0) {
3474 GtkTreeIter iter;
3475 char *content;
3476 gtk_tree_model_iter_nth_child
3477 (swl->model, &iter, NULL, selection - 1);
3478 gtk_tree_model_get (swl->model, &iter,
3479 0, &content, -1);
3480 v = value_new_string_nocopy (content);
3481 } else
3482 v = value_new_string ("");
3483 cmd_so_set_value (wbc, _("Clicking in list"), &ref, v,
3484 sheet_object_get_sheet (GNM_SO (swl)));
3486 g_signal_emit (G_OBJECT (swl),
3487 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3491 static void
3492 sheet_widget_list_base_set_selection_value (SheetWidgetListBase *swl, GnmValue *v)
3494 GtkTreeIter iter;
3495 int selection = 0, i = 1;
3497 if (swl->model != NULL && gtk_tree_model_get_iter_first (swl->model, &iter)) {
3498 char *str = value_get_as_string (v);
3499 do {
3500 char *content;
3501 gboolean match;
3502 gtk_tree_model_get (swl->model, &iter,
3503 0, &content, -1);
3504 match = 0 == g_ascii_strcasecmp (str, content);
3505 g_free (content);
3506 if (match) {
3507 selection = i;
3508 break;
3510 i++;
3511 } while (gtk_tree_model_iter_next (swl->model, &iter));
3512 g_free (str);
3515 if (swl->selection != selection) {
3516 swl->selection = selection;
3517 g_signal_emit (G_OBJECT (swl),
3518 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3522 static void
3523 list_output_eval (GnmDependent *dep)
3525 GnmEvalPos pos;
3526 GnmValue *v = gnm_expr_top_eval (dep->texpr,
3527 eval_pos_init_dep (&pos, dep),
3528 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
3529 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_OUTPUT (dep);
3531 if (swl->result_as_index)
3532 sheet_widget_list_base_set_selection
3533 (swl, floor (value_get_as_float (v)), NULL);
3534 else
3535 sheet_widget_list_base_set_selection_value (swl, v);
3536 value_release (v);
3539 static void
3540 list_output_debug_name (GnmDependent const *dep, GString *target)
3542 g_string_append_printf (target, "ListOutput%p", (void *)dep);
3545 static DEPENDENT_MAKE_TYPE (list_output, NULL)
3547 /*-----------*/
3548 static GnmValue *
3549 cb_collect (GnmValueIter const *iter, GtkListStore *model)
3551 GtkTreeIter list_iter;
3553 gtk_list_store_append (model, &list_iter);
3554 if (NULL != iter->v) {
3555 GOFormat const *fmt = (NULL != iter->cell_iter)
3556 ? gnm_cell_get_format (iter->cell_iter->cell) : NULL;
3557 char *label = format_value (fmt, iter->v, -1, NULL);
3558 gtk_list_store_set (model, &list_iter, 0, label, -1);
3559 g_free (label);
3560 } else
3561 gtk_list_store_set (model, &list_iter, 0, "", -1);
3563 return NULL;
3565 static void
3566 list_content_eval (GnmDependent *dep)
3568 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_CONTENT (dep);
3569 GnmEvalPos ep;
3570 GnmValue *v = NULL;
3571 GtkListStore *model;
3573 if (dep->texpr != NULL) {
3574 v = gnm_expr_top_eval (dep->texpr,
3575 eval_pos_init_dep (&ep, dep),
3576 GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
3577 GNM_EXPR_EVAL_PERMIT_EMPTY);
3579 model = gtk_list_store_new (1, G_TYPE_STRING);
3580 if (v) {
3581 value_area_foreach (v, &ep, CELL_ITER_ALL,
3582 (GnmValueIterFunc) cb_collect, model);
3583 value_release (v);
3586 if (NULL != swl->model)
3587 g_object_unref (swl->model);
3588 swl->model = GTK_TREE_MODEL (model);
3589 g_signal_emit (G_OBJECT (swl), list_base_signals [LIST_BASE_MODEL_CHANGED], 0);
3592 static void
3593 list_content_debug_name (GnmDependent const *dep, GString *target)
3595 g_string_append_printf (target, "ListContent%p", (void *)dep);
3598 static DEPENDENT_MAKE_TYPE (list_content, NULL)
3600 /*-----------*/
3602 static void
3603 sheet_widget_list_base_init (SheetObjectWidget *sow)
3605 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3606 SheetObject *so = GNM_SO (sow);
3608 so->flags &= ~SHEET_OBJECT_PRINT;
3610 swl->content_dep.sheet = NULL;
3611 swl->content_dep.flags = list_content_get_dep_type ();
3612 swl->content_dep.texpr = NULL;
3614 swl->output_dep.sheet = NULL;
3615 swl->output_dep.flags = list_output_get_dep_type ();
3616 swl->output_dep.texpr = NULL;
3618 swl->model = NULL;
3619 swl->selection = 0;
3620 swl->result_as_index = TRUE;
3623 static void
3624 sheet_widget_list_base_finalize (GObject *obj)
3626 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (obj);
3627 dependent_set_expr (&swl->content_dep, NULL);
3628 dependent_set_expr (&swl->output_dep, NULL);
3629 if (swl->model != NULL) {
3630 g_object_unref (swl->model);
3631 swl->model = NULL;
3633 sheet_object_widget_class->finalize (obj);
3636 static void
3637 sheet_widget_list_base_user_config (SheetObject *so, SheetControl *sc)
3639 dialog_so_list (scg_wbcg (GNM_SCG (sc)), G_OBJECT (so));
3641 static gboolean
3642 sheet_widget_list_base_set_sheet (SheetObject *so, Sheet *sheet)
3644 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3646 g_return_val_if_fail (swl != NULL, TRUE);
3647 g_return_val_if_fail (swl->content_dep.sheet == NULL, TRUE);
3648 g_return_val_if_fail (swl->output_dep.sheet == NULL, TRUE);
3650 dependent_set_sheet (&swl->content_dep, sheet);
3651 dependent_set_sheet (&swl->output_dep, sheet);
3653 list_content_eval (&swl->content_dep); /* populate the list */
3655 return FALSE;
3658 static void
3659 sheet_widget_list_base_foreach_dep (SheetObject *so,
3660 SheetObjectForeachDepFunc func,
3661 gpointer user)
3663 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3664 func (&swl->content_dep, so, user);
3665 func (&swl->output_dep, so, user);
3668 static void
3669 sheet_widget_list_base_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
3670 GnmConventions const *convs)
3672 SheetWidgetListBase const *swl = GNM_SOW_LIST_BASE (so);
3673 sax_write_dep (output, &swl->content_dep, "Content", convs);
3674 sax_write_dep (output, &swl->output_dep, "Output", convs);
3675 gsf_xml_out_add_int (output, "OutputAsIndex", swl->result_as_index ? 1 : 0);
3678 static void
3679 sheet_widget_list_base_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3680 xmlChar const **attrs,
3681 GnmConventions const *convs)
3683 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3685 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
3686 if (sax_read_dep (attrs, "Content", &swl->content_dep, xin, convs))
3688 else if (sax_read_dep (attrs, "Output", &swl->output_dep, xin, convs))
3690 else if (gnm_xml_attr_bool (attrs, "OutputAsIndex", &swl->result_as_index))
3694 static GtkWidget *
3695 sheet_widget_list_base_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
3697 g_warning("ERROR: sheet_widget_list_base_create_widget SHOULD NEVER BE CALLED (but it has been)!\n");
3698 return gtk_frame_new ("invisiwidget(WARNING: I AM A BUG!)");
3701 SOW_MAKE_TYPE (list_base, ListBase,
3702 sheet_widget_list_base_user_config,
3703 sheet_widget_list_base_set_sheet,
3704 so_clear_sheet,
3705 sheet_widget_list_base_foreach_dep,
3706 NULL,
3707 sheet_widget_list_base_write_xml_sax,
3708 sheet_widget_list_base_prep_sax_parser,
3709 NULL,
3710 NULL,
3711 sheet_widget_draw_cairo,
3713 list_base_signals[LIST_BASE_MODEL_CHANGED] = g_signal_new ("model-changed",
3714 GNM_SOW_LIST_BASE_TYPE,
3715 G_SIGNAL_RUN_LAST,
3716 G_STRUCT_OFFSET (SheetWidgetListBaseClass, model_changed),
3717 NULL, NULL,
3718 g_cclosure_marshal_VOID__VOID,
3719 G_TYPE_NONE, 0);
3720 list_base_signals[LIST_BASE_SELECTION_CHANGED] = g_signal_new ("selection-changed",
3721 GNM_SOW_LIST_BASE_TYPE,
3722 G_SIGNAL_RUN_LAST,
3723 G_STRUCT_OFFSET (SheetWidgetListBaseClass, selection_changed),
3724 NULL, NULL,
3725 g_cclosure_marshal_VOID__VOID,
3726 G_TYPE_NONE, 0);
3729 void
3730 sheet_widget_list_base_set_links (SheetObject *so,
3731 GnmExprTop const *output,
3732 GnmExprTop const *content)
3734 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3735 dependent_set_expr (&swl->output_dep, output);
3736 if (output && swl->output_dep.sheet)
3737 dependent_link (&swl->output_dep);
3738 dependent_set_expr (&swl->content_dep, content);
3739 if (content && swl->content_dep.sheet) {
3740 dependent_link (&swl->content_dep);
3741 list_content_eval (&swl->content_dep); /* populate the list */
3745 GnmExprTop const *
3746 sheet_widget_list_base_get_result_link (SheetObject const *so)
3748 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3749 GnmExprTop const *texpr = swl->output_dep.texpr;
3751 if (texpr)
3752 gnm_expr_top_ref (texpr);
3754 return texpr;
3757 GnmExprTop const *
3758 sheet_widget_list_base_get_content_link (SheetObject const *so)
3760 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3761 GnmExprTop const *texpr = swl->content_dep.texpr;
3763 if (texpr)
3764 gnm_expr_top_ref (texpr);
3766 return texpr;
3769 gboolean
3770 sheet_widget_list_base_result_type_is_index (SheetObject const *so)
3772 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3774 return swl->result_as_index;
3777 void
3778 sheet_widget_list_base_set_result_type (SheetObject *so, gboolean as_index)
3780 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3782 if (swl->result_as_index == as_index)
3783 return;
3785 swl->result_as_index = as_index;
3790 * sheet_widget_list_base_get_adjustment:
3791 * @so: #SheetObject
3793 * Note: allocates a new adjustment.
3794 * Returns: (transfer full): the newly created #GtkAdjustment.
3796 GtkAdjustment *
3797 sheet_widget_list_base_get_adjustment (SheetObject *so)
3799 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3800 GtkAdjustment *adj;
3802 g_return_val_if_fail (swl, NULL);
3804 adj = (GtkAdjustment*)gtk_adjustment_new
3805 (swl->selection,
3807 1 + gtk_tree_model_iter_n_children (swl->model, NULL),
3811 g_object_ref_sink (adj);
3813 return adj;
3816 /****************************************************************************/
3818 #define GNM_SOW_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_LIST_TYPE, SheetWidgetList))
3820 typedef SheetWidgetListBase SheetWidgetList;
3821 typedef SheetWidgetListBaseClass SheetWidgetListClass;
3823 static void
3824 cb_list_selection_changed (SheetWidgetListBase *swl,
3825 GtkTreeSelection *selection)
3827 if (swl->selection > 0) {
3828 GtkTreePath *path = gtk_tree_path_new_from_indices (swl->selection-1, -1);
3829 gtk_tree_selection_select_path (selection, path);
3830 gtk_tree_path_free (path);
3831 } else
3832 gtk_tree_selection_unselect_all (selection);
3835 static void
3836 cb_list_model_changed (SheetWidgetListBase *swl, GtkTreeView *list)
3838 int old_selection = swl->selection;
3839 swl->selection = -1;
3840 gtk_tree_view_set_model (GTK_TREE_VIEW (list), swl->model);
3841 sheet_widget_list_base_set_selection (swl, old_selection, NULL);
3843 static void
3844 cb_selection_changed (GtkTreeSelection *selection,
3845 SheetWidgetListBase *swl)
3847 GtkWidget *view = (GtkWidget *)gtk_tree_selection_get_tree_view (selection);
3848 GnmSimpleCanvas *scanvas = GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (view, GNM_SIMPLE_CANVAS_TYPE));
3849 GtkTreeModel *model;
3850 GtkTreeIter iter;
3851 int pos = 0;
3852 if (swl->selection != -1) {
3853 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3854 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
3855 if (NULL != path) {
3856 pos = *gtk_tree_path_get_indices (path) + 1;
3857 gtk_tree_path_free (path);
3860 sheet_widget_list_base_set_selection
3861 (swl, pos, scg_wbc (scanvas->scg));
3865 static GtkWidget *
3866 sheet_widget_list_create_widget (SheetObjectWidget *sow)
3868 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3869 GtkTreeSelection *selection;
3870 GtkTreeIter iter;
3871 GtkWidget *list = gtk_tree_view_new_with_model (swl->model);
3872 GtkWidget *sw = gtk_scrolled_window_new (
3873 gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (list)),
3874 gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (list)));
3875 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
3876 GTK_POLICY_AUTOMATIC,
3877 GTK_POLICY_ALWAYS);
3878 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
3879 gtk_tree_view_append_column (GTK_TREE_VIEW (list),
3880 gtk_tree_view_column_new_with_attributes ("ID",
3881 gtk_cell_renderer_text_new (), "text", 0,
3882 NULL));
3884 gtk_container_add (GTK_CONTAINER (sw), list);
3886 g_signal_connect_object (G_OBJECT (swl), "model-changed",
3887 G_CALLBACK (cb_list_model_changed), list, 0);
3889 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
3890 if ((swl->model != NULL) && (swl->selection > 0) &&
3891 gtk_tree_model_iter_nth_child (swl->model, &iter, NULL, swl->selection - 1))
3892 gtk_tree_selection_select_iter (selection, &iter);
3893 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
3894 G_CALLBACK (cb_list_selection_changed), selection, 0);
3895 g_signal_connect (selection, "changed",
3896 G_CALLBACK (cb_selection_changed), swl);
3897 return sw;
3900 static void
3901 sheet_widget_list_draw_cairo (SheetObject const *so, cairo_t *cr,
3902 double width, double height)
3904 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3906 cairo_save (cr);
3907 cairo_set_line_width (cr, 0.5);
3908 cairo_set_source_rgb(cr, 0, 0, 0);
3910 cairo_new_path (cr);
3911 cairo_move_to (cr, 0, 0);
3912 cairo_line_to (cr, width, 0);
3913 cairo_line_to (cr, width, height);
3914 cairo_line_to (cr, 0, height);
3915 cairo_close_path (cr);
3916 cairo_stroke (cr);
3918 cairo_new_path (cr);
3919 cairo_move_to (cr, width - 10, 0);
3920 cairo_rel_line_to (cr, 0, height);
3921 cairo_stroke (cr);
3923 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
3925 cairo_new_path (cr);
3926 cairo_move_to (cr, width - 5 -3, height - 12);
3927 cairo_rel_line_to (cr, 6, 0);
3928 cairo_rel_line_to (cr, -3, 8);
3929 cairo_close_path (cr);
3930 cairo_fill (cr);
3932 cairo_new_path (cr);
3933 cairo_move_to (cr, width - 5 -3, 12);
3934 cairo_rel_line_to (cr, 6, 0);
3935 cairo_rel_line_to (cr, -3, -8);
3936 cairo_close_path (cr);
3937 cairo_fill (cr);
3939 if (swl->model != NULL) {
3940 GtkTreeIter iter;
3941 GString*str = g_string_new (NULL);
3942 int twidth = width, theight = height;
3945 cairo_new_path (cr);
3946 cairo_rectangle (cr, 2, 1, width - 2 - 12, height - 2);
3947 cairo_clip (cr);
3948 if (gtk_tree_model_get_iter_first (swl->model, &iter))
3949 do {
3950 char *astr = NULL, *newline;
3951 gtk_tree_model_get (swl->model, &iter, 0, &astr, -1);
3952 while (NULL != (newline = strchr (astr, '\n')))
3953 *newline = ' ';
3954 g_string_append (str, astr);
3955 g_string_append_c (str, '\n');
3956 g_free (astr);
3957 } while (gtk_tree_model_iter_next (swl->model, &iter));
3959 cairo_translate (cr, 4., 2.);
3961 draw_cairo_text (cr, str->str, &twidth, &theight, FALSE, FALSE, FALSE,
3962 swl->selection, FALSE);
3964 g_string_free (str, TRUE);
3967 cairo_new_path (cr);
3968 cairo_restore (cr);
3971 static void
3972 sheet_widget_list_class_init (SheetObjectWidgetClass *sow_class)
3974 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
3976 so_class->draw_cairo = &sheet_widget_list_draw_cairo;
3977 sow_class->create_widget = &sheet_widget_list_create_widget;
3980 GSF_CLASS (SheetWidgetList, sheet_widget_list,
3981 &sheet_widget_list_class_init, NULL,
3982 GNM_SOW_LIST_BASE_TYPE)
3984 /****************************************************************************/
3986 #define GNM_SOW_COMBO(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_COMBO_TYPE, SheetWidgetCombo))
3988 typedef SheetWidgetListBase SheetWidgetCombo;
3989 typedef SheetWidgetListBaseClass SheetWidgetComboClass;
3991 static void
3992 cb_combo_selection_changed (SheetWidgetListBase *swl,
3993 GtkComboBox *combo)
3995 int pos = swl->selection - 1;
3996 if (pos < 0) {
3997 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo))), "");
3998 pos = -1;
4000 gtk_combo_box_set_active (combo, pos);
4003 static void
4004 cb_combo_model_changed (SheetWidgetListBase *swl, GtkComboBox *combo)
4006 gtk_combo_box_set_model (GTK_COMBO_BOX (combo), swl->model);
4008 /* we can not set this until we have a model,
4009 * but after that we can not reset it */
4010 if (gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo)) < 0)
4011 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), 0);
4013 /* force entry to reload */
4014 cb_combo_selection_changed (swl, combo);
4017 static void
4018 cb_combo_changed (GtkComboBox *combo, SheetWidgetListBase *swl)
4020 int pos = gtk_combo_box_get_active (combo) + 1;
4021 sheet_widget_list_base_set_selection (swl, pos,
4022 widget_wbc (GTK_WIDGET (combo)));
4025 static GtkWidget *
4026 sheet_widget_combo_create_widget (SheetObjectWidget *sow)
4028 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
4029 GtkWidget *widget = gtk_event_box_new (), *combo;
4031 combo = gtk_combo_box_new_with_entry ();
4032 gtk_widget_set_can_focus (gtk_bin_get_child (GTK_BIN (combo)),
4033 FALSE);
4034 if (swl->model != NULL)
4035 g_object_set (G_OBJECT (combo),
4036 "model", swl->model,
4037 "entry-text-column", 0,
4038 "active", swl->selection - 1,
4039 NULL);
4041 g_signal_connect_object (G_OBJECT (swl), "model-changed",
4042 G_CALLBACK (cb_combo_model_changed), combo, 0);
4043 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
4044 G_CALLBACK (cb_combo_selection_changed), combo, 0);
4045 g_signal_connect (G_OBJECT (combo), "changed",
4046 G_CALLBACK (cb_combo_changed), swl);
4048 gtk_container_add (GTK_CONTAINER (widget), combo);
4049 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
4050 return widget;
4053 static void
4054 sheet_widget_combo_draw_cairo (SheetObject const *so, cairo_t *cr,
4055 double width, double height)
4057 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
4058 double halfheight = height/2;
4060 cairo_save (cr);
4061 cairo_set_line_width (cr, 0.5);
4062 cairo_set_source_rgb(cr, 0, 0, 0);
4064 cairo_new_path (cr);
4065 cairo_move_to (cr, 0, 0);
4066 cairo_line_to (cr, width, 0);
4067 cairo_line_to (cr, width, height);
4068 cairo_line_to (cr, 0, height);
4069 cairo_close_path (cr);
4070 cairo_stroke (cr);
4072 cairo_new_path (cr);
4073 cairo_move_to (cr, width - 10, 0);
4074 cairo_rel_line_to (cr, 0, height);
4075 cairo_stroke (cr);
4077 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
4079 cairo_new_path (cr);
4080 cairo_move_to (cr, width - 5 -3, halfheight - 4);
4081 cairo_rel_line_to (cr, 6, 0);
4082 cairo_rel_line_to (cr, -3, 8);
4083 cairo_close_path (cr);
4084 cairo_fill (cr);
4086 cairo_set_source_rgb(cr, 0, 0, 0);
4087 cairo_move_to (cr, 4., halfheight);
4089 if (swl->model != NULL) {
4090 GtkTreeIter iter;
4091 if (gtk_tree_model_iter_nth_child (swl->model, &iter, NULL,
4092 swl->selection - 1)) {
4093 char *str = NULL;
4094 gtk_tree_model_get (swl->model, &iter, 0, &str, -1);
4095 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
4096 g_free (str);
4100 cairo_new_path (cr);
4101 cairo_restore (cr);
4104 static void
4105 sheet_widget_combo_class_init (SheetObjectWidgetClass *sow_class)
4107 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
4109 so_class->draw_cairo = &sheet_widget_combo_draw_cairo;
4110 sow_class->create_widget = &sheet_widget_combo_create_widget;
4113 GSF_CLASS (SheetWidgetCombo, sheet_widget_combo,
4114 &sheet_widget_combo_class_init, NULL,
4115 GNM_SOW_LIST_BASE_TYPE)
4121 /**************************************************************************/
4124 * sheet_widget_init_clases:
4125 * @void:
4127 * Initilize the classes for the sheet-object-widgets. We need to initalize
4128 * them before we try loading a sheet that might contain sheet-object-widgets
4130 void
4131 sheet_object_widget_register (void)
4133 GNM_SOW_FRAME_TYPE;
4134 GNM_SOW_BUTTON_TYPE;
4135 GNM_SOW_SCROLLBAR_TYPE;
4136 GNM_SOW_CHECKBOX_TYPE;
4137 GNM_SOW_RADIO_BUTTON_TYPE;
4138 GNM_SOW_LIST_TYPE;
4139 GNM_SOW_COMBO_TYPE;
4140 GNM_SOW_SPIN_BUTTON_TYPE;
4141 GNM_SOW_SLIDER_TYPE;