GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnucash-sheet.c
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or *
3  * modify it under the terms of the GNU General Public License as *
4  * published by the Free Software Foundation; either version 2 of *
5  * the License, or (at your option) any later version. *
6  * *
7  * This program is distributed in the hope that it will be useful, *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
10  * GNU General Public License for more details. *
11  * *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact: *
14  * *
15  * Free Software Foundation Voice: +1-617-542-5942 *
16  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
17  * Boston, MA 02110-1301, USA [email protected] *
18  * *
19 \********************************************************************/
20 
21 /*
22  * The Gnucash Sheet widget
23  *
24  * Based heavily on the Gnumeric Sheet widget.
25  *
26  * Authors:
27  * Heath Martin <[email protected]>
28  * Dave Peticolas <[email protected]>
29  */
30 
31 #include "config.h"
32 #include <glib.h>
33 #include <glib/gprintf.h>
34 #include <gdk/gdkkeysyms.h>
35 #include <libgnomecanvas/libgnomecanvas.h>
36 
37 #include "gnucash-sheet.h"
38 #include "gnucash-sheetP.h"
39 
40 #include "dialog-utils.h"
41 #include "gnc-prefs.h"
42 #include "gnucash-color.h"
43 #include "gnucash-grid.h"
44 #include "gnucash-cursor.h"
45 #include "gnucash-style.h"
46 #include "gnucash-header.h"
47 #include "gnucash-item-edit.h"
48 #include "split-register.h"
49 #include "gnc-engine.h" // For debugging, e.g. ENTER(), LEAVE()
50 
51 #ifdef G_OS_WIN32
52 # include <gdk/gdkwin32.h>
53 #endif
54 
55 #define DEFAULT_REGISTER_HEIGHT 400
56 #define DEFAULT_REGISTER_WIDTH 400
57 /* Used to calculate the minimum preferred height of the register window: */
58 #define DEFAULT_REGISTER_INITIAL_ROWS 10
59 
60 
61 /* Register signals */
62 enum
63 {
64  ACTIVATE_CURSOR,
65  REDRAW_ALL,
66  REDRAW_HELP,
67  LAST_SIGNAL
68 };
69 
70 
73 /* This static indicates the debugging module that this .o belongs to. */
74 static QofLogModule log_module = GNC_MOD_REGISTER;
75 static GnomeCanvasClass *sheet_parent_class;
76 static GtkTableClass *register_parent_class;
77 static guint register_signals[LAST_SIGNAL];
78 
79 
82 static void gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet);
83 
84 static gboolean gnucash_sheet_cursor_move (GnucashSheet *sheet,
85  VirtualLocation virt_loc);
86 
87 static void gnucash_sheet_deactivate_cursor_cell (GnucashSheet *sheet);
88 static void gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
89  gboolean changed_cells);
90 static void gnucash_sheet_stop_editing (GnucashSheet *sheet);
91 static void gnucash_sheet_im_context_reset (GnucashSheet *sheet);
92 static void gnucash_sheet_commit_cb (GtkIMContext *context, const gchar *str,
93  GnucashSheet *sheet);
94 static void gnucash_sheet_preedit_changed_cb (GtkIMContext *context,
95  GnucashSheet *sheet);
96 static gboolean gnucash_sheet_retrieve_surrounding_cb (GtkIMContext *context,
97  GnucashSheet *sheet);
98 static gboolean gnucash_sheet_delete_surrounding_cb (GtkIMContext *context,
99  gint offset,
100  gint n_chars,
101  GnucashSheet *sheet);
102 static gboolean gnucash_sheet_check_direct_update_cell(GnucashSheet *sheet,
103  const VirtualLocation virt_loc);
104 
107 G_INLINE_FUNC gboolean
108 gnucash_sheet_virt_cell_out_of_bounds (GnucashSheet *sheet,
109  VirtualCellLocation vcell_loc);
110 gboolean
111 gnucash_sheet_virt_cell_out_of_bounds (GnucashSheet *sheet,
112  VirtualCellLocation vcell_loc)
113 {
114  return (vcell_loc.virt_row < 1 ||
115  vcell_loc.virt_row >= sheet->num_virt_rows ||
116  vcell_loc.virt_col < 0 ||
117  vcell_loc.virt_col >= sheet->num_virt_cols);
118 }
119 
120 static gboolean
121 gnucash_sheet_cell_valid (GnucashSheet *sheet, VirtualLocation virt_loc)
122 {
123  gboolean valid;
124  SheetBlockStyle *style;
125 
126  valid = !gnucash_sheet_virt_cell_out_of_bounds (sheet,
127  virt_loc.vcell_loc);
128 
129  if (valid)
130  {
131  style = gnucash_sheet_get_style (sheet, virt_loc.vcell_loc);
132 
133  valid = (virt_loc.phys_row_offset >= 0 &&
134  virt_loc.phys_row_offset < style->nrows &&
135  virt_loc.phys_col_offset >= 0 &&
136  virt_loc.phys_col_offset < style->ncols);
137  }
138 
139  return valid;
140 }
141 
142 
143 void
144 gnucash_sheet_cursor_set (GnucashSheet *sheet, VirtualLocation virt_loc)
145 {
146  g_return_if_fail (sheet != NULL);
147  g_return_if_fail (GNUCASH_IS_SHEET (sheet));
148 
149  g_return_if_fail (virt_loc.vcell_loc.virt_row >= 0 ||
150  virt_loc.vcell_loc.virt_row <= sheet->num_virt_rows);
151  g_return_if_fail (virt_loc.vcell_loc.virt_col >= 0 ||
152  virt_loc.vcell_loc.virt_col <= sheet->num_virt_cols);
153 
154  gnucash_cursor_set (GNUCASH_CURSOR(sheet->cursor), virt_loc);
155 }
156 
157 void
158 gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet, gboolean do_scroll)
159 {
160  Table *table;
161  VirtualLocation v_loc;
162 
163  g_return_if_fail (sheet != NULL);
164  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
165 
166  table = sheet->table;
167  v_loc = table->current_cursor_loc;
168 
169  g_return_if_fail (gnucash_sheet_cell_valid (sheet, v_loc));
170 
171  gnucash_sheet_cursor_set (sheet, v_loc);
172 
173  if (do_scroll)
174  gnucash_sheet_make_cell_visible (sheet, v_loc);
175 }
176 
177 
178 static void
179 gnucash_sheet_set_popup (GnucashSheet *sheet, GtkWidget *popup, gpointer data)
180 {
181  if (popup)
182  g_object_ref (popup);
183 
184  if (sheet->popup)
185  g_object_unref (sheet->popup);
186 
187  sheet->popup = popup;
188  sheet->popup_data = data;
189 }
190 
191 
192 static void
193 gnucash_sheet_hide_editing_cursor (GnucashSheet *sheet)
194 {
195  if (sheet->item_editor == NULL)
196  return;
197 
198  gnome_canvas_item_hide (GNOME_CANVAS_ITEM (sheet->item_editor));
199  gnc_item_edit_hide_popup (GNC_ITEM_EDIT(sheet->item_editor));
200 }
201 
202 static void
203 gnucash_sheet_stop_editing (GnucashSheet *sheet)
204 {
205  /* Rollback an uncommitted string if it exists *
206  * *before* disconnecting signal handlers. */
207  gnucash_sheet_im_context_reset(sheet);
208 
209  if (sheet->insert_signal != 0)
210  g_signal_handler_disconnect (G_OBJECT(sheet->entry),
211  sheet->insert_signal);
212  if (sheet->delete_signal != 0)
213  g_signal_handler_disconnect (G_OBJECT(sheet->entry),
214  sheet->delete_signal);
215  if (sheet->commit_signal != 0)
216  g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
217  sheet->commit_signal);
218  if (sheet->preedit_changed_signal != 0)
219  g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
220  sheet->preedit_changed_signal);
221  if (sheet->retrieve_surrounding_signal != 0)
222  g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
223  sheet->retrieve_surrounding_signal);
224  if (sheet->delete_surrounding_signal != 0)
225  g_signal_handler_disconnect (G_OBJECT(sheet->im_context),
226  sheet->delete_surrounding_signal);
227  sheet->insert_signal = 0;
228  sheet->delete_signal = 0;
229  sheet->commit_signal = 0;
230  sheet->preedit_changed_signal = 0;
231  sheet->retrieve_surrounding_signal = 0;
232  sheet->delete_surrounding_signal = 0;
233  sheet->direct_update_cell = FALSE;
234 
235  gnucash_sheet_hide_editing_cursor (sheet);
236 
237  sheet->editing = FALSE;
238  sheet->input_cancelled = FALSE;
239 }
240 
241 
242 static void
243 gnucash_sheet_deactivate_cursor_cell (GnucashSheet *sheet)
244 {
245  VirtualLocation virt_loc;
246 
247  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
248 
249  gnucash_sheet_stop_editing (sheet);
250 
251  if (!gnc_table_model_read_only (sheet->table->model))
252  gnc_table_leave_update (sheet->table, virt_loc);
253 
254  gnucash_sheet_redraw_block (sheet, virt_loc.vcell_loc);
255 }
256 
257 
258 static void
259 gnucash_sheet_activate_cursor_cell (GnucashSheet *sheet,
260  gboolean changed_cells)
261 {
262  Table *table = sheet->table;
263  VirtualLocation virt_loc;
264  SheetBlockStyle *style;
265  GtkEditable *editable;
266  int cursor_pos, start_sel, end_sel;
267  gboolean allow_edits;
268 
269  /* Sanity check */
270  if (sheet->editing)
271  gnucash_sheet_deactivate_cursor_cell (sheet);
272 
273  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
274 
275  /* This should be a no-op */
276  gnc_table_wrap_verify_cursor_position (table, virt_loc);
277 
278  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
279 
280  if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE))
281  return;
282 
283  style = gnucash_sheet_get_style (sheet, virt_loc.vcell_loc);
284  if (strcmp (style->cursor->cursor_name, CURSOR_HEADER) == 0)
285  return;
286 
287  editable = GTK_EDITABLE(sheet->entry);
288 
289  cursor_pos = -1;
290  start_sel = 0;
291  end_sel = 0;
292 
293  if (gnc_table_model_read_only (table->model))
294  allow_edits = FALSE;
295  else
296  allow_edits = gnc_table_enter_update (table, virt_loc,
297  &cursor_pos,
298  &start_sel, &end_sel);
299 
300  if (!allow_edits)
301  gnucash_sheet_redraw_block (sheet, virt_loc.vcell_loc);
302  else
303  {
304  gnucash_sheet_im_context_reset(sheet);
305  gnucash_sheet_start_editing_at_cursor (sheet);
306  gtk_editable_set_position (editable, cursor_pos);
307  gtk_editable_select_region (editable, start_sel, end_sel);
308  sheet->direct_update_cell =
309  gnucash_sheet_check_direct_update_cell (sheet, virt_loc);
310  }
311 
312  gtk_widget_grab_focus (GTK_WIDGET(sheet));
313 }
314 
315 
316 static gboolean
317 gnucash_sheet_cursor_move (GnucashSheet *sheet, VirtualLocation virt_loc)
318 {
319  VirtualLocation old_virt_loc;
320  gboolean changed_cells;
321  Table *table;
322 
323  table = sheet->table;
324 
325  /* Get the old cursor position */
326  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &old_virt_loc);
327 
328  /* Turn off the editing controls */
329  gnucash_sheet_deactivate_cursor_cell (sheet);
330 
331  /* Do the move. This may result in table restructuring due to
332  * commits, auto modes, etc. */
333  gnc_table_wrap_verify_cursor_position (table, virt_loc);
334 
335  /* A complete reload can leave us with editing back on */
336  if (sheet->editing)
337  gnucash_sheet_deactivate_cursor_cell (sheet);
338 
339  /* Find out where we really landed. We have to get the new
340  * physical position as well, as the table may have been
341  * restructured. */
342  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
343 
344  gnucash_sheet_cursor_set (sheet, virt_loc);
345 
346  /* We should be at our new location now. Show it on screen and
347  * configure the cursor. */
348  gnucash_sheet_make_cell_visible (sheet, virt_loc);
349 
350  changed_cells = !virt_loc_equal (virt_loc, old_virt_loc);
351 
352  /* If we've changed cells, redraw the headers */
353  if (changed_cells)
354  gnc_header_request_redraw (GNC_HEADER(sheet->header_item));
355 
356  /* Now turn on the editing controls. */
357  gnucash_sheet_activate_cursor_cell (sheet, changed_cells);
358 
359  if (sheet->moved_cb)
360  (sheet->moved_cb)(sheet, sheet->moved_cb_data);
361  return changed_cells;
362 }
363 
364 
365 static gint
366 gnucash_sheet_y_pixel_to_block (GnucashSheet *sheet, int y)
367 {
368  VirtualCellLocation vcell_loc = { 1, 0 };
369 
370  for (;
371  vcell_loc.virt_row < sheet->num_virt_rows;
372  vcell_loc.virt_row++)
373  {
374  SheetBlock *block;
375 
376  block = gnucash_sheet_get_block (sheet, vcell_loc);
377  if (!block || !block->visible)
378  continue;
379 
380  if (block->origin_y + block->style->dimensions->height > y)
381  break;
382  }
383 
384  return vcell_loc.virt_row;
385 }
386 
387 
388 void
389 gnucash_sheet_compute_visible_range (GnucashSheet *sheet)
390 {
391  VirtualCellLocation vcell_loc;
392  gint height;
393  gint cy;
394  gint old_visible_blocks, old_visible_rows;
395 
396  g_return_if_fail (sheet != NULL);
397  g_return_if_fail (GNUCASH_IS_SHEET (sheet));
398 
399  height = GTK_WIDGET(sheet)->allocation.height;
400 
401  gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), NULL, &cy);
402 
403  sheet->top_block = gnucash_sheet_y_pixel_to_block (sheet, cy);
404 
405  old_visible_blocks = sheet->num_visible_blocks;
406  old_visible_rows = sheet->num_visible_phys_rows;
407  sheet->num_visible_blocks = 0;
408  sheet->num_visible_phys_rows = 0;
409 
410  for ( vcell_loc.virt_row = sheet->top_block, vcell_loc.virt_col = 0;
411  vcell_loc.virt_row < sheet->num_virt_rows;
412  vcell_loc.virt_row++ )
413  {
414  SheetBlock *block;
415 
416  block = gnucash_sheet_get_block (sheet, vcell_loc);
417  if (!block->visible)
418  continue;
419 
420  sheet->num_visible_blocks++;
421  sheet->num_visible_phys_rows += block->style->nrows;
422 
423  if (block->origin_y - cy + block->style->dimensions->height
424  >= height)
425  break;
426  }
427 
428  sheet->bottom_block = vcell_loc.virt_row;
429 
430  /* FIXME */
431  sheet->left_block = 0;
432  sheet->right_block = 0;
433 
434  if ((old_visible_blocks > sheet->num_visible_blocks) ||
435  (old_visible_rows > sheet->num_visible_phys_rows))
436  {
437  /* Reach up and tell the parent widget to redraw as
438  * well. The sheet doesn't occupy all the visible
439  * area in the notebook page, and this will cause the
440  * parent to color in the empty grey space after the
441  * area occupied by the sheet. */
442  gtk_widget_queue_draw(gtk_widget_get_parent(GTK_WIDGET(sheet)));
443  }
444 }
445 
446 
447 static void
448 gnucash_sheet_show_row (GnucashSheet *sheet, gint virt_row)
449 {
450  VirtualCellLocation vcell_loc = { virt_row, 0 };
451  SheetBlock *block;
452  gint block_height;
453  gint height;
454  gint cx, cy;
455  gint x, y;
456 
457  g_return_if_fail (virt_row >= 0);
458  g_return_if_fail (sheet != NULL);
459  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
460 
461  vcell_loc.virt_row = MAX (vcell_loc.virt_row, 1);
462  vcell_loc.virt_row = MIN (vcell_loc.virt_row,
463  sheet->num_virt_rows - 1);
464 
465  gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), &cx, &cy);
466  x = cx;
467 
468  height = GTK_WIDGET(sheet)->allocation.height;
469 
470  block = gnucash_sheet_get_block (sheet, vcell_loc);
471 
472  y = block->origin_y;
473  block_height = block->style->dimensions->height;
474 
475  if ((cy <= y) && (cy + height >= y + block_height))
476  {
477  gnucash_sheet_compute_visible_range (sheet);
478  return;
479  }
480 
481  if (y > cy)
482  y -= height - MIN (block_height, height);
483 
484  if ((sheet->height - y) < height)
485  y = sheet->height - height;
486 
487  if (y < 0)
488  y = 0;
489 
490  if (y != cy)
491  gtk_adjustment_set_value (sheet->vadj, y);
492  if (x != cx)
493  gtk_adjustment_set_value (sheet->hadj, x);
494 
495  gnucash_sheet_compute_visible_range (sheet);
496  gnucash_sheet_update_adjustments (sheet);
497 }
498 
499 
500 void
501 gnucash_sheet_make_cell_visible (GnucashSheet *sheet, VirtualLocation virt_loc)
502 {
503  g_return_if_fail (sheet != NULL);
504  g_return_if_fail (GNUCASH_IS_SHEET (sheet));
505 
506  if (!gnucash_sheet_cell_valid (sheet, virt_loc))
507  return;
508 
509  gnucash_sheet_show_row (sheet, virt_loc.vcell_loc.virt_row);
510 
511  gnucash_sheet_update_adjustments (sheet);
512 }
513 
514 
515 void
516 gnucash_sheet_show_range (GnucashSheet *sheet,
517  VirtualCellLocation start_loc,
518  VirtualCellLocation end_loc)
519 {
520  SheetBlock *start_block;
521  SheetBlock *end_block;
522  gint block_height;
523  gint height;
524  gint cx, cy;
525  gint x, y;
526 
527  g_return_if_fail (sheet != NULL);
528  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
529 
530  start_loc.virt_row = MAX (start_loc.virt_row, 1);
531  start_loc.virt_row = MIN (start_loc.virt_row,
532  sheet->num_virt_rows - 1);
533 
534  end_loc.virt_row = MAX (end_loc.virt_row, 1);
535  end_loc.virt_row = MIN (end_loc.virt_row,
536  sheet->num_virt_rows - 1);
537 
538  gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), &cx, &cy);
539  x = cx;
540 
541  height = GTK_WIDGET(sheet)->allocation.height;
542 
543  start_block = gnucash_sheet_get_block (sheet, start_loc);
544  end_block = gnucash_sheet_get_block (sheet, end_loc);
545 
546  y = start_block->origin_y;
547  block_height = (end_block->origin_y +
548  end_block->style->dimensions->height) - y;
549 
550  if ((cy <= y) && (cy + height >= y + block_height))
551  {
552  gnucash_sheet_compute_visible_range (sheet);
553  return;
554  }
555 
556  if (y > cy)
557  y -= height - MIN (block_height, height);
558 
559  if ((sheet->height - y) < height)
560  y = sheet->height - height;
561 
562  if (y < 0)
563  y = 0;
564 
565  if (y != cy)
566  gtk_adjustment_set_value (sheet->vadj, y);
567  if (x != cx)
568  gtk_adjustment_set_value (sheet->hadj, x);
569 
570  gnucash_sheet_compute_visible_range (sheet);
571  gnucash_sheet_update_adjustments (sheet);
572 }
573 
574 
575 void
576 gnucash_sheet_update_adjustments (GnucashSheet *sheet)
577 {
578  GtkAdjustment *vadj;
579 
580  g_return_if_fail (sheet != NULL);
581  g_return_if_fail (GNUCASH_IS_SHEET (sheet));
582  g_return_if_fail (sheet->vadj != NULL);
583 
584  vadj = sheet->vadj;
585 
586  if (sheet->num_visible_blocks > 0)
587  vadj->step_increment =
588  vadj->page_size / sheet->num_visible_blocks;
589  else
590  vadj->step_increment = 0;
591 
592  gtk_adjustment_changed(vadj);
593 }
594 
595 
596 static void
597 gnucash_sheet_vadjustment_value_changed (GtkAdjustment *adj,
598  GnucashSheet *sheet)
599 {
600  gnucash_sheet_compute_visible_range (sheet);
601 }
602 
603 
604 static void
605 gnucash_sheet_hadjustment_changed (GtkAdjustment *adj,
606  GnucashSheet *sheet)
607 {
608  GnucashRegister *reg;
609 
610  g_return_if_fail (sheet != NULL);
611  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
612  reg = GNUCASH_REGISTER(sheet->reg);
613  g_return_if_fail (reg != NULL);
614 
615  if (adj->upper - adj->lower > adj->page_size)
616  {
617  if (!reg->hscrollbar_visible)
618  {
619  gtk_widget_show(reg->hscrollbar);
620  reg->hscrollbar_visible = TRUE;
621  }
622  }
623  else
624  {
625  if (reg->hscrollbar_visible)
626  {
627  gtk_widget_hide(reg->hscrollbar);
628  reg->hscrollbar_visible = FALSE;
629  }
630  }
631 }
632 
633 
634 void
635 gnucash_sheet_redraw_all (GnucashSheet *sheet)
636 {
637  g_return_if_fail (sheet != NULL);
638  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
639 
640  gnome_canvas_request_redraw (GNOME_CANVAS (sheet), 0, 0,
641  sheet->width + 1, sheet->height + 1);
642 
643  g_signal_emit_by_name (sheet->reg, "redraw_all");
644 }
645 
646 void
647 gnucash_sheet_redraw_help (GnucashSheet *sheet)
648 {
649  g_return_if_fail (sheet != NULL);
650  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
651 
652  g_signal_emit_by_name (sheet->reg, "redraw_help");
653 }
654 
655 void
656 gnucash_sheet_redraw_block (GnucashSheet *sheet, VirtualCellLocation vcell_loc)
657 {
658  gint x, y, w, h;
659  GnomeCanvas *canvas;
660  SheetBlock *block;
661 
662  g_return_if_fail (sheet != NULL);
663  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
664 
665  canvas = GNOME_CANVAS(sheet);
666 
667  block = gnucash_sheet_get_block (sheet, vcell_loc);
668  if (!block || !block->style)
669  return;
670 
671  x = block->origin_x;
672  y = block->origin_y;
673 
674  h = block->style->dimensions->height;
675  w = MIN(block->style->dimensions->width,
676  GTK_WIDGET(sheet)->allocation.width);
677 
678  gnome_canvas_request_redraw (canvas, x, y, x + w + 1, y + h + 1);
679 }
680 
681 static void
682 gnucash_sheet_finalize (GObject *object)
683 {
684  GnucashSheet *sheet;
685 
686  sheet = GNUCASH_SHEET (object);
687 
688  g_table_destroy (sheet->blocks);
689  sheet->blocks = NULL;
690 
691  gnucash_sheet_clear_styles (sheet);
692 
693  g_hash_table_destroy (sheet->cursor_styles);
694  g_hash_table_destroy (sheet->dimensions_hash_table);
695 
696  if (G_OBJECT_CLASS (sheet_parent_class)->finalize)
697  (*G_OBJECT_CLASS (sheet_parent_class)->finalize)(object);
698 
699  /* Clean up IMContext and unref */
700  gnucash_sheet_im_context_reset(sheet);
701  g_object_unref (sheet->im_context);
702 
703  /* This has to come after the parent destroy, so the item edit
704  destruction can do its disconnects. */
705  g_object_unref (sheet->entry);
706 }
707 
708 
709 static void
710 gnucash_sheet_realize (GtkWidget *widget)
711 {
712  GdkWindow *window;
713 
714  if (GTK_WIDGET_CLASS (sheet_parent_class)->realize)
715  (*GTK_WIDGET_CLASS (sheet_parent_class)->realize)(widget);
716 
717  window = widget->window;
718  gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window,
719  NULL, FALSE);
720  gtk_im_context_set_client_window( GNUCASH_SHEET (widget)->im_context,
721  window);
722 }
723 
724 
725 static GnucashSheet *
726 gnucash_sheet_create (Table *table)
727 {
728  GnucashSheet *sheet;
729  GnomeCanvas *canvas;
730 
731  ENTER("table=%p", table);
732 
733  sheet = g_object_new (GNUCASH_TYPE_SHEET, NULL);
734  canvas = GNOME_CANVAS (sheet);
735 
736  sheet->table = table;
737  sheet->entry = NULL;
738 
739  sheet->vadj = gtk_layout_get_vadjustment (GTK_LAYOUT(canvas));
740  sheet->hadj = gtk_layout_get_hadjustment (GTK_LAYOUT(canvas));
741 
742  g_signal_connect (G_OBJECT (sheet->vadj), "value_changed",
743  G_CALLBACK (gnucash_sheet_vadjustment_value_changed), sheet);
744  g_signal_connect (G_OBJECT (sheet->hadj), "changed",
745  G_CALLBACK (gnucash_sheet_hadjustment_changed), sheet);
746 
747  LEAVE("%p", sheet);
748  return sheet;
749 }
750 
751 static gint
752 compute_optimal_width (GnucashSheet *sheet)
753 {
754  return DEFAULT_REGISTER_WIDTH;
755 }
756 
757 
758 /* Compute the height needed to show DEFAULT_REGISTER_INITIAL_ROWS rows */
759 static gint
760 compute_optimal_height (GnucashSheet *sheet)
761 {
762  SheetBlockStyle *style;
763  CellDimensions *cd;
764  gint row_height;
765 
766  if (!sheet)
767  return DEFAULT_REGISTER_HEIGHT;
768 
769  style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
770  if (!style)
771  return DEFAULT_REGISTER_HEIGHT;
772 
773  cd = gnucash_style_get_cell_dimensions (style, 0, 0);
774  if (cd == NULL)
775  return DEFAULT_REGISTER_HEIGHT;
776 
777  row_height = cd->pixel_height;
778 
779  return row_height * DEFAULT_REGISTER_INITIAL_ROWS;
780 }
781 
782 
783 static void
784 gnucash_sheet_size_request (GtkWidget *widget, GtkRequisition *requisition)
785 {
786  GnucashSheet *sheet = GNUCASH_SHEET(widget);
787 
788  requisition->width = compute_optimal_width (sheet);
789  requisition->height = compute_optimal_height (sheet);
790 }
791 
792 const char *
793 gnucash_sheet_modify_current_cell (GnucashSheet *sheet, const gchar *new_text)
794 {
795  GtkEditable *editable;
796  Table *table = sheet->table;
797  VirtualLocation virt_loc;
798  int new_text_len;
799 
800  const char *retval;
801 
802  int cursor_position, start_sel, end_sel;
803 
804  gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor), &virt_loc);
805 
806  if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE))
807  return NULL;
808 
809  if (gnc_table_model_read_only (table->model))
810  return NULL;
811 
812  editable = GTK_EDITABLE(sheet->entry);
813 
814  cursor_position = gtk_editable_get_position (editable);
815  gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel);
816 
817  new_text_len = strlen (new_text);
818 
819  retval = gnc_table_modify_update (table, virt_loc,
820  new_text, new_text_len,
821  new_text, new_text_len,
822  &cursor_position,
823  &start_sel, &end_sel,
824  NULL);
825 
826 
827  if (retval)
828  {
829  gnc_item_edit_reset_offset (GNC_ITEM_EDIT(sheet->item_editor));
830 
831  g_signal_handler_block (G_OBJECT (sheet->entry),
832  sheet->insert_signal);
833  g_signal_handler_block (G_OBJECT (sheet->entry),
834  sheet->delete_signal);
835 
836  gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
837 
838  g_signal_handler_unblock (G_OBJECT (sheet->entry),
839  sheet->delete_signal);
840  g_signal_handler_unblock (G_OBJECT (sheet->entry),
841  sheet->insert_signal);
842  }
843 
844  gtk_editable_set_position (editable, cursor_position);
845  gtk_editable_select_region(editable, start_sel, end_sel);
846 
847  return retval;
848 }
849 
850 typedef struct
851 {
852  GtkEditable *editable;
853  int start_sel;
854  int end_sel;
855 
856 } select_info;
857 
858 static void
859 gnucash_sheet_insert_cb (GtkWidget *widget,
860  const gchar *insert_text,
861  const gint insert_text_len,
862  gint *position,
863  GnucashSheet *sheet)
864 {
865  GtkEditable *editable;
866  Table *table = sheet->table;
867  VirtualLocation virt_loc;
868 
869  char *change_text;
870  GString *change_text_gs;
871 
872  int new_text_len;
873  int change_text_len;
874 
875  const char *old_text;
876  const char *retval;
877  char *new_text;
878  GString *new_text_gs;
879 
880  int start_sel, end_sel;
881  int old_position;
882  int i;
883  const char *c;
884  gunichar uc;
885 
886  if (sheet->input_cancelled)
887  {
888  g_signal_stop_emission_by_name (G_OBJECT (sheet->entry),
889  "insert_text");
890  return;
891  }
892 
893  if (insert_text_len <= 0)
894  return;
895 
896  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
897 
898  if (!gnc_table_virtual_loc_valid (table, virt_loc, FALSE))
899  return;
900 
901  if (gnc_table_model_read_only (table->model))
902  return;
903 
904  change_text_gs = g_string_new_len (insert_text, insert_text_len);
905 
906  old_text = gtk_entry_get_text (GTK_ENTRY(sheet->entry));
907  if (old_text == NULL)
908  old_text = "";
909 
910  old_position = *position;
911 
912  /* we set new_text_gs to what the entry contents would be if
913  the insert was processed */
914  new_text_gs = g_string_new ("");
915 
916  i = 0;
917  c = old_text;
918  //Copy old text up to insert position
919  while (*c && (i < old_position))
920  {
921  uc = g_utf8_get_char (c);
922  g_string_append_unichar (new_text_gs, uc);
923  c = g_utf8_next_char (c);
924  i++;
925  }
926 
927  //Copy inserted text
928  g_string_append (new_text_gs, change_text_gs->str);
929 
930  //Copy old text after insert position
931  while (*c)
932  {
933  uc = g_utf8_get_char (c);
934  g_string_append_unichar (new_text_gs, uc);
935  c = g_utf8_next_char (c);
936  }
937 
938  new_text = new_text_gs->str;
939  new_text_len = new_text_gs->len;
940 
941  change_text = change_text_gs->str;
942  change_text_len = change_text_gs->len;
943 
944  editable = GTK_EDITABLE (sheet->entry);
945 
946  gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel);
947 
948  retval = gnc_table_modify_update (table, virt_loc,
949  change_text, change_text_len,
950  new_text, new_text_len,
951  position, &start_sel, &end_sel,
952  &sheet->input_cancelled);
953 
954  if (retval &&
955  ((strcmp (retval, new_text) != 0) ||
956  (*position != old_position)))
957  {
958  g_signal_handler_block (G_OBJECT (sheet->entry),
959  sheet->insert_signal);
960  g_signal_handler_block (G_OBJECT (sheet->entry),
961  sheet->delete_signal);
962 
963  gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
964 
965  g_signal_handler_unblock (G_OBJECT (sheet->entry),
966  sheet->delete_signal);
967  g_signal_handler_unblock (G_OBJECT (sheet->entry),
968  sheet->insert_signal);
969 
970  g_signal_stop_emission_by_name (G_OBJECT(sheet->entry),
971  "insert_text");
972  }
973  else if (retval == NULL)
974  {
975  retval = old_text;
976 
977  /* reset IMContext if disallowed chars and clear preedit*/
978  gnucash_sheet_im_context_reset(sheet);
979  /* the entry was disallowed, so we stop the insert signal */
980  g_signal_stop_emission_by_name (G_OBJECT (sheet->entry),
981  "insert_text");
982  }
983 
984  /* sync cursor position and selection to preedit if it exists */
985  if (sheet->preedit_length)
986  {
987  gtk_editable_set_position (editable,
988  sheet->preedit_start_position
989  + sheet->preedit_cursor_position);
990  }
991  else if (*position < 0)
992  *position = g_utf8_strlen(retval, -1);
993 
994  if (start_sel != end_sel)
995  gtk_editable_select_region(editable, start_sel, end_sel);
996 
997  g_string_free (new_text_gs, TRUE);
998  g_string_free (change_text_gs, TRUE);
999 }
1000 
1001 
1002 static void
1003 gnucash_sheet_delete_cb (GtkWidget *widget,
1004  const gint start_pos,
1005  const gint end_pos,
1006  GnucashSheet *sheet)
1007 {
1008  GtkEditable *editable;
1009  Table *table = sheet->table;
1010  VirtualLocation virt_loc;
1011 
1012  int new_text_len;
1013 
1014  const char *old_text;
1015  const char *retval;
1016  char *new_text;
1017  GString *new_text_gs;
1018 
1019  int cursor_position = start_pos;
1020  int start_sel, end_sel;
1021  int i;
1022  const char *c;
1023  gunichar uc;
1024 
1025  if (end_pos <= start_pos)
1026  return;
1027 
1028  gnucash_cursor_get_virt (GNUCASH_CURSOR (sheet->cursor), &virt_loc);
1029 
1030  if (!gnc_table_virtual_loc_valid (table, virt_loc, FALSE))
1031  return;
1032 
1033  if (gnc_table_model_read_only (table->model))
1034  return;
1035 
1036  old_text = gtk_entry_get_text (GTK_ENTRY(sheet->entry));
1037  if (!old_text)
1038  old_text = "";
1039 
1040  new_text_gs = g_string_new ("");
1041  i = 0;
1042  c = old_text;
1043  while (*c && (i < start_pos))
1044  {
1045  uc = g_utf8_get_char (c);
1046  g_string_append_unichar (new_text_gs, uc);
1047  c = g_utf8_next_char (c);
1048  i++;
1049  }
1050 
1051  c = g_utf8_offset_to_pointer (old_text, end_pos);
1052  while (*c)
1053  {
1054  uc = g_utf8_get_char (c);
1055  g_string_append_unichar (new_text_gs, uc);
1056  c = g_utf8_next_char (c);
1057  }
1058 
1059  new_text = new_text_gs->str;
1060  new_text_len = new_text_gs->len;
1061 
1062  editable = GTK_EDITABLE (sheet->entry);
1063 
1064  gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel);
1065 
1066  retval = gnc_table_modify_update (table, virt_loc,
1067  NULL, 0,
1068  new_text, new_text_len,
1069  &cursor_position,
1070  &start_sel, &end_sel,
1071  &sheet->input_cancelled);
1072 
1073  if (retval && (strcmp (retval, new_text) != 0))
1074  {
1075  g_signal_handler_block (G_OBJECT (sheet->entry),
1076  sheet->insert_signal);
1077  g_signal_handler_block (G_OBJECT (sheet->entry),
1078  sheet->delete_signal);
1079 
1080  gtk_entry_set_text (GTK_ENTRY (sheet->entry), retval);
1081 
1082  g_signal_handler_unblock (G_OBJECT (sheet->entry),
1083  sheet->delete_signal);
1084  g_signal_handler_unblock (G_OBJECT (sheet->entry),
1085  sheet->insert_signal);
1086 
1087  g_signal_stop_emission_by_name (G_OBJECT(sheet->entry),
1088  "delete_text");
1089  }
1090  else if (retval == NULL)
1091  {
1092  /* the entry was disallowed, so we stop the delete signal */
1093  g_signal_stop_emission_by_name (G_OBJECT(sheet->entry),
1094  "delete_text");
1095  }
1096 
1097  gtk_editable_set_position (editable, cursor_position);
1098 
1099  if (start_sel != end_sel)
1100  gtk_editable_select_region (editable, start_sel, end_sel);
1101 
1102  g_string_free (new_text_gs, TRUE);
1103 }
1104 
1105 
1106 static void
1107 gnucash_sheet_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
1108 {
1109  GnucashSheet *sheet = GNUCASH_SHEET(widget);
1110 
1111  ENTER("widget=%p, allocation=%p", widget, allocation);
1112 
1113  if (GTK_WIDGET_CLASS(sheet_parent_class)->size_allocate)
1114  (*GTK_WIDGET_CLASS (sheet_parent_class)->size_allocate)
1115  (widget, allocation);
1116 
1117  if (allocation->height == sheet->window_height &&
1118  allocation->width == sheet->window_width)
1119  {
1120  LEAVE("size unchanged");
1121  return;
1122  }
1123 
1124  if (allocation->width != sheet->window_width)
1125  {
1126  gnucash_sheet_styles_set_dimensions (sheet, allocation->width);
1127  gnucash_sheet_recompute_block_offsets (sheet);
1128  }
1129 
1130  sheet->window_height = allocation->height;
1131  sheet->window_width = allocation->width;
1132 
1133  gnucash_cursor_configure (GNUCASH_CURSOR (sheet->cursor));
1134  gnc_header_reconfigure (GNC_HEADER(sheet->header_item));
1135  gnucash_sheet_set_scroll_region (sheet);
1136 
1137  gnc_item_edit_configure (GNC_ITEM_EDIT(sheet->item_editor));
1138  gnucash_sheet_update_adjustments (sheet);
1139 
1140  if (sheet->table)
1141  {
1142  VirtualLocation virt_loc;
1143 
1144  virt_loc = sheet->table->current_cursor_loc;
1145 
1146  if (gnucash_sheet_cell_valid (sheet, virt_loc))
1147  gnucash_sheet_show_row (sheet,
1148  virt_loc.vcell_loc.virt_row);
1149  }
1150 
1151  LEAVE(" ");
1152 }
1153 
1154 static gboolean
1155 gnucash_sheet_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
1156 {
1157  GnucashSheet *sheet = GNUCASH_SHEET(widget);
1158 
1159  if (GTK_WIDGET_CLASS(sheet_parent_class)->focus_in_event)
1160  (*GTK_WIDGET_CLASS (sheet_parent_class)->focus_in_event)
1161  (widget, event);
1162 
1163  gnc_item_edit_focus_in (GNC_ITEM_EDIT(sheet->item_editor));
1164  gtk_im_context_focus_in(sheet->im_context);
1165 
1166 #ifdef G_OS_WIN32
1167  gnucash_sheet_im_context_reset(sheet);
1168 #endif /* G_OS_WIN32 */
1169 
1170  return FALSE;
1171 }
1172 
1173 static gboolean
1174 gnucash_sheet_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
1175 {
1176  GnucashSheet *sheet = GNUCASH_SHEET(widget);
1177 
1178  if (GTK_WIDGET_CLASS(sheet_parent_class)->focus_out_event)
1179  (*GTK_WIDGET_CLASS (sheet_parent_class)->focus_out_event)
1180  (widget, event);
1181 
1182 #ifdef G_OS_WIN32
1183  gnucash_sheet_im_context_reset(sheet);
1184 #endif /* G_OS_WIN32 */
1185 
1186  gtk_im_context_focus_out (sheet->im_context);
1187  gnc_item_edit_focus_out (GNC_ITEM_EDIT(sheet->item_editor));
1188  return FALSE;
1189 }
1190 
1191 static gboolean
1192 gnucash_sheet_check_direct_update_cell(GnucashSheet *sheet,
1193  const VirtualLocation virt_loc)
1194 {
1195  const gchar *type_name;
1196 
1197  type_name = gnc_table_get_cell_type_name (sheet->table, virt_loc);
1198 
1199  if ( (g_strcmp0 (type_name, DATE_CELL_TYPE_NAME) == 0)
1200  || (g_strcmp0 (type_name, COMBO_CELL_TYPE_NAME) == 0)
1201  || (g_strcmp0 (type_name, NUM_CELL_TYPE_NAME) == 0)
1202  || (g_strcmp0 (type_name, PRICE_CELL_TYPE_NAME) == 0)
1203  || (g_strcmp0 (type_name, FORMULA_CELL_TYPE_NAME) == 0)) return TRUE;
1204 
1205  return FALSE;
1206 }
1207 
1208 static void
1209 gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet)
1210 {
1211  const char *text;
1212  VirtualLocation virt_loc;
1213 
1214  g_return_if_fail (sheet != NULL);
1215  g_return_if_fail (GNUCASH_IS_SHEET (sheet));
1216 
1217  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
1218 
1219  text = gnc_table_get_entry (sheet->table, virt_loc);
1220 
1221  gnc_item_edit_configure (GNC_ITEM_EDIT(sheet->item_editor));
1222  gnome_canvas_item_show (GNOME_CANVAS_ITEM (sheet->item_editor));
1223 
1224  gtk_entry_set_text (GTK_ENTRY(sheet->entry), text);
1225 
1226  sheet->editing = TRUE;
1227 
1228  /* set up the signals */
1229  sheet->insert_signal =
1230  g_signal_connect(G_OBJECT(sheet->entry), "insert_text",
1231  G_CALLBACK(gnucash_sheet_insert_cb), sheet);
1232  sheet->delete_signal =
1233  g_signal_connect(G_OBJECT(sheet->entry), "delete_text",
1234  G_CALLBACK(gnucash_sheet_delete_cb), sheet);
1235 
1236  sheet->commit_signal =
1237  g_signal_connect (G_OBJECT (sheet->im_context), "commit",
1238  G_CALLBACK (gnucash_sheet_commit_cb), sheet);
1239  sheet->preedit_changed_signal =
1240  g_signal_connect (G_OBJECT (sheet->im_context), "preedit_changed",
1241  G_CALLBACK (gnucash_sheet_preedit_changed_cb),
1242  sheet);
1243  sheet->retrieve_surrounding_signal =
1244  g_signal_connect (G_OBJECT (sheet->im_context),
1245  "retrieve_surrounding",
1246  G_CALLBACK (gnucash_sheet_retrieve_surrounding_cb),
1247  sheet);
1248  sheet->delete_surrounding_signal =
1249  g_signal_connect (G_OBJECT (sheet->im_context), "delete_surrounding",
1250  G_CALLBACK (gnucash_sheet_delete_surrounding_cb),
1251  sheet);
1252 }
1253 
1254 
1255 static gboolean
1256 gnucash_motion_event (GtkWidget *widget, GdkEventMotion *event)
1257 {
1258  GnucashSheet *sheet;
1259  VirtualLocation virt_loc;
1260 
1261  g_return_val_if_fail(widget != NULL, TRUE);
1262  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1263  g_return_val_if_fail(event != NULL, TRUE);
1264 
1265  sheet = GNUCASH_SHEET(widget);
1266 
1267  if (!(event->state & GDK_BUTTON1_MASK) && sheet->grabbed)
1268  {
1269  gtk_grab_remove (widget);
1270  sheet->grabbed = FALSE;
1271  }
1272 
1273  if (sheet->button != 1)
1274  return FALSE;
1275 
1276  if (!sheet->editing || event->type != GDK_MOTION_NOTIFY)
1277  return FALSE;
1278 
1279  if (!(event->state & GDK_BUTTON1_MASK))
1280  return FALSE;
1281 
1282  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
1283 
1284  gnc_item_edit_set_cursor_pos (GNC_ITEM_EDIT(sheet->item_editor),
1285  virt_loc, event->x, FALSE, TRUE);
1286 
1287  return TRUE;
1288 }
1289 
1290 static gboolean
1291 gnucash_button_release_event (GtkWidget *widget, GdkEventButton *event)
1292 {
1293  GnucashSheet *sheet;
1294 
1295  g_return_val_if_fail(widget != NULL, TRUE);
1296  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1297  g_return_val_if_fail(event != NULL, TRUE);
1298 
1299  sheet = GNUCASH_SHEET (widget);
1300 
1301  if (sheet->button != event->button)
1302  return FALSE;
1303 
1304  sheet->button = 0;
1305 
1306  if (event->button != 1)
1307  return FALSE;
1308 
1309  gtk_grab_remove (widget);
1310  sheet->grabbed = FALSE;
1311 
1312  gnc_item_edit_set_has_selection(GNC_ITEM_EDIT(sheet->item_editor), FALSE);
1313  return TRUE;
1314 }
1315 
1316 static gboolean
1317 gnucash_scroll_event (GtkWidget *widget, GdkEventScroll *event)
1318 {
1319  GnucashSheet *sheet;
1320  GtkAdjustment *vadj;
1321  gfloat v_value;
1322 
1323  g_return_val_if_fail(widget != NULL, TRUE);
1324  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1325  g_return_val_if_fail(event != NULL, TRUE);
1326 
1327  sheet = GNUCASH_SHEET (widget);
1328  vadj = sheet->vadj;
1329  v_value = vadj->value;
1330 
1331  switch (event->direction)
1332  {
1333  case GDK_SCROLL_UP:
1334  v_value -= vadj->step_increment;
1335  break;
1336  case GDK_SCROLL_DOWN:
1337  v_value += vadj->step_increment;
1338  break;
1339  default:
1340  return FALSE;
1341  }
1342 
1343  v_value = CLAMP(v_value, vadj->lower, vadj->upper - vadj->page_size);
1344 
1345  gtk_adjustment_set_value(vadj, v_value);
1346 
1347  return TRUE;
1348 }
1349 
1350 static void
1351 gnucash_sheet_check_grab (GnucashSheet *sheet)
1352 {
1353  GdkModifierType mods;
1354  GdkDevice *device;
1355 
1356  if (!sheet->grabbed)
1357  return;
1358 
1359  device = gdk_device_get_core_pointer ();
1360 
1361  gdk_device_get_state (device, GTK_WIDGET(sheet)->window,
1362  0, &mods);
1363 
1364  if (!(mods & GDK_BUTTON1_MASK))
1365  {
1366  gtk_grab_remove (GTK_WIDGET(sheet));
1367  sheet->grabbed = FALSE;
1368  }
1369 }
1370 
1371 static gboolean
1372 gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event)
1373 {
1374  GnucashSheet *sheet;
1375  GtkEditable *editable;
1376  VirtualCell *vcell;
1377  gboolean changed_cells;
1378 
1379  VirtualLocation cur_virt_loc;
1380  VirtualLocation new_virt_loc;
1381 
1382  Table *table;
1383  gboolean abort_move;
1384  gboolean button_1;
1385  gboolean do_popup;
1386 
1387  g_return_val_if_fail(widget != NULL, TRUE);
1388  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1389  g_return_val_if_fail(event != NULL, TRUE);
1390 
1391  sheet = GNUCASH_SHEET (widget);
1392  table = sheet->table;
1393 
1394  if (sheet->button && (sheet->button != event->button))
1395  return FALSE;
1396 
1397  sheet->button = event->button;
1398  if (sheet->button == 3)
1399  sheet->button = 0;
1400 
1401  if (!gtk_widget_has_focus(widget))
1402  gtk_widget_grab_focus(widget);
1403 
1404  button_1 = FALSE;
1405  do_popup = FALSE;
1406 
1407  switch (event->button)
1408  {
1409  case 1:
1410  button_1 = TRUE;
1411  break;
1412  case 2:
1413  if (event->type != GDK_BUTTON_PRESS)
1414  return FALSE;
1415  gnc_item_edit_paste_selection (GNC_ITEM_EDIT(sheet->item_editor),
1416  GDK_SELECTION_PRIMARY,
1417  event->time);
1418  return TRUE;
1419  case 3:
1420  do_popup = (sheet->popup != NULL);
1421  break;
1422  default:
1423  return FALSE;
1424  }
1425 
1426  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &cur_virt_loc);
1427 
1428  if (!gnucash_grid_find_loc_by_pixel(GNUCASH_GRID(sheet->grid),
1429  event->x, event->y, &new_virt_loc))
1430  return TRUE;
1431 
1432  vcell = gnc_table_get_virtual_cell (table, new_virt_loc.vcell_loc);
1433  if (vcell == NULL)
1434  return TRUE;
1435 
1436  if (virt_loc_equal (new_virt_loc, cur_virt_loc) && button_1 &&
1437  (event->type == GDK_2BUTTON_PRESS))
1438  {
1439  gnc_item_edit_set_cursor_pos (GNC_ITEM_EDIT(sheet->item_editor),
1440  cur_virt_loc, event->x, FALSE, FALSE);
1441 
1442  editable = GTK_EDITABLE(sheet->entry);
1443  gtk_editable_set_position(editable, -1);
1444  gtk_editable_select_region(editable, 0, -1);
1445  return TRUE;
1446  }
1447 
1448  if (event->type != GDK_BUTTON_PRESS)
1449  return FALSE;
1450 
1451  if (button_1)
1452  {
1453  gtk_grab_add(widget);
1454  sheet->grabbed = TRUE;
1455  gnc_item_edit_set_has_selection (GNC_ITEM_EDIT(sheet->item_editor),
1456  TRUE);
1457  }
1458 
1459  if (virt_loc_equal (new_virt_loc, cur_virt_loc) && sheet->editing)
1460  {
1461  gboolean extend_selection = event->state & GDK_SHIFT_MASK;
1462 
1463  gnc_item_edit_set_cursor_pos (GNC_ITEM_EDIT(sheet->item_editor),
1464  cur_virt_loc, event->x, FALSE,
1465  extend_selection);
1466 
1467  if (do_popup)
1468  gtk_menu_popup(GTK_MENU(sheet->popup), NULL, NULL, NULL,
1469  sheet->popup_data, event->button, event->time);
1470 
1471  return button_1 || do_popup;
1472  }
1473 
1474  /* and finally...process this as a POINTER_TRAVERSE */
1475  abort_move = gnc_table_traverse_update (table,
1476  cur_virt_loc,
1477  GNC_TABLE_TRAVERSE_POINTER,
1478  &new_virt_loc);
1479 
1480  if (button_1)
1481  gnucash_sheet_check_grab (sheet);
1482 
1483  if (abort_move)
1484  return TRUE;
1485 
1486  changed_cells = gnucash_sheet_cursor_move (sheet, new_virt_loc);
1487 
1488  if (button_1)
1489  gnucash_sheet_check_grab (sheet);
1490 
1491  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &new_virt_loc);
1492 
1493  gnc_item_edit_set_cursor_pos (GNC_ITEM_EDIT(sheet->item_editor),
1494  new_virt_loc, event->x, changed_cells, FALSE);
1495 
1496  if (do_popup)
1497  gtk_menu_popup(GTK_MENU(sheet->popup), NULL, NULL, NULL,
1498  sheet->popup_data, event->button, event->time);
1499  return button_1 || do_popup;
1500 }
1501 
1502 gboolean
1503 gnucash_register_has_selection (GnucashRegister *reg)
1504 {
1505  GnucashSheet *sheet;
1506  GncItemEdit *item_edit;
1507 
1508  g_return_val_if_fail((reg != NULL), FALSE);
1509  g_return_val_if_fail(GNUCASH_IS_REGISTER(reg), FALSE);
1510 
1511  sheet = GNUCASH_SHEET(reg->sheet);
1512  item_edit = GNC_ITEM_EDIT(sheet->item_editor);
1513 
1514  return gnc_item_edit_get_has_selection(item_edit);
1515 }
1516 
1517 void
1518 gnucash_register_cut_clipboard (GnucashRegister *reg)
1519 {
1520  GnucashSheet *sheet;
1521  GncItemEdit *item_edit;
1522 
1523  g_return_if_fail(reg != NULL);
1524  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
1525 
1526  sheet = GNUCASH_SHEET(reg->sheet);
1527  item_edit = GNC_ITEM_EDIT(sheet->item_editor);
1528 
1529  gnc_item_edit_cut_clipboard(item_edit, GDK_CURRENT_TIME);
1530 }
1531 
1532 void
1533 gnucash_register_copy_clipboard (GnucashRegister *reg)
1534 {
1535  GnucashSheet *sheet;
1536  GncItemEdit *item_edit;
1537 
1538  g_return_if_fail(reg != NULL);
1539  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
1540 
1541  sheet = GNUCASH_SHEET(reg->sheet);
1542  item_edit = GNC_ITEM_EDIT(sheet->item_editor);
1543 
1544  gnc_item_edit_copy_clipboard(item_edit, GDK_CURRENT_TIME);
1545 }
1546 
1547 void
1548 gnucash_register_paste_clipboard (GnucashRegister *reg)
1549 {
1550  GnucashSheet *sheet;
1551  GncItemEdit *item_edit;
1552 
1553  g_return_if_fail(reg != NULL);
1554  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
1555 
1556  sheet = GNUCASH_SHEET(reg->sheet);
1557  item_edit = GNC_ITEM_EDIT(sheet->item_editor);
1558 
1559  gnc_item_edit_paste_selection (item_edit, GDK_SELECTION_CLIPBOARD,
1560  GDK_CURRENT_TIME);
1561 }
1562 
1563 static void
1564 gnucash_sheet_refresh_from_prefs (GnucashSheet *sheet)
1565 {
1566  g_return_if_fail(sheet != NULL);
1567  g_return_if_fail(GNUCASH_IS_SHEET(sheet));
1568 
1569  sheet->use_theme_colors = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
1570  GNC_PREF_USE_THEME_COLORS);
1571  sheet->use_horizontal_lines = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
1572  GNC_PREF_DRAW_HOR_LINES);
1573  sheet->use_vertical_lines = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
1574  GNC_PREF_DRAW_VERT_LINES);
1575 }
1576 
1577 void
1578 gnucash_register_refresh_from_prefs (GnucashRegister *reg)
1579 {
1580  GnucashSheet *sheet;
1581 
1582  g_return_if_fail(reg != NULL);
1583  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
1584 
1585  sheet = GNUCASH_SHEET(reg->sheet);
1586  gnucash_sheet_refresh_from_prefs(sheet);
1587 }
1588 
1589 static gboolean
1590 gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event)
1591 {
1592  GncItemEdit *item_edit;
1593  gboolean handled = FALSE;
1594  guint32 time;
1595 
1596  item_edit = GNC_ITEM_EDIT(sheet->item_editor);
1597  time = event->time;
1598 
1599  switch (event->keyval)
1600  {
1601  case GDK_KEY_C:
1602  case GDK_KEY_c:
1603  if (event->state & GDK_CONTROL_MASK)
1604  {
1605  gnc_item_edit_copy_clipboard(item_edit, time);
1606  handled = TRUE;
1607  }
1608  break;
1609  case GDK_KEY_X:
1610  case GDK_KEY_x:
1611  if (event->state & GDK_CONTROL_MASK)
1612  {
1613  gnc_item_edit_cut_clipboard(item_edit, time);
1614  handled = TRUE;
1615  }
1616  break;
1617  case GDK_KEY_V:
1618  case GDK_KEY_v:
1619  if (event->state & GDK_CONTROL_MASK)
1620  {
1621  gnc_item_edit_paste_selection (item_edit, GDK_SELECTION_CLIPBOARD,
1622  time);
1623  handled = TRUE;
1624  }
1625  break;
1626  case GDK_KEY_Insert:
1627  if (event->state & GDK_SHIFT_MASK)
1628  {
1629  gnc_item_edit_paste_selection (item_edit, GDK_SELECTION_CLIPBOARD,
1630  time);
1631  handled = TRUE;
1632  }
1633  else if (event->state & GDK_CONTROL_MASK)
1634  {
1635  gnc_item_edit_copy_clipboard(item_edit, time);
1636  handled = TRUE;
1637  }
1638  break;
1639  }
1640 
1641  return handled;
1642 }
1643 
1644 static gboolean
1645 gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event)
1646 {
1647  GtkEditable *editable;
1648  Table *table = sheet->table;
1649  VirtualLocation virt_loc;
1650  gboolean changed;
1651  gboolean result;
1652 
1653  char *new_text = NULL;
1654 
1655  int cursor_position, start_sel, end_sel;
1656  int new_position, new_start, new_end;
1657 
1658  gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor), &virt_loc);
1659 
1660  if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE))
1661  return FALSE;
1662 
1663  if (gnc_table_model_read_only (table->model))
1664  return FALSE;
1665 
1666  editable = GTK_EDITABLE(sheet->entry);
1667 
1668  cursor_position = gtk_editable_get_position (editable);
1669  gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel);
1670 
1671  new_position = cursor_position;
1672  new_start = start_sel;
1673  new_end = end_sel;
1674 
1675  result = gnc_table_direct_update (table, virt_loc,
1676  &new_text,
1677  &new_position,
1678  &new_start, &new_end,
1679  event);
1680 
1681  changed = FALSE;
1682 
1683  if (new_text != NULL)
1684  {
1685  g_signal_handler_block (G_OBJECT (sheet->entry),
1686  sheet->insert_signal);
1687  g_signal_handler_block (G_OBJECT (sheet->entry),
1688  sheet->delete_signal);
1689 
1690  gtk_entry_set_text (GTK_ENTRY (sheet->entry), new_text);
1691 
1692  g_signal_handler_unblock (G_OBJECT (sheet->entry),
1693  sheet->delete_signal);
1694  g_signal_handler_unblock (G_OBJECT (sheet->entry),
1695  sheet->insert_signal);
1696 
1697  changed = TRUE;
1698  }
1699 
1700  if (new_position != cursor_position)
1701  {
1702  gtk_editable_set_position (editable, new_position);
1703  changed = TRUE;
1704  }
1705 
1706  if ((new_start != start_sel) || (new_end != end_sel))
1707  {
1708  gtk_editable_select_region(editable, new_start, new_end);
1709  changed = TRUE;
1710  }
1711 
1712  if (changed)
1713  gnc_item_edit_redraw(GNC_ITEM_EDIT(sheet->item_editor));
1714 
1715  return result;
1716 }
1717 
1718 static gint
1719 gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event)
1720 {
1721  Table *table;
1722  GnucashSheet *sheet;
1723  gboolean pass_on = FALSE;
1724  gboolean abort_move;
1725  VirtualLocation cur_virt_loc;
1726  VirtualLocation new_virt_loc;
1727  gncTableTraversalDir direction = 0;
1728  int distance;
1729 
1730  g_return_val_if_fail(widget != NULL, TRUE);
1731  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1732  g_return_val_if_fail(event != NULL, TRUE);
1733 
1734  sheet = GNUCASH_SHEET (widget);
1735  table = sheet->table;
1736 
1737  if (gnucash_sheet_direct_event(sheet, (GdkEvent *) event))
1738  return TRUE;
1739 
1740  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &cur_virt_loc);
1741  new_virt_loc = cur_virt_loc;
1742 
1743  /* Don't process any keystrokes where a modifier key (Alt,
1744  * Meta, etc.) is being held down. This should't include
1745  * MOD2, aka NUM LOCK. */
1746  if (event->state & (GDK_MOD1_MASK | GDK_MOD3_MASK |
1747  GDK_MOD4_MASK | GDK_MOD5_MASK))
1748  pass_on = TRUE;
1749 
1750  /* Calculate tentative physical values */
1751  if (!pass_on)
1752  {
1753  switch (event->keyval)
1754  {
1755  case GDK_KEY_Return:
1756  case GDK_KEY_KP_Enter:
1757  g_signal_emit_by_name(sheet->reg, "activate_cursor");
1758  return TRUE;
1759  break;
1760  case GDK_KEY_Tab:
1761  case GDK_KEY_ISO_Left_Tab:
1762  if (event->state & GDK_SHIFT_MASK)
1763  {
1764  direction = GNC_TABLE_TRAVERSE_LEFT;
1765  gnc_table_move_tab (table, &new_virt_loc,
1766  FALSE);
1767  }
1768  else
1769  {
1770  direction = GNC_TABLE_TRAVERSE_RIGHT;
1771  gnc_table_move_tab (table, &new_virt_loc,
1772  TRUE);
1773  }
1774  break;
1775  case GDK_KEY_KP_Page_Up:
1776  case GDK_KEY_Page_Up:
1777  direction = GNC_TABLE_TRAVERSE_UP;
1778  new_virt_loc.phys_col_offset = 0;
1779  if (event->state & GDK_SHIFT_MASK)
1780  new_virt_loc.vcell_loc.virt_row = 1;
1781  else
1782  {
1783  distance = sheet->num_visible_phys_rows - 1;
1785  (table, &new_virt_loc, -distance);
1786  }
1787  break;
1788  case GDK_KEY_KP_Page_Down:
1789  case GDK_KEY_Page_Down:
1790  direction = GNC_TABLE_TRAVERSE_DOWN;
1791  new_virt_loc.phys_col_offset = 0;
1792  if (event->state & GDK_SHIFT_MASK)
1793  new_virt_loc.vcell_loc.virt_row =
1794  sheet->num_virt_rows - 1;
1795  else
1796  {
1797  distance = sheet->num_visible_phys_rows - 1;
1799  (table, &new_virt_loc, distance);
1800  }
1801  break;
1802  case GDK_KEY_KP_Up:
1803  case GDK_KEY_Up:
1804  direction = GNC_TABLE_TRAVERSE_UP;
1806  &new_virt_loc, -1);
1807  break;
1808  case GDK_KEY_KP_Down:
1809  case GDK_KEY_Down:
1810  case GDK_KEY_Menu:
1811  if (event->keyval == GDK_Menu ||
1812  (event->state & GDK_CONTROL_MASK))
1813  {
1814  GncItemEdit *item_edit;
1815 
1816  item_edit = GNC_ITEM_EDIT (sheet->item_editor);
1817 
1818  if (gnc_table_confirm_change (table,
1819  cur_virt_loc))
1820  gnc_item_edit_show_popup (item_edit);
1821 
1822  return TRUE;
1823  }
1824 
1825  direction = GNC_TABLE_TRAVERSE_DOWN;
1827  &new_virt_loc, 1);
1828  break;
1829  case GDK_KEY_Control_L:
1830  case GDK_KEY_Control_R:
1831  case GDK_KEY_Shift_L:
1832  case GDK_KEY_Shift_R:
1833  case GDK_KEY_Alt_L:
1834  case GDK_KEY_Alt_R:
1835  pass_on = TRUE;
1836  break;
1837  default:
1838  if (gnucash_sheet_clipboard_event(sheet, event))
1839  return TRUE;
1840 
1841  pass_on = TRUE;
1842  break;
1843  }
1844  }
1845 
1846  /* Forward the keystroke to the input line */
1847  if (pass_on)
1848  {
1849  GValue gval = {0,};
1850  gboolean result;
1851  g_value_init (&gval, G_TYPE_BOOLEAN);
1852  g_value_set_boolean (&gval, TRUE);
1853  g_object_set_property (G_OBJECT (sheet->entry), "editable", &gval);
1854  result = gtk_widget_event (sheet->entry, (GdkEvent *) event);
1855  g_value_set_boolean (&gval, FALSE);
1856  g_object_set_property (G_OBJECT (sheet->entry), "editable", &gval);
1857  return result;
1858  }
1859 
1860  abort_move = gnc_table_traverse_update (table, cur_virt_loc,
1861  direction, &new_virt_loc);
1862 
1863  /* If that would leave the register, abort */
1864  if (abort_move)
1865  return TRUE;
1866 
1867  gnucash_sheet_cursor_move (sheet, new_virt_loc);
1868 
1869  /* return true because we handled the key press */
1870  return TRUE;
1871 }
1872 
1873 static gint
1874 gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event)
1875 {
1876  GnucashSheet *sheet;
1877 
1878  g_return_val_if_fail(widget != NULL, TRUE);
1879  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1880  g_return_val_if_fail(event != NULL, TRUE);
1881 
1882  sheet = GNUCASH_SHEET (widget);
1883 
1884  /* bug#60582 comment#27 2
1885  save shift state to enable <shift minus> and <shift equal>
1886  bug#618434
1887  save keyval to handle GDK_KP_Decimal event
1888  */
1889 #ifdef G_OS_WIN32
1890  /* gdk never sends GDK_KP_Decimal on win32. See #486658 */
1891  if (event->hardware_keycode == VK_DECIMAL)
1892  event->keyval = GDK_KP_Decimal;
1893 #endif
1894  if (sheet->preedit_length)
1895  {
1896  sheet->shift_state = 0;
1897  sheet->keyval_state = 0;
1898  }
1899  else
1900  {
1901  sheet->shift_state = event->state & GDK_SHIFT_MASK;
1902  sheet->keyval_state = (event->keyval == GDK_KEY_KP_Decimal) ? GDK_KEY_KP_Decimal : 0;
1903  }
1904  if (gtk_im_context_filter_keypress (sheet->im_context, event))
1905  {
1906  sheet->need_im_reset = TRUE;
1907  return TRUE;
1908  }
1909 
1910  return gnucash_sheet_key_press_event_internal (widget, event);
1911 }
1912 
1913 static gint
1914 gnucash_sheet_key_release_event(GtkWidget *widget, GdkEventKey *event)
1915 {
1916  GnucashSheet *sheet;
1917 
1918  g_return_val_if_fail(widget != NULL, TRUE);
1919  g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE);
1920  g_return_val_if_fail(event != NULL, TRUE);
1921 
1922  sheet = GNUCASH_SHEET (widget);
1923 
1924  if (gtk_im_context_filter_keypress (sheet->im_context, event))
1925  {
1926  sheet->need_im_reset = TRUE;
1927  return TRUE;
1928  }
1929 
1930  return FALSE;
1931 }
1932 
1933 static void
1934 gnucash_sheet_im_context_reset_flags(GnucashSheet *sheet)
1935 {
1936  sheet->preedit_length = 0;
1937  sheet->preedit_char_length = 0;
1938  sheet->preedit_start_position = -1;
1939  sheet->preedit_cursor_position = 0;
1940  sheet->preedit_selection_length = 0;
1941 }
1942 
1943 static void
1944 gnucash_sheet_im_context_reset(GnucashSheet *sheet)
1945 {
1946  if (sheet->need_im_reset)
1947  {
1948  if (sheet->preedit_attrs)
1949  {
1950  pango_attr_list_unref (sheet->preedit_attrs);
1951  sheet->preedit_attrs = NULL;
1952  }
1953  gtk_im_context_reset (sheet->im_context);
1954  sheet->need_im_reset = FALSE;
1955  }
1956  gnucash_sheet_im_context_reset_flags(sheet);
1957 }
1958 
1959 static void
1960 gnucash_sheet_commit_cb (GtkIMContext *context, const gchar *str,
1961  GnucashSheet *sheet)
1962 {
1963  GtkEditable *editable;
1964  gint tmp_pos, sel_start, sel_end;
1965 
1966  g_return_if_fail(strlen(str) > 0);
1967  g_return_if_fail(sheet->editing == TRUE);
1968 
1969  editable = GTK_EDITABLE (sheet->entry);
1970 
1971  if (strlen(str) == 1 && sheet->direct_update_cell)
1972  {
1973  /* Reconstruct keyevent and direct update */
1974  GdkEvent *event;
1975  GdkEventKey *keyevent;
1976  gboolean result;
1977 
1978  event = gdk_event_new (GDK_KEY_PRESS);
1979  keyevent = (GdkEventKey *) event;
1980  keyevent->keyval =
1981  sheet->keyval_state ? sheet->keyval_state
1982  : gdk_unicode_to_keyval(str[0]);
1983  keyevent->state |= sheet->shift_state;
1984  result = gnucash_sheet_direct_event(sheet, event);
1985  gdk_event_free(event);
1986 
1987  if (result)
1988  {
1989  gnucash_sheet_im_context_reset_flags(sheet);
1990  return;
1991  }
1992  }
1993 
1994  /* delete preedit string from editable*/
1995  if (sheet->preedit_length)
1996  {
1997  g_signal_handler_block (G_OBJECT (sheet->entry),
1998  sheet->delete_signal);
1999  gtk_editable_delete_text (editable, sheet->preedit_start_position,
2000  sheet->preedit_start_position
2001  + sheet->preedit_char_length);
2002  g_signal_handler_unblock (G_OBJECT (sheet->entry),
2003  sheet->delete_signal);
2004  }
2005 
2006  if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
2007  {
2008  if (sel_start != sel_end)
2009  {
2010  sheet->preedit_selection_length = 0;
2011  gtk_editable_delete_selection (editable);
2012  }
2013  }
2014 
2015  tmp_pos = (sheet->preedit_start_position == -1) ?
2016  gtk_editable_get_position (editable)
2017  : sheet->preedit_start_position;
2018  gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
2019 
2020  /* insert_cb may have changed the selection, but gtk_editable_set_position
2021  (erroneously?) clears it. If a selection is set, preserve it. */
2022  gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
2023  gtk_editable_set_position (editable, tmp_pos);
2024  if (sel_start != sel_end)
2025  gtk_editable_select_region (editable, sel_start, sel_end);
2026 
2027  gnucash_sheet_im_context_reset_flags(sheet);
2028 }
2029 
2030 static void
2031 gnucash_sheet_preedit_changed_cb (GtkIMContext *context, GnucashSheet *sheet)
2032 {
2033  gchar *preedit_string;
2034  GtkEditable *editable;
2035 
2036  g_return_if_fail(context != NULL);
2037  g_return_if_fail(sheet->editing == TRUE);
2038 
2039  editable = GTK_EDITABLE (sheet->entry);
2040 
2041  /* save preedit start position and selection */
2042  if (sheet->preedit_length == 0)
2043  {
2044  int start_pos, end_pos;
2045  if ( gtk_editable_get_selection_bounds (editable, &start_pos, &end_pos))
2046  {
2047  sheet->preedit_start_position = start_pos;
2048  sheet->preedit_selection_length = end_pos - start_pos;
2049  }
2050  else
2051  {
2052  sheet->preedit_start_position =
2053  gtk_editable_get_position (editable);
2054  }
2055  }
2056 #ifdef G_OS_WIN32
2057  else /* sheet->preedit_length != 0 */
2058  {
2059  /* On Windows, gtk_im_context_reset() in gnucash_sheet_key_press_event()
2060  * always returns FALSE because Windows IME handles key press at the
2061  * top level window. So sheet->need_im_reset = TRUE here. */
2062  sheet->need_im_reset = TRUE;
2063  }
2064 #endif /* G_OS_WIN32 */
2065 
2066  if (sheet->preedit_attrs)
2067  pango_attr_list_unref (sheet->preedit_attrs);
2068 
2069  gtk_im_context_get_preedit_string (sheet->im_context, &preedit_string,
2070  &sheet->preedit_attrs, &(sheet->preedit_cursor_position));
2071 
2072  if (sheet->preedit_length)
2073  {
2074  g_signal_handler_block (G_OBJECT (sheet->entry),
2075  sheet->delete_signal);
2076  gtk_editable_delete_text (editable, sheet->preedit_start_position,
2077  sheet->preedit_start_position
2078  + sheet->preedit_char_length);
2079  g_signal_handler_unblock (G_OBJECT (sheet->entry),
2080  sheet->delete_signal);
2081  }
2082 
2083  sheet->preedit_length = strlen (preedit_string);
2084  sheet->preedit_char_length = g_utf8_strlen(preedit_string, -1);
2085 
2086  if (sheet->preedit_length)
2087  {
2088  int tmp_pos = sheet->preedit_start_position;
2089  g_signal_handler_block (G_OBJECT (sheet->entry),
2090  sheet->insert_signal);
2091  gtk_editable_insert_text (editable, preedit_string, sheet->preedit_length,
2092  &tmp_pos);
2093  g_signal_handler_unblock (G_OBJECT (sheet->entry),
2094  sheet->insert_signal);
2095 
2096  gtk_editable_set_position (editable, sheet->preedit_start_position
2097  + sheet->preedit_cursor_position);
2098 
2099  if ( sheet->preedit_selection_length != 0)
2100  {
2101  gtk_editable_select_region (editable,
2102  sheet->preedit_start_position
2103  + sheet->preedit_char_length,
2104  sheet->preedit_start_position
2105  + sheet->preedit_char_length
2106  + sheet->preedit_selection_length);
2107  }
2108  }
2109  else
2110  {
2111  gnucash_sheet_im_context_reset_flags(sheet);
2112  }
2113 
2114  g_free (preedit_string);
2115 }
2116 
2117 static gboolean
2118 gnucash_sheet_retrieve_surrounding_cb (GtkIMContext *context, GnucashSheet *sheet)
2119 {
2120  GtkEditable *editable;
2121  gchar *surrounding;
2122  gint cur_pos;
2123 
2124  editable = GTK_EDITABLE (sheet->entry);
2125  surrounding = gtk_editable_get_chars (editable, 0, -1);
2126  cur_pos = gtk_editable_get_position (editable);
2127 
2128  gtk_im_context_set_surrounding (context,
2129  surrounding, strlen (surrounding),
2130  g_utf8_offset_to_pointer (surrounding, cur_pos) - surrounding);
2131  g_free (surrounding);
2132  return TRUE;
2133 }
2134 
2135 static gboolean
2136 gnucash_sheet_delete_surrounding_cb (GtkIMContext *context, gint offset,
2137  gint n_chars, GnucashSheet *sheet)
2138 {
2139  GtkEditable *editable;
2140  gint cur_pos;
2141 
2142  editable = GTK_EDITABLE (sheet->entry);
2143  cur_pos = gtk_editable_get_position (editable);
2144 
2145  gtk_editable_delete_text (editable,
2146  cur_pos + offset,
2147  cur_pos + offset + n_chars);
2148  return TRUE;
2149 }
2150 
2151 
2152 static void
2153 gnucash_sheet_goto_virt_loc (GnucashSheet *sheet, VirtualLocation virt_loc)
2154 {
2155  Table *table;
2156  gboolean abort_move;
2157  VirtualLocation cur_virt_loc;
2158 
2159  g_return_if_fail(GNUCASH_IS_SHEET(sheet));
2160 
2161  table = sheet->table;
2162 
2163  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &cur_virt_loc);
2164 
2165  /* It's not really a pointer traverse, but it seems the most
2166  * appropriate here. */
2167  abort_move = gnc_table_traverse_update (table, cur_virt_loc,
2168  GNC_TABLE_TRAVERSE_POINTER,
2169  &virt_loc);
2170 
2171  if (abort_move)
2172  return;
2173 
2174  gnucash_sheet_cursor_move (sheet, virt_loc);
2175 }
2176 
2177 
2178 void
2179 gnucash_register_goto_virt_cell (GnucashRegister *reg,
2180  VirtualCellLocation vcell_loc)
2181 {
2182  GnucashSheet *sheet;
2183  VirtualLocation virt_loc;
2184 
2185  g_return_if_fail(reg != NULL);
2186  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
2187 
2188  sheet = GNUCASH_SHEET(reg->sheet);
2189 
2190  virt_loc.vcell_loc = vcell_loc;
2191  virt_loc.phys_row_offset = 0;
2192  virt_loc.phys_col_offset = 0;
2193 
2194  gnucash_sheet_goto_virt_loc(sheet, virt_loc);
2195 }
2196 
2197 void
2198 gnucash_register_goto_virt_loc (GnucashRegister *reg,
2199  VirtualLocation virt_loc)
2200 {
2201  GnucashSheet *sheet;
2202 
2203  g_return_if_fail(reg != NULL);
2204  g_return_if_fail(GNUCASH_IS_REGISTER(reg));
2205 
2206  sheet = GNUCASH_SHEET(reg->sheet);
2207 
2208  gnucash_sheet_goto_virt_loc(sheet, virt_loc);
2209 }
2210 
2211 void
2212 gnucash_register_goto_next_virt_row (GnucashRegister *reg)
2213 {
2214  GnucashSheet *sheet;
2215  VirtualLocation virt_loc;
2216  int start_virt_row;
2217 
2218  g_return_if_fail (reg != NULL);
2219  g_return_if_fail (GNUCASH_IS_REGISTER(reg));
2220 
2221  sheet = GNUCASH_SHEET(reg->sheet);
2222 
2223  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
2224 
2225  /* Move down one physical row at a time until we
2226  * reach the next visible virtual cell. */
2227  start_virt_row = virt_loc.vcell_loc.virt_row;
2228  do
2229  {
2230  if (!gnc_table_move_vertical_position (sheet->table, &virt_loc, 1))
2231  return;
2232  }
2233  while (start_virt_row == virt_loc.vcell_loc.virt_row);
2234 
2235  if (virt_loc.vcell_loc.virt_row >= sheet->num_virt_rows)
2236  return;
2237 
2238  virt_loc.phys_row_offset = 0;
2239  virt_loc.phys_col_offset = 0;
2240 
2241  gnucash_sheet_goto_virt_loc (sheet, virt_loc);
2242 }
2243 
2244 void
2245 gnucash_register_goto_next_matching_row (GnucashRegister *reg,
2246  VirtualLocationMatchFunc match,
2247  gpointer user_data)
2248 {
2249  GnucashSheet *sheet;
2250  SheetBlockStyle *style;
2251  VirtualLocation virt_loc;
2252 
2253  g_return_if_fail (reg != NULL);
2254  g_return_if_fail (GNUCASH_IS_REGISTER(reg));
2255  g_return_if_fail (match != NULL);
2256 
2257  sheet = GNUCASH_SHEET (reg->sheet);
2258 
2259  gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);
2260 
2261  do
2262  {
2263  if (!gnc_table_move_vertical_position (sheet->table,
2264  &virt_loc, 1))
2265  return;
2266 
2267  if (virt_loc.vcell_loc.virt_row >= sheet->num_virt_rows)
2268  return;
2269 
2270  style = gnucash_sheet_get_style (sheet, virt_loc.vcell_loc);
2271  if (!style || !style->cursor)
2272  return;
2273  }
2274  while (!match (virt_loc, user_data));
2275 
2276  virt_loc.phys_row_offset = 0;
2277  virt_loc.phys_col_offset = 0;
2278 
2279  gnucash_sheet_goto_virt_loc (sheet, virt_loc);
2280 }
2281 
2282 SheetBlock *
2283 gnucash_sheet_get_block (GnucashSheet *sheet, VirtualCellLocation vcell_loc)
2284 {
2285  g_return_val_if_fail (sheet != NULL, NULL);
2286  g_return_val_if_fail (GNUCASH_IS_SHEET(sheet), NULL);
2287 
2288  return g_table_index (sheet->blocks,
2289  vcell_loc.virt_row,
2290  vcell_loc.virt_col);
2291 }
2292 
2293 GncItemEdit *gnucash_sheet_get_item_edit (GnucashSheet *sheet)
2294 {
2295  g_return_val_if_fail (sheet != NULL, NULL);
2296  g_return_val_if_fail (GNUCASH_IS_SHEET(sheet), NULL);
2297 
2298  if (sheet->item_editor == NULL)
2299  return NULL;
2300  else
2301  return GNC_ITEM_EDIT (sheet->item_editor);
2302 }
2303 
2304 
2305 void gnucash_sheet_set_window (GnucashSheet *sheet, GtkWidget *window)
2306 {
2307  g_return_if_fail (sheet != NULL);
2308  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
2309 
2310  if (window)
2311  g_return_if_fail (GTK_IS_WIDGET(window));
2312 
2313  sheet->window = window;
2314 }
2315 
2316 
2317 /* This fills up a block from the table; it sets the style and returns
2318  * true if the style changed. */
2319 gboolean
2320 gnucash_sheet_block_set_from_table (GnucashSheet *sheet,
2321  VirtualCellLocation vcell_loc)
2322 {
2323  Table *table;
2324  SheetBlock *block;
2325  SheetBlockStyle *style;
2326  VirtualCell *vcell;
2327 
2328  block = gnucash_sheet_get_block (sheet, vcell_loc);
2329  style = gnucash_sheet_get_style_from_table (sheet, vcell_loc);
2330 
2331  if (block == NULL)
2332  return FALSE;
2333 
2334  table = sheet->table;
2335 
2336  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
2337 
2338  if (block->style && (block->style != style))
2339  {
2340  gnucash_style_unref (block->style);
2341  block->style = NULL;
2342  }
2343 
2344  block->visible = (vcell) ? vcell->visible : TRUE;
2345 
2346  if (block->style == NULL)
2347  {
2348  block->style = style;
2349  gnucash_style_ref(block->style);
2350  return TRUE;
2351  }
2352 
2353  return FALSE;
2354 }
2355 
2356 
2357 gint
2358 gnucash_sheet_col_max_width (GnucashSheet *sheet, gint virt_col, gint cell_col)
2359 {
2360  int virt_row;
2361  int cell_row;
2362  int max = 0;
2363  int width;
2364  SheetBlock *block;
2365  SheetBlockStyle *style;
2366  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), "");
2367 
2368  g_return_val_if_fail (virt_col >= 0, 0);
2369  g_return_val_if_fail (virt_col < sheet->num_virt_cols, 0);
2370  g_return_val_if_fail (cell_col >= 0, 0);
2371 
2372  for (virt_row = 0; virt_row < sheet->num_virt_rows ; virt_row++)
2373  {
2374  VirtualCellLocation vcell_loc = { virt_row, virt_col };
2375 
2376  block = gnucash_sheet_get_block (sheet, vcell_loc);
2377  style = block->style;
2378 
2379  if (!style)
2380  continue;
2381 
2382  if (cell_col < style->ncols)
2383  for (cell_row = 0; cell_row < style->nrows; cell_row++)
2384  {
2385  VirtualLocation virt_loc;
2386  const char *text;
2387 
2388  virt_loc.vcell_loc = vcell_loc;
2389  virt_loc.phys_row_offset = cell_row;
2390  virt_loc.phys_col_offset = cell_col;
2391 
2392  if (virt_row == 0)
2393  {
2394  text = gnc_table_get_label
2395  (sheet->table, virt_loc);
2396  }
2397  else
2398  {
2399  text = gnc_table_get_entry
2400  (sheet->table, virt_loc);
2401  }
2402 
2403  pango_layout_set_text (layout, text, strlen (text));
2404  pango_layout_get_pixel_size (layout, &width, NULL);
2405 
2406  width += 2 * CELL_HPADDING;
2407 
2408  max = MAX (max, width);
2409  }
2410  }
2411 
2412  g_object_unref (layout);
2413 
2414  return max;
2415 }
2416 
2417 void
2418 gnucash_sheet_set_scroll_region (GnucashSheet *sheet)
2419 {
2420  int height, width;
2421  GtkWidget *widget;
2422  double x, y;
2423 
2424  if (!sheet)
2425  return;
2426 
2427  widget = GTK_WIDGET(sheet);
2428 
2429  if (!sheet->header_item || !GNC_HEADER(sheet->header_item)->style)
2430  return;
2431 
2432  gnome_canvas_get_scroll_region (GNOME_CANVAS(sheet),
2433  NULL, NULL, &x, &y);
2434 
2435  height = MAX (sheet->height, widget->allocation.height);
2436  width = MAX (sheet->width, widget->allocation.width);
2437 
2438  if (width != (int)x || height != (int)y)
2439  gnome_canvas_set_scroll_region (GNOME_CANVAS(sheet),
2440  0, 0, width, height);
2441 }
2442 
2443 static void
2444 gnucash_sheet_block_destroy (gpointer _block, gpointer user_data)
2445 {
2446  SheetBlock *block = _block;
2447 
2448  if (block == NULL)
2449  return;
2450 
2451  if (block->style)
2452  {
2453  gnucash_style_unref (block->style);
2454  block->style = NULL;
2455  }
2456 }
2457 
2458 static void
2459 gnucash_sheet_block_construct (gpointer _block, gpointer user_data)
2460 {
2461  SheetBlock *block = _block;
2462 
2463  block->style = NULL;
2464  block->visible = TRUE;
2465 }
2466 
2467 static void
2468 gnucash_sheet_resize (GnucashSheet *sheet)
2469 {
2470  g_return_if_fail (sheet != NULL);
2471  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
2472 
2473  if (sheet->table->num_virt_cols > 1)
2474  g_warning ("num_virt_cols > 1");
2475 
2476  sheet->num_virt_cols = 1;
2477 
2478  g_table_resize (sheet->blocks, sheet->table->num_virt_rows, 1);
2479 
2480  sheet->num_virt_rows = sheet->table->num_virt_rows;
2481 }
2482 
2483 void
2484 gnucash_sheet_recompute_block_offsets (GnucashSheet *sheet)
2485 {
2486  Table *table;
2487  SheetBlock *block;
2488  gint i, j;
2489  gint height;
2490  gint width;
2491 
2492  g_return_if_fail (sheet != NULL);
2493  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
2494  g_return_if_fail (sheet->table != NULL);
2495 
2496  table = sheet->table;
2497 
2498  height = 0;
2499  block = NULL;
2500  for (i = 0; i < table->num_virt_rows; i++)
2501  {
2502  width = 0;
2503 
2504  for (j = 0; j < table->num_virt_cols; j++)
2505  {
2506  VirtualCellLocation vcell_loc = { i, j };
2507 
2508  block = gnucash_sheet_get_block (sheet, vcell_loc);
2509 
2510  block->origin_x = width;
2511  block->origin_y = height;
2512 
2513  if (block->visible)
2514  width += block->style->dimensions->width;
2515  }
2516 
2517  if (i > 0 && block->visible)
2518  height += block->style->dimensions->height;
2519  }
2520 
2521  sheet->height = height;
2522 }
2523 
2524 void
2525 gnucash_sheet_table_load (GnucashSheet *sheet, gboolean do_scroll)
2526 {
2527  Table *table;
2528  gint num_header_phys_rows;
2529  gint i, j;
2530 
2531  g_return_if_fail (sheet != NULL);
2532  g_return_if_fail (GNUCASH_IS_SHEET(sheet));
2533  g_return_if_fail (sheet->table != NULL);
2534 
2535  table = sheet->table;
2536 
2537  gnucash_sheet_stop_editing (sheet);
2538 
2539  gnucash_sheet_resize (sheet);
2540 
2541  num_header_phys_rows = 0;
2542 
2543  /* fill it up */
2544  for (i = 0; i < table->num_virt_rows; i++)
2545  for (j = 0; j < table->num_virt_cols; j++)
2546  {
2547  VirtualCellLocation vcell_loc = { i, j };
2548  VirtualCell *vcell;
2549 
2550  gnucash_sheet_block_set_from_table (sheet, vcell_loc);
2551 
2552  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
2553 
2554  num_header_phys_rows =
2555  MAX (num_header_phys_rows,
2556  vcell->cellblock->num_rows);
2557  }
2558 
2559  gnc_header_set_header_rows (GNC_HEADER (sheet->header_item),
2560  num_header_phys_rows);
2561  gnc_header_reconfigure (GNC_HEADER(sheet->header_item));
2562 
2563  gnucash_sheet_recompute_block_offsets (sheet);
2564 
2565  gnucash_sheet_set_scroll_region (sheet);
2566 
2567  if (do_scroll)
2568  {
2569  VirtualLocation virt_loc;
2570 
2571  virt_loc = table->current_cursor_loc;
2572 
2573  if (gnucash_sheet_cell_valid (sheet, virt_loc))
2574  gnucash_sheet_show_row (sheet,
2575  virt_loc.vcell_loc.virt_row);
2576  }
2577 
2578  gnucash_sheet_cursor_set_from_table (sheet, do_scroll);
2579  gnucash_sheet_activate_cursor_cell (sheet, TRUE);
2580 }
2581 
2582 static void
2583 gnucash_sheet_realize_entry (GnucashSheet *sheet, GtkWidget *entry)
2584 {
2585  GValue gval = {0,};
2586  g_value_init (&gval, G_TYPE_BOOLEAN);
2587  g_value_set_boolean (&gval, FALSE);
2588  g_object_set_property (G_OBJECT (entry), "editable", &gval);
2589 
2590  gtk_widget_realize (entry);
2591 }
2592 
2593 /*************************************************************/
2594 
2595 /* This code is one big hack to use gtkrc to set cell colors in a
2596  * register. Because the cells are just boxes drawn on a gnome
2597  * canvas, there's no way to specify the individual cells in a gtkrc
2598  * file. This code creates four hidden GtkEntry widgets and names
2599  * them so that they *can* be specified in gtkrc. It then looks up
2600  * the colors specified on these hidden widgets and uses it for the
2601  * cells drawn on the canvas. This code should all go away whenever
2602  * the register is rewritten.
2603  */
2604 
2606 GdkColor *
2607 get_gtkrc_color (GnucashSheet *sheet,
2608  RegisterColor field_type)
2609 {
2610  GtkWidget *widget = NULL;
2611  GtkStyle *style;
2612  GdkColor *white, *black, *red;
2613  GdkColor *color = NULL;
2614 
2615  white = gnucash_color_argb_to_gdk (0xFFFFFF);
2616  black = gnucash_color_argb_to_gdk (0x000000);
2617  red = gnucash_color_argb_to_gdk (0xFF0000); /* Hardcoded...*/
2618  switch (field_type)
2619  {
2620  default:
2621  return white;
2622 
2623  case COLOR_UNKNOWN_BG:
2624  return white;
2625 
2626  case COLOR_UNKNOWN_FG:
2627  return black;
2628 
2629  case COLOR_NEGATIVE:
2630  return red;
2631 
2632  case COLOR_HEADER_BG:
2633  case COLOR_HEADER_FG:
2634  widget = sheet->header_color;
2635  break;
2636 
2637  case COLOR_PRIMARY_BG:
2638  case COLOR_PRIMARY_BG_ACTIVE:
2639  case COLOR_PRIMARY_FG:
2640  case COLOR_PRIMARY_FG_ACTIVE:
2641  widget = sheet->primary_color;
2642  break;
2643 
2644  case COLOR_SECONDARY_BG:
2645  case COLOR_SECONDARY_BG_ACTIVE:
2646  case COLOR_SECONDARY_FG:
2647  case COLOR_SECONDARY_FG_ACTIVE:
2648  widget = sheet->secondary_color;
2649  break;
2650 
2651  case COLOR_SPLIT_BG:
2652  case COLOR_SPLIT_BG_ACTIVE:
2653  case COLOR_SPLIT_FG:
2654  case COLOR_SPLIT_FG_ACTIVE:
2655  widget = sheet->split_color;
2656  break;
2657  }
2658 
2659  style = gtk_widget_get_style(widget);
2660  if (!style)
2661  return white;
2662 
2663  switch (field_type)
2664  {
2665  default:
2666  return white;
2667 
2668  case COLOR_HEADER_BG:
2669  case COLOR_PRIMARY_BG:
2670  case COLOR_SECONDARY_BG:
2671  case COLOR_SPLIT_BG:
2672  color = &style->base[GTK_STATE_NORMAL];
2673  break;
2674 
2675  case COLOR_PRIMARY_BG_ACTIVE:
2676  case COLOR_SECONDARY_BG_ACTIVE:
2677  case COLOR_SPLIT_BG_ACTIVE:
2678  color = &style->base[GTK_STATE_SELECTED];
2679  break;
2680 
2681  case COLOR_HEADER_FG:
2682  case COLOR_PRIMARY_FG:
2683  case COLOR_SECONDARY_FG:
2684  case COLOR_SPLIT_FG:
2685  color = &style->text[GTK_STATE_NORMAL];
2686  break;
2687 
2688  case COLOR_PRIMARY_FG_ACTIVE:
2689  case COLOR_SECONDARY_FG_ACTIVE:
2690  case COLOR_SPLIT_FG_ACTIVE:
2691  color = &style->text[GTK_STATE_SELECTED];
2692  break;
2693  }
2694 
2695  gnucash_color_alloc_gdk(color);
2696  return color;
2697 }
2698 
2700 static void
2701 gnucash_sheet_create_color_hack(GnucashSheet *sheet)
2702 {
2703  sheet->header_color = gtk_entry_new();
2704  sheet->primary_color = gtk_entry_new();
2705  sheet->secondary_color = gtk_entry_new();
2706  sheet->split_color = gtk_entry_new();
2707 
2708  gtk_widget_set_name(sheet->header_color, "header_color");
2709  gtk_widget_set_name(sheet->primary_color, "primary_color");
2710  gtk_widget_set_name(sheet->secondary_color, "secondary_color");
2711  gtk_widget_set_name(sheet->split_color, "split_color");
2712 
2713  g_signal_connect_after(sheet, "realize",
2714  G_CALLBACK(gnucash_sheet_realize_entry),
2715  sheet->header_color);
2716  g_signal_connect_after(sheet, "realize",
2717  G_CALLBACK(gnucash_sheet_realize_entry),
2718  sheet->primary_color);
2719  g_signal_connect_after(sheet, "realize",
2720  G_CALLBACK(gnucash_sheet_realize_entry),
2721  sheet->secondary_color);
2722  g_signal_connect_after(sheet, "realize",
2723  G_CALLBACK(gnucash_sheet_realize_entry),
2724  sheet->split_color);
2725 }
2726 
2727 /*************************************************************/
2728 
2729 static void
2730 gnucash_sheet_class_init (GnucashSheetClass *klass)
2731 {
2732  GObjectClass *gobject_class;
2733  GtkWidgetClass *widget_class;
2734 
2735  gobject_class = G_OBJECT_CLASS (klass);
2736  widget_class = GTK_WIDGET_CLASS (klass);
2737 
2738  sheet_parent_class = g_type_class_peek_parent (klass);
2739 
2740  /* Method override */
2741  gobject_class->finalize = gnucash_sheet_finalize;
2742 
2743  widget_class->realize = gnucash_sheet_realize;
2744 
2745  widget_class->size_request = gnucash_sheet_size_request;
2746  widget_class->size_allocate = gnucash_sheet_size_allocate;
2747 
2748  widget_class->focus_in_event = gnucash_sheet_focus_in_event;
2749  widget_class->focus_out_event = gnucash_sheet_focus_out_event;
2750 
2751  widget_class->key_press_event = gnucash_sheet_key_press_event;
2752  widget_class->key_release_event = gnucash_sheet_key_release_event;
2753  widget_class->button_press_event = gnucash_button_press_event;
2754  widget_class->button_release_event = gnucash_button_release_event;
2755  widget_class->motion_notify_event = gnucash_motion_event;
2756  widget_class->scroll_event = gnucash_scroll_event;
2757 
2758 }
2759 
2760 
2761 static void
2762 gnucash_sheet_init (GnucashSheet *sheet)
2763 {
2764  GnomeCanvas *canvas = GNOME_CANVAS (sheet);
2765 
2766  gtk_widget_set_can_focus (GTK_WIDGET(canvas), TRUE);
2767  gtk_widget_set_can_default (GTK_WIDGET(canvas), TRUE);
2768 
2769  sheet->top_block = 1;
2770  sheet->bottom_block = 1;
2771  sheet->num_visible_blocks = 1;
2772  sheet->num_visible_phys_rows = 1;
2773 
2774  sheet->input_cancelled = FALSE;
2775 
2776  sheet->popup = NULL;
2777  sheet->num_virt_rows = 0;
2778  sheet->num_virt_cols = 0;
2779  sheet->item_editor = NULL;
2780  sheet->entry = NULL;
2781  sheet->editing = FALSE;
2782  sheet->button = 0;
2783  sheet->grabbed = FALSE;
2784  sheet->window_width = -1;
2785  sheet->window_height = -1;
2786  sheet->width = 0;
2787  sheet->height = 0;
2788 
2789  sheet->cursor_styles = g_hash_table_new (g_str_hash, g_str_equal);
2790 
2791  sheet->blocks = g_table_new (sizeof (SheetBlock),
2792  gnucash_sheet_block_construct,
2793  gnucash_sheet_block_destroy, NULL);
2794 
2795  /* setup IMContext */
2796  sheet->im_context = gtk_im_multicontext_new ();
2797  sheet->preedit_length = 0;
2798  sheet->preedit_char_length = 0;
2799  sheet->preedit_start_position = -1;
2800  sheet->preedit_cursor_position = 0;
2801  sheet->preedit_selection_length = 0;
2802  sheet->preedit_attrs = NULL;
2803  sheet->direct_update_cell = FALSE;
2804  sheet->need_im_reset = FALSE;
2805  sheet->commit_signal = 0;
2806  sheet->preedit_changed_signal = 0;
2807  sheet->retrieve_surrounding_signal = 0;
2808  sheet->delete_surrounding_signal = 0;
2809  sheet->shift_state = 0;
2810  sheet->keyval_state = 0;
2811 }
2812 
2813 
2814 GType
2815 gnucash_sheet_get_type (void)
2816 {
2817  static GType gnucash_sheet_type = 0;
2818 
2819  if (!gnucash_sheet_type)
2820  {
2821  static const GTypeInfo gnucash_sheet_info =
2822  {
2823  sizeof (GnucashSheetClass),
2824  NULL, /* base_init */
2825  NULL, /* base_finalize */
2826  (GClassInitFunc) gnucash_sheet_class_init,
2827  NULL, /* class_finalize */
2828  NULL, /* class_data */
2829  sizeof (GnucashSheet),
2830  0, /* n_preallocs */
2831  (GInstanceInitFunc) gnucash_sheet_init
2832  };
2833 
2834  gnucash_sheet_type =
2835  g_type_register_static (gnome_canvas_get_type (),
2836  "GnucashSheet",
2837  &gnucash_sheet_info, 0);
2838  }
2839 
2840  return gnucash_sheet_type;
2841 }
2842 
2843 GtkWidget *
2844 gnucash_sheet_new (Table *table)
2845 {
2846  GnucashSheet *sheet;
2847  GnomeCanvasItem *item;
2848  GnomeCanvasGroup *sheet_group;
2849 
2850  g_return_val_if_fail (table != NULL, NULL);
2851 
2852  sheet = gnucash_sheet_create (table);
2853 
2854  /* handy shortcuts */
2855  sheet_group = gnome_canvas_root (GNOME_CANVAS(sheet));
2856 
2857  /* The grid */
2858  item = gnome_canvas_item_new (sheet_group,
2859  gnucash_grid_get_type (),
2860  "sheet", sheet,
2861  NULL);
2862  sheet->grid = item;
2863 
2864  /* some register data */
2865  sheet->dimensions_hash_table = g_hash_table_new_full (g_int_hash,
2866  g_int_equal,
2867  g_free, NULL);
2868 
2869  /* The cursor */
2870  sheet->cursor = gnucash_cursor_new (sheet_group);
2871  gnome_canvas_item_set (sheet->cursor,
2872  "sheet", sheet,
2873  "grid", sheet->grid,
2874  NULL);
2875 
2876  /* The entry widget */
2877  sheet->entry = gtk_entry_new ();
2878  g_object_ref_sink(sheet->entry);
2879 
2880  /*gtk_layout_put (GTK_LAYOUT (sheet), sheet->entry, 0, 0);*/
2881 
2882  /* set up the editor */
2883  sheet->item_editor = gnc_item_edit_new(sheet_group, sheet, sheet->entry);
2884 
2885  gnome_canvas_item_hide (GNOME_CANVAS_ITEM(sheet->item_editor));
2886 
2887  /* The GtkEntry must be realized in order to send events to
2888  * it. We don't want to show it on the screen, but can't
2889  * realize it until after the window and all its parent
2890  * widgets have been realized. Thus this callback. */
2891  g_signal_connect_after(sheet, "realize",
2892  G_CALLBACK(gnucash_sheet_realize_entry),
2893  sheet->entry);
2894 
2895  gnucash_sheet_refresh_from_prefs(sheet);
2896  gnucash_sheet_create_color_hack(sheet);
2897 
2898  return GTK_WIDGET(sheet);
2899 }
2900 
2901 
2902 static void
2903 gnucash_register_class_init (GnucashRegisterClass *klass)
2904 {
2905  GObjectClass *gobject_class;
2906 
2907  gobject_class = G_OBJECT_CLASS (klass);
2908 
2909  register_parent_class = g_type_class_peek_parent (klass);
2910 
2911  register_signals[ACTIVATE_CURSOR] =
2912  g_signal_new("activate_cursor",
2913  G_TYPE_FROM_CLASS(gobject_class),
2914  G_SIGNAL_RUN_LAST,
2915  G_STRUCT_OFFSET(GnucashRegisterClass,
2916  activate_cursor),
2917  NULL, NULL,
2918  g_cclosure_marshal_VOID__VOID,
2919  G_TYPE_NONE, 0);
2920 
2921  register_signals[REDRAW_ALL] =
2922  g_signal_new("redraw_all",
2923  G_TYPE_FROM_CLASS(gobject_class),
2924  G_SIGNAL_RUN_LAST,
2925  G_STRUCT_OFFSET(GnucashRegisterClass,
2926  redraw_all),
2927  NULL, NULL,
2928  g_cclosure_marshal_VOID__VOID,
2929  G_TYPE_NONE, 0);
2930 
2931  register_signals[REDRAW_HELP] =
2932  g_signal_new("redraw_help",
2933  G_TYPE_FROM_CLASS(gobject_class),
2934  G_SIGNAL_RUN_LAST,
2935  G_STRUCT_OFFSET(GnucashRegisterClass,
2936  redraw_help),
2937  NULL, NULL,
2938  g_cclosure_marshal_VOID__VOID,
2939  G_TYPE_NONE, 0);
2940 
2941  klass->activate_cursor = NULL;
2942  klass->redraw_all = NULL;
2943  klass->redraw_help = NULL;
2944 }
2945 
2946 
2947 static void
2948 gnucash_register_init (GnucashRegister *g_reg)
2949 {
2950  GtkTable *table = GTK_TABLE(g_reg);
2951 
2952  gtk_widget_set_can_focus (GTK_WIDGET(table), FALSE);
2953  gtk_widget_set_can_default (GTK_WIDGET(table), FALSE);
2954 
2955  gtk_table_set_homogeneous (table, FALSE);
2956  gtk_table_resize (table, 3, 2);
2957 }
2958 
2959 
2960 GType
2961 gnucash_register_get_type (void)
2962 {
2963  static GType gnucash_register_type = 0;
2964 
2965  if (!gnucash_register_type)
2966  {
2967  static const GTypeInfo gnucash_register_info =
2968  {
2969  sizeof (GnucashRegisterClass),
2970  NULL, /* base_init */
2971  NULL, /* base_finalize */
2972  (GClassInitFunc) gnucash_register_class_init,
2973  NULL, /* class_finalize */
2974  NULL, /* class_data */
2975  sizeof (GnucashRegister),
2976  0, /* n_preallocs */
2977  (GInstanceInitFunc) gnucash_register_init,
2978  };
2979 
2980  gnucash_register_type = g_type_register_static
2981  (gtk_table_get_type (),
2982  "GnucashRegister",
2983  &gnucash_register_info, 0);
2984  }
2985 
2986  return gnucash_register_type;
2987 }
2988 
2989 
2990 void
2991 gnucash_register_attach_popup (GnucashRegister *reg,
2992  GtkWidget *popup,
2993  gpointer data)
2994 {
2995  g_return_if_fail (GNUCASH_IS_REGISTER(reg));
2996  g_return_if_fail (reg->sheet != NULL);
2997  if (popup)
2998  g_return_if_fail (GTK_IS_WIDGET(popup));
2999 
3000  gnucash_sheet_set_popup (GNUCASH_SHEET (reg->sheet), popup, data);
3001 }
3002 
3003 
3004 GtkWidget *
3005 gnucash_register_new (Table *table)
3006 {
3007  GnucashRegister *reg;
3008  GtkWidget *header_canvas;
3009  GtkWidget *widget;
3010  GtkWidget *sheet;
3011  GtkWidget *scrollbar;
3012  GtkWidget *box;
3013 
3014  reg = g_object_new (GNUCASH_TYPE_REGISTER, NULL);
3015  widget = GTK_WIDGET(reg);
3016 
3017  sheet = gnucash_sheet_new (table);
3018  reg->sheet = sheet;
3019  GNUCASH_SHEET(sheet)->reg = widget;
3020 
3021  header_canvas = gnc_header_new (GNUCASH_SHEET(sheet));
3022  reg->header_canvas = header_canvas;
3023 
3024  gtk_table_attach (GTK_TABLE(widget), header_canvas,
3025  0, 1, 0, 1,
3026  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3027  GTK_FILL,
3028  0, 0);
3029  gtk_widget_show(header_canvas);
3030 
3031  gtk_table_attach (GTK_TABLE(widget), sheet,
3032  0, 1, 1, 2,
3033  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3034  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3035  0, 0);
3036  gtk_widget_show(sheet);
3037 
3038  gtk_table_attach (GTK_TABLE(widget), GNUCASH_SHEET(sheet)->entry,
3039  0, 1, 2, 3,
3040  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3041  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3042  0, 0);
3043  gtk_widget_hide(GNUCASH_SHEET(sheet)->entry);
3044  gtk_widget_set_no_show_all(GNUCASH_SHEET(sheet)->entry, TRUE);
3045 
3046  scrollbar = gtk_vscrollbar_new(GNUCASH_SHEET(sheet)->vadj);
3047  gtk_table_attach (GTK_TABLE(widget), GTK_WIDGET(scrollbar),
3048  1, 2, 0, 3,
3049  GTK_FILL,
3050  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
3051  0, 0);
3052  reg->vscrollbar = scrollbar;
3053  gtk_widget_show(scrollbar);
3054 
3055  scrollbar = gtk_hscrollbar_new(GNUCASH_SHEET(sheet)->hadj);
3056  gtk_table_attach (GTK_TABLE(widget), GTK_WIDGET(scrollbar),
3057  0, 1, 3, 4,
3058  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
3059  GTK_FILL,
3060  0, 0);
3061  reg->hscrollbar = scrollbar;
3062  gtk_widget_show(scrollbar);
3063  reg->hscrollbar_visible = TRUE;
3064 
3065  /* The gtkrc color helper widgets need to be part of a window
3066  * hierarchy so they can be realized. Stick them in a box
3067  * underneath the register, but don't show the box to the
3068  * user. */
3069  box = gtk_hbox_new(FALSE, 0);
3070  gtk_widget_set_no_show_all(GTK_WIDGET(box), TRUE);
3071  gtk_box_pack_start(GTK_BOX(box),
3072  GNUCASH_SHEET(sheet)->header_color, TRUE, TRUE, 0);
3073  gtk_box_pack_start(GTK_BOX(box),
3074  GNUCASH_SHEET(sheet)->primary_color, TRUE, TRUE, 0);
3075  gtk_box_pack_start(GTK_BOX(box),
3076  GNUCASH_SHEET(sheet)->secondary_color, TRUE, TRUE, 0);
3077  gtk_box_pack_start(GTK_BOX(box),
3078  GNUCASH_SHEET(sheet)->split_color, TRUE, TRUE, 0);
3079 
3080  gtk_table_attach (GTK_TABLE(widget), box,
3081  0, 1, 4, 5,
3082  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3083  GTK_FILL | GTK_EXPAND | GTK_SHRINK,
3084  0, 0);
3085 
3086  return widget;
3087 }
3088 
3089 
3090 void gnucash_register_set_moved_cb (GnucashRegister *reg,
3091  GFunc cb, gpointer cb_data)
3092 {
3093  GnucashSheet *sheet;
3094 
3095  if (!reg || !reg->sheet)
3096  return;
3097  sheet = GNUCASH_SHEET(reg->sheet);
3098  sheet->moved_cb = cb;
3099  sheet->moved_cb_data = cb_data;
3100 }
3101 
3102 
3103 GnucashSheet *gnucash_register_get_sheet (GnucashRegister *reg)
3104 {
3105  g_return_val_if_fail (reg != NULL, NULL);
3106  g_return_val_if_fail (GNUCASH_IS_REGISTER(reg), NULL);
3107 
3108  return GNUCASH_SHEET(reg->sheet);
3109 }
3110 
RegisterColor
Definition: table-allgui.h:160
gboolean gnc_table_move_vertical_position(Table *table, VirtualLocation *virt_loc, int phys_row_offset)
#define ENTER(format, args...)
Definition: qoflog.h:261
All type declarations for the whole Gnucash engine.
API for checkbook register display area.
Generic api to store and retrieve preferences.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
#define LEAVE(format, args...)
Definition: qoflog.h:271
const gchar * QofLogModule
Definition: qofid.h:89