GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnucash-item-edit.c
1 /********************************************************************\
2  * gnucash-item-edit.c -- cell editor cut-n-paste from gnumeric *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA [email protected] *
20  * *
21 \********************************************************************/
22 
23 /*
24  * An editor for the gnucash sheet.
25  * Cut and pasted from the gnumeric item-edit.c file.
26  *
27  * And then substantially rewritten by Dave Peticolas <[email protected]>.
28  */
29 
30 
31 #include "config.h"
32 
33 #include <string.h>
34 
35 #include "gnucash-color.h"
36 #include "gnucash-cursor.h"
37 #include "gnucash-item-edit.h"
38 #include "gnucash-grid.h"
39 #include "gnucash-sheet.h"
40 #include "gnucash-sheetP.h"
41 #include "gnucash-style.h"
42 
43 
44 /* FIXME GNOME2 Port
45  * - ButtonEvents are not delegated to GtkEntry.
46  */
47 
48 /* The arguments we take */
49 enum
50 {
51  PROP_0,
52  PROP_SHEET, /* The sheet property */
53  PROP_EDITOR, /* The entry property */
54 };
55 
56 /* values for selection info */
57 enum
58 {
59  TARGET_UTF8_STRING,
60  TARGET_STRING,
61  TARGET_TEXT,
62  TARGET_COMPOUND_TEXT
63 };
64 
65 static GnomeCanvasItemClass *gnc_item_edit_parent_class;
66 static GdkAtom clipboard_atom = GDK_NONE;
67 
68 
69 typedef struct _TextDrawInfo TextDrawInfo;
71 {
72  PangoLayout *layout;
73 
74  GdkRectangle bg_rect;
75  GdkRectangle text_rect;
76  GdkRectangle hatch_rect;
77  GdkRectangle cursor_rect;
78 
79  GdkColor *fg_color;
80  GdkColor *bg_color;
81 
82  GdkColor *fg_color2;
83  GdkColor *bg_color2;
84 
85  gboolean hatching;
86 };
87 
88 
89 static void queue_sync (GncItemEdit *item_edit);
90 static void gnc_item_edit_show_popup_toggle (GncItemEdit *item_edit,
91  gint x, gint y,
92  gint width, gint height,
93  GtkAnchorType anchor);
94 
95 /*
96  * Returns the coordinates for the editor bounding box
97  */
98 void
99 gnc_item_edit_get_pixel_coords (GncItemEdit *item_edit,
100  int *x, int *y,
101  int *w, int *h)
102 {
103  GnucashSheet *sheet = item_edit->sheet;
104  SheetBlock *block;
105  int xd, yd;
106 
107  block = gnucash_sheet_get_block (sheet, item_edit->virt_loc.vcell_loc);
108  if (block == NULL)
109  return;
110 
111  xd = block->origin_x;
112  yd = block->origin_y;
113 
114  gnucash_sheet_style_get_cell_pixel_rel_coords
115  (item_edit->style,
116  item_edit->virt_loc.phys_row_offset,
117  item_edit->virt_loc.phys_col_offset,
118  x, y, w, h);
119 
120  *x += xd;
121  *y += yd;
122 }
123 
124 static void
125 gnc_item_edit_update_offset (GncItemEdit *item_edit, TextDrawInfo *info)
126 {
127  gint drawable_width;
128  gint visible_width;
129  PangoRectangle logical_rect;
130 
131  g_return_if_fail (item_edit != NULL);
132  g_return_if_fail (GNC_IS_ITEM_EDIT (item_edit));
133 
134  pango_layout_get_pixel_extents (info->layout, NULL, &logical_rect);
135 
136  drawable_width = info->text_rect.width - 2 * CELL_HPADDING;
137 
138  // Layout is smaller than drawable area, or we've entered a
139  // new cell. Use default x_offset.
140  if (logical_rect.width <= drawable_width || item_edit->reset_pos)
141  {
142  gnc_item_edit_reset_offset (item_edit);
143  }
144 
145  // Layout is wider than drawable area
146  if (logical_rect.width > drawable_width)
147  {
148  //Make sure cursor is inside the drawn area
149  if (info->cursor_rect.x + item_edit->x_offset >
150  info->text_rect.x + drawable_width)
151  {
152  item_edit->x_offset = (info->text_rect.x + drawable_width)
153  - info->cursor_rect.x;
154  }
155  else if (info->cursor_rect.x + item_edit->x_offset < info->text_rect.x)
156  {
157  item_edit->x_offset = - info->cursor_rect.x;
158  }
159 
160  // Make sure the entire drawable area is filled.
161  visible_width = logical_rect.width + item_edit->x_offset;
162 
163  if (visible_width < drawable_width)
164  {
165  item_edit->x_offset += (drawable_width - visible_width);
166  }
167  }
168 }
169 
170 static void
171 gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *info)
172 {
173  const char LINE_FEED = 0x0a;
174 
175  GtkEditable *editable;
176  Table *table;
177 
178  gboolean hatching;
179  guint32 argb, color_type;
180 
181  int xd, yd, wd, hd, dx, dy;
182  int start_pos, end_pos;
183  int toggle_space, cursor_pos, cursor_byte_pos, pos, loc;
184  const gchar *text;
185  PangoRectangle strong_pos;
186  PangoAttribute *attr;
187  PangoAttrList *attr_list;
188  GnucashSheet *sheet;
189 
190  sheet = GNUCASH_SHEET (item_edit->sheet);
191  table = item_edit->sheet->table;
192 
193  if (item_edit->sheet->use_theme_colors)
194  {
195  color_type = gnc_table_get_gtkrc_bg_color (table,
196  item_edit->virt_loc,
197  &hatching);
198  info->bg_color = get_gtkrc_color(item_edit->sheet, color_type);
199  color_type = gnc_table_get_gtkrc_fg_color (table,
200  item_edit->virt_loc);
201  info->fg_color = get_gtkrc_color(item_edit->sheet, color_type);
202  }
203  else
204  {
205  argb = gnc_table_get_bg_color (table, item_edit->virt_loc,
206  &hatching);
207  info->bg_color = gnucash_color_argb_to_gdk (argb);
208  argb = gnc_table_get_fg_color (table, item_edit->virt_loc);
209  info->fg_color = gnucash_color_argb_to_gdk (argb);
210  }
211 
212  info->hatching = hatching;
213 
214  info->bg_color2 = &gn_dark_gray;
215  info->fg_color2 = &gn_white;
216 
217  editable = GTK_EDITABLE (item_edit->editor);
218  text = gtk_entry_get_text (GTK_ENTRY (item_edit->editor));
219  cursor_pos = gtk_editable_get_position (editable);
220  cursor_byte_pos = g_utf8_offset_to_pointer (text, cursor_pos) - text;
221 
222  gtk_editable_get_selection_bounds (editable, &start_pos, &end_pos);
223 
224  if (cursor_pos == cursor_byte_pos)
225  {
226  /* display at character after LF before cursor_pos */
227  /* (but not for UTF-8, which is messier to implement) */
228  for (pos = 0, loc = 0; pos <= start_pos; pos++)
229  {
230  if ((pos > 0) && (text[pos-1] == LINE_FEED))
231  {
232  loc = pos;
233  }
234  }
235  text += loc;
236  start_pos -= loc;
237  end_pos -= loc;
238  cursor_pos -= loc;
239  cursor_byte_pos = g_utf8_offset_to_pointer (text, cursor_pos) - text;
240  }
241 
242  info->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item_edit->sheet), text);
243 
244  /* IMContext attributes*/
245  if (sheet->preedit_length && sheet->preedit_attrs != NULL)
246  {
247  PangoAttrList *tmp_attrs = pango_attr_list_new ();
248  pango_attr_list_splice (tmp_attrs, sheet->preedit_attrs,
249  g_utf8_offset_to_pointer (text, sheet->preedit_start_position) - text ,
250  g_utf8_offset_to_pointer (text, sheet->preedit_start_position + sheet->preedit_char_length) - text);
251  pango_layout_set_attributes (info->layout, tmp_attrs);
252  pango_attr_list_unref (tmp_attrs);
253  }
254 
255 
256  /* Selection */
257  if (start_pos != end_pos)
258  {
259  gint start_byte_pos, end_byte_pos, color;
260 
261  start_byte_pos = g_utf8_offset_to_pointer (text, start_pos) - text;
262  end_byte_pos = g_utf8_offset_to_pointer (text, end_pos) - text;
263 
264  attr_list = pango_attr_list_new ();
265 
266  attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
267  attr->start_index = start_byte_pos;
268  attr->end_index = end_byte_pos;
269  pango_attr_list_insert (attr_list, attr);
270 
271  color = gtk_widget_has_focus(GTK_WIDGET(item_edit->sheet)) ? 0x0 : 0x7fff;
272  attr = pango_attr_background_new (color, color, color);
273  attr->start_index = start_byte_pos;
274  attr->end_index = end_byte_pos;
275  pango_attr_list_insert (attr_list, attr);
276 
277  pango_layout_set_attributes (info->layout, attr_list);
278  pango_attr_list_unref (attr_list);
279  }
280 
281  gnc_item_edit_get_pixel_coords (item_edit, &xd, &yd, &wd, &hd);
282 
283  dx = xd - x;
284  dy = yd - y;
285 
286  info->bg_rect.x = dx + CELL_HPADDING;
287  info->bg_rect.y = dy + 1;
288  info->bg_rect.width = wd - (2 * CELL_HPADDING);
289  info->bg_rect.height = hd - 2;
290 
291  toggle_space = item_edit->is_popup ?
292  item_edit->popup_toggle.toggle_offset : 0;
293 
294  info->text_rect.x = dx;
295  info->text_rect.y = dy + 1;
296  info->text_rect.width = wd - toggle_space;
297  info->text_rect.height = hd - 2;
298 
299  // this width affects line-wrapping; setting it to -1 should turn wrapping off:
300  pango_layout_set_width( info->layout, -1 );
301 
302  // pango_layout_set_ellipsize(...) as of pango 1.6 may be useful for
303  // strings longer than the field width.
304 
305  pango_layout_get_cursor_pos (info->layout, cursor_byte_pos, &strong_pos, NULL);
306 
307  info->cursor_rect.x = dx + PANGO_PIXELS (strong_pos.x);
308  info->cursor_rect.y = dy + PANGO_PIXELS (strong_pos.y);
309  info->cursor_rect.width = PANGO_PIXELS (strong_pos.width);
310  info->cursor_rect.height = PANGO_PIXELS (strong_pos.height);
311 
312  if (info->hatching)
313  {
314  info->hatch_rect.x = dx;
315  info->hatch_rect.y = dy;
316  info->hatch_rect.width = wd;
317  info->hatch_rect.height = hd;
318  }
319 
320  gnc_item_edit_update_offset (item_edit, info);
321 
322  /* Calculate IMContext aux window position */
323  {
324  gint xoff, yoff;
325  GdkRectangle rect;
326  rect = info->cursor_rect;
327  gnome_canvas_get_scroll_offsets(GNOME_CANVAS(sheet), &xoff, &yoff);
328  rect.x += (x - xoff + item_edit->x_offset);
329  rect.y += (y - yoff);
330  gtk_im_context_set_cursor_location (sheet->im_context, &rect);
331  }
332 
333 }
334 
335 static void
336 gnc_item_edit_free_draw_info_members(TextDrawInfo *info)
337 {
338  if (info == NULL)
339  return;
340 
341  g_object_unref (info->layout);
342 }
343 
344 static void
345 gnc_item_edit_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
346  int x, int y, int width, int height)
347 {
348  GncItemEdit *item_edit = GNC_ITEM_EDIT (item);
349  TextDrawInfo info;
350 
351  /* be sure we're valid */
352  if (item_edit->virt_loc.vcell_loc.virt_row < 0 ||
353  item_edit->virt_loc.vcell_loc.virt_col < 0)
354  return;
355 
356  /* Get the measurements for drawing */
357  gnc_item_edit_draw_info (item_edit, x, y, &info);
358 
359  /* Draw the background */
360  gdk_gc_set_foreground (item_edit->gc, info.bg_color);
361  gdk_draw_rectangle (drawable, item_edit->gc, TRUE,
362  info.bg_rect.x, info.bg_rect.y,
363  info.bg_rect.width, info.bg_rect.height);
364 
365  if (info.hatching)
366  gnucash_draw_hatching (drawable, item_edit->gc,
367  info.hatch_rect.x,
368  info.hatch_rect.y,
369  info.hatch_rect.width,
370  info.hatch_rect.height);
371 
372  /* Draw the foreground text and cursor */
373  gdk_gc_set_clip_rectangle (item_edit->gc, &info.text_rect);
374 
375  gdk_gc_set_foreground (item_edit->gc, info.fg_color);
376 
377  gdk_draw_layout (drawable,
378  item_edit->gc,
379  info.text_rect.x + CELL_HPADDING + item_edit->x_offset,
380  info.text_rect.y + CELL_VPADDING,
381  info.layout);
382 
383  gdk_draw_line (drawable,
384  item_edit->gc,
385  info.cursor_rect.x + CELL_HPADDING + item_edit->x_offset,
386  info.cursor_rect.y + CELL_VPADDING,
387  info.cursor_rect.x + CELL_HPADDING + item_edit->x_offset,
388  info.cursor_rect.y + CELL_VPADDING + info.cursor_rect.height);
389 
390  gdk_gc_set_clip_rectangle (item_edit->gc, NULL);
391 
392  gnc_item_edit_free_draw_info_members (&info);
393 }
394 
395 
396 static double
397 gnc_item_edit_point (GnomeCanvasItem *item, double c_x, double c_y, int cx, int cy,
398  GnomeCanvasItem **actual_item)
399 {
400  int x, y, w, h;
401 
402  gnc_item_edit_get_pixel_coords (GNC_ITEM_EDIT (item), &x, &y, &w, &h);
403 
404  *actual_item = NULL;
405  if ((cx < x) || (cy < y) || (cx > x + w) || (cy > y + w))
406  return 10000.0;
407 
408  *actual_item = item;
409 
410  return 0.0;
411 }
412 
413 
414 int
415 gnc_item_edit_get_toggle_offset (int row_height)
416 {
417  /* sync with gnc_item_edit_update */
418  return row_height - (2 * (CELL_VPADDING + 1)) + 3;
419 }
420 
421 static void
422 gnc_item_edit_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path,
423  int flags)
424 {
425  GncItemEdit *item_edit = GNC_ITEM_EDIT (item);
426  gint toggle_x, toggle_y, toggle_width, toggle_height;
427  gint x, y, w, h;
428 
429  if (GNOME_CANVAS_ITEM_CLASS (gnc_item_edit_parent_class)->update)
430  (*GNOME_CANVAS_ITEM_CLASS(gnc_item_edit_parent_class)->update)
431  (item, affine, clip_path, flags);
432 
433  gnc_item_edit_get_pixel_coords (item_edit, &x, &y, &w, &h);
434 
435  item->x1 = x;
436  item->y1 = y;
437  item->x2 = x + w;
438  item->y2 = y + h;
439 
440  if (!item_edit->is_popup)
441  return;
442 
443  toggle_height = h - (2 * (CELL_VPADDING + 1));
444  toggle_width = toggle_height;
445  toggle_x = x + w - (toggle_width + 3);
446  toggle_y = y + (h / 2) - (toggle_height / 2);
447 
448  item_edit->popup_toggle.toggle_offset = toggle_width + 3;
449 
450  gnc_item_edit_show_popup_toggle (item_edit, toggle_x, toggle_y,
451  toggle_width, toggle_height,
452  GTK_ANCHOR_NW);
453 
454  if (item_edit->show_popup)
455  gnc_item_edit_show_popup (item_edit);
456 }
457 
458 
459 static void
460 gnc_item_edit_realize (GnomeCanvasItem *item)
461 {
462  GnomeCanvas *canvas = item->canvas;
463  GdkWindow *window;
464  GncItemEdit *item_edit;
465 
466  if (GNOME_CANVAS_ITEM_CLASS (gnc_item_edit_parent_class)->realize)
467  (*GNOME_CANVAS_ITEM_CLASS
468  (gnc_item_edit_parent_class)->realize) (item);
469 
470  item_edit = GNC_ITEM_EDIT (item);
471  window = GTK_WIDGET (canvas)->window;
472 
473  item_edit->gc = gdk_gc_new (window);
474 }
475 
476 
477 static void
478 gnc_item_edit_unrealize (GnomeCanvasItem *item)
479 {
480  if (GNOME_CANVAS_ITEM_CLASS (gnc_item_edit_parent_class)->unrealize)
481  (*GNOME_CANVAS_ITEM_CLASS
482  (gnc_item_edit_parent_class)->unrealize) (item);
483 }
484 
485 void
486 gnc_item_edit_focus_in (GncItemEdit *item_edit)
487 {
488  GdkEventFocus ev;
489 
490  g_return_if_fail (item_edit != NULL);
491  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
492 
493  ev.type = GDK_FOCUS_CHANGE;
494  ev.window = GTK_WIDGET (item_edit->sheet)->window;
495  ev.in = TRUE;
496  gtk_widget_event (item_edit->editor, (GdkEvent*) &ev);
497  queue_sync(item_edit);
498 }
499 
500 void
501 gnc_item_edit_focus_out (GncItemEdit *item_edit)
502 {
503  GdkEventFocus ev;
504 
505  g_return_if_fail (item_edit != NULL);
506  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
507 
508  ev.type = GDK_FOCUS_CHANGE;
509  ev.window = GTK_WIDGET (item_edit->sheet)->window;
510  ev.in = FALSE;
511  gtk_widget_event (item_edit->editor, (GdkEvent*) &ev);
512  queue_sync(item_edit);
513 }
514 
515 void
516 gnc_item_edit_reset_offset (GncItemEdit *item_edit)
517 {
518  Table *table;
519  PangoRectangle logical_rect;
520  PangoLayout *layout;
521  gint x, y, width, height;
522  gint drawable_width;
523  gint toggle_space;
524 
525  g_return_if_fail (item_edit != NULL);
526  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
527 
528  table = item_edit->sheet->table;
529  layout = gtk_entry_get_layout (GTK_ENTRY(item_edit->editor));
530 
531  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
532  gnc_item_edit_get_pixel_coords (item_edit, &x, &y, &width, &height);
533 
534  toggle_space = item_edit->is_popup ?
535  item_edit->popup_toggle.toggle_offset : 0;
536 
537  drawable_width = width - 2 * CELL_HPADDING - toggle_space;
538 
539  switch (gnc_table_get_align (table, item_edit->virt_loc))
540  {
541  default:
542  case CELL_ALIGN_LEFT:
543  item_edit->x_offset = 0;
544  break;
545 
546  case CELL_ALIGN_RIGHT:
547  item_edit->x_offset = drawable_width - logical_rect.width;
548  break;
549 
550  case CELL_ALIGN_CENTER:
551  if (logical_rect.width > drawable_width)
552  item_edit->x_offset = 0;
553  else
554  item_edit->x_offset = (drawable_width - logical_rect.width) / 2;
555  break;
556  }
557 
558  item_edit->reset_pos = FALSE;
559 }
560 
561 /*
562  * Instance initialization
563  */
564 static void
565 gnc_item_edit_init (GncItemEdit *item_edit)
566 {
567  GnomeCanvasItem *item = GNOME_CANVAS_ITEM (item_edit);
568 
569  item->x1 = 0;
570  item->y1 = 0;
571  item->x2 = 1;
572  item->y2 = 1;
573 
574  /* Set invalid values so that we know when we have been fully
575  initialized */
576  item_edit->sheet = NULL;
577  item_edit->parent = NULL;
578  item_edit->editor = NULL;
579 
580  item_edit->is_popup = FALSE;
581  item_edit->show_popup = FALSE;
582 
583  item_edit->popup_toggle.toggle_button = NULL;
584  item_edit->popup_toggle.toggle_button_item = NULL;
585  item_edit->popup_toggle.toggle_offset = 0;
586  item_edit->popup_toggle.arrow = NULL;
587  item_edit->popup_toggle.signals_connected = FALSE;
588 
589  item_edit->popup_item = NULL;
590  item_edit->get_popup_height = NULL;
591  item_edit->popup_autosize = NULL;
592  item_edit->popup_set_focus = NULL;
593  item_edit->popup_post_show = NULL;
594  item_edit->popup_user_data = NULL;
595 
596  item_edit->gc = NULL;
597  item_edit->style = NULL;
598 
599  item_edit->reset_pos = TRUE;
600  item_edit->x_offset = 0;
601 
602  gnc_virtual_location_init(&item_edit->virt_loc);
603 }
604 
605 
606 static void
607 queue_sync (GncItemEdit *item_edit)
608 {
609  GnomeCanvas *canvas = GNOME_CANVAS_ITEM (item_edit)->canvas;
610  int x, y, w, h;
611 
612  gnc_item_edit_get_pixel_coords (item_edit, &x, &y, &w, &h);
613 
614  gnome_canvas_request_redraw (canvas, x, y, x + w + 1, y + h + 1);
615 }
616 
617 void
618 gnc_item_edit_redraw (GncItemEdit *item_edit)
619 {
620  g_return_if_fail (item_edit != NULL);
621  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
622 
623  queue_sync (item_edit);
624 }
625 
626 static void
627 entry_changed (GtkEntry *entry, void *data)
628 {
629  queue_sync(GNC_ITEM_EDIT (data));
630 }
631 
632 
633 static void
634 gnc_item_edit_dispose (GObject *object)
635 {
636  GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
637 
638  g_signal_handlers_disconnect_matched (G_OBJECT (item_edit->editor), G_SIGNAL_MATCH_DATA,
639  0, 0, NULL, NULL, item_edit);
640 
641  G_OBJECT_CLASS (gnc_item_edit_parent_class)->dispose (object);
642 }
643 
644 static void
645 gnc_item_edit_finalize (GObject *object)
646 {
647  GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
648 
649  if (item_edit->gc)
650  {
651  g_object_unref (item_edit->gc);
652  item_edit->gc = NULL;
653  }
654 
655  G_OBJECT_CLASS (gnc_item_edit_parent_class)->finalize (object);
656 }
657 
658 
659 gboolean
660 gnc_item_edit_set_cursor_pos (GncItemEdit *item_edit,
661  VirtualLocation virt_loc,
662  int x,
663  gboolean changed_cells,
664  gboolean extend_selection)
665 {
666  GtkEditable *editable;
667  Table *table;
668  gint pos = 0;
669  gint o_x;
670  CellDimensions *cd;
671  gint cell_row, cell_col;
672  SheetBlockStyle *style;
673 
674  g_return_val_if_fail (GNC_IS_ITEM_EDIT(item_edit), FALSE);
675 
676  table = item_edit->sheet->table;
677 
678  cell_row = virt_loc.phys_row_offset;
679  cell_col = virt_loc.phys_col_offset;
680 
681  style = gnucash_sheet_get_style (item_edit->sheet, virt_loc.vcell_loc);
682 
683  cd = gnucash_style_get_cell_dimensions (style, cell_row, cell_col);
684 
685  if (!virt_loc_equal (virt_loc, item_edit->virt_loc))
686  return FALSE;
687 
688  editable = GTK_EDITABLE (item_edit->editor);
689 
690  if (changed_cells)
691  gnc_item_edit_reset_offset (item_edit);
692 
693  o_x = cd->origin_x + item_edit->x_offset;
694 
695  if (changed_cells)
696  {
697  CellAlignment align;
698 
699  align = gnc_table_get_align (table, item_edit->virt_loc);
700 
701  if (align == CELL_ALIGN_RIGHT && item_edit->is_popup)
702  o_x += item_edit->popup_toggle.toggle_offset;
703  }
704 
705  // get the text index for the mouse position into pos
706  {
707  PangoLayout *layout;
708  int textByteIndex, textIndex, textTrailing;
709  const gchar *text;
710 
711  layout = gtk_entry_get_layout (GTK_ENTRY(item_edit->editor));
712  text = pango_layout_get_text (layout);
713  pango_layout_xy_to_index (layout,
714  ((x - o_x) - CELL_HPADDING) * PANGO_SCALE,
715  10 * PANGO_SCALE, &textByteIndex,
716  &textTrailing);
717  textIndex = (int) g_utf8_pointer_to_offset (text, text + textByteIndex);
718  pos = textIndex + textTrailing;
719  }
720 
721  if (extend_selection)
722  {
723  gtk_editable_select_region (editable, item_edit->anchor_pos, pos);
724  }
725  else
726  {
727  gtk_editable_set_position (editable, pos);
728  item_edit->anchor_pos = pos;
729  }
730 
731  queue_sync (item_edit);
732 
733  return TRUE;
734 }
735 
736 static int
737 entry_event (GtkEntry *entry, GdkEvent *event, GncItemEdit *item_edit)
738 {
739  switch (event->type)
740  {
741  case GDK_KEY_PRESS:
742  case GDK_KEY_RELEASE:
743  case GDK_BUTTON_PRESS:
744  queue_sync (item_edit);
745  break;
746 
747  default:
748  break;
749  }
750 
751  return FALSE;
752 }
753 
754 static void
755 gnc_item_edit_set_editor (GncItemEdit *item_edit, void *data)
756 {
757  item_edit->editor = GTK_WIDGET (data);
758 
759  g_signal_connect (G_OBJECT (item_edit->editor), "changed",
760  G_CALLBACK (entry_changed), item_edit);
761 
762  g_signal_connect_after (G_OBJECT (item_edit->editor), "event",
763  G_CALLBACK (entry_event), item_edit);
764 }
765 
766 
767 void
768 gnc_item_edit_configure (GncItemEdit *item_edit)
769 {
770  GnucashSheet *sheet = item_edit->sheet;
771  GnucashItemCursor *cursor;
772 
773  cursor = GNUCASH_ITEM_CURSOR
774  (GNUCASH_CURSOR(sheet->cursor)->cursor[GNUCASH_CURSOR_BLOCK]);
775 
776  if (item_edit->virt_loc.vcell_loc.virt_row != cursor->row)
777  {
778  item_edit->virt_loc.vcell_loc.virt_row = cursor->row;
779  item_edit->reset_pos = TRUE;
780  }
781 
782  if (item_edit->virt_loc.vcell_loc.virt_col != cursor->col)
783  {
784  item_edit->virt_loc.vcell_loc.virt_col = cursor->col;
785  item_edit->reset_pos = TRUE;
786  }
787 
788  item_edit->style =
789  gnucash_sheet_get_style (item_edit->sheet,
790  item_edit->virt_loc.vcell_loc);
791 
792  cursor = GNUCASH_ITEM_CURSOR
793  (GNUCASH_CURSOR(sheet->cursor)->cursor[GNUCASH_CURSOR_CELL]);
794 
795  if (item_edit->virt_loc.phys_row_offset != cursor->row)
796  {
797  item_edit->virt_loc.phys_row_offset = cursor->row;
798  item_edit->reset_pos = TRUE;
799  }
800 
801  if (item_edit->virt_loc.phys_col_offset != cursor->col)
802  {
803  item_edit->virt_loc.phys_col_offset = cursor->col;
804  item_edit->reset_pos = TRUE;
805  }
806 
807  if (!gnc_table_is_popup (item_edit->sheet->table, item_edit->virt_loc))
808  gnc_item_edit_set_popup (item_edit, NULL, NULL, NULL,
809  NULL, NULL, NULL, NULL);
810 
811  gnc_item_edit_update (GNOME_CANVAS_ITEM(item_edit), NULL, NULL, 0);
812 }
813 
814 
815 static void
816 gnc_item_edit_cut_copy_clipboard (GncItemEdit *item_edit, guint32 time, gboolean cut)
817 {
818  GtkEditable *editable;
819  GtkClipboard *clipboard;
820  gint start_sel, end_sel;
821  gchar *clip;
822 
823  g_return_if_fail(item_edit != NULL);
824  g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
825 
826  editable = GTK_EDITABLE (item_edit->editor);
827 
828  if (!gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel))
829  return;
830 
831  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (editable),
832  clipboard_atom);
833  g_return_if_fail (clipboard != NULL);
834  g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
835  clip = gtk_editable_get_chars (editable, start_sel, end_sel);
836  gtk_clipboard_set_text (clipboard, clip, -1);
837  g_free (clip);
838 
839  if (!cut)
840  return;
841 
842  gtk_editable_delete_text(editable, start_sel, end_sel);
843  gtk_editable_select_region(editable, 0, 0);
844  gtk_editable_set_position(editable, start_sel);
845 }
846 
847 
848 void
849 gnc_item_edit_cut_clipboard (GncItemEdit *item_edit, guint32 time)
850 {
851  gnc_item_edit_cut_copy_clipboard(item_edit, time, TRUE);
852 }
853 
854 void
855 gnc_item_edit_copy_clipboard (GncItemEdit *item_edit, guint32 time)
856 {
857  gnc_item_edit_cut_copy_clipboard(item_edit, time, FALSE);
858 }
859 
860 
861 
862 static void
863 paste_received (GtkClipboard *clipboard, const gchar *text, gpointer data)
864 {
865  GtkEditable *editable = GTK_EDITABLE (data);
866  gboolean reselect = FALSE;
867  gint old_pos, tmp_pos;
868  gint start_sel, end_sel;
869 
870  if (text == NULL)
871  return;
872  if (gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel))
873  {
874  reselect = TRUE;
875  gtk_editable_delete_text (editable, start_sel, end_sel);
876  }
877 
878  tmp_pos = old_pos = gtk_editable_get_position (editable);
879 
880  gtk_editable_insert_text (editable, text, -1, &tmp_pos);
881  gtk_editable_set_position (editable, tmp_pos);
882 
883  if (!reselect)
884  return;
885 
886  gtk_editable_select_region (editable, old_pos,
887  gtk_editable_get_position (editable));
888 
889 }
890 
891 void
892 gnc_item_edit_paste_selection (GncItemEdit *item_edit, GdkAtom selection,
893  guint32 time)
894 {
895  GtkClipboard *clipboard;
896  g_return_if_fail(item_edit != NULL);
897  g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
898 
899  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (item_edit->sheet),
900  selection);
901 
902  g_return_if_fail (clipboard != NULL);
903  g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
904 
905  gtk_clipboard_request_text (clipboard, paste_received, item_edit->editor);
906 }
907 
908 static void
909 gnc_item_edit_show_popup_toggle (GncItemEdit *item_edit,
910  gint x, gint y,
911  gint width, gint height,
912  GtkAnchorType anchor)
913 {
914  g_return_if_fail (GNC_IS_ITEM_EDIT (item_edit));
915 
916  gnome_canvas_item_raise_to_top
917  (item_edit->popup_toggle.toggle_button_item);
918 
919  gnome_canvas_item_set (item_edit->popup_toggle.toggle_button_item,
920  "x", (gdouble) x,
921  "y", (gdouble) y,
922  "width", (gdouble) width,
923  "height", (gdouble) height,
924  "anchor", anchor,
925  NULL);
926 }
927 
928 
929 static void
930 gnc_item_edit_hide_popup_toggle (GncItemEdit *item_edit)
931 {
932  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
933 
934  /* safely out of the way */
935  gnome_canvas_item_set (item_edit->popup_toggle.toggle_button_item,
936  "x", -10000.0, NULL);
937 }
938 
939 
940 static gboolean
941 key_press_popup_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
942 {
943  GncItemEdit *item_edit = GNC_ITEM_EDIT (data);
944 
945  g_signal_stop_emission_by_name (widget, "key_press_event");
946 
947  gtk_widget_event (GTK_WIDGET(item_edit->sheet), (GdkEvent *) event);
948 
949  return TRUE;
950 }
951 
952 
953 static void
954 gnc_item_edit_popup_toggled (GtkToggleButton *button, gpointer data)
955 {
956  GncItemEdit *item_edit = GNC_ITEM_EDIT (data);
957  gboolean show_popup;
958 
959  show_popup = gtk_toggle_button_get_active (button);
960  if (show_popup)
961  {
962  Table *table;
963  VirtualLocation virt_loc;
964 
965  table = item_edit->sheet->table;
966  virt_loc = table->current_cursor_loc;
967 
968  if (!gnc_table_confirm_change (table, virt_loc))
969  {
970  g_signal_handlers_block_matched
971  (button, G_SIGNAL_MATCH_DATA,
972  0, 0, NULL, NULL, data);
973 
974  gtk_toggle_button_set_active (button, FALSE);
975 
976  g_signal_handlers_unblock_matched
977  (button, G_SIGNAL_MATCH_DATA,
978  0, 0, NULL, NULL, data);
979 
980  return;
981  }
982  }
983 
984  item_edit->show_popup = show_popup;
985 
986  if (!item_edit->show_popup)
987  gnc_item_edit_hide_popup (item_edit);
988 
989  gnc_item_edit_configure (item_edit);
990 }
991 
992 
993 static void
994 block_toggle_signals(GncItemEdit *item_edit)
995 {
996  GtkObject *obj;
997 
998  if (!item_edit->popup_toggle.signals_connected)
999  return;
1000 
1001  obj = GTK_OBJECT (item_edit->popup_toggle.toggle_button);
1002 
1003  g_signal_handlers_block_matched (obj, G_SIGNAL_MATCH_DATA,
1004  0, 0, NULL, NULL, item_edit);
1005 }
1006 
1007 
1008 static void
1009 unblock_toggle_signals(GncItemEdit *item_edit)
1010 {
1011  GtkObject *obj;
1012 
1013  if (!item_edit->popup_toggle.signals_connected)
1014  return;
1015 
1016  obj = GTK_OBJECT (item_edit->popup_toggle.toggle_button);
1017 
1018  g_signal_handlers_unblock_matched (obj, G_SIGNAL_MATCH_DATA,
1019  0, 0, NULL, NULL, item_edit);
1020 }
1021 
1022 
1023 static void
1024 connect_popup_toggle_signals (GncItemEdit *item_edit)
1025 {
1026  GtkObject *object;
1027 
1028  g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
1029 
1030  if (item_edit->popup_toggle.signals_connected)
1031  return;
1032 
1033  object = GTK_OBJECT(item_edit->popup_toggle.toggle_button);
1034 
1035  g_signal_connect (object, "toggled",
1036  G_CALLBACK(gnc_item_edit_popup_toggled),
1037  item_edit);
1038 
1039  g_signal_connect (object, "key_press_event",
1040  G_CALLBACK(key_press_popup_cb),
1041  item_edit);
1042 
1043  item_edit->popup_toggle.signals_connected = TRUE;
1044 }
1045 
1046 
1047 static void
1048 disconnect_popup_toggle_signals (GncItemEdit *item_edit)
1049 {
1050  g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
1051 
1052  if (!item_edit->popup_toggle.signals_connected)
1053  return;
1054 
1055  g_signal_handlers_disconnect_matched
1056  (item_edit->popup_toggle.toggle_button, G_SIGNAL_MATCH_DATA,
1057  0, 0, NULL, NULL, item_edit);
1058 
1059  item_edit->popup_toggle.signals_connected = FALSE;
1060 }
1061 
1062 /* Note that g_value_set_object() refs the object, as does
1063  * g_object_get(). But g_object_get() only unrefs once when it disgorges
1064  * the object, leaving an unbalanced ref, which leaks. So instead of
1065  * using g_value_set_object(), use g_value_take_object() which doesn't
1066  * ref the object when used in get_property().
1067  */
1068 static void
1069 gnc_item_edit_get_property (GObject *object,
1070  guint param_id,
1071  GValue *value,
1072  GParamSpec *pspec)
1073 {
1074  GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
1075 
1076  switch (param_id)
1077  {
1078  case PROP_SHEET:
1079  g_value_take_object (value, item_edit->sheet);
1080  break;
1081  case PROP_EDITOR:
1082  g_value_take_object (value, item_edit->editor);
1083  break;
1084  default:
1085  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1086  break;
1087  }
1088 }
1089 
1090 static void
1091 gnc_item_edit_set_property (GObject *object,
1092  guint param_id,
1093  const GValue *value,
1094  GParamSpec *pspec)
1095 {
1096  GncItemEdit *item_edit = GNC_ITEM_EDIT (object);
1097 
1098  switch (param_id)
1099  {
1100  case PROP_SHEET:
1101  item_edit->sheet = GNUCASH_SHEET (g_value_get_object (value));
1102  break;
1103  case PROP_EDITOR:
1104  gnc_item_edit_set_editor (item_edit, GTK_ENTRY (g_value_get_object (value)));
1105  break;
1106  default:
1107  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1108  break;
1109  }
1110 }
1111 
1112 /*
1113  * GncItemEdit class initialization
1114  */
1115 static void
1116 gnc_item_edit_class_init (GncItemEditClass *gnc_item_edit_class)
1117 {
1118  GObjectClass *object_class;
1119  GnomeCanvasItemClass *item_class;
1120 
1121  gnc_item_edit_parent_class = g_type_class_peek_parent (gnc_item_edit_class);
1122 
1123  object_class = G_OBJECT_CLASS (gnc_item_edit_class);
1124  item_class = GNOME_CANVAS_ITEM_CLASS (gnc_item_edit_class);
1125 
1126  object_class->get_property = gnc_item_edit_get_property;
1127  object_class->set_property = gnc_item_edit_set_property;
1128  object_class->dispose = gnc_item_edit_dispose;
1129  object_class->finalize = gnc_item_edit_finalize;
1130 
1131  g_object_class_install_property (object_class,
1132  PROP_SHEET,
1133  g_param_spec_object ("sheet",
1134  "Sheet Value",
1135  "Sheet Value",
1136  GNUCASH_TYPE_SHEET,
1137  G_PARAM_READWRITE));
1138  g_object_class_install_property (object_class,
1139  PROP_EDITOR,
1140  g_param_spec_object ("editor",
1141  "Editor Value",
1142  "Editor Value",
1143  GTK_TYPE_ENTRY,
1144  G_PARAM_READWRITE));
1145 
1146  /* GnomeCanvasItem method overrides */
1147  item_class->update = gnc_item_edit_update;
1148  item_class->draw = gnc_item_edit_draw;
1149  item_class->point = gnc_item_edit_point;
1150  item_class->realize = gnc_item_edit_realize;
1151  item_class->unrealize = gnc_item_edit_unrealize;
1152  //item_class->event = gnc_item_edit_event;
1153 }
1154 
1155 
1156 GType
1157 gnc_item_edit_get_type (void)
1158 {
1159  static GType gnc_item_edit_type = 0;
1160 
1161  if (!gnc_item_edit_type)
1162  {
1163  static const GTypeInfo gnc_item_edit_info =
1164  {
1165  sizeof (GncItemEditClass),
1166  NULL,
1167  NULL,
1168  (GClassInitFunc) gnc_item_edit_class_init,
1169  NULL,
1170  NULL,
1171  sizeof (GncItemEdit),
1172  0, /* n_preallocs */
1173  (GInstanceInitFunc) gnc_item_edit_init,
1174  NULL,
1175  };
1176 
1177  gnc_item_edit_type =
1178  g_type_register_static(gnome_canvas_item_get_type (),
1179  "GncItemEdit",
1180  &gnc_item_edit_info, 0);
1181  }
1182 
1183  return gnc_item_edit_type;
1184 }
1185 
1186 
1187 static void
1188 create_popup_toggle(GnomeCanvasGroup *parent, PopupToggle *pt)
1189 {
1190  GtkWidget *button, *arrow;
1191 
1192  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN);
1193  gtk_misc_set_alignment(GTK_MISC(arrow), 0.5, 0.5);
1194  pt->arrow = GTK_ARROW(arrow);
1195 
1196  button = gtk_toggle_button_new();
1197  pt->toggle_button = GTK_TOGGLE_BUTTON(button);
1198  gtk_container_add(GTK_CONTAINER(button), arrow);
1199 
1200  gtk_widget_show_all( GTK_WIDGET(pt->toggle_button) );
1201 
1202  pt->toggle_button_item =
1203  gnome_canvas_item_new(parent, gnome_canvas_widget_get_type(),
1204  "widget", button,
1205  "size-pixels", TRUE,
1206  NULL);
1207 }
1208 
1209 
1210 GnomeCanvasItem *
1211 gnc_item_edit_new (GnomeCanvasGroup *parent, GnucashSheet *sheet, GtkWidget *entry)
1212 {
1213  static const GtkTargetEntry targets[] =
1214  {
1215  { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1216  { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
1217  { "TEXT", 0, TARGET_TEXT },
1218  { "STRING", 0, TARGET_STRING },
1219  };
1220  static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
1221 
1222  GnomeCanvasItem *item;
1223  GncItemEdit *item_edit;
1224 
1225  item = gnome_canvas_item_new (parent,
1226  GNC_TYPE_ITEM_EDIT,
1227  "sheet", sheet,
1228  "editor", sheet->entry,
1229  NULL);
1230 
1231  item_edit = GNC_ITEM_EDIT(item);
1232 
1233  item_edit->parent = parent;
1234 
1235  create_popup_toggle (parent, &item_edit->popup_toggle);
1236 
1237  if (clipboard_atom == GDK_NONE)
1238  clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
1239 
1240  gtk_selection_add_targets (GTK_WIDGET(sheet),
1241  GDK_SELECTION_PRIMARY,
1242  targets, n_targets);
1243 
1244  gtk_selection_add_targets (GTK_WIDGET(sheet),
1245  clipboard_atom,
1246  targets, n_targets);
1247 
1248  return item;
1249 }
1250 
1251 
1252 GncItemList *
1253 gnc_item_edit_new_list (GncItemEdit *item_edit, GtkListStore *shared_store)
1254 {
1255  GncItemList *item_list;
1256 
1257  g_return_val_if_fail (GNC_IS_ITEM_EDIT(item_edit), NULL);
1258 
1259  item_list = GNC_ITEM_LIST (gnc_item_list_new (item_edit->parent,
1260  shared_store));
1261 
1262  return item_list;
1263 }
1264 
1265 GNCDatePicker *
1266 gnc_item_edit_new_date_picker (GncItemEdit *item_edit)
1267 {
1268  GNCDatePicker *gdp;
1269 
1270  g_return_val_if_fail (GNC_IS_ITEM_EDIT (item_edit), NULL);
1271 
1272  gdp = GNC_DATE_PICKER (gnc_date_picker_new (item_edit->parent));
1273 
1274  return gdp;
1275 }
1276 
1277 
1278 void
1279 gnc_item_edit_show_popup (GncItemEdit *item_edit)
1280 {
1281  GtkToggleButton *toggle;
1282  GtkAnchorType popup_anchor;
1283  GnucashSheet *sheet;
1284  gint x, y, w, h;
1285  gint y_offset;
1286  gint popup_x, popup_y;
1287  gint popup_width;
1288  gint popup_height;
1289  gint popup_max_width;
1290  gint view_height;
1291  gint view_width;
1292  gint up_height;
1293  gint down_height;
1294 
1295  g_return_if_fail (item_edit != NULL);
1296  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
1297 
1298  if (!item_edit->is_popup)
1299  return;
1300 
1301  sheet = item_edit->sheet;
1302 
1303  view_height = GTK_WIDGET (sheet)->allocation.height;
1304  view_width = GTK_WIDGET (sheet)->allocation.width;
1305 
1306  gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), NULL, &y_offset);
1307  gnc_item_edit_get_pixel_coords (item_edit, &x, &y, &w, &h);
1308 
1309  popup_x = x;
1310 
1311  up_height = y - y_offset;
1312  down_height = view_height - (up_height + h);
1313 
1314  if (up_height > down_height)
1315  {
1316  popup_y = y;
1317  popup_anchor = GTK_ANCHOR_SW;
1318  popup_height = up_height;
1319  }
1320  else
1321  {
1322  popup_y = y + h;
1323  popup_anchor = GTK_ANCHOR_NW;
1324  popup_height = down_height;
1325  }
1326 
1327  popup_max_width = view_width - popup_x;
1328 
1329  if (item_edit->get_popup_height)
1330  popup_height = item_edit->get_popup_height
1331  (item_edit->popup_item, popup_height, h,
1332  item_edit->popup_user_data);
1333 
1334  if (item_edit->popup_autosize)
1335  popup_width =
1336  item_edit->popup_autosize (item_edit->popup_item,
1337  popup_max_width,
1338  item_edit->popup_user_data);
1339  else
1340  popup_width = 0;
1341 
1342  if (popup_width > 0)
1343  gnome_canvas_item_set (item_edit->popup_item,
1344  "x", (gdouble) popup_x,
1345  "y", (gdouble) popup_y,
1346  "height", (gdouble) popup_height,
1347  "width", (gdouble) popup_width,
1348  "anchor", popup_anchor,
1349  NULL);
1350  else
1351  gnome_canvas_item_set (item_edit->popup_item,
1352  "x", (gdouble) popup_x,
1353  "y", (gdouble) popup_y,
1354  "height", (gdouble) popup_height,
1355  "anchor", popup_anchor,
1356  NULL);
1357 
1358  toggle = item_edit->popup_toggle.toggle_button;
1359 
1360  if (!gtk_toggle_button_get_active (toggle))
1361  {
1362  block_toggle_signals (item_edit);
1363  gtk_toggle_button_set_active (toggle, TRUE);
1364  unblock_toggle_signals (item_edit);
1365  }
1366 
1367  gtk_arrow_set (item_edit->popup_toggle.arrow,
1368  GTK_ARROW_UP, GTK_SHADOW_OUT);
1369 
1370  if (item_edit->popup_set_focus)
1371  item_edit->popup_set_focus (item_edit->popup_item,
1372  item_edit->popup_user_data);
1373 
1374  if (item_edit->popup_post_show)
1375  item_edit->popup_post_show (item_edit->popup_item,
1376  item_edit->popup_user_data);
1377 
1378  if (item_edit->popup_get_width)
1379  {
1380  int popup_width;
1381 
1382  popup_width = item_edit->popup_get_width
1383  (item_edit->popup_item,
1384  item_edit->popup_user_data);
1385 
1386  if (popup_width > popup_max_width)
1387  {
1388  popup_x -= popup_width - popup_max_width;
1389  popup_x = MAX (0, popup_x);
1390 
1391  gnome_canvas_item_set (item_edit->popup_item,
1392  "x", (gdouble) popup_x,
1393  NULL);
1394  }
1395  }
1396 }
1397 
1398 
1399 void
1400 gnc_item_edit_hide_popup (GncItemEdit *item_edit)
1401 {
1402  g_return_if_fail(item_edit != NULL);
1403  g_return_if_fail(GNC_IS_ITEM_EDIT(item_edit));
1404 
1405  if (!item_edit->is_popup)
1406  return;
1407 
1408  gnome_canvas_item_set (item_edit->popup_item, "x", -10000.0, NULL);
1409 
1410  gtk_arrow_set (item_edit->popup_toggle.arrow,
1411  GTK_ARROW_DOWN, GTK_SHADOW_IN);
1412 
1413  gtk_toggle_button_set_active
1414  (item_edit->popup_toggle.toggle_button, FALSE);
1415 
1416  gtk_widget_grab_focus (GTK_WIDGET (item_edit->sheet));
1417 }
1418 
1419 void
1420 gnc_item_edit_set_popup (GncItemEdit *item_edit,
1421  GnomeCanvasItem *popup_item,
1422  GetPopupHeight get_popup_height,
1423  PopupAutosize popup_autosize,
1424  PopupSetFocus popup_set_focus,
1425  PopupPostShow popup_post_show,
1426  PopupGetWidth popup_get_width,
1427  gpointer popup_user_data)
1428 {
1429  g_return_if_fail (GNC_IS_ITEM_EDIT(item_edit));
1430 
1431  if (item_edit->is_popup)
1432  gnc_item_edit_hide_popup (item_edit);
1433 
1434  item_edit->is_popup = popup_item != NULL;
1435 
1436  item_edit->popup_item = popup_item;
1437  item_edit->get_popup_height = get_popup_height;
1438  item_edit->popup_autosize = popup_autosize;
1439  item_edit->popup_set_focus = popup_set_focus;
1440  item_edit->popup_post_show = popup_post_show;
1441  item_edit->popup_get_width = popup_get_width;
1442  item_edit->popup_user_data = popup_user_data;
1443 
1444  if (item_edit->is_popup)
1445  connect_popup_toggle_signals (item_edit);
1446  else
1447  {
1448  disconnect_popup_toggle_signals (item_edit);
1449 
1450  gnc_item_edit_hide_popup (item_edit);
1451  gnc_item_edit_hide_popup_toggle (item_edit);
1452  }
1453 
1454  gnc_item_edit_update (GNOME_CANVAS_ITEM (item_edit), NULL, NULL, 0);
1455 }
1456 
1457 void
1458 gnc_item_edit_set_has_selection (GncItemEdit *item_edit, gboolean has_selection)
1459 {
1460  g_return_if_fail (item_edit != NULL);
1461  g_return_if_fail (GNC_IS_ITEM_EDIT (item_edit));
1462 
1463  item_edit->has_selection = has_selection;
1464 }
1465 
1466 gboolean
1467 gnc_item_edit_get_has_selection (GncItemEdit *item_edit)
1468 {
1469  GtkEditable *editable;
1470 
1471  g_return_val_if_fail ((item_edit != NULL), FALSE);
1472  g_return_val_if_fail (GNC_IS_ITEM_EDIT (item_edit), FALSE);
1473 
1474  editable = GTK_EDITABLE (item_edit->editor);
1475  return gtk_editable_get_selection_bounds(editable, NULL, NULL);
1476 }
1477