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