1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * gnumeric-expr-entry.c: An entry widget specialized to handle expressions
8 * Jon Kåre Hellan (hellan@acm.org)
11 #include <gnumeric-config.h>
14 #include "gnumeric-expr-entry.h"
16 #include <wbc-gtk-impl.h>
17 #include <sheet-control-gui-priv.h>
19 #include <sheet-merge.h>
20 #include <parse-util.h>
26 #include <dependent.h>
28 #include <sheet-style.h>
30 #include <sheet-view.h>
31 #include <selection.h>
33 #include <gnm-format.h>
34 #include <number-match.h>
35 #include <gnm-datetime.h>
36 #include <gnumeric-conf.h>
37 #include <dead-kittens.h>
38 #include <dialogs/dialogs.h>
39 #include <goffice/goffice.h>
41 #include <gsf/gsf-impl-utils.h>
43 #include <gdk/gdkkeysyms.h>
46 #define UNICODE_LEFT_TRIANGLE "\xe2\x97\x80"
47 #define UNICODE_RIGHT_TRIANGLE "\xe2\x96\xb6"
48 #define UNICODE_CROSS_AND_SKULLBONES "\xe2\x98\xa0"
49 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
50 #define UNICODE_ELLIPSIS_VERT "\xe2\x8b\xae"
51 #define UNICODE_ARROW_UP "\xe2\x87\xa7"
52 #define UNICODE_CHECKMARK "\342\234\223"
54 #warning We should replace these token names with the correct values
64 #define TOKEN_UNMATCHED_APOSTROPHE INVALID_TOKEN
67 gnm_update_type_get_type (void)
69 static GType etype
= 0;
71 static const GEnumValue values
[] = {
72 { GNM_UPDATE_CONTINUOUS
, "GNM_UPDATE_CONTINUOUS", "continuous" },
73 { GNM_UPDATE_DISCONTINUOUS
, "GNM_UPDATE_DISCONTINUOUS", "discontinuous" },
74 { GNM_UPDATE_DELAYED
, "GNM_UPDATE_DELAYED", "delayed" },
77 etype
= g_enum_register_static (g_intern_static_string ("GnmUpdateType"), values
);
89 struct _GnmExprEntry
{
93 GtkWidget
*calendar_combo
;
94 gulong calendar_combo_changed
;
96 SheetControlGUI
*scg
; /* the source of the edit */
97 Sheet
*sheet
; /* from scg */
98 GnmParsePos pp
; /* from scg->sv */
99 WBCGtk
*wbcg
; /* from scg */
102 GnmExprEntryFlags flags
;
105 GnmUpdateType update_policy
;
106 guint update_timeout_id
;
108 gboolean is_cell_renderer
; /* as cell_editable */
109 gboolean editing_canceled
; /* as cell_editable */
110 gboolean ignore_changes
; /* internal mutex */
112 gboolean feedback_disabled
;
113 GnmLexerItem
*lexer_items
;
114 GnmExprTop
const *texpr
;
124 gboolean completion_se_valid
;
126 guint completion_start
;
127 guint completion_end
;
130 GOFormat
const *constant_format
;
133 typedef struct _GnmExprEntryClass
{
136 void (* update
) (GnmExprEntry
*gee
, gboolean user_requested_update
);
137 void (* changed
) (GnmExprEntry
*gee
);
138 void (* activate
) (GnmExprEntry
*gee
);
158 PROP_CONSTANT_FORMAT
,
159 PROP_EDITING_CANCELED
162 static guint signals
[LAST_SIGNAL
] = { 0 };
164 static void gee_set_value_double (GogDataEditor
*editor
, double val
,
165 GODateConventions
const *date_conv
);
169 static void gee_rangesel_reset (GnmExprEntry
*gee
);
170 static void gee_rangesel_update_text (GnmExprEntry
*gee
);
171 static void gee_detach_scg (GnmExprEntry
*gee
);
172 static void gee_remove_update_timer (GnmExprEntry
*range
);
173 static void cb_gee_notify_cursor_position (GnmExprEntry
*gee
);
175 static gboolean gee_debug
;
176 static GtkWidgetClass
*parent_class
= NULL
;
179 gee_is_editing (GnmExprEntry
*gee
)
181 return (gee
!= NULL
&& gee
->wbcg
!= NULL
&& wbcg_is_editing (gee
->wbcg
));
184 static GnmConventions
const *
185 gee_convs (const GnmExprEntry
*gee
)
187 return sheet_get_conventions (gee
->sheet
);
191 gee_force_abs_rel (GnmExprEntry
*gee
)
193 Rangesel
*rs
= &gee
->rangesel
;
194 rs
->is_valid
= FALSE
;
195 if ((gee
->flags
& GNM_EE_FORCE_ABS_REF
))
196 rs
->ref
.a
.col_relative
= rs
->ref
.b
.col_relative
=
197 rs
->ref
.a
.row_relative
= rs
->ref
.b
.row_relative
= FALSE
;
198 else if ((gee
->flags
& GNM_EE_FORCE_REL_REF
))
199 rs
->ref
.a
.col_relative
= rs
->ref
.b
.col_relative
=
200 rs
->ref
.a
.row_relative
= rs
->ref
.b
.row_relative
= TRUE
;
204 gee_rangesel_reset (GnmExprEntry
*gee
)
206 Rangesel
*rs
= &gee
->rangesel
;
210 memset (&rs
->ref
, 0, sizeof (rs
->ref
));
211 rs
->ref
.a
.col_relative
=
212 rs
->ref
.b
.col_relative
=
213 rs
->ref
.a
.row_relative
=
214 rs
->ref
.b
.row_relative
= ((gee
->flags
& (GNM_EE_FORCE_ABS_REF
|GNM_EE_DEFAULT_ABS_REF
)) == 0);
216 rs
->is_valid
= FALSE
;
220 gee_destroy (GtkWidget
*widget
)
222 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (widget
);
223 gee_remove_update_timer (gee
);
224 gee_detach_scg (gee
);
225 ((GtkWidgetClass
*)(parent_class
))->destroy (widget
);
229 cb_icon_clicked (GtkButton
*icon
,
232 GtkWidget
*toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (entry
));
234 /* TODO special-case GnmExprEntry being directly packed
235 * into a GtkWindow. Currently, we just use it in
236 * GtkDialogs so the current window child widget
237 * is never identical to the entry when it is
241 if (toplevel
!= NULL
&& gtk_widget_is_toplevel (toplevel
)) {
242 GtkWidget
*old_entry_parent
;
243 GtkWidget
*old_toplevel_child
;
244 GParamSpec
**container_props_pspec
;
245 GArray
*container_props
;
247 g_assert (GTK_IS_WINDOW (toplevel
));
249 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (icon
))) {
253 /* roll-up request */
255 old_toplevel_child
= gtk_bin_get_child (GTK_BIN (toplevel
));
256 g_assert (GTK_IS_WIDGET (old_toplevel_child
));
258 old_entry_parent
= gtk_widget_get_parent (GTK_WIDGET (entry
));
259 g_assert (GTK_IS_CONTAINER (old_entry_parent
));
261 g_object_set_data_full (G_OBJECT (entry
), "old_entry_parent",
262 g_object_ref (old_entry_parent
),
263 (GDestroyNotify
) g_object_unref
);
265 g_return_if_fail ((GtkWidget
*) entry
!= old_toplevel_child
);
267 g_object_set_data_full (G_OBJECT (entry
), "old_toplevel_child",
268 g_object_ref (old_toplevel_child
),
269 (GDestroyNotify
) g_object_unref
);
271 gtk_window_get_size (GTK_WINDOW (toplevel
), &width
, &height
);
272 g_object_set_data (G_OBJECT (entry
), "old_window_width", GUINT_TO_POINTER (width
));
273 g_object_set_data (G_OBJECT (entry
), "old_window_height", GUINT_TO_POINTER (height
));
274 g_object_set_data (G_OBJECT (entry
), "old_default",
275 gtk_window_get_default_widget (GTK_WINDOW (toplevel
)));
277 container_props
= NULL
;
279 container_props_pspec
= gtk_container_class_list_child_properties
280 (G_OBJECT_GET_CLASS (old_entry_parent
), &n
);
282 if (container_props_pspec
[0] != NULL
) {
285 container_props
= g_array_sized_new (FALSE
, TRUE
, sizeof (GValue
), n
);
287 for (ui
= 0; ui
< n
; ui
++) {
288 GValue value
= G_VALUE_INIT
;
289 g_value_init (&value
, G_PARAM_SPEC_VALUE_TYPE (container_props_pspec
[ui
]));
291 gtk_container_child_get_property (GTK_CONTAINER (old_entry_parent
), GTK_WIDGET (entry
),
292 g_param_spec_get_name (container_props_pspec
[ui
]),
294 g_array_append_val (container_props
, value
);
298 g_object_set_data_full (G_OBJECT (entry
), "container_props",
300 (GDestroyNotify
) g_array_unref
);
301 g_object_set_data_full (G_OBJECT (entry
), "container_props_pspec",
302 container_props_pspec
,
303 (GDestroyNotify
) g_free
);
305 gtk_container_remove (GTK_CONTAINER (toplevel
), old_toplevel_child
);
306 gtk_widget_reparent (GTK_WIDGET (entry
), toplevel
);
308 gtk_widget_grab_focus (GTK_WIDGET (entry
->entry
));
309 gtk_widget_set_can_default (GTK_WIDGET (icon
), TRUE
);
310 gtk_widget_grab_default (GTK_WIDGET (icon
));
312 gtk_window_resize (GTK_WINDOW (toplevel
), 1, 1);
316 gpointer default_widget
;
318 /* reset rolled-up window */
320 old_toplevel_child
= g_object_get_data (G_OBJECT (entry
), "old_toplevel_child");
321 g_assert (GTK_IS_WIDGET (old_toplevel_child
));
323 old_entry_parent
= g_object_get_data (G_OBJECT (entry
), "old_entry_parent");
324 g_assert (GTK_IS_CONTAINER (old_entry_parent
));
326 g_object_ref (entry
);
327 gtk_container_remove (GTK_CONTAINER (toplevel
), GTK_WIDGET (entry
));
328 gtk_container_add (GTK_CONTAINER (toplevel
), old_toplevel_child
);
329 gtk_container_add (GTK_CONTAINER (old_entry_parent
), GTK_WIDGET (entry
));
330 g_object_unref (entry
);
332 container_props
= g_object_get_data (G_OBJECT (entry
), "container_props");
333 container_props_pspec
= g_object_get_data (G_OBJECT (entry
), "container_props_pspec");
335 for (i
= 0; container_props_pspec
[i
] != NULL
; i
++) {
336 gtk_container_child_set_property (GTK_CONTAINER (old_entry_parent
), GTK_WIDGET (entry
),
337 g_param_spec_get_name (container_props_pspec
[i
]),
338 &g_array_index (container_props
, GValue
, i
));
341 gtk_window_resize (GTK_WINDOW (toplevel
),
342 GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry
), "old_window_width")),
343 GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (entry
), "old_window_height")));
344 default_widget
= g_object_get_data (G_OBJECT (entry
), "old_default");
345 if (default_widget
!= NULL
) {
346 gtk_window_set_default (GTK_WINDOW (toplevel
), GTK_WIDGET (default_widget
));
347 g_object_set_data (G_OBJECT (entry
), "old_default", NULL
);
350 g_object_set_data (G_OBJECT (entry
), "old_entry_parent", NULL
);
351 g_object_set_data (G_OBJECT (entry
), "old_toplevel_child", NULL
);
352 g_object_set_data (G_OBJECT (entry
), "container_props", NULL
);
353 g_object_set_data (G_OBJECT (entry
), "container_props_pspec", NULL
);
356 g_warning ("GnmExprEntry button was clicked, but entry has no toplevel parent.");
361 get_matched_value (GnmExprEntry
*gee
)
363 GODateConventions
const *date_conv
=
364 workbook_date_conv (gee
->sheet
->workbook
);
365 const char *text
= gnm_expr_entry_get_text (gee
);
367 return format_match_number (text
, gee
->constant_format
, date_conv
);
372 gee_update_calendar (GnmExprEntry
*gee
)
376 GODateConventions
const *date_conv
=
377 workbook_date_conv (gee
->sheet
->workbook
);
379 if (!gee
->calendar_combo
)
382 v
= get_matched_value (gee
);
386 if (datetime_value_to_g (&date
, v
, date_conv
)) {
387 g_signal_handler_block (gee
->calendar_combo
,
388 gee
->calendar_combo_changed
);
389 go_calendar_button_set_date
390 (GO_CALENDAR_BUTTON (gee
->calendar_combo
),
392 g_signal_handler_unblock (gee
->calendar_combo
,
393 gee
->calendar_combo_changed
);
400 cb_calendar_changed (GOCalendarButton
*calb
, GnmExprEntry
*gee
)
403 GODateConventions
const *date_conv
=
404 workbook_date_conv (gee
->sheet
->workbook
);
407 if (!go_calendar_button_get_date (calb
, &date
))
410 serial
= go_date_g_to_serial (&date
, date_conv
);
412 gee_set_value_double (GOG_DATA_EDITOR (gee
), serial
, date_conv
);
416 gee_set_format (GnmExprEntry
*gee
, GOFormat
const *fmt
)
418 if (fmt
== gee
->constant_format
)
421 if (fmt
) go_format_ref (fmt
);
422 go_format_unref (gee
->constant_format
);
423 gee
->constant_format
= fmt
;
426 g_printerr ("Setting format %s\n",
427 fmt
? go_format_as_XL (fmt
) : "-");
429 if (fmt
&& go_format_is_date (fmt
)) {
430 if (!gee
->calendar_combo
) {
431 gee
->calendar_combo
= go_calendar_button_new ();
432 gtk_widget_show (gee
->calendar_combo
);
433 gtk_box_pack_start (GTK_BOX (gee
), gee
->calendar_combo
,
435 gee
->calendar_combo_changed
=
436 g_signal_connect (G_OBJECT (gee
->calendar_combo
),
438 G_CALLBACK (cb_calendar_changed
),
440 gee_update_calendar (gee
);
443 if (gee
->calendar_combo
) {
444 gtk_widget_destroy (gee
->calendar_combo
);
445 gee
->calendar_combo
= NULL
;
446 gee
->calendar_combo_changed
= 0;
450 g_object_notify (G_OBJECT (gee
), "constant-format");
454 gee_set_with_icon (GnmExprEntry
*gee
, gboolean with_icon
)
456 gboolean has_icon
= (gee
->icon
!= NULL
);
457 with_icon
= !!with_icon
;
459 if (has_icon
== with_icon
)
463 gee
->icon
= gtk_toggle_button_new ();
464 gtk_container_add (GTK_CONTAINER (gee
->icon
),
465 gtk_image_new_from_icon_name ("gnumeric-exprentry",
466 GTK_ICON_SIZE_MENU
));
467 gtk_box_pack_end (GTK_BOX (gee
), gee
->icon
, FALSE
, FALSE
, 0);
468 gtk_widget_show_all (gee
->icon
);
469 g_signal_connect (gee
->icon
, "clicked",
470 G_CALLBACK (cb_icon_clicked
), gee
);
472 gtk_widget_destroy (gee
->icon
);
476 gee_set_property (GObject
*object
,
481 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (object
);
483 case PROP_UPDATE_POLICY
:
484 gnm_expr_entry_set_update_policy (gee
, g_value_get_enum (value
));
488 gee_set_with_icon (gee
, g_value_get_boolean (value
));
492 const char *new_txt
= g_value_get_string (value
);
493 const char *old_txt
= gnm_expr_entry_get_text (gee
);
494 if (go_str_compare (new_txt
, old_txt
)) {
495 gnm_expr_entry_load_from_text (gee
, new_txt
);
496 gnm_expr_entry_signal_update (gee
, FALSE
);
502 gnm_expr_entry_set_flags (gee
,
503 g_value_get_uint (value
), GNM_EE_MASK
);
506 gnm_expr_entry_set_scg (gee
,
507 GNM_SCG (g_value_get_object (value
)));
510 g_return_if_fail (gee
->wbcg
== NULL
);
511 gee
->wbcg
= WBC_GTK (g_value_get_object (value
));
513 case PROP_CONSTANT_FORMAT
:
514 gee_set_format (gee
, g_value_get_boxed (value
));
516 case PROP_EDITING_CANCELED
:
517 gee
->editing_canceled
= g_value_get_boolean (value
);
519 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
524 gee_get_property (GObject
*object
,
529 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (object
);
531 case PROP_UPDATE_POLICY
:
532 g_value_set_enum (value
, gee
->update_policy
);
535 g_value_set_boolean (value
, gee
->icon
!= NULL
);
538 g_value_set_string (value
, gnm_expr_entry_get_text (gee
));
541 g_value_set_uint (value
, gee
->flags
);
544 g_value_set_object (value
, G_OBJECT (gee
->scg
));
547 g_value_set_object (value
, G_OBJECT (gee
->wbcg
));
549 case PROP_CONSTANT_FORMAT
:
550 g_value_set_boxed (value
, (gpointer
)gee
->constant_format
);
552 case PROP_EDITING_CANCELED
:
553 g_value_set_boolean (value
, gee
->editing_canceled
);
555 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
560 cb_entry_activate (GnmExprEntry
*gee
)
562 g_signal_emit (G_OBJECT (gee
), signals
[ACTIVATE
], 0);
563 gnm_expr_entry_signal_update (gee
, TRUE
);
567 gee_destroy_feedback_range (GnmExprEntry
*gee
)
569 WBCGtk
*wbcg
= scg_wbcg (gee
->scg
);
570 int page
, pages
= wbcg_get_n_scg (wbcg
);
572 for (page
= 0; page
< pages
; page
++) {
573 SheetControlGUI
*scg
= wbcg_get_nth_scg (wbcg
, page
);
574 SCG_FOREACH_PANE (scg
, pane
,
575 gnm_pane_expr_cursor_stop (pane
););
580 gnm_expr_entry_colour_ranges (GnmExprEntry
*gee
, int start
, int end
, GnmRangeRef
*rr
, int colour
,
581 PangoAttrList
**attrs
, gboolean insert_cursor
)
583 static const GOColor colours
[] = {
584 GO_COLOR_FROM_RGB (0x00, 0xff, 0x00),
585 GO_COLOR_FROM_RGB (0x00, 0x00, 0xff),
586 GO_COLOR_FROM_RGB (0xff, 0x00, 0x00),
587 GO_COLOR_FROM_RGB (0x00, 0x80, 0x80),
588 GO_COLOR_FROM_RGB (0xa0, 0xa0, 0x00),
589 GO_COLOR_FROM_RGB (0xa0, 0x00, 0xa0)
593 GnmRange
const *merge
; /*[#127415]*/
594 Sheet
*start_sheet
, *end_sheet
;
595 Sheet
*sheet
= scg_sheet (gee
->scg
);
596 SheetControlGUI
*scg
= NULL
;
598 if (rr
->a
.sheet
->workbook
!= gee
->sheet
->workbook
) {
599 /* We should show the range in an external workbook! */
604 *attrs
= pango_attr_list_new ();
606 colour
= colour
% G_N_ELEMENTS (colours
);
608 gnm_rangeref_normalize_pp (rr
, &gee
->pp
,
612 if (start_sheet
!= end_sheet
)
615 if (range_is_singleton (&r
) &&
616 NULL
!= (merge
= gnm_sheet_merge_is_corner
617 (start_sheet
, &r
.start
)))
619 if (start_sheet
== sheet
)
622 WBCGtk
*wbcg
= scg_wbcg (gee
->scg
);
623 scg
= wbcg_get_nth_scg (wbcg
, start_sheet
->index_in_wb
);
626 SCG_FOREACH_PANE (scg
, pane
, gnm_pane_expr_cursor_bound_set
627 (pane
, &r
, colours
[colour
]););
630 at
= go_color_to_pango (colours
[colour
], TRUE
);
631 at
->start_index
= start
;
634 pango_attr_list_change (*attrs
, at
);
637 /* WARNING : DO NOT CALL THIS FROM FROM UPDATE. It may create another
638 * canvas-item which would in turn call update and confuse the
642 gee_scan_for_range (GnmExprEntry
*gee
)
644 PangoAttrList
*attrs
= NULL
;
646 parse_pos_init_editpos (&gee
->pp
, scg_view (gee
->scg
));
647 gee_destroy_feedback_range (gee
);
648 if (!gee
->feedback_disabled
&& gee_is_editing (gee
) && gee
->lexer_items
!= NULL
) {
649 GnmLexerItem
*gli
= gee
->lexer_items
;
650 int colour
= 1; /* We start with 1 since GINT_TO_POINTER (0) == NULL */
651 GHashTable
*hash
= g_hash_table_new_full ((GHashFunc
) gnm_rangeref_hash
,
652 (GEqualFunc
) gnm_rangeref_equal
,
656 if (gli
->token
== RANGEREF
) {
657 char const *text
= gtk_entry_get_text (gee
->entry
);
658 char *rtext
= g_strndup (text
+ gli
->start
,
659 gli
->end
- gli
->start
);
662 tmp
= rangeref_parse (&rr
, rtext
,
663 &gee
->pp
, gee_convs (gee
));
667 gboolean insert_cursor
;
668 if (rr
.a
.sheet
== NULL
)
669 rr
.a
.sheet
= gee
->sheet
;
670 if (rr
.b
.sheet
== NULL
)
671 rr
.b
.sheet
= rr
.a
.sheet
;
672 val
= g_hash_table_lookup (hash
, &rr
);
674 GnmRangeRef
*rrr
= gnm_rangeref_dup (&rr
);
675 this_colour
= colour
++;
676 g_hash_table_insert (hash
, rrr
, GINT_TO_POINTER (this_colour
));
677 insert_cursor
= TRUE
;
679 this_colour
= GPOINTER_TO_INT (val
);
680 insert_cursor
= FALSE
;
682 gnm_expr_entry_colour_ranges (gee
, gli
->start
, gli
->end
, &rr
,
683 this_colour
, &attrs
, insert_cursor
);
687 } while (gli
++->token
!= 0);
688 g_hash_table_destroy (hash
);
691 g_object_set_data_full (G_OBJECT (gee
->entry
), "gnm:range-attributes", attrs
,
692 (GDestroyNotify
) pango_attr_list_unref
);
694 g_object_set_data (G_OBJECT (gee
->entry
), "gnm:range-attributes", NULL
);
698 gee_update_env (GnmExprEntry
*gee
)
700 if (!gee
->ignore_changes
) {
701 if (NULL
!= gee
->scg
&&
702 #warning why do we want this dichotomy
703 !gee
->is_cell_renderer
&&
704 !gnm_expr_entry_can_rangesel (gee
))
705 scg_rangesel_stop (gee
->scg
, FALSE
);
707 if (gnm_expr_char_start_p (gtk_entry_get_text (gee
->entry
)))
708 gee_scan_for_range (gee
);
714 gee_delete_tooltip (GnmExprEntry
*gee
, gboolean remove_completion
)
716 gboolean has_tooltip
= (gee
->tooltip
.tooltip
!= NULL
&&
717 gee
->tooltip
.timerid
== 0);
719 if (gee
->tooltip
.timerid
) {
720 g_source_remove (gee
->tooltip
.timerid
);
721 gee
->tooltip
.timerid
= 0;
723 if (gee
->tooltip
.tooltip
) {
724 gtk_widget_destroy (gee
->tooltip
.tooltip
);
725 gee
->tooltip
.tooltip
= NULL
;
727 if (gee
->tooltip
.fd
) {
728 gnm_func_unref (gee
->tooltip
.fd
);
729 gee
->tooltip
.fd
= NULL
;
731 if (gee
->tooltip
.handlerid
!= 0 && gee
->entry
!= NULL
) {
732 g_signal_handler_disconnect (gtk_widget_get_toplevel
733 (GTK_WIDGET (gee
->entry
)),
734 gee
->tooltip
.handlerid
);
735 gee
->tooltip
.handlerid
= 0;
737 if (remove_completion
) {
738 g_free (gee
->tooltip
.completion
);
739 gee
->tooltip
.completion
= NULL
;
740 gee
->tooltip
.completion_se_valid
= FALSE
;
746 gnm_expr_entry_close_tips (GnmExprEntry
*gee
)
749 gee_delete_tooltip (gee
, FALSE
);
753 cb_gee_focus_out_event (GtkWidget
*widget
,
754 GdkEventFocus
*event
,
758 cb_show_tooltip (gpointer user_data
)
760 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (user_data
);
761 gtk_widget_show_all (gee
->tooltip
.tooltip
);
762 gee
->tooltip
.timerid
= 0;
768 gee_create_tooltip (GnmExprEntry
*gee
, gchar
const *str
,
769 gchar
const *marked_str
, gboolean set_tabs
)
771 GtkWidget
*toplevel
, *label
, *tip
;
772 gint root_x
= 0, root_y
= 0;
773 GtkAllocation allocation
;
775 gchar
*markup
= NULL
;
777 GtkTextBuffer
*buffer
;
778 PangoAttrList
*attr_list
= NULL
;
781 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (gee
->entry
));
782 gtk_widget_add_events(toplevel
, GDK_FOCUS_CHANGE_MASK
);
783 if (gee
->tooltip
.handlerid
== 0)
784 gee
->tooltip
.handlerid
= g_signal_connect
785 (G_OBJECT (toplevel
), "focus-out-event",
786 G_CALLBACK (cb_gee_focus_out_event
), gee
);
788 label
= gnm_convert_to_tooltip (toplevel
, gtk_text_view_new ());
789 tip
= gtk_widget_get_toplevel (label
);
791 gtk_style_context_add_class (gtk_widget_get_style_context (label
),
795 markup
= gnm_func_convert_markup_to_pango (str
, label
);
796 string
= g_string_new (markup
);
798 g_string_append (string
, marked_str
);
799 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (label
));
801 if (pango_parse_markup (string
->str
, -1, 0,
804 go_create_std_tags_for_buffer (buffer
);
805 gtk_text_buffer_set_text (buffer
, text
, -1);
806 gnm_load_pango_attributes_into_buffer (attr_list
, buffer
, text
);
808 pango_attr_list_unref (attr_list
);
810 gtk_text_buffer_set_text (buffer
, string
->str
, -1);
812 g_string_free (string
, TRUE
);
816 tabs
= pango_tab_array_new_with_positions
822 PANGO_TAB_LEFT
, 200);
823 gtk_text_view_set_tabs (GTK_TEXT_VIEW (label
), tabs
);
824 pango_tab_array_free (tabs
);
827 gdkw
= gtk_widget_get_window (GTK_WIDGET (gee
->entry
));
828 gdk_window_get_origin (gdkw
, &root_x
, &root_y
);
829 gtk_widget_get_allocation (GTK_WIDGET (gee
->entry
), &allocation
);
831 gtk_window_move (GTK_WINDOW (tip
),
832 root_x
+ allocation
.x
,
833 root_y
+ allocation
.y
+ allocation
.height
);
839 gee_set_tooltip_argument (GString
*str
, char *arg
, gboolean optional
)
842 g_string_append_c (str
, '[');
843 g_string_append (str
, arg
);
845 g_string_append_c (str
, ']');
849 gee_set_tooltip (GnmExprEntry
*gee
, GnmFunc
*fd
, gint args
, gboolean had_stuff
)
852 gchar sep
= go_locale_get_arg_sep ();
854 gboolean first
= TRUE
;
856 gboolean localized_function_names
= gee
->sheet
->convs
->localized_function_names
;
859 gnm_func_load_if_stub (fd
);
860 gnm_func_count_args (fd
, &min
, &max
);
862 if ((gee
->tooltip
.fd
)
863 && (gee
->tooltip
.fd
== fd
&& gee
->tooltip
.args
== args
864 && gee
->tooltip
.had_stuff
== (max
== 0 && args
== 0 && had_stuff
)))
866 gee_delete_tooltip (gee
, FALSE
);
868 gee
->tooltip
.fd
= fd
;
869 gnm_func_ref (gee
->tooltip
.fd
);
871 fdname
= gnm_func_get_name (fd
, localized_function_names
);
873 str
= g_string_new (fdname
);
874 g_string_append_c (str
, '(');
876 for (i
= 0; i
< max
; i
++) {
877 char *arg_name
= gnm_func_get_arg_name
879 if (arg_name
!= NULL
) {
883 g_string_append_c (str
, sep
);
885 extra
= g_strdup_printf
888 gnm_func_get_arg_description (fd
, i
));
889 g_string_append (str
, UNICODE_RIGHT_TRIANGLE
);
891 gee_set_tooltip_argument (str
, arg_name
, i
>= min
);
893 g_string_append (str
, UNICODE_LEFT_TRIANGLE
);
900 g_string_append_c (str
, sep
);
902 (str
, (args
>= i
&& args
< max
)
903 ? UNICODE_RIGHT_TRIANGLE UNICODE_ELLIPSIS UNICODE_LEFT_TRIANGLE
906 if (max
== 0 && args
== 0 && !had_stuff
) {
907 extra
= g_strdup_printf (_("%s takes no arguments"),
909 } else if (args
>= max
) {
910 g_string_append (str
, UNICODE_RIGHT_TRIANGLE UNICODE_CROSS_AND_SKULLBONES UNICODE_LEFT_TRIANGLE
);
911 extra
= g_strdup_printf (_("Too many arguments for %s"),
914 g_string_append_c (str
, ')');
916 g_string_append_c (str
, '\n');
917 g_string_append (str
, extra
);
921 gee
->tooltip
.tooltip
= gee_create_tooltip
922 (gee
, str
->str
, _("\n\n<i>Ctrl-F4 to close tooltip</i>"), FALSE
);
923 gtk_widget_show_all (gee
->tooltip
.tooltip
);
924 gee
->tooltip
.args
= args
;
925 gee
->tooltip
.had_stuff
= (max
== 0 && args
== 0 && had_stuff
);
927 g_string_free (str
, TRUE
);
931 gee_set_tooltip_completion (GnmExprEntry
*gee
, GSList
*list
, guint start
, guint end
)
937 GSList
*list_c
= list
;
938 gchar
const *name
= NULL
;
939 gboolean show_tool_tip
, had_tool_tip
;
940 gboolean localized_function_names
= gee
->sheet
->convs
->localized_function_names
;
942 had_tool_tip
= gee_delete_tooltip (gee
, TRUE
);
944 str
= g_string_new (NULL
);
945 for (; list_c
!= NULL
&& ++i
< max
; list_c
= list_c
->next
) {
946 GnmFunc
*fd
= list_c
->data
;
947 name
= gnm_func_get_name (fd
, localized_function_names
);
948 if ((end
- start
) < (guint
) g_utf8_strlen (name
, -1))
949 /* xgettext: the first %s is a function name and */
950 /* the second %s the function description */
951 g_string_append_printf (str
, _("\t%s \t%s\n"), name
,
952 gnm_func_get_description (fd
));
954 /* xgettext: the first %s is a function name and */
955 /* the second %s the function description */
956 g_string_append_printf (str
, _("\342\234\223\t%s \t%s\n"), name
,
957 gnm_func_get_description (fd
));
962 str_marked
= g_string_new (NULL
);
964 g_string_append (str_marked
, "\t" UNICODE_ELLIPSIS_VERT
"\n");
966 gee
->tooltip
.completion
968 /*xgettext: short form for: "type F4-key to complete the name"*/
969 g_string_append (str_marked
, _("\n\t<i>F4 to complete</i>"));
971 /*xgettext: short form for: "type shift-F4-keys to select the completion"*/
972 g_string_append (str_marked
, _("\n\t<i>\342\207\247F4 to select</i>"));
974 g_string_truncate (str
, str
->len
- 1);
975 gee
->tooltip
.completion_start
= start
;
976 gee
->tooltip
.completion_end
= end
;
977 gee
->tooltip
.completion_se_valid
= TRUE
;
978 show_tool_tip
= gnm_conf_get_core_gui_editing_function_name_tooltips ();
980 gee
->tooltip
.tooltip
= gee_create_tooltip
981 (gee
, str
->str
, str_marked
->str
, TRUE
);
983 gtk_widget_show_all (gee
->tooltip
.tooltip
);
985 gee
->tooltip
.timerid
= g_timeout_add_full
986 (G_PRIORITY_DEFAULT
, 750,
991 g_string_free (str
, TRUE
);
992 g_string_free (str_marked
, TRUE
);
993 g_slist_free_full (list
, (GDestroyNotify
) gnm_func_unref
);
994 return show_tool_tip
;
998 gee_dump_lexer (GnmLexerItem
*gli
) {
999 g_printerr ("************\n");
1001 g_printerr ("%2" G_GSIZE_FORMAT
" to %2" G_GSIZE_FORMAT
": %d\n",
1002 gli
->start
, gli
->end
, gli
->token
);
1003 } while (gli
++->token
!= 0);
1004 g_printerr ("************\n");
1009 func_def_cmp (gconstpointer a_
, gconstpointer b_
, gpointer user
)
1011 GnmFunc
const * const a
= (GnmFunc
const * const)a_
;
1012 GnmFunc
const * const b
= (GnmFunc
const * const)b_
;
1013 GnmExprEntry
*gee
= user
;
1014 gboolean localized
= gee
->sheet
->convs
->localized_function_names
;
1016 return g_utf8_collate (gnm_func_get_name (a
, localized
),
1017 gnm_func_get_name (b
, localized
));
1022 gee_update_lexer_items (GnmExprEntry
*gee
)
1024 GtkEditable
*editable
= GTK_EDITABLE (gee
->entry
);
1025 char *str
= gtk_editable_get_chars (editable
, 0, -1);
1026 Sheet
*sheet
= scg_sheet (gee
->scg
);
1027 GOFormat
const *format
;
1028 gboolean forced_text
;
1030 g_free (gee
->lexer_items
);
1031 gee
->lexer_items
= NULL
;
1033 if (gee
->texpr
!= NULL
) {
1034 gnm_expr_top_unref (gee
->texpr
);
1038 parse_pos_init_editpos (&gee
->pp
, scg_view (gee
->scg
));
1039 format
= gnm_style_get_format
1040 (sheet_style_get (sheet
, gee
->pp
.eval
.col
, gee
->pp
.eval
.row
));
1041 forced_text
= ((format
!= NULL
) && go_format_is_text (format
));
1043 if (!gee
->feedback_disabled
&& !forced_text
) {
1044 gee
->texpr
= gnm_expr_parse_str
1045 ((str
[0] == '=') ? str
+1 : str
,
1046 &gee
->pp
, GNM_EXPR_PARSE_DEFAULT
1047 | GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
,
1048 sheet_get_conventions (sheet
), NULL
);
1051 gee
->tooltip
.is_expr
= (!forced_text
) &&
1052 (NULL
!= gnm_expr_char_start_p (str
));
1053 if (!(gee
->flags
& GNM_EE_SINGLE_RANGE
)) {
1054 gee
->lexer_items
= gnm_expr_lex_all
1056 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
,
1058 if (gnm_debug_flag ("functooltip"))
1059 gee_dump_lexer (gee
->lexer_items
);
1064 static GnmLexerItem
*
1065 gee_duplicate_lexer_items (GnmLexerItem
*gli
)
1068 GnmLexerItem
*gli_c
= gli
;
1070 while (gli_c
->token
!= 0) {
1075 return g_memdup (gli
, n
* sizeof (GnmLexerItem
));
1079 gee_check_tooltip (GnmExprEntry
*gee
)
1081 GtkEditable
*editable
= GTK_EDITABLE (gee
->entry
);
1085 gboolean stuff
= FALSE
, completion_se_set
= FALSE
;
1086 GnmLexerItem
*gli
, *gli_c
;
1089 if (gee
->lexer_items
== NULL
|| !gee
->tooltip
.enabled
||
1090 (!gee
->tooltip
.is_expr
&& !gee
->is_cell_renderer
)) {
1091 gee_delete_tooltip (gee
, TRUE
);
1095 end
= gtk_editable_get_position (editable
);
1098 gee_delete_tooltip (gee
, TRUE
);
1102 str
= gtk_editable_get_chars (editable
, 0, -1);
1103 end_t
= g_utf8_offset_to_pointer (str
, end
) - str
;
1106 gli_c
= gli
= gee_duplicate_lexer_items (gee
->lexer_items
);
1109 * If we have an open string at the end of the entry, we
1113 for (; gli
->token
!= 0; gli
++) {
1114 if (gli
->start
>= end_t
) {
1118 if (gli
->token
!= TOKEN_UNMATCHED_APOSTROPHE
)
1120 if (gli
->start
== 0)
1129 last_token
= (gli
- 1)->token
;
1131 /* This creates the completion tooltip */
1133 gli
->token
== STRING
&&
1134 last_token
!= CONSTANT
&&
1135 last_token
!= '$') {
1136 guint start_t
= gli
->start
;
1141 prefix
= g_strndup (str
+ start_t
, end_t
- start_t
);
1142 list
= gnm_func_lookup_prefix
1143 (prefix
, gee
->sheet
->workbook
,
1144 gee_convs (gee
)->localized_function_names
);
1147 list
= g_slist_sort_with_data
1151 if (gee_set_tooltip_completion
1152 (gee
, list
, start_t
, end_t
)) {
1158 g_free (gee
->tooltip
.completion
);
1159 gee
->tooltip
.completion
= NULL
;
1160 gee
->tooltip
.completion_start
= start_t
;
1161 gee
->tooltip
.completion_end
= end_t
;
1162 gee
->tooltip
.completion_se_valid
= TRUE
;
1164 completion_se_set
= TRUE
;
1166 g_free (gee
->tooltip
.completion
);
1167 gee
->tooltip
.completion
= NULL
;
1168 gee
->tooltip
.completion_se_valid
= FALSE
;
1172 if (!gnm_conf_get_core_gui_editing_function_argument_tooltips ())
1175 if (gnm_debug_flag ("functooltip"))
1176 g_printerr ("Last token considered is %d from %2"
1177 G_GSIZE_FORMAT
" to %2" G_GSIZE_FORMAT
".\n",
1178 gli
->token
, gli
->start
, gli
->end
);
1181 while (gli
->start
> 1) {
1182 switch (gli
->token
) {
1184 if ((gli
- 1)->token
== STRING
) {
1185 gint start_t
= (gli
- 1)->start
;
1186 gint end_t
= (gli
- 1)->end
;
1187 char *name
= g_strndup (str
+ start_t
,
1189 GnmFunc
*fd
= gee_convs (gee
)->localized_function_names
1190 ? gnm_func_lookup_localized (name
, NULL
)
1191 : gnm_func_lookup (name
, NULL
);
1194 gee_set_tooltip (gee
, fd
, args
, stuff
);
1204 stuff
= (args
== 0);
1210 while (gli
->start
> 1 && para
> 0) {
1211 switch (gli
->token
) {
1224 stuff
= (args
== 0);
1230 while (gli
->start
> 1 && para
> 0) {
1231 switch (gli
->token
) {
1244 stuff
= (args
== 0);
1251 stuff
= (args
== 0);
1261 gee_delete_tooltip (gee
, !completion_se_set
);
1266 cb_gee_focus_out_event (G_GNUC_UNUSED GtkWidget
*widget
,
1267 G_GNUC_UNUSED GdkEventFocus
*event
,
1270 gee_delete_tooltip (user_data
, FALSE
);
1275 cb_gee_notify_cursor_position (GnmExprEntry
*gee
)
1277 gee_update_env (gee
);
1278 gee_check_tooltip (gee
);
1282 cb_entry_changed (GnmExprEntry
*gee
)
1284 gee_update_lexer_items (gee
);
1285 gee_update_env (gee
);
1286 gee_update_calendar (gee
);
1287 gee_check_tooltip (gee
);
1288 g_signal_emit (G_OBJECT (gee
), signals
[CHANGED
], 0);
1292 cb_gee_key_press_event (GtkEntry
*entry
,
1296 WBCGtk
*wbcg
= gee
->wbcg
;
1297 gboolean is_enter
= FALSE
;
1298 int state
= gnm_filter_modifiers (event
->state
);
1300 switch (event
->keyval
) {
1301 case GDK_KEY_Up
: case GDK_KEY_KP_Up
:
1302 case GDK_KEY_Down
: case GDK_KEY_KP_Down
:
1303 if (gee
->is_cell_renderer
)
1305 /* Ignore these keys */
1307 /* GDK_KEY_F2 starts editing */
1308 /* GDK_KEY_F3 opens the paste names dialog */
1310 /* Cycle absolute reference mode through the sequence rel/rel,
1311 * abs/abs, rel/abs, abs/rel and back to rel/rel. Update text
1312 * displayed in entry.
1314 /* Shift F4 provides the paste names dialog based on the current name */
1315 /* Control F4 closes the tooltips */
1316 Rangesel
*rs
= &gee
->rangesel
;
1319 if (state
== GDK_SHIFT_MASK
) {
1320 if (gee
->tooltip
.completion_se_valid
)
1321 dialog_function_select_paste
1323 gee
->tooltip
.completion_start
,
1324 gee
->tooltip
.completion_end
);
1326 dialog_function_select_paste
1327 (gee
->wbcg
, -1, -1);
1330 if (state
== GDK_CONTROL_MASK
) {
1331 gnm_expr_entry_close_tips (gee
);
1335 if (gee
->tooltip
.completion
!= NULL
) {
1336 guint start
= gee
->tooltip
.completion_start
;
1337 guint end
= gee
->tooltip
.completion_end
;
1338 gint new_start
= (gint
) start
;
1339 GtkEditable
*editable
= GTK_EDITABLE (gee
->entry
);
1341 gtk_editable_insert_text (editable
,
1342 gee
->tooltip
.completion
,
1343 strlen (gee
->tooltip
.completion
),
1345 gtk_editable_delete_text (editable
, new_start
,
1346 end
+ new_start
- start
);
1347 gtk_editable_set_position (editable
, new_start
);
1351 /* FIXME: since the range can't have changed we should just be able to */
1352 /* look it up rather than reparse */
1354 /* Look for a range */
1355 if (!rs
->is_valid
|| rs
->text_start
>= rs
->text_end
)
1356 gnm_expr_entry_find_range (gee
);
1358 /* no range found */
1359 if (!rs
->is_valid
|| rs
->text_start
>= rs
->text_end
)
1362 if ((GNM_EE_FORCE_ABS_REF
| GNM_EE_FORCE_REL_REF
) & gee
->flags
)
1365 c
= rs
->ref
.a
.col_relative
;
1366 r
= rs
->ref
.a
.row_relative
;
1367 gnm_cellref_set_col_ar (&rs
->ref
.a
, &gee
->pp
, !c
);
1368 gnm_cellref_set_col_ar (&rs
->ref
.b
, &gee
->pp
, !c
);
1369 gnm_cellref_set_row_ar (&rs
->ref
.a
, &gee
->pp
, c
^r
);
1370 gnm_cellref_set_row_ar (&rs
->ref
.b
, &gee
->pp
, c
^r
);
1372 gee_rangesel_update_text (gee
);
1377 case GDK_KEY_Escape
:
1378 if (gee
->is_cell_renderer
) {
1379 gtk_entry_set_editing_cancelled (entry
, TRUE
);
1380 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (gee
));
1381 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (gee
));
1384 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
1387 case GDK_KEY_KP_Enter
:
1388 case GDK_KEY_Return
:
1389 if (gee
->is_cell_renderer
)
1391 /* Is this the right way to append a newline ?? */
1392 if (state
== GDK_MOD1_MASK
) {
1393 gint pos
= gtk_editable_get_position (GTK_EDITABLE (entry
));
1394 gtk_editable_insert_text (GTK_EDITABLE (entry
), "\n", 1, &pos
);
1395 gtk_editable_set_position (GTK_EDITABLE (entry
), pos
);
1399 /* Ctrl-enter is only applicable for the main entry */
1400 if (!wbcg_is_editing (wbcg
))
1407 case GDK_KEY_ISO_Left_Tab
:
1408 case GDK_KEY_KP_Tab
:
1409 /* Tab is only applicable for the main entry */
1410 if (gee
->is_cell_renderer
|| !wbcg_is_editing (wbcg
))
1414 WBCEditResult result
;
1416 if (is_enter
&& (state
& GDK_CONTROL_MASK
))
1417 result
= (state
& GDK_SHIFT_MASK
) ? WBC_EDIT_ACCEPT_ARRAY
: WBC_EDIT_ACCEPT_RANGE
;
1419 result
= WBC_EDIT_ACCEPT
;
1421 /* Be careful to restore the editing sheet if we are editing */
1422 sv
= sheet_get_view (wbcg
->editing_sheet
,
1423 wb_control_view (GNM_WBC (wbcg
)));
1425 /* move the edit pos for normal entry */
1426 if (wbcg_edit_finish (wbcg
, result
, NULL
) && result
== WBC_EDIT_ACCEPT
) {
1427 GODirection dir
= gnm_conf_get_core_gui_editing_enter_moves_dir ();
1428 if (!is_enter
|| dir
!= GO_DIRECTION_NONE
) {
1429 gboolean forward
= TRUE
;
1430 gboolean horizontal
= TRUE
;
1432 horizontal
= go_direction_is_horizontal (dir
);
1433 forward
= go_direction_is_forward (dir
);
1436 if (event
->state
& GDK_SHIFT_MASK
)
1439 sv_selection_walk_step (sv
, forward
, horizontal
);
1441 /* invalidate, in case Enter direction changes */
1443 sv
->first_tab_col
= -1;
1444 gnm_sheet_view_update (sv
);
1450 case GDK_KEY_KP_Separator
:
1451 case GDK_KEY_KP_Decimal
: {
1452 GtkEditable
*editable
= GTK_EDITABLE (entry
);
1454 GString
const* s
= go_locale_get_decimal ();
1455 gchar
const* decimal
= s
->str
;
1457 gtk_editable_get_selection_bounds (editable
, &start
, &end
);
1458 gtk_editable_delete_text (editable
, start
, end
);
1459 gtk_editable_insert_text (editable
, decimal
, l
, &start
);
1460 gtk_editable_set_position (editable
, start
);
1465 /* Replace selection by its evaluated result. */
1466 GtkEditable
*editable
= GTK_EDITABLE (entry
);
1469 GnmExprTop
const *texpr
;
1470 Sheet
*sheet
= gee
->pp
.sheet
;
1472 gtk_editable_get_selection_bounds (editable
, &start
, &end
);
1475 str
= gtk_editable_get_chars (editable
, start
, end
);
1477 texpr
= gnm_expr_parse_str (str
, &gee
->pp
,
1478 GNM_EXPR_PARSE_DEFAULT
,
1485 GnmExpr
const *expr
;
1487 eval_pos_init_pos (&ep
, sheet
, &gee
->pp
.eval
);
1488 v
= gnm_expr_top_eval (texpr
, &ep
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
1489 gnm_expr_top_unref (texpr
);
1492 * Turn the value into an expression so we get
1495 expr
= gnm_expr_new_constant (v
);
1496 cst
= gnm_expr_as_string (expr
, &gee
->pp
,
1498 gnm_expr_free (expr
);
1500 gtk_editable_delete_text (editable
, start
, end
);
1501 gtk_editable_insert_text (editable
, cst
, -1, &start
);
1502 gtk_editable_set_position (editable
, start
);
1519 cb_gee_button_press_event (G_GNUC_UNUSED GtkEntry
*entry
,
1520 G_GNUC_UNUSED GdkEventButton
*event
,
1523 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
1526 scg_rangesel_stop (gee
->scg
, FALSE
);
1527 gnm_expr_entry_find_range (gee
);
1528 g_signal_emit (G_OBJECT (gee
), signals
[CHANGED
], 0);
1535 gee_mnemonic_activate (GtkWidget
*w
, G_GNUC_UNUSED gboolean group_cycling
)
1537 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (w
);
1538 gtk_widget_grab_focus (GTK_WIDGET (gee
->entry
));
1543 gee_init (GnmExprEntry
*gee
)
1545 gee
->editing_canceled
= FALSE
;
1546 gee
->is_cell_renderer
= FALSE
;
1547 gee
->ignore_changes
= FALSE
;
1552 gee
->freeze_count
= 0;
1553 gee
->update_timeout_id
= 0;
1554 gee
->update_policy
= GNM_UPDATE_CONTINUOUS
;
1555 gee
->feedback_disabled
= FALSE
;
1556 gee
->lexer_items
= NULL
;
1558 gee
->tooltip
.tooltip
= NULL
;
1559 gee
->tooltip
.fd
= NULL
;
1560 gee
->tooltip
.handlerid
= 0;
1561 gee
->tooltip
.enabled
= TRUE
;
1562 gee_rangesel_reset (gee
);
1564 gee
->entry
= GTK_ENTRY (gtk_entry_new ());
1566 /* Disable selecting the entire content when the widget gets focus */
1567 g_object_set (gtk_widget_get_settings (GTK_WIDGET (gee
->entry
)),
1568 "gtk-entry-select-on-focus", FALSE
,
1571 g_signal_connect_swapped (G_OBJECT (gee
->entry
), "activate",
1572 G_CALLBACK (cb_entry_activate
), gee
);
1573 g_signal_connect_swapped (G_OBJECT (gee
->entry
), "changed",
1574 G_CALLBACK (cb_entry_changed
), gee
);
1575 g_signal_connect (G_OBJECT (gee
->entry
), "key_press_event",
1576 G_CALLBACK (cb_gee_key_press_event
), gee
);
1577 g_signal_connect (G_OBJECT (gee
->entry
), "button_press_event",
1578 G_CALLBACK (cb_gee_button_press_event
), gee
);
1579 g_signal_connect_swapped (G_OBJECT (gee
->entry
), "notify::cursor-position",
1580 G_CALLBACK (cb_gee_notify_cursor_position
), gee
);
1581 gtk_box_pack_start (GTK_BOX (gee
), GTK_WIDGET (gee
->entry
),
1583 gtk_widget_show (GTK_WIDGET (gee
->entry
));
1587 gee_finalize (GObject
*obj
)
1589 GnmExprEntry
*gee
= (GnmExprEntry
*)obj
;
1591 go_format_unref (gee
->constant_format
);
1592 gee_delete_tooltip (gee
, TRUE
);
1593 g_free (gee
->lexer_items
);
1594 if (gee
->texpr
!= NULL
)
1595 gnm_expr_top_unref (gee
->texpr
);
1597 ((GObjectClass
*)parent_class
)->finalize (obj
);
1601 gee_set_value_double (GogDataEditor
*editor
, double val
,
1602 GODateConventions
const *date_conv
)
1604 GnmExprEntry
*gee
= GNM_EXPR_ENTRY (editor
);
1605 GnmValue
*v
= value_new_float (val
);
1606 char *txt
= format_value (gee
->constant_format
, v
, -1, date_conv
);
1612 txt
= g_strdup_printf ("%g", val
);
1616 g_printerr ("Setting text %s\n", txt
);
1618 g_object_set (G_OBJECT (editor
), "text", txt
, NULL
);
1624 gee_data_editor_set_format (GogDataEditor
*deditor
, GOFormat
const *fmt
)
1626 GnmExprEntry
*gee
= (GnmExprEntry
*)deditor
;
1628 GODateConventions
const *date_conv
=
1629 workbook_date_conv (gee
->sheet
->workbook
);
1631 if (fmt
== gee
->constant_format
)
1634 v
= get_matched_value (gee
);
1636 gee_set_format (gee
, fmt
);
1638 if (v
&& VALUE_IS_FLOAT (v
)) {
1639 char *txt
= format_value (gee
->constant_format
, v
,
1641 gtk_entry_set_text (gee
->entry
, txt
);
1649 gee_go_plot_data_editor_init (GogDataEditorClass
*iface
)
1651 iface
->set_format
= gee_data_editor_set_format
;
1652 iface
->set_value_double
= gee_set_value_double
;
1657 gee_class_init (GObjectClass
*gobject_class
)
1659 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*)gobject_class
;
1661 parent_class
= g_type_class_peek_parent (gobject_class
);
1663 gobject_class
->set_property
= gee_set_property
;
1664 gobject_class
->get_property
= gee_get_property
;
1665 gobject_class
->finalize
= gee_finalize
;
1666 widget_class
->destroy
= gee_destroy
;
1667 widget_class
->mnemonic_activate
= gee_mnemonic_activate
;
1669 signals
[UPDATE
] = g_signal_new ("update",
1670 GNM_EXPR_ENTRY_TYPE
,
1672 G_STRUCT_OFFSET (GnmExprEntryClass
, update
),
1674 g_cclosure_marshal_VOID__BOOLEAN
,
1677 signals
[CHANGED
] = g_signal_new ("changed",
1678 GNM_EXPR_ENTRY_TYPE
,
1680 G_STRUCT_OFFSET (GnmExprEntryClass
, changed
),
1682 g_cclosure_marshal_VOID__VOID
,
1685 g_signal_new ("activate",
1686 G_OBJECT_CLASS_TYPE (gobject_class
),
1687 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
1688 G_STRUCT_OFFSET (GnmExprEntryClass
, activate
),
1690 g_cclosure_marshal_VOID__VOID
,
1694 g_object_class_override_property
1695 (gobject_class
, PROP_EDITING_CANCELED
, "editing-canceled");
1697 g_object_class_install_property
1698 (gobject_class
, PROP_UPDATE_POLICY
,
1699 g_param_spec_enum ("update-policy",
1700 P_("Update policy"),
1701 P_("How frequently changes to the entry should be applied"),
1702 GNM_TYPE_UPDATE_TYPE
, GNM_UPDATE_CONTINUOUS
,
1703 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1705 g_object_class_install_property
1706 (gobject_class
, PROP_WITH_ICON
,
1707 g_param_spec_boolean ("with-icon",
1709 P_("Should there be an icon to the right of the entry?"),
1711 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1713 g_object_class_install_property
1714 (gobject_class
, PROP_TEXT
,
1715 g_param_spec_string ("text",
1717 P_("The contents of the entry"),
1719 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1721 g_object_class_install_property
1722 (gobject_class
, PROP_FLAGS
,
1723 g_param_spec_uint ("flags", NULL
, NULL
,
1725 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1727 g_object_class_install_property
1728 (gobject_class
, PROP_SCG
,
1729 g_param_spec_object ("scg",
1730 P_("SheetControlGUI"),
1731 P_("The GUI container associated with the entry."),
1733 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1735 g_object_class_install_property
1736 (gobject_class
, PROP_WBCG
,
1737 g_param_spec_object ("wbcg",
1739 P_("The toplevel GUI container associated with the entry."),
1741 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1743 g_object_class_install_property
1744 (gobject_class
, PROP_CONSTANT_FORMAT
,
1745 g_param_spec_boxed ("constant-format",
1746 P_("Constant Format"),
1747 P_("Format for constants"),
1748 go_format_get_type (),
1749 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1751 gee_debug
= gnm_debug_flag ("gee");
1754 /***************************************************************************/
1757 gee_editable_start_editing (GtkCellEditable
*cell_editable
,
1758 G_GNUC_UNUSED GdkEvent
*event
)
1760 GtkEntry
*entry
= gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (cell_editable
));
1761 GNM_EXPR_ENTRY (cell_editable
)->is_cell_renderer
= TRUE
;
1762 g_signal_connect_swapped (G_OBJECT (entry
), "activate",
1763 G_CALLBACK (gtk_cell_editable_editing_done
), cell_editable
);
1764 gtk_widget_grab_focus (GTK_WIDGET (entry
));
1768 gee_cell_editable_init (GtkCellEditableIface
*iface
)
1770 iface
->start_editing
= gee_editable_start_editing
;
1772 /***************************************************************************/
1774 GSF_CLASS_FULL (GnmExprEntry
, gnm_expr_entry
,
1775 NULL
, NULL
, gee_class_init
, NULL
,
1776 gee_init
, GTK_TYPE_BOX
, 0,
1777 GSF_INTERFACE (gee_cell_editable_init
, GTK_TYPE_CELL_EDITABLE
);
1778 GSF_INTERFACE (gee_go_plot_data_editor_init
, GOG_TYPE_DATA_EDITOR
))
1781 * gee_prepare_range :
1785 * Adjust @dst as necessary to conform to @gee's requirements
1786 * Produces the _logical_ range, a merge is displayed as only the topleft.
1789 gee_prepare_range (GnmExprEntry
const *gee
, GnmRangeRef
*dst
)
1791 Rangesel
const *rs
= &gee
->rangesel
;
1795 if (dst
->a
.sheet
== NULL
&& !(gee
->flags
& GNM_EE_SHEET_OPTIONAL
))
1796 dst
->a
.sheet
= gee
->sheet
;
1797 if (gee
->flags
& GNM_EE_FULL_ROW
) {
1799 dst
->b
.col
= gnm_sheet_get_last_col (gee
->sheet
);
1801 if (gee
->flags
& GNM_EE_FULL_COL
) {
1803 dst
->b
.row
= gnm_sheet_get_last_row (gee
->sheet
);
1806 /* special case a single merge to be only corner */
1807 if (!(gee
->flags
& (GNM_EE_FULL_ROW
|GNM_EE_FULL_COL
))) {
1810 GnmRange
const *merge
;
1811 Sheet
*start_sheet
, *end_sheet
;
1812 gnm_rangeref_normalize(dst
,
1813 eval_pos_init_pos (&ep
, gee
->sheet
, &gee
->pp
.eval
),
1814 &start_sheet
, &end_sheet
,
1816 merge
= gnm_sheet_merge_is_corner (gee
->sheet
, &r
.start
);
1817 if (merge
!= NULL
&& range_equal (merge
, &r
))
1823 gee_rangesel_make_text (GnmExprEntry
const *gee
)
1826 GnmConventionsOut out
;
1828 gee_prepare_range (gee
, &ref
);
1830 out
.accum
= g_string_new (NULL
);
1832 out
.convs
= gee_convs (gee
);
1833 rangeref_as_string (&out
, &ref
);
1834 return g_string_free (out
.accum
, FALSE
);
1838 gee_rangesel_update_text (GnmExprEntry
*gee
)
1840 GtkEditable
*editable
= GTK_EDITABLE (gee
->entry
);
1841 Rangesel
*rs
= &gee
->rangesel
;
1843 char *text
= gee_rangesel_make_text (gee
);
1845 g_return_if_fail (!gee
->ignore_changes
);
1847 gee
->ignore_changes
= TRUE
;
1848 if (rs
->text_end
> rs
->text_start
) {
1850 gtk_editable_delete_text (editable
,
1854 /* We don't call gtk_editable_delete_text since we don't want */
1855 /* to emit a signal yet */
1856 GTK_EDITABLE_GET_IFACE (gee
->entry
)->delete_text (editable
,
1859 rs
->text_end
= rs
->text_start
;
1860 gtk_editable_set_position (GTK_EDITABLE (gee
->entry
), rs
->text_end
);
1862 rs
->text_start
= rs
->text_end
=
1863 gtk_editable_get_position (GTK_EDITABLE (gee
->entry
));
1866 /* Set the cursor at the end. It looks nicer */
1867 len
= strlen (text
);
1869 gtk_editable_insert_text (editable
, text
, len
, &rs
->text_end
);
1870 gtk_editable_set_position (editable
, rs
->text_end
);
1874 gee
->ignore_changes
= FALSE
;
1878 gee_find_lexer_token (GnmLexerItem
const *gli
, guint token_pos
,
1879 GnmLexerItem
const **gli_before
, GnmLexerItem
const **gli_after
)
1881 *gli_before
= *gli_after
= NULL
;
1882 if (gli
->token
== 0)
1884 if (gli
->start
== token_pos
) {
1888 while (gli
->token
!= 0) {
1889 if (gli
->start
< token_pos
&& token_pos
< gli
->end
) {
1890 *gli_before
= *gli_after
= gli
;
1893 if (gli
->start
== token_pos
) {
1894 *gli_before
= gli
- 1;
1898 if (gli
->end
== token_pos
) {
1900 *gli_after
= ((gli
+ 1)->token
!= 0) ? (gli
+ 1) : NULL
;
1905 *gli_before
= gli
- 1;
1910 * gnm_expr_entry_find_range:
1911 * @gee: a #GnmExprEntry
1913 * Look at the current selection to see how much of it needs to be changed when
1914 * selecting a range.
1917 gnm_expr_entry_find_range (GnmExprEntry
*gee
)
1919 gboolean single
, formula_only
;
1920 char const *text
, *cursor
, *tmp
, *ptr
;
1925 GnmLexerItem
const *gli
, *gli_before
, *gli_after
;
1927 g_return_val_if_fail (gee
!= NULL
, FALSE
);
1929 single
= (gee
->flags
& GNM_EE_SINGLE_RANGE
) != 0;
1930 rs
= &gee
->rangesel
;
1931 memset (rs
, 0, sizeof (*rs
));
1932 rs
->ref
.a
.col_relative
= rs
->ref
.a
.row_relative
= TRUE
;
1933 rs
->ref
.b
.col_relative
= rs
->ref
.b
.row_relative
= TRUE
;
1934 gee_force_abs_rel (gee
);
1936 text
= gtk_entry_get_text (gee
->entry
);
1940 formula_only
= (gee
->flags
& GNM_EE_FORMULA_ONLY
) != 0;
1941 if (formula_only
&& !gnm_expr_char_start_p (text
))
1944 len
= g_utf8_strlen (text
, -1);
1950 tmp
= rangeref_parse (&range
, text
, &gee
->pp
, gee_convs (gee
));
1952 rs
->is_valid
= TRUE
;
1958 cursor
= g_utf8_offset_to_pointer
1959 (text
, gtk_editable_get_position (GTK_EDITABLE (gee
->entry
)));
1961 ptr
= gnm_expr_char_start_p (text
);
1965 if (gnm_debug_flag ("rangeselection"))
1966 g_printerr ("text: >%s< -- cursor: >%s<\n", text
, cursor
);
1968 if (ptr
[0] == '\0') {
1969 rs
->text_end
= rs
->text_start
=
1970 g_utf8_pointer_to_offset
1975 if (gee
->lexer_items
== NULL
)
1976 gee_update_lexer_items (gee
);
1977 g_return_val_if_fail (gee
->lexer_items
!= NULL
, FALSE
);
1979 gli
= gee
->lexer_items
;
1980 while (gli
->token
!= 0 && gli
->start
< (guint
) (ptr
- text
))
1983 if (gli
->token
== 0) {
1984 rs
->text_start
= g_utf8_pointer_to_offset
1990 token_pos
= cursor
- text
;
1992 gee_find_lexer_token (gli
, (guint
)token_pos
, &gli_before
, &gli_after
);
1994 if (gnm_debug_flag ("rangeselection")) {
1995 g_printerr ("before: %p -- after: %p\n", gli_before
, gli_after
);
1997 g_printerr ("before token: %d\n", gli_before
->token
);
1999 g_printerr ("after token: %d\n", gli_after
->token
);
2002 if (gli_before
== NULL
&& gli_after
== NULL
)
2005 if (gli_before
== gli_after
) {
2006 if ((gli_after
+ 1)->token
== '(' ||
2007 (gli_after
+ 1)->token
== '{')
2009 if (gli
< gli_before
&&
2010 ((gli_before
- 1)->token
== ')' ||
2011 (gli_before
- 1)->token
== '}'))
2013 rs
->text_start
= g_utf8_pointer_to_offset
2014 (text
, text
+ gli_before
->start
);
2015 rs
->text_end
= g_utf8_pointer_to_offset
2016 (text
, text
+ gli_before
->end
);
2017 } else if (gli_before
!= NULL
&& gli_after
!= NULL
) {
2018 switch (gli_before
->token
) {
2024 if (gli_after
->token
== '(' ||
2025 gli_after
->token
== '{')
2027 rs
->text_start
= g_utf8_pointer_to_offset
2028 (text
, text
+ gli_before
->start
);
2029 rs
->text_end
= g_utf8_pointer_to_offset
2030 (text
, text
+ gli_before
->end
);
2033 switch (gli_after
->token
) {
2039 rs
->text_start
= g_utf8_pointer_to_offset
2040 (text
, text
+ gli_after
->start
);
2041 rs
->text_end
= g_utf8_pointer_to_offset
2042 (text
, text
+ gli_after
->end
);
2045 rs
->text_start
= g_utf8_pointer_to_offset
2046 (text
, text
+ gli_before
->end
);
2047 rs
->text_end
= g_utf8_pointer_to_offset
2048 (text
, text
+ gli_after
->start
);
2052 } else if (gli_before
== NULL
)
2053 switch (gli_after
->token
) {
2059 if ((gli_after
+ 1)->token
== '(' ||
2060 (gli_after
+ 1)->token
== '{')
2062 rs
->text_start
= g_utf8_pointer_to_offset
2063 (text
, text
+ gli_after
->start
);
2064 rs
->text_end
= g_utf8_pointer_to_offset
2065 (text
, text
+ gli_after
->end
);
2068 rs
->text_end
= rs
->text_start
=
2069 g_utf8_pointer_to_offset
2070 (text
, text
+ gli_after
->start
);
2073 else switch (gli_before
->token
) {
2079 if (gli
< gli_before
&&
2080 ((gli_before
- 1)->token
== ')' ||
2081 (gli_before
- 1)->token
== '}'))
2083 rs
->text_start
= g_utf8_pointer_to_offset
2084 (text
, text
+ gli_before
->start
);
2085 rs
->text_end
= g_utf8_pointer_to_offset
2086 (text
, text
+ gli_before
->end
);
2092 rs
->text_end
= rs
->text_start
=
2093 g_utf8_pointer_to_offset
2094 (text
, text
+ gli_before
->start
);
2098 if (gnm_debug_flag ("rangeselection"))
2099 g_printerr ("characters from %d to %d\n",
2100 rs
->text_start
, rs
->text_end
);
2102 rs_text
= gtk_editable_get_chars (GTK_EDITABLE (gee
->entry
),
2103 rs
->text_start
, rs
->text_end
);
2104 tmp
= rangeref_parse (&range
, rs_text
, &gee
->pp
, gee_convs (gee
));
2106 if (tmp
!= rs_text
) {
2107 rs
->is_valid
= TRUE
;
2114 * gnm_expr_entry_rangesel_stop:
2115 * @gee: a #GnmExprEntry
2116 * @clear_string: clear string flag
2118 * Perform the appropriate action when a range selection has been completed.
2121 gnm_expr_entry_rangesel_stop (GnmExprEntry
*gee
,
2122 gboolean clear_string
)
2126 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2128 rs
= &gee
->rangesel
;
2129 if (clear_string
&& rs
->text_end
> rs
->text_start
)
2130 gtk_editable_delete_text (GTK_EDITABLE (gee
->entry
),
2131 rs
->text_start
, rs
->text_end
);
2133 if (!(gee
->flags
& GNM_EE_SINGLE_RANGE
) || clear_string
)
2134 gee_rangesel_reset (gee
);
2137 /***************************************************************************/
2140 cb_scg_destroy (GnmExprEntry
*gee
, SheetControlGUI
*scg
)
2142 g_return_if_fail (scg
== gee
->scg
);
2144 gee_rangesel_reset (gee
);
2150 gee_detach_scg (GnmExprEntry
*gee
)
2152 if (gee
->scg
!= NULL
) {
2153 g_object_weak_unref (G_OBJECT (gee
->scg
),
2154 (GWeakNotify
) cb_scg_destroy
, gee
);
2160 /***************************************************************************/
2164 gboolean user_requested
;
2168 cb_gee_update_timeout (GEETimerClosure
const *info
)
2170 info
->gee
->update_timeout_id
= 0;
2171 g_signal_emit (G_OBJECT (info
->gee
), signals
[UPDATE
], 0,
2172 info
->user_requested
);
2177 gee_remove_update_timer (GnmExprEntry
*gee
)
2179 if (gee
->update_timeout_id
!= 0) {
2180 g_source_remove (gee
->update_timeout_id
);
2181 gee
->update_timeout_id
= 0;
2186 gee_reset_update_timer (GnmExprEntry
*gee
, gboolean user_requested
)
2188 GEETimerClosure
*dat
= g_new (GEETimerClosure
, 1);
2189 gee_remove_update_timer (gee
);
2191 dat
->user_requested
= user_requested
;
2192 gee
->update_timeout_id
= g_timeout_add_full (G_PRIORITY_DEFAULT
, 300,
2193 (GSourceFunc
) cb_gee_update_timeout
, dat
, g_free
);
2197 * gnm_expr_entry_signal_update:
2199 * @user_requested: is the update requested by the user (eg activation)
2201 * Higher level operations know when they are logically complete and can notify
2202 * GnmExprEntry clients. For example, button-up after a drag selection
2203 * indicates a logical end to the change and offers a good time to update.
2206 gnm_expr_entry_signal_update (GnmExprEntry
*gee
, gboolean user_requested
)
2208 gee_reset_update_timer (gee
, user_requested
);
2212 * gnm_expr_entry_set_update_policy:
2213 * @gee: a #GnmExprEntry
2214 * @policy: update policy
2216 * Sets the update policy for the expr-entry. #GNM_UPDATE_CONTINUOUS means that
2217 * anytime the entry's content changes, the update signal will be emitted.
2218 * #GNM_UPDATE_DELAYED means that the signal will be emitted after a brief
2219 * timeout when no changes occur, so updates are spaced by a short time rather
2220 * than continuous. #GNM_UPDATE_DISCONTINUOUS means that the signal will only
2221 * be emitted when the user releases the button and ends the rangeselection.
2225 gnm_expr_entry_set_update_policy (GnmExprEntry
*gee
,
2226 GnmUpdateType policy
)
2228 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2230 if (gee
->update_policy
== policy
)
2232 gee
->update_policy
= policy
;
2233 g_object_notify (G_OBJECT (gee
), "update-policy");
2237 * gnm_expr_entry_new:
2238 * @wbcg: #WBCGtk non-NULL
2239 * @with_icon: append a rollup icon to the end of the entry
2241 * Creates a new #GnmExprEntry, which is an entry widget with support
2242 * for range selections.
2243 * The entry is created with default flag settings which are suitable for use
2244 * in many dialogs, but see #gnm_expr_entry_set_flags.
2246 * Return value: a new #GnmExprEntry.
2249 gnm_expr_entry_new (WBCGtk
*wbcg
, gboolean with_icon
)
2251 return g_object_new (GNM_EXPR_ENTRY_TYPE
,
2252 "scg", wbcg_cur_scg (wbcg
),
2253 "with-icon", with_icon
,
2258 gnm_expr_entry_freeze (GnmExprEntry
*gee
)
2260 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2262 gee
->freeze_count
++;
2266 gnm_expr_entry_thaw (GnmExprEntry
*gee
)
2268 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2270 if (gee
->freeze_count
> 0 && (--gee
->freeze_count
) == 0) {
2271 gee_rangesel_update_text (gee
);
2272 switch (gee
->update_policy
) {
2273 case GNM_UPDATE_DELAYED
:
2274 gee_reset_update_timer (gee
, FALSE
);
2278 case GNM_UPDATE_DISCONTINUOUS
:
2279 if (gee
->scg
->rangesel
.active
)
2281 case GNM_UPDATE_CONTINUOUS
:
2282 g_signal_emit (G_OBJECT (gee
), signals
[UPDATE
], 0, FALSE
);
2288 * gnm_expr_entry_set_flags:
2289 * @gee: a #GnmExprEntry
2290 * @flags: bitmap of flag values
2291 * @mask: bitmap with ones for flags to be changed
2293 * Changes the flags specified in @mask to values given in @flags.
2295 * Flags (%FALSE by default):
2296 * %GNM_EE_SINGLE_RANGE Entry will only hold a single range.
2297 * %GNM_EE_ABS_COL Column reference must be absolute.
2298 * %GNM_EE_ABS_ROW Row reference must be absolute.
2299 * %GNM_EE_FULL_COL GnmRange consists of full columns.
2300 * %GNM_EE_FULL_ROW GnmRange consists of full rows.
2301 * %GNM_EE_SHEET_OPTIONAL Current sheet name not auto-added.
2304 gnm_expr_entry_set_flags (GnmExprEntry
*gee
,
2305 GnmExprEntryFlags flags
,
2306 GnmExprEntryFlags mask
)
2308 GnmExprEntryFlags newflags
;
2309 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2311 newflags
= (gee
->flags
& ~mask
) | (flags
& mask
);
2312 if (gee
->flags
== newflags
)
2315 gee
->flags
= newflags
;
2316 gee_rangesel_reset (gee
);
2320 * gnm_expr_entry_set_scg:
2321 * @gee: a #GnmExprEntry
2322 * @scg: a #SheetControlGUI
2324 * Associates the entry with a SheetControlGUI. The entry widget
2325 * automatically removes the association when the SheetControlGUI is
2329 gnm_expr_entry_set_scg (GnmExprEntry
*gee
, SheetControlGUI
*scg
)
2331 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2332 g_return_if_fail (scg
== NULL
|| GNM_IS_SCG (scg
));
2334 if ((gee
->flags
& GNM_EE_SINGLE_RANGE
) || scg
!= gee
->scg
)
2335 gee_rangesel_reset (gee
);
2337 gee_detach_scg (gee
);
2340 g_object_weak_ref (G_OBJECT (gee
->scg
),
2341 (GWeakNotify
) cb_scg_destroy
, gee
);
2342 gee
->sheet
= sc_sheet (GNM_SHEET_CONTROL (scg
));
2343 parse_pos_init_editpos (&gee
->pp
, scg_view (gee
->scg
));
2344 gee
->wbcg
= scg_wbcg (gee
->scg
);
2349 g_printerr ("Setting gee (%p)->sheet = %s\n",
2350 gee
, gee
->sheet
->name_unquoted
);
2354 * gnm_expr_entry_get_scg:
2357 * Returns: (transfer none): the associated #SheetControlGUI.
2360 gnm_expr_entry_get_scg (GnmExprEntry
*gee
)
2366 * gnm_expr_entry_load_from_text:
2371 gnm_expr_entry_load_from_text (GnmExprEntry
*gee
, char const *txt
)
2373 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2374 /* We have nowhere to store the text while frozen. */
2375 g_return_if_fail (gee
->freeze_count
== 0);
2377 gee_rangesel_reset (gee
);
2380 g_printerr ("Setting entry text: [%s]\n", txt
);
2382 gtk_entry_set_text (gee
->entry
, txt
);
2383 gee_delete_tooltip (gee
, TRUE
);
2387 * gnm_expr_entry_load_from_dep:
2388 * @gee: a #GnmExprEntry
2391 * Sets the text of the entry, and removes saved information about earlier
2395 gnm_expr_entry_load_from_dep (GnmExprEntry
*gee
, GnmDependent
const *dep
)
2397 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2398 g_return_if_fail (dep
!= NULL
);
2399 /* We have nowhere to store the text while frozen. */
2400 g_return_if_fail (gee
->freeze_count
== 0);
2402 if (dep
->texpr
!= NULL
) {
2406 parse_pos_init_dep (&pp
, dep
);
2407 text
= gnm_expr_top_as_string (dep
->texpr
, &pp
,
2410 gee_rangesel_reset (gee
);
2411 gtk_entry_set_text (gee
->entry
, text
);
2412 gee
->rangesel
.text_end
= strlen (text
);
2415 gee_delete_tooltip (gee
, TRUE
);
2417 gnm_expr_entry_load_from_text (gee
, "");
2421 * gnm_expr_entry_load_from_expr:
2422 * @gee: a #GnmExprEntry
2423 * @texpr: An expression
2424 * @pp: The parse position
2426 * Sets the text of the entry, and removes saved information about earlier
2430 gnm_expr_entry_load_from_expr (GnmExprEntry
*gee
,
2431 GnmExprTop
const *texpr
,
2432 GnmParsePos
const *pp
)
2434 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2435 /* We have nowhere to store the text while frozen. */
2436 g_return_if_fail (gee
->freeze_count
== 0);
2438 if (texpr
!= NULL
) {
2439 char *text
= gnm_expr_top_as_string
2440 (texpr
, pp
, gee_convs (gee
));
2441 gee_rangesel_reset (gee
);
2443 g_printerr ("Setting entry text: [%s]\n", text
);
2444 gtk_entry_set_text (gee
->entry
, text
);
2445 gee
->rangesel
.text_end
= strlen (text
);
2447 gee_delete_tooltip (gee
, TRUE
);
2449 gnm_expr_entry_load_from_text (gee
, "");
2453 * gnm_expr_entry_load_from_range:
2454 * @gee: a #GnmExprEntry
2458 * Returns: true if displayed range is different from input range. false
2461 * Sets the range selection and displays it in the entry text. If the widget
2462 * already contains a range selection, the new text replaces the
2463 * old. Otherwise, it is inserted at @pos.
2466 gnm_expr_entry_load_from_range (GnmExprEntry
*gee
,
2467 Sheet
*sheet
, GnmRange
const *r
)
2471 gboolean needs_change
= FALSE
;
2473 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
2474 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
2475 g_return_val_if_fail (r
!= NULL
, FALSE
);
2477 needs_change
= (gee
->flags
& GNM_EE_FULL_COL
&&
2478 !range_is_full (r
, sheet
, TRUE
)) ||
2479 (gee
->flags
& GNM_EE_FULL_ROW
&&
2480 !range_is_full (r
, sheet
, FALSE
));
2482 rs
= &gee
->rangesel
;
2484 ref
.a
.col
= r
->start
.col
; if (rs
->ref
.a
.col_relative
) ref
.a
.col
-= gee
->pp
.eval
.col
;
2485 ref
.b
.col
= r
->end
.col
; if (rs
->ref
.b
.col_relative
) ref
.b
.col
-= gee
->pp
.eval
.col
;
2486 ref
.a
.row
= r
->start
.row
; if (rs
->ref
.a
.row_relative
) ref
.a
.row
-= gee
->pp
.eval
.row
;
2487 ref
.b
.row
= r
->end
.row
; if (rs
->ref
.b
.row_relative
) ref
.b
.row
-= gee
->pp
.eval
.row
;
2489 if (rs
->ref
.a
.col
== ref
.a
.col
&&
2490 rs
->ref
.b
.col
== ref
.b
.col
&&
2491 rs
->ref
.a
.row
== ref
.a
.row
&&
2492 rs
->ref
.b
.row
== ref
.b
.row
&&
2493 rs
->ref
.a
.sheet
== sheet
&&
2494 (rs
->ref
.b
.sheet
== NULL
|| rs
->ref
.b
.sheet
== sheet
))
2495 return needs_change
; /* FIXME ??? */
2497 rs
->ref
.a
.col
= ref
.a
.col
;
2498 rs
->ref
.b
.col
= ref
.b
.col
;
2499 rs
->ref
.a
.row
= ref
.a
.row
;
2500 rs
->ref
.b
.row
= ref
.b
.row
;
2502 (sheet
!= gee
->sheet
|| !(gee
->flags
& GNM_EE_SHEET_OPTIONAL
)) ? sheet
: NULL
;
2503 rs
->ref
.b
.sheet
= NULL
;
2505 if (gee
->freeze_count
== 0)
2506 gee_rangesel_update_text (gee
);
2508 rs
->is_valid
= TRUE
; /* we just loaded it up */
2510 return needs_change
;
2514 * gnm_expr_entry_get_rangesel:
2515 * @gee: a #GnmExprEntry
2516 * @r: address to receive #GnmRange
2517 * @sheet: address to receive #sheet
2519 * Get the range selection. GnmRange is copied, Sheet is not. If sheet
2520 * argument is NULL, the corresponding value is not returned.
2521 * Returns TRUE if the returned range is indeed valid.
2522 * The resulting range is normalized.
2525 gnm_expr_entry_get_rangesel (GnmExprEntry
const *gee
,
2526 GnmRange
*r
, Sheet
**sheet
)
2529 Rangesel
const *rs
= &gee
->rangesel
;
2531 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
2533 gee_prepare_range (gee
, &ref
);
2535 ref
.a
.sheet
= eval_sheet (rs
->ref
.a
.sheet
, gee
->sheet
);
2536 ref
.b
.sheet
= eval_sheet (rs
->ref
.b
.sheet
, ref
.a
.sheet
);
2538 /* TODO : does not handle 3d, neither does this interface
2539 * should probably scrap the interface in favour of returning a
2543 *sheet
= ref
.a
.sheet
;
2546 gnm_cellpos_init_cellref (&r
->start
, &ref
.a
, &gee
->pp
.eval
, ref
.a
.sheet
);
2547 gnm_cellpos_init_cellref (&r
->end
, &ref
.b
, &gee
->pp
.eval
, ref
.b
.sheet
);
2548 range_normalize (r
);
2551 return rs
->is_valid
;
2555 * gnm_expr_entry_can_rangesel:
2556 * @gee: a #GnmExprEntry
2558 * Returns TRUE if a range selection is meaningful at current position.
2561 gnm_expr_entry_can_rangesel (GnmExprEntry
*gee
)
2565 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
2567 if (wbc_gtk_get_guru (gee
->wbcg
) != NULL
&&
2568 gee
== gee
->wbcg
->edit_line
.entry
)
2571 text
= gtk_entry_get_text (gee
->entry
);
2573 /* We need to be editing an expression */
2574 if (wbc_gtk_get_guru (gee
->wbcg
) == NULL
&&
2575 gnm_expr_char_start_p (text
) == NULL
)
2578 return (gnm_expr_entry_find_range (gee
));
2582 * gnm_expr_entry_parse:
2584 * @pp: a parse position
2585 * @start_sel: start range selection when things change.
2588 * Attempts to parse the content of the entry line honouring
2592 gnm_expr_entry_parse (GnmExprEntry
*gee
, GnmParsePos
const *pp
,
2593 GnmParseError
*perr
, gboolean start_sel
,
2594 GnmExprParseFlags flags
)
2598 GnmExprTop
const *texpr
;
2600 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2602 text
= gtk_entry_get_text (gee
->entry
);
2604 if (text
== NULL
|| text
[0] == '\0')
2608 g_printerr ("Parsing %s\n", text
);
2610 if ((gee
->flags
& GNM_EE_FORCE_ABS_REF
))
2611 flags
|= GNM_EXPR_PARSE_FORCE_ABSOLUTE_REFERENCES
;
2612 else if ((gee
->flags
& GNM_EE_FORCE_REL_REF
))
2613 flags
|= GNM_EXPR_PARSE_FORCE_RELATIVE_REFERENCES
;
2614 if (!(gee
->flags
& GNM_EE_SHEET_OPTIONAL
))
2615 flags
|= GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES
;
2617 /* First try parsing as a value. */
2619 GnmValue
*v
= get_matched_value (gee
);
2621 GODateConventions
const *date_conv
=
2622 workbook_date_conv (gee
->sheet
->workbook
);
2623 GnmExprTop
const *texpr
= gnm_expr_top_new_constant (v
);
2624 char *str
= format_value (gee
->constant_format
, v
, -1, date_conv
);
2626 g_printerr ("Setting entry text: [%s]\n", str
);
2627 gtk_entry_set_text (gee
->entry
, str
);
2633 /* Failing that, try as an expression. */
2634 texpr
= gnm_expr_parse_str (text
, pp
, flags
,
2635 gee_convs (gee
), perr
);
2640 if (gee
->flags
& GNM_EE_SINGLE_RANGE
) {
2641 GnmValue
*range
= gnm_expr_top_get_range (texpr
);
2642 if (range
== NULL
) {
2644 perr
->err
= g_error_new (1, PERR_SINGLE_RANGE
,
2645 _("Expecting a single range"));
2646 perr
->begin_char
= perr
->end_char
= 0;
2648 gnm_expr_top_unref (texpr
);
2651 value_release (range
);
2654 /* Reset the entry in case something changed */
2655 str
= gnm_expr_top_as_string (texpr
, pp
, gee_convs (gee
));
2656 if (strcmp (str
, text
)) {
2657 SheetControlGUI
*scg
= wbcg_cur_scg (gee
->wbcg
);
2658 Rangesel
const *rs
= &gee
->rangesel
;
2659 if (gee
== wbcg_get_entry_logical (gee
->wbcg
) &&
2660 start_sel
&& sc_sheet (GNM_SHEET_CONTROL (scg
)) == rs
->ref
.a
.sheet
) {
2661 scg_rangesel_bound (scg
,
2662 rs
->ref
.a
.col
, rs
->ref
.a
.row
,
2663 rs
->ref
.b
.col
, rs
->ref
.b
.row
);
2666 g_printerr ("Setting entry text: [%s]\n", str
);
2667 gtk_entry_set_text (gee
->entry
, str
);
2676 * gnm_expr_entry_get_text:
2679 * A small convenience routine. Think long and hard before using this.
2680 * There are lots of parse routines that serve the common case.
2682 * Returns: The content of the entry. Caller should not modify the result.
2685 gnm_expr_entry_get_text (GnmExprEntry
const *gee
)
2687 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2688 return gtk_entry_get_text (gee
->entry
);
2692 * gnm_expr_entry_parse_as_value:
2693 * @gee: GnmExprEntry
2694 * @sheet: the sheet where the cell range is evaluated.
2696 * Returns a (GnmValue *) of type VALUE_CELLRANGE if the @range was
2697 * succesfully parsed or NULL on failure.
2700 gnm_expr_entry_parse_as_value (GnmExprEntry
*gee
, Sheet
*sheet
)
2703 GnmExprParseFlags flags
= GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
;
2707 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2709 if ((gee
->flags
& GNM_EE_FORCE_ABS_REF
))
2710 flags
|= GNM_EXPR_PARSE_FORCE_ABSOLUTE_REFERENCES
;
2711 else if ((gee
->flags
& GNM_EE_FORCE_REL_REF
))
2712 flags
|= GNM_EXPR_PARSE_FORCE_RELATIVE_REFERENCES
;
2713 if (!(gee
->flags
& GNM_EE_SHEET_OPTIONAL
))
2714 flags
|= GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES
;
2716 txt
= gtk_entry_get_text (gnm_expr_entry_get_entry (gee
));
2718 parse_pos_init_sheet (&pp
, sheet
);
2719 v
= value_new_cellrange_parsepos_str (&pp
, txt
, flags
);
2721 if (!v
&& (gee
->flags
& GNM_EE_CONSTANT_ALLOWED
)) {
2722 GODateConventions
const *date_conv
=
2723 sheet
? workbook_date_conv (sheet
->workbook
) : NULL
;
2724 v
= format_match_number (txt
, NULL
, date_conv
);
2731 * gnm_expr_entry_parse_as_list:
2732 * @gee: GnmExprEntry
2733 * @sheet: the sheet where the cell range is evaluated. This really only needed if
2734 * the range given does not include a sheet specification.
2736 * Returns: (element-type GnmValue) (transfer full): a (GSList *)
2737 * or NULL on failure.
2740 gnm_expr_entry_parse_as_list (GnmExprEntry
*gee
, Sheet
*sheet
)
2742 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2744 return global_range_list_parse (sheet
,
2745 gtk_entry_get_text (gnm_expr_entry_get_entry (gee
)));
2749 * gnm_expr_entry_get_entry:
2750 * @gee: #GnmExprEntry
2752 * Returns: (transfer none): the associated #GtkEntry.
2755 gnm_expr_entry_get_entry (GnmExprEntry
*gee
)
2757 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2763 gnm_expr_entry_is_cell_ref (GnmExprEntry
*gee
, Sheet
*sheet
,
2764 gboolean allow_multiple_cell
)
2769 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
2771 val
= gnm_expr_entry_parse_as_value (gee
, sheet
);
2775 res
= ((VALUE_IS_CELLRANGE (val
)) &&
2776 (allow_multiple_cell
||
2777 ((val
->v_range
.cell
.a
.col
== val
->v_range
.cell
.b
.col
) &&
2778 (val
->v_range
.cell
.a
.row
== val
->v_range
.cell
.b
.row
))));
2779 value_release (val
);
2785 gnm_expr_entry_is_blank (GnmExprEntry
*gee
)
2790 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), FALSE
);
2792 entry
= gnm_expr_entry_get_entry (gee
);
2794 text
= gtk_entry_get_text (entry
);
2799 if (!g_unichar_isspace (g_utf8_get_char (text
)))
2801 text
= g_utf8_next_char (text
);
2808 gnm_expr_entry_global_range_name (GnmExprEntry
*gee
, Sheet
*sheet
)
2813 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), NULL
);
2815 val
= gnm_expr_entry_parse_as_value (gee
, sheet
);
2817 if (VALUE_IS_CELLRANGE (val
))
2818 text
= value_get_as_string (val
);
2819 value_release (val
);
2826 gnm_expr_entry_grab_focus (GnmExprEntry
*gee
, gboolean select_all
)
2828 g_return_if_fail (GNM_EXPR_ENTRY_IS (gee
));
2830 gtk_widget_grab_focus (GTK_WIDGET (gee
->entry
));
2832 gtk_editable_set_position (GTK_EDITABLE (gee
->entry
), -1);
2833 gtk_editable_select_region (GTK_EDITABLE (gee
->entry
), 0, -1);
2838 gnm_expr_entry_editing_canceled (GnmExprEntry
*gee
)
2840 g_return_val_if_fail (GNM_EXPR_ENTRY_IS (gee
), TRUE
);
2842 return gee
->editing_canceled
;
2845 /*****************************************************************************/
2848 gnm_expr_entry_disable_tips (GnmExprEntry
*gee
)
2850 g_return_if_fail (gee
!= NULL
);
2851 gee_delete_tooltip (gee
, TRUE
);
2852 gee
->tooltip
.enabled
= FALSE
;
2856 gnm_expr_entry_enable_tips (GnmExprEntry
*gee
)
2858 g_return_if_fail (gee
!= NULL
);
2859 gee
->tooltip
.enabled
= TRUE
;