ssdiff: move comparison engine into its own file.
[gnumeric.git] / src / item-edit.c
blob0907e46ed47d0fdb29c8cbdca3a662c9ba388989
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * item-edit.c : Edit facilities for worksheets.
5 * (C) 1999-2004 Miguel de Icaza & Jody Goldberg
7 * This module provides:
8 * * Integration of an in-sheet text editor (GtkEntry) with the Workbook
9 * GtkEntry as a canvas item.
11 * * Feedback on expressions in the spreadsheet (referenced cells or
12 * ranges are highlighted on the spreadsheet).
14 #include <gnumeric-config.h>
15 #include "gnm-i18n.h"
16 #include "gnumeric.h"
17 #include "item-edit.h"
18 #include "gnm-pane-impl.h"
20 #include "item-cursor.h"
21 #include "sheet-control-gui-priv.h"
22 #include "sheet.h"
23 #include "sheet-view.h"
24 #include "sheet-style.h"
25 #include "sheet-merge.h"
26 #include "value.h"
27 #include "ranges.h"
28 #include "style.h"
29 #include "style-font.h"
30 #include "style-color.h"
31 #include "pattern.h"
32 #include "parse-util.h"
33 #include "workbook.h"
34 #include "wbc-gtk.h"
35 #include "gui-util.h"
36 #include "widgets/gnumeric-expr-entry.h"
37 #define GNUMERIC_ITEM "EDIT"
39 #include <gtk/gtk.h>
40 #include <gsf/gsf-impl-utils.h>
41 #include <string.h>
42 #include <goffice/goffice.h>
44 static GocItemClass *parent_class;
46 struct _GnmItemEdit {
47 GocItem item;
49 SheetControlGUI *scg;
50 GtkEntry *entry; /* Utility pointer to the workbook entry */
52 PangoLayout *layout;
54 /* Where are we */
55 GnmCellPos pos;
56 gboolean cursor_visible;
57 guint blink_timer;
58 int sel_start;
60 GnmFont *gfont;
61 GnmStyle *style;
64 typedef GocItemClass GnmItemEditClass;
66 /* The arguments we take */
67 enum {
68 ARG_0,
69 ARG_SHEET_CONTROL_GUI /* The SheetControlGUI * argument */
72 static void
73 get_top_left (GnmItemEdit const *ie, int *top, int *left, PangoDirection dir)
75 GnmVAlign const align = gnm_style_get_align_v (ie->style);
76 GocItem *item = GOC_ITEM (ie);
77 GocCanvas *canvas = item->canvas;
78 double l = ((goc_canvas_get_direction (canvas) == GOC_DIRECTION_LTR && dir == PANGO_DIRECTION_RTL)
79 || (goc_canvas_get_direction (canvas) == GOC_DIRECTION_RTL && dir != PANGO_DIRECTION_RTL))?
80 item->x1 - 1: item->x0;
82 goc_canvas_c2w (canvas, l, item->y0, left, top);
84 if (align == GNM_VALIGN_CENTER || align == GNM_VALIGN_DISTRIBUTED ||
85 align == GNM_VALIGN_BOTTOM) {
86 int text_height, height = (int)(ie->item.y1 - ie->item.y0) * canvas->pixels_per_unit;
87 pango_layout_get_pixel_size (ie->layout, NULL, &text_height);
88 *top += (align != GNM_VALIGN_BOTTOM)
89 ? (height - text_height)/2
90 : (height - text_height);
94 static gboolean
95 gnm_apply_attribute_list_cb (PangoAttribute *attribute,
96 gpointer data)
98 PangoAttrList *attrs = data;
99 if (attribute->klass->type == PANGO_ATTR_FOREGROUND) {
100 PangoAttribute *copy = pango_attribute_copy (attribute);
101 pango_attr_list_change (attrs, copy);
103 return FALSE;
106 static void
107 gnm_apply_attribute_list (PangoAttrList *attrs, PangoAttrList *added_attrs)
109 if (added_attrs == NULL)
110 return;
111 pango_attr_list_unref (pango_attr_list_filter (added_attrs,
112 gnm_apply_attribute_list_cb,
113 attrs));
116 static void
117 item_edit_draw (GocItem const *item, cairo_t *cr)
119 GnmItemEdit const *ie = GNM_ITEM_EDIT (item);
120 GtkStyleContext *context = goc_item_get_style_context (item);
121 int top, left;
122 GOColor color;
123 int x0, y0, x1, y1; /* in widget coordinates */
124 int start, end;
125 PangoRectangle pos, weak;
126 char const *text = gtk_entry_get_text (ie->entry);
127 GdkRGBA fcolor;
128 PangoDirection dir = pango_find_base_dir (text, -1);
129 PangoAttrList *entry_attributes
130 = g_object_get_data(G_OBJECT (ie->entry),
131 "gnm:range-attributes");
133 if (entry_attributes != NULL) {
134 if (go_pango_attr_list_is_empty (entry_attributes))
135 entry_attributes = NULL;
136 else
137 entry_attributes = pango_attr_list_ref (entry_attributes);
140 get_top_left (ie, &top, &left, dir);
141 if (goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL) {
142 goc_canvas_c2w (item->canvas, item->x1, item->y0, &x0, &y0);
143 goc_canvas_c2w (item->canvas, item->x0, item->y1, &x1, &y1);
144 } else {
145 goc_canvas_c2w (item->canvas, item->x0, item->y0, &x0, &y0);
146 goc_canvas_c2w (item->canvas, item->x1, item->y1, &x1, &y1);
149 cairo_save (cr);
151 cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
152 /* avoid a weak/strong cursor to extend outside the item,
153 a better fix would be to have enough room for cursors */
154 cairo_clip (cr);
155 if (gnm_pattern_background_set (ie->style, cr, FALSE, NULL)) {
156 cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
157 cairo_fill (cr);
158 } else {
159 gtk_render_background (context, cr, x0, y0, x1 - x0, y1 - y0);
162 /* set the default color */
163 gnm_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fcolor);
164 gdk_cairo_set_source_rgba (cr, &fcolor);
165 color = go_color_from_gdk_rgba (&fcolor, NULL);
167 if (dir == PANGO_DIRECTION_RTL) {
168 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
169 left -= pos.width + GNM_COL_MARGIN / goc_canvas_get_pixels_per_unit (item->canvas);
171 cairo_move_to (cr, left, top);
172 gtk_editable_get_selection_bounds (GTK_EDITABLE (ie->entry), &start, &end);
173 if (start != end) {
174 PangoAttribute *attr;
175 PangoAttrList *orig = pango_attr_list_ref (pango_layout_get_attributes (ie->layout)),
176 *attrs = pango_attr_list_copy (orig);
177 start = g_utf8_offset_to_pointer (text, start) - text;
178 end = g_utf8_offset_to_pointer (text, end) - text;
179 color = gnm_style_get_back_color (ie->style)->go_color;
180 attr = go_color_to_pango (color, FALSE);
181 attr->start_index = start;
182 attr->end_index = end;
183 pango_attr_list_change (attrs, attr);
184 color = gnm_style_get_font_color (ie->style)->go_color;
185 attr = go_color_to_pango (color, TRUE);
186 attr->start_index = start;
187 attr->end_index = end;
188 pango_attr_list_change (attrs, attr);
189 gnm_apply_attribute_list (attrs, entry_attributes);
190 pango_layout_set_attributes (ie->layout, attrs);
191 pango_attr_list_unref (attrs);
192 pango_cairo_show_layout (cr, ie->layout);
193 pango_layout_set_attributes (ie->layout, orig);
194 pango_attr_list_unref (orig);
195 } else if (entry_attributes != NULL) {
196 PangoAttrList *orig = pango_attr_list_ref (pango_layout_get_attributes (ie->layout)),
197 *attrs = pango_attr_list_copy (orig);
198 gnm_apply_attribute_list (attrs, entry_attributes);
199 pango_layout_set_attributes (ie->layout, attrs);
200 pango_attr_list_unref (attrs);
201 pango_cairo_show_layout (cr, ie->layout);
202 pango_layout_set_attributes (ie->layout, orig);
203 pango_attr_list_unref (orig);
204 } else {
205 pango_cairo_show_layout (cr, ie->layout);
207 pango_attr_list_unref (entry_attributes);
209 if (ie->cursor_visible) {
210 int cursor_pos = gtk_editable_get_position (GTK_EDITABLE (ie->entry));
211 double incr = (dir == PANGO_DIRECTION_RTL)? -.5: .5, x, ytop, ybottom;
212 pango_layout_get_cursor_pos (ie->layout,
213 g_utf8_offset_to_pointer (text, cursor_pos) - text, &pos, &weak);
214 cairo_set_line_width (cr, 1.);
215 cairo_set_dash (cr, NULL, 0, 0.);
216 cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
217 cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
218 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (gnm_style_get_back_color (ie->style)->go_color ^ 0xffffff00));
219 x = left + PANGO_PIXELS (pos.x) + incr;
220 ytop = top + PANGO_PIXELS (pos.y);
221 ybottom = top + PANGO_PIXELS (pos.y + pos.height) - 1;
222 cairo_move_to (cr, x, ytop);
223 cairo_line_to (cr, x, ybottom);
224 cairo_stroke (cr);
225 if (weak.x != pos.x) {
226 double w = (ybottom - ytop) / 4.;
227 GOColor color1 = color ^ 0xffffff00;
228 x += incr;
229 cairo_move_to (cr, x, ybottom);
230 cairo_line_to (cr, x + w * incr, ybottom - w / 2.);
231 cairo_line_to (cr, x, ybottom - w);
232 cairo_close_path (cr);
233 cairo_fill (cr);
234 color = GO_COLOR_INTERPOLATE (color, color1, 0.5);
235 x = left + PANGO_PIXELS (weak.x) - incr;
236 ytop = top + PANGO_PIXELS (weak.y);
237 ybottom = top + PANGO_PIXELS (weak.y + weak.height) - 1;
238 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (color));
239 cairo_move_to (cr, x, ytop);
240 cairo_line_to (cr, x, ybottom);
241 cairo_stroke (cr);
242 x -= incr;
243 cairo_move_to (cr, x, ybottom);
244 cairo_line_to (cr, x - w * incr, ybottom - w / 2.);
245 cairo_line_to (cr, x, ybottom - w);
246 cairo_close_path (cr);
247 cairo_fill (cr);
250 cairo_restore (cr);
253 static double
254 item_edit_distance (GocItem *item, double cx, double cy,
255 GocItem **actual_item)
257 *actual_item = NULL;
258 if ((cx < item->x0) || (cy < item->y0) || (cx >= item->x1) || (cy >= item->y1))
259 return 10000.0;
261 *actual_item = item;
262 return 0.0;
265 static gboolean
266 item_edit_enter_notify (GocItem *item, G_GNUC_UNUSED double x, G_GNUC_UNUSED double y)
268 gnm_widget_set_cursor_type (GTK_WIDGET (item->canvas), GDK_XTERM);
269 return TRUE;
272 static int
273 item_edit_button_pressed (GocItem *item, int button, double x, double y)
275 if (button == 1) {
276 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
277 GtkEditable *ed = GTK_EDITABLE (ie->entry);
278 int target_index, trailing;
279 char const *text = pango_layout_get_text (ie->layout);
280 PangoDirection dir = pango_find_base_dir (text, -1);
281 PangoRectangle pos;
282 GocDirection cdir = goc_canvas_get_direction (item->canvas);
284 if (cdir == GOC_DIRECTION_RTL)
285 x = item->x1 - x - 1;
286 else
287 x = x - item->x0;
288 y = y - item->y0;
289 if (dir == PANGO_DIRECTION_RTL) {
290 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
291 x -= item->x1 - item->x0 - (pos.width
292 + 2 * GNM_COL_MARGIN) / goc_canvas_get_pixels_per_unit (item->canvas);
294 /* the layout might be zoomed, we need to adjust x and y accordingly */
295 x *= goc_canvas_get_pixels_per_unit (item->canvas);
296 y *= goc_canvas_get_pixels_per_unit (item->canvas);
298 if (pango_layout_xy_to_index (ie->layout,
299 x * PANGO_SCALE, y * PANGO_SCALE,
300 &target_index, &trailing)) {
301 int preedit = GNM_PANE (item->canvas)->preedit_length;
302 gint cur_index = gtk_editable_get_position (ed);
303 cur_index = g_utf8_offset_to_pointer (text, cur_index) - text;
305 if (target_index >= cur_index && preedit > 0) {
306 if (target_index < (cur_index + preedit)) {
307 target_index = cur_index;
308 trailing = 0;
309 } else
310 target_index -= preedit;
312 } else if (x < 0) {
313 /* the click occured after text end (#388342) */
314 target_index = strlen (text);
315 trailing = 0;
317 ie->sel_start = g_utf8_pointer_to_offset (text, text + target_index) + trailing;
318 gtk_editable_set_position (GTK_EDITABLE (ie->entry), ie->sel_start);
320 return TRUE;
323 return FALSE;
326 static gboolean
327 item_edit_motion (GocItem *item, double x, double y)
329 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
330 if (ie->sel_start >= 0) {
331 GtkEditable *ed = GTK_EDITABLE (ie->entry);
332 int target_index, trailing;
333 char const *text = pango_layout_get_text (ie->layout);
334 PangoDirection dir = pango_find_base_dir (text, -1);
335 PangoRectangle pos;
336 GocDirection cdir = goc_canvas_get_direction (item->canvas);
338 if (cdir == GOC_DIRECTION_RTL)
339 x = item->x1 - x - 1;
340 else
341 x = x - item->x0;
342 y = y - item->y0;
343 if (dir == PANGO_DIRECTION_RTL) {
344 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
345 x -= item->x1 - item->x0 - (pos.width
346 + 2 * GNM_COL_MARGIN) / goc_canvas_get_pixels_per_unit (item->canvas);
348 /* the layout might be zoomed, we need to adjust x and y accordingly */
349 x *= goc_canvas_get_pixels_per_unit (item->canvas);
350 y *= goc_canvas_get_pixels_per_unit (item->canvas);
352 if (pango_layout_xy_to_index (ie->layout,
353 x * PANGO_SCALE, y * PANGO_SCALE,
354 &target_index, &trailing)) {
355 int preedit = GNM_PANE (item->canvas)->preedit_length;
356 gint cur_index = gtk_editable_get_position (ed);
357 cur_index = g_utf8_offset_to_pointer (text, cur_index) - text;
359 if (target_index >= cur_index && preedit > 0) {
360 if (target_index < (cur_index + preedit)) {
361 target_index = cur_index;
362 trailing = 0;
363 } else
364 target_index -= preedit;
366 } else {
367 /* the click occured after text end (#388342) */
368 target_index = strlen (text);
369 trailing = 0;
371 target_index = g_utf8_pointer_to_offset (text, text + target_index) + trailing;
372 if (target_index > ie->sel_start)
373 gtk_editable_select_region (GTK_EDITABLE (ie->entry), ie->sel_start, target_index);
374 else
375 gtk_editable_select_region (GTK_EDITABLE (ie->entry), target_index, ie->sel_start);
376 goc_item_invalidate (item);
378 return TRUE;
380 return FALSE;
383 static gboolean
384 item_edit_button_released (GocItem *item, G_GNUC_UNUSED int button,
385 G_GNUC_UNUSED double x, G_GNUC_UNUSED double y)
387 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
388 if (ie->sel_start >= 0) {
389 ie->sel_start = -1;
390 return TRUE;
392 return FALSE;
395 static void
396 item_edit_update_bounds (GocItem *item)
398 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
399 double scale = item->canvas->pixels_per_unit;
401 if (ie->gfont != NULL) {
402 GtkWidget const *canvas = GTK_WIDGET (item->canvas);
403 GnmPane const *pane = GNM_PANE (item->canvas);
404 ColRowInfo const *ci;
405 Sheet const *sheet = scg_sheet (ie->scg);
406 GnmFont const *gfont = ie->gfont;
407 GnmRange const *merged;
408 int col, tmp, width, height, col_size;
409 char const *text, *entered_text;
410 PangoAttrList *attrs;
411 PangoAttribute *attr;
412 int cursor_pos = gtk_editable_get_position (GTK_EDITABLE (ie->entry));
413 PangoDirection dir;
414 GocDirection cdir = goc_canvas_get_direction (item->canvas);
416 entered_text = gtk_entry_get_text (ie->entry);
417 text = wbcg_edit_get_display_text (scg_wbcg (ie->scg));
418 pango_layout_set_text (ie->layout, text, -1);
419 dir = pango_find_base_dir (text, -1);
421 pango_layout_set_font_description (ie->layout, gfont->go.font->desc);
422 pango_layout_set_wrap (ie->layout, PANGO_WRAP_WORD_CHAR);
423 pango_layout_set_width (ie->layout, (int)(item->x1 - item->x0)*PANGO_SCALE);
425 attrs = wbcg_edit_get_markup (scg_wbcg (ie->scg), TRUE);
426 if (attrs != NULL)
427 attrs = pango_attr_list_copy (attrs);
428 else
429 attrs = gnm_style_generate_attrs_full (ie->style);
431 /* reverse video the auto completion text */
432 if (entered_text != NULL && entered_text != text) {
433 int const start = strlen (entered_text);
434 GnmColor const *color = gnm_style_get_font_color (ie->style);
435 attr = go_color_to_pango (color->go_color, FALSE);
436 attr->start_index = start;
437 attr->end_index = G_MAXINT;
438 pango_attr_list_insert (attrs, attr);
440 color = gnm_style_get_back_color (ie->style);
441 attr = go_color_to_pango (color->go_color, TRUE);
442 attr->start_index = start;
443 attr->end_index = G_MAXINT;
444 pango_attr_list_insert (attrs, attr);
446 pango_attr_list_insert_before (attrs,
447 pango_attr_scale_new (scale));
449 pango_layout_set_attributes (ie->layout, attrs);
450 pango_attr_list_unref (attrs);
452 go_pango_translate_layout (ie->layout);
454 if (pane->preedit_length) {
455 PangoAttrList *tmp_attrs = pango_attr_list_new ();
456 pango_attr_list_splice (tmp_attrs, pane->preedit_attrs,
457 g_utf8_offset_to_pointer (text, cursor_pos) - text,
458 g_utf8_offset_to_pointer (text, cursor_pos + pane->preedit_length) - text);
459 pango_layout_set_attributes (ie->layout, tmp_attrs);
460 pango_attr_list_unref (tmp_attrs);
463 pango_layout_set_width (ie->layout, -1);
464 pango_layout_get_pixel_size (ie->layout, &width, &height);
466 col = ie->pos.col;
467 if (NULL == (merged = gnm_sheet_merge_is_corner (sheet, &ie->pos))) {
468 ci = sheet_col_get_info (sheet, col);
469 g_return_if_fail (ci != NULL);
470 col_size = ci->size_pixels;
471 } else
472 col_size = scg_colrow_distance_get (ie->scg, TRUE,
473 merged->start.col, merged->end.col+1);
475 /* both margins and the gridline */
476 col_size -= GNM_COL_MARGIN + GNM_COL_MARGIN + 1;
478 /* far corner based on the span size
479 * - margin on each end
480 * - the bound excludes the far point => +1 */
481 if (merged != NULL)
482 col = merged->end.col;
484 if ((dir == PANGO_DIRECTION_RTL && cdir == GOC_DIRECTION_RTL) ||
485 (dir != PANGO_DIRECTION_RTL && cdir == GOC_DIRECTION_LTR)) {
486 GtkAllocation a;
488 while (col_size < width &&
489 col <= pane->last_full.col &&
490 col < gnm_sheet_get_last_col (sheet)) {
491 ci = sheet_col_get_info (sheet, ++col);
493 g_return_if_fail (ci != NULL);
495 if (ci->visible)
496 col_size += ci->size_pixels;
498 gtk_widget_get_allocation (GTK_WIDGET (canvas), &a);
499 tmp = (pane->first_offset.x + a.width) / scale;
500 item->x1 = item->x0 + (col_size + GNM_COL_MARGIN + GNM_COL_MARGIN + 1) / scale;
502 if (item->x1 >= tmp) {
503 item->x1 = tmp;
504 pango_layout_set_width (ie->layout, (item->x1 - item->x0 + 1)*PANGO_SCALE);
505 pango_layout_get_pixel_size (ie->layout, &width, &height);
507 } else {
508 item->x1 = (1 + pane->first_offset.x +
509 scg_colrow_distance_get (ie->scg, TRUE,
510 pane->first.col,
511 ie->pos.col+1)) / scale;
512 while (col_size < width &&
513 col > pane->first.col &&
514 col > 0) {
515 ci = sheet_col_get_info (sheet, --col);
517 g_return_if_fail (ci != NULL);
519 if (ci->visible)
520 col_size += ci->size_pixels;
522 if (col_size < width)
523 col_size = width;
524 tmp = pane->first_offset.x / scale;
525 item->x0 = item->x1 - (col_size + GNM_COL_MARGIN + GNM_COL_MARGIN + 1) / scale;
526 if (item->x0 <= tmp) {
527 item->x0 = tmp;
528 pango_layout_set_width (ie->layout, (item->x1 - item->x0 + 1)*PANGO_SCALE);
529 pango_layout_get_pixel_size (ie->layout, &width, &height);
533 tmp = scg_colrow_distance_get (ie->scg, FALSE, ie->pos.row,
534 (merged ? merged->end.row : ie->pos.row) + 1) - 1;
535 item->y1 = item->y0 + (MAX (height, tmp)) / scale;
539 static int
540 cb_entry_key_press (GocItem *item)
542 goc_item_bounds_changed (item);
543 return TRUE;
546 static int
547 cb_entry_cursor_event (GocItem *item)
549 /* ensure we draw a cursor when moving quickly no matter what the
550 * current state is */
551 GNM_ITEM_EDIT (item)->cursor_visible = TRUE;
552 goc_item_invalidate (item);
554 return TRUE;
557 static int
558 cb_item_edit_cursor_blink (GnmItemEdit *ie)
560 GocItem *item = GOC_ITEM (ie);
562 ie->cursor_visible = !ie->cursor_visible;
564 goc_item_invalidate (item);
565 return TRUE;
568 static void
569 item_edit_cursor_blink_stop (GnmItemEdit *ie)
571 if (ie->blink_timer != 0) {
572 g_source_remove (ie->blink_timer);
573 ie->blink_timer = 0;
577 static void
578 item_edit_cursor_blink_start (GnmItemEdit *ie)
580 gboolean blink;
581 int blink_time;
583 g_object_get (gtk_widget_get_settings (
584 GTK_WIDGET (ie->item.canvas)),
585 "gtk-cursor-blink-time", &blink_time,
586 "gtk-cursor-blink", &blink,
587 NULL);
588 if (blink)
589 ie->blink_timer = g_timeout_add ( blink_time,
590 (GSourceFunc) cb_item_edit_cursor_blink, ie);
593 static void
594 item_edit_realize (GocItem *item)
596 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
597 Sheet const *sheet;
598 GnmPane *pane;
599 double scale;
601 parent_class->realize (item);
603 sheet = scg_sheet (ie->scg);
605 g_signal_connect_object (G_OBJECT (scg_wbcg (ie->scg)),
606 "markup-changed",
607 G_CALLBACK (goc_item_bounds_changed), G_OBJECT (ie),
608 G_CONNECT_SWAPPED);
610 g_signal_connect_object (G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (ie->entry))),
611 "changed",
612 G_CALLBACK (goc_item_bounds_changed), G_OBJECT (ie),
613 G_CONNECT_SWAPPED);
615 g_signal_connect_object (G_OBJECT (ie->entry),
616 "key-press-event",
617 G_CALLBACK (cb_entry_key_press), G_OBJECT (ie),
618 G_CONNECT_AFTER|G_CONNECT_SWAPPED);
620 g_signal_connect_object (G_OBJECT (ie->entry),
621 "notify::cursor-position",
622 G_CALLBACK (cb_entry_cursor_event), G_OBJECT (ie),
623 G_CONNECT_AFTER|G_CONNECT_SWAPPED);
625 pane = GNM_PANE (item->canvas);
626 scale = item->canvas->pixels_per_unit;
627 ie->style = gnm_style_dup
628 (sheet_style_get (sheet, ie->pos.col, ie->pos.row));
629 ie->gfont = gnm_style_get_font
630 (ie->style,
631 gtk_widget_get_pango_context (GTK_WIDGET (pane)));
632 gnm_font_ref (ie->gfont);
634 if (gnm_style_get_align_h (ie->style) == GNM_HALIGN_GENERAL)
635 gnm_style_set_align_h (ie->style, GNM_HALIGN_LEFT);
637 /* move inwards 1 pixel from the grid line */
638 item->y0 = (1 + pane->first_offset.y +
639 scg_colrow_distance_get (ie->scg, FALSE,
640 pane->first.row,
641 ie->pos.row)) / scale;
642 item->x0 = (1 + pane->first_offset.x +
643 scg_colrow_distance_get (ie->scg, TRUE,
644 pane->first.col,
645 ie->pos.col)) / scale;
647 item->x1 = item->x0 + 1 / scale;
648 item->y1 = item->y0 + 1 / scale;
650 ie->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item->canvas),
651 NULL);
653 pango_layout_set_alignment (ie->layout,
654 sheet->text_is_rtl
655 ? PANGO_ALIGN_RIGHT
656 : PANGO_ALIGN_LEFT);
658 item_edit_cursor_blink_start (ie);
661 static void
662 item_edit_unrealize (GocItem *item)
664 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
666 item_edit_cursor_blink_stop (ie);
668 /* to destroy the feedback ranges */
669 SCG_FOREACH_PANE (ie->scg, pane,
670 gnm_pane_expr_cursor_stop (pane););
672 g_clear_object (&ie->layout);
674 if (ie->gfont != NULL) {
675 gnm_font_unref (ie->gfont);
676 ie->gfont = NULL;
678 if (ie->style != NULL) {
679 gnm_style_unref (ie->style);
680 ie->style= NULL;
683 parent_class->unrealize (item);
686 static void
687 gnm_item_edit_init (GnmItemEdit *ie)
689 ie->scg = NULL;
690 ie->pos.col = -1;
691 ie->pos.row = -1;
692 ie->gfont = NULL;
693 ie->style = NULL;
694 ie->cursor_visible = TRUE;
695 ie->sel_start = -1;
696 ie->blink_timer = 0;
699 static void
700 item_edit_set_property (GObject *gobject, guint param_id,
701 GValue const *value, GParamSpec *pspec)
703 GnmItemEdit *ie = GNM_ITEM_EDIT (gobject);
705 switch (param_id) {
706 case ARG_SHEET_CONTROL_GUI: {
707 /* We can only set the sheet-control-gui once */
708 g_return_if_fail (ie->scg == NULL);
710 ie->scg = GNM_SCG (g_value_get_object (value));
711 ie->pos = scg_view (ie->scg)->edit_pos;
712 ie->entry = wbcg_get_entry (scg_wbcg (ie->scg));
713 break;
716 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
717 return; /* NOTE : RETURN */
721 static void
722 gnm_item_edit_class_init (GObjectClass *gobject_class)
724 GocItemClass *item_class = (GocItemClass *) gobject_class;
726 parent_class = g_type_class_peek_parent (gobject_class);
728 gobject_class->set_property = item_edit_set_property;
730 g_object_class_install_property (gobject_class, ARG_SHEET_CONTROL_GUI,
731 g_param_spec_object ("SheetControlGUI",
732 P_("SheetControlGUI"),
733 P_("The sheet control gui controlling the item"),
734 GNM_SCG_TYPE,
735 /* resist the urge to use G_PARAM_CONSTRUCT_ONLY
736 * We are going through goc_item_new, which
737 * calls g_object_new assigns the parent pointer before
738 * setting the construction parameters */
739 GSF_PARAM_STATIC | G_PARAM_WRITABLE));
741 /* GocItem method overrides */
742 item_class->realize = item_edit_realize;
743 item_class->unrealize = item_edit_unrealize;
744 item_class->draw = item_edit_draw;
745 item_class->distance = item_edit_distance;
746 item_class->update_bounds = item_edit_update_bounds;
747 item_class->button_pressed = item_edit_button_pressed;
748 item_class->enter_notify = item_edit_enter_notify;
749 item_class->motion = item_edit_motion;
750 item_class->button_released = item_edit_button_released;
753 GSF_CLASS (GnmItemEdit, gnm_item_edit,
754 gnm_item_edit_class_init, gnm_item_edit_init,
755 GOC_TYPE_ITEM)