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