Move plugin init code
[gnumeric.git] / src / item-edit.c
blob4c0ec970b0800e6313b676f73efeacc34e1a2cb0
1 /*
2 * item-edit.c : Edit facilities for worksheets.
4 * (C) 1999-2004 Miguel de Icaza & Jody Goldberg
6 * This module provides:
7 * * Integration of an in-sheet text editor (GtkEntry) with the Workbook
8 * GtkEntry as a canvas item.
10 * * Feedback on expressions in the spreadsheet (referenced cells or
11 * ranges are highlighted on the spreadsheet).
13 #include <gnumeric-config.h>
14 #include <gnm-i18n.h>
15 #include <gnumeric.h>
16 #include <item-edit.h>
17 #include <gnm-pane-impl.h>
19 #include <item-cursor.h>
20 #include <sheet-control-gui-priv.h>
21 #include <sheet.h>
22 #include <sheet-view.h>
23 #include <sheet-style.h>
24 #include <sheet-merge.h>
25 #include <value.h>
26 #include <ranges.h>
27 #include <style.h>
28 #include <style-font.h>
29 #include <style-color.h>
30 #include <pattern.h>
31 #include <parse-util.h>
32 #include <workbook.h>
33 #include <wbc-gtk.h>
34 #include <gui-util.h>
35 #include <widgets/gnumeric-expr-entry.h>
36 #define GNUMERIC_ITEM "EDIT"
38 #include <gsf/gsf-impl-utils.h>
39 #include <string.h>
40 #include <goffice/goffice.h>
42 static GocItemClass *parent_class;
44 struct _GnmItemEdit {
45 GocItem item;
47 SheetControlGUI *scg;
48 GtkEntry *entry; /* Utility pointer to the workbook entry */
50 PangoLayout *layout;
52 /* Where are we */
53 GnmCellPos pos;
54 gboolean cursor_visible;
55 guint blink_timer;
56 int sel_start;
58 GnmFont *gfont;
59 GnmStyle *style;
62 typedef GocItemClass GnmItemEditClass;
64 /* The arguments we take */
65 enum {
66 ARG_0,
67 ARG_SHEET_CONTROL_GUI /* The SheetControlGUI * argument */
70 static void
71 get_top_left (GnmItemEdit const *ie, int *top, int *left, PangoDirection dir)
73 GnmVAlign const align = gnm_style_get_align_v (ie->style);
74 GocItem *item = GOC_ITEM (ie);
75 GocCanvas *canvas = item->canvas;
76 double l = ((goc_canvas_get_direction (canvas) == GOC_DIRECTION_LTR && dir == PANGO_DIRECTION_RTL)
77 || (goc_canvas_get_direction (canvas) == GOC_DIRECTION_RTL && dir != PANGO_DIRECTION_RTL))?
78 item->x1 - 1: item->x0;
80 goc_canvas_c2w (canvas, l, item->y0, left, top);
82 if (align == GNM_VALIGN_CENTER || align == GNM_VALIGN_DISTRIBUTED ||
83 align == GNM_VALIGN_BOTTOM) {
84 int text_height, height = (int)(ie->item.y1 - ie->item.y0) * canvas->pixels_per_unit;
85 pango_layout_get_pixel_size (ie->layout, NULL, &text_height);
86 *top += (align != GNM_VALIGN_BOTTOM)
87 ? (height - text_height)/2
88 : (height - text_height);
92 static gboolean
93 gnm_apply_attribute_list_cb (PangoAttribute *attribute,
94 gpointer data)
96 PangoAttrList *attrs = data;
97 if (attribute->klass->type == PANGO_ATTR_FOREGROUND) {
98 PangoAttribute *copy = pango_attribute_copy (attribute);
99 pango_attr_list_change (attrs, copy);
101 return FALSE;
104 static void
105 gnm_apply_attribute_list (PangoAttrList *attrs, PangoAttrList *added_attrs)
107 if (added_attrs == NULL)
108 return;
109 pango_attr_list_unref (pango_attr_list_filter (added_attrs,
110 gnm_apply_attribute_list_cb,
111 attrs));
114 static void
115 item_edit_draw (GocItem const *item, cairo_t *cr)
117 GnmItemEdit const *ie = GNM_ITEM_EDIT (item);
118 GtkStyleContext *context = goc_item_get_style_context (item);
119 int top, left;
120 GOColor color;
121 int x0, y0, x1, y1; /* in widget coordinates */
122 int start, end;
123 PangoRectangle pos, weak;
124 char const *text = gtk_entry_get_text (ie->entry);
125 GdkRGBA fcolor;
126 PangoDirection dir = pango_find_base_dir (text, -1);
127 PangoAttrList *entry_attributes
128 = g_object_get_data(G_OBJECT (ie->entry),
129 "gnm:range-attributes");
131 if (entry_attributes != NULL) {
132 if (go_pango_attr_list_is_empty (entry_attributes))
133 entry_attributes = NULL;
134 else
135 entry_attributes = pango_attr_list_ref (entry_attributes);
138 get_top_left (ie, &top, &left, dir);
139 if (goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL) {
140 goc_canvas_c2w (item->canvas, item->x1, item->y0, &x0, &y0);
141 goc_canvas_c2w (item->canvas, item->x0, item->y1, &x1, &y1);
142 } else {
143 goc_canvas_c2w (item->canvas, item->x0, item->y0, &x0, &y0);
144 goc_canvas_c2w (item->canvas, item->x1, item->y1, &x1, &y1);
147 cairo_save (cr);
149 cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
150 /* avoid a weak/strong cursor to extend outside the item,
151 a better fix would be to have enough room for cursors */
152 cairo_clip (cr);
153 if (gnm_pattern_background_set (ie->style, cr, FALSE, NULL)) {
154 cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
155 cairo_fill (cr);
156 } else {
157 gtk_render_background (context, cr, x0, y0, x1 - x0, y1 - y0);
160 /* set the default color */
161 gnm_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fcolor);
162 gdk_cairo_set_source_rgba (cr, &fcolor);
163 color = go_color_from_gdk_rgba (&fcolor, NULL);
165 if (dir == PANGO_DIRECTION_RTL) {
166 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
167 left -= pos.width + GNM_COL_MARGIN / goc_canvas_get_pixels_per_unit (item->canvas);
169 cairo_move_to (cr, left, top);
170 gtk_editable_get_selection_bounds (GTK_EDITABLE (ie->entry), &start, &end);
171 if (start != end) {
172 PangoAttribute *attr;
173 PangoAttrList *orig = pango_attr_list_ref (pango_layout_get_attributes (ie->layout)),
174 *attrs = pango_attr_list_copy (orig);
175 start = g_utf8_offset_to_pointer (text, start) - text;
176 end = g_utf8_offset_to_pointer (text, end) - text;
177 color = gnm_style_get_back_color (ie->style)->go_color;
178 attr = go_color_to_pango (color, FALSE);
179 attr->start_index = start;
180 attr->end_index = end;
181 pango_attr_list_change (attrs, attr);
182 color = gnm_style_get_font_color (ie->style)->go_color;
183 attr = go_color_to_pango (color, TRUE);
184 attr->start_index = start;
185 attr->end_index = end;
186 pango_attr_list_change (attrs, attr);
187 gnm_apply_attribute_list (attrs, entry_attributes);
188 pango_layout_set_attributes (ie->layout, attrs);
189 pango_attr_list_unref (attrs);
190 pango_cairo_show_layout (cr, ie->layout);
191 pango_layout_set_attributes (ie->layout, orig);
192 pango_attr_list_unref (orig);
193 } else if (entry_attributes != NULL) {
194 PangoAttrList *orig = pango_attr_list_ref (pango_layout_get_attributes (ie->layout)),
195 *attrs = pango_attr_list_copy (orig);
196 gnm_apply_attribute_list (attrs, entry_attributes);
197 pango_layout_set_attributes (ie->layout, attrs);
198 pango_attr_list_unref (attrs);
199 pango_cairo_show_layout (cr, ie->layout);
200 pango_layout_set_attributes (ie->layout, orig);
201 pango_attr_list_unref (orig);
202 } else {
203 pango_cairo_show_layout (cr, ie->layout);
205 pango_attr_list_unref (entry_attributes);
207 if (ie->cursor_visible) {
208 int cursor_pos = gtk_editable_get_position (GTK_EDITABLE (ie->entry));
209 double incr = (dir == PANGO_DIRECTION_RTL)? -.5: .5, x, ytop, ybottom;
210 pango_layout_get_cursor_pos (ie->layout,
211 g_utf8_offset_to_pointer (text, cursor_pos) - text, &pos, &weak);
212 cairo_set_line_width (cr, 1.);
213 cairo_set_dash (cr, NULL, 0, 0.);
214 cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
215 cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
216 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (gnm_style_get_back_color (ie->style)->go_color ^ 0xffffff00));
217 x = left + PANGO_PIXELS (pos.x) + incr;
218 ytop = top + PANGO_PIXELS (pos.y);
219 ybottom = top + PANGO_PIXELS (pos.y + pos.height) - 1;
220 cairo_move_to (cr, x, ytop);
221 cairo_line_to (cr, x, ybottom);
222 cairo_stroke (cr);
223 if (weak.x != pos.x) {
224 double w = (ybottom - ytop) / 4.;
225 GOColor color1 = color ^ 0xffffff00;
226 x += incr;
227 cairo_move_to (cr, x, ybottom);
228 cairo_line_to (cr, x + w * incr, ybottom - w / 2.);
229 cairo_line_to (cr, x, ybottom - w);
230 cairo_close_path (cr);
231 cairo_fill (cr);
232 color = GO_COLOR_INTERPOLATE (color, color1, 0.5);
233 x = left + PANGO_PIXELS (weak.x) - incr;
234 ytop = top + PANGO_PIXELS (weak.y);
235 ybottom = top + PANGO_PIXELS (weak.y + weak.height) - 1;
236 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (color));
237 cairo_move_to (cr, x, ytop);
238 cairo_line_to (cr, x, ybottom);
239 cairo_stroke (cr);
240 x -= incr;
241 cairo_move_to (cr, x, ybottom);
242 cairo_line_to (cr, x - w * incr, ybottom - w / 2.);
243 cairo_line_to (cr, x, ybottom - w);
244 cairo_close_path (cr);
245 cairo_fill (cr);
248 cairo_restore (cr);
251 static double
252 item_edit_distance (GocItem *item, double cx, double cy,
253 GocItem **actual_item)
255 *actual_item = NULL;
256 if ((cx < item->x0) || (cy < item->y0) || (cx >= item->x1) || (cy >= item->y1))
257 return 10000.0;
259 *actual_item = item;
260 return 0.0;
263 static gboolean
264 item_edit_enter_notify (GocItem *item, G_GNUC_UNUSED double x, G_GNUC_UNUSED double y)
266 gnm_widget_set_cursor_type (GTK_WIDGET (item->canvas), GDK_XTERM);
267 return TRUE;
270 static int
271 item_edit_button_pressed (GocItem *item, int button, double x, double y)
273 if (button == 1) {
274 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
275 GtkEditable *ed = GTK_EDITABLE (ie->entry);
276 int target_index, trailing;
277 char const *text = pango_layout_get_text (ie->layout);
278 PangoDirection dir = pango_find_base_dir (text, -1);
279 PangoRectangle pos;
280 GocDirection cdir = goc_canvas_get_direction (item->canvas);
282 if (cdir == GOC_DIRECTION_RTL)
283 x = item->x1 - x - 1;
284 else
285 x = x - item->x0;
286 y = y - item->y0;
287 if (dir == PANGO_DIRECTION_RTL) {
288 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
289 x -= item->x1 - item->x0 - (pos.width
290 + 2 * GNM_COL_MARGIN) / goc_canvas_get_pixels_per_unit (item->canvas);
292 /* the layout might be zoomed, we need to adjust x and y accordingly */
293 x *= goc_canvas_get_pixels_per_unit (item->canvas);
294 y *= goc_canvas_get_pixels_per_unit (item->canvas);
296 if (pango_layout_xy_to_index (ie->layout,
297 x * PANGO_SCALE, y * PANGO_SCALE,
298 &target_index, &trailing)) {
299 int preedit = GNM_PANE (item->canvas)->preedit_length;
300 gint cur_index = gtk_editable_get_position (ed);
301 cur_index = g_utf8_offset_to_pointer (text, cur_index) - text;
303 if (target_index >= cur_index && preedit > 0) {
304 if (target_index < (cur_index + preedit)) {
305 target_index = cur_index;
306 trailing = 0;
307 } else
308 target_index -= preedit;
310 } else if (x < 0) {
311 /* the click occured after text end (#388342) */
312 target_index = strlen (text);
313 trailing = 0;
315 ie->sel_start = g_utf8_pointer_to_offset (text, text + target_index) + trailing;
316 gtk_editable_set_position (GTK_EDITABLE (ie->entry), ie->sel_start);
318 return TRUE;
321 return FALSE;
324 static gboolean
325 item_edit_motion (GocItem *item, double x, double y)
327 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
328 if (ie->sel_start >= 0) {
329 GtkEditable *ed = GTK_EDITABLE (ie->entry);
330 int target_index, trailing;
331 char const *text = pango_layout_get_text (ie->layout);
332 PangoDirection dir = pango_find_base_dir (text, -1);
333 PangoRectangle pos;
334 GocDirection cdir = goc_canvas_get_direction (item->canvas);
336 if (cdir == GOC_DIRECTION_RTL)
337 x = item->x1 - x - 1;
338 else
339 x = x - item->x0;
340 y = y - item->y0;
341 if (dir == PANGO_DIRECTION_RTL) {
342 pango_layout_get_pixel_extents (ie->layout, NULL, &pos);
343 x -= item->x1 - item->x0 - (pos.width
344 + 2 * GNM_COL_MARGIN) / goc_canvas_get_pixels_per_unit (item->canvas);
346 /* the layout might be zoomed, we need to adjust x and y accordingly */
347 x *= goc_canvas_get_pixels_per_unit (item->canvas);
348 y *= goc_canvas_get_pixels_per_unit (item->canvas);
350 if (pango_layout_xy_to_index (ie->layout,
351 x * PANGO_SCALE, y * PANGO_SCALE,
352 &target_index, &trailing)) {
353 int preedit = GNM_PANE (item->canvas)->preedit_length;
354 gint cur_index = gtk_editable_get_position (ed);
355 cur_index = g_utf8_offset_to_pointer (text, cur_index) - text;
357 if (target_index >= cur_index && preedit > 0) {
358 if (target_index < (cur_index + preedit)) {
359 target_index = cur_index;
360 trailing = 0;
361 } else
362 target_index -= preedit;
364 } else {
365 /* the click occured after text end (#388342) */
366 target_index = strlen (text);
367 trailing = 0;
369 target_index = g_utf8_pointer_to_offset (text, text + target_index) + trailing;
370 if (target_index > ie->sel_start)
371 gtk_editable_select_region (GTK_EDITABLE (ie->entry), ie->sel_start, target_index);
372 else
373 gtk_editable_select_region (GTK_EDITABLE (ie->entry), target_index, ie->sel_start);
374 goc_item_invalidate (item);
376 return TRUE;
378 return FALSE;
381 static gboolean
382 item_edit_button_released (GocItem *item, G_GNUC_UNUSED int button,
383 G_GNUC_UNUSED double x, G_GNUC_UNUSED double y)
385 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
386 if (ie->sel_start >= 0) {
387 ie->sel_start = -1;
388 return TRUE;
390 return FALSE;
393 static void
394 item_edit_update_bounds (GocItem *item)
396 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
397 double scale = item->canvas->pixels_per_unit;
399 if (ie->gfont != NULL) {
400 GtkWidget const *canvas = GTK_WIDGET (item->canvas);
401 GnmPane const *pane = GNM_PANE (item->canvas);
402 ColRowInfo const *ci;
403 Sheet const *sheet = scg_sheet (ie->scg);
404 GnmFont const *gfont = ie->gfont;
405 GnmRange const *merged;
406 int col, tmp, width, height, col_size;
407 char const *text, *entered_text;
408 PangoAttrList *attrs;
409 PangoAttribute *attr;
410 int cursor_pos = gtk_editable_get_position (GTK_EDITABLE (ie->entry));
411 PangoDirection dir;
412 GocDirection cdir = goc_canvas_get_direction (item->canvas);
414 entered_text = gtk_entry_get_text (ie->entry);
415 text = wbcg_edit_get_display_text (scg_wbcg (ie->scg));
416 pango_layout_set_text (ie->layout, text, -1);
417 dir = pango_find_base_dir (text, -1);
419 pango_layout_set_font_description (ie->layout, gfont->go.font->desc);
420 pango_layout_set_wrap (ie->layout, PANGO_WRAP_WORD_CHAR);
421 pango_layout_set_width (ie->layout, (int)(item->x1 - item->x0)*PANGO_SCALE);
423 attrs = wbcg_edit_get_markup (scg_wbcg (ie->scg), TRUE);
424 if (attrs != NULL)
425 attrs = pango_attr_list_copy (attrs);
426 else
427 attrs = gnm_style_generate_attrs_full (ie->style);
429 /* reverse video the auto completion text */
430 if (entered_text != NULL && entered_text != text) {
431 int const start = strlen (entered_text);
432 GnmColor const *color = gnm_style_get_font_color (ie->style);
433 attr = go_color_to_pango (color->go_color, FALSE);
434 attr->start_index = start;
435 attr->end_index = G_MAXINT;
436 pango_attr_list_insert (attrs, attr);
438 color = gnm_style_get_back_color (ie->style);
439 attr = go_color_to_pango (color->go_color, TRUE);
440 attr->start_index = start;
441 attr->end_index = G_MAXINT;
442 pango_attr_list_insert (attrs, attr);
444 pango_attr_list_insert_before (attrs,
445 pango_attr_scale_new (scale));
447 pango_layout_set_attributes (ie->layout, attrs);
448 pango_attr_list_unref (attrs);
450 go_pango_translate_layout (ie->layout);
452 if (pane->preedit_length) {
453 PangoAttrList *tmp_attrs = pango_attr_list_new ();
454 pango_attr_list_splice (tmp_attrs, pane->preedit_attrs,
455 g_utf8_offset_to_pointer (text, cursor_pos) - text,
456 g_utf8_offset_to_pointer (text, cursor_pos + pane->preedit_length) - text);
457 pango_layout_set_attributes (ie->layout, tmp_attrs);
458 pango_attr_list_unref (tmp_attrs);
461 pango_layout_set_width (ie->layout, -1);
462 pango_layout_get_pixel_size (ie->layout, &width, &height);
464 col = ie->pos.col;
465 if (NULL == (merged = gnm_sheet_merge_is_corner (sheet, &ie->pos))) {
466 ci = sheet_col_get_info (sheet, col);
467 g_return_if_fail (ci != NULL);
468 col_size = ci->size_pixels;
469 } else
470 col_size = scg_colrow_distance_get (ie->scg, TRUE,
471 merged->start.col, merged->end.col+1);
473 /* both margins and the gridline */
474 col_size -= GNM_COL_MARGIN + GNM_COL_MARGIN + 1;
476 /* far corner based on the span size
477 * - margin on each end
478 * - the bound excludes the far point => +1 */
479 if (merged != NULL)
480 col = merged->end.col;
482 if ((dir == PANGO_DIRECTION_RTL && cdir == GOC_DIRECTION_RTL) ||
483 (dir != PANGO_DIRECTION_RTL && cdir == GOC_DIRECTION_LTR)) {
484 GtkAllocation a;
486 while (col_size < width &&
487 col <= pane->last_full.col &&
488 col < gnm_sheet_get_last_col (sheet)) {
489 ci = sheet_col_get_info (sheet, ++col);
491 g_return_if_fail (ci != NULL);
493 if (ci->visible)
494 col_size += ci->size_pixels;
496 gtk_widget_get_allocation (GTK_WIDGET (canvas), &a);
497 tmp = (pane->first_offset.x + a.width) / scale;
498 item->x1 = item->x0 + (col_size + GNM_COL_MARGIN + GNM_COL_MARGIN + 1) / scale;
500 if (item->x1 >= tmp) {
501 item->x1 = tmp;
502 pango_layout_set_width (ie->layout, (item->x1 - item->x0 + 1)*PANGO_SCALE);
503 pango_layout_get_pixel_size (ie->layout, &width, &height);
505 } else {
506 item->x1 = (1 + pane->first_offset.x +
507 scg_colrow_distance_get (ie->scg, TRUE,
508 pane->first.col,
509 ie->pos.col+1)) / scale;
510 while (col_size < width &&
511 col > pane->first.col &&
512 col > 0) {
513 ci = sheet_col_get_info (sheet, --col);
515 g_return_if_fail (ci != NULL);
517 if (ci->visible)
518 col_size += ci->size_pixels;
520 if (col_size < width)
521 col_size = width;
522 tmp = pane->first_offset.x / scale;
523 item->x0 = item->x1 - (col_size + GNM_COL_MARGIN + GNM_COL_MARGIN + 1) / scale;
524 if (item->x0 <= tmp) {
525 item->x0 = tmp;
526 pango_layout_set_width (ie->layout, (item->x1 - item->x0 + 1)*PANGO_SCALE);
527 pango_layout_get_pixel_size (ie->layout, &width, &height);
531 tmp = scg_colrow_distance_get (ie->scg, FALSE, ie->pos.row,
532 (merged ? merged->end.row : ie->pos.row) + 1) - 1;
533 item->y1 = item->y0 + (MAX (height, tmp)) / scale;
537 static int
538 cb_entry_key_press (GocItem *item)
540 goc_item_bounds_changed (item);
541 return TRUE;
544 static int
545 cb_entry_cursor_event (GocItem *item)
547 /* ensure we draw a cursor when moving quickly no matter what the
548 * current state is */
549 GNM_ITEM_EDIT (item)->cursor_visible = TRUE;
550 goc_item_invalidate (item);
552 return TRUE;
555 static int
556 cb_item_edit_cursor_blink (GnmItemEdit *ie)
558 GocItem *item = GOC_ITEM (ie);
560 ie->cursor_visible = !ie->cursor_visible;
562 goc_item_invalidate (item);
563 return TRUE;
566 static void
567 item_edit_cursor_blink_stop (GnmItemEdit *ie)
569 if (ie->blink_timer != 0) {
570 g_source_remove (ie->blink_timer);
571 ie->blink_timer = 0;
575 static void
576 item_edit_cursor_blink_start (GnmItemEdit *ie)
578 gboolean blink;
579 int blink_time;
581 g_object_get (gtk_widget_get_settings (
582 GTK_WIDGET (ie->item.canvas)),
583 "gtk-cursor-blink-time", &blink_time,
584 "gtk-cursor-blink", &blink,
585 NULL);
586 if (blink)
587 ie->blink_timer = g_timeout_add ( blink_time,
588 (GSourceFunc) cb_item_edit_cursor_blink, ie);
591 static void
592 item_edit_realize (GocItem *item)
594 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
595 Sheet const *sheet;
596 GnmPane *pane;
597 double scale;
599 parent_class->realize (item);
601 sheet = scg_sheet (ie->scg);
603 g_signal_connect_object (G_OBJECT (scg_wbcg (ie->scg)),
604 "markup-changed",
605 G_CALLBACK (goc_item_bounds_changed), G_OBJECT (ie),
606 G_CONNECT_SWAPPED);
608 g_signal_connect_object (G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (ie->entry))),
609 "changed",
610 G_CALLBACK (goc_item_bounds_changed), G_OBJECT (ie),
611 G_CONNECT_SWAPPED);
613 g_signal_connect_object (G_OBJECT (ie->entry),
614 "key-press-event",
615 G_CALLBACK (cb_entry_key_press), G_OBJECT (ie),
616 G_CONNECT_AFTER|G_CONNECT_SWAPPED);
618 g_signal_connect_object (G_OBJECT (ie->entry),
619 "notify::cursor-position",
620 G_CALLBACK (cb_entry_cursor_event), G_OBJECT (ie),
621 G_CONNECT_AFTER|G_CONNECT_SWAPPED);
623 pane = GNM_PANE (item->canvas);
624 scale = item->canvas->pixels_per_unit;
625 ie->style = gnm_style_dup
626 (sheet_style_get (sheet, ie->pos.col, ie->pos.row));
627 ie->gfont = gnm_style_get_font
628 (ie->style,
629 gtk_widget_get_pango_context (GTK_WIDGET (pane)));
630 gnm_font_ref (ie->gfont);
632 if (gnm_style_get_align_h (ie->style) == GNM_HALIGN_GENERAL)
633 gnm_style_set_align_h (ie->style, GNM_HALIGN_LEFT);
635 /* move inwards 1 pixel from the grid line */
636 item->y0 = (1 + pane->first_offset.y +
637 scg_colrow_distance_get (ie->scg, FALSE,
638 pane->first.row,
639 ie->pos.row)) / scale;
640 item->x0 = (1 + pane->first_offset.x +
641 scg_colrow_distance_get (ie->scg, TRUE,
642 pane->first.col,
643 ie->pos.col)) / scale;
645 item->x1 = item->x0 + 1 / scale;
646 item->y1 = item->y0 + 1 / scale;
648 ie->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item->canvas),
649 NULL);
651 pango_layout_set_alignment (ie->layout,
652 sheet->text_is_rtl
653 ? PANGO_ALIGN_RIGHT
654 : PANGO_ALIGN_LEFT);
656 item_edit_cursor_blink_start (ie);
659 static void
660 item_edit_unrealize (GocItem *item)
662 GnmItemEdit *ie = GNM_ITEM_EDIT (item);
664 item_edit_cursor_blink_stop (ie);
666 /* to destroy the feedback ranges */
667 SCG_FOREACH_PANE (ie->scg, pane,
668 gnm_pane_expr_cursor_stop (pane););
670 g_clear_object (&ie->layout);
672 if (ie->gfont != NULL) {
673 gnm_font_unref (ie->gfont);
674 ie->gfont = NULL;
676 if (ie->style != NULL) {
677 gnm_style_unref (ie->style);
678 ie->style= NULL;
681 parent_class->unrealize (item);
684 static void
685 gnm_item_edit_init (GnmItemEdit *ie)
687 ie->scg = NULL;
688 ie->pos.col = -1;
689 ie->pos.row = -1;
690 ie->gfont = NULL;
691 ie->style = NULL;
692 ie->cursor_visible = TRUE;
693 ie->sel_start = -1;
694 ie->blink_timer = 0;
697 static void
698 item_edit_set_property (GObject *gobject, guint param_id,
699 GValue const *value, GParamSpec *pspec)
701 GnmItemEdit *ie = GNM_ITEM_EDIT (gobject);
703 switch (param_id) {
704 case ARG_SHEET_CONTROL_GUI: {
705 /* We can only set the sheet-control-gui once */
706 g_return_if_fail (ie->scg == NULL);
708 ie->scg = GNM_SCG (g_value_get_object (value));
709 ie->pos = scg_view (ie->scg)->edit_pos;
710 ie->entry = wbcg_get_entry (scg_wbcg (ie->scg));
711 break;
714 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
715 return; /* NOTE : RETURN */
719 static void
720 gnm_item_edit_class_init (GObjectClass *gobject_class)
722 GocItemClass *item_class = (GocItemClass *) gobject_class;
724 parent_class = g_type_class_peek_parent (gobject_class);
726 gobject_class->set_property = item_edit_set_property;
728 g_object_class_install_property (gobject_class, ARG_SHEET_CONTROL_GUI,
729 g_param_spec_object ("SheetControlGUI",
730 P_("SheetControlGUI"),
731 P_("The sheet control gui controlling the item"),
732 GNM_SCG_TYPE,
733 /* resist the urge to use G_PARAM_CONSTRUCT_ONLY
734 * We are going through goc_item_new, which
735 * calls g_object_new assigns the parent pointer before
736 * setting the construction parameters */
737 GSF_PARAM_STATIC | G_PARAM_WRITABLE));
739 /* GocItem method overrides */
740 item_class->realize = item_edit_realize;
741 item_class->unrealize = item_edit_unrealize;
742 item_class->draw = item_edit_draw;
743 item_class->distance = item_edit_distance;
744 item_class->update_bounds = item_edit_update_bounds;
745 item_class->button_pressed = item_edit_button_pressed;
746 item_class->enter_notify = item_edit_enter_notify;
747 item_class->motion = item_edit_motion;
748 item_class->button_released = item_edit_button_released;
751 GSF_CLASS (GnmItemEdit, gnm_item_edit,
752 gnm_item_edit_class_init, gnm_item_edit_init,
753 GOC_TYPE_ITEM)