1
2 /*
3 * sheet-object-widget.c: SheetObject wrappers for simple gtk widgets.
4 *
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <application.h>
26 #include <sheet-object-widget-impl.h>
27 #include <widgets/gnm-radiobutton.h>
28 #include <gnm-pane.h>
29 #include <gnumeric-simple-canvas.h>
30 #include <gui-util.h>
31 #include <gutils.h>
32 #include <dependent.h>
33 #include <sheet-control-gui.h>
34 #include <sheet-object-impl.h>
35 #include <expr.h>
36 #include <parse-util.h>
37 #include <value.h>
38 #include <ranges.h>
39 #include <selection.h>
40 #include <wbc-gtk.h>
41 #include <workbook.h>
42 #include <sheet.h>
43 #include <cell.h>
44 #include <mathfunc.h>
45 #include <widgets/gnm-expr-entry.h>
46 #include <dialogs/dialogs.h>
47 #include <dialogs/help.h>
48 #include <xml-sax.h>
49 #include <commands.h>
50 #include <gnm-format.h>
51 #include <number-match.h>
52
53 #include <goffice/goffice.h>
54
55 #include <gsf/gsf-impl-utils.h>
56 #include <libxml/globals.h>
57 #include <gdk/gdkkeysyms.h>
58 #include <math.h>
59 #include <string.h>
60
61 #define CXML2C(s) ((char const *)(s))
62 #define CC2XML(s) ((xmlChar const *)(s))
63
64 static inline gboolean
attr_eq(const xmlChar * a,const char * s)65 attr_eq (const xmlChar *a, const char *s)
66 {
67 return !strcmp (CXML2C (a), s);
68 }
69
70 /****************************************************************************/
71
72 static void
cb_so_get_ref(GnmDependent * dep,G_GNUC_UNUSED SheetObject * so,gpointer user)73 cb_so_get_ref (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, gpointer user)
74 {
75 GnmDependent **pdep = user;
76 *pdep = dep;
77 }
78
79 static GnmCellRef *
so_get_ref(SheetObject const * so,GnmCellRef * res,gboolean force_sheet)80 so_get_ref (SheetObject const *so, GnmCellRef *res, gboolean force_sheet)
81 {
82 GnmValue *target;
83 GnmDependent *dep = NULL;
84
85 g_return_val_if_fail (so != NULL, NULL);
86
87 /* Let's hope there's just one. */
88 sheet_object_foreach_dep ((SheetObject*)so, cb_so_get_ref, &dep);
89 g_return_val_if_fail (dep, NULL);
90
91 if (dep->texpr == NULL)
92 return NULL;
93
94 target = gnm_expr_top_get_range (dep->texpr);
95 if (target == NULL)
96 return NULL;
97
98 *res = target->v_range.cell.a;
99 value_release (target);
100
101 if (force_sheet && res->sheet == NULL)
102 res->sheet = sheet_object_get_sheet (so);
103 return res;
104 }
105
106 static void
cb_so_clear_sheet(GnmDependent * dep,G_GNUC_UNUSED SheetObject * so,G_GNUC_UNUSED gpointer user)107 cb_so_clear_sheet (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, G_GNUC_UNUSED gpointer user)
108 {
109 if (dependent_is_linked (dep))
110 dependent_unlink (dep);
111 dep->sheet = NULL;
112 }
113
114 static gboolean
so_clear_sheet(SheetObject * so)115 so_clear_sheet (SheetObject *so)
116 {
117 /* Note: This implements sheet_object_clear_sheet. */
118 sheet_object_foreach_dep (so, cb_so_clear_sheet, NULL);
119 return FALSE;
120 }
121
122 static GocWidget *
get_goc_widget(SheetObjectView * view)123 get_goc_widget (SheetObjectView *view)
124 {
125 GocItem *item = sheet_object_view_get_item (view);
126 return item ? GOC_WIDGET (item) : NULL;
127 }
128
129 static void
so_widget_view_set_bounds(SheetObjectView * sov,double const * coords,gboolean visible)130 so_widget_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
131 {
132 GocItem *view = GOC_ITEM (sov);
133 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
134 double left = MIN (coords [0], coords [2]) / scale;
135 double top = MIN (coords [1], coords [3]) / scale;
136 double width = (fabs (coords [2] - coords [0]) + 1.) / scale;
137 double height = (fabs (coords [3] - coords [1]) + 1.) / scale;
138
139 /* We only need the next check for frames, but it doesn't hurt otherwise. */
140 if (width < 8.)
141 width = 8.;
142
143 if (visible) {
144 /* NOTE : far point is EXCLUDED so we add 1 */
145 goc_widget_set_bounds (get_goc_widget (sov),
146 left, top, width, height);
147 goc_item_show (view);
148 } else
149 goc_item_hide (view);
150 }
151
152 static GdkWindow *
so_widget_view_get_window(GocItem * item)153 so_widget_view_get_window (GocItem *item)
154 {
155 GocItem *item0 = sheet_object_view_get_item (GNM_SO_VIEW (item));
156 return goc_item_get_window (item0);
157 }
158
159 static void
so_widget_view_class_init(SheetObjectViewClass * sov_klass)160 so_widget_view_class_init (SheetObjectViewClass *sov_klass)
161 {
162 GocItemClass *item_klass = (GocItemClass *) sov_klass;
163 sov_klass->set_bounds = so_widget_view_set_bounds;
164 item_klass->get_window = so_widget_view_get_window;
165 }
166
167 static GSF_CLASS (SOWidgetView, so_widget_view,
168 so_widget_view_class_init, NULL,
169 GNM_SO_VIEW_TYPE)
170
171 /****************************************************************************/
172
173 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-config-dialog"
174
175 #define GNM_SOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_TYPE, SheetObjectWidgetClass))
176 #define SOW_CLASS(so) (GNM_SOW_CLASS (G_OBJECT_GET_CLASS(so)))
177
178 #define SOW_MAKE_TYPE(n1, n2, fn_config, fn_set_sheet, fn_clear_sheet, fn_foreach_dep, \
179 fn_copy, fn_write_sax, fn_prep_sax_parser, \
180 fn_get_property, fn_set_property, \
181 fn_draw_cairo, class_init_code) \
182 \
183 static void \
184 sheet_widget_ ## n1 ## _class_init (GObjectClass *object_class) \
185 { \
186 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class); \
187 SheetObjectClass *so_class = GNM_SO_CLASS (object_class); \
188 object_class->finalize = &sheet_widget_ ## n1 ## _finalize; \
189 object_class->set_property = fn_set_property; \
190 object_class->get_property = fn_get_property; \
191 so_class->user_config = fn_config; \
192 so_class->interactive = TRUE; \
193 so_class->assign_to_sheet = fn_set_sheet; \
194 so_class->remove_from_sheet = fn_clear_sheet; \
195 so_class->foreach_dep = fn_foreach_dep; \
196 so_class->copy = fn_copy; \
197 so_class->write_xml_sax = fn_write_sax; \
198 so_class->prep_sax_parser = fn_prep_sax_parser; \
199 so_class->draw_cairo = fn_draw_cairo; \
200 sow_class->create_widget = &sheet_widget_ ## n1 ## _create_widget; \
201 { class_init_code; } \
202 } \
203 \
204 GSF_CLASS (SheetWidget ## n2, sheet_widget_ ## n1, \
205 &sheet_widget_ ## n1 ## _class_init, \
206 &sheet_widget_ ## n1 ## _init, \
207 GNM_SOW_TYPE)
208
209 typedef struct {
210 SheetObject so;
211 } SheetObjectWidget;
212
213 typedef struct {
214 SheetObjectClass parent_class;
215 GtkWidget *(*create_widget)(SheetObjectWidget *);
216 } SheetObjectWidgetClass;
217
218 static GObjectClass *sheet_object_widget_class = NULL;
219
220 static GtkWidget *
sow_create_widget(SheetObjectWidget * sow)221 sow_create_widget (SheetObjectWidget *sow)
222 {
223 GtkWidget *w = SOW_CLASS(sow)->create_widget (sow);
224 GtkStyleContext *context = gtk_widget_get_style_context (w);
225 gtk_style_context_add_class (context, "sheet-object");
226 return w;
227 }
228
229 static void
sheet_widget_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)230 sheet_widget_draw_cairo (SheetObject const *so, cairo_t *cr,
231 double width, double height)
232 {
233 /* This is the default for so widgets without their own method */
234 /* See bugs #705638 and #705640 */
235 if (NULL != gdk_screen_get_default ()) {
236 GtkWidget *win = gtk_offscreen_window_new ();
237 GtkWidget *w = sow_create_widget (GNM_SOW (so));
238
239 gtk_container_add (GTK_CONTAINER (win), w);
240 gtk_widget_set_size_request (w, width, height);
241 gtk_widget_show_all (win);
242 gtk_container_propagate_draw (GTK_CONTAINER (win), w, cr);
243 gtk_widget_destroy (win);
244 } else
245 g_warning (_("Because of GTK bug #705640, a sheet object widget is not being printed."));
246 }
247
248 static void
sax_write_dep(GsfXMLOut * output,GnmDependent const * dep,char const * id,GnmConventions const * convs)249 sax_write_dep (GsfXMLOut *output, GnmDependent const *dep, char const *id,
250 GnmConventions const *convs)
251 {
252 if (dep->texpr != NULL) {
253 GnmParsePos pos;
254 char *val;
255
256 parse_pos_init_dep (&pos, dep);
257 val = gnm_expr_top_as_string (dep->texpr, &pos, convs);
258 gsf_xml_out_add_cstr (output, id, val);
259 g_free (val);
260 }
261 }
262
263 static gboolean
sax_read_dep(xmlChar const * const * attrs,char const * name,GnmDependent * dep,GsfXMLIn * xin,GnmConventions const * convs)264 sax_read_dep (xmlChar const * const *attrs, char const *name,
265 GnmDependent *dep, GsfXMLIn *xin, GnmConventions const *convs)
266 {
267 g_return_val_if_fail (attrs != NULL, FALSE);
268 g_return_val_if_fail (attrs[0] != NULL, FALSE);
269 g_return_val_if_fail (attrs[1] != NULL, FALSE);
270
271 if (!attr_eq (attrs[0], name))
272 return FALSE;
273
274 dep->sheet = NULL;
275 if (attrs[1] != NULL && *attrs[1] != '\0') {
276 GnmParsePos pp;
277
278 parse_pos_init_sheet (&pp, gnm_xml_in_cur_sheet (xin));
279 dep->texpr = gnm_expr_parse_str (CXML2C (attrs[1]), &pp,
280 GNM_EXPR_PARSE_DEFAULT,
281 convs, NULL);
282 } else
283 dep->texpr = NULL;
284
285 return TRUE;
286 }
287
288 static SheetObjectView *
sheet_object_widget_new_view(SheetObject * so,SheetObjectViewContainer * container)289 sheet_object_widget_new_view (SheetObject *so, SheetObjectViewContainer *container)
290 {
291 GtkWidget *view_widget = sow_create_widget (GNM_SOW (so));
292 GocItem *view_item = goc_item_new (
293 gnm_pane_object_group (GNM_PANE (container)),
294 so_widget_view_get_type (),
295 NULL);
296 goc_item_new (GOC_GROUP (view_item),
297 GOC_TYPE_WIDGET,
298 "widget", view_widget,
299 NULL);
300 /* g_warning ("%p is widget for so %p", (void *)view_widget, (void *)so);*/
301 gtk_widget_show_all (view_widget);
302 goc_item_hide (view_item);
303 gnm_pane_widget_register (so, view_widget, view_item);
304 return gnm_pane_object_register (so, view_item, TRUE);
305 }
306
307 static void
sheet_object_widget_class_init(GObjectClass * object_class)308 sheet_object_widget_class_init (GObjectClass *object_class)
309 {
310 SheetObjectClass *so_class = GNM_SO_CLASS (object_class);
311 SheetObjectWidgetClass *sow_class = GNM_SOW_CLASS (object_class);
312
313 sheet_object_widget_class = G_OBJECT_CLASS (object_class);
314
315 /* SheetObject class method overrides */
316 so_class->new_view = sheet_object_widget_new_view;
317 so_class->rubber_band_directly = TRUE;
318 so_class->draw_cairo = sheet_widget_draw_cairo;
319
320 sow_class->create_widget = NULL;
321 }
322
323 static void
sheet_object_widget_init(SheetObjectWidget * sow)324 sheet_object_widget_init (SheetObjectWidget *sow)
325 {
326 SheetObject *so = GNM_SO (sow);
327 so->flags |= SHEET_OBJECT_CAN_PRESS;
328 }
329
GSF_CLASS(SheetObjectWidget,sheet_object_widget,sheet_object_widget_class_init,sheet_object_widget_init,GNM_SO_TYPE)330 GSF_CLASS (SheetObjectWidget, sheet_object_widget,
331 sheet_object_widget_class_init,
332 sheet_object_widget_init,
333 GNM_SO_TYPE)
334
335 static WorkbookControl *
336 widget_wbc (GtkWidget *widget)
337 {
338 return scg_wbc (GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (widget, GNM_SIMPLE_CANVAS_TYPE))->scg);
339 }
340
341
342 /****************************************************************************/
343 #define GNM_SOW_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_FRAME_TYPE, SheetWidgetFrame))
344 typedef struct {
345 SheetObjectWidget sow;
346 char *label;
347 } SheetWidgetFrame;
348 typedef SheetObjectWidgetClass SheetWidgetFrameClass;
349
350 enum {
351 SOF_PROP_0 = 0,
352 SOF_PROP_TEXT
353 };
354
355 static void
sheet_widget_frame_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)356 sheet_widget_frame_get_property (GObject *obj, guint param_id,
357 GValue *value, GParamSpec *pspec)
358 {
359 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
360
361 switch (param_id) {
362 case SOF_PROP_TEXT:
363 g_value_set_string (value, swf->label);
364 break;
365 default:
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
367 break;
368 }
369 }
370
371 static void
sheet_widget_frame_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)372 sheet_widget_frame_set_property (GObject *obj, guint param_id,
373 GValue const *value, GParamSpec *pspec)
374 {
375 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
376
377 switch (param_id) {
378 case SOF_PROP_TEXT:
379 sheet_widget_frame_set_label (GNM_SO (swf),
380 g_value_get_string (value));
381 break;
382 default:
383 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
384 return;
385 }
386 }
387
388
389 static void
sheet_widget_frame_init_full(SheetWidgetFrame * swf,char const * text)390 sheet_widget_frame_init_full (SheetWidgetFrame *swf, char const *text)
391 {
392 swf->label = g_strdup (text);
393 }
394
395 static void
sheet_widget_frame_init(SheetWidgetFrame * swf)396 sheet_widget_frame_init (SheetWidgetFrame *swf)
397 {
398 sheet_widget_frame_init_full (swf, _("Frame"));
399 }
400
401 static void
sheet_widget_frame_finalize(GObject * obj)402 sheet_widget_frame_finalize (GObject *obj)
403 {
404 SheetWidgetFrame *swf = GNM_SOW_FRAME (obj);
405
406 g_free (swf->label);
407 swf->label = NULL;
408
409 sheet_object_widget_class->finalize (obj);
410 }
411
412 static GtkWidget *
sheet_widget_frame_create_widget(SheetObjectWidget * sow)413 sheet_widget_frame_create_widget (SheetObjectWidget *sow)
414 {
415 GtkWidget *widget = gtk_event_box_new (),
416 *frame = gtk_frame_new (GNM_SOW_FRAME (sow)->label);
417 gtk_container_add (GTK_CONTAINER (widget), frame);
418 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
419 return widget;
420 }
421
422 static void
sheet_widget_frame_copy(SheetObject * dst,SheetObject const * src)423 sheet_widget_frame_copy (SheetObject *dst, SheetObject const *src)
424 {
425 sheet_widget_frame_init_full (GNM_SOW_FRAME (dst),
426 GNM_SOW_FRAME (src)->label);
427 }
428
429 static void
sheet_widget_frame_write_xml_sax(SheetObject const * so,GsfXMLOut * output,G_GNUC_UNUSED GnmConventions const * convs)430 sheet_widget_frame_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
431 G_GNUC_UNUSED GnmConventions const *convs)
432 {
433 SheetWidgetFrame const *swf = GNM_SOW_FRAME (so);
434 gsf_xml_out_add_cstr (output, "Label", swf->label);
435 }
436
437 static void
sheet_widget_frame_prep_sax_parser(SheetObject * so,G_GNUC_UNUSED GsfXMLIn * xin,xmlChar const ** attrs,G_GNUC_UNUSED GnmConventions const * convs)438 sheet_widget_frame_prep_sax_parser (SheetObject *so, G_GNUC_UNUSED GsfXMLIn *xin,
439 xmlChar const **attrs,
440 G_GNUC_UNUSED GnmConventions const *convs)
441 {
442 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
443 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
444 if (attr_eq (attrs[0], "Label")) {
445 g_free (swf->label);
446 swf->label = g_strdup (CXML2C (attrs[1]));
447 }
448 }
449
450 typedef struct {
451 GtkWidget *dialog;
452 GtkWidget *label;
453
454 char *old_label;
455 GtkWidget *old_focus;
456
457 WBCGtk *wbcg;
458 SheetWidgetFrame *swf;
459 Sheet *sheet;
460 } FrameConfigState;
461
462 static void
cb_frame_config_destroy(FrameConfigState * state)463 cb_frame_config_destroy (FrameConfigState *state)
464 {
465 g_return_if_fail (state != NULL);
466
467 g_free (state->old_label);
468 state->old_label = NULL;
469 state->dialog = NULL;
470 g_free (state);
471 }
472
473 static void
cb_frame_config_ok_clicked(G_GNUC_UNUSED GtkWidget * button,FrameConfigState * state)474 cb_frame_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
475 {
476 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
477
478 cmd_so_set_frame_label (GNM_WBC (state->wbcg),
479 GNM_SO (state->swf),
480 g_strdup (state->old_label), g_strdup (text));
481 gtk_widget_destroy (state->dialog);
482 }
483
484 void
sheet_widget_frame_set_label(SheetObject * so,char const * str)485 sheet_widget_frame_set_label (SheetObject *so, char const* str)
486 {
487 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
488 GList *ptr;
489
490 str = str ? str : "";
491
492 if (go_str_compare (str, swf->label) == 0)
493 return;
494
495 g_free (swf->label);
496 swf->label = g_strdup (str);
497
498 for (ptr = swf->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
499 SheetObjectView *view = ptr->data;
500 GocWidget *item = get_goc_widget (view);
501 GList *children = gtk_container_get_children (GTK_CONTAINER (item->widget));
502 gtk_frame_set_label (GTK_FRAME (children->data), str);
503 g_list_free (children);
504 }
505 }
506
507 static void
cb_frame_config_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,FrameConfigState * state)508 cb_frame_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, FrameConfigState *state)
509 {
510 sheet_widget_frame_set_label (GNM_SO (state->swf), state->old_label);
511
512 gtk_widget_destroy (state->dialog);
513 }
514
515 static void
cb_frame_label_changed(GtkWidget * entry,FrameConfigState * state)516 cb_frame_label_changed (GtkWidget *entry, FrameConfigState *state)
517 {
518 gchar const *text;
519
520 text = gtk_entry_get_text(GTK_ENTRY(entry));
521 sheet_widget_frame_set_label (GNM_SO (state->swf), text);
522 }
523
524 static void
sheet_widget_frame_user_config(SheetObject * so,SheetControl * sc)525 sheet_widget_frame_user_config (SheetObject *so, SheetControl *sc)
526 {
527 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
528 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
529 FrameConfigState *state;
530 GtkBuilder *gui;
531
532 g_return_if_fail (swf != NULL);
533
534 /* Only pop up one copy per workbook */
535 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
536 return;
537
538 gui = gnm_gtk_builder_load ("res:ui/so-frame.ui", NULL, GO_CMD_CONTEXT (wbcg));
539 if (!gui)
540 return;
541 state = g_new (FrameConfigState, 1);
542 state->swf = swf;
543 state->wbcg = wbcg;
544 state->sheet = sc_sheet (sc);
545 state->old_focus = NULL;
546 state->old_label = g_strdup(swf->label);
547 state->dialog = go_gtk_builder_get_widget (gui, "so_frame");
548
549 state->label = go_gtk_builder_get_widget (gui, "entry");
550 gtk_entry_set_text (GTK_ENTRY(state->label), swf->label);
551 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
552 gnm_editable_enters (GTK_WINDOW (state->dialog),
553 GTK_WIDGET (state->label));
554
555 g_signal_connect (G_OBJECT(state->label),
556 "changed",
557 G_CALLBACK (cb_frame_label_changed), state);
558 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
559 "ok_button")),
560 "clicked",
561 G_CALLBACK (cb_frame_config_ok_clicked), state);
562 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui,
563 "cancel_button")),
564 "clicked",
565 G_CALLBACK (cb_frame_config_cancel_clicked), state);
566
567 gnm_init_help_button (
568 go_gtk_builder_get_widget (gui, "help_button"),
569 GNUMERIC_HELP_LINK_SO_FRAME);
570
571
572 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
573 SHEET_OBJECT_CONFIG_KEY);
574
575 wbc_gtk_attach_guru (state->wbcg, state->dialog);
576 g_object_set_data_full (G_OBJECT (state->dialog),
577 "state", state, (GDestroyNotify) cb_frame_config_destroy);
578 g_object_unref (gui);
579
580 gtk_widget_show (state->dialog);
581 }
582
583 static PangoFontDescription *
get_font(void)584 get_font (void)
585 {
586 // Note: Under gnumeric, we get a proper font using GtkStyleContext.
587 // Under ssconvert, we try GSettings.
588 // The 'sans 10' is just insurance
589
590 PangoFontDescription *desc;
591 PangoFontMask mask;
592 int size = 0;
593
594 if (gdk_screen_get_default ()) {
595 // Without a default screen, the following will crash
596 // with newer gtk+.
597 GtkStyleContext *style = gtk_style_context_new ();
598 GtkWidgetPath *path = gtk_widget_path_new ();
599
600 gtk_style_context_set_path (style, path);
601 gtk_widget_path_unref (path);
602
603 gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL,
604 GTK_STYLE_PROPERTY_FONT, &desc, NULL);
605 g_object_unref (style);
606 } else
607 desc = pango_font_description_new ();
608
609 mask = pango_font_description_get_set_fields (desc);
610 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
611 size = pango_font_description_get_size (desc);
612
613 if (gnm_debug_flag ("so-font")) {
614 char *s = pango_font_description_to_string (desc);
615 g_printerr ("from GtkStyleContext font=\"%s\", family set = %i,"
616 " size set = %i, size = %i\n",
617 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
618 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
619 g_free (s);
620 }
621
622 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
623 /* Trying gsettings */
624 GSettings *set = g_settings_new ("org.gnome.desktop.interface");
625 char *font_name = g_settings_get_string (set, "font-name");
626 if (font_name != NULL) {
627 pango_font_description_free (desc);
628 desc = pango_font_description_from_string (font_name);
629 g_free (font_name);
630 mask = pango_font_description_get_set_fields (desc);
631 if ((mask & PANGO_FONT_MASK_SIZE) != 0)
632 size = pango_font_description_get_size (desc);
633 else
634 size = 0;
635 if (gnm_debug_flag ("so-font")) {
636 char *s = pango_font_description_to_string (desc);
637 g_printerr ("from GSettings: font=\"%s\", family set = %i,"
638 " size set = %i, size = %i\n",
639 s, ((mask & PANGO_FONT_MASK_FAMILY) != 0),
640 ((mask & PANGO_FONT_MASK_SIZE) != 0), size);
641 g_free (s);
642 }
643 }
644 }
645
646 if ((mask & PANGO_FONT_MASK_FAMILY) == 0 || size == 0) {
647 pango_font_description_free (desc);
648 desc = pango_font_description_from_string ("sans 10");
649 if (gnm_debug_flag ("so-font"))
650 g_printerr ("Using \"sans 10\" instead.\n");
651 }
652
653 return desc;
654 }
655
656 static void
draw_cairo_text(cairo_t * cr,char const * text,int * pwidth,int * pheight,gboolean centered_v,gboolean centered_h,gboolean single,gint highlight_n,gboolean scale)657 draw_cairo_text (cairo_t *cr, char const *text, int *pwidth, int *pheight,
658 gboolean centered_v, gboolean centered_h, gboolean single, gint highlight_n, gboolean scale)
659 {
660 PangoLayout *layout = pango_cairo_create_layout (cr);
661 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
662 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
663 PangoFontDescription *desc = get_font ();
664 int width, height;
665
666 pango_context_set_font_description
667 (pango_layout_get_context (layout), desc);
668 pango_layout_set_spacing (layout, 3 * PANGO_SCALE);
669 pango_layout_set_single_paragraph_mode (layout, single);
670 pango_layout_set_text (layout, text, -1);
671 pango_layout_get_pixel_size (layout, &width, &height);
672
673 cairo_scale (cr, scale_h, scale_v);
674
675 if (scale && pwidth != NULL && pheight != NULL) {
676 double sc_x = ((double) *pwidth)/(width * scale_h);
677 double sc_y = ((double) *pheight)/(height * scale_v);
678 double sc = MIN(sc_x, sc_y);
679
680 if (sc < 1.)
681 cairo_scale (cr, sc, sc);
682 }
683
684 if (centered_v)
685 cairo_rel_move_to (cr, 0., 0.5 - ((double)height)/2.);
686 if (centered_h)
687 cairo_rel_move_to (cr, 0.5 - ((double)width)/2., 0.);
688 if (highlight_n > 0 && pheight != NULL && pwidth != NULL) {
689 PangoLayoutIter *pliter;
690 gboolean got_line = TRUE;
691 int i;
692 pliter = pango_layout_get_iter (layout);
693 for (i = 1; i < highlight_n; i++)
694 got_line = pango_layout_iter_next_line (pliter);
695
696 if (got_line) {
697 int y0, y1;
698 double dy0 = 0, dy1 = 0;
699 pango_layout_iter_get_line_yrange (pliter, &y0, &y1);
700 dy0 = y0 / (double)PANGO_SCALE;
701 dy1 = y1 / (double)PANGO_SCALE;
702
703 if (dy1 > (*pheight - 4)/scale_v)
704 cairo_translate (cr, 0, (*pheight - 4)/scale_v - dy1);
705
706 cairo_new_path (cr);
707 cairo_rectangle (cr, -4/scale_h, dy0,
708 *pwidth/scale_h, dy1 - dy0);
709 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
710 cairo_fill (cr);
711 }
712 pango_layout_iter_free (pliter);
713 cairo_set_source_rgb(cr, 0, 0, 0);
714 }
715 pango_cairo_show_layout (cr, layout);
716 pango_font_description_free (desc);
717 g_object_unref (layout);
718
719 if (pwidth)
720 *pwidth = width * scale_h;
721 if (pheight)
722 *pheight = height * scale_v;
723 }
724
725 static void
sheet_widget_frame_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)726 sheet_widget_frame_draw_cairo (SheetObject const *so, cairo_t *cr,
727 double width, double height)
728 {
729 SheetWidgetFrame *swf = GNM_SOW_FRAME (so);
730
731 int theight = 0, twidth = 0;
732 cairo_save (cr);
733 cairo_move_to (cr, 10, 0);
734
735 cairo_save (cr);
736 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
737 draw_cairo_text (cr, swf->label, &twidth, &theight, FALSE, FALSE, TRUE, 0, FALSE);
738 cairo_restore (cr);
739
740 cairo_set_line_width (cr, 1);
741 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
742 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
743 cairo_new_path (cr);
744 cairo_move_to (cr, 6, theight/2);
745 cairo_line_to (cr, 0, theight/2);
746 cairo_line_to (cr, 0, height);
747 cairo_line_to (cr, width, height);
748 cairo_line_to (cr, width, theight/2);
749 cairo_line_to (cr, 14 + twidth, theight/2);
750 cairo_stroke (cr);
751
752 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
753 cairo_new_path (cr);
754 cairo_move_to (cr, 6, theight/2 + 1);
755 cairo_line_to (cr, 1, theight/2 + 1);
756 cairo_line_to (cr, 1, height - 1);
757 cairo_line_to (cr, width - 1, height - 1);
758 cairo_line_to (cr, width - 1, theight/2 + 1);
759 cairo_line_to (cr, 14 + twidth, theight/2 + 1);
760 cairo_stroke (cr);
761
762 cairo_new_path (cr);
763 cairo_restore (cr);
764 }
765
766 SOW_MAKE_TYPE (frame, Frame,
767 sheet_widget_frame_user_config,
768 NULL,
769 NULL,
770 NULL,
771 sheet_widget_frame_copy,
772 sheet_widget_frame_write_xml_sax,
773 sheet_widget_frame_prep_sax_parser,
774 sheet_widget_frame_get_property,
775 sheet_widget_frame_set_property,
776 sheet_widget_frame_draw_cairo,
777 {
778 g_object_class_install_property
779 (object_class, SOF_PROP_TEXT,
780 g_param_spec_string ("text", NULL, NULL, NULL,
781 GSF_PARAM_STATIC | G_PARAM_READWRITE));
782 })
783
784 /****************************************************************************/
785 #define GNM_SOW_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_BUTTON_TYPE, SheetWidgetButton))
786 #define DEP_TO_BUTTON(d_ptr) (SheetWidgetButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetButton, dep))
787 typedef struct {
788 SheetObjectWidget sow;
789
790 GnmDependent dep;
791 char *label;
792 PangoAttrList *markup;
793 gboolean value;
794 } SheetWidgetButton;
795 typedef SheetObjectWidgetClass SheetWidgetButtonClass;
796
797 enum {
798 SOB_PROP_0 = 0,
799 SOB_PROP_TEXT,
800 SOB_PROP_MARKUP
801 };
802
803 static void
sheet_widget_button_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)804 sheet_widget_button_get_property (GObject *obj, guint param_id,
805 GValue *value, GParamSpec *pspec)
806 {
807 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
808
809 switch (param_id) {
810 case SOB_PROP_TEXT:
811 g_value_set_string (value, swb->label);
812 break;
813 case SOB_PROP_MARKUP:
814 g_value_set_boxed (value, NULL); /* swb->markup */
815 break;
816 default:
817 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
818 break;
819 }
820 }
821
822 static void
sheet_widget_button_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)823 sheet_widget_button_set_property (GObject *obj, guint param_id,
824 GValue const *value, GParamSpec *pspec)
825 {
826 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
827
828 switch (param_id) {
829 case SOB_PROP_TEXT:
830 sheet_widget_button_set_label (GNM_SO (swb),
831 g_value_get_string (value));
832 break;
833 case SOB_PROP_MARKUP:
834 #if 0
835 sheet_widget_button_set_markup (GNM_SO (swb),
836 g_value_peek_pointer (value));
837 #endif
838 break;
839 default:
840 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
841 return;
842 }
843 }
844
845 static void
button_eval(GnmDependent * dep)846 button_eval (GnmDependent *dep)
847 {
848 GnmValue *v;
849 GnmEvalPos pos;
850 gboolean err, result;
851
852 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
853 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
854 result = value_get_as_bool (v, &err);
855 value_release (v);
856 if (!err) {
857 SheetWidgetButton *swb = DEP_TO_BUTTON(dep);
858
859 swb->value = result;
860 }
861 }
862
863 static void
button_debug_name(GnmDependent const * dep,GString * target)864 button_debug_name (GnmDependent const *dep, GString *target)
865 {
866 g_string_append_printf (target, "Button%p", (void *)dep);
867 }
868
869 static DEPENDENT_MAKE_TYPE (button, .eval = button_eval, .debug_name = button_debug_name )
870
871 static void
sheet_widget_button_init_full(SheetWidgetButton * swb,GnmCellRef const * ref,char const * text,PangoAttrList * markup)872 sheet_widget_button_init_full (SheetWidgetButton *swb,
873 GnmCellRef const *ref,
874 char const *text,
875 PangoAttrList *markup)
876 {
877 SheetObject *so = GNM_SO (swb);
878
879 so->flags &= ~SHEET_OBJECT_PRINT;
880 swb->label = g_strdup (text);
881 swb->markup = markup;
882 swb->value = FALSE;
883 swb->dep.sheet = NULL;
884 swb->dep.flags = button_get_dep_type ();
885 swb->dep.texpr = (ref != NULL)
886 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
887 : NULL;
888 if (markup) pango_attr_list_ref (markup);
889 }
890
891 static void
sheet_widget_button_init(SheetWidgetButton * swb)892 sheet_widget_button_init (SheetWidgetButton *swb)
893 {
894 sheet_widget_button_init_full (swb, NULL, _("Button"), NULL);
895 }
896
897 static void
sheet_widget_button_finalize(GObject * obj)898 sheet_widget_button_finalize (GObject *obj)
899 {
900 SheetWidgetButton *swb = GNM_SOW_BUTTON (obj);
901
902 g_free (swb->label);
903 swb->label = NULL;
904
905 if (swb->markup) {
906 pango_attr_list_unref (swb->markup);
907 swb->markup = NULL;
908 }
909
910 dependent_set_expr (&swb->dep, NULL);
911
912 sheet_object_widget_class->finalize (obj);
913 }
914
915 static void
cb_button_pressed(GtkToggleButton * button,SheetWidgetButton * swb)916 cb_button_pressed (GtkToggleButton *button, SheetWidgetButton *swb)
917 {
918 GnmCellRef ref;
919
920 swb->value = TRUE;
921
922 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
923 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
924 _("Pressed Button"),
925 &ref, value_new_bool (TRUE),
926 sheet_object_get_sheet (GNM_SO (swb)));
927 }
928 }
929
930 static void
cb_button_released(GtkToggleButton * button,SheetWidgetButton * swb)931 cb_button_released (GtkToggleButton *button, SheetWidgetButton *swb)
932 {
933 GnmCellRef ref;
934
935 swb->value = FALSE;
936
937 if (so_get_ref (GNM_SO (swb), &ref, TRUE) != NULL) {
938 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
939 _("Released Button"),
940 &ref, value_new_bool (FALSE),
941 sheet_object_get_sheet (GNM_SO (swb)));
942 }
943 }
944
945 static GtkWidget *
sheet_widget_button_create_widget(SheetObjectWidget * sow)946 sheet_widget_button_create_widget (SheetObjectWidget *sow)
947 {
948 SheetWidgetButton *swb = GNM_SOW_BUTTON (sow);
949 GtkWidget *w = gtk_button_new_with_label (swb->label);
950 gtk_widget_set_can_focus (w, FALSE);
951 gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))),
952 swb->markup);
953 g_signal_connect (G_OBJECT (w),
954 "pressed",
955 G_CALLBACK (cb_button_pressed), swb);
956 g_signal_connect (G_OBJECT (w),
957 "released",
958 G_CALLBACK (cb_button_released), swb);
959 return w;
960 }
961
962 static void
sheet_widget_button_copy(SheetObject * dst,SheetObject const * src)963 sheet_widget_button_copy (SheetObject *dst, SheetObject const *src)
964 {
965 SheetWidgetButton const *src_swb = GNM_SOW_BUTTON (src);
966 SheetWidgetButton *dst_swb = GNM_SOW_BUTTON (dst);
967 GnmCellRef ref;
968 sheet_widget_button_init_full (dst_swb,
969 so_get_ref (src, &ref, FALSE),
970 src_swb->label,
971 src_swb->markup);
972 dst_swb->value = src_swb->value;
973 }
974
975 typedef struct {
976 GtkWidget *dialog;
977 GnmExprEntry *expression;
978 GtkWidget *label;
979
980 char *old_label;
981 GtkWidget *old_focus;
982
983 WBCGtk *wbcg;
984 SheetWidgetButton *swb;
985 Sheet *sheet;
986 } ButtonConfigState;
987
988 static void
cb_button_set_focus(G_GNUC_UNUSED GtkWidget * window,GtkWidget * focus_widget,ButtonConfigState * state)989 cb_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
990 ButtonConfigState *state)
991 {
992 /* Note: half of the set-focus action is handle by the default
993 * callback installed by wbc_gtk_attach_guru */
994
995 /* Force an update of the content in case it needs tweaking (eg make it
996 * absolute) */
997 if (state->old_focus != NULL &&
998 GNM_EXPR_ENTRY_IS (gtk_widget_get_parent (state->old_focus))) {
999 GnmParsePos pp;
1000 GnmExprTop const *texpr = gnm_expr_entry_parse
1001 (GNM_EXPR_ENTRY (gtk_widget_get_parent (state->old_focus)),
1002 parse_pos_init_sheet (&pp, state->sheet),
1003 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1004 if (texpr != NULL)
1005 gnm_expr_top_unref (texpr);
1006 }
1007 state->old_focus = focus_widget;
1008 }
1009
1010 static void
cb_button_config_destroy(ButtonConfigState * state)1011 cb_button_config_destroy (ButtonConfigState *state)
1012 {
1013 g_return_if_fail (state != NULL);
1014
1015 g_free (state->old_label);
1016 state->old_label = NULL;
1017 state->dialog = NULL;
1018 g_free (state);
1019 }
1020
1021 static void
cb_button_config_ok_clicked(G_GNUC_UNUSED GtkWidget * button,ButtonConfigState * state)1022 cb_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1023 {
1024 SheetObject *so = GNM_SO (state->swb);
1025 GnmParsePos pp;
1026 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1027 parse_pos_init_sheet (&pp, so->sheet),
1028 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1029 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
1030
1031 cmd_so_set_button (GNM_WBC (state->wbcg), so,
1032 texpr, g_strdup (state->old_label), g_strdup (text));
1033
1034 gtk_widget_destroy (state->dialog);
1035 }
1036
1037 static void
cb_button_config_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,ButtonConfigState * state)1038 cb_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, ButtonConfigState *state)
1039 {
1040 sheet_widget_button_set_label (GNM_SO (state->swb),
1041 state->old_label);
1042 gtk_widget_destroy (state->dialog);
1043 }
1044
1045 static void
cb_button_label_changed(GtkEntry * entry,ButtonConfigState * state)1046 cb_button_label_changed (GtkEntry *entry, ButtonConfigState *state)
1047 {
1048 sheet_widget_button_set_label (GNM_SO (state->swb),
1049 gtk_entry_get_text (entry));
1050 }
1051
1052 static void
sheet_widget_button_user_config(SheetObject * so,SheetControl * sc)1053 sheet_widget_button_user_config (SheetObject *so, SheetControl *sc)
1054 {
1055 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1056 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1057 ButtonConfigState *state;
1058 GtkWidget *grid;
1059 GtkBuilder *gui;
1060
1061 g_return_if_fail (swb != NULL);
1062
1063 /* Only pop up one copy per workbook */
1064 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1065 return;
1066
1067 gui = gnm_gtk_builder_load ("res:ui/so-button.ui", NULL, GO_CMD_CONTEXT (wbcg));
1068 if (!gui)
1069 return;
1070 state = g_new (ButtonConfigState, 1);
1071 state->swb = swb;
1072 state->wbcg = wbcg;
1073 state->sheet = sc_sheet (sc);
1074 state->old_focus = NULL;
1075 state->old_label = g_strdup (swb->label);
1076 state->dialog = go_gtk_builder_get_widget (gui, "SO-Button");
1077
1078 grid = go_gtk_builder_get_widget (gui, "main-grid");
1079
1080 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1081 gnm_expr_entry_set_flags (state->expression,
1082 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1083 GNM_EE_MASK);
1084 gnm_expr_entry_load_from_dep (state->expression, &swb->dep);
1085 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1086 GTK_WIDGET (state->expression));
1087 gtk_grid_attach (GTK_GRID (grid),
1088 GTK_WIDGET (state->expression), 1, 0, 1, 1);
1089 gtk_widget_show (GTK_WIDGET (state->expression));
1090
1091 state->label = go_gtk_builder_get_widget (gui, "label_entry");
1092 gtk_entry_set_text (GTK_ENTRY (state->label), swb->label);
1093 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
1094 gnm_editable_enters (GTK_WINDOW (state->dialog),
1095 GTK_WIDGET (state->expression));
1096 gnm_editable_enters (GTK_WINDOW (state->dialog),
1097 GTK_WIDGET (state->label));
1098
1099 g_signal_connect (G_OBJECT (state->label),
1100 "changed",
1101 G_CALLBACK (cb_button_label_changed), state);
1102 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1103 "clicked",
1104 G_CALLBACK (cb_button_config_ok_clicked), state);
1105 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1106 "clicked",
1107 G_CALLBACK (cb_button_config_cancel_clicked), state);
1108
1109 gnm_init_help_button (
1110 go_gtk_builder_get_widget (gui, "help_button"),
1111 GNUMERIC_HELP_LINK_SO_BUTTON);
1112
1113 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1114 SHEET_OBJECT_CONFIG_KEY);
1115
1116 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1117 g_object_set_data_full (G_OBJECT (state->dialog),
1118 "state", state, (GDestroyNotify) cb_button_config_destroy);
1119
1120 /* Note: half of the set-focus action is handle by the default */
1121 /* callback installed by wbc_gtk_attach_guru */
1122 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1123 G_CALLBACK (cb_button_set_focus), state);
1124 g_object_unref (gui);
1125
1126 gtk_widget_show (state->dialog);
1127 }
1128
1129 static gboolean
sheet_widget_button_set_sheet(SheetObject * so,Sheet * sheet)1130 sheet_widget_button_set_sheet (SheetObject *so, Sheet *sheet)
1131 {
1132 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1133
1134 dependent_set_sheet (&swb->dep, sheet);
1135
1136 return FALSE;
1137 }
1138
1139 static void
sheet_widget_button_foreach_dep(SheetObject * so,SheetObjectForeachDepFunc func,gpointer user)1140 sheet_widget_button_foreach_dep (SheetObject *so,
1141 SheetObjectForeachDepFunc func,
1142 gpointer user)
1143 {
1144 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1145 func (&swb->dep, so, user);
1146 }
1147
1148 static void
sheet_widget_button_write_xml_sax(SheetObject const * so,GsfXMLOut * output,GnmConventions const * convs)1149 sheet_widget_button_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1150 GnmConventions const *convs)
1151 {
1152 /* FIXME: markup */
1153 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1154 gsf_xml_out_add_cstr (output, "Label", swb->label);
1155 gsf_xml_out_add_int (output, "Value", swb->value);
1156 sax_write_dep (output, &swb->dep, "Input", convs);
1157 }
1158
1159 static void
sheet_widget_button_prep_sax_parser(SheetObject * so,GsfXMLIn * xin,xmlChar const ** attrs,GnmConventions const * convs)1160 sheet_widget_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1161 xmlChar const **attrs,
1162 GnmConventions const *convs)
1163 {
1164 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1165 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1166 if (attr_eq (attrs[0], "Label"))
1167 g_object_set (G_OBJECT (swb), "text", attrs[1], NULL);
1168 else if (gnm_xml_attr_int (attrs, "Value", &swb->value))
1169 ;
1170 else if (sax_read_dep (attrs, "Input", &swb->dep, xin, convs))
1171 ;
1172 }
1173
1174 void
sheet_widget_button_set_link(SheetObject * so,GnmExprTop const * texpr)1175 sheet_widget_button_set_link (SheetObject *so, GnmExprTop const *texpr)
1176 {
1177 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1178 dependent_set_expr (&swb->dep, texpr);
1179 if (texpr && swb->dep.sheet)
1180 dependent_link (&swb->dep);
1181 }
1182
1183 GnmExprTop const *
sheet_widget_button_get_link(SheetObject * so)1184 sheet_widget_button_get_link (SheetObject *so)
1185 {
1186 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1187 GnmExprTop const *texpr = swb->dep.texpr;
1188
1189 if (texpr)
1190 gnm_expr_top_ref (texpr);
1191
1192 return texpr;
1193 }
1194
1195
1196 void
sheet_widget_button_set_label(SheetObject * so,char const * str)1197 sheet_widget_button_set_label (SheetObject *so, char const *str)
1198 {
1199 GList *ptr;
1200 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1201 char *new_label;
1202
1203 if (go_str_compare (str, swb->label) == 0)
1204 return;
1205
1206 new_label = g_strdup (str);
1207 g_free (swb->label);
1208 swb->label = new_label;
1209
1210 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1211 SheetObjectView *view = ptr->data;
1212 GocWidget *item = get_goc_widget (view);
1213 gtk_button_set_label (GTK_BUTTON (item->widget), swb->label);
1214 }
1215 }
1216
1217 void
sheet_widget_button_set_markup(SheetObject * so,PangoAttrList * markup)1218 sheet_widget_button_set_markup (SheetObject *so, PangoAttrList *markup)
1219 {
1220 GList *ptr;
1221 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1222
1223 if (markup == swb->markup)
1224 return;
1225
1226 if (swb->markup) pango_attr_list_unref (swb->markup);
1227 swb->markup = markup;
1228 if (markup) pango_attr_list_ref (markup);
1229
1230 for (ptr = swb->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1231 SheetObjectView *view = ptr->data;
1232 GocWidget *item = get_goc_widget (view);
1233 GtkLabel *lab =
1234 GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->widget)));
1235 gtk_label_set_attributes (lab, swb->markup);
1236 }
1237 }
1238
1239 static void
sheet_widget_button_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)1240 sheet_widget_button_draw_cairo (SheetObject const *so, cairo_t *cr,
1241 double width, double height)
1242 {
1243 SheetWidgetButton *swb = GNM_SOW_BUTTON (so);
1244 int twidth, theight;
1245 double half_line;
1246 double radius = 10;
1247
1248 if (height < 3 * radius)
1249 radius = height / 3.;
1250 if (width < 3 * radius)
1251 radius = width / 3.;
1252 if (radius < 1)
1253 radius = 1;
1254 half_line = radius * 0.15;
1255
1256 cairo_save (cr);
1257 cairo_set_line_width (cr, 2 * half_line);
1258 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1259
1260 cairo_new_path (cr);
1261 cairo_arc (cr, radius + half_line, radius + half_line, radius, M_PI, - M_PI/2);
1262 cairo_arc (cr, width - (radius + half_line), radius + half_line,
1263 radius, - M_PI/2, 0);
1264 cairo_arc (cr, width - (radius + half_line), height - (radius + half_line),
1265 radius, 0, M_PI/2);
1266 cairo_arc (cr, (radius + half_line), height - (radius + half_line),
1267 radius, M_PI/2, M_PI);
1268 cairo_close_path (cr);
1269 cairo_stroke (cr);
1270
1271 cairo_set_source_rgb(cr, 0, 0, 0);
1272
1273 cairo_move_to (cr, width/2., height/2.);
1274
1275 twidth = 0.8 * width;
1276 theight = 0.8 * height;
1277 draw_cairo_text (cr, swb->label, &twidth, &theight, TRUE, TRUE, TRUE, 0, TRUE);
1278
1279 cairo_new_path (cr);
1280 cairo_restore (cr);
1281 }
1282
1283 SOW_MAKE_TYPE (button, Button,
1284 sheet_widget_button_user_config,
1285 sheet_widget_button_set_sheet,
1286 so_clear_sheet,
1287 sheet_widget_button_foreach_dep,
1288 sheet_widget_button_copy,
1289 sheet_widget_button_write_xml_sax,
1290 sheet_widget_button_prep_sax_parser,
1291 sheet_widget_button_get_property,
1292 sheet_widget_button_set_property,
1293 sheet_widget_button_draw_cairo,
1294 {
1295 g_object_class_install_property
1296 (object_class, SOB_PROP_TEXT,
1297 g_param_spec_string ("text", NULL, NULL, NULL,
1298 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1299 g_object_class_install_property
1300 (object_class, SOB_PROP_MARKUP,
1301 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
1302 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1303 })
1304
1305 /****************************************************************************/
1306
1307 #define DEP_TO_ADJUSTMENT(d_ptr) (SheetWidgetAdjustment *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetAdjustment, dep))
1308 #define GNM_SOW_ADJUSTMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SOW_ADJUSTMENT_TYPE, SheetWidgetAdjustmentClass))
1309 #define SWA_CLASS(so) (GNM_SOW_ADJUSTMENT_CLASS (G_OBJECT_GET_CLASS(so)))
1310
1311 typedef struct {
1312 SheetObjectWidget sow;
1313
1314 gboolean being_updated;
1315 GnmDependent dep;
1316 GtkAdjustment *adjustment;
1317
1318 gboolean horizontal;
1319 } SheetWidgetAdjustment;
1320
1321 typedef struct {
1322 SheetObjectWidgetClass parent_class;
1323 GType type;
1324 gboolean has_orientation;
1325 } SheetWidgetAdjustmentClass;
1326
1327 enum {
1328 SWA_PROP_0 = 0,
1329 SWA_PROP_HORIZONTAL
1330 };
1331
1332 #ifndef g_signal_handlers_disconnect_by_data
1333 #define g_signal_handlers_disconnect_by_data(instance, data) \
1334 g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (data))
1335 #endif
1336 static void
cb_range_destroyed(GtkWidget * w,SheetWidgetAdjustment * swa)1337 cb_range_destroyed (GtkWidget *w, SheetWidgetAdjustment *swa)
1338 {
1339 GObject *accessible = G_OBJECT (gtk_widget_get_accessible (w));
1340 if (accessible)
1341 g_signal_handlers_disconnect_by_data (swa->adjustment, accessible);
1342 }
1343
1344 static void
sheet_widget_adjustment_set_value(SheetWidgetAdjustment * swa,double new_val)1345 sheet_widget_adjustment_set_value (SheetWidgetAdjustment *swa, double new_val)
1346 {
1347 if (swa->being_updated)
1348 return;
1349 swa->being_updated = TRUE;
1350 gtk_adjustment_set_value (swa->adjustment, new_val);
1351 swa->being_updated = FALSE;
1352 }
1353
1354 /**
1355 * sheet_widget_adjustment_get_adjustment:
1356 * @so: #SheetObject
1357 *
1358 * Returns: (transfer none): the associated #GtkAdjustment.
1359 **/
1360 GtkAdjustment *
sheet_widget_adjustment_get_adjustment(SheetObject * so)1361 sheet_widget_adjustment_get_adjustment (SheetObject *so)
1362 {
1363 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), NULL);
1364 return (GNM_SOW_ADJUSTMENT (so)->adjustment);
1365 }
1366
1367 gboolean
sheet_widget_adjustment_get_horizontal(SheetObject * so)1368 sheet_widget_adjustment_get_horizontal (SheetObject *so)
1369 {
1370 g_return_val_if_fail (GNM_IS_SOW_ADJUSTMENT (so), TRUE);
1371 return (GNM_SOW_ADJUSTMENT (so)->horizontal);
1372 }
1373
1374 void
sheet_widget_adjustment_set_link(SheetObject * so,GnmExprTop const * texpr)1375 sheet_widget_adjustment_set_link (SheetObject *so, GnmExprTop const *texpr)
1376 {
1377 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1378 dependent_set_expr (&swa->dep, texpr);
1379 if (texpr && swa->dep.sheet)
1380 dependent_link (&swa->dep);
1381 }
1382
1383 GnmExprTop const *
sheet_widget_adjustment_get_link(SheetObject * so)1384 sheet_widget_adjustment_get_link (SheetObject *so)
1385 {
1386 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1387 GnmExprTop const *texpr = swa->dep.texpr;
1388
1389 if (texpr)
1390 gnm_expr_top_ref (texpr);
1391
1392 return texpr;
1393 }
1394
1395
1396 static void
adjustment_eval(GnmDependent * dep)1397 adjustment_eval (GnmDependent *dep)
1398 {
1399 GnmValue *v;
1400 GnmEvalPos pos;
1401
1402 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
1403 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
1404 sheet_widget_adjustment_set_value (DEP_TO_ADJUSTMENT(dep),
1405 value_get_as_float (v));
1406 value_release (v);
1407 }
1408
1409 static void
adjustment_debug_name(GnmDependent const * dep,GString * target)1410 adjustment_debug_name (GnmDependent const *dep, GString *target)
1411 {
1412 g_string_append_printf (target, "Adjustment%p", (void *)dep);
1413 }
1414
1415 static DEPENDENT_MAKE_TYPE (adjustment, .eval = adjustment_eval, .debug_name = adjustment_debug_name )
1416
1417 static void
cb_adjustment_widget_value_changed(GtkWidget * widget,SheetWidgetAdjustment * swa)1418 cb_adjustment_widget_value_changed (GtkWidget *widget,
1419 SheetWidgetAdjustment *swa)
1420 {
1421 GnmCellRef ref;
1422
1423 if (swa->being_updated)
1424 return;
1425
1426 if (so_get_ref (GNM_SO (swa), &ref, TRUE) != NULL) {
1427 GnmCell *cell = sheet_cell_fetch (ref.sheet, ref.col, ref.row);
1428 /* TODO : add more control for precision, XL is stupid */
1429 int new_val = gnm_fake_round (gtk_adjustment_get_value (swa->adjustment));
1430 if (cell->value != NULL &&
1431 VALUE_IS_FLOAT (cell->value) &&
1432 value_get_as_float (cell->value) == new_val)
1433 return;
1434
1435 swa->being_updated = TRUE;
1436 cmd_so_set_value (widget_wbc (widget),
1437 /* FIXME: This text sucks: */
1438 _("Change widget"),
1439 &ref, value_new_int (new_val),
1440 sheet_object_get_sheet (GNM_SO (swa)));
1441 swa->being_updated = FALSE;
1442 }
1443 }
1444
1445 void
sheet_widget_adjustment_set_horizontal(SheetObject * so,gboolean horizontal)1446 sheet_widget_adjustment_set_horizontal (SheetObject *so,
1447 gboolean horizontal)
1448 {
1449 SheetWidgetAdjustment *swa = (SheetWidgetAdjustment *)so;
1450 GList *ptr;
1451 GtkOrientation o;
1452
1453 if (!SWA_CLASS (swa)->has_orientation)
1454 return;
1455 horizontal = !!horizontal;
1456 if (horizontal == swa->horizontal)
1457 return;
1458 swa->horizontal = horizontal;
1459 o = horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1460
1461 /* Change direction for all realized widgets. */
1462 for (ptr = swa->sow.so.realized_list; ptr != NULL; ptr = ptr->next) {
1463 SheetObjectView *view = ptr->data;
1464 GocWidget *item = get_goc_widget (view);
1465 gtk_orientable_set_orientation (GTK_ORIENTABLE (item->widget), o);
1466 }
1467 }
1468
1469
1470 static void
sheet_widget_adjustment_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)1471 sheet_widget_adjustment_get_property (GObject *obj, guint param_id,
1472 GValue *value, GParamSpec *pspec)
1473 {
1474 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1475
1476 switch (param_id) {
1477 case SWA_PROP_HORIZONTAL:
1478 g_value_set_boolean (value, swa->horizontal);
1479 break;
1480 default:
1481 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1482 break;
1483 }
1484 }
1485
1486 static void
sheet_widget_adjustment_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)1487 sheet_widget_adjustment_set_property (GObject *obj, guint param_id,
1488 GValue const *value, GParamSpec *pspec)
1489 {
1490 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1491
1492 switch (param_id) {
1493 case SWA_PROP_HORIZONTAL:
1494 sheet_widget_adjustment_set_horizontal (GNM_SO (swa), g_value_get_boolean (value));
1495 break;
1496 default:
1497 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1498 return;
1499 }
1500 }
1501
1502 static void
sheet_widget_adjustment_init_full(SheetWidgetAdjustment * swa,GnmCellRef const * ref,gboolean horizontal)1503 sheet_widget_adjustment_init_full (SheetWidgetAdjustment *swa,
1504 GnmCellRef const *ref,
1505 gboolean horizontal)
1506 {
1507 SheetObject *so;
1508 g_return_if_fail (swa != NULL);
1509
1510 so = GNM_SO (swa);
1511 so->flags &= ~SHEET_OBJECT_PRINT;
1512
1513 swa->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0., 0., 100., 1., 10., 0.));
1514 g_object_ref_sink (swa->adjustment);
1515
1516 swa->horizontal = horizontal;
1517 swa->being_updated = FALSE;
1518 swa->dep.sheet = NULL;
1519 swa->dep.flags = adjustment_get_dep_type ();
1520 swa->dep.texpr = (ref != NULL)
1521 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
1522 : NULL;
1523 }
1524
1525 static void
sheet_widget_adjustment_init(SheetWidgetAdjustment * swa)1526 sheet_widget_adjustment_init (SheetWidgetAdjustment *swa)
1527 {
1528 sheet_widget_adjustment_init_full (swa, NULL, FALSE);
1529 }
1530
1531 static void
sheet_widget_adjustment_finalize(GObject * obj)1532 sheet_widget_adjustment_finalize (GObject *obj)
1533 {
1534 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (obj);
1535
1536 g_return_if_fail (swa != NULL);
1537
1538 dependent_set_expr (&swa->dep, NULL);
1539 if (swa->adjustment != NULL) {
1540 g_object_unref (swa->adjustment);
1541 swa->adjustment = NULL;
1542 }
1543
1544 sheet_object_widget_class->finalize (obj);
1545 }
1546
1547 static void
sheet_widget_adjustment_copy(SheetObject * dst,SheetObject const * src)1548 sheet_widget_adjustment_copy (SheetObject *dst, SheetObject const *src)
1549 {
1550 SheetWidgetAdjustment const *src_swa = GNM_SOW_ADJUSTMENT (src);
1551 SheetWidgetAdjustment *dst_swa = GNM_SOW_ADJUSTMENT (dst);
1552 GtkAdjustment *dst_adjust, *src_adjust;
1553 GnmCellRef ref;
1554
1555 sheet_widget_adjustment_init_full (dst_swa,
1556 so_get_ref (src, &ref, FALSE),
1557 src_swa->horizontal);
1558 dst_adjust = dst_swa->adjustment;
1559 src_adjust = src_swa->adjustment;
1560
1561 gtk_adjustment_configure
1562 (dst_adjust,
1563 gtk_adjustment_get_value (src_adjust),
1564 gtk_adjustment_get_lower (src_adjust),
1565 gtk_adjustment_get_upper (src_adjust),
1566 gtk_adjustment_get_step_increment (src_adjust),
1567 gtk_adjustment_get_page_increment (src_adjust),
1568 gtk_adjustment_get_page_size (src_adjust));
1569 }
1570
1571 typedef struct {
1572 GtkWidget *dialog;
1573 GnmExprEntry *expression;
1574 GtkWidget *min;
1575 GtkWidget *max;
1576 GtkWidget *inc;
1577 GtkWidget *page;
1578 GtkWidget *direction_h;
1579 GtkWidget *direction_v;
1580
1581 char *undo_label;
1582 GtkWidget *old_focus;
1583
1584 WBCGtk *wbcg;
1585 SheetWidgetAdjustment *swa;
1586 Sheet *sheet;
1587 } AdjustmentConfigState;
1588
1589 static void
cb_adjustment_set_focus(G_GNUC_UNUSED GtkWidget * window,GtkWidget * focus_widget,AdjustmentConfigState * state)1590 cb_adjustment_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
1591 AdjustmentConfigState *state)
1592 {
1593 GtkWidget *ofp;
1594
1595 /* Note: half of the set-focus action is handle by the default
1596 * callback installed by wbc_gtk_attach_guru. */
1597
1598 ofp = state->old_focus
1599 ? gtk_widget_get_parent (state->old_focus)
1600 : NULL;
1601 /* Force an update of the content in case it needs tweaking (eg make it
1602 * absolute) */
1603 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
1604 GnmParsePos pp;
1605 GnmExprTop const *texpr = gnm_expr_entry_parse (
1606 GNM_EXPR_ENTRY (ofp),
1607 parse_pos_init_sheet (&pp, state->sheet),
1608 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1609 if (texpr != NULL)
1610 gnm_expr_top_unref (texpr);
1611 }
1612 state->old_focus = focus_widget;
1613 }
1614
1615 static void
cb_adjustment_config_destroy(AdjustmentConfigState * state)1616 cb_adjustment_config_destroy (AdjustmentConfigState *state)
1617 {
1618 g_return_if_fail (state != NULL);
1619
1620 g_free (state->undo_label);
1621
1622 state->dialog = NULL;
1623 g_free (state);
1624 }
1625
1626 static void
cb_adjustment_config_ok_clicked(G_GNUC_UNUSED GtkWidget * button,AdjustmentConfigState * state)1627 cb_adjustment_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1628 {
1629 SheetObject *so = GNM_SO (state->swa);
1630 GnmParsePos pp;
1631 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
1632 parse_pos_init_sheet (&pp, so->sheet),
1633 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
1634 gboolean horizontal;
1635
1636 horizontal = state->direction_h
1637 ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state->direction_h))
1638 : state->swa->horizontal;
1639
1640 cmd_so_set_adjustment (GNM_WBC (state->wbcg), so,
1641 texpr,
1642 horizontal,
1643 gtk_spin_button_get_value_as_int (
1644 GTK_SPIN_BUTTON (state->min)),
1645 gtk_spin_button_get_value_as_int (
1646 GTK_SPIN_BUTTON (state->max)),
1647 gtk_spin_button_get_value_as_int (
1648 GTK_SPIN_BUTTON (state->inc)),
1649 gtk_spin_button_get_value_as_int (
1650 GTK_SPIN_BUTTON (state->page)),
1651 state->undo_label);
1652
1653 gtk_widget_destroy (state->dialog);
1654 }
1655
1656 static void
cb_adjustment_config_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,AdjustmentConfigState * state)1657 cb_adjustment_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, AdjustmentConfigState *state)
1658 {
1659 gtk_widget_destroy (state->dialog);
1660 }
1661
1662 static void
sheet_widget_adjustment_user_config_impl(SheetObject * so,SheetControl * sc,char const * undo_label,char const * dialog_label)1663 sheet_widget_adjustment_user_config_impl (SheetObject *so, SheetControl *sc, char const *undo_label, char const *dialog_label)
1664 {
1665 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1666 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (swa);
1667 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
1668 AdjustmentConfigState *state;
1669 GtkWidget *grid;
1670 GtkBuilder *gui;
1671 gboolean has_directions = swa_class->has_orientation;
1672
1673 /* Only pop up one copy per workbook */
1674 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
1675 return;
1676
1677 gui = gnm_gtk_builder_load ("res:ui/so-scrollbar.ui", NULL, GO_CMD_CONTEXT (wbcg));
1678 if (!gui)
1679 return;
1680 state = g_new (AdjustmentConfigState, 1);
1681 state->swa = swa;
1682 state->wbcg = wbcg;
1683 state->sheet = sc_sheet (sc);
1684 state->old_focus = NULL;
1685 state->undo_label = (undo_label == NULL) ? NULL : g_strdup (undo_label);
1686 state->dialog = go_gtk_builder_get_widget (gui, "SO-Scrollbar");
1687
1688 if (dialog_label != NULL)
1689 gtk_window_set_title (GTK_WINDOW (state->dialog), dialog_label);
1690
1691 grid = go_gtk_builder_get_widget (gui, "main-grid");
1692
1693 state->expression = gnm_expr_entry_new (wbcg, TRUE);
1694 gnm_expr_entry_set_flags (state->expression,
1695 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
1696 GNM_EE_MASK);
1697 gnm_expr_entry_load_from_dep (state->expression, &swa->dep);
1698 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
1699 GTK_WIDGET (state->expression));
1700 gtk_grid_attach (GTK_GRID (grid),
1701 GTK_WIDGET (state->expression), 1, 0, 2, 1);
1702 gtk_widget_show (GTK_WIDGET (state->expression));
1703
1704 if (has_directions) {
1705 state->direction_h = go_gtk_builder_get_widget (gui, "direction_h");
1706 state->direction_v = go_gtk_builder_get_widget (gui, "direction_v");
1707 gtk_toggle_button_set_active
1708 (GTK_TOGGLE_BUTTON (swa->horizontal
1709 ? state->direction_h
1710 : state->direction_v),
1711 TRUE);
1712 } else {
1713 state->direction_h = NULL;
1714 state->direction_v = NULL;
1715 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_label"));
1716 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_h"));
1717 gtk_widget_destroy (go_gtk_builder_get_widget (gui, "direction_v"));
1718 }
1719
1720 /* TODO : This is silly, no need to be similar to XL here. */
1721 state->min = go_gtk_builder_get_widget (gui, "spin_min");
1722 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->min),
1723 gtk_adjustment_get_lower (swa->adjustment));
1724 state->max = go_gtk_builder_get_widget (gui, "spin_max");
1725 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max),
1726 gtk_adjustment_get_upper (swa->adjustment));
1727 state->inc = go_gtk_builder_get_widget (gui, "spin_increment");
1728 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->inc),
1729 gtk_adjustment_get_step_increment (swa->adjustment));
1730 state->page = go_gtk_builder_get_widget (gui, "spin_page");
1731 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->page),
1732 gtk_adjustment_get_page_increment (swa->adjustment));
1733
1734 gnm_editable_enters (GTK_WINDOW (state->dialog),
1735 GTK_WIDGET (state->expression));
1736 gnm_editable_enters (GTK_WINDOW (state->dialog),
1737 GTK_WIDGET (state->min));
1738 gnm_editable_enters (GTK_WINDOW (state->dialog),
1739 GTK_WIDGET (state->max));
1740 gnm_editable_enters (GTK_WINDOW (state->dialog),
1741 GTK_WIDGET (state->inc));
1742 gnm_editable_enters (GTK_WINDOW (state->dialog),
1743 GTK_WIDGET (state->page));
1744 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
1745 "clicked",
1746 G_CALLBACK (cb_adjustment_config_ok_clicked), state);
1747 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
1748 "clicked",
1749 G_CALLBACK (cb_adjustment_config_cancel_clicked), state);
1750
1751 gnm_init_help_button (
1752 go_gtk_builder_get_widget (gui, "help_button"),
1753 GNUMERIC_HELP_LINK_SO_ADJUSTMENT);
1754
1755 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1756 SHEET_OBJECT_CONFIG_KEY);
1757
1758 wbc_gtk_attach_guru (state->wbcg, state->dialog);
1759 g_object_set_data_full (G_OBJECT (state->dialog),
1760 "state", state, (GDestroyNotify) cb_adjustment_config_destroy);
1761
1762 /* Note: half of the set-focus action is handle by the default */
1763 /* callback installed by wbc_gtk_attach_guru */
1764 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
1765 G_CALLBACK (cb_adjustment_set_focus), state);
1766 g_object_unref (gui);
1767
1768 gtk_widget_show (state->dialog);
1769 }
1770
1771 static void
sheet_widget_adjustment_user_config(SheetObject * so,SheetControl * sc)1772 sheet_widget_adjustment_user_config (SheetObject *so, SheetControl *sc)
1773 {
1774 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Adjustment"),
1775 N_("Adjustment Properties"));
1776 }
1777
1778 static gboolean
sheet_widget_adjustment_set_sheet(SheetObject * so,Sheet * sheet)1779 sheet_widget_adjustment_set_sheet (SheetObject *so, Sheet *sheet)
1780 {
1781 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1782
1783 dependent_set_sheet (&swa->dep, sheet);
1784
1785 return FALSE;
1786 }
1787
1788 static void
sheet_widget_adjustment_foreach_dep(SheetObject * so,SheetObjectForeachDepFunc func,gpointer user)1789 sheet_widget_adjustment_foreach_dep (SheetObject *so,
1790 SheetObjectForeachDepFunc func,
1791 gpointer user)
1792 {
1793 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1794 func (&swa->dep, so, user);
1795 }
1796
1797 static void
sheet_widget_adjustment_write_xml_sax(SheetObject const * so,GsfXMLOut * output,GnmConventions const * convs)1798 sheet_widget_adjustment_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
1799 GnmConventions const *convs)
1800 {
1801 SheetWidgetAdjustment const *swa = GNM_SOW_ADJUSTMENT (so);
1802 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1803
1804 go_xml_out_add_double (output, "Min", gtk_adjustment_get_lower (swa->adjustment));
1805 go_xml_out_add_double (output, "Max", gtk_adjustment_get_upper (swa->adjustment));
1806 go_xml_out_add_double (output, "Inc", gtk_adjustment_get_step_increment (swa->adjustment));
1807 go_xml_out_add_double (output, "Page", gtk_adjustment_get_page_increment (swa->adjustment));
1808 go_xml_out_add_double (output, "Value", gtk_adjustment_get_value (swa->adjustment));
1809
1810 if (swa_class->has_orientation)
1811 gsf_xml_out_add_bool (output, "Horizontal", swa->horizontal);
1812
1813 sax_write_dep (output, &swa->dep, "Input", convs);
1814 }
1815
1816 static void
sheet_widget_adjustment_prep_sax_parser(SheetObject * so,GsfXMLIn * xin,xmlChar const ** attrs,GnmConventions const * convs)1817 sheet_widget_adjustment_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
1818 xmlChar const **attrs,
1819 GnmConventions const *convs)
1820 {
1821 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1822 SheetWidgetAdjustmentClass *swa_class = SWA_CLASS (so);
1823 swa->horizontal = FALSE;
1824
1825 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1826 double tmp;
1827 gboolean b;
1828
1829 if (gnm_xml_attr_double (attrs, "Min", &tmp))
1830 gtk_adjustment_set_lower (swa->adjustment, tmp);
1831 else if (gnm_xml_attr_double (attrs, "Max", &tmp))
1832 gtk_adjustment_set_upper (swa->adjustment, tmp); /* allow scrolling to max */
1833 else if (gnm_xml_attr_double (attrs, "Inc", &tmp))
1834 gtk_adjustment_set_step_increment (swa->adjustment, tmp);
1835 else if (gnm_xml_attr_double (attrs, "Page", &tmp))
1836 gtk_adjustment_set_page_increment (swa->adjustment, tmp);
1837 else if (gnm_xml_attr_double (attrs, "Value", &tmp))
1838 gtk_adjustment_set_value (swa->adjustment, tmp);
1839 else if (sax_read_dep (attrs, "Input", &swa->dep, xin, convs))
1840 ;
1841 else if (swa_class->has_orientation &&
1842 gnm_xml_attr_bool (attrs, "Horizontal", &b))
1843 swa->horizontal = b;
1844 }
1845
1846 swa->dep.flags = adjustment_get_dep_type ();
1847 }
1848
1849 void
sheet_widget_adjustment_set_details(SheetObject * so,GnmExprTop const * tlink,int value,int min,int max,int inc,int page)1850 sheet_widget_adjustment_set_details (SheetObject *so, GnmExprTop const *tlink,
1851 int value, int min, int max,
1852 int inc, int page)
1853 {
1854 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1855 double page_size;
1856
1857 g_return_if_fail (swa != NULL);
1858
1859 dependent_set_expr (&swa->dep, tlink);
1860 if (tlink && swa->dep.sheet)
1861 dependent_link (&swa->dep);
1862
1863 page_size = gtk_adjustment_get_page_size (swa->adjustment); /* ??? */
1864 gtk_adjustment_configure (swa->adjustment,
1865 value, min, max, inc, page, page_size);
1866 }
1867
1868 static GtkWidget *
sheet_widget_adjustment_create_widget(G_GNUC_UNUSED SheetObjectWidget * sow)1869 sheet_widget_adjustment_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
1870 {
1871 g_assert_not_reached ();
1872 return NULL;
1873 }
1874
1875 SOW_MAKE_TYPE (adjustment, Adjustment,
1876 sheet_widget_adjustment_user_config,
1877 sheet_widget_adjustment_set_sheet,
1878 so_clear_sheet,
1879 sheet_widget_adjustment_foreach_dep,
1880 sheet_widget_adjustment_copy,
1881 sheet_widget_adjustment_write_xml_sax,
1882 sheet_widget_adjustment_prep_sax_parser,
1883 sheet_widget_adjustment_get_property,
1884 sheet_widget_adjustment_set_property,
1885 sheet_widget_draw_cairo,
1886 {
1887 ((SheetWidgetAdjustmentClass *) object_class)->has_orientation = TRUE;
1888 g_object_class_install_property
1889 (object_class, SWA_PROP_HORIZONTAL,
1890 g_param_spec_boolean ("horizontal", NULL, NULL,
1891 FALSE,
1892 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1893 })
1894
1895 /****************************************************************************/
1896
1897 #define GNM_SOW_SCROLLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SCROLLBAR_TYPE, SheetWidgetScrollbar))
1898 #define DEP_TO_SCROLLBAR(d_ptr) (SheetWidgetScrollbar *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetScrollbar, dep))
1899
1900 typedef SheetWidgetAdjustment SheetWidgetScrollbar;
1901 typedef SheetWidgetAdjustmentClass SheetWidgetScrollbarClass;
1902
1903 static GtkWidget *
sheet_widget_scrollbar_create_widget(SheetObjectWidget * sow)1904 sheet_widget_scrollbar_create_widget (SheetObjectWidget *sow)
1905 {
1906 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
1907 GtkWidget *bar;
1908
1909 swa->being_updated = TRUE;
1910 bar = gtk_scrollbar_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
1911 gtk_widget_set_can_focus (bar, FALSE);
1912 g_signal_connect (G_OBJECT (bar),
1913 "value_changed",
1914 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
1915 g_signal_connect (G_OBJECT (bar), "destroy",
1916 G_CALLBACK (cb_range_destroyed), swa);
1917 swa->being_updated = FALSE;
1918
1919 return bar;
1920 }
1921
1922 static void
sheet_widget_scrollbar_user_config(SheetObject * so,SheetControl * sc)1923 sheet_widget_scrollbar_user_config (SheetObject *so, SheetControl *sc)
1924 {
1925 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Scrollbar"),
1926 N_("Scrollbar Properties"));
1927 }
1928
1929 static void sheet_widget_slider_horizontal_draw_cairo
1930 (SheetObject const *so, cairo_t *cr, double width, double height);
1931
1932 static void
sheet_widget_scrollbar_horizontal_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)1933 sheet_widget_scrollbar_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
1934 double width, double height)
1935 {
1936 cairo_save (cr);
1937 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
1938
1939 cairo_new_path (cr);
1940 cairo_move_to (cr, 0., height/2);
1941 cairo_rel_line_to (cr, 15., 7.5);
1942 cairo_rel_line_to (cr, 0, -15);
1943 cairo_close_path (cr);
1944 cairo_fill (cr);
1945
1946 cairo_new_path (cr);
1947 cairo_move_to (cr, width, height/2);
1948 cairo_rel_line_to (cr, -15., 7.5);
1949 cairo_rel_line_to (cr, 0, -15);
1950 cairo_close_path (cr);
1951 cairo_fill (cr);
1952
1953 cairo_new_path (cr);
1954 cairo_translate (cr, 15., 0.);
1955 sheet_widget_slider_horizontal_draw_cairo (so, cr, width - 30, height);
1956 cairo_restore (cr);
1957 }
1958
1959 static void
sheet_widget_scrollbar_vertical_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)1960 sheet_widget_scrollbar_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
1961 double width, double height)
1962 {
1963 cairo_save (cr);
1964 cairo_rotate (cr, M_PI/2);
1965 cairo_translate (cr, 0., -width);
1966 sheet_widget_scrollbar_horizontal_draw_cairo (so, cr, height, width);
1967 cairo_restore (cr);
1968 }
1969
1970 static void
sheet_widget_scrollbar_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)1971 sheet_widget_scrollbar_draw_cairo (SheetObject const *so, cairo_t *cr,
1972 double width, double height)
1973 {
1974 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
1975 if (swa->horizontal)
1976 sheet_widget_scrollbar_horizontal_draw_cairo
1977 (so, cr, width, height);
1978 else
1979 sheet_widget_scrollbar_vertical_draw_cairo
1980 (so, cr, width, height);
1981 }
1982
1983 static void
sheet_widget_scrollbar_class_init(SheetObjectWidgetClass * sow_class)1984 sheet_widget_scrollbar_class_init (SheetObjectWidgetClass *sow_class)
1985 {
1986 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
1987 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
1988
1989 sow_class->create_widget = &sheet_widget_scrollbar_create_widget;
1990 so_class->user_config = &sheet_widget_scrollbar_user_config;
1991 so_class->draw_cairo = &sheet_widget_scrollbar_draw_cairo;
1992 swa_class->type = GTK_TYPE_SCROLLBAR;
1993 }
1994
1995 GSF_CLASS (SheetWidgetScrollbar, sheet_widget_scrollbar,
1996 &sheet_widget_scrollbar_class_init, NULL,
1997 GNM_SOW_ADJUSTMENT_TYPE)
1998
1999 /****************************************************************************/
2000
2001 #define GNM_SOW_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SPIN_BUTTON_TYPE, SheetWidgetSpinbutton))
2002 #define DEP_TO_SPINBUTTON(d_ptr) (SheetWidgetSpinbutton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSpinbutton, dep))
2003
2004 typedef SheetWidgetAdjustment SheetWidgetSpinbutton;
2005 typedef SheetWidgetAdjustmentClass SheetWidgetSpinbuttonClass;
2006
2007 static GtkWidget *
sheet_widget_spinbutton_create_widget(SheetObjectWidget * sow)2008 sheet_widget_spinbutton_create_widget (SheetObjectWidget *sow)
2009 {
2010 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2011 GtkWidget *spinbutton;
2012
2013 swa->being_updated = TRUE;
2014 spinbutton = gtk_spin_button_new
2015 (swa->adjustment,
2016 gtk_adjustment_get_step_increment (swa->adjustment),
2017 0);
2018 gtk_widget_set_can_focus (spinbutton, FALSE);
2019 g_signal_connect (G_OBJECT (spinbutton),
2020 "value_changed",
2021 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2022 g_signal_connect (G_OBJECT (spinbutton), "destroy",
2023 G_CALLBACK (cb_range_destroyed), swa);
2024 swa->being_updated = FALSE;
2025 return spinbutton;
2026 }
2027
2028 static void
sheet_widget_spinbutton_user_config(SheetObject * so,SheetControl * sc)2029 sheet_widget_spinbutton_user_config (SheetObject *so, SheetControl *sc)
2030 {
2031 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Spinbutton"),
2032 N_("Spinbutton Properties"));
2033 }
2034
2035 static void
sheet_widget_spinbutton_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)2036 sheet_widget_spinbutton_draw_cairo (SheetObject const *so, cairo_t *cr,
2037 double width, double height)
2038 {
2039 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2040 GtkAdjustment *adjustment = swa->adjustment;
2041 double value = gtk_adjustment_get_value (adjustment);
2042 int ivalue = (int) value;
2043 double halfheight = height/2;
2044 char *str;
2045
2046 cairo_save (cr);
2047 cairo_set_line_width (cr, 0.5);
2048 cairo_set_source_rgb(cr, 0, 0, 0);
2049
2050 cairo_new_path (cr);
2051 cairo_move_to (cr, 0, 0);
2052 cairo_line_to (cr, width, 0);
2053 cairo_line_to (cr, width, height);
2054 cairo_line_to (cr, 0, height);
2055 cairo_close_path (cr);
2056 cairo_stroke (cr);
2057
2058 cairo_new_path (cr);
2059 cairo_move_to (cr, width - 10, 0);
2060 cairo_rel_line_to (cr, 0, height);
2061 cairo_stroke (cr);
2062
2063 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2064
2065 cairo_new_path (cr);
2066 cairo_move_to (cr, width - 5, 3);
2067 cairo_rel_line_to (cr, 3, 3);
2068 cairo_rel_line_to (cr, -6, 0);
2069 cairo_close_path (cr);
2070 cairo_fill (cr);
2071
2072 cairo_new_path (cr);
2073 cairo_move_to (cr, width - 5, height - 3);
2074 cairo_rel_line_to (cr, 3, -3);
2075 cairo_rel_line_to (cr, -6, 0);
2076 cairo_close_path (cr);
2077 cairo_fill (cr);
2078
2079 str = g_strdup_printf ("%i", ivalue);
2080 cairo_set_source_rgb(cr, 0, 0, 0);
2081 cairo_move_to (cr, 4., halfheight);
2082 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
2083 g_free (str);
2084
2085 cairo_new_path (cr);
2086 cairo_restore (cr);
2087 }
2088
2089 static void
sheet_widget_spinbutton_class_init(SheetObjectWidgetClass * sow_class)2090 sheet_widget_spinbutton_class_init (SheetObjectWidgetClass *sow_class)
2091 {
2092 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2093 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2094
2095 sow_class->create_widget = &sheet_widget_spinbutton_create_widget;
2096 so_class->user_config = &sheet_widget_spinbutton_user_config;
2097 so_class->draw_cairo = &sheet_widget_spinbutton_draw_cairo;
2098
2099 swa_class->type = GTK_TYPE_SPIN_BUTTON;
2100 swa_class->has_orientation = FALSE;
2101 }
2102
2103 GSF_CLASS (SheetWidgetSpinbutton, sheet_widget_spinbutton,
2104 &sheet_widget_spinbutton_class_init, NULL,
2105 GNM_SOW_ADJUSTMENT_TYPE)
2106
2107 /****************************************************************************/
2108
2109 #define GNM_SOW_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_SLIDER_TYPE, SheetWidgetSlider))
2110 #define DEP_TO_SLIDER(d_ptr) (SheetWidgetSlider *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetSlider, dep))
2111
2112 typedef SheetWidgetAdjustment SheetWidgetSlider;
2113 typedef SheetWidgetAdjustmentClass SheetWidgetSliderClass;
2114
2115 static GtkWidget *
sheet_widget_slider_create_widget(SheetObjectWidget * sow)2116 sheet_widget_slider_create_widget (SheetObjectWidget *sow)
2117 {
2118 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (sow);
2119 GtkWidget *slider;
2120
2121 swa->being_updated = TRUE;
2122 slider = gtk_scale_new (swa->horizontal? GTK_ORIENTATION_HORIZONTAL: GTK_ORIENTATION_VERTICAL, swa->adjustment);
2123 gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
2124 gtk_widget_set_can_focus (slider, FALSE);
2125 g_signal_connect (G_OBJECT (slider),
2126 "value_changed",
2127 G_CALLBACK (cb_adjustment_widget_value_changed), swa);
2128 g_signal_connect (G_OBJECT (slider), "destroy",
2129 G_CALLBACK (cb_range_destroyed), swa);
2130 swa->being_updated = FALSE;
2131
2132 return slider;
2133 }
2134
2135 static void
sheet_widget_slider_user_config(SheetObject * so,SheetControl * sc)2136 sheet_widget_slider_user_config (SheetObject *so, SheetControl *sc)
2137 {
2138 sheet_widget_adjustment_user_config_impl (so, sc, N_("Configure Slider"),
2139 N_("Slider Properties"));
2140 }
2141
2142 static void
sheet_widget_slider_horizontal_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)2143 sheet_widget_slider_horizontal_draw_cairo (SheetObject const *so, cairo_t *cr,
2144 double width, double height)
2145 {
2146 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2147 GtkAdjustment *adjustment = swa->adjustment;
2148 double value = gtk_adjustment_get_value (adjustment);
2149 double upper = gtk_adjustment_get_upper (adjustment);
2150 double lower = gtk_adjustment_get_lower (adjustment);
2151 double fraction = (upper == lower) ? 0.0 : (value - lower)/(upper- lower);
2152
2153 cairo_save (cr);
2154 cairo_set_line_width (cr, 5);
2155 cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
2156 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2157
2158 cairo_new_path (cr);
2159 cairo_move_to (cr, 4, height/2);
2160 cairo_rel_line_to (cr, width - 8., 0);
2161 cairo_stroke (cr);
2162
2163 cairo_set_line_width (cr, 15);
2164 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
2165 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
2166
2167 cairo_new_path (cr);
2168 cairo_move_to (cr, fraction * (width - 8. - 1. - 5. - 5. + 2.5 + 2.5)
2169 - 10. + 10. + 4. + 5. - 2.5, height/2);
2170 cairo_rel_line_to (cr, 1, 0);
2171 cairo_stroke (cr);
2172
2173 cairo_new_path (cr);
2174 cairo_restore (cr);
2175 }
2176
2177 static void
sheet_widget_slider_vertical_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)2178 sheet_widget_slider_vertical_draw_cairo (SheetObject const *so, cairo_t *cr,
2179 double width, double height)
2180 {
2181 cairo_save (cr);
2182 cairo_rotate (cr, M_PI/2);
2183 cairo_translate (cr, 0., -width);
2184 sheet_widget_slider_horizontal_draw_cairo (so, cr, height, width);
2185 cairo_restore (cr);
2186 }
2187
2188 static void
sheet_widget_slider_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)2189 sheet_widget_slider_draw_cairo (SheetObject const *so, cairo_t *cr,
2190 double width, double height)
2191 {
2192 SheetWidgetAdjustment *swa = GNM_SOW_ADJUSTMENT (so);
2193 if (swa->horizontal)
2194 sheet_widget_slider_horizontal_draw_cairo (so, cr, width, height);
2195 else
2196 sheet_widget_slider_vertical_draw_cairo (so, cr, width, height);
2197 }
2198
2199 static void
sheet_widget_slider_class_init(SheetObjectWidgetClass * sow_class)2200 sheet_widget_slider_class_init (SheetObjectWidgetClass *sow_class)
2201 {
2202 SheetWidgetAdjustmentClass *swa_class = (SheetWidgetAdjustmentClass *)sow_class;
2203 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
2204
2205 sow_class->create_widget = &sheet_widget_slider_create_widget;
2206 so_class->user_config = &sheet_widget_slider_user_config;
2207 so_class->draw_cairo = &sheet_widget_slider_draw_cairo;
2208
2209 swa_class->type = GTK_TYPE_SCALE;
2210 }
2211
2212 GSF_CLASS (SheetWidgetSlider, sheet_widget_slider,
2213 &sheet_widget_slider_class_init, NULL,
2214 GNM_SOW_ADJUSTMENT_TYPE)
2215
2216 /****************************************************************************/
2217
2218 #define GNM_SOW_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_CHECKBOX_TYPE, SheetWidgetCheckbox))
2219 #define DEP_TO_CHECKBOX(d_ptr) (SheetWidgetCheckbox *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetCheckbox, dep))
2220
2221 typedef struct {
2222 SheetObjectWidget sow;
2223
2224 GnmDependent dep;
2225 char *label;
2226 gboolean value;
2227 gboolean being_updated;
2228 } SheetWidgetCheckbox;
2229 typedef SheetObjectWidgetClass SheetWidgetCheckboxClass;
2230
2231 enum {
2232 SOC_PROP_0 = 0,
2233 SOC_PROP_ACTIVE,
2234 SOC_PROP_TEXT,
2235 SOC_PROP_MARKUP
2236 };
2237
2238 static void
sheet_widget_checkbox_set_active(SheetWidgetCheckbox * swc)2239 sheet_widget_checkbox_set_active (SheetWidgetCheckbox *swc)
2240 {
2241 GList *ptr;
2242
2243 swc->being_updated = TRUE;
2244
2245 for (ptr = swc->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2246 SheetObjectView *view = ptr->data;
2247 GocWidget *item = get_goc_widget (view);
2248 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2249 swc->value);
2250 }
2251
2252 g_object_notify (G_OBJECT (swc), "active");
2253
2254 swc->being_updated = FALSE;
2255 }
2256
2257 static void
sheet_widget_checkbox_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)2258 sheet_widget_checkbox_get_property (GObject *obj, guint param_id,
2259 GValue *value, GParamSpec *pspec)
2260 {
2261 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2262
2263 switch (param_id) {
2264 case SOC_PROP_ACTIVE:
2265 g_value_set_boolean (value, swc->value);
2266 break;
2267 case SOC_PROP_TEXT:
2268 g_value_set_string (value, swc->label);
2269 break;
2270 case SOC_PROP_MARKUP:
2271 g_value_set_boxed (value, NULL); /* swc->markup */
2272 break;
2273 default:
2274 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2275 break;
2276 }
2277 }
2278
2279 static void
sheet_widget_checkbox_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)2280 sheet_widget_checkbox_set_property (GObject *obj, guint param_id,
2281 GValue const *value, GParamSpec *pspec)
2282 {
2283 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2284
2285 switch (param_id) {
2286 case SOC_PROP_ACTIVE:
2287 swc->value = g_value_get_boolean (value);
2288 sheet_widget_checkbox_set_active (swc);
2289 break;
2290 case SOC_PROP_TEXT:
2291 sheet_widget_checkbox_set_label (GNM_SO (swc),
2292 g_value_get_string (value));
2293 break;
2294 case SOC_PROP_MARKUP:
2295 #if 0
2296 sheet_widget_checkbox_set_markup (GNM_SO (swc),
2297 g_value_peek_pointer (value));
2298 #endif
2299 break;
2300 default:
2301 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2302 return;
2303 }
2304 }
2305
2306 static void
checkbox_eval(GnmDependent * dep)2307 checkbox_eval (GnmDependent *dep)
2308 {
2309 GnmValue *v;
2310 GnmEvalPos pos;
2311 gboolean err, result;
2312
2313 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2314 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2315 result = value_get_as_bool (v, &err);
2316 value_release (v);
2317 if (!err) {
2318 SheetWidgetCheckbox *swc = DEP_TO_CHECKBOX(dep);
2319
2320 swc->value = result;
2321 sheet_widget_checkbox_set_active (swc);
2322 }
2323 }
2324
2325 static void
checkbox_debug_name(GnmDependent const * dep,GString * target)2326 checkbox_debug_name (GnmDependent const *dep, GString *target)
2327 {
2328 g_string_append_printf (target, "Checkbox%p", (void *)dep);
2329 }
2330
2331 static DEPENDENT_MAKE_TYPE (checkbox, .eval = checkbox_eval, .debug_name = checkbox_debug_name)
2332
2333 static void
sheet_widget_checkbox_init_full(SheetWidgetCheckbox * swc,GnmCellRef const * ref,char const * label)2334 sheet_widget_checkbox_init_full (SheetWidgetCheckbox *swc,
2335 GnmCellRef const *ref, char const *label)
2336 {
2337 static int counter = 0;
2338
2339 g_return_if_fail (swc != NULL);
2340
2341 swc->label = label ? g_strdup (label) : g_strdup_printf (_("CheckBox %d"), ++counter);
2342 swc->being_updated = FALSE;
2343 swc->value = FALSE;
2344 swc->dep.sheet = NULL;
2345 swc->dep.flags = checkbox_get_dep_type ();
2346 swc->dep.texpr = (ref != NULL)
2347 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2348 : NULL;
2349 }
2350
2351 static void
sheet_widget_checkbox_init(SheetWidgetCheckbox * swc)2352 sheet_widget_checkbox_init (SheetWidgetCheckbox *swc)
2353 {
2354 sheet_widget_checkbox_init_full (swc, NULL, NULL);
2355 }
2356
2357 static void
sheet_widget_checkbox_finalize(GObject * obj)2358 sheet_widget_checkbox_finalize (GObject *obj)
2359 {
2360 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (obj);
2361
2362 g_return_if_fail (swc != NULL);
2363
2364 g_free (swc->label);
2365 swc->label = NULL;
2366
2367 dependent_set_expr (&swc->dep, NULL);
2368
2369 sheet_object_widget_class->finalize (obj);
2370 }
2371
2372 static void
cb_checkbox_toggled(GtkToggleButton * button,SheetWidgetCheckbox * swc)2373 cb_checkbox_toggled (GtkToggleButton *button, SheetWidgetCheckbox *swc)
2374 {
2375 GnmCellRef ref;
2376
2377 if (swc->being_updated)
2378 return;
2379 swc->value = gtk_toggle_button_get_active (button);
2380 sheet_widget_checkbox_set_active (swc);
2381
2382 if (so_get_ref (GNM_SO (swc), &ref, TRUE) != NULL) {
2383 gboolean new_val = gtk_toggle_button_get_active (button);
2384 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2385 /* FIXME: This text sucks: */
2386 _("Clicking checkbox"),
2387 &ref, value_new_bool (new_val),
2388 sheet_object_get_sheet (GNM_SO (swc)));
2389 }
2390 }
2391
2392 static GtkWidget *
sheet_widget_checkbox_create_widget(SheetObjectWidget * sow)2393 sheet_widget_checkbox_create_widget (SheetObjectWidget *sow)
2394 {
2395 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2396 GtkWidget *button;
2397
2398 g_return_val_if_fail (swc != NULL, NULL);
2399
2400 button = gtk_check_button_new_with_label (swc->label);
2401 gtk_widget_set_can_focus (button, FALSE);
2402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2403 g_signal_connect (G_OBJECT (button),
2404 "toggled",
2405 G_CALLBACK (cb_checkbox_toggled), swc);
2406
2407 return button;
2408 }
2409
2410 static void
sheet_widget_checkbox_copy(SheetObject * dst,SheetObject const * src)2411 sheet_widget_checkbox_copy (SheetObject *dst, SheetObject const *src)
2412 {
2413 SheetWidgetCheckbox const *src_swc = GNM_SOW_CHECKBOX (src);
2414 SheetWidgetCheckbox *dst_swc = GNM_SOW_CHECKBOX (dst);
2415 GnmCellRef ref;
2416 sheet_widget_checkbox_init_full (dst_swc,
2417 so_get_ref (src, &ref, FALSE),
2418 src_swc->label);
2419 dst_swc->value = src_swc->value;
2420 }
2421
2422 typedef struct {
2423 GtkWidget *dialog;
2424 GnmExprEntry *expression;
2425 GtkWidget *label;
2426
2427 char *old_label;
2428 GtkWidget *old_focus;
2429
2430 WBCGtk *wbcg;
2431 SheetWidgetCheckbox *swc;
2432 Sheet *sheet;
2433 } CheckboxConfigState;
2434
2435 static void
cb_checkbox_set_focus(G_GNUC_UNUSED GtkWidget * window,GtkWidget * focus_widget,CheckboxConfigState * state)2436 cb_checkbox_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
2437 CheckboxConfigState *state)
2438 {
2439 GtkWidget *ofp;
2440
2441 /* Note: half of the set-focus action is handle by the default
2442 * callback installed by wbc_gtk_attach_guru. */
2443
2444 ofp = state->old_focus
2445 ? gtk_widget_get_parent (state->old_focus)
2446 : NULL;
2447
2448 /* Force an update of the content in case it needs tweaking (eg make it
2449 * absolute) */
2450 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
2451 GnmParsePos pp;
2452 GnmExprTop const *texpr = gnm_expr_entry_parse (
2453 GNM_EXPR_ENTRY (ofp),
2454 parse_pos_init_sheet (&pp, state->sheet),
2455 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2456 if (texpr != NULL)
2457 gnm_expr_top_unref (texpr);
2458 }
2459 state->old_focus = focus_widget;
2460 }
2461
2462 static void
cb_checkbox_config_destroy(CheckboxConfigState * state)2463 cb_checkbox_config_destroy (CheckboxConfigState *state)
2464 {
2465 g_return_if_fail (state != NULL);
2466
2467 g_free (state->old_label);
2468 state->old_label = NULL;
2469 state->dialog = NULL;
2470 g_free (state);
2471 }
2472
2473 static void
cb_checkbox_config_ok_clicked(G_GNUC_UNUSED GtkWidget * button,CheckboxConfigState * state)2474 cb_checkbox_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2475 {
2476 SheetObject *so = GNM_SO (state->swc);
2477 GnmParsePos pp;
2478 GnmExprTop const *texpr = gnm_expr_entry_parse (state->expression,
2479 parse_pos_init_sheet (&pp, so->sheet),
2480 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
2481 gchar const *text = gtk_entry_get_text(GTK_ENTRY(state->label));
2482
2483 cmd_so_set_checkbox (GNM_WBC (state->wbcg), so,
2484 texpr, g_strdup (state->old_label), g_strdup (text));
2485
2486 gtk_widget_destroy (state->dialog);
2487 }
2488
2489 static void
cb_checkbox_config_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,CheckboxConfigState * state)2490 cb_checkbox_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, CheckboxConfigState *state)
2491 {
2492 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2493 state->old_label);
2494 gtk_widget_destroy (state->dialog);
2495 }
2496
2497 static void
cb_checkbox_label_changed(GtkEntry * entry,CheckboxConfigState * state)2498 cb_checkbox_label_changed (GtkEntry *entry, CheckboxConfigState *state)
2499 {
2500 sheet_widget_checkbox_set_label (GNM_SO (state->swc),
2501 gtk_entry_get_text (entry));
2502 }
2503
2504 static void
sheet_widget_checkbox_user_config(SheetObject * so,SheetControl * sc)2505 sheet_widget_checkbox_user_config (SheetObject *so, SheetControl *sc)
2506 {
2507 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2508 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
2509 CheckboxConfigState *state;
2510 GtkWidget *grid;
2511 GtkBuilder *gui;
2512
2513 g_return_if_fail (swc != NULL);
2514
2515 /* Only pop up one copy per workbook */
2516 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
2517 return;
2518
2519 gui = gnm_gtk_builder_load ("res:ui/so-checkbox.ui", NULL, GO_CMD_CONTEXT (wbcg));
2520 if (!gui)
2521 return;
2522 state = g_new (CheckboxConfigState, 1);
2523 state->swc = swc;
2524 state->wbcg = wbcg;
2525 state->sheet = sc_sheet (sc);
2526 state->old_focus = NULL;
2527 state->old_label = g_strdup (swc->label);
2528 state->dialog = go_gtk_builder_get_widget (gui, "SO-Checkbox");
2529
2530 grid = go_gtk_builder_get_widget (gui, "main-grid");
2531
2532 state->expression = gnm_expr_entry_new (wbcg, TRUE);
2533 gnm_expr_entry_set_flags (state->expression,
2534 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
2535 GNM_EE_MASK);
2536 gnm_expr_entry_load_from_dep (state->expression, &swc->dep);
2537 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
2538 GTK_WIDGET (state->expression));
2539 gtk_grid_attach (GTK_GRID (grid),
2540 GTK_WIDGET (state->expression), 1, 0, 1, 1);
2541 gtk_widget_show (GTK_WIDGET (state->expression));
2542
2543 state->label = go_gtk_builder_get_widget (gui, "label_entry");
2544 gtk_entry_set_text (GTK_ENTRY (state->label), swc->label);
2545 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
2546 gnm_editable_enters (GTK_WINDOW (state->dialog),
2547 GTK_WIDGET (state->expression));
2548 gnm_editable_enters (GTK_WINDOW (state->dialog),
2549 GTK_WIDGET (state->label));
2550
2551 g_signal_connect (G_OBJECT (state->label),
2552 "changed",
2553 G_CALLBACK (cb_checkbox_label_changed), state);
2554 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
2555 "clicked",
2556 G_CALLBACK (cb_checkbox_config_ok_clicked), state);
2557 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
2558 "clicked",
2559 G_CALLBACK (cb_checkbox_config_cancel_clicked), state);
2560
2561 gnm_init_help_button (
2562 go_gtk_builder_get_widget (gui, "help_button"),
2563 GNUMERIC_HELP_LINK_SO_CHECKBOX);
2564
2565 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
2566 SHEET_OBJECT_CONFIG_KEY);
2567
2568 wbc_gtk_attach_guru (state->wbcg, state->dialog);
2569 g_object_set_data_full (G_OBJECT (state->dialog),
2570 "state", state, (GDestroyNotify) cb_checkbox_config_destroy);
2571
2572 /* Note: half of the set-focus action is handle by the default */
2573 /* callback installed by wbc_gtk_attach_guru */
2574 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
2575 G_CALLBACK (cb_checkbox_set_focus), state);
2576 g_object_unref (gui);
2577
2578 gtk_widget_show (state->dialog);
2579 }
2580
2581 static gboolean
sheet_widget_checkbox_set_sheet(SheetObject * so,Sheet * sheet)2582 sheet_widget_checkbox_set_sheet (SheetObject *so, Sheet *sheet)
2583 {
2584 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2585
2586 dependent_set_sheet (&swc->dep, sheet);
2587 sheet_widget_checkbox_set_active (swc);
2588
2589 return FALSE;
2590 }
2591
2592 static void
sheet_widget_checkbox_foreach_dep(SheetObject * so,SheetObjectForeachDepFunc func,gpointer user)2593 sheet_widget_checkbox_foreach_dep (SheetObject *so,
2594 SheetObjectForeachDepFunc func,
2595 gpointer user)
2596 {
2597 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2598 func (&swc->dep, so, user);
2599 }
2600
2601 static void
sheet_widget_checkbox_write_xml_sax(SheetObject const * so,GsfXMLOut * output,GnmConventions const * convs)2602 sheet_widget_checkbox_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
2603 GnmConventions const *convs)
2604 {
2605 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2606 gsf_xml_out_add_cstr (output, "Label", swc->label);
2607 gsf_xml_out_add_int (output, "Value", swc->value);
2608 sax_write_dep (output, &swc->dep, "Input", convs);
2609 }
2610
2611 static void
sheet_widget_checkbox_prep_sax_parser(SheetObject * so,GsfXMLIn * xin,xmlChar const ** attrs,GnmConventions const * convs)2612 sheet_widget_checkbox_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
2613 xmlChar const **attrs,
2614 GnmConventions const *convs)
2615 {
2616 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2617
2618 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2619 if (attr_eq (attrs[0], "Label")) {
2620 g_free (swc->label);
2621 swc->label = g_strdup (CXML2C (attrs[1]));
2622 } else if (gnm_xml_attr_int (attrs, "Value", &swc->value))
2623 ; /* ??? */
2624 else if (sax_read_dep (attrs, "Input", &swc->dep, xin, convs))
2625 ; /* ??? */
2626 }
2627
2628 void
sheet_widget_checkbox_set_link(SheetObject * so,GnmExprTop const * texpr)2629 sheet_widget_checkbox_set_link (SheetObject *so, GnmExprTop const *texpr)
2630 {
2631 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2632 dependent_set_expr (&swc->dep, texpr);
2633 if (texpr && swc->dep.sheet)
2634 dependent_link (&swc->dep);
2635 }
2636
2637 GnmExprTop const *
sheet_widget_checkbox_get_link(SheetObject * so)2638 sheet_widget_checkbox_get_link (SheetObject *so)
2639 {
2640 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2641 GnmExprTop const *texpr = swc->dep.texpr;
2642
2643 if (texpr)
2644 gnm_expr_top_ref (texpr);
2645
2646 return texpr;
2647 }
2648
2649
2650 void
sheet_widget_checkbox_set_label(SheetObject * so,char const * str)2651 sheet_widget_checkbox_set_label (SheetObject *so, char const *str)
2652 {
2653 GList *list;
2654 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (so);
2655 char *new_label;
2656
2657 if (go_str_compare (str, swc->label) == 0)
2658 return;
2659
2660 new_label = g_strdup (str);
2661 g_free (swc->label);
2662 swc->label = new_label;
2663
2664 for (list = swc->sow.so.realized_list; list; list = list->next) {
2665 SheetObjectView *view = list->data;
2666 GocWidget *item = get_goc_widget (view);
2667 gtk_button_set_label (GTK_BUTTON (item->widget), swc->label);
2668 }
2669 }
2670
2671 static void
sheet_widget_checkbox_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)2672 sheet_widget_checkbox_draw_cairo (SheetObject const *so, cairo_t *cr,
2673 double width, double height)
2674 {
2675 SheetWidgetCheckbox const *swc = GNM_SOW_CHECKBOX (so);
2676 double halfheight = height/2;
2677 double dx = 8., dxh, pm;
2678 int pw, ph;
2679
2680 pm = MIN (height - 2, width - 12);
2681 if (dx > pm)
2682 dx = MAX (pm, 3);
2683 dxh = dx/2;
2684
2685 cairo_save (cr);
2686 cairo_set_line_width (cr, 0.5);
2687 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
2688
2689 cairo_new_path (cr);
2690 cairo_move_to (cr, dxh, halfheight - dxh);
2691 cairo_rel_line_to (cr, 0, dx);
2692 cairo_rel_line_to (cr, dx, 0);
2693 cairo_rel_line_to (cr, 0., -dx);
2694 cairo_rel_line_to (cr, -dx, 0.);
2695 cairo_close_path (cr);
2696 cairo_fill_preserve (cr);
2697 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
2698 cairo_stroke (cr);
2699
2700 if (swc->value) {
2701 cairo_new_path (cr);
2702 cairo_move_to (cr, dxh, halfheight - dxh);
2703 cairo_rel_line_to (cr, dx, dx);
2704 cairo_rel_line_to (cr, -dx, 0.);
2705 cairo_rel_line_to (cr, dx, -dx);
2706 cairo_rel_line_to (cr, -dx, 0.);
2707 cairo_close_path (cr);
2708 cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
2709 cairo_stroke (cr);
2710 }
2711
2712 cairo_move_to (cr, 2 * dx, halfheight);
2713
2714 pw = width - 2 * dx;
2715 ph = height;
2716
2717 draw_cairo_text (cr, swc->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
2718
2719 cairo_new_path (cr);
2720 cairo_restore (cr);
2721 }
2722
2723
2724 SOW_MAKE_TYPE (checkbox, Checkbox,
2725 sheet_widget_checkbox_user_config,
2726 sheet_widget_checkbox_set_sheet,
2727 so_clear_sheet,
2728 sheet_widget_checkbox_foreach_dep,
2729 sheet_widget_checkbox_copy,
2730 sheet_widget_checkbox_write_xml_sax,
2731 sheet_widget_checkbox_prep_sax_parser,
2732 sheet_widget_checkbox_get_property,
2733 sheet_widget_checkbox_set_property,
2734 sheet_widget_checkbox_draw_cairo,
2735 {
2736 g_object_class_install_property
2737 (object_class, SOC_PROP_ACTIVE,
2738 g_param_spec_boolean ("active", NULL, NULL,
2739 FALSE,
2740 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2741 g_object_class_install_property
2742 (object_class, SOC_PROP_TEXT,
2743 g_param_spec_string ("text", NULL, NULL, NULL,
2744 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2745 g_object_class_install_property
2746 (object_class, SOC_PROP_MARKUP,
2747 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
2748 GSF_PARAM_STATIC | G_PARAM_READWRITE));
2749 })
2750
2751 /****************************************************************************/
2752 typedef SheetWidgetCheckbox SheetWidgetToggleButton;
2753 typedef SheetWidgetCheckboxClass SheetWidgetToggleButtonClass;
2754 static GtkWidget *
sheet_widget_toggle_button_create_widget(SheetObjectWidget * sow)2755 sheet_widget_toggle_button_create_widget (SheetObjectWidget *sow)
2756 {
2757 SheetWidgetCheckbox *swc = GNM_SOW_CHECKBOX (sow);
2758 GtkWidget *button = gtk_toggle_button_new_with_label (swc->label);
2759 gtk_widget_set_can_focus (button, FALSE);
2760 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), swc->value);
2761 g_signal_connect (G_OBJECT (button),
2762 "toggled",
2763 G_CALLBACK (cb_checkbox_toggled), swc);
2764 return button;
2765 }
2766 static void
sheet_widget_toggle_button_class_init(SheetObjectWidgetClass * sow_class)2767 sheet_widget_toggle_button_class_init (SheetObjectWidgetClass *sow_class)
2768 {
2769 sow_class->create_widget = &sheet_widget_toggle_button_create_widget;
2770 }
2771
2772 GSF_CLASS (SheetWidgetToggleButton, sheet_widget_toggle_button,
2773 &sheet_widget_toggle_button_class_init, NULL,
2774 GNM_SOW_CHECKBOX_TYPE)
2775
2776 /****************************************************************************/
2777
2778 #define GNM_SOW_RADIO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_RADIO_BUTTON_TYPE, SheetWidgetRadioButton))
2779 #define DEP_TO_RADIO_BUTTON(d_ptr) (SheetWidgetRadioButton *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetRadioButton, dep))
2780
2781 typedef struct {
2782 SheetObjectWidget sow;
2783
2784 gboolean being_updated;
2785 char *label;
2786 GnmValue *value;
2787 gboolean active;
2788 GnmDependent dep;
2789 } SheetWidgetRadioButton;
2790 typedef SheetObjectWidgetClass SheetWidgetRadioButtonClass;
2791
2792 enum {
2793 SOR_PROP_0 = 0,
2794 SOR_PROP_ACTIVE,
2795 SOR_PROP_TEXT,
2796 SOR_PROP_MARKUP,
2797 SOR_PROP_VALUE
2798 };
2799
2800 static void
sheet_widget_radio_button_set_active(SheetWidgetRadioButton * swrb,gboolean active)2801 sheet_widget_radio_button_set_active (SheetWidgetRadioButton *swrb,
2802 gboolean active)
2803 {
2804 GList *ptr;
2805
2806 if (swrb->active == active)
2807 return;
2808 swrb->active = active;
2809
2810 swrb->being_updated = TRUE;
2811
2812 for (ptr = swrb->sow.so.realized_list; ptr != NULL ; ptr = ptr->next) {
2813 SheetObjectView *view = ptr->data;
2814 GocWidget *item = get_goc_widget (view);
2815 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget),
2816 active);
2817 }
2818
2819 g_object_notify (G_OBJECT (swrb), "active");
2820
2821 swrb->being_updated = FALSE;
2822 }
2823
2824
2825 static void
sheet_widget_radio_button_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)2826 sheet_widget_radio_button_get_property (GObject *obj, guint param_id,
2827 GValue *value, GParamSpec *pspec)
2828 {
2829 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2830
2831 switch (param_id) {
2832 case SOR_PROP_ACTIVE:
2833 g_value_set_boolean (value, swrb->active);
2834 break;
2835 case SOR_PROP_TEXT:
2836 g_value_set_string (value, swrb->label);
2837 break;
2838 case SOR_PROP_MARKUP:
2839 g_value_set_boxed (value, NULL); /* swrb->markup */
2840 break;
2841 case SOR_PROP_VALUE:
2842 g_value_set_boxed (value, swrb->value);
2843 break;
2844 default:
2845 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2846 break;
2847 }
2848 }
2849
2850 static void
sheet_widget_radio_button_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)2851 sheet_widget_radio_button_set_property (GObject *obj, guint param_id,
2852 GValue const *value, GParamSpec *pspec)
2853 {
2854 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2855
2856 switch (param_id) {
2857 case SOR_PROP_ACTIVE:
2858 sheet_widget_radio_button_set_active (swrb,
2859 g_value_get_boolean (value));
2860 break;
2861 case SOR_PROP_TEXT:
2862 sheet_widget_radio_button_set_label (GNM_SO (swrb),
2863 g_value_get_string (value));
2864 break;
2865 case SOR_PROP_MARKUP:
2866 #if 0
2867 sheet_widget_radio_button_set_markup (GNM_SO (swrb),
2868 g_value_peek_pointer (value));
2869 #endif
2870 break;
2871 case SOR_PROP_VALUE:
2872 sheet_widget_radio_button_set_value (GNM_SO (swrb),
2873 g_value_get_boxed (value));
2874 break;
2875 default:
2876 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
2877 return;
2878 }
2879 }
2880
2881 GnmValue const *
sheet_widget_radio_button_get_value(SheetObject * so)2882 sheet_widget_radio_button_get_value (SheetObject *so)
2883 {
2884 SheetWidgetRadioButton *swrb;
2885
2886 g_return_val_if_fail (GNM_IS_SOW_RADIO_BUTTON (so), NULL);
2887
2888 swrb = GNM_SOW_RADIO_BUTTON (so);
2889 return swrb->value;
2890 }
2891
2892 void
sheet_widget_radio_button_set_value(SheetObject * so,GnmValue const * val)2893 sheet_widget_radio_button_set_value (SheetObject *so, GnmValue const *val)
2894 {
2895 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
2896
2897 value_release (swrb->value);
2898 swrb->value = value_dup (val);
2899 }
2900
2901 static void
radio_button_eval(GnmDependent * dep)2902 radio_button_eval (GnmDependent *dep)
2903 {
2904 GnmValue *v;
2905 GnmEvalPos pos;
2906 SheetWidgetRadioButton *swrb = DEP_TO_RADIO_BUTTON (dep);
2907
2908 v = gnm_expr_top_eval (dep->texpr, eval_pos_init_dep (&pos, dep),
2909 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
2910 if (v && swrb->value) {
2911 gboolean active = value_equal (swrb->value, v);
2912 sheet_widget_radio_button_set_active (swrb, active);
2913 }
2914 value_release (v);
2915 }
2916
2917 static void
radio_button_debug_name(GnmDependent const * dep,GString * target)2918 radio_button_debug_name (GnmDependent const *dep, GString *target)
2919 {
2920 g_string_append_printf (target, "RadioButton%p", (void *)dep);
2921 }
2922
2923 static DEPENDENT_MAKE_TYPE (radio_button, .eval = radio_button_eval, .debug_name = radio_button_debug_name)
2924
2925 static void
sheet_widget_radio_button_init_full(SheetWidgetRadioButton * swrb,GnmCellRef const * ref,char const * label,GnmValue const * value,gboolean active)2926 sheet_widget_radio_button_init_full (SheetWidgetRadioButton *swrb,
2927 GnmCellRef const *ref,
2928 char const *label,
2929 GnmValue const *value,
2930 gboolean active)
2931 {
2932 g_return_if_fail (swrb != NULL);
2933
2934 swrb->being_updated = FALSE;
2935 swrb->label = g_strdup (label ? label : _("RadioButton"));
2936 swrb->value = value ? value_dup (value) : value_new_empty ();
2937 swrb->active = active;
2938
2939 swrb->dep.sheet = NULL;
2940 swrb->dep.flags = radio_button_get_dep_type ();
2941 swrb->dep.texpr = (ref != NULL)
2942 ? gnm_expr_top_new (gnm_expr_new_cellref (ref))
2943 : NULL;
2944 }
2945
2946 static void
sheet_widget_radio_button_init(SheetWidgetRadioButton * swrb)2947 sheet_widget_radio_button_init (SheetWidgetRadioButton *swrb)
2948 {
2949 sheet_widget_radio_button_init_full (swrb, NULL, NULL, NULL, TRUE);
2950 }
2951
2952 static void
sheet_widget_radio_button_finalize(GObject * obj)2953 sheet_widget_radio_button_finalize (GObject *obj)
2954 {
2955 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (obj);
2956
2957 g_return_if_fail (swrb != NULL);
2958
2959 g_free (swrb->label);
2960 swrb->label = NULL;
2961 value_release (swrb->value);
2962 swrb->value = NULL;
2963
2964 dependent_set_expr (&swrb->dep, NULL);
2965
2966 sheet_object_widget_class->finalize (obj);
2967 }
2968
2969 static void
sheet_widget_radio_button_toggled(GtkToggleButton * button,SheetWidgetRadioButton * swrb)2970 sheet_widget_radio_button_toggled (GtkToggleButton *button,
2971 SheetWidgetRadioButton *swrb)
2972 {
2973 GnmCellRef ref;
2974
2975 if (swrb->being_updated)
2976 return;
2977 swrb->active = gtk_toggle_button_get_active (button);
2978
2979 if (so_get_ref (GNM_SO (swrb), &ref, TRUE) != NULL) {
2980 cmd_so_set_value (widget_wbc (GTK_WIDGET (button)),
2981 /* FIXME: This text sucks: */
2982 _("Clicking radiobutton"),
2983 &ref, value_dup (swrb->value),
2984 sheet_object_get_sheet (GNM_SO (swrb)));
2985 }
2986 }
2987
2988 static GtkWidget *
sheet_widget_radio_button_create_widget(SheetObjectWidget * sow)2989 sheet_widget_radio_button_create_widget (SheetObjectWidget *sow)
2990 {
2991 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (sow);
2992 GtkWidget *w = g_object_new (GNM_TYPE_RADIO_BUTTON,
2993 "label", swrb->label,
2994 NULL) ;
2995
2996 gtk_widget_set_can_focus (w, FALSE);
2997
2998 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), swrb->active);
2999
3000 g_signal_connect (G_OBJECT (w),
3001 "toggled",
3002 G_CALLBACK (sheet_widget_radio_button_toggled), sow);
3003 return w;
3004 }
3005
3006 static void
sheet_widget_radio_button_copy(SheetObject * dst,SheetObject const * src)3007 sheet_widget_radio_button_copy (SheetObject *dst, SheetObject const *src)
3008 {
3009 SheetWidgetRadioButton const *src_swrb = GNM_SOW_RADIO_BUTTON (src);
3010 SheetWidgetRadioButton *dst_swrb = GNM_SOW_RADIO_BUTTON (dst);
3011 GnmCellRef ref;
3012
3013 sheet_widget_radio_button_init_full (dst_swrb,
3014 so_get_ref (src, &ref, FALSE),
3015 src_swrb->label,
3016 src_swrb->value,
3017 src_swrb->active);
3018 }
3019
3020 static gboolean
sheet_widget_radio_button_set_sheet(SheetObject * so,Sheet * sheet)3021 sheet_widget_radio_button_set_sheet (SheetObject *so, Sheet *sheet)
3022 {
3023 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3024
3025 dependent_set_sheet (&swrb->dep, sheet);
3026
3027 return FALSE;
3028 }
3029
3030 static void
sheet_widget_radio_button_foreach_dep(SheetObject * so,SheetObjectForeachDepFunc func,gpointer user)3031 sheet_widget_radio_button_foreach_dep (SheetObject *so,
3032 SheetObjectForeachDepFunc func,
3033 gpointer user)
3034 {
3035 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3036 func (&swrb->dep, so, user);
3037 }
3038
3039 static void
sheet_widget_radio_button_write_xml_sax(SheetObject const * so,GsfXMLOut * output,GnmConventions const * convs)3040 sheet_widget_radio_button_write_xml_sax (SheetObject const *so,
3041 GsfXMLOut *output,
3042 GnmConventions const *convs)
3043 {
3044 SheetWidgetRadioButton const *swrb = GNM_SOW_RADIO_BUTTON (so);
3045 GString *valstr = g_string_new (NULL);
3046
3047 value_get_as_gstring (swrb->value, valstr, convs);
3048
3049 gsf_xml_out_add_cstr (output, "Label", swrb->label);
3050 gsf_xml_out_add_cstr (output, "Value", valstr->str);
3051 gsf_xml_out_add_int (output, "ValueType", swrb->value->v_any.type);
3052 gsf_xml_out_add_int (output, "Active", swrb->active);
3053 sax_write_dep (output, &swrb->dep, "Input", convs);
3054
3055 g_string_free (valstr, TRUE);
3056 }
3057
3058 static void
sheet_widget_radio_button_prep_sax_parser(SheetObject * so,GsfXMLIn * xin,xmlChar const ** attrs,GnmConventions const * convs)3059 sheet_widget_radio_button_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3060 xmlChar const **attrs,
3061 GnmConventions const *convs)
3062 {
3063 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3064 const char *valstr = NULL;
3065 int value_type = 0;
3066
3067 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
3068 if (attr_eq (attrs[0], "Label")) {
3069 g_free (swrb->label);
3070 swrb->label = g_strdup (CXML2C (attrs[1]));
3071 } else if (attr_eq (attrs[0], "Value")) {
3072 valstr = CXML2C (attrs[1]);
3073 } else if (gnm_xml_attr_bool (attrs, "Active", &swrb->active) ||
3074 gnm_xml_attr_int (attrs, "ValueType", &value_type) ||
3075 sax_read_dep (attrs, "Input", &swrb->dep, xin, convs))
3076 ; /* Nothing */
3077 }
3078
3079 value_release (swrb->value);
3080 swrb->value = NULL;
3081 if (valstr) {
3082 swrb->value = value_type
3083 ? value_new_from_string (value_type, valstr, NULL, FALSE)
3084 : format_match (valstr, NULL, NULL);
3085 }
3086 if (!swrb->value)
3087 swrb->value = value_new_empty ();
3088 }
3089
3090 void
sheet_widget_radio_button_set_link(SheetObject * so,GnmExprTop const * texpr)3091 sheet_widget_radio_button_set_link (SheetObject *so, GnmExprTop const *texpr)
3092 {
3093 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3094 dependent_set_expr (&swrb->dep, texpr);
3095 if (texpr && swrb->dep.sheet)
3096 dependent_link (&swrb->dep);
3097 }
3098
3099 GnmExprTop const *
sheet_widget_radio_button_get_link(SheetObject * so)3100 sheet_widget_radio_button_get_link (SheetObject *so)
3101 {
3102 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3103 GnmExprTop const *texpr = swrb->dep.texpr;
3104
3105 if (texpr)
3106 gnm_expr_top_ref (texpr);
3107
3108 return texpr;
3109 }
3110
3111 void
sheet_widget_radio_button_set_label(SheetObject * so,char const * str)3112 sheet_widget_radio_button_set_label (SheetObject *so, char const *str)
3113 {
3114 GList *list;
3115 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3116 char *new_label;
3117
3118 if (go_str_compare (str, swrb->label) == 0)
3119 return;
3120
3121 new_label = g_strdup (str);
3122 g_free (swrb->label);
3123 swrb->label = new_label;
3124
3125 for (list = swrb->sow.so.realized_list; list; list = list->next) {
3126 SheetObjectView *view = list->data;
3127 GocWidget *item = get_goc_widget (view);
3128 gtk_button_set_label (GTK_BUTTON (item->widget), swrb->label);
3129 }
3130 }
3131
3132
3133 typedef struct {
3134 GtkWidget *dialog;
3135 GnmExprEntry *expression;
3136 GtkWidget *label, *value;
3137
3138 char *old_label;
3139 GnmValue *old_value;
3140 GtkWidget *old_focus;
3141
3142 WBCGtk *wbcg;
3143 SheetWidgetRadioButton *swrb;
3144 Sheet *sheet;
3145 } RadioButtonConfigState;
3146
3147 static void
cb_radio_button_set_focus(G_GNUC_UNUSED GtkWidget * window,GtkWidget * focus_widget,RadioButtonConfigState * state)3148 cb_radio_button_set_focus (G_GNUC_UNUSED GtkWidget *window, GtkWidget *focus_widget,
3149 RadioButtonConfigState *state)
3150 {
3151 GtkWidget *ofp;
3152
3153 /* Note: half of the set-focus action is handle by the default
3154 * callback installed by wbc_gtk_attach_guru */
3155
3156 ofp = state->old_focus
3157 ? gtk_widget_get_parent (state->old_focus)
3158 : NULL;
3159
3160 /* Force an update of the content in case it needs tweaking (eg make it
3161 * absolute) */
3162 if (ofp && GNM_EXPR_ENTRY_IS (ofp)) {
3163 GnmParsePos pp;
3164 GnmExprTop const *texpr = gnm_expr_entry_parse (
3165 GNM_EXPR_ENTRY (ofp),
3166 parse_pos_init_sheet (&pp, state->sheet),
3167 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3168 if (texpr != NULL)
3169 gnm_expr_top_unref (texpr);
3170 }
3171 state->old_focus = focus_widget;
3172 }
3173
3174 static void
cb_radio_button_config_destroy(RadioButtonConfigState * state)3175 cb_radio_button_config_destroy (RadioButtonConfigState *state)
3176 {
3177 g_return_if_fail (state != NULL);
3178
3179 g_free (state->old_label);
3180 state->old_label = NULL;
3181
3182 value_release (state->old_value);
3183 state->old_value = NULL;
3184
3185 state->dialog = NULL;
3186
3187 g_free (state);
3188 }
3189
3190 static GnmValue *
so_parse_value(SheetObject * so,const char * s)3191 so_parse_value (SheetObject *so, const char *s)
3192 {
3193 Sheet *sheet = so->sheet;
3194 return format_match (s, NULL, sheet_date_conv (sheet));
3195 }
3196
3197 static void
cb_radio_button_config_ok_clicked(G_GNUC_UNUSED GtkWidget * button,RadioButtonConfigState * state)3198 cb_radio_button_config_ok_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3199 {
3200 SheetObject *so = GNM_SO (state->swrb);
3201 GnmParsePos pp;
3202 GnmExprTop const *texpr = gnm_expr_entry_parse
3203 (state->expression,
3204 parse_pos_init_sheet (&pp, so->sheet),
3205 NULL, FALSE, GNM_EXPR_PARSE_DEFAULT);
3206 gchar const *text = gtk_entry_get_text (GTK_ENTRY (state->label));
3207 gchar const *val = gtk_entry_get_text (GTK_ENTRY (state->value));
3208 GnmValue *new_val = so_parse_value (so, val);
3209
3210 cmd_so_set_radio_button (GNM_WBC (state->wbcg), so,
3211 texpr,
3212 g_strdup (state->old_label), g_strdup (text),
3213 value_dup (state->old_value), new_val);
3214
3215 gtk_widget_destroy (state->dialog);
3216 }
3217
3218 static void
cb_radio_button_config_cancel_clicked(G_GNUC_UNUSED GtkWidget * button,RadioButtonConfigState * state)3219 cb_radio_button_config_cancel_clicked (G_GNUC_UNUSED GtkWidget *button, RadioButtonConfigState *state)
3220 {
3221 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3222 state->old_label);
3223 sheet_widget_radio_button_set_value (GNM_SO (state->swrb),
3224 state->old_value);
3225 gtk_widget_destroy (state->dialog);
3226 }
3227
3228 static void
cb_radio_button_label_changed(GtkEntry * entry,RadioButtonConfigState * state)3229 cb_radio_button_label_changed (GtkEntry *entry, RadioButtonConfigState *state)
3230 {
3231 sheet_widget_radio_button_set_label (GNM_SO (state->swrb),
3232 gtk_entry_get_text (entry));
3233 }
3234
3235 static void
cb_radio_button_value_changed(GtkEntry * entry,RadioButtonConfigState * state)3236 cb_radio_button_value_changed (GtkEntry *entry, RadioButtonConfigState *state)
3237 {
3238 const char *text = gtk_entry_get_text (entry);
3239 SheetObject *so = GNM_SO (state->swrb);
3240 GnmValue *val = so_parse_value (so, text);
3241
3242 sheet_widget_radio_button_set_value (so, val);
3243 value_release (val);
3244 }
3245
3246 static void
sheet_widget_radio_button_user_config(SheetObject * so,SheetControl * sc)3247 sheet_widget_radio_button_user_config (SheetObject *so, SheetControl *sc)
3248 {
3249 SheetWidgetRadioButton *swrb = GNM_SOW_RADIO_BUTTON (so);
3250 WBCGtk *wbcg = scg_wbcg (GNM_SCG (sc));
3251 RadioButtonConfigState *state;
3252 GtkWidget *grid;
3253 GString *valstr;
3254 GtkBuilder *gui;
3255
3256 g_return_if_fail (swrb != NULL);
3257
3258 /* Only pop up one copy per workbook */
3259 if (gnm_dialog_raise_if_exists (wbcg, SHEET_OBJECT_CONFIG_KEY))
3260 return;
3261
3262 gui = gnm_gtk_builder_load ("res:ui/so-radiobutton.ui", NULL, GO_CMD_CONTEXT (wbcg));
3263 if (!gui)
3264 return;
3265 state = g_new (RadioButtonConfigState, 1);
3266 state->swrb = swrb;
3267 state->wbcg = wbcg;
3268 state->sheet = sc_sheet (sc);
3269 state->old_focus = NULL;
3270 state->old_label = g_strdup (swrb->label);
3271 state->old_value = value_dup (swrb->value);
3272 state->dialog = go_gtk_builder_get_widget (gui, "SO-Radiobutton");
3273
3274 grid = go_gtk_builder_get_widget (gui, "main-grid");
3275
3276 state->expression = gnm_expr_entry_new (wbcg, TRUE);
3277 gnm_expr_entry_set_flags (state->expression,
3278 GNM_EE_FORCE_ABS_REF | GNM_EE_SHEET_OPTIONAL | GNM_EE_SINGLE_RANGE,
3279 GNM_EE_MASK);
3280 gnm_expr_entry_load_from_dep (state->expression, &swrb->dep);
3281 go_atk_setup_label (go_gtk_builder_get_widget (gui, "label_linkto"),
3282 GTK_WIDGET (state->expression));
3283 gtk_grid_attach (GTK_GRID (grid),
3284 GTK_WIDGET (state->expression), 1, 0, 1, 1);
3285 gtk_widget_show (GTK_WIDGET (state->expression));
3286
3287 state->label = go_gtk_builder_get_widget (gui, "label_entry");
3288 gtk_entry_set_text (GTK_ENTRY (state->label), swrb->label);
3289 gtk_editable_select_region (GTK_EDITABLE(state->label), 0, -1);
3290 state->value = go_gtk_builder_get_widget (gui, "value_entry");
3291
3292 valstr = g_string_new (NULL);
3293 value_get_as_gstring (swrb->value, valstr, so->sheet->convs);
3294 gtk_entry_set_text (GTK_ENTRY (state->value), valstr->str);
3295 g_string_free (valstr, TRUE);
3296
3297 gnm_editable_enters (GTK_WINDOW (state->dialog),
3298 GTK_WIDGET (state->expression));
3299 gnm_editable_enters (GTK_WINDOW (state->dialog),
3300 GTK_WIDGET (state->label));
3301 gnm_editable_enters (GTK_WINDOW (state->dialog),
3302 GTK_WIDGET (state->value));
3303
3304 g_signal_connect (G_OBJECT (state->label),
3305 "changed",
3306 G_CALLBACK (cb_radio_button_label_changed), state);
3307 g_signal_connect (G_OBJECT (state->value),
3308 "changed",
3309 G_CALLBACK (cb_radio_button_value_changed), state);
3310 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "ok_button")),
3311 "clicked",
3312 G_CALLBACK (cb_radio_button_config_ok_clicked), state);
3313 g_signal_connect (G_OBJECT (go_gtk_builder_get_widget (gui, "cancel_button")),
3314 "clicked",
3315 G_CALLBACK (cb_radio_button_config_cancel_clicked), state);
3316
3317 gnm_init_help_button (
3318 go_gtk_builder_get_widget (gui, "help_button"),
3319 GNUMERIC_HELP_LINK_SO_RADIO_BUTTON);
3320
3321 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
3322 SHEET_OBJECT_CONFIG_KEY);
3323
3324 wbc_gtk_attach_guru (state->wbcg, state->dialog);
3325 g_object_set_data_full (G_OBJECT (state->dialog),
3326 "state", state, (GDestroyNotify) cb_radio_button_config_destroy);
3327 g_object_unref (gui);
3328
3329 /* Note: half of the set-focus action is handle by the default */
3330 /* callback installed by wbc_gtk_attach_guru */
3331 g_signal_connect (G_OBJECT (state->dialog), "set-focus",
3332 G_CALLBACK (cb_radio_button_set_focus), state);
3333
3334 gtk_widget_show (state->dialog);
3335 }
3336
3337 static void
sheet_widget_radio_button_draw_cairo(SheetObject const * so,cairo_t * cr,G_GNUC_UNUSED double width,double height)3338 sheet_widget_radio_button_draw_cairo (SheetObject const *so, cairo_t *cr,
3339 G_GNUC_UNUSED double width, double height)
3340 {
3341 SheetWidgetRadioButton const *swr = GNM_SOW_RADIO_BUTTON (so);
3342 double halfheight = height/2;
3343 double dx = 8., dxh, pm;
3344 int pw, ph;
3345
3346 pm = MIN (height - 2, width - 12);
3347 if (dx > pm)
3348 dx = MAX (pm, 3);
3349 dxh = dx/2;
3350
3351 cairo_save (cr);
3352 cairo_set_line_width (cr, 0.5);
3353 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
3354
3355 cairo_new_path (cr);
3356 cairo_move_to (cr, dxh + dx, halfheight);
3357 cairo_arc (cr, dx, halfheight, dxh, 0., 2*M_PI);
3358 cairo_close_path (cr);
3359 cairo_fill_preserve (cr);
3360 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
3361 cairo_stroke (cr);
3362
3363 if (swr->active) {
3364 cairo_new_path (cr);
3365 cairo_move_to (cr, dx + dxh/2 + 0.5, halfheight);
3366 cairo_arc (cr, dx, halfheight, dxh/2 + 0.5, 0., 2*M_PI);
3367 cairo_close_path (cr);
3368 cairo_fill (cr);
3369 }
3370
3371 cairo_move_to (cr, 2 * dx, halfheight);
3372
3373 pw = width - 2 * dx;
3374 ph = height;
3375
3376 draw_cairo_text (cr, swr->label, &pw, &ph, TRUE, FALSE, TRUE, 0, TRUE);
3377
3378 cairo_new_path (cr);
3379 cairo_restore (cr);
3380 }
3381
3382 SOW_MAKE_TYPE (radio_button, RadioButton,
3383 sheet_widget_radio_button_user_config,
3384 sheet_widget_radio_button_set_sheet,
3385 so_clear_sheet,
3386 sheet_widget_radio_button_foreach_dep,
3387 sheet_widget_radio_button_copy,
3388 sheet_widget_radio_button_write_xml_sax,
3389 sheet_widget_radio_button_prep_sax_parser,
3390 sheet_widget_radio_button_get_property,
3391 sheet_widget_radio_button_set_property,
3392 sheet_widget_radio_button_draw_cairo,
3393 {
3394 g_object_class_install_property
3395 (object_class, SOR_PROP_ACTIVE,
3396 g_param_spec_boolean ("active", NULL, NULL,
3397 FALSE,
3398 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3399 g_object_class_install_property
3400 (object_class, SOR_PROP_TEXT,
3401 g_param_spec_string ("text", NULL, NULL, NULL,
3402 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3403 g_object_class_install_property
3404 (object_class, SOR_PROP_MARKUP,
3405 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
3406 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3407 g_object_class_install_property
3408 (object_class, SOR_PROP_VALUE,
3409 g_param_spec_boxed ("value", NULL, NULL,
3410 gnm_value_get_type (),
3411 GSF_PARAM_STATIC | G_PARAM_READWRITE));
3412 })
3413
3414 /****************************************************************************/
3415
3416 #define GNM_SOW_LIST_BASE_TYPE (sheet_widget_list_base_get_type ())
3417 #define GNM_SOW_LIST_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNM_SOW_LIST_BASE_TYPE, SheetWidgetListBase))
3418 #define DEP_TO_LIST_BASE_CONTENT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, content_dep))
3419 #define DEP_TO_LIST_BASE_OUTPUT(d_ptr) (SheetWidgetListBase *)(((char *)d_ptr) - G_STRUCT_OFFSET(SheetWidgetListBase, output_dep))
3420
3421 typedef struct {
3422 SheetObjectWidget sow;
3423
3424 GnmDependent content_dep; /* content of the list */
3425 GnmDependent output_dep; /* selected element */
3426
3427 GtkTreeModel *model;
3428 int selection;
3429 gboolean result_as_index;
3430 } SheetWidgetListBase;
3431 typedef struct {
3432 SheetObjectWidgetClass base;
3433
3434 void (*model_changed) (SheetWidgetListBase *list);
3435 void (*selection_changed) (SheetWidgetListBase *list);
3436 } SheetWidgetListBaseClass;
3437
3438 enum {
3439 LIST_BASE_MODEL_CHANGED,
3440 LIST_BASE_SELECTION_CHANGED,
3441 LIST_BASE_LAST_SIGNAL
3442 };
3443
3444 static guint list_base_signals [LIST_BASE_LAST_SIGNAL] = { 0 };
3445 static GType sheet_widget_list_base_get_type (void);
3446
3447 static void
sheet_widget_list_base_set_selection(SheetWidgetListBase * swl,int selection,WorkbookControl * wbc)3448 sheet_widget_list_base_set_selection (SheetWidgetListBase *swl, int selection,
3449 WorkbookControl *wbc)
3450 {
3451 GnmCellRef ref;
3452
3453 if (selection >= 0 && swl->model != NULL) {
3454 int n = gtk_tree_model_iter_n_children (swl->model, NULL);
3455 if (selection > n)
3456 selection = n;
3457 } else
3458 selection = 0;
3459
3460 if (swl->selection != selection) {
3461 swl->selection = selection;
3462 if (NULL!= wbc &&
3463 so_get_ref (GNM_SO (swl), &ref, TRUE) != NULL) {
3464 GnmValue *v;
3465 if (swl->result_as_index)
3466 v = value_new_int (swl->selection);
3467 else if (selection != 0) {
3468 GtkTreeIter iter;
3469 char *content;
3470 gtk_tree_model_iter_nth_child
3471 (swl->model, &iter, NULL, selection - 1);
3472 gtk_tree_model_get (swl->model, &iter,
3473 0, &content, -1);
3474 v = value_new_string_nocopy (content);
3475 } else
3476 v = value_new_string ("");
3477 cmd_so_set_value (wbc, _("Clicking in list"), &ref, v,
3478 sheet_object_get_sheet (GNM_SO (swl)));
3479 }
3480 g_signal_emit (G_OBJECT (swl),
3481 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3482 }
3483 }
3484
3485 static void
sheet_widget_list_base_set_selection_value(SheetWidgetListBase * swl,GnmValue * v)3486 sheet_widget_list_base_set_selection_value (SheetWidgetListBase *swl, GnmValue *v)
3487 {
3488 GtkTreeIter iter;
3489 int selection = 0, i = 1;
3490
3491 if (swl->model != NULL && gtk_tree_model_get_iter_first (swl->model, &iter)) {
3492 char *str = value_get_as_string (v);
3493 do {
3494 char *content;
3495 gboolean match;
3496 gtk_tree_model_get (swl->model, &iter,
3497 0, &content, -1);
3498 match = 0 == g_ascii_strcasecmp (str, content);
3499 g_free (content);
3500 if (match) {
3501 selection = i;
3502 break;
3503 }
3504 i++;
3505 } while (gtk_tree_model_iter_next (swl->model, &iter));
3506 g_free (str);
3507 }
3508
3509 if (swl->selection != selection) {
3510 swl->selection = selection;
3511 g_signal_emit (G_OBJECT (swl),
3512 list_base_signals [LIST_BASE_SELECTION_CHANGED], 0);
3513 }
3514 }
3515
3516 static void
list_output_eval(GnmDependent * dep)3517 list_output_eval (GnmDependent *dep)
3518 {
3519 GnmEvalPos pos;
3520 GnmValue *v = gnm_expr_top_eval (dep->texpr,
3521 eval_pos_init_dep (&pos, dep),
3522 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
3523 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_OUTPUT (dep);
3524
3525 if (swl->result_as_index)
3526 sheet_widget_list_base_set_selection
3527 (swl, floor (value_get_as_float (v)), NULL);
3528 else
3529 sheet_widget_list_base_set_selection_value (swl, v);
3530 value_release (v);
3531 }
3532
3533 static void
list_output_debug_name(GnmDependent const * dep,GString * target)3534 list_output_debug_name (GnmDependent const *dep, GString *target)
3535 {
3536 g_string_append_printf (target, "ListOutput%p", (void *)dep);
3537 }
3538
3539 static DEPENDENT_MAKE_TYPE (list_output, .eval = list_output_eval, .debug_name = list_output_debug_name)
3540
3541 /*-----------*/
3542 static GnmValue *
cb_collect(GnmValueIter const * iter,GtkListStore * model)3543 cb_collect (GnmValueIter const *iter, GtkListStore *model)
3544 {
3545 GtkTreeIter list_iter;
3546
3547 gtk_list_store_append (model, &list_iter);
3548 if (NULL != iter->v) {
3549 GOFormat const *fmt = (NULL != iter->cell_iter)
3550 ? gnm_cell_get_format (iter->cell_iter->cell) : NULL;
3551 char *label = format_value (fmt, iter->v, -1, NULL);
3552 gtk_list_store_set (model, &list_iter, 0, label, -1);
3553 g_free (label);
3554 } else
3555 gtk_list_store_set (model, &list_iter, 0, "", -1);
3556
3557 return NULL;
3558 }
3559 static void
list_content_eval(GnmDependent * dep)3560 list_content_eval (GnmDependent *dep)
3561 {
3562 SheetWidgetListBase *swl = DEP_TO_LIST_BASE_CONTENT (dep);
3563 GnmEvalPos ep;
3564 GnmValue *v = NULL;
3565 GtkListStore *model;
3566
3567 if (dep->texpr != NULL) {
3568 v = gnm_expr_top_eval (dep->texpr,
3569 eval_pos_init_dep (&ep, dep),
3570 GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
3571 GNM_EXPR_EVAL_PERMIT_EMPTY);
3572 }
3573 model = gtk_list_store_new (1, G_TYPE_STRING);
3574 if (v) {
3575 value_area_foreach (v, &ep, CELL_ITER_ALL,
3576 (GnmValueIterFunc) cb_collect, model);
3577 value_release (v);
3578 }
3579
3580 if (NULL != swl->model)
3581 g_object_unref (swl->model);
3582 swl->model = GTK_TREE_MODEL (model);
3583 g_signal_emit (G_OBJECT (swl), list_base_signals [LIST_BASE_MODEL_CHANGED], 0);
3584 }
3585
3586 static void
list_content_debug_name(GnmDependent const * dep,GString * target)3587 list_content_debug_name (GnmDependent const *dep, GString *target)
3588 {
3589 g_string_append_printf (target, "ListContent%p", (void *)dep);
3590 }
3591
3592 static DEPENDENT_MAKE_TYPE (list_content, .eval = list_content_eval, .debug_name = list_content_debug_name)
3593
3594 /*-----------*/
3595
3596 static void
sheet_widget_list_base_init(SheetObjectWidget * sow)3597 sheet_widget_list_base_init (SheetObjectWidget *sow)
3598 {
3599 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3600 SheetObject *so = GNM_SO (sow);
3601
3602 so->flags &= ~SHEET_OBJECT_PRINT;
3603
3604 swl->content_dep.sheet = NULL;
3605 swl->content_dep.flags = list_content_get_dep_type ();
3606 swl->content_dep.texpr = NULL;
3607
3608 swl->output_dep.sheet = NULL;
3609 swl->output_dep.flags = list_output_get_dep_type ();
3610 swl->output_dep.texpr = NULL;
3611
3612 swl->model = NULL;
3613 swl->selection = 0;
3614 swl->result_as_index = TRUE;
3615 }
3616
3617 static void
sheet_widget_list_base_finalize(GObject * obj)3618 sheet_widget_list_base_finalize (GObject *obj)
3619 {
3620 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (obj);
3621 dependent_set_expr (&swl->content_dep, NULL);
3622 dependent_set_expr (&swl->output_dep, NULL);
3623 if (swl->model != NULL) {
3624 g_object_unref (swl->model);
3625 swl->model = NULL;
3626 }
3627 sheet_object_widget_class->finalize (obj);
3628 }
3629
3630 static void
sheet_widget_list_base_user_config(SheetObject * so,SheetControl * sc)3631 sheet_widget_list_base_user_config (SheetObject *so, SheetControl *sc)
3632 {
3633 dialog_so_list (scg_wbcg (GNM_SCG (sc)), G_OBJECT (so));
3634 }
3635 static gboolean
sheet_widget_list_base_set_sheet(SheetObject * so,Sheet * sheet)3636 sheet_widget_list_base_set_sheet (SheetObject *so, Sheet *sheet)
3637 {
3638 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3639
3640 g_return_val_if_fail (swl != NULL, TRUE);
3641 g_return_val_if_fail (swl->content_dep.sheet == NULL, TRUE);
3642 g_return_val_if_fail (swl->output_dep.sheet == NULL, TRUE);
3643
3644 dependent_set_sheet (&swl->content_dep, sheet);
3645 dependent_set_sheet (&swl->output_dep, sheet);
3646
3647 list_content_eval (&swl->content_dep); /* populate the list */
3648
3649 return FALSE;
3650 }
3651
3652 static void
sheet_widget_list_base_foreach_dep(SheetObject * so,SheetObjectForeachDepFunc func,gpointer user)3653 sheet_widget_list_base_foreach_dep (SheetObject *so,
3654 SheetObjectForeachDepFunc func,
3655 gpointer user)
3656 {
3657 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3658 func (&swl->content_dep, so, user);
3659 func (&swl->output_dep, so, user);
3660 }
3661
3662 static void
sheet_widget_list_base_write_xml_sax(SheetObject const * so,GsfXMLOut * output,GnmConventions const * convs)3663 sheet_widget_list_base_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
3664 GnmConventions const *convs)
3665 {
3666 SheetWidgetListBase const *swl = GNM_SOW_LIST_BASE (so);
3667 sax_write_dep (output, &swl->content_dep, "Content", convs);
3668 sax_write_dep (output, &swl->output_dep, "Output", convs);
3669 gsf_xml_out_add_int (output, "OutputAsIndex", swl->result_as_index ? 1 : 0);
3670 }
3671
3672 static void
sheet_widget_list_base_prep_sax_parser(SheetObject * so,GsfXMLIn * xin,xmlChar const ** attrs,GnmConventions const * convs)3673 sheet_widget_list_base_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
3674 xmlChar const **attrs,
3675 GnmConventions const *convs)
3676 {
3677 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3678
3679 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
3680 if (sax_read_dep (attrs, "Content", &swl->content_dep, xin, convs))
3681 ;
3682 else if (sax_read_dep (attrs, "Output", &swl->output_dep, xin, convs))
3683 ;
3684 else if (gnm_xml_attr_bool (attrs, "OutputAsIndex", &swl->result_as_index))
3685 ;
3686 }
3687
3688 static GtkWidget *
sheet_widget_list_base_create_widget(G_GNUC_UNUSED SheetObjectWidget * sow)3689 sheet_widget_list_base_create_widget (G_GNUC_UNUSED SheetObjectWidget *sow)
3690 {
3691 g_warning("ERROR: sheet_widget_list_base_create_widget SHOULD NEVER BE CALLED (but it has been)!\n");
3692 return gtk_frame_new ("invisiwidget(WARNING: I AM A BUG!)");
3693 }
3694
3695 SOW_MAKE_TYPE (list_base, ListBase,
3696 sheet_widget_list_base_user_config,
3697 sheet_widget_list_base_set_sheet,
3698 so_clear_sheet,
3699 sheet_widget_list_base_foreach_dep,
3700 NULL,
3701 sheet_widget_list_base_write_xml_sax,
3702 sheet_widget_list_base_prep_sax_parser,
3703 NULL,
3704 NULL,
3705 sheet_widget_draw_cairo,
3706 {
3707 list_base_signals[LIST_BASE_MODEL_CHANGED] = g_signal_new ("model-changed",
3708 GNM_SOW_LIST_BASE_TYPE,
3709 G_SIGNAL_RUN_LAST,
3710 G_STRUCT_OFFSET (SheetWidgetListBaseClass, model_changed),
3711 NULL, NULL,
3712 g_cclosure_marshal_VOID__VOID,
3713 G_TYPE_NONE, 0);
3714 list_base_signals[LIST_BASE_SELECTION_CHANGED] = g_signal_new ("selection-changed",
3715 GNM_SOW_LIST_BASE_TYPE,
3716 G_SIGNAL_RUN_LAST,
3717 G_STRUCT_OFFSET (SheetWidgetListBaseClass, selection_changed),
3718 NULL, NULL,
3719 g_cclosure_marshal_VOID__VOID,
3720 G_TYPE_NONE, 0);
3721 })
3722
3723 void
sheet_widget_list_base_set_links(SheetObject * so,GnmExprTop const * output,GnmExprTop const * content)3724 sheet_widget_list_base_set_links (SheetObject *so,
3725 GnmExprTop const *output,
3726 GnmExprTop const *content)
3727 {
3728 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3729 dependent_set_expr (&swl->output_dep, output);
3730 if (output && swl->output_dep.sheet)
3731 dependent_link (&swl->output_dep);
3732 dependent_set_expr (&swl->content_dep, content);
3733 if (content && swl->content_dep.sheet) {
3734 dependent_link (&swl->content_dep);
3735 list_content_eval (&swl->content_dep); /* populate the list */
3736 }
3737 }
3738
3739 GnmExprTop const *
sheet_widget_list_base_get_result_link(SheetObject const * so)3740 sheet_widget_list_base_get_result_link (SheetObject const *so)
3741 {
3742 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3743 GnmExprTop const *texpr = swl->output_dep.texpr;
3744
3745 if (texpr)
3746 gnm_expr_top_ref (texpr);
3747
3748 return texpr;
3749 }
3750
3751 GnmExprTop const *
sheet_widget_list_base_get_content_link(SheetObject const * so)3752 sheet_widget_list_base_get_content_link (SheetObject const *so)
3753 {
3754 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3755 GnmExprTop const *texpr = swl->content_dep.texpr;
3756
3757 if (texpr)
3758 gnm_expr_top_ref (texpr);
3759
3760 return texpr;
3761 }
3762
3763 gboolean
sheet_widget_list_base_result_type_is_index(SheetObject const * so)3764 sheet_widget_list_base_result_type_is_index (SheetObject const *so)
3765 {
3766 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3767
3768 return swl->result_as_index;
3769 }
3770
3771 void
sheet_widget_list_base_set_result_type(SheetObject * so,gboolean as_index)3772 sheet_widget_list_base_set_result_type (SheetObject *so, gboolean as_index)
3773 {
3774 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3775
3776 if (swl->result_as_index == as_index)
3777 return;
3778
3779 swl->result_as_index = as_index;
3780
3781 }
3782
3783 /**
3784 * sheet_widget_list_base_get_adjustment:
3785 * @so: #SheetObject
3786 *
3787 * Note: allocates a new adjustment.
3788 * Returns: (transfer full): the newly created #GtkAdjustment.
3789 **/
3790 GtkAdjustment *
sheet_widget_list_base_get_adjustment(SheetObject * so)3791 sheet_widget_list_base_get_adjustment (SheetObject *so)
3792 {
3793 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3794 GtkAdjustment *adj;
3795
3796 g_return_val_if_fail (swl, NULL);
3797
3798 adj = (GtkAdjustment*)gtk_adjustment_new
3799 (swl->selection,
3800 1,
3801 1 + gtk_tree_model_iter_n_children (swl->model, NULL),
3802 1,
3803 5,
3804 5);
3805 g_object_ref_sink (adj);
3806
3807 return adj;
3808 }
3809
3810 /****************************************************************************/
3811
3812 #define GNM_SOW_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_LIST_TYPE, SheetWidgetList))
3813
3814 typedef SheetWidgetListBase SheetWidgetList;
3815 typedef SheetWidgetListBaseClass SheetWidgetListClass;
3816
3817 static void
cb_list_selection_changed(SheetWidgetListBase * swl,GtkTreeSelection * selection)3818 cb_list_selection_changed (SheetWidgetListBase *swl,
3819 GtkTreeSelection *selection)
3820 {
3821 if (swl->selection > 0) {
3822 GtkTreePath *path = gtk_tree_path_new_from_indices (swl->selection-1, -1);
3823 gtk_tree_selection_select_path (selection, path);
3824 gtk_tree_path_free (path);
3825 } else
3826 gtk_tree_selection_unselect_all (selection);
3827 }
3828
3829 static void
cb_list_model_changed(SheetWidgetListBase * swl,GtkTreeView * list)3830 cb_list_model_changed (SheetWidgetListBase *swl, GtkTreeView *list)
3831 {
3832 int old_selection = swl->selection;
3833 swl->selection = -1;
3834 gtk_tree_view_set_model (GTK_TREE_VIEW (list), swl->model);
3835 sheet_widget_list_base_set_selection (swl, old_selection, NULL);
3836 }
3837 static void
cb_selection_changed(GtkTreeSelection * selection,SheetWidgetListBase * swl)3838 cb_selection_changed (GtkTreeSelection *selection,
3839 SheetWidgetListBase *swl)
3840 {
3841 GtkWidget *view = (GtkWidget *)gtk_tree_selection_get_tree_view (selection);
3842 GnmSimpleCanvas *scanvas = GNM_SIMPLE_CANVAS (gtk_widget_get_ancestor (view, GNM_SIMPLE_CANVAS_TYPE));
3843 GtkTreeModel *model;
3844 GtkTreeIter iter;
3845 int pos = 0;
3846 if (swl->selection != -1) {
3847 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3848 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
3849 if (NULL != path) {
3850 pos = *gtk_tree_path_get_indices (path) + 1;
3851 gtk_tree_path_free (path);
3852 }
3853 }
3854 sheet_widget_list_base_set_selection
3855 (swl, pos, scg_wbc (scanvas->scg));
3856 }
3857 }
3858
3859 static GtkWidget *
sheet_widget_list_create_widget(SheetObjectWidget * sow)3860 sheet_widget_list_create_widget (SheetObjectWidget *sow)
3861 {
3862 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
3863 GtkTreeSelection *selection;
3864 GtkTreeIter iter;
3865 GtkWidget *list = gtk_tree_view_new_with_model (swl->model);
3866 GtkWidget *sw = gtk_scrolled_window_new (
3867 gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (list)),
3868 gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (list)));
3869 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
3870 GTK_POLICY_AUTOMATIC,
3871 GTK_POLICY_ALWAYS);
3872 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
3873 gtk_tree_view_append_column (GTK_TREE_VIEW (list),
3874 gtk_tree_view_column_new_with_attributes ("ID",
3875 gtk_cell_renderer_text_new (), "text", 0,
3876 NULL));
3877
3878 gtk_container_add (GTK_CONTAINER (sw), list);
3879
3880 g_signal_connect_object (G_OBJECT (swl), "model-changed",
3881 G_CALLBACK (cb_list_model_changed), list, 0);
3882
3883 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
3884 if ((swl->model != NULL) && (swl->selection > 0) &&
3885 gtk_tree_model_iter_nth_child (swl->model, &iter, NULL, swl->selection - 1))
3886 gtk_tree_selection_select_iter (selection, &iter);
3887 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
3888 G_CALLBACK (cb_list_selection_changed), selection, 0);
3889 g_signal_connect (selection, "changed",
3890 G_CALLBACK (cb_selection_changed), swl);
3891 return sw;
3892 }
3893
3894 static void
sheet_widget_list_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)3895 sheet_widget_list_draw_cairo (SheetObject const *so, cairo_t *cr,
3896 double width, double height)
3897 {
3898 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
3899
3900 cairo_save (cr);
3901 cairo_set_line_width (cr, 0.5);
3902 cairo_set_source_rgb(cr, 0, 0, 0);
3903
3904 cairo_new_path (cr);
3905 cairo_move_to (cr, 0, 0);
3906 cairo_line_to (cr, width, 0);
3907 cairo_line_to (cr, width, height);
3908 cairo_line_to (cr, 0, height);
3909 cairo_close_path (cr);
3910 cairo_stroke (cr);
3911
3912 cairo_new_path (cr);
3913 cairo_move_to (cr, width - 10, 0);
3914 cairo_rel_line_to (cr, 0, height);
3915 cairo_stroke (cr);
3916
3917 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
3918
3919 cairo_new_path (cr);
3920 cairo_move_to (cr, width - 5 -3, height - 12);
3921 cairo_rel_line_to (cr, 6, 0);
3922 cairo_rel_line_to (cr, -3, 8);
3923 cairo_close_path (cr);
3924 cairo_fill (cr);
3925
3926 cairo_new_path (cr);
3927 cairo_move_to (cr, width - 5 -3, 12);
3928 cairo_rel_line_to (cr, 6, 0);
3929 cairo_rel_line_to (cr, -3, -8);
3930 cairo_close_path (cr);
3931 cairo_fill (cr);
3932
3933 if (swl->model != NULL) {
3934 GtkTreeIter iter;
3935 GString*str = g_string_new (NULL);
3936 int twidth = width, theight = height;
3937
3938
3939 cairo_new_path (cr);
3940 cairo_rectangle (cr, 2, 1, width - 2 - 12, height - 2);
3941 cairo_clip (cr);
3942 if (gtk_tree_model_get_iter_first (swl->model, &iter))
3943 do {
3944 char *astr = NULL, *newline;
3945 gtk_tree_model_get (swl->model, &iter, 0, &astr, -1);
3946 while (NULL != (newline = strchr (astr, '\n')))
3947 *newline = ' ';
3948 g_string_append (str, astr);
3949 g_string_append_c (str, '\n');
3950 g_free (astr);
3951 } while (gtk_tree_model_iter_next (swl->model, &iter));
3952
3953 cairo_translate (cr, 4., 2.);
3954
3955 draw_cairo_text (cr, str->str, &twidth, &theight, FALSE, FALSE, FALSE,
3956 swl->selection, FALSE);
3957
3958 g_string_free (str, TRUE);
3959 }
3960
3961 cairo_new_path (cr);
3962 cairo_restore (cr);
3963 }
3964
3965 static void
sheet_widget_list_class_init(SheetObjectWidgetClass * sow_class)3966 sheet_widget_list_class_init (SheetObjectWidgetClass *sow_class)
3967 {
3968 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
3969
3970 so_class->draw_cairo = &sheet_widget_list_draw_cairo;
3971 sow_class->create_widget = &sheet_widget_list_create_widget;
3972 }
3973
3974 GSF_CLASS (SheetWidgetList, sheet_widget_list,
3975 &sheet_widget_list_class_init, NULL,
3976 GNM_SOW_LIST_BASE_TYPE)
3977
3978 /****************************************************************************/
3979
3980 #define GNM_SOW_COMBO(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SOW_COMBO_TYPE, SheetWidgetCombo))
3981
3982 typedef SheetWidgetListBase SheetWidgetCombo;
3983 typedef SheetWidgetListBaseClass SheetWidgetComboClass;
3984
3985 static void
cb_combo_selection_changed(SheetWidgetListBase * swl,GtkComboBox * combo)3986 cb_combo_selection_changed (SheetWidgetListBase *swl,
3987 GtkComboBox *combo)
3988 {
3989 int pos = swl->selection - 1;
3990 if (pos < 0) {
3991 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo))), "");
3992 pos = -1;
3993 }
3994 gtk_combo_box_set_active (combo, pos);
3995 }
3996
3997 static void
cb_combo_model_changed(SheetWidgetListBase * swl,GtkComboBox * combo)3998 cb_combo_model_changed (SheetWidgetListBase *swl, GtkComboBox *combo)
3999 {
4000 gtk_combo_box_set_model (GTK_COMBO_BOX (combo), swl->model);
4001
4002 /* we cannot set this until we have a model,
4003 * but after that we cannot reset it */
4004 if (gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo)) < 0)
4005 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), 0);
4006
4007 /* force entry to reload */
4008 cb_combo_selection_changed (swl, combo);
4009 }
4010
4011 static void
cb_combo_changed(GtkComboBox * combo,SheetWidgetListBase * swl)4012 cb_combo_changed (GtkComboBox *combo, SheetWidgetListBase *swl)
4013 {
4014 int pos = gtk_combo_box_get_active (combo) + 1;
4015 sheet_widget_list_base_set_selection (swl, pos,
4016 widget_wbc (GTK_WIDGET (combo)));
4017 }
4018
4019 static GtkWidget *
sheet_widget_combo_create_widget(SheetObjectWidget * sow)4020 sheet_widget_combo_create_widget (SheetObjectWidget *sow)
4021 {
4022 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (sow);
4023 GtkWidget *widget = gtk_event_box_new (), *combo;
4024
4025 combo = gtk_combo_box_new_with_entry ();
4026 gtk_widget_set_can_focus (gtk_bin_get_child (GTK_BIN (combo)),
4027 FALSE);
4028 if (swl->model != NULL)
4029 g_object_set (G_OBJECT (combo),
4030 "model", swl->model,
4031 "entry-text-column", 0,
4032 "active", swl->selection - 1,
4033 NULL);
4034
4035 g_signal_connect_object (G_OBJECT (swl), "model-changed",
4036 G_CALLBACK (cb_combo_model_changed), combo, 0);
4037 g_signal_connect_object (G_OBJECT (swl), "selection-changed",
4038 G_CALLBACK (cb_combo_selection_changed), combo, 0);
4039 g_signal_connect (G_OBJECT (combo), "changed",
4040 G_CALLBACK (cb_combo_changed), swl);
4041
4042 gtk_container_add (GTK_CONTAINER (widget), combo);
4043 gtk_event_box_set_visible_window (GTK_EVENT_BOX (widget), FALSE);
4044 return widget;
4045 }
4046
4047 static void
sheet_widget_combo_draw_cairo(SheetObject const * so,cairo_t * cr,double width,double height)4048 sheet_widget_combo_draw_cairo (SheetObject const *so, cairo_t *cr,
4049 double width, double height)
4050 {
4051 SheetWidgetListBase *swl = GNM_SOW_LIST_BASE (so);
4052 double halfheight = height/2;
4053
4054 cairo_save (cr);
4055 cairo_set_line_width (cr, 0.5);
4056 cairo_set_source_rgb(cr, 0, 0, 0);
4057
4058 cairo_new_path (cr);
4059 cairo_move_to (cr, 0, 0);
4060 cairo_line_to (cr, width, 0);
4061 cairo_line_to (cr, width, height);
4062 cairo_line_to (cr, 0, height);
4063 cairo_close_path (cr);
4064 cairo_stroke (cr);
4065
4066 cairo_new_path (cr);
4067 cairo_move_to (cr, width - 10, 0);
4068 cairo_rel_line_to (cr, 0, height);
4069 cairo_stroke (cr);
4070
4071 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
4072
4073 cairo_new_path (cr);
4074 cairo_move_to (cr, width - 5 -3, halfheight - 4);
4075 cairo_rel_line_to (cr, 6, 0);
4076 cairo_rel_line_to (cr, -3, 8);
4077 cairo_close_path (cr);
4078 cairo_fill (cr);
4079
4080 cairo_set_source_rgb(cr, 0, 0, 0);
4081 cairo_move_to (cr, 4., halfheight);
4082
4083 if (swl->model != NULL) {
4084 GtkTreeIter iter;
4085 if (gtk_tree_model_iter_nth_child (swl->model, &iter, NULL,
4086 swl->selection - 1)) {
4087 char *str = NULL;
4088 gtk_tree_model_get (swl->model, &iter, 0, &str, -1);
4089 draw_cairo_text (cr, str, NULL, NULL, TRUE, FALSE, TRUE, 0, FALSE);
4090 g_free (str);
4091 }
4092 }
4093
4094 cairo_new_path (cr);
4095 cairo_restore (cr);
4096 }
4097
4098 static void
sheet_widget_combo_class_init(SheetObjectWidgetClass * sow_class)4099 sheet_widget_combo_class_init (SheetObjectWidgetClass *sow_class)
4100 {
4101 SheetObjectClass *so_class = GNM_SO_CLASS (sow_class);
4102
4103 so_class->draw_cairo = &sheet_widget_combo_draw_cairo;
4104 sow_class->create_widget = &sheet_widget_combo_create_widget;
4105 }
4106
4107 GSF_CLASS (SheetWidgetCombo, sheet_widget_combo,
4108 &sheet_widget_combo_class_init, NULL,
4109 GNM_SOW_LIST_BASE_TYPE)
4110
4111
4112
4113
4114
4115 /**************************************************************************/
4116
4117 /**
4118 * sheet_object_widget_register:
4119 *
4120 * Initialize the classes for the sheet-object-widgets. We need to initialize
4121 * them before we try loading a sheet that might contain sheet-object-widgets
4122 **/
4123 void
sheet_object_widget_register(void)4124 sheet_object_widget_register (void)
4125 {
4126 GNM_SOW_FRAME_TYPE;
4127 GNM_SOW_BUTTON_TYPE;
4128 GNM_SOW_SCROLLBAR_TYPE;
4129 GNM_SOW_CHECKBOX_TYPE;
4130 GNM_SOW_RADIO_BUTTON_TYPE;
4131 GNM_SOW_LIST_TYPE;
4132 GNM_SOW_COMBO_TYPE;
4133 GNM_SOW_SPIN_BUTTON_TYPE;
4134 GNM_SOW_SLIDER_TYPE;
4135 }
4136