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