1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * wbc-gtk-edit.c: Keeps track of the cell editing process.
6 * Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2000-2005 Miguel de Icaza (miguel@novell.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) version 3.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
27 #include "gnm-pane-impl.h"
28 #include "wbc-gtk-impl.h"
29 #include "workbook-view.h"
30 #include "workbook-priv.h"
31 #include "application.h"
32 #include "clipboard.h"
33 #include "complete-sheet.h"
35 #include "gnumeric-conf.h"
37 #include "style-color.h"
38 #include "sheet-control-gui-priv.h"
39 #include "sheet-style.h"
40 #include "sheet-view.h"
44 #include "gnm-format.h"
45 #include "number-match.h"
46 #include "parse-util.h"
48 #include "selection.h"
49 #include "validation.h"
51 #include "widgets/gnumeric-expr-entry.h"
53 #include "command-context.h"
55 #include <goffice/goffice.h>
57 #include <glib/gi18n-lib.h>
60 #define GNM_RESPONSE_REMOVE -1000
63 * Shuts down the auto completion engine
66 wbcg_auto_complete_destroy (WBCGtk
*wbcg
)
68 g_free (wbcg
->auto_complete_text
);
69 wbcg
->auto_complete_text
= NULL
;
71 if (wbcg
->edit_line
.signal_changed
) {
72 g_signal_handler_disconnect (wbcg_get_entry (wbcg
),
73 wbcg
->edit_line
.signal_changed
);
74 wbcg
->edit_line
.signal_changed
= 0;
77 if (wbcg
->auto_complete
!= NULL
) {
78 g_object_unref (wbcg
->auto_complete
);
79 wbcg
->auto_complete
= NULL
;
82 wbcg
->auto_completing
= FALSE
;
88 * @result: what should we do with the content
89 * @showed_dialog: If non-NULL will indicate if a dialog was displayed.
91 * Return: TRUE if editing completed successfully, or we were no editing.
94 wbcg_edit_finish (WBCGtk
*wbcg
, WBCEditResult result
,
95 gboolean
*showed_dialog
)
102 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
104 wbc
= GNM_WBC (wbcg
);
105 wbv
= wb_control_view (wbc
);
107 wbcg_focus_cur_scg (wbcg
);
109 gnm_expr_entry_close_tips (wbcg_get_entry_logical (wbcg
));
111 if (showed_dialog
!= NULL
)
112 *showed_dialog
= FALSE
;
114 /* Remove the range selection cursor if it exists */
115 if (NULL
!= wbcg
->rangesel
)
116 scg_rangesel_stop (wbcg
->rangesel
, result
== WBC_EDIT_REJECT
);
118 if (!wbcg_is_editing (wbcg
)) {
119 /* We may have a guru up even if we are not editing. remove it.
120 * Do NOT remove until later it if we are editing, it is possible
121 * that we may want to continue editing.
123 if (wbcg
->edit_line
.guru
!= NULL
) {
124 GtkWidget
*w
= wbcg
->edit_line
.guru
;
125 wbc_gtk_detach_guru (wbcg
);
126 gtk_widget_destroy (w
);
132 g_return_val_if_fail (IS_SHEET (wbcg
->editing_sheet
), TRUE
);
134 sheet
= wbcg
->editing_sheet
;
135 sv
= sheet_get_view (sheet
, wbv
);
137 /* Save the results before changing focus */
138 if (result
!= WBC_EDIT_REJECT
) {
139 ValidationStatus valid
= GNM_VALIDATION_STATUS_VALID
;
140 char *free_txt
= NULL
;
142 GnmStyle
const *mstyle
;
143 char const *expr_txt
= NULL
;
147 GSList
*selection
= selection_get_ranges (sv
, FALSE
);
149 GnmExprTop
const *texpr
= NULL
;
151 parse_pos_init_editpos (&pp
, sv
);
153 /* Array only works on single range. */
154 if (result
== WBC_EDIT_ACCEPT_ARRAY
&&
155 (selection
== NULL
|| selection
->next
!= NULL
))
156 result
= WBC_EDIT_ACCEPT_RANGE
;
158 /******* Check whether we would split a range ********/
161 case (WBC_EDIT_ACCEPT_RANGE
):
162 case (WBC_EDIT_ACCEPT_ARRAY
): {
163 if (sheet_ranges_split_region (sheet
, selection
,
164 GO_CMD_CONTEXT (wbc
), _("Set Text"))) {
165 range_fragment_free (selection
);
166 if (showed_dialog
!= NULL
)
167 *showed_dialog
= TRUE
;
171 if (result
== WBC_EDIT_ACCEPT_ARRAY
&&
172 sheet_range_contains_merges_or_arrays
173 (sheet
, selection
->data
,
174 GO_CMD_CONTEXT (wbc
), _("Set Text"),
176 range_fragment_free (selection
);
177 if (showed_dialog
!= NULL
)
178 *showed_dialog
= TRUE
;
184 case (WBC_EDIT_ACCEPT_WO_AC
):
185 case (WBC_EDIT_ACCEPT
): {
186 GnmCell
const *cell
= sheet_cell_get
187 (sheet
, sv
->edit_pos
.col
, sv
->edit_pos
.row
);
188 if (gnm_cell_is_nonsingleton_array (cell
)) {
189 gnm_cmd_context_error_splits_array (GO_CMD_CONTEXT (wbc
),
190 _("Set Text"), NULL
);
191 if (showed_dialog
!= NULL
)
192 *showed_dialog
= TRUE
;
193 range_fragment_free (selection
);
198 case (WBC_EDIT_REJECT
):
200 /* We should not be able to get here! */
205 /******* Check whether the range is locked ********/
208 case (WBC_EDIT_ACCEPT_RANGE
):
209 case (WBC_EDIT_ACCEPT_ARRAY
): {
210 if (cmd_selection_is_locked_effective (sheet
, selection
, wbc
,
212 range_fragment_free (selection
);
213 if (showed_dialog
!= NULL
)
214 *showed_dialog
= TRUE
;
219 case (WBC_EDIT_ACCEPT_WO_AC
):
220 case (WBC_EDIT_ACCEPT
): {
222 r
.end
= r
.start
= pp
.eval
;
224 if (cmd_cell_range_is_locked_effective (sheet
, &r
, wbc
,
226 range_fragment_free (selection
);
227 if (showed_dialog
!= NULL
)
228 *showed_dialog
= TRUE
;
233 case (WBC_EDIT_REJECT
):
235 /* We should not be able to get here! */
238 /*****************************************************/
240 txt
= wbcg_edit_get_display_text (wbcg
);
241 mstyle
= sheet_style_get (sheet
, sv
->edit_pos
.col
, sv
->edit_pos
.row
);
242 fmt
= gnm_cell_get_format (sheet_cell_fetch (sheet
, sv
->edit_pos
.col
,
245 value
= format_match (txt
, fmt
,
246 workbook_date_conv (sheet
->workbook
));
248 expr_txt
= gnm_expr_char_start_p (txt
);
250 value_release (value
);
252 /* NOTE : do not modify gnm_expr_char_start_p to exclude "-"
253 * it _can_ start an expression, which is required for rangesel
254 * it just isn't an expression. */
255 if (expr_txt
!= NULL
&& *expr_txt
!= '\0' && strcmp (expr_txt
, "-")) {
256 GnmExprTop
const *texpr_test
= NULL
;
259 parse_error_init (&perr
);
260 texpr_test
= gnm_expr_parse_str (expr_txt
,
261 &pp
, GNM_EXPR_PARSE_DEFAULT
, NULL
, &perr
);
262 /* Try adding a single extra closing paren to see if it helps */
263 if (texpr_test
== NULL
&& perr
.err
!= NULL
&&
264 perr
.err
->code
== PERR_MISSING_PAREN_CLOSE
) {
265 GnmParseError tmp_err
;
266 char *tmp
= g_strconcat (txt
, ")", NULL
);
267 parse_error_init (&tmp_err
);
268 texpr_test
= gnm_expr_parse_str (gnm_expr_char_start_p (tmp
),
269 &pp
, GNM_EXPR_PARSE_DEFAULT
,
271 parse_error_free (&tmp_err
);
273 if (texpr_test
!= NULL
) {
274 txt
= free_txt
= tmp
;
275 expr_txt
= gnm_expr_char_start_p (txt
);
280 if (texpr_test
== NULL
&& perr
.err
!= NULL
) {
281 ValidationStatus reedit
;
283 /* set focus _before_ selection. gtk2 seems to
284 * screw with selection in gtk_entry_grab_focus
285 * (no longer required now that we clear
286 * gtk-entry-select-on-focus) */
287 gtk_window_set_focus (wbcg_toplevel (wbcg
),
288 (GtkWidget
*) wbcg_get_entry (wbcg
));
290 if (perr
.begin_char
!= 0 || perr
.end_char
!= 0) {
291 int offset
= expr_txt
- txt
;
292 gtk_editable_select_region (GTK_EDITABLE (wbcg_get_entry (wbcg
)),
293 offset
+ perr
.begin_char
,
294 offset
+ perr
.end_char
);
296 gtk_editable_set_position (
297 GTK_EDITABLE (wbcg_get_entry (wbcg
)), -1);
299 reedit
= wb_control_validation_msg (GNM_WBC (wbcg
),
300 GNM_VALIDATION_STYLE_PARSE_ERROR
, NULL
,
302 if (showed_dialog
!= NULL
)
303 *showed_dialog
= TRUE
;
305 parse_error_free (&perr
);
306 if (reedit
== GNM_VALIDATION_STATUS_INVALID_EDIT
) {
307 range_fragment_free (selection
);
310 /* restore focus to sheet , or we'll leave edit
311 * mode only to jump right back in the new
312 * cell because it looks like someone just
313 * focused on the edit line (eg hit F2) */
314 wbcg_focus_cur_scg (wbcg
);
316 if (texpr_test
!= NULL
)
317 gnm_expr_top_unref (texpr_test
);
320 /* We only enter an array formula if the text is a formula */
321 if (result
== WBC_EDIT_ACCEPT_ARRAY
&& !expr_txt
)
322 result
= WBC_EDIT_ACCEPT_RANGE
;
324 if (result
== WBC_EDIT_ACCEPT_ARRAY
) {
325 GnmParsePos pp_array
;
326 GnmRange
*r
= selection
->data
;
328 parse_pos_init (&pp_array
, sheet
->workbook
, sheet
, r
->start
.col
, r
->start
.row
);
330 if ((texpr
= gnm_expr_parse_str
331 (expr_txt
, &pp_array
, GNM_EXPR_PARSE_DEFAULT
,
332 sheet_get_conventions (sheet
), NULL
)) == NULL
)
333 result
= WBC_EDIT_ACCEPT_RANGE
;
336 /* We need to save the information that we will temporarily overwrite */
337 /* We then assign the information. No need to worry about formatting */
338 /* Finally we can check the validation! */
341 case (WBC_EDIT_ACCEPT_RANGE
): {
344 for (l
= selection
; l
!= NULL
; l
= l
->next
) {
345 GnmRange
*r
= l
->data
;
346 u
= go_undo_combine (u
, clipboard_copy_range_undo (sheet
, r
));
348 for (l
= selection
; l
!= NULL
; l
= l
->next
) {
349 GnmRange
*r
= l
->data
;
350 /* We do this separately since there may be overlap between ranges */
351 sheet_range_set_text (&pp
, r
, txt
);
352 valid
= gnm_validation_eval_range (wbc
, sheet
, &sv
->edit_pos
, r
,
354 if (valid
!= GNM_VALIDATION_STATUS_VALID
)
359 case (WBC_EDIT_ACCEPT_ARRAY
): {
360 GnmRange
*r
= selection
->data
;
362 u
= go_undo_combine (u
, clipboard_copy_range_undo (sheet
, r
));
364 gnm_expr_top_ref (texpr
);
365 gnm_cell_set_array_formula (sheet
,
366 r
->start
.col
, r
->start
.row
,
367 r
->end
.col
, r
->end
.row
,
369 sheet_region_queue_recalc (sheet
, r
);
371 valid
= gnm_validation_eval_range (wbc
, sheet
, &sv
->edit_pos
, r
,
375 case (WBC_EDIT_ACCEPT_WO_AC
):
376 case (WBC_EDIT_ACCEPT
): {
380 range_init_cellpos (&r
, &sv
->edit_pos
);
381 u
= clipboard_copy_range_undo (sheet
, &r
);
383 cell
= sheet_cell_fetch (sheet
,
386 sheet_cell_set_text (cell
, txt
, wbcg
->edit_line
.markup
);
387 valid
= gnm_validation_eval (wbc
, mstyle
, sheet
, &sv
->edit_pos
, showed_dialog
);
390 case (WBC_EDIT_REJECT
):
392 /* We should not be able to get here! */
396 range_fragment_free (selection
);
398 /* We need to rebuild the original info first. */
403 /* Now we can respond to our validation information */
405 if (valid
!= GNM_VALIDATION_STATUS_VALID
) {
406 result
= WBC_EDIT_REJECT
;
407 if (valid
== GNM_VALIDATION_STATUS_INVALID_EDIT
) {
408 gtk_window_set_focus (wbcg_toplevel (wbcg
),
409 (GtkWidget
*) wbcg_get_entry (wbcg
));
412 gnm_expr_top_unref (texpr
);
416 if (result
== WBC_EDIT_ACCEPT_ARRAY
) {
417 cmd_area_set_array_expr (wbc
, sv
, texpr
);
420 PangoAttrList
*res_markup
= wbcg
->edit_line
.markup
421 ? pango_attr_list_copy (wbcg
->edit_line
.markup
)
423 if (result
== WBC_EDIT_ACCEPT
)
424 cmd_set_text (wbc
, sheet
, &sv
->edit_pos
, txt
, res_markup
, TRUE
);
425 else if (result
== WBC_EDIT_ACCEPT_WO_AC
)
426 cmd_set_text (wbc
, sheet
, &sv
->edit_pos
, txt
, res_markup
, FALSE
);
428 cmd_area_set_text (wbc
, sv
, txt
, res_markup
);
430 pango_attr_list_unref (res_markup
);
434 gnm_expr_top_unref (texpr
);
437 if (sv
== wb_control_cur_sheet_view (wbc
)) {
438 /* Redraw the cell contents in case there was a span */
439 GnmRange tmp
; tmp
.start
= tmp
.end
= sv
->edit_pos
;
440 sheet_range_bounding_box (sv
->sheet
, &tmp
);
441 gnm_sheet_view_redraw_range (wb_control_cur_sheet_view (wbc
), &tmp
);
444 /* Reload the entry widget with the original contents */
445 wb_view_edit_line_set (wbv
, wbc
);
449 wbcg
->editing
= FALSE
;
450 wbcg
->editing_sheet
= NULL
;
451 wbcg
->editing_cell
= NULL
;
453 if (wbcg
->edit_line
.guru
!= NULL
) {
454 GtkWidget
*w
= wbcg
->edit_line
.guru
;
455 wbc_gtk_detach_guru (wbcg
);
456 gtk_widget_destroy (w
);
459 if (wbcg
->edit_line
.signal_insert
) {
460 g_signal_handler_disconnect (wbcg_get_entry (wbcg
),
461 wbcg
->edit_line
.signal_insert
);
462 wbcg
->edit_line
.signal_insert
= 0;
464 if (wbcg
->edit_line
.signal_delete
) {
465 g_signal_handler_disconnect (wbcg_get_entry (wbcg
),
466 wbcg
->edit_line
.signal_delete
);
467 wbcg
->edit_line
.signal_delete
= 0;
469 if (wbcg
->edit_line
.signal_cursor_pos
) {
470 g_signal_handler_disconnect (wbcg_get_entry (wbcg
),
471 wbcg
->edit_line
.signal_cursor_pos
);
472 wbcg
->edit_line
.signal_cursor_pos
= 0;
474 if (wbcg
->edit_line
.signal_selection_bound
) {
475 g_signal_handler_disconnect (wbcg_get_entry (wbcg
),
476 wbcg
->edit_line
.signal_selection_bound
);
477 wbcg
->edit_line
.signal_selection_bound
= 0;
480 if (wbcg
->edit_line
.cell_attrs
!= NULL
) {
481 pango_attr_list_unref (wbcg
->edit_line
.cell_attrs
);
482 wbcg
->edit_line
.cell_attrs
= NULL
;
485 if (wbcg
->edit_line
.markup
) {
486 pango_attr_list_unref (wbcg
->edit_line
.markup
);
487 wbcg
->edit_line
.markup
= NULL
;
490 if (wbcg
->edit_line
.full_content
!= NULL
) {
491 pango_attr_list_unref (wbcg
->edit_line
.full_content
);
492 wbcg
->edit_line
.full_content
= NULL
;
495 if (wbcg
->edit_line
.cur_fmt
) {
496 pango_attr_list_unref (wbcg
->edit_line
.cur_fmt
);
497 wbcg
->edit_line
.cur_fmt
= NULL
;
500 /* set pos to 0, to ensure that if we start editing by clicking on the
501 * editline at the last position, we'll get the right style feedback */
502 gtk_editable_set_position ((GtkEditable
*) wbcg_get_entry (wbcg
), 0);
504 wb_control_update_action_sensitivity (wbc
);
506 if (!sheet
->workbook
->during_destruction
) {
507 /* restore focus to original sheet in case things were being selected
508 * on a different page. Do no go through the view, rangesel is
509 * specific to the control. */
510 wb_control_sheet_focus (wbc
, sheet
);
511 /* Only the edit sheet has an edit cursor */
512 scg_edit_stop (wbcg_cur_scg (wbcg
));
514 wbcg_auto_complete_destroy (wbcg
);
515 wb_control_style_feedback (wbc
, NULL
); /* in case markup messed with things */
521 workbook_edit_complete_notify (char const *text
, void *closure
)
523 WBCGtk
*wbcg
= closure
;
525 g_free (wbcg
->auto_complete_text
);
526 wbcg
->auto_complete_text
= g_strdup (text
);
528 scg_reload_item_edits (wbcg_cur_scg (wbcg
));
532 cb_entry_changed (G_GNUC_UNUSED GtkEntry
*entry
, WBCGtk
*wbcg
)
536 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
538 text
= gtk_entry_get_text (wbcg_get_entry (wbcg
));
539 text_len
= strlen (text
);
541 if (text_len
> wbcg
->auto_max_size
)
542 wbcg
->auto_max_size
= text_len
;
544 if (wbv
->do_auto_completion
&& wbcg
->auto_completing
)
545 gnm_complete_start (GNM_COMPLETE (wbcg
->auto_complete
), text
);
549 cb_set_attr_list_len (PangoAttribute
*a
, gpointer len_bytes
)
552 a
->end_index
= GPOINTER_TO_INT (len_bytes
);
557 cb_entry_insert_text (GtkEditable
*editable
,
563 char const *str
= gtk_entry_get_text (GTK_ENTRY (editable
));
564 int pos_in_bytes
= g_utf8_offset_to_pointer (str
, *pos_in_chars
) - str
;
566 if (wbcg
->auto_completing
&&
568 (!g_unichar_isalpha (g_utf8_get_char (text
)) ||
569 *pos_in_chars
!= gtk_entry_get_text_length (GTK_ENTRY (editable
)))) {
570 wbcg
->auto_completing
= FALSE
;
573 if (wbcg
->edit_line
.full_content
) {
574 (void)pango_attr_list_filter (wbcg
->edit_line
.cur_fmt
,
575 cb_set_attr_list_len
,
576 GINT_TO_POINTER (len_bytes
));
578 go_pango_attr_list_open_hole (wbcg
->edit_line
.full_content
,
579 pos_in_bytes
, len_bytes
);
580 pango_attr_list_splice (wbcg
->edit_line
.full_content
,
581 wbcg
->edit_line
.cur_fmt
,
584 go_pango_attr_list_open_hole (wbcg
->edit_line
.markup
,
585 pos_in_bytes
, len_bytes
);
586 pango_attr_list_splice (wbcg
->edit_line
.markup
,
587 wbcg
->edit_line
.cur_fmt
,
593 attrs_at_byte (PangoAttrList
*alist
, gint bytepos
)
595 PangoAttrIterator
*iter
= pango_attr_list_get_iterator (alist
);
596 GSList
*attrs
= NULL
;
600 pango_attr_iterator_range (iter
, &start
, &end
);
601 if (start
<= bytepos
&& bytepos
< end
) {
602 attrs
= pango_attr_iterator_get_attrs (iter
);
605 } while (pango_attr_iterator_next (iter
));
606 pango_attr_iterator_destroy (iter
);
611 /* Find the markup to be used for new characters. */
613 set_cur_fmt (WBCGtk
*wbcg
, int target_pos_in_bytes
)
615 PangoAttrList
*new_list
= pango_attr_list_new ();
616 GSList
*ptr
, *attrs
= attrs_at_byte (wbcg
->edit_line
.markup
, target_pos_in_bytes
);
618 for (ptr
= attrs
; ptr
!= NULL
; ptr
= ptr
->next
) {
619 PangoAttribute
*attr
= ptr
->data
;
620 attr
->start_index
= 0;
621 attr
->end_index
= INT_MAX
;
622 pango_attr_list_change (new_list
, attr
);
624 g_slist_free (attrs
);
625 if (wbcg
->edit_line
.cur_fmt
)
626 pango_attr_list_unref (wbcg
->edit_line
.cur_fmt
);
627 wbcg
->edit_line
.cur_fmt
= new_list
;
631 cb_entry_cursor_pos (WBCGtk
*wbcg
)
633 gint start
, end
, target_pos_in_chars
, target_pos_in_bytes
;
634 GtkEditable
*entry
= GTK_EDITABLE (wbcg_get_entry (wbcg
));
635 char const *str
= gtk_entry_get_text (GTK_ENTRY (entry
));
636 int edit_pos
= gtk_editable_get_position (entry
);
641 if (edit_pos
!= gtk_entry_get_text_length (GTK_ENTRY (entry
))) {
642 /* The cursor is no longer at the end. */
643 wbcg
->auto_completing
= FALSE
;
646 if (!wbcg
->edit_line
.full_content
)
649 /* 1) Use first selected character if there is a selection
650 * 2) Use the character just before the edit pos if it exists
651 * 3) Use the first character */
652 if (gtk_editable_get_selection_bounds (entry
, &start
, &end
))
653 target_pos_in_chars
= start
;
655 target_pos_in_chars
= edit_pos
;
656 if (target_pos_in_chars
> 0)
657 target_pos_in_chars
--;
660 target_pos_in_bytes
= g_utf8_offset_to_pointer (str
, target_pos_in_chars
) - str
;
662 /* Make bold/italic/etc buttons show the right thing. */
664 GnmStyle
*style
= gnm_style_new ();
665 GSList
*ptr
, *attrs
= attrs_at_byte (wbcg
->edit_line
.full_content
, target_pos_in_bytes
);
666 for (ptr
= attrs
; ptr
!= NULL
; ptr
= ptr
->next
) {
667 PangoAttribute
*attr
= ptr
->data
;
668 gnm_style_set_from_pango_attribute (style
, attr
);
669 pango_attribute_destroy (attr
);
671 wb_control_style_feedback (GNM_WBC (wbcg
), style
);
672 gnm_style_unref (style
);
673 g_slist_free (attrs
);
676 set_cur_fmt (wbcg
, target_pos_in_bytes
);
680 cb_entry_delete_text (GtkEditable
*editable
,
685 if (wbcg
->auto_completing
)
686 wbcg_auto_complete_destroy (wbcg
);
688 if (wbcg
->edit_line
.full_content
) {
689 char const *str
= gtk_entry_get_text (GTK_ENTRY (editable
));
690 guint start_pos_in_bytes
=
691 g_utf8_offset_to_pointer (str
, start_pos
) - str
;
692 guint end_pos_in_bytes
=
693 g_utf8_offset_to_pointer (str
, end_pos
) - str
;
694 guint len_bytes
= end_pos_in_bytes
- start_pos_in_bytes
;
696 go_pango_attr_list_erase (wbcg
->edit_line
.full_content
,
699 go_pango_attr_list_erase (wbcg
->edit_line
.markup
,
702 cb_entry_cursor_pos (wbcg
);
707 wbcg_edit_init_markup (WBCGtk
*wbcg
, PangoAttrList
*markup
)
711 GnmStyle
const *style
;
713 g_return_if_fail (wbcg
->edit_line
.full_content
== NULL
);
715 wbcg
->edit_line
.markup
= markup
;
717 sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
718 style
= sheet_style_get (sv
->sheet
, sv
->edit_pos
.col
, sv
->edit_pos
.row
);
719 wbcg
->edit_line
.cell_attrs
= gnm_style_generate_attrs_full (style
);
721 wbcg
->edit_line
.full_content
= pango_attr_list_copy (wbcg
->edit_line
.cell_attrs
);
722 pango_attr_list_splice (wbcg
->edit_line
.full_content
, markup
, 0, 0);
724 text
= gtk_entry_get_text (wbcg_get_entry (wbcg
));
725 set_cur_fmt (wbcg
, strlen (text
) - 1);
728 struct cb_set_or_unset
{
729 const PangoAttribute
*attr
;
734 cb_set_or_unset (PangoAttribute
*attr
, gpointer _data
)
736 struct cb_set_or_unset
*data
= _data
;
737 if (pango_attribute_equal (attr
, data
->attr
))
738 data
->set_in_ref
= TRUE
;
743 set_or_unset (PangoAttrList
*dst
, const PangoAttribute
*attr
,
746 struct cb_set_or_unset data
;
749 data
.set_in_ref
= FALSE
;
750 (void)pango_attr_list_filter (ref
, cb_set_or_unset
, &data
);
753 go_pango_attr_list_unset (dst
,
754 attr
->start_index
, attr
->end_index
,
757 pango_attr_list_change (dst
, pango_attribute_copy (attr
));
761 * wbcg_edit_add_markup:
763 * @attr: #PangoAttribute
765 * Absorbs the ref to @attr.
768 wbcg_edit_add_markup (WBCGtk
*wbcg
, PangoAttribute
*attr
)
770 GObject
*entry
= (GObject
*)wbcg_get_entry (wbcg
);
771 if (wbcg
->edit_line
.full_content
== NULL
)
772 wbcg_edit_init_markup (wbcg
, pango_attr_list_new ());
774 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry
),
775 &attr
->start_index
, &attr
->end_index
)) {
776 char const *str
= gtk_entry_get_text (GTK_ENTRY (entry
));
778 attr
->start_index
= g_utf8_offset_to_pointer (str
, attr
->start_index
) - str
;
779 attr
->end_index
= g_utf8_offset_to_pointer (str
, attr
->end_index
) - str
;
780 set_or_unset (wbcg
->edit_line
.full_content
, attr
,
781 wbcg
->edit_line
.cell_attrs
);
782 set_or_unset (wbcg
->edit_line
.markup
, attr
,
783 wbcg
->edit_line
.cell_attrs
);
786 /* the format to use when inserting text, we will resize it later */
787 attr
->start_index
= 0;
788 attr
->end_index
= INT_MAX
;
789 set_or_unset (wbcg
->edit_line
.cur_fmt
, attr
,
790 wbcg
->edit_line
.cell_attrs
);
791 pango_attribute_destroy (attr
);
792 wbc_gtk_markup_changer (wbcg
);
796 * wbcg_edit_get_markup:
799 * Returns: a potentially NULL PangoAttrList of the current markup while
800 * editing. The list belongs to @wbcg and should not be freed.
803 wbcg_edit_get_markup (WBCGtk
*wbcg
, gboolean full
)
805 return full
? wbcg
->edit_line
.full_content
: wbcg
->edit_line
.markup
;
810 cb_warn_toggled (GtkToggleButton
*button
, gboolean
*b
)
812 *b
= gtk_toggle_button_get_active (button
);
817 * @wbcg: The workbook to be edited.
818 * @blankp: If true, erase current cell contents first. If false, leave the
820 * @cursorp: If true, create an editing cursor in the current sheet. (If
821 * false, the text will be editing in the edit box above the sheet,
822 * but this is not handled by this function.)
824 * Initiate editing of a cell in the sheet. Note that we have two modes of
826 * 1) in-cell editing when you just start typing, and
827 * 2) above sheet editing when you hit F2.
829 * Returns TRUE if we did indeed start editing. Returns FALSE if the
830 * cell-to-be-edited was locked.
833 wbcg_edit_start (WBCGtk
*wbcg
,
834 gboolean blankp
, gboolean cursorp
)
836 /* We could save this, but the situation is rare, if confusing. */
837 static gboolean warn_on_text_format
= TRUE
;
839 SheetControlGUI
*scg
;
846 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
848 if (wbcg_is_editing (wbcg
))
851 /* Avoid recursion, and do not begin editing if a guru is up */
852 if (wbcg
->inside_editing
|| wbc_gtk_get_guru (wbcg
) != NULL
)
854 wbcg
->inside_editing
= TRUE
;
856 wbv
= wb_control_view (GNM_WBC (wbcg
));
857 sv
= wb_control_cur_sheet_view (GNM_WBC (wbcg
));
858 scg
= wbcg_cur_scg (wbcg
);
860 col
= sv
->edit_pos
.col
;
861 row
= sv
->edit_pos
.row
;
863 /* don't edit a locked cell */
864 /* TODO : extend this to disable edits that cannot succeed
865 * like editing a single cell of an array. I think we have enough
866 * information if we look at the selection.
868 if (wb_view_is_protected (wbv
, TRUE
) &&
869 gnm_style_get_contents_locked (sheet_style_get (sv
->sheet
, col
, row
))) {
870 char *pos
= g_strdup_printf ( _("%s!%s is locked"),
871 sv
->sheet
->name_quoted
, cell_coord_name (col
, row
));
872 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg
), pos
,
873 wb_view_is_protected (wbv
, FALSE
)
874 ? _("Unprotect the workbook to enable editing.")
875 : _("Unprotect the sheet to enable editing."));
876 wbcg
->inside_editing
= FALSE
;
881 cell
= sheet_cell_get (sv
->sheet
, col
, row
);
883 warn_on_text_format
&&
884 go_format_is_text (gnm_cell_get_format (cell
)) &&
885 (gnm_cell_has_expr (cell
) || !VALUE_IS_STRING (cell
->value
))) {
886 gint res
; /* Using GtkResponseType would yield a warning on the switch */
890 GtkWidget
*d
= gnm_message_dialog_create
891 (wbcg_toplevel (wbcg
),
892 GTK_DIALOG_DESTROY_WITH_PARENT
,
894 _("You are about to edit a cell with \"text\" format."),
895 _("The cell does not currently contain text, though, so if "
896 "you go on editing then the contents will be turned into "
898 gtk_dialog_add_button (GTK_DIALOG (d
), GTK_STOCK_EDIT
, GTK_RESPONSE_OK
);
899 go_gtk_dialog_add_button
900 (GTK_DIALOG (d
), _("Remove format"), GTK_STOCK_REMOVE
,
901 GNM_RESPONSE_REMOVE
);
902 gtk_dialog_add_button (GTK_DIALOG (d
), GNM_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
903 gtk_dialog_set_default_response (GTK_DIALOG (d
), GTK_RESPONSE_CANCEL
);
905 check
= gtk_check_button_new_with_label (_("Show this dialog next time."));
906 g_signal_connect (check
, "toggled", G_CALLBACK (cb_warn_toggled
), &warn_on_text_format
);
907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check
), TRUE
);
908 align
= gtk_alignment_new (0.5, 0.5, 0, 0);
909 gtk_container_add (GTK_CONTAINER (align
), check
);
910 gtk_widget_show_all (align
);
911 gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d
))), align
, TRUE
, TRUE
, 0);
912 res
= go_gtk_dialog_run (GTK_DIALOG (d
), wbcg_toplevel (wbcg
));
915 case GNM_RESPONSE_REMOVE
: {
916 GnmStyle
*style
= gnm_style_new ();
917 gnm_style_set_format (style
, go_format_general ());
918 if (!cmd_selection_format (GNM_WBC (wbcg
),
924 case GTK_RESPONSE_CANCEL
:
925 wbcg
->inside_editing
= FALSE
;
927 case GTK_RESPONSE_OK
:
932 gnm_app_clipboard_unant ();
935 gtk_entry_set_text (wbcg_get_entry (wbcg
), "");
936 else if (cell
!= NULL
) {
937 gboolean quoted
= FALSE
;
939 text
= gnm_cell_get_text_for_editing (cell
, "ed
, &cursor_pos
);
942 gtk_entry_set_text (wbcg_get_entry (wbcg
), text
);
944 if (cell
->value
!= NULL
) {
945 GOFormat
const *fmt
= VALUE_FMT (cell
->value
);
946 if (fmt
!= NULL
&& go_format_is_markup (fmt
)) {
947 PangoAttrList
*markup
=
948 pango_attr_list_copy ((PangoAttrList
*)go_format_get_markup (fmt
));
950 go_pango_attr_list_open_hole (markup
, 0, 1);
951 wbcg_edit_init_markup (wbcg
, markup
);
956 gnm_expr_entry_set_scg (wbcg
->edit_line
.entry
, scg
);
957 gnm_expr_entry_set_flags (wbcg
->edit_line
.entry
,
958 GNM_EE_SHEET_OPTIONAL
| GNM_EE_FORMULA_ONLY
,
959 GNM_EE_SINGLE_RANGE
| GNM_EE_SHEET_OPTIONAL
| GNM_EE_FORMULA_ONLY
| GNM_EE_FORCE_REL_REF
| GNM_EE_FORCE_ABS_REF
);
960 scg_edit_start (scg
);
962 /* Redraw the cell contents in case there was a span */
963 sheet_redraw_region (sv
->sheet
, col
, row
, col
, row
);
965 if (cursorp
&& /* autocompletion code will not work in the edit line */
966 wbv
->do_auto_completion
&&
967 (text
== NULL
|| g_unichar_isalpha (g_utf8_get_char (text
)))) {
968 wbcg
->auto_complete
= gnm_complete_sheet_new (
970 workbook_edit_complete_notify
, wbcg
);
971 wbcg
->auto_completing
= TRUE
;
972 wbcg
->auto_max_size
= 0;
974 wbcg
->auto_complete
= NULL
;
976 /* Give the focus to the edit line */
978 gtk_window_set_focus (wbcg_toplevel (wbcg
),
979 (GtkWidget
*) wbcg_get_entry (wbcg
));
981 wbcg
->editing
= TRUE
;
982 wbcg
->editing_sheet
= sv
->sheet
;
983 wbcg
->editing_cell
= cell
;
985 /* If this assert fails, it means editing was not shut down
988 g_return_val_if_fail (wbcg
->edit_line
.signal_changed
== 0, TRUE
);
989 wbcg
->edit_line
.signal_changed
= g_signal_connect (
990 G_OBJECT (wbcg_get_entry (wbcg
)),
992 G_CALLBACK (cb_entry_changed
), wbcg
);
993 wbcg
->edit_line
.signal_insert
= g_signal_connect (
994 G_OBJECT (wbcg_get_entry (wbcg
)),
996 G_CALLBACK (cb_entry_insert_text
), wbcg
);
997 wbcg
->edit_line
.signal_delete
= g_signal_connect (
998 G_OBJECT (wbcg_get_entry (wbcg
)),
1000 G_CALLBACK (cb_entry_delete_text
), wbcg
);
1001 wbcg
->edit_line
.signal_cursor_pos
= g_signal_connect_swapped (
1002 G_OBJECT (wbcg_get_entry (wbcg
)),
1003 "notify::cursor-position",
1004 G_CALLBACK (cb_entry_cursor_pos
), wbcg
);
1005 wbcg
->edit_line
.signal_selection_bound
= g_signal_connect_swapped (
1006 G_OBJECT (wbcg_get_entry (wbcg
)),
1007 "notify::selection-bound",
1008 G_CALLBACK (cb_entry_cursor_pos
), wbcg
);
1011 wb_control_update_action_sensitivity (GNM_WBC (wbcg
));
1013 wbcg
->inside_editing
= FALSE
;
1015 gtk_editable_set_position (GTK_EDITABLE (wbcg_get_entry (wbcg
)), cursor_pos
);
1021 * wbcg_insert_object:
1023 * @so: The object the needs to be placed
1025 * Takes a newly created #SheetObject that has not yet been realized and
1026 * prepares to place it on the sheet.
1028 * NOTE : Absorbs a reference to the object.
1031 wbcg_insert_object (WBCGtk
*wbcg
, SheetObject
*so
)
1034 SheetControlGUI
*scg
;
1036 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1037 g_return_if_fail (GNM_IS_SO (so
));
1039 wbcg_insert_object_clear (wbcg
);
1041 npages
= wbcg_get_n_scg (wbcg
);
1042 for (i
= 0; i
< npages
; i
++) {
1043 if (NULL
!= (scg
= wbcg_get_nth_scg (wbcg
, i
))) {
1044 scg_object_unselect (scg
, NULL
);
1045 scg_cursor_visible (scg
, FALSE
);
1046 scg_set_display_cursor (scg
);
1047 sc_unant (GNM_SHEET_CONTROL (scg
));
1050 /* we can't set wbcg->new_object before now because if one sheet has a
1051 * selected object, the new object will be destroyed by the loop
1052 * above. See #669648. */
1053 wbcg
->new_object
= so
;
1054 wb_control_update_action_sensitivity (GNM_WBC (wbcg
));
1058 * wbcg_insert_object_clear:
1061 * If we are preparing to insert a new object, unref the object, and restore
1062 * a normal state to the scgs that was changed in wbcg_insert_object
1063 * (e.g., visibility of cursors)
1066 wbcg_insert_object_clear (WBCGtk
*wbcg
)
1068 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1070 if (NULL
!= wbcg
->new_object
) {
1072 SheetControlGUI
*scg
;
1074 g_object_unref (wbcg
->new_object
);
1075 wbcg
->new_object
= NULL
;
1077 npages
= wbcg_get_n_scg (wbcg
);
1078 for (i
= 0; i
< npages
; i
++)
1079 if (NULL
!= (scg
= wbcg_get_nth_scg (wbcg
, i
)))
1080 scg_cursor_visible (scg
, TRUE
);
1089 * Returns: (transfer none): the #GtkEntry associated with the current GnmExprEntry
1092 wbcg_get_entry (WBCGtk
const *wbcg
)
1094 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
1095 g_return_val_if_fail (wbcg
!= NULL
, NULL
);
1097 return gnm_expr_entry_get_entry (wbcg
->edit_line
.entry
);
1101 * wbcg_get_entry_logical:
1104 * Returns: (transfer none): the logical (allowing redirection via
1105 * wbcg_set_entry for gurus) #GnmExprEntry
1108 wbcg_get_entry_logical (WBCGtk
const *wbcg
)
1110 g_return_val_if_fail (wbcg
!= NULL
, NULL
);
1112 if (wbcg
->edit_line
.temp_entry
!= NULL
)
1113 return wbcg
->edit_line
.temp_entry
;
1115 return wbcg
->edit_line
.entry
;
1119 * wbcg_get_entry_underlying:
1122 * Returns: (transfer none): the #GtkEntry associated with the logical
1126 wbcg_get_entry_underlying (WBCGtk
const *wbcg
)
1128 GnmExprEntry
*ee
= wbcg_get_entry_logical (wbcg
);
1129 GtkEntry
*entry
= gnm_expr_entry_get_entry (ee
);
1130 return GTK_WIDGET (entry
);
1134 wbcg_set_entry (WBCGtk
*wbcg
, GnmExprEntry
*entry
)
1136 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1138 if (wbcg
->edit_line
.temp_entry
!= entry
) {
1139 scg_rangesel_stop (wbcg_cur_scg (wbcg
), FALSE
);
1140 wbcg
->edit_line
.temp_entry
= entry
;
1145 * wbcg_entry_has_logical:
1148 * Returns: TRUE if wbcg_set_entry has redirected the edit_entry.
1151 wbcg_entry_has_logical (WBCGtk
const *wbcg
)
1153 return (wbcg
->edit_line
.temp_entry
!= NULL
);
1156 /****************************************************************************/
1159 wbcg_edit_attach_guru_main (WBCGtk
*wbcg
, GtkWidget
*guru
)
1161 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
1163 g_return_if_fail (guru
!= NULL
);
1164 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1165 g_return_if_fail (wbcg
->edit_line
.guru
== NULL
);
1167 /* Make sure we don't have anything anted.
1168 * this protects against two anted regions showing up
1170 gnm_app_clipboard_unant ();
1172 /* don't set end 'End' mode when a dialog comes up */
1173 wbcg_set_end_mode (wbcg
, FALSE
);
1175 wbcg
->edit_line
.guru
= guru
;
1176 gtk_editable_set_editable (GTK_EDITABLE (wbcg_get_entry (wbcg
)), FALSE
);
1177 wb_control_update_action_sensitivity (wbc
);
1178 wb_control_menu_state_update (wbc
, MS_GURU_MENU_ITEMS
);
1180 g_signal_connect_object (guru
, "destroy",
1181 G_CALLBACK (wbc_gtk_detach_guru
), wbcg
, G_CONNECT_SWAPPED
);
1185 cb_guru_set_focus (G_GNUC_UNUSED GtkWidget
*window
,
1186 GtkWidget
*focus_widget
, WBCGtk
*wbcg
)
1188 GnmExprEntry
*gee
= NULL
;
1189 if (focus_widget
!= NULL
&&
1190 GNM_EXPR_ENTRY_IS (gtk_widget_get_parent (focus_widget
)))
1191 gee
= GNM_EXPR_ENTRY (gtk_widget_get_parent (focus_widget
));
1192 wbcg_set_entry (wbcg
, gee
);
1195 /****************************************************************************/
1198 wbc_gtk_attach_guru (WBCGtk
*wbcg
, GtkWidget
*guru
)
1200 g_return_if_fail (guru
!= NULL
);
1201 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1203 wbcg_edit_attach_guru_main (wbcg
, guru
);
1204 g_signal_connect_object (G_OBJECT (guru
), "set-focus",
1205 G_CALLBACK (cb_guru_set_focus
), wbcg
, 0);
1209 wbc_gtk_attach_guru_with_unfocused_rs (WBCGtk
*wbcg
, GtkWidget
*guru
,
1212 g_return_if_fail (guru
!= NULL
);
1213 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1215 wbcg_edit_attach_guru_main (wbcg
, guru
);
1217 if (gnm_conf_get_dialogs_rs_unfocused ()) {
1219 wbcg_set_entry (wbcg
, gee
);
1221 g_signal_connect (G_OBJECT (guru
), "set-focus",
1222 G_CALLBACK (cb_guru_set_focus
), wbcg
);
1226 wbc_gtk_detach_guru (WBCGtk
*wbcg
)
1228 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
1230 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
1232 /* don't sit end 'End' mode when a dialog comes up */
1233 wbcg_set_end_mode (wbcg
, FALSE
);
1234 if (wbcg
->edit_line
.guru
== NULL
)
1237 wbcg_set_entry (wbcg
, NULL
);
1238 wbcg
->edit_line
.guru
= NULL
;
1239 gtk_editable_set_editable (GTK_EDITABLE (wbcg_get_entry (wbcg
)), TRUE
);
1240 wb_control_update_action_sensitivity (wbc
);
1241 wb_control_menu_state_update (wbc
, MS_GURU_MENU_ITEMS
);
1248 * Returns: (transfer none): the guru attached to the workbook view.
1251 wbc_gtk_get_guru (WBCGtk
const *wbcg
)
1253 return wbcg
->edit_line
.guru
;
1256 /****************************************************************************/
1259 auto_complete_matches (WBCGtk
*wbcg
)
1261 if (!wbcg
->auto_completing
|| wbcg
->auto_complete_text
== NULL
)
1264 GtkEntry
*entry
= wbcg_get_entry (wbcg
);
1265 char const *text
= gtk_entry_get_text (entry
);
1266 size_t len
= strlen (text
);
1267 return strncmp (text
, wbcg
->auto_complete_text
, len
) == 0;
1272 * Returns the text that must be shown by the editing entry, takes
1273 * into account the auto-completion text.
1276 wbcg_edit_get_display_text (WBCGtk
*wbcg
)
1278 if (auto_complete_matches (wbcg
))
1279 return wbcg
->auto_complete_text
;
1281 return gtk_entry_get_text (wbcg_get_entry (wbcg
));
1285 wbc_gtk_init_editline (WBCGtk
*wbcg
)
1287 g_assert (GNM_IS_WBC_GTK (wbcg
));
1288 g_assert (wbcg
->edit_line
.entry
== NULL
);
1290 wbcg
->edit_line
.entry
= g_object_new (GNM_EXPR_ENTRY_TYPE
,
1294 wbcg
->edit_line
.temp_entry
= NULL
;
1295 wbcg
->edit_line
.guru
= NULL
;
1296 wbcg
->edit_line
.signal_changed
= 0;
1297 wbcg
->edit_line
.full_content
= NULL
;
1298 wbcg
->edit_line
.markup
= NULL
;
1299 wbcg
->edit_line
.cur_fmt
= NULL
;