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