GnuCash  5.6-150-g038405b370+
table-allgui.c
1 /********************************************************************\
2  * table-allgui.c -- 2D grid table object, embeds cells for i/o *
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 gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 /*
24  * FILE:
25  * table-allgui.c
26  *
27  * FUNCTION:
28  * Implements the gui-independent parts of the table infrastructure.
29  *
30  * HISTORY:
31  * Copyright (c) 1998,1999,2000 Linas Vepstas
32  * Copyright (c) 2000 Dave Peticolas
33  */
34 
35 #include <config.h>
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <glib.h>
42 
43 #include "table-allgui.h"
44 #include "cellblock.h"
45 #include "gnc-engine.h"
46 
47 
50 static TableGUIHandlers default_gui_handlers;
51 
52 /* This static indicates the debugging module that this .o belongs to. */
53 static QofLogModule log_module = GNC_MOD_REGISTER;
54 
55 
57 static void gnc_table_init (Table * table);
58 static void gnc_table_free_data (Table * table);
59 static void gnc_virtual_cell_construct (gpointer vcell, gpointer user_data);
60 static void gnc_virtual_cell_destroy (gpointer vcell, gpointer user_data);
61 static void gnc_table_resize (Table * table, int virt_rows, int virt_cols);
62 
63 
66 void
68 {
69  if (!gui_handlers)
70  memset (&default_gui_handlers, 0, sizeof (default_gui_handlers));
71  else
72  default_gui_handlers = *gui_handlers;
73 }
74 
75 Table *
76 gnc_table_new (TableLayout *layout, TableModel *model, TableControl *control)
77 {
78  Table *table;
79 
80  g_return_val_if_fail (layout != NULL, NULL);
81  g_return_val_if_fail (model != NULL, NULL);
82  g_return_val_if_fail (control != NULL, NULL);
83 
84  table = g_new0 (Table, 1);
85 
86  table->layout = layout;
87  table->model = model;
88  table->control = control;
89 
90  table->gui_handlers = default_gui_handlers;
91 
92  gnc_table_init (table);
93 
94  table->virt_cells = g_table_new (sizeof (VirtualCell),
95  gnc_virtual_cell_construct,
96  gnc_virtual_cell_destroy, table);
97 
98  return table;
99 }
100 
101 static void
102 gnc_table_init (Table * table)
103 {
104  table->num_virt_rows = -1;
105  table->num_virt_cols = -1;
106 
107  table->current_cursor = NULL;
108 
109  gnc_virtual_location_init (&table->current_cursor_loc);
110 
111  /* initialize private data */
112 
113  table->virt_cells = NULL;
114  table->ui_data = NULL;
115 }
116 
117 void
118 gnc_table_destroy (Table * table)
119 {
120  /* invoke destroy callback */
121  if (table->gui_handlers.destroy)
122  table->gui_handlers.destroy (table);
123 
124  /* free the dynamic structures */
125  gnc_table_free_data (table);
126 
127  /* free the cell tables */
128  g_table_destroy (table->virt_cells);
129 
130  gnc_table_layout_destroy (table->layout);
131  table->layout = NULL;
132 
133  gnc_table_control_destroy (table->control);
134  table->control = NULL;
135 
136  gnc_table_model_destroy (table->model);
137  table->model = NULL;
138 
139  /* initialize vars to null value so that any access is voided. */
140  gnc_table_init (table);
141 
142  g_free (table);
143 }
144 
145 int
146 gnc_table_current_cursor_changed (Table *table,
147  gboolean include_conditional)
148 {
149  if (!table)
150  return FALSE;
151 
152  return gnc_cellblock_changed (table->current_cursor, include_conditional);
153 }
154 
155 void
156 gnc_table_clear_current_cursor_changes (Table *table)
157 {
158  if (!table)
159  return;
160 
161  gnc_cellblock_clear_changes (table->current_cursor);
162 }
163 
164 void
165 gnc_table_save_current_cursor (Table *table, CursorBuffer *buffer)
166 {
167  if (!table || !buffer)
168  return;
169 
170  gnc_table_layout_save_cursor (table->layout, table->current_cursor, buffer);
171 }
172 
173 void
174 gnc_table_restore_current_cursor (Table *table,
175  CursorBuffer *buffer)
176 {
177  if (!table || !buffer)
178  return;
179 
180  gnc_table_layout_restore_cursor (table->layout,
181  table->current_cursor, buffer);
182 }
183 
184 const char *
185 gnc_table_get_current_cell_name (Table *table)
186 {
187  if (table == NULL)
188  return NULL;
189 
190  return gnc_table_get_cell_name (table, table->current_cursor_loc);
191 }
192 
193 gboolean
194 gnc_table_get_current_cell_location (Table *table,
195  const char *cell_name,
196  VirtualLocation *virt_loc)
197 {
198  if (table == NULL)
199  return FALSE;
200 
201  return gnc_table_get_cell_location (table, cell_name,
202  table->current_cursor_loc.vcell_loc,
203  virt_loc);
204 }
205 
206 gboolean
208  VirtualCellLocation vcell_loc)
209 {
210  if (!table)
211  return TRUE;
212 
213  return ((vcell_loc.virt_row < 0) ||
214  (vcell_loc.virt_row >= table->num_virt_rows) ||
215  (vcell_loc.virt_col < 0) ||
216  (vcell_loc.virt_col >= table->num_virt_cols));
217 }
218 
219 gboolean
220 gnc_table_virtual_location_in_header (Table *table,
221  VirtualLocation virt_loc)
222 {
223  return (virt_loc.vcell_loc.virt_row == 0);
224 }
225 
226 VirtualCell *
227 gnc_table_get_virtual_cell (Table *table, VirtualCellLocation vcell_loc)
228 {
229  if (table == NULL)
230  return NULL;
231 
232  return g_table_index (table->virt_cells,
233  vcell_loc.virt_row, vcell_loc.virt_col);
234 }
235 
236 VirtualCell *
238 {
239  VirtualCellLocation vcell_loc = { 0, 0 };
240 
241  return gnc_table_get_virtual_cell (table, vcell_loc);
242 }
243 
244 static const char *
245 table_get_model_entry (Table *table, VirtualLocation virt_loc,
246  gboolean translate, gboolean *conditionally_changed, const char *cell_name)
247 {
248  TableGetEntryHandler entry_handler = gnc_table_model_get_entry_handler (
249  table->model, cell_name);
250 
251  if (entry_handler)
252  {
253  const char *entry = entry_handler (virt_loc, translate, conditionally_changed,
254  table->model->handler_user_data);
255  if (entry)
256  return entry;
257  }
258 
259  return "";
260 }
261 
262 static const char *
263 table_get_entry (Table *table, VirtualLocation virt_loc,
264  gboolean *conditionally_changed)
265 {
266  const char *cell_name = gnc_table_get_cell_name (table, virt_loc);
267 
268  return table_get_model_entry (table, virt_loc, FALSE, conditionally_changed,
269  cell_name);
270 }
271 
272 const char *
273 gnc_table_get_entry (Table *table, VirtualLocation virt_loc)
274 {
275  BasicCell *cell = gnc_table_get_cell (table, virt_loc);
276 
277  if (!cell || !cell->cell_name)
278  return "";
279 
280  if (virt_cell_loc_equal (table->current_cursor_loc.vcell_loc,
281  virt_loc.vcell_loc))
282  {
283  CellIOFlags io_flags = gnc_table_get_io_flags (table, virt_loc);
284 
285  if (io_flags & XACC_CELL_ALLOW_INPUT)
286  return cell->value;
287  }
288 
289  return table_get_model_entry (table, virt_loc, TRUE, NULL, cell->cell_name);
290 }
291 
292 const char *
293 gnc_table_get_model_entry (Table *table, const char *cell_name)
294 {
295  return table_get_model_entry (table, table->current_cursor_loc, TRUE, NULL,
296  cell_name);
297 }
298 
299 char *
300 gnc_table_get_tooltip (Table *table, VirtualLocation virt_loc)
301 {
302  TableGetTooltipHandler tooltip_handler;
303  BasicCell *cell;
304 
305  cell = gnc_table_get_cell (table, virt_loc);
306  if (!cell || !cell->cell_name)
307  return NULL;
308 
309  tooltip_handler =
310  gnc_table_model_get_tooltip_handler (table->model, cell->cell_name);
311 
312  if (!tooltip_handler)
313  return NULL;
314 
315  return tooltip_handler (virt_loc, table->model->handler_user_data);
316 }
317 
318 CellIOFlags
319 gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc)
320 {
321  TableGetCellIOFlagsHandler io_flags_handler;
322  const char *cell_name;
323  CellIOFlags flags;
324 
325  if (!table || !table->model)
326  return XACC_CELL_ALLOW_NONE;
327 
328  cell_name = gnc_table_get_cell_name (table, virt_loc);
329 
330  io_flags_handler =
331  gnc_table_model_get_io_flags_handler (table->model, cell_name);
332  if (!io_flags_handler)
333  return XACC_CELL_ALLOW_NONE;
334 
335  flags = io_flags_handler (virt_loc, table->model->handler_user_data);
336 
337  if (gnc_table_model_read_only (table->model))
338  flags &= XACC_CELL_ALLOW_SHADOW;
339 
340  return flags;
341 }
342 
343 const char *
344 gnc_table_get_label (Table *table, VirtualLocation virt_loc)
345 {
346  TableGetLabelHandler label_handler;
347  const char *cell_name;
348  const char *label;
349 
350  if (!table || !table->model)
351  return "";
352 
353  cell_name = gnc_table_get_cell_name (table, virt_loc);
354 
355  label_handler = gnc_table_model_get_label_handler (table->model, cell_name);
356  if (!label_handler)
357  return "";
358 
359  label = label_handler (virt_loc, table->model->handler_user_data);
360  if (!label)
361  return "";
362 
363  return label;
364 }
365 
366 guint32
367 gnc_table_get_color (Table *table, VirtualLocation virt_loc,
368  gboolean *hatching)
369 {
370  TableGetCellColorHandler color_handler;
371  const char *handler_name;
372 
373  if (hatching)
374  *hatching = FALSE;
375 
376  if (!table || !table->model)
377  return COLOR_UNDEFINED;
378 
379  handler_name = gnc_table_get_cell_name (table, virt_loc);
380 
381  color_handler =
382  gnc_table_model_get_cell_color_handler (table->model, handler_name);
383 
384  if (!color_handler)
385  return COLOR_UNDEFINED;
386 
387  return color_handler (virt_loc, hatching,
388  table->model->handler_user_data);
389 }
390 
391 void
392 gnc_table_get_borders (Table *table, VirtualLocation virt_loc,
393  PhysicalCellBorders *borders)
394 {
395  TableGetCellBorderHandler cell_border_handler;
396  const char *cell_name;
397 
398  if (!table || !table->model)
399  return;
400 
401  cell_name = gnc_table_get_cell_name (table, virt_loc);
402 
403  cell_border_handler =
404  gnc_table_model_get_cell_border_handler (table->model, cell_name);
405  if (!cell_border_handler)
406  return;
407 
408  cell_border_handler (virt_loc, borders, table->model->handler_user_data);
409 }
410 
411 CellAlignment
412 gnc_table_get_align (Table *table, VirtualLocation virt_loc)
413 {
414  BasicCell *cell;
415 
416  cell = gnc_table_get_cell (table, virt_loc);
417  if (!cell)
418  return CELL_ALIGN_RIGHT;
419 
420  return cell->alignment;
421 }
422 
423 gboolean
424 gnc_table_is_popup (Table *table, VirtualLocation virt_loc)
425 {
426  BasicCell *cell;
427 
428  cell = gnc_table_get_cell (table, virt_loc);
429  if (!cell)
430  return FALSE;
431 
432  return cell->is_popup;
433 }
434 
435 char *
436 gnc_table_get_help (Table *table)
437 {
438  TableGetHelpHandler help_handler;
439  VirtualLocation virt_loc;
440  const char * cell_name;
441 
442  if (!table)
443  return NULL;
444 
445  virt_loc = table->current_cursor_loc;
446 
447  cell_name = gnc_table_get_cell_name (table, virt_loc);
448 
449  help_handler = gnc_table_model_get_help_handler (table->model, cell_name);
450  if (!help_handler)
451  return NULL;
452 
453  return help_handler (virt_loc, table->model->handler_user_data);
454 }
455 
456 BasicCell *
457 gnc_table_get_cell (Table *table, VirtualLocation virt_loc)
458 {
459  VirtualCell *vcell;
460 
461  if (!table)
462  return NULL;
463 
464  vcell = gnc_table_get_virtual_cell (table, virt_loc.vcell_loc);
465  if (!vcell)
466  return NULL;
467 
468  return gnc_cellblock_get_cell (vcell->cellblock,
469  virt_loc.phys_row_offset,
470  virt_loc.phys_col_offset);
471 }
472 
473 const char *
474 gnc_table_get_cell_name (Table *table, VirtualLocation virt_loc)
475 {
476  BasicCell *cell;
477 
478  cell = gnc_table_get_cell (table, virt_loc);
479  if (cell == NULL)
480  return NULL;
481 
482  return cell->cell_name;
483 }
484 
485 const gchar *
486 gnc_table_get_cell_type_name (Table *table, VirtualLocation virt_loc)
487 {
488  BasicCell *cell;
489 
490  cell = gnc_table_get_cell (table, virt_loc);
491  if (cell == NULL)
492  return NULL;
493 
494  return cell->cell_type_name;
495 }
496 
497 
498 gboolean
499 gnc_table_get_cell_location (Table *table,
500  const char *cell_name,
501  VirtualCellLocation vcell_loc,
502  VirtualLocation *virt_loc)
503 {
504  VirtualCell *vcell;
505  CellBlock *cellblock;
506  int cell_row, cell_col;
507 
508  if (table == NULL)
509  return FALSE;
510 
511  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
512  if (vcell == NULL)
513  return FALSE;
514 
515  cellblock = vcell->cellblock;
516 
517  for (cell_row = 0; cell_row < cellblock->num_rows; cell_row++)
518  for (cell_col = 0; cell_col < cellblock->num_cols; cell_col++)
519  {
520  BasicCell *cell;
521 
522  cell = gnc_cellblock_get_cell (cellblock, cell_row, cell_col);
523  if (!cell)
524  continue;
525 
526  if (gnc_basic_cell_has_name (cell, cell_name))
527  {
528  if (virt_loc != NULL)
529  {
530  virt_loc->vcell_loc = vcell_loc;
531 
532  virt_loc->phys_row_offset = cell_row;
533  virt_loc->phys_col_offset = cell_col;
534  }
535 
536  return TRUE;
537  }
538  }
539 
540  return FALSE;
541 }
542 
543 void
544 gnc_table_save_cells (Table *table, gpointer save_data)
545 {
546  TableSaveHandler save_handler;
547  GList * cells;
548  GList * node;
549 
550  g_return_if_fail (table);
551 
552  /* ignore any changes to read-only tables */
553  if (gnc_table_model_read_only (table->model))
554  return;
555 
556  // gnc_table_leave_update (table, table->current_cursor_loc);
557 
558  save_handler = gnc_table_model_get_pre_save_handler (table->model);
559  if (save_handler)
560  save_handler (save_data, table->model->handler_user_data);
561 
562  cells = gnc_table_layout_get_cells (table->layout);
563  for (node = cells; node; node = node->next)
564  {
565  BasicCell * cell = node->data;
566  TableSaveCellHandler save_cell_handler;
567 
568  if (!cell) continue;
569 
570  if (!gnc_table_layout_get_cell_changed (table->layout,
571  cell->cell_name, TRUE))
572  continue;
573 
574  save_cell_handler = gnc_table_model_get_save_handler (table->model,
575  cell->cell_name);
576  if (save_cell_handler)
577  save_cell_handler (cell, save_data, table->model->handler_user_data);
578  }
579 
580  save_handler = gnc_table_model_get_post_save_handler (table->model);
581  if (save_handler)
582  save_handler (save_data, table->model->handler_user_data);
583 }
584 
585 void
586 gnc_table_set_size (Table * table, int virt_rows, int virt_cols)
587 {
588  /* Invalidate the current cursor position, if the array is
589  * shrinking. This must be done since the table is probably
590  * shrinking because some rows were deleted, and the cursor
591  * could be on the deleted rows. */
592  if ((virt_rows < table->num_virt_rows) ||
593  (virt_cols < table->num_virt_cols))
594  {
595  gnc_virtual_location_init (&table->current_cursor_loc);
596  table->current_cursor = NULL;
597  }
598 
599  gnc_table_resize (table, virt_rows, virt_cols);
600 }
601 
602 static void
603 gnc_table_free_data (Table * table)
604 {
605  if (table == NULL)
606  return;
607 
608  g_table_resize (table->virt_cells, 0, 0);
609 }
610 
611 void
612 gnc_virtual_location_init (VirtualLocation *vloc)
613 {
614  if (vloc == NULL)
615  return;
616 
617  vloc->phys_row_offset = -1;
618  vloc->phys_col_offset = -1;
619  vloc->vcell_loc.virt_row = -1;
620  vloc->vcell_loc.virt_col = -1;
621 }
622 
623 static void
624 gnc_virtual_cell_construct (gpointer _vcell, gpointer user_data)
625 {
626  VirtualCell *vcell = _vcell;
627  Table *table = user_data;
628 
629  vcell->cellblock = NULL;
630 
631  if (table && table->model->cell_data_allocator)
632  vcell->vcell_data = table->model->cell_data_allocator ();
633  else
634  vcell->vcell_data = NULL;
635 
636  vcell->visible = 1;
637 }
638 
639 static void
640 gnc_virtual_cell_destroy (gpointer _vcell, gpointer user_data)
641 {
642  VirtualCell *vcell = _vcell;
643  Table *table = user_data;
644 
645  if (vcell->vcell_data && table && table->model->cell_data_deallocator)
646  table->model->cell_data_deallocator (vcell->vcell_data);
647 
648  vcell->vcell_data = NULL;
649 }
650 
651 static void
652 gnc_table_resize (Table * table, int new_virt_rows, int new_virt_cols)
653 {
654  if (!table) return;
655 
656  g_table_resize (table->virt_cells, new_virt_rows, new_virt_cols);
657 
658  table->num_virt_rows = new_virt_rows;
659  table->num_virt_cols = new_virt_cols;
660 }
661 
662 void
664  CellBlock *cursor,
665  gconstpointer vcell_data,
666  gboolean visible,
667  gboolean start_primary_color,
668  VirtualCellLocation vcell_loc)
669 {
670  VirtualCell *vcell;
671 
672  if ((table == NULL) || (cursor == NULL))
673  return;
674 
675  if ((vcell_loc.virt_row >= table->num_virt_rows) ||
676  (vcell_loc.virt_col >= table->num_virt_cols))
677  gnc_table_resize (table,
678  MAX (table->num_virt_rows, vcell_loc.virt_row + 1),
679  MAX (table->num_virt_cols, vcell_loc.virt_col + 1));
680 
681  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
682  if (vcell == NULL)
683  return;
684 
685  /* this cursor is the handler for this block */
686  vcell->cellblock = cursor;
687 
688  /* copy the vcell user data */
689  if (table->model->cell_data_copy)
690  table->model->cell_data_copy (vcell->vcell_data, vcell_data);
691  else
692  vcell->vcell_data = (gpointer) vcell_data;
693 
694  vcell->visible = visible ? 1 : 0;
695  vcell->start_primary_color = start_primary_color ? 1 : 0;
696 }
697 
698 void
700  VirtualCellLocation vcell_loc,
701  gconstpointer vcell_data)
702 {
703  VirtualCell *vcell;
704 
705  if (table == NULL)
706  return;
707 
708  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
709  if (vcell == NULL)
710  return;
711 
712  if (table->model->cell_data_copy)
713  table->model->cell_data_copy (vcell->vcell_data, vcell_data);
714  else
715  vcell->vcell_data = (gpointer) vcell_data;
716 }
717 
718 void
720  VirtualCellLocation vcell_loc,
721  gboolean visible)
722 {
723  VirtualCell *vcell;
724 
725  if (table == NULL)
726  return;
727 
728  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
729  if (vcell == NULL)
730  return;
731 
732  vcell->visible = visible ? 1 : 0;
733 }
734 
735 void
737  VirtualCellLocation vcell_loc,
738  CellBlock *cursor)
739 {
740  VirtualCell *vcell;
741 
742  if (table == NULL)
743  return;
744 
745  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
746  if (vcell == NULL)
747  return;
748 
749  vcell->cellblock = cursor;
750 }
751 
752 static void
753 gnc_table_move_cursor_internal (Table *table,
754  VirtualLocation new_virt_loc,
755  gboolean do_move_gui)
756 {
757  int cell_row, cell_col;
758  VirtualLocation virt_loc;
759  VirtualCell *vcell;
760  CellBlock *curs;
761 
762  ENTER("new_virt=(%d %d) do_move_gui=%d\n",
763  new_virt_loc.vcell_loc.virt_row,
764  new_virt_loc.vcell_loc.virt_col, do_move_gui);
765 
766  /* call the callback, allowing the app to commit any changes
767  * associated with the current location of the cursor. Note that
768  * this callback may recursively call this routine. */
769  if (table->control->move_cursor && table->control->allow_move)
770  {
771  table->control->move_cursor (&new_virt_loc, table->control->user_data);
772 
773  /* The above callback can cause this routine to be called
774  * recursively. As a result of this recursion, the cursor may
775  * have gotten repositioned. We need to make sure we make
776  * passive again. */
777  if (do_move_gui)
779  }
780 
781  /* invalidate the cursor for now; we'll fix it back up below */
782  gnc_virtual_location_init (&table->current_cursor_loc);
783 
784  curs = table->current_cursor;
785  table->current_cursor = NULL;
786 
787  /* check for out-of-bounds conditions (which may be deliberate) */
788  if ((new_virt_loc.vcell_loc.virt_row < 0) ||
789  (new_virt_loc.vcell_loc.virt_col < 0))
790  {
791  /* if the location is invalid, then we should take this
792  * as a command to unmap the cursor gui. */
793  if (do_move_gui && curs)
794  {
795  for (cell_row = 0; cell_row < curs->num_rows; cell_row++)
796  for (cell_col = 0; cell_col < curs->num_cols; cell_col++)
797  {
798  BasicCell *cell;
799 
800  cell = gnc_cellblock_get_cell (curs, cell_row, cell_col);
801  if (cell)
802  {
803  cell->changed = FALSE;
804  cell->conditionally_changed = FALSE;
805 
806  if (cell->gui_move)
807  cell->gui_move (cell);
808  }
809  }
810  }
811 
812  LEAVE("out of bounds\n");
813  return;
814  }
815 
816  if (!gnc_table_virtual_loc_valid (table, new_virt_loc, TRUE))
817  {
818  PWARN("bad table location");
819  LEAVE("");
820  return;
821  }
822 
823  /* ok, we now have a valid position. Find the new cursor to use,
824  * and initialize its cells */
825  vcell = gnc_table_get_virtual_cell (table, new_virt_loc.vcell_loc);
826  curs = vcell->cellblock;
827  table->current_cursor = curs;
828 
829  /* record the new position */
830  table->current_cursor_loc = new_virt_loc;
831 
832  virt_loc.vcell_loc = new_virt_loc.vcell_loc;
833 
834  /* update the cell values to reflect the new position */
835  for (cell_row = 0; cell_row < curs->num_rows; cell_row++)
836  for (cell_col = 0; cell_col < curs->num_cols; cell_col++)
837  {
838  BasicCell *cell;
839  CellIOFlags io_flags;
840 
841  virt_loc.phys_row_offset = cell_row;
842  virt_loc.phys_col_offset = cell_col;
843 
844  cell = gnc_cellblock_get_cell(curs, cell_row, cell_col);
845  if (cell)
846  {
847  /* if a cell has a GUI, move that first, before setting
848  * the cell value. Otherwise, we'll end up putting the
849  * new values in the old cell locations, and that would
850  * lead to confusion of all sorts. */
851  if (do_move_gui && cell->gui_move)
852  cell->gui_move (cell);
853 
854  /* OK, now copy the string value from the table at large
855  * into the cell handler. */
856  io_flags = gnc_table_get_io_flags (table, virt_loc);
857  if (io_flags & XACC_CELL_ALLOW_SHADOW)
858  {
859  const char *entry;
860  gboolean conditionally_changed = FALSE;
861 
862  entry = table_get_entry (table, virt_loc,
863  &conditionally_changed);
864 
865  gnc_basic_cell_set_value (cell, entry);
866 
867  cell->changed = FALSE;
868  cell->conditionally_changed = conditionally_changed;
869  }
870  }
871  }
872 
873  LEAVE("did move\n");
874 }
875 
876 void
877 gnc_table_move_cursor (Table *table, VirtualLocation new_virt_loc)
878 {
879  if (!table) return;
880 
881  gnc_table_move_cursor_internal (table, new_virt_loc, FALSE);
882 }
883 
884 /* same as above, but be sure to deal with GUI elements as well */
885 void
886 gnc_table_move_cursor_gui (Table *table, VirtualLocation new_virt_loc)
887 {
888  if (!table) return;
889 
890  gnc_table_move_cursor_internal (table, new_virt_loc, TRUE);
891 }
892 
893 /* gnc_table_verify_cursor_position checks the location of the cursor
894  * with respect to a virtual location, and repositions the cursor
895  * if necessary. Returns true if the cell cursor was repositioned. */
896 gboolean
897 gnc_table_verify_cursor_position (Table *table, VirtualLocation virt_loc)
898 {
899  gboolean do_move = FALSE;
900  gboolean moved_cursor = FALSE;
901 
902  if (!table) return FALSE;
903 
904  /* Someone may be trying to intentionally invalidate the cursor, in
905  * which case the physical addresses could be out of bounds. For
906  * example, in order to unmap it in preparation for a reconfig.
907  * So, if the specified location is out of bounds, then the cursor
908  * MUST be moved. */
909  if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc.vcell_loc))
910  do_move = TRUE;
911 
912  if (!virt_cell_loc_equal (virt_loc.vcell_loc,
913  table->current_cursor_loc.vcell_loc))
914  do_move = TRUE;
915 
916  if (do_move)
917  {
918  gnc_table_move_cursor_gui (table, virt_loc);
919  moved_cursor = TRUE;
920  }
921  else if (!virt_loc_equal (virt_loc, table->current_cursor_loc))
922  {
923  table->current_cursor_loc = virt_loc;
924  moved_cursor = TRUE;
925  }
926 
927  return moved_cursor;
928 }
929 
930 gpointer
931 gnc_table_get_vcell_data (Table *table, VirtualCellLocation vcell_loc)
932 {
933  VirtualCell *vcell;
934 
935  if (!table) return NULL;
936 
937  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
938  if (vcell == NULL)
939  return NULL;
940 
941  return vcell->vcell_data;
942 }
943 
944 /* If any of the cells have GUI specific components that need
945  * initialization, initialize them now. The realize() callback
946  * on the cursor cell is how we inform the cell handler that
947  * now is the time to initialize its GUI. */
948 void
949 gnc_table_realize_gui (Table * table)
950 {
951  GList *cells;
952  GList *node;
953 
954  if (!table) return;
955  if (!table->ui_data) return;
956 
957  cells = gnc_table_layout_get_cells (table->layout);
958 
959  for (node = cells; node; node = node->next)
960  {
961  BasicCell *cell = node->data;
962 
963  if (cell->gui_realize)
964  cell->gui_realize (cell, table->ui_data);
965  }
966 }
967 
968 void
969 gnc_table_wrap_verify_cursor_position (Table *table, VirtualLocation virt_loc)
970 {
971  VirtualLocation save_loc;
972  gboolean moved_cursor;
973 
974  if (!table) return;
975 
976  ENTER("(%d %d)", virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_col);
977 
978  save_loc = table->current_cursor_loc;
979 
980  /* VerifyCursor will do all sorts of gui-independent machinations */
981  moved_cursor = gnc_table_verify_cursor_position (table, virt_loc);
982 
983  if (moved_cursor)
984  {
985  /* make sure *both* the old and the new cursor rows get redrawn */
987  gnc_table_refresh_cursor_gui (table, save_loc.vcell_loc, FALSE);
988  }
989 
990  LEAVE ("");
991 }
992 
993 void
994 gnc_table_refresh_current_cursor_gui (Table * table, gboolean do_scroll)
995 {
996  if (!table) return;
997 
998  gnc_table_refresh_cursor_gui (table, table->current_cursor_loc.vcell_loc,
999  do_scroll);
1000 }
1001 
1002 gboolean
1003 gnc_table_virtual_loc_valid(Table *table,
1004  VirtualLocation virt_loc,
1005  gboolean exact_pointer)
1006 {
1007  VirtualCell *vcell;
1008  CellIOFlags io_flags;
1009 
1010  if (!table) return FALSE;
1011 
1012  /* header rows cannot be modified */
1013  if (virt_loc.vcell_loc.virt_row == 0)
1014  return FALSE;
1015 
1016  vcell = gnc_table_get_virtual_cell(table, virt_loc.vcell_loc);
1017  if (vcell == NULL)
1018  return FALSE;
1019 
1020  if (!vcell->visible)
1021  return FALSE;
1022 
1023  /* verify that offsets are valid. This may occur if the app that is
1024  * using the table has a partially initialized cursor. (probably due
1025  * to a programming error, but maybe they meant to do this). */
1026  if ((0 > virt_loc.phys_row_offset) || (0 > virt_loc.phys_col_offset))
1027  return FALSE;
1028 
1029  /* check for a cell handler, but only if cell address is valid */
1030  if (vcell->cellblock == NULL) return FALSE;
1031 
1032  /* if table is read-only, any cell is ok :) */
1033  if (gnc_table_model_read_only (table->model)) return TRUE;
1034 
1035  io_flags = gnc_table_get_io_flags (table, virt_loc);
1036 
1037  /* if the cell allows ENTER, then it is ok */
1038  if (io_flags & XACC_CELL_ALLOW_ENTER) return TRUE;
1039 
1040  /* if cell is marked as output-only, you can't enter */
1041  if (0 == (XACC_CELL_ALLOW_INPUT & io_flags)) return FALSE;
1042 
1043  /* if cell is pointer only and this is not an exact pointer test,
1044  * it cannot be entered. */
1045  if (!exact_pointer && ((XACC_CELL_ALLOW_EXACT_ONLY & io_flags) != 0))
1046  return FALSE;
1047 
1048  return TRUE;
1049 }
1050 
1051 /* Handle the non gui-specific parts of a cell enter callback */
1052 gboolean
1053 gnc_table_enter_update (Table *table,
1054  VirtualLocation virt_loc,
1055  int *cursor_position,
1056  int *start_selection,
1057  int *end_selection)
1058 {
1059  gboolean can_edit = TRUE;
1060  CellEnterFunc enter;
1061  BasicCell *cell;
1062  CellBlock *cb;
1063  int cell_row;
1064  int cell_col;
1065  CellIOFlags io_flags;
1066 
1067  if (table == NULL)
1068  return FALSE;
1069 
1070  cb = table->current_cursor;
1071 
1072  cell_row = virt_loc.phys_row_offset;
1073  cell_col = virt_loc.phys_col_offset;
1074 
1075  ENTER("enter %d %d (relrow=%d relcol=%d)",
1076  virt_loc.vcell_loc.virt_row,
1077  virt_loc.vcell_loc.virt_col,
1078  cell_row, cell_col);
1079 
1080  /* OK, if there is a callback for this cell, call it */
1081  cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
1082  if (!cell)
1083  {
1084  LEAVE("no cell");
1085  return FALSE;
1086  }
1087 
1088  io_flags = gnc_table_get_io_flags (table, virt_loc);
1089  if (io_flags == XACC_CELL_ALLOW_READ_ONLY)
1090  {
1091  if (table->gui_handlers.redraw_help)
1092  table->gui_handlers.redraw_help (table);
1093  LEAVE("read only cell");
1094  return FALSE;
1095  }
1096 
1097  enter = cell->enter_cell;
1098 
1099  if (enter)
1100  {
1101  char * old_value;
1102 
1103  DEBUG("gnc_table_enter_update(): %d %d has enter handler\n",
1104  cell_row, cell_col);
1105 
1106  old_value = g_strdup (cell->value);
1107 
1108  can_edit = enter (cell, cursor_position, start_selection, end_selection);
1109 
1110  if (g_strcmp0 (old_value, cell->value) != 0)
1111  {
1112  if (gnc_table_model_read_only (table->model))
1113  {
1114  PWARN ("enter update changed read-only table");
1115  }
1116 
1117  cell->changed = TRUE;
1118  }
1119 
1120  g_free (old_value);
1121  }
1122 
1123  if (table->gui_handlers.redraw_help)
1124  table->gui_handlers.redraw_help (table);
1125 
1126  LEAVE("return %d\n", can_edit);
1127  return can_edit;
1128 }
1129 
1130 void
1131 gnc_table_leave_update (Table *table, VirtualLocation virt_loc)
1132 {
1133  CellLeaveFunc leave;
1134  BasicCell *cell;
1135  CellBlock *cb;
1136  int cell_row;
1137  int cell_col;
1138 
1139  if (table == NULL)
1140  return;
1141 
1142  cb = table->current_cursor;
1143 
1144  cell_row = virt_loc.phys_row_offset;
1145  cell_col = virt_loc.phys_col_offset;
1146 
1147  ENTER("proposed (%d %d) rel(%d %d)\n",
1148  virt_loc.vcell_loc.virt_row,
1149  virt_loc.vcell_loc.virt_col,
1150  cell_row, cell_col);
1151 
1152  /* OK, if there is a callback for this cell, call it */
1153  cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
1154  if (!cell)
1155  {
1156  LEAVE("no cell");
1157  return;
1158  }
1159 
1160  leave = cell->leave_cell;
1161 
1162  if (leave)
1163  {
1164  char * old_value;
1165 
1166  old_value = g_strdup (cell->value);
1167 
1168  leave (cell);
1169 
1170  if (g_strcmp0 (old_value, cell->value) != 0)
1171  {
1172  if (gnc_table_model_read_only (table->model))
1173  {
1174  PWARN ("leave update changed read-only table");
1175  }
1176 
1177  cell->changed = TRUE;
1178  }
1179 
1180  g_free (old_value);
1181  }
1182  LEAVE("");
1183 }
1184 
1185 gboolean
1186 gnc_table_confirm_change (Table *table, VirtualLocation virt_loc)
1187 {
1188  TableConfirmHandler confirm_handler;
1189  const char *cell_name;
1190 
1191  if (!table || !table->model)
1192  return TRUE;
1193 
1194  cell_name = gnc_table_get_cell_name (table, virt_loc);
1195 
1196  confirm_handler = gnc_table_model_get_confirm_handler (table->model,
1197  cell_name);
1198  if (!confirm_handler)
1199  return TRUE;
1200 
1201  return confirm_handler (virt_loc, table->model->handler_user_data);
1202 }
1203 
1204 /* Returned result should not be touched by the caller.
1205  * NULL return value means the edit was rejected. */
1206 const char *
1207 gnc_table_modify_update (Table *table,
1208  VirtualLocation virt_loc,
1209  const char *change,
1210  int change_len,
1211  const char *newval,
1212  int newval_len,
1213  int *cursor_position,
1214  int *start_selection,
1215  int *end_selection,
1216  gboolean *cancelled)
1217 {
1218  gboolean changed = FALSE;
1219  CellModifyVerifyFunc mv;
1220  BasicCell *cell;
1221  CellBlock *cb;
1222  int cell_row;
1223  int cell_col;
1224  char * old_value;
1225 
1226  g_return_val_if_fail (table, NULL);
1227  g_return_val_if_fail (table->model, NULL);
1228 
1229  if (gnc_table_model_read_only (table->model))
1230  {
1231  PWARN ("change to read-only table");
1232  return NULL;
1233  }
1234 
1235  cb = table->current_cursor;
1236 
1237  cell_row = virt_loc.phys_row_offset;
1238  cell_col = virt_loc.phys_col_offset;
1239 
1240  ENTER ("");
1241 
1242  if (!gnc_table_confirm_change (table, virt_loc))
1243  {
1244  if (cancelled)
1245  *cancelled = TRUE;
1246 
1247  LEAVE("change cancelled");
1248  return NULL;
1249  }
1250 
1251  if (cancelled)
1252  *cancelled = FALSE;
1253 
1254  /* OK, if there is a callback for this cell, call it */
1255  cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
1256  if (!cell)
1257  {
1258  LEAVE("no cell");
1259  return NULL;
1260  }
1261 
1262  mv = cell->modify_verify;
1263 
1264  old_value = g_strdup (cell->value);
1265 
1266  if (mv)
1267  {
1268  mv (cell, change, change_len, newval, newval_len,
1269  cursor_position, start_selection, end_selection);
1270  }
1271  else
1272  {
1273  gnc_basic_cell_set_value (cell, newval);
1274  }
1275 
1276  if (g_strcmp0 (old_value, cell->value) != 0)
1277  {
1278  changed = TRUE;
1279  cell->changed = TRUE;
1280  }
1281 
1282  g_free (old_value);
1283 
1284  if (table->gui_handlers.redraw_help)
1285  table->gui_handlers.redraw_help (table);
1286 
1287  LEAVE ("change %d %d (relrow=%d relcol=%d) val=%s\n",
1288  virt_loc.vcell_loc.virt_row,
1289  virt_loc.vcell_loc.virt_col,
1290  cell_row, cell_col,
1291  cell->value ? cell->value : "(null)");
1292 
1293  if (changed)
1294  return cell->value;
1295  else
1296  return NULL;
1297 }
1298 
1299 gboolean
1300 gnc_table_direct_update (Table *table,
1301  VirtualLocation virt_loc,
1302  char **newval_ptr,
1303  int *cursor_position,
1304  int *start_selection,
1305  int *end_selection,
1306  gpointer gui_data)
1307 {
1308  gboolean result;
1309  BasicCell *cell;
1310  CellBlock *cb;
1311  int cell_row;
1312  int cell_col;
1313  char * old_value;
1314 
1315  g_return_val_if_fail (table, FALSE);
1316  g_return_val_if_fail (table->model, FALSE);
1317 
1318  if (gnc_table_model_read_only (table->model))
1319  {
1320  PWARN ("input to read-only table");
1321  return FALSE;
1322  }
1323 
1324  cb = table->current_cursor;
1325 
1326  cell_row = virt_loc.phys_row_offset;
1327  cell_col = virt_loc.phys_col_offset;
1328 
1329  cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
1330  if (!cell)
1331  return FALSE;
1332 
1333  ENTER ("");
1334 
1335  gboolean changed = cell->changed;
1336  old_value = g_strdup (cell->value);
1337 
1338  CellDirectUpdateFunc du = cell->direct_update;
1339 
1340  if (du == NULL)
1341  du = table->gui_handlers.default_direct_update;
1342 
1343  if (du == NULL)
1344  {
1345  LEAVE("no direct update");
1346  return FALSE;
1347  }
1348 
1349  result = du (cell, cursor_position, start_selection, end_selection, gui_data);
1350 
1351  if (g_strcmp0 (old_value, cell->value) != 0)
1352  {
1353  if (!gnc_table_confirm_change (table, virt_loc))
1354  {
1355  gnc_basic_cell_set_value (cell, old_value);
1356  *newval_ptr = NULL;
1357  result = TRUE;
1358  }
1359  else
1360  {
1361  if (!changed)
1362  cell->changed = TRUE;
1363  *newval_ptr = cell->value;
1364  }
1365  }
1366  else
1367  *newval_ptr = NULL;
1368 
1369  g_free (old_value);
1370 
1371  if (table->gui_handlers.redraw_help)
1372  table->gui_handlers.redraw_help (table);
1373 
1374  LEAVE("");
1375  return result;
1376 }
1377 
1378 static gboolean gnc_table_find_valid_cell_horiz (Table *table,
1379  VirtualLocation *virt_loc,
1380  gboolean exact_cell);
1381 
1382 static gboolean
1383 gnc_table_find_valid_row_vert (Table *table, VirtualLocation *virt_loc)
1384 {
1385  VirtualLocation vloc;
1386  VirtualCell *vcell = NULL;
1387  int top;
1388  int bottom;
1389 
1390  if (table == NULL)
1391  return FALSE;
1392 
1393  if (virt_loc == NULL)
1394  return FALSE;
1395 
1396  vloc = *virt_loc;
1397 
1398  if (vloc.vcell_loc.virt_row < 1)
1399  vloc.vcell_loc.virt_row = 1;
1400  if (vloc.vcell_loc.virt_row >= table->num_virt_rows)
1401  vloc.vcell_loc.virt_row = table->num_virt_rows - 1;
1402 
1403  top = vloc.vcell_loc.virt_row;
1404  bottom = vloc.vcell_loc.virt_row + 1;
1405 
1406  while (top >= 1 || bottom < table->num_virt_rows)
1407  {
1408  vloc.vcell_loc.virt_row = top;
1409  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1410  if (vcell && vcell->cellblock && vcell->visible)
1411  {
1412  vloc.phys_row_offset = 0;
1413  vloc.phys_col_offset = 0;
1414 
1415  if (gnc_table_find_valid_cell_horiz (table, &vloc, FALSE))
1416  break;
1417  }
1418 
1419  vloc.vcell_loc.virt_row = bottom;
1420  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1421  if (vcell && vcell->cellblock && vcell->visible)
1422  {
1423  vloc.phys_row_offset = 0;
1424  vloc.phys_col_offset = 0;
1425 
1426  if (gnc_table_find_valid_cell_horiz (table, &vloc, FALSE))
1427  break;
1428  }
1429 
1430  top--;
1431  bottom++;
1432  }
1433 
1434  if (!vcell || !vcell->cellblock || !vcell->visible)
1435  return FALSE;
1436 
1437  if (vloc.phys_row_offset < 0)
1438  vloc.phys_row_offset = 0;
1439  if (vloc.phys_row_offset >= vcell->cellblock->num_rows)
1440  vloc.phys_row_offset = vcell->cellblock->num_rows - 1;
1441 
1442  virt_loc->vcell_loc = vloc.vcell_loc;
1443 
1444  return TRUE;
1445 }
1446 
1447 static gboolean
1448 gnc_table_find_valid_cell_horiz (Table *table,
1449  VirtualLocation *virt_loc,
1450  gboolean exact_cell)
1451 {
1452  VirtualLocation vloc;
1453  VirtualCell *vcell;
1454  int left;
1455  int right;
1456 
1457  if (table == NULL)
1458  return FALSE;
1459 
1460  if (virt_loc == NULL)
1461  return FALSE;
1462 
1463  if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc->vcell_loc))
1464  return FALSE;
1465 
1466  if (gnc_table_virtual_loc_valid (table, *virt_loc, exact_cell))
1467  return TRUE;
1468 
1469  vloc = *virt_loc;
1470 
1471  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1472  if (vcell == NULL)
1473  return FALSE;
1474  if (vcell->cellblock == NULL)
1475  return FALSE;
1476 
1477  if (vloc.phys_col_offset < 0)
1478  vloc.phys_col_offset = 0;
1479  if (vloc.phys_col_offset >= vcell->cellblock->num_cols)
1480  vloc.phys_col_offset = vcell->cellblock->num_cols - 1;
1481 
1482  left = vloc.phys_col_offset - 1;
1483  right = vloc.phys_col_offset + 1;
1484 
1485  while (left >= 0 || right < vcell->cellblock->num_cols)
1486  {
1487  vloc.phys_col_offset = right;
1488  if (gnc_table_virtual_loc_valid(table, vloc, FALSE))
1489  {
1490  *virt_loc = vloc;
1491  return TRUE;
1492  }
1493 
1494  vloc.phys_col_offset = left;
1495  if (gnc_table_virtual_loc_valid(table, vloc, FALSE))
1496  {
1497  *virt_loc = vloc;
1498  return TRUE;
1499  }
1500 
1501  left--;
1502  right++;
1503  }
1504 
1505  return FALSE;
1506 }
1507 
1508 gboolean
1509 gnc_table_find_close_valid_cell (Table *table, VirtualLocation *virt_loc,
1510  gboolean exact_pointer)
1511 {
1512  if (!gnc_table_find_valid_row_vert (table, virt_loc))
1513  return FALSE;
1514 
1515  return gnc_table_find_valid_cell_horiz (table, virt_loc, exact_pointer);
1516 }
1517 
1518 void
1520  VirtualCellLocation vcell_loc,
1521  gboolean do_scroll)
1522 {
1523  g_return_if_fail (table != NULL);
1524  g_return_if_fail (table->gui_handlers.cursor_refresh != NULL);
1525 
1526  table->gui_handlers.cursor_refresh (table, vcell_loc, do_scroll);
1527 }
1528 
1529 gboolean
1530 gnc_table_move_tab (Table *table,
1531  VirtualLocation *virt_loc,
1532  gboolean move_right)
1533 {
1534  VirtualCell *vcell;
1535  VirtualLocation vloc;
1536  BasicCell *cell;
1537 
1538  if ((table == NULL) || (virt_loc == NULL))
1539  return FALSE;
1540 
1541  vloc = *virt_loc;
1542 
1543  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1544  if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
1545  return FALSE;
1546 
1547  while (1)
1548  {
1549  CellIOFlags io_flags;
1550 
1551  if (move_right)
1552  {
1553  vloc.phys_col_offset++;
1554 
1555  if (vloc.phys_col_offset >= vcell->cellblock->num_cols)
1556  {
1557  if (!gnc_table_move_vertical_position (table, &vloc, 1))
1558  return FALSE;
1559 
1560  vloc.phys_col_offset = 0;
1561  }
1562  }
1563  else
1564  {
1565  vloc.phys_col_offset--;
1566 
1567  if (vloc.phys_col_offset < 0)
1568  {
1569  if (!gnc_table_move_vertical_position (table, &vloc, -1))
1570  return FALSE;
1571 
1572  vloc.phys_col_offset = vcell->cellblock->num_cols - 1;
1573  }
1574  }
1575 
1576  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1577  if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
1578  return FALSE;
1579 
1580  cell = gnc_cellblock_get_cell (vcell->cellblock,
1581  vloc.phys_row_offset,
1582  vloc.phys_col_offset);
1583  if (!cell)
1584  continue;
1585 
1586  io_flags = gnc_table_get_io_flags (table, vloc);
1587 
1588  if (!(io_flags & XACC_CELL_ALLOW_INPUT))
1589  continue;
1590 
1591  if (io_flags & XACC_CELL_ALLOW_EXACT_ONLY)
1592  continue;
1593 
1594  break;
1595  }
1596 
1597  {
1598  gboolean changed = !virt_loc_equal (vloc, *virt_loc);
1599 
1600  *virt_loc = vloc;
1601 
1602  return changed;
1603  }
1604 }
1605 
1606 gboolean
1608  VirtualLocation *virt_loc,
1609  int phys_row_offset)
1610 {
1611  VirtualLocation vloc;
1612  VirtualCell *vcell;
1613  gint last_visible_row;
1614 
1615  if ((table == NULL) || (virt_loc == NULL))
1616  return FALSE;
1617 
1618  vloc = *virt_loc;
1619  last_visible_row = vloc.vcell_loc.virt_row;
1620 
1621  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1622  if ((vcell == NULL) || (vcell->cellblock == NULL))
1623  return FALSE;
1624 
1625  while (phys_row_offset != 0)
1626  {
1627  /* going up */
1628  if (phys_row_offset < 0)
1629  {
1630  phys_row_offset++;
1631 
1632  /* room left in the current cursor */
1633  if (vloc.phys_row_offset > 0)
1634  {
1635  vloc.phys_row_offset--;
1636  continue;
1637  }
1638 
1639  /* end of the line */
1640  if (vloc.vcell_loc.virt_row == 1)
1641  break;
1642 
1643  do
1644  {
1645  vloc.vcell_loc.virt_row--;
1646 
1647  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1648  }
1649  while (vcell && vcell->cellblock && !vcell->visible);
1650 
1651  if (!vcell || !vcell->cellblock)
1652  break;
1653 
1654  last_visible_row = vloc.vcell_loc.virt_row;
1655  vloc.phys_row_offset = vcell->cellblock->num_rows - 1;
1656  }
1657  /* going down */
1658  else
1659  {
1660  phys_row_offset--;
1661 
1662  /* room left in the current cursor */
1663  if (vloc.phys_row_offset < (vcell->cellblock->num_rows - 1))
1664  {
1665  vloc.phys_row_offset++;
1666  continue;
1667  }
1668 
1669  /* end of the line */
1670  if (vloc.vcell_loc.virt_row == (table->num_virt_rows - 1))
1671  break;
1672 
1673  do
1674  {
1675  vloc.vcell_loc.virt_row++;
1676 
1677  vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
1678  }
1679  while (vcell && vcell->cellblock && !vcell->visible);
1680 
1681  if (!vcell || !vcell->cellblock)
1682  break;
1683 
1684  last_visible_row = vloc.vcell_loc.virt_row;
1685  vloc.phys_row_offset = 0;
1686  }
1687  }
1688 
1689  vloc.vcell_loc.virt_row = last_visible_row;
1690 
1691  {
1692  gboolean changed = !virt_loc_equal (vloc, *virt_loc);
1693 
1694  *virt_loc = vloc;
1695 
1696  return changed;
1697  }
1698 }
1699 
1700 gboolean
1701 gnc_table_traverse_update(Table *table,
1702  VirtualLocation virt_loc,
1703  gncTableTraversalDir dir,
1704  VirtualLocation *dest_loc)
1705 {
1706  gboolean abort_move;
1707 
1708  if ((table == NULL) || (dest_loc == NULL))
1709  return FALSE;
1710 
1711  ENTER("proposed (%d %d) -> (%d %d)\n",
1712  virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_row,
1713  dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col);
1714 
1715  /* first, make sure our destination cell is valid. If it is out
1716  * of bounds report an error. I don't think this ever happens. */
1717  if (gnc_table_virtual_cell_out_of_bounds (table, dest_loc->vcell_loc))
1718  {
1719  PERR("destination (%d, %d) out of bounds (%d, %d)\n",
1720  dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col,
1721  table->num_virt_rows, table->num_virt_cols);
1722  LEAVE("");
1723  return TRUE;
1724  }
1725 
1726  /* next, check the current row and column. If they are out of bounds
1727  * we can recover by treating the traversal as a mouse point. This can
1728  * occur whenever the register widget is resized smaller, maybe?. */
1729  if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE))
1730  {
1731  PINFO("source (%d, %d) out of bounds (%d, %d)\n",
1732  virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_col,
1733  table->num_virt_rows, table->num_virt_cols);
1734 
1735  dir = GNC_TABLE_TRAVERSE_POINTER;
1736  }
1737 
1738  /* process forward-moving traversals */
1739  switch (dir)
1740  {
1741  case GNC_TABLE_TRAVERSE_RIGHT:
1742  case GNC_TABLE_TRAVERSE_LEFT:
1743  gnc_table_find_valid_cell_horiz(table, dest_loc, FALSE);
1744 
1745  break;
1746 
1747  case GNC_TABLE_TRAVERSE_UP:
1748  case GNC_TABLE_TRAVERSE_DOWN:
1749  {
1750  VirtualLocation new_loc = *dest_loc;
1751  int increment;
1752  int col_offset = 0;
1753  gboolean second_traversal = FALSE;
1754 
1755  /* Keep going in the specified direction until we find a valid
1756  * row to land on, or we hit the end of the table. At the end,
1757  * turn around and go back until we find a valid row or we get
1758  * to where we started. If we still can't find anything, try
1759  * going left and right. */
1760  increment = (dir == GNC_TABLE_TRAVERSE_DOWN) ? 1 : -1;
1761 
1762  while (!gnc_table_virtual_loc_valid(table, new_loc, FALSE))
1763  {
1764  if (virt_loc_equal (new_loc, virt_loc))
1765  {
1766  new_loc = *dest_loc;
1767  gnc_table_find_valid_cell_horiz(table, &new_loc, FALSE);
1768  break;
1769  }
1770 
1771  if (!gnc_table_move_vertical_position (table, &new_loc, increment))
1772  {
1773  /* Special case: if there is no valid cell at all in the column
1774  * we are scanning, (both up and down directions didn't work)
1775  * attempt to do the same in the next column.
1776  * Hack alert: there is no check to see if there really is a
1777  * valid next column. However this situation so far only happens
1778  * after a pagedown/pageup key event in the SX transaction editor
1779  * which always tests the first column to start (which has no
1780  * editable cells) and in that situation there is a valid next column.
1781  */
1782  if (!second_traversal)
1783  second_traversal = TRUE;
1784  else
1785  {
1786  second_traversal = FALSE;
1787  col_offset++;
1788  }
1789  increment *= -1;
1790  new_loc = *dest_loc;
1791  new_loc.phys_col_offset = new_loc.phys_col_offset + col_offset;
1792  }
1793  }
1794 
1795  *dest_loc = new_loc;
1796  }
1797 
1798  if (!gnc_table_virtual_loc_valid(table, *dest_loc, FALSE))
1799  {
1800  LEAVE("");
1801  return TRUE;
1802  }
1803 
1804  break;
1805 
1806  case GNC_TABLE_TRAVERSE_POINTER:
1807  if (!gnc_table_find_valid_cell_horiz(table, dest_loc, TRUE))
1808  {
1809  LEAVE("");
1810  return TRUE;
1811  }
1812 
1813  break;
1814 
1815  default:
1816  g_return_val_if_fail (FALSE, TRUE);
1817  break;
1818  }
1819 
1820  /* Call the table traverse callback for any modifications. */
1821  if (table->control->traverse)
1822  abort_move = table->control->traverse (dest_loc, dir,
1823  table->control->user_data);
1824  else
1825  abort_move = FALSE;
1826 
1827  LEAVE("dest_row = %d, dest_col = %d\n",
1828  dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col);
1829 
1830  return abort_move;
1831 }
gpointer vcell_data
Array of physical cells.
Definition: table-allgui.h:135
GTable * g_table_new(guint entry_size, g_table_entry_constructor constructor, g_table_entry_destroyer destroyer, gpointer user_data)
Create a new table with the given entry constructor and destroyer.
Definition: gtable.c:44
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gpointer gnc_table_get_vcell_data(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell data associated with a cursor located at the given virtual coords...
Definition: table-allgui.c:931
void gnc_table_set_virt_cell_visible(Table *table, VirtualCellLocation vcell_loc, gboolean visible)
Set the visibility flag for a particular location.
Definition: table-allgui.c:719
gpointer g_table_index(GTable *gtable, int row, int col)
Return the element at the given row and column.
Definition: gtable.c:84
holds information about each virtual cell.
Definition: table-allgui.h:132
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_table_move_cursor_gui(Table *table, VirtualLocation new_virt_loc)
will move the cursor and its GUI to the indicated location.
Definition: table-allgui.c:886
gboolean gnc_table_find_close_valid_cell(Table *table, VirtualLocation *virt_loc, gboolean exact_pointer)
Find a close valid cell.
void gnc_cellblock_clear_changes(CellBlock *cursor)
Sets all cells in the cellblock to not changed.
Definition: cellblock.c:189
void gnc_table_set_size(Table *table, int virt_rows, int virt_cols)
The gnc_table_set_size() method will resize the table to the indicated dimensions.
Definition: table-allgui.c:586
VirtualCell * gnc_table_get_header_cell(Table *table)
Return the virtual cell of the header.
Definition: table-allgui.c:237
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_table_set_virt_cell_data(Table *table, VirtualCellLocation vcell_loc, gconstpointer vcell_data)
Set the virtual cell data for a particular location.
Definition: table-allgui.c:699
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
VirtualCell * gnc_table_get_virtual_cell(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell associated with a particular virtual location.
Definition: table-allgui.c:227
void gnc_table_set_default_gui_handlers(TableGUIHandlers *gui_handlers)
Implementation.
Definition: table-allgui.c:67
gboolean gnc_table_verify_cursor_position(Table *table, VirtualLocation virt_loc)
checks the location of the cursor with respect to a virtual location position, and if the resulting v...
Definition: table-allgui.c:897
unsigned int start_primary_color
visible in the GUI
Definition: table-allgui.h:139
void gnc_table_set_vcell(Table *table, CellBlock *cursor, gconstpointer vcell_data, gboolean visible, gboolean start_primary_color, VirtualCellLocation vcell_loc)
Indicate what handler should be used for a given virtual block.
Definition: table-allgui.c:663
gboolean gnc_table_move_vertical_position(Table *table, VirtualLocation *virt_loc, int phys_row_offset)
Moves away from virtual location virt_loc by phys_row_offset physical rows.
All type declarations for the whole Gnucash engine.
unsigned int visible
Used by higher-level code.
Definition: table-allgui.h:138
void g_table_destroy(GTable *gtable)
Free the table and all associated table elements.
Definition: gtable.c:69
void gnc_table_refresh_cursor_gui(Table *table, VirtualCellLocation vcell_loc, gboolean do_scroll)
Refresh the cursor in the given location.
void gnc_table_refresh_current_cursor_gui(Table *table, gboolean do_scroll)
Refresh the current cursor gui.
Definition: table-allgui.c:994
void gnc_table_set_virt_cell_cursor(Table *table, VirtualCellLocation vcell_loc, CellBlock *cursor)
Set the cellblock handler for a virtual cell.
Definition: table-allgui.c:736
gboolean gnc_table_virtual_cell_out_of_bounds(Table *table, VirtualCellLocation vcell_loc)
checks the given location and returns true if it is out of bounds of the table.
Definition: table-allgui.c:207
Declarations for the Table object.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void g_table_resize(GTable *gtable, int rows, int cols)
Resize the table, allocating and deallocating extra table members if needed.
Definition: gtable.c:104
Declarations for the CellBlock object.
void gnc_table_move_cursor(Table *table, VirtualLocation new_virt_loc)
will move the cursor (but not the cursor GUI) to the indicated location.
Definition: table-allgui.c:877
int gnc_cellblock_changed(CellBlock *cursor, gboolean include_conditional)
Return number of changed cells.
Definition: cellblock.c:157
BasicCell * gnc_cellblock_get_cell(CellBlock *cellblock, int row, int col)
Retrieve the Cell at the specified coordinates.
Definition: cellblock.c:109