GnuCash 2.4.99
table-allgui.c
00001 /********************************************************************\
00002  * table-allgui.c -- 2D grid table object, embeds cells for i/o     *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020  *                                                                  *
00021 \********************************************************************/
00022 
00023 /*
00024  * FILE:
00025  * table-allgui.c
00026  *
00027  * FUNCTION:
00028  * Implements the gui-independent parts of the table infrastructure.
00029  *
00030  * HISTORY:
00031  * Copyright (c) 1998,1999,2000 Linas Vepstas
00032  * Copyright (c) 2000 Dave Peticolas
00033  */
00034 
00035 #include "config.h"
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 
00041 #include <glib.h>
00042 
00043 #include "table-allgui.h"
00044 #include "cellblock.h"
00045 #include "gnc-engine.h"
00046 
00047 
00050 static TableGUIHandlers default_gui_handlers;
00051 
00052 /* This static indicates the debugging module that this .o belongs to. */
00053 static QofLogModule log_module = GNC_MOD_REGISTER;
00054 
00055 
00057 static void gnc_table_init (Table * table);
00058 static void gnc_table_free_data (Table * table);
00059 static void gnc_virtual_cell_construct (gpointer vcell, gpointer user_data);
00060 static void gnc_virtual_cell_destroy (gpointer vcell, gpointer user_data);
00061 static void gnc_table_resize (Table * table, int virt_rows, int virt_cols);
00062 
00063 
00066 void
00067 gnc_table_set_default_gui_handlers (TableGUIHandlers *gui_handlers)
00068 {
00069     if (!gui_handlers)
00070         memset (&default_gui_handlers, 0, sizeof (default_gui_handlers));
00071     else
00072         default_gui_handlers = *gui_handlers;
00073 }
00074 
00075 Table *
00076 gnc_table_new (TableLayout *layout, TableModel *model, TableControl *control)
00077 {
00078     Table *table;
00079 
00080     g_return_val_if_fail (layout != NULL, NULL);
00081     g_return_val_if_fail (model != NULL, NULL);
00082     g_return_val_if_fail (control != NULL, NULL);
00083 
00084     table = g_new0 (Table, 1);
00085 
00086     table->layout = layout;
00087     table->model = model;
00088     table->control = control;
00089 
00090     table->gui_handlers = default_gui_handlers;
00091 
00092     gnc_table_init (table);
00093 
00094     table->virt_cells = g_table_new (sizeof (VirtualCell),
00095                                      gnc_virtual_cell_construct,
00096                                      gnc_virtual_cell_destroy, table);
00097 
00098     return table;
00099 }
00100 
00101 static void
00102 gnc_table_init (Table * table)
00103 {
00104     table->num_virt_rows = -1;
00105     table->num_virt_cols = -1;
00106 
00107     table->current_cursor = NULL;
00108 
00109     gnc_virtual_location_init (&table->current_cursor_loc);
00110 
00111     /* initialize private data */
00112 
00113     table->virt_cells = NULL;
00114     table->ui_data = NULL;
00115 }
00116 
00117 void
00118 gnc_table_destroy (Table * table)
00119 {
00120     /* invoke destroy callback */
00121     if (table->gui_handlers.destroy)
00122         table->gui_handlers.destroy (table);
00123 
00124     /* free the dynamic structures */
00125     gnc_table_free_data (table);
00126 
00127     /* free the cell tables */
00128     g_table_destroy (table->virt_cells);
00129 
00130     gnc_table_layout_destroy (table->layout);
00131     table->layout = NULL;
00132 
00133     gnc_table_control_destroy (table->control);
00134     table->control = NULL;
00135 
00136     gnc_table_model_destroy (table->model);
00137     table->model = NULL;
00138 
00139     /* intialize vars to null value so that any access is voided. */
00140     gnc_table_init (table);
00141 
00142     g_free (table);
00143 }
00144 
00145 int
00146 gnc_table_current_cursor_changed (Table *table,
00147                                   gboolean include_conditional)
00148 {
00149     if (!table)
00150         return FALSE;
00151 
00152     return gnc_cellblock_changed (table->current_cursor, include_conditional);
00153 }
00154 
00155 void
00156 gnc_table_clear_current_cursor_changes (Table *table)
00157 {
00158     if (!table)
00159         return;
00160 
00161     gnc_cellblock_clear_changes (table->current_cursor);
00162 }
00163 
00164 void
00165 gnc_table_save_current_cursor (Table *table, CursorBuffer *buffer)
00166 {
00167     if (!table || !buffer)
00168         return;
00169 
00170     gnc_table_layout_save_cursor (table->layout, table->current_cursor, buffer);
00171 }
00172 
00173 void
00174 gnc_table_restore_current_cursor (Table *table,
00175                                   CursorBuffer *buffer)
00176 {
00177     if (!table || !buffer)
00178         return;
00179 
00180     gnc_table_layout_restore_cursor (table->layout,
00181                                      table->current_cursor, buffer);
00182 }
00183 
00184 const char *
00185 gnc_table_get_current_cell_name (Table *table)
00186 {
00187     if (table == NULL)
00188         return NULL;
00189 
00190     return gnc_table_get_cell_name (table, table->current_cursor_loc);
00191 }
00192 
00193 gboolean
00194 gnc_table_get_current_cell_location (Table *table,
00195                                      const char *cell_name,
00196                                      VirtualLocation *virt_loc)
00197 {
00198     if (table == NULL)
00199         return FALSE;
00200 
00201     return gnc_table_get_cell_location (table, cell_name,
00202                                         table->current_cursor_loc.vcell_loc,
00203                                         virt_loc);
00204 }
00205 
00206 gboolean
00207 gnc_table_virtual_cell_out_of_bounds (Table *table,
00208                                       VirtualCellLocation vcell_loc)
00209 {
00210     if (!table)
00211         return TRUE;
00212 
00213     return ((vcell_loc.virt_row < 0) ||
00214             (vcell_loc.virt_row >= table->num_virt_rows) ||
00215             (vcell_loc.virt_col < 0) ||
00216             (vcell_loc.virt_col >= table->num_virt_cols));
00217 }
00218 
00219 gboolean
00220 gnc_table_virtual_location_in_header (Table *table,
00221                                       VirtualLocation virt_loc)
00222 {
00223     return (virt_loc.vcell_loc.virt_row == 0);
00224 }
00225 
00226 VirtualCell *
00227 gnc_table_get_virtual_cell (Table *table, VirtualCellLocation vcell_loc)
00228 {
00229     if (table == NULL)
00230         return NULL;
00231 
00232     return g_table_index (table->virt_cells,
00233                           vcell_loc.virt_row, vcell_loc.virt_col);
00234 }
00235 
00236 VirtualCell *
00237 gnc_table_get_header_cell (Table *table)
00238 {
00239     VirtualCellLocation vcell_loc = { 0, 0 };
00240 
00241     return gnc_table_get_virtual_cell (table, vcell_loc);
00242 }
00243 
00244 static const char *
00245 gnc_table_get_entry_internal (Table *table, VirtualLocation virt_loc,
00246                               gboolean *conditionally_changed)
00247 {
00248     TableGetEntryHandler entry_handler;
00249     const char *cell_name;
00250     const char *entry;
00251 
00252     cell_name = gnc_table_get_cell_name (table, virt_loc);
00253 
00254     entry_handler = gnc_table_model_get_entry_handler (table->model, cell_name);
00255     if (!entry_handler) return "";
00256 
00257     entry = entry_handler (virt_loc, FALSE,
00258                            conditionally_changed,
00259                            table->model->handler_user_data);
00260     if (!entry)
00261         entry = "";
00262 
00263     return entry;
00264 }
00265 
00266 const char *
00267 gnc_table_get_entry (Table *table, VirtualLocation virt_loc)
00268 {
00269     TableGetEntryHandler entry_handler;
00270     const char *entry;
00271     BasicCell *cell;
00272 
00273     cell = gnc_table_get_cell (table, virt_loc);
00274     if (!cell || !cell->cell_name)
00275         return "";
00276 
00277     if (virt_cell_loc_equal (table->current_cursor_loc.vcell_loc,
00278                              virt_loc.vcell_loc))
00279     {
00280         CellIOFlags io_flags;
00281 
00282         io_flags = gnc_table_get_io_flags (table, virt_loc);
00283 
00284         if (io_flags & XACC_CELL_ALLOW_INPUT)
00285             return cell->value;
00286     }
00287 
00288     entry_handler = gnc_table_model_get_entry_handler (table->model,
00289                     cell->cell_name);
00290     if (!entry_handler) return "";
00291 
00292     entry = entry_handler (virt_loc, TRUE, NULL,
00293                            table->model->handler_user_data);
00294     if (!entry)
00295         entry = "";
00296 
00297     return entry;
00298 }
00299 
00300 CellIOFlags
00301 gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc)
00302 {
00303     TableGetCellIOFlagsHandler io_flags_handler;
00304     const char *cell_name;
00305     CellIOFlags flags;
00306 
00307     if (!table || !table->model)
00308         return XACC_CELL_ALLOW_NONE;
00309 
00310     cell_name = gnc_table_get_cell_name (table, virt_loc);
00311 
00312     io_flags_handler = gnc_table_model_get_io_flags_handler (table->model,
00313                        cell_name);
00314     if (!io_flags_handler)
00315         return XACC_CELL_ALLOW_NONE;
00316 
00317     flags = io_flags_handler (virt_loc, table->model->handler_user_data);
00318 
00319     if (gnc_table_model_read_only (table->model))
00320         flags &= XACC_CELL_ALLOW_SHADOW;
00321 
00322     return flags;
00323 }
00324 
00325 const char *
00326 gnc_table_get_label (Table *table, VirtualLocation virt_loc)
00327 {
00328     TableGetLabelHandler label_handler;
00329     const char *cell_name;
00330     const char *label;
00331 
00332     if (!table || !table->model)
00333         return "";
00334 
00335     cell_name = gnc_table_get_cell_name (table, virt_loc);
00336 
00337     label_handler = gnc_table_model_get_label_handler (table->model, cell_name);
00338     if (!label_handler)
00339         return "";
00340 
00341     label = label_handler (virt_loc, table->model->handler_user_data);
00342     if (!label)
00343         return "";
00344 
00345     return label;
00346 }
00347 
00348 guint32
00349 gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
00350 {
00351     TableGetFGColorHandler fg_color_handler;
00352     const char *cell_name;
00353 
00354     if (!table || !table->model)
00355         return 0x0; /* black */
00356 
00357     cell_name = gnc_table_get_cell_name (table, virt_loc);
00358 
00359     fg_color_handler = gnc_table_model_get_fg_color_handler (table->model,
00360                        cell_name);
00361     if (!fg_color_handler)
00362         return 0x0;
00363 
00364     return fg_color_handler (virt_loc, table->model->handler_user_data);
00365 }
00366 
00367 guint32
00368 gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
00369                         gboolean *hatching)
00370 {
00371     TableGetBGColorHandler bg_color_handler;
00372     const char *cell_name;
00373 
00374     if (hatching)
00375         *hatching = FALSE;
00376 
00377     if (!table || !table->model)
00378         return 0xffffff; /* white */
00379 
00380     cell_name = gnc_table_get_cell_name (table, virt_loc);
00381 
00382     bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
00383                        cell_name);
00384     if (!bg_color_handler)
00385         return 0xffffff;
00386 
00387     return bg_color_handler (virt_loc, hatching,
00388                              table->model->handler_user_data);
00389 }
00390 
00391 guint32
00392 gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
00393                               gboolean *hatching)
00394 {
00395     TableGetBGColorHandler bg_color_handler;
00396     const char *cell_name;
00397 
00398     if (hatching)
00399         *hatching = FALSE;
00400 
00401     if (!table || !table->model)
00402         return 0xffffff; /* white */
00403 
00404     cell_name = gnc_table_get_cell_name (table, virt_loc);
00405 
00406     bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
00407                        "gtkrc");
00408     if (!bg_color_handler)
00409         return 0xffffff;
00410 
00411     return bg_color_handler (virt_loc, hatching,
00412                              table->model->handler_user_data);
00413 }
00414 
00415 void
00416 gnc_table_get_borders (Table *table, VirtualLocation virt_loc,
00417                        PhysicalCellBorders *borders)
00418 {
00419     TableGetCellBorderHandler cell_border_handler;
00420     const char *cell_name;
00421 
00422     if (!table || !table->model)
00423         return;
00424 
00425     cell_name = gnc_table_get_cell_name (table, virt_loc);
00426 
00427     cell_border_handler = gnc_table_model_get_cell_border_handler (table->model,
00428                           cell_name);
00429     if (!cell_border_handler)
00430         return;
00431 
00432     cell_border_handler (virt_loc, borders, table->model->handler_user_data);
00433 }
00434 
00435 CellAlignment
00436 gnc_table_get_align (Table *table, VirtualLocation virt_loc)
00437 {
00438     BasicCell *cell;
00439 
00440     cell = gnc_table_get_cell (table, virt_loc);
00441     if (!cell)
00442         return CELL_ALIGN_RIGHT;
00443 
00444     return cell->alignment;
00445 }
00446 
00447 gboolean
00448 gnc_table_is_popup (Table *table, VirtualLocation virt_loc)
00449 {
00450     BasicCell *cell;
00451 
00452     cell = gnc_table_get_cell (table, virt_loc);
00453     if (!cell)
00454         return FALSE;
00455 
00456     return cell->is_popup;
00457 }
00458 
00459 char *
00460 gnc_table_get_help (Table *table)
00461 {
00462     TableGetHelpHandler help_handler;
00463     VirtualLocation virt_loc;
00464     const char * cell_name;
00465 
00466     if (!table)
00467         return NULL;
00468 
00469     virt_loc = table->current_cursor_loc;
00470 
00471     cell_name = gnc_table_get_cell_name (table, virt_loc);
00472 
00473     help_handler = gnc_table_model_get_help_handler (table->model, cell_name);
00474     if (!help_handler)
00475         return NULL;
00476 
00477     return help_handler (virt_loc, table->model->handler_user_data);
00478 }
00479 
00480 BasicCell *
00481 gnc_table_get_cell (Table *table, VirtualLocation virt_loc)
00482 {
00483     VirtualCell *vcell;
00484 
00485     if (!table)
00486         return NULL;
00487 
00488     vcell = gnc_table_get_virtual_cell (table, virt_loc.vcell_loc);
00489     if (!vcell)
00490         return NULL;
00491 
00492     return gnc_cellblock_get_cell (vcell->cellblock,
00493                                    virt_loc.phys_row_offset,
00494                                    virt_loc.phys_col_offset);
00495 }
00496 
00497 const char *
00498 gnc_table_get_cell_name (Table *table, VirtualLocation virt_loc)
00499 {
00500     BasicCell *cell;
00501 
00502     cell = gnc_table_get_cell (table, virt_loc);
00503     if (cell == NULL)
00504         return NULL;
00505 
00506     return cell->cell_name;
00507 }
00508 
00509 const gchar *
00510 gnc_table_get_cell_type_name (Table *table, VirtualLocation virt_loc)
00511 {
00512     BasicCell *cell;
00513 
00514     cell = gnc_table_get_cell (table, virt_loc);
00515     if (cell == NULL)
00516         return NULL;
00517 
00518     return cell->cell_type_name;
00519 }
00520 
00521 
00522 gboolean
00523 gnc_table_get_cell_location (Table *table,
00524                              const char *cell_name,
00525                              VirtualCellLocation vcell_loc,
00526                              VirtualLocation *virt_loc)
00527 {
00528     VirtualCell *vcell;
00529     CellBlock *cellblock;
00530     int cell_row, cell_col;
00531 
00532     if (table == NULL)
00533         return FALSE;
00534 
00535     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00536     if (vcell == NULL)
00537         return FALSE;
00538 
00539     cellblock = vcell->cellblock;
00540 
00541     for (cell_row = 0; cell_row < cellblock->num_rows; cell_row++)
00542         for (cell_col = 0; cell_col < cellblock->num_cols; cell_col++)
00543         {
00544             BasicCell *cell;
00545 
00546             cell = gnc_cellblock_get_cell (cellblock, cell_row, cell_col);
00547             if (!cell)
00548                 continue;
00549 
00550             if (gnc_basic_cell_has_name (cell, cell_name))
00551             {
00552                 if (virt_loc != NULL)
00553                 {
00554                     virt_loc->vcell_loc = vcell_loc;
00555 
00556                     virt_loc->phys_row_offset = cell_row;
00557                     virt_loc->phys_col_offset = cell_col;
00558                 }
00559 
00560                 return TRUE;
00561             }
00562         }
00563 
00564     return FALSE;
00565 }
00566 
00567 void
00568 gnc_table_save_cells (Table *table, gpointer save_data)
00569 {
00570     TableSaveHandler save_handler;
00571     GList * cells;
00572     GList * node;
00573 
00574     g_return_if_fail (table);
00575 
00576     /* ignore any changes to read-only tables */
00577     if (gnc_table_model_read_only (table->model))
00578         return;
00579 
00580     // gnc_table_leave_update (table, table->current_cursor_loc);
00581 
00582     save_handler = gnc_table_model_get_pre_save_handler (table->model);
00583     if (save_handler)
00584         save_handler (save_data, table->model->handler_user_data);
00585 
00586     cells = gnc_table_layout_get_cells (table->layout);
00587     for (node = cells; node; node = node->next)
00588     {
00589         BasicCell * cell = node->data;
00590         TableSaveCellHandler save_cell_handler;
00591 
00592         if (!cell) continue;
00593 
00594         if (!gnc_table_layout_get_cell_changed (table->layout,
00595                                                 cell->cell_name, TRUE))
00596             continue;
00597 
00598         save_cell_handler = gnc_table_model_get_save_handler (table->model,
00599                             cell->cell_name);
00600         if (save_cell_handler)
00601             save_cell_handler (cell, save_data, table->model->handler_user_data);
00602     }
00603 
00604     save_handler = gnc_table_model_get_post_save_handler (table->model);
00605     if (save_handler)
00606         save_handler (save_data, table->model->handler_user_data);
00607 }
00608 
00609 void
00610 gnc_table_set_size (Table * table, int virt_rows, int virt_cols)
00611 {
00612     /* Invalidate the current cursor position, if the array is
00613      * shrinking. This must be done since the table is probably
00614      * shrinking because some rows were deleted, and the cursor
00615      * could be on the deleted rows. */
00616     if ((virt_rows < table->num_virt_rows) ||
00617             (virt_cols < table->num_virt_cols))
00618     {
00619         gnc_virtual_location_init (&table->current_cursor_loc);
00620         table->current_cursor = NULL;
00621     }
00622 
00623     gnc_table_resize (table, virt_rows, virt_cols);
00624 }
00625 
00626 static void
00627 gnc_table_free_data (Table * table)
00628 {
00629     if (table == NULL)
00630         return;
00631 
00632     g_table_resize (table->virt_cells, 0, 0);
00633 }
00634 
00635 void
00636 gnc_virtual_location_init (VirtualLocation *vloc)
00637 {
00638     if (vloc == NULL)
00639         return;
00640 
00641     vloc->phys_row_offset = -1;
00642     vloc->phys_col_offset = -1;
00643     vloc->vcell_loc.virt_row = -1;
00644     vloc->vcell_loc.virt_col = -1;
00645 }
00646 
00647 static void
00648 gnc_virtual_cell_construct (gpointer _vcell, gpointer user_data)
00649 {
00650     VirtualCell *vcell = _vcell;
00651     Table *table = user_data;
00652 
00653     vcell->cellblock = NULL;
00654 
00655     if (table && table->model->cell_data_allocator)
00656         vcell->vcell_data = table->model->cell_data_allocator ();
00657     else
00658         vcell->vcell_data = NULL;
00659 
00660     vcell->visible = 1;
00661 }
00662 
00663 static void
00664 gnc_virtual_cell_destroy (gpointer _vcell, gpointer user_data)
00665 {
00666     VirtualCell *vcell = _vcell;
00667     Table *table = user_data;
00668 
00669     if (vcell->vcell_data && table && table->model->cell_data_deallocator)
00670         table->model->cell_data_deallocator (vcell->vcell_data);
00671 
00672     vcell->vcell_data = NULL;
00673 }
00674 
00675 static void
00676 gnc_table_resize (Table * table, int new_virt_rows, int new_virt_cols)
00677 {
00678     if (!table) return;
00679 
00680     g_table_resize (table->virt_cells, new_virt_rows, new_virt_cols);
00681 
00682     table->num_virt_rows = new_virt_rows;
00683     table->num_virt_cols = new_virt_cols;
00684 }
00685 
00686 void
00687 gnc_table_set_vcell (Table *table,
00688                      CellBlock *cursor,
00689                      gconstpointer vcell_data,
00690                      gboolean visible,
00691                      gboolean start_primary_color,
00692                      VirtualCellLocation vcell_loc)
00693 {
00694     VirtualCell *vcell;
00695 
00696     if ((table == NULL) || (cursor == NULL))
00697         return;
00698 
00699     if ((vcell_loc.virt_row >= table->num_virt_rows) ||
00700             (vcell_loc.virt_col >= table->num_virt_cols))
00701         gnc_table_resize (table,
00702                           MAX (table->num_virt_rows, vcell_loc.virt_row + 1),
00703                           MAX (table->num_virt_cols, vcell_loc.virt_col + 1));
00704 
00705     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00706     if (vcell == NULL)
00707         return;
00708 
00709     /* this cursor is the handler for this block */
00710     vcell->cellblock = cursor;
00711 
00712     /* copy the vcell user data */
00713     if (table->model->cell_data_copy)
00714         table->model->cell_data_copy (vcell->vcell_data, vcell_data);
00715     else
00716         vcell->vcell_data = (gpointer) vcell_data;
00717 
00718     vcell->visible = visible ? 1 : 0;
00719     vcell->start_primary_color = start_primary_color ? 1 : 0;
00720 }
00721 
00722 void
00723 gnc_table_set_virt_cell_data (Table *table,
00724                               VirtualCellLocation vcell_loc,
00725                               gconstpointer vcell_data)
00726 {
00727     VirtualCell *vcell;
00728 
00729     if (table == NULL)
00730         return;
00731 
00732     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00733     if (vcell == NULL)
00734         return;
00735 
00736     if (table->model->cell_data_copy)
00737         table->model->cell_data_copy (vcell->vcell_data, vcell_data);
00738     else
00739         vcell->vcell_data = (gpointer) vcell_data;
00740 }
00741 
00742 void
00743 gnc_table_set_virt_cell_visible (Table *table,
00744                                  VirtualCellLocation vcell_loc,
00745                                  gboolean visible)
00746 {
00747     VirtualCell *vcell;
00748 
00749     if (table == NULL)
00750         return;
00751 
00752     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00753     if (vcell == NULL)
00754         return;
00755 
00756     vcell->visible = visible ? 1 : 0;
00757 }
00758 
00759 void
00760 gnc_table_set_virt_cell_cursor (Table *table,
00761                                 VirtualCellLocation vcell_loc,
00762                                 CellBlock *cursor)
00763 {
00764     VirtualCell *vcell;
00765 
00766     if (table == NULL)
00767         return;
00768 
00769     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00770     if (vcell == NULL)
00771         return;
00772 
00773     vcell->cellblock = cursor;
00774 }
00775 
00776 static void
00777 gnc_table_move_cursor_internal (Table *table,
00778                                 VirtualLocation new_virt_loc,
00779                                 gboolean do_move_gui)
00780 {
00781     int cell_row, cell_col;
00782     VirtualLocation virt_loc;
00783     VirtualCell *vcell;
00784     CellBlock *curs;
00785 
00786     ENTER("new_virt=(%d %d) do_move_gui=%d\n",
00787           new_virt_loc.vcell_loc.virt_row,
00788           new_virt_loc.vcell_loc.virt_col, do_move_gui);
00789 
00790     /* call the callback, allowing the app to commit any changes
00791      * associated with the current location of the cursor. Note that
00792      * this callback may recursively call this routine. */
00793     if (table->control->move_cursor && table->control->allow_move)
00794     {
00795         table->control->move_cursor (&new_virt_loc, table->control->user_data);
00796 
00797         /* The above callback can cause this routine to be called
00798          * recursively. As a result of this recursion, the cursor may
00799          * have gotten repositioned. We need to make sure we make
00800          * passive again. */
00801         if (do_move_gui)
00802             gnc_table_refresh_current_cursor_gui (table, FALSE);
00803     }
00804 
00805     /* invalidate the cursor for now; we'll fix it back up below */
00806     gnc_virtual_location_init (&table->current_cursor_loc);
00807 
00808     curs = table->current_cursor;
00809     table->current_cursor = NULL;
00810 
00811     /* check for out-of-bounds conditions (which may be deliberate) */
00812     if ((new_virt_loc.vcell_loc.virt_row < 0) ||
00813             (new_virt_loc.vcell_loc.virt_col < 0))
00814     {
00815         /* if the location is invalid, then we should take this
00816          * as a command to unmap the cursor gui. */
00817         if (do_move_gui && curs)
00818         {
00819             for (cell_row = 0; cell_row < curs->num_rows; cell_row++)
00820                 for (cell_col = 0; cell_col < curs->num_cols; cell_col++)
00821                 {
00822                     BasicCell *cell;
00823 
00824                     cell = gnc_cellblock_get_cell (curs, cell_row, cell_col);
00825                     if (cell)
00826                     {
00827                         cell->changed = FALSE;
00828                         cell->conditionally_changed = FALSE;
00829 
00830                         if (cell->gui_move)
00831                             cell->gui_move (cell);
00832                     }
00833                 }
00834         }
00835 
00836         LEAVE("out of bounds\n");
00837         return;
00838     }
00839 
00840     if (!gnc_table_virtual_loc_valid (table, new_virt_loc, TRUE))
00841     {
00842         PWARN("bad table location");
00843         LEAVE("");
00844         return;
00845     }
00846 
00847     /* ok, we now have a valid position. Find the new cursor to use,
00848      * and initialize its cells */
00849     vcell = gnc_table_get_virtual_cell (table, new_virt_loc.vcell_loc);
00850     curs = vcell->cellblock;
00851     table->current_cursor = curs;
00852 
00853     /* record the new position */
00854     table->current_cursor_loc = new_virt_loc;
00855 
00856     virt_loc.vcell_loc = new_virt_loc.vcell_loc;
00857 
00858     /* update the cell values to reflect the new position */
00859     for (cell_row = 0; cell_row < curs->num_rows; cell_row++)
00860         for (cell_col = 0; cell_col < curs->num_cols; cell_col++)
00861         {
00862             BasicCell *cell;
00863             CellIOFlags io_flags;
00864 
00865             virt_loc.phys_row_offset = cell_row;
00866             virt_loc.phys_col_offset = cell_col;
00867 
00868             cell = gnc_cellblock_get_cell(curs, cell_row, cell_col);
00869             if (cell)
00870             {
00871                 /* if a cell has a GUI, move that first, before setting
00872                  * the cell value.  Otherwise, we'll end up putting the
00873                  * new values in the old cell locations, and that would
00874                  * lead to confusion of all sorts. */
00875                 if (do_move_gui && cell->gui_move)
00876                     cell->gui_move (cell);
00877 
00878                 /* OK, now copy the string value from the table at large
00879                  * into the cell handler. */
00880                 io_flags = gnc_table_get_io_flags (table, virt_loc);
00881                 if (io_flags & XACC_CELL_ALLOW_SHADOW)
00882                 {
00883                     const char *entry;
00884                     gboolean conditionally_changed = FALSE;
00885 
00886                     entry = gnc_table_get_entry_internal (table, virt_loc,
00887                                                           &conditionally_changed);
00888 
00889                     gnc_basic_cell_set_value (cell, entry);
00890 
00891                     cell->changed = FALSE;
00892                     cell->conditionally_changed = conditionally_changed;
00893                 }
00894             }
00895         }
00896 
00897     LEAVE("did move\n");
00898 }
00899 
00900 void
00901 gnc_table_move_cursor (Table *table, VirtualLocation new_virt_loc)
00902 {
00903     if (!table) return;
00904 
00905     gnc_table_move_cursor_internal (table, new_virt_loc, FALSE);
00906 }
00907 
00908 /* same as above, but be sure to deal with GUI elements as well */
00909 void
00910 gnc_table_move_cursor_gui (Table *table, VirtualLocation new_virt_loc)
00911 {
00912     if (!table) return;
00913 
00914     gnc_table_move_cursor_internal (table, new_virt_loc, TRUE);
00915 }
00916 
00917 /* gnc_table_verify_cursor_position checks the location of the cursor
00918  * with respect to a virtual location, and repositions the cursor
00919  * if necessary. Returns true if the cell cursor was repositioned. */
00920 gboolean
00921 gnc_table_verify_cursor_position (Table *table, VirtualLocation virt_loc)
00922 {
00923     gboolean do_move = FALSE;
00924     gboolean moved_cursor = FALSE;
00925 
00926     if (!table) return FALSE;
00927 
00928     /* Someone may be trying to intentionally invalidate the cursor, in
00929      * which case the physical addresses could be out of bounds. For
00930      * example, in order to unmap it in preparation for a reconfig.
00931      * So, if the specified location is out of bounds, then the cursor
00932      * MUST be moved. */
00933     if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc.vcell_loc))
00934         do_move = TRUE;
00935 
00936     if (!virt_cell_loc_equal (virt_loc.vcell_loc,
00937                               table->current_cursor_loc.vcell_loc))
00938         do_move = TRUE;
00939 
00940     if (do_move)
00941     {
00942         gnc_table_move_cursor_gui (table, virt_loc);
00943         moved_cursor = TRUE;
00944     }
00945     else if (!virt_loc_equal (virt_loc, table->current_cursor_loc))
00946     {
00947         table->current_cursor_loc = virt_loc;
00948         moved_cursor = TRUE;
00949     }
00950 
00951     return moved_cursor;
00952 }
00953 
00954 gpointer
00955 gnc_table_get_vcell_data (Table *table, VirtualCellLocation vcell_loc)
00956 {
00957     VirtualCell *vcell;
00958 
00959     if (!table) return NULL;
00960 
00961     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
00962     if (vcell == NULL)
00963         return NULL;
00964 
00965     return vcell->vcell_data;
00966 }
00967 
00968 /* If any of the cells have GUI specific components that need
00969  * initialization, initialize them now. The realize() callback
00970  * on the cursor cell is how we inform the cell handler that
00971  * now is the time to initialize its GUI.  */
00972 void
00973 gnc_table_realize_gui (Table * table)
00974 {
00975     GList *cells;
00976     GList *node;
00977 
00978     if (!table) return;
00979     if (!table->ui_data) return;
00980 
00981     cells = gnc_table_layout_get_cells (table->layout);
00982 
00983     for (node = cells; node; node = node->next)
00984     {
00985         BasicCell *cell = node->data;
00986 
00987         if (cell->gui_realize)
00988             cell->gui_realize (cell, table->ui_data);
00989     }
00990 }
00991 
00992 void
00993 gnc_table_wrap_verify_cursor_position (Table *table, VirtualLocation virt_loc)
00994 {
00995     VirtualLocation save_loc;
00996     gboolean moved_cursor;
00997 
00998     if (!table) return;
00999 
01000     ENTER("(%d %d)", virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_col);
01001 
01002     save_loc = table->current_cursor_loc;
01003 
01004     /* VerifyCursor will do all sorts of gui-independent machinations */
01005     moved_cursor = gnc_table_verify_cursor_position (table, virt_loc);
01006 
01007     if (moved_cursor)
01008     {
01009         /* make sure *both* the old and the new cursor rows get redrawn */
01010         gnc_table_refresh_current_cursor_gui (table, TRUE);
01011         gnc_table_refresh_cursor_gui (table, save_loc.vcell_loc, FALSE);
01012     }
01013 
01014     LEAVE ("");
01015 }
01016 
01017 void
01018 gnc_table_refresh_current_cursor_gui (Table * table, gboolean do_scroll)
01019 {
01020     if (!table) return;
01021 
01022     gnc_table_refresh_cursor_gui (table, table->current_cursor_loc.vcell_loc,
01023                                   do_scroll);
01024 }
01025 
01026 gboolean
01027 gnc_table_virtual_loc_valid(Table *table,
01028                             VirtualLocation virt_loc,
01029                             gboolean exact_pointer)
01030 {
01031     VirtualCell *vcell;
01032     CellIOFlags io_flags;
01033 
01034     if (!table) return FALSE;
01035 
01036     /* header rows cannot be modified */
01037     if (virt_loc.vcell_loc.virt_row == 0)
01038         return FALSE;
01039 
01040     vcell = gnc_table_get_virtual_cell(table, virt_loc.vcell_loc);
01041     if (vcell == NULL)
01042         return FALSE;
01043 
01044     if (!vcell->visible)
01045         return FALSE;
01046 
01047     /* verify that offsets are valid. This may occur if the app that is
01048      * using the table has a paritally initialized cursor. (probably due
01049      * to a programming error, but maybe they meant to do this). */
01050     if ((0 > virt_loc.phys_row_offset) || (0 > virt_loc.phys_col_offset))
01051         return FALSE;
01052 
01053     /* check for a cell handler, but only if cell address is valid */
01054     if (vcell->cellblock == NULL) return FALSE;
01055 
01056     /* if table is read-only, any cell is ok :) */
01057     if (gnc_table_model_read_only (table->model)) return TRUE;
01058 
01059     io_flags = gnc_table_get_io_flags (table, virt_loc);
01060 
01061     /* if the cell allows ENTER, then it is ok */
01062     if (io_flags & XACC_CELL_ALLOW_ENTER) return TRUE;
01063 
01064     /* if cell is marked as output-only, you can't enter */
01065     if (0 == (XACC_CELL_ALLOW_INPUT & io_flags)) return FALSE;
01066 
01067     /* if cell is pointer only and this is not an exact pointer test,
01068      * it cannot be entered. */
01069     if (!exact_pointer && ((XACC_CELL_ALLOW_EXACT_ONLY & io_flags) != 0))
01070         return FALSE;
01071 
01072     return TRUE;
01073 }
01074 
01075 /* Handle the non gui-specific parts of a cell enter callback */
01076 gboolean
01077 gnc_table_enter_update (Table *table,
01078                         VirtualLocation virt_loc,
01079                         int *cursor_position,
01080                         int *start_selection,
01081                         int *end_selection)
01082 {
01083     gboolean can_edit = TRUE;
01084     CellEnterFunc enter;
01085     BasicCell *cell;
01086     CellBlock *cb;
01087     int cell_row;
01088     int cell_col;
01089     CellIOFlags io_flags;
01090 
01091     if (table == NULL)
01092         return FALSE;
01093 
01094     cb = table->current_cursor;
01095 
01096     cell_row = virt_loc.phys_row_offset;
01097     cell_col = virt_loc.phys_col_offset;
01098 
01099     ENTER("enter %d %d (relrow=%d relcol=%d)",
01100           virt_loc.vcell_loc.virt_row,
01101           virt_loc.vcell_loc.virt_col,
01102           cell_row, cell_col);
01103 
01104     /* OK, if there is a callback for this cell, call it */
01105     cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
01106     if (!cell)
01107     {
01108         LEAVE("no cell");
01109         return FALSE;
01110     }
01111 
01112     io_flags = gnc_table_get_io_flags (table, virt_loc);
01113     if (io_flags == XACC_CELL_ALLOW_READ_ONLY)
01114     {
01115         LEAVE("read only cell");
01116         return FALSE;
01117     }
01118 
01119     enter = cell->enter_cell;
01120 
01121     if (enter)
01122     {
01123         char * old_value;
01124 
01125         DEBUG("gnc_table_enter_update(): %d %d has enter handler\n",
01126               cell_row, cell_col);
01127 
01128         old_value = g_strdup (cell->value);
01129 
01130         can_edit = enter (cell, cursor_position, start_selection, end_selection);
01131 
01132         if (safe_strcmp (old_value, cell->value) != 0)
01133         {
01134             if (gnc_table_model_read_only (table->model))
01135             {
01136                 PWARN ("enter update changed read-only table");
01137             }
01138 
01139             cell->changed = TRUE;
01140         }
01141 
01142         g_free (old_value);
01143     }
01144 
01145     if (table->gui_handlers.redraw_help)
01146         table->gui_handlers.redraw_help (table);
01147 
01148     LEAVE("return %d\n", can_edit);
01149     return can_edit;
01150 }
01151 
01152 void
01153 gnc_table_leave_update (Table *table, VirtualLocation virt_loc)
01154 {
01155     CellLeaveFunc leave;
01156     BasicCell *cell;
01157     CellBlock *cb;
01158     int cell_row;
01159     int cell_col;
01160 
01161     if (table == NULL)
01162         return;
01163 
01164     cb = table->current_cursor;
01165 
01166     cell_row = virt_loc.phys_row_offset;
01167     cell_col = virt_loc.phys_col_offset;
01168 
01169     ENTER("proposed (%d %d) rel(%d %d)\n",
01170           virt_loc.vcell_loc.virt_row,
01171           virt_loc.vcell_loc.virt_col,
01172           cell_row, cell_col);
01173 
01174     /* OK, if there is a callback for this cell, call it */
01175     cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
01176     if (!cell)
01177     {
01178         LEAVE("no cell");
01179         return;
01180     }
01181 
01182     leave = cell->leave_cell;
01183 
01184     if (leave)
01185     {
01186         char * old_value;
01187 
01188         old_value = g_strdup (cell->value);
01189 
01190         leave (cell);
01191 
01192         if (safe_strcmp (old_value, cell->value) != 0)
01193         {
01194             if (gnc_table_model_read_only (table->model))
01195             {
01196                 PWARN ("leave update changed read-only table");
01197             }
01198 
01199             cell->changed = TRUE;
01200         }
01201 
01202         g_free (old_value);
01203     }
01204     LEAVE("");
01205 }
01206 
01207 gboolean
01208 gnc_table_confirm_change (Table *table, VirtualLocation virt_loc)
01209 {
01210     TableConfirmHandler confirm_handler;
01211     const char *cell_name;
01212 
01213     if (!table || !table->model)
01214         return TRUE;
01215 
01216     cell_name = gnc_table_get_cell_name (table, virt_loc);
01217 
01218     confirm_handler = gnc_table_model_get_confirm_handler (table->model,
01219                       cell_name);
01220     if (!confirm_handler)
01221         return TRUE;
01222 
01223     return confirm_handler (virt_loc, table->model->handler_user_data);
01224 }
01225 
01226 /* Returned result should not be touched by the caller.
01227  * NULL return value means the edit was rejected. */
01228 const char *
01229 gnc_table_modify_update (Table *table,
01230                          VirtualLocation virt_loc,
01231                          const char *change,
01232                          int change_len,
01233                          const char *newval,
01234                          int newval_len,
01235                          int *cursor_position,
01236                          int *start_selection,
01237                          int *end_selection,
01238                          gboolean *cancelled)
01239 {
01240     gboolean changed = FALSE;
01241     CellModifyVerifyFunc mv;
01242     BasicCell *cell;
01243     CellBlock *cb;
01244     int cell_row;
01245     int cell_col;
01246     char * old_value;
01247 
01248     g_return_val_if_fail (table, NULL);
01249     g_return_val_if_fail (table->model, NULL);
01250 
01251     if (gnc_table_model_read_only (table->model))
01252     {
01253         PWARN ("change to read-only table");
01254         return NULL;
01255     }
01256 
01257     cb = table->current_cursor;
01258 
01259     cell_row = virt_loc.phys_row_offset;
01260     cell_col = virt_loc.phys_col_offset;
01261 
01262     ENTER ("");
01263 
01264     if (!gnc_table_confirm_change (table, virt_loc))
01265     {
01266         if (cancelled)
01267             *cancelled = TRUE;
01268 
01269         LEAVE("change cancelled");
01270         return NULL;
01271     }
01272 
01273     if (cancelled)
01274         *cancelled = FALSE;
01275 
01276     /* OK, if there is a callback for this cell, call it */
01277     cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
01278     if (!cell)
01279     {
01280         LEAVE("no cell");
01281         return NULL;
01282     }
01283 
01284     mv = cell->modify_verify;
01285 
01286     old_value = g_strdup (cell->value);
01287 
01288     if (mv)
01289     {
01290         mv (cell, change, change_len, newval, newval_len,
01291             cursor_position, start_selection, end_selection);
01292     }
01293     else
01294     {
01295         gnc_basic_cell_set_value (cell, newval);
01296     }
01297 
01298     if (safe_strcmp (old_value, cell->value) != 0)
01299     {
01300         changed = TRUE;
01301         cell->changed = TRUE;
01302     }
01303 
01304     g_free (old_value);
01305 
01306     if (table->gui_handlers.redraw_help)
01307         table->gui_handlers.redraw_help (table);
01308 
01309     LEAVE ("change %d %d (relrow=%d relcol=%d) val=%s\n",
01310            virt_loc.vcell_loc.virt_row,
01311            virt_loc.vcell_loc.virt_col,
01312            cell_row, cell_col,
01313            cell->value ? cell->value : "(null)");
01314 
01315     if (changed)
01316         return cell->value;
01317     else
01318         return NULL;
01319 }
01320 
01321 gboolean
01322 gnc_table_direct_update (Table *table,
01323                          VirtualLocation virt_loc,
01324                          char **newval_ptr,
01325                          int *cursor_position,
01326                          int *start_selection,
01327                          int *end_selection,
01328                          gpointer gui_data)
01329 {
01330     gboolean result;
01331     BasicCell *cell;
01332     CellBlock *cb;
01333     int cell_row;
01334     int cell_col;
01335     char * old_value;
01336 
01337     g_return_val_if_fail (table, FALSE);
01338     g_return_val_if_fail (table->model, FALSE);
01339 
01340     if (gnc_table_model_read_only (table->model))
01341     {
01342         PWARN ("input to read-only table");
01343         return FALSE;
01344     }
01345 
01346     cb = table->current_cursor;
01347 
01348     cell_row = virt_loc.phys_row_offset;
01349     cell_col = virt_loc.phys_col_offset;
01350 
01351     cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
01352     if (!cell)
01353         return FALSE;
01354 
01355     ENTER ("");
01356 
01357     if (cell->direct_update == NULL)
01358     {
01359         LEAVE("no direct update");
01360         return FALSE;
01361     }
01362 
01363     old_value = g_strdup (cell->value);
01364 
01365     result = cell->direct_update (cell, cursor_position, start_selection,
01366                                   end_selection, gui_data);
01367 
01368     if (safe_strcmp (old_value, cell->value) != 0)
01369     {
01370         if (!gnc_table_confirm_change (table, virt_loc))
01371         {
01372             gnc_basic_cell_set_value (cell, old_value);
01373             *newval_ptr = NULL;
01374             result = TRUE;
01375         }
01376         else
01377         {
01378             cell->changed = TRUE;
01379             *newval_ptr = cell->value;
01380         }
01381     }
01382     else
01383         *newval_ptr = NULL;
01384 
01385     g_free (old_value);
01386 
01387     if (table->gui_handlers.redraw_help)
01388         table->gui_handlers.redraw_help (table);
01389 
01390     LEAVE("");
01391     return result;
01392 }
01393 
01394 static gboolean gnc_table_find_valid_cell_horiz (Table *table,
01395         VirtualLocation *virt_loc,
01396         gboolean exact_cell);
01397 
01398 static gboolean
01399 gnc_table_find_valid_row_vert (Table *table, VirtualLocation *virt_loc)
01400 {
01401     VirtualLocation vloc;
01402     VirtualCell *vcell = NULL;
01403     int top;
01404     int bottom;
01405 
01406     if (table == NULL)
01407         return FALSE;
01408 
01409     if (virt_loc == NULL)
01410         return FALSE;
01411 
01412     vloc = *virt_loc;
01413 
01414     if (vloc.vcell_loc.virt_row < 1)
01415         vloc.vcell_loc.virt_row = 1;
01416     if (vloc.vcell_loc.virt_row >= table->num_virt_rows)
01417         vloc.vcell_loc.virt_row = table->num_virt_rows - 1;
01418 
01419     top  = vloc.vcell_loc.virt_row;
01420     bottom = vloc.vcell_loc.virt_row + 1;
01421 
01422     while (top >= 1 || bottom < table->num_virt_rows)
01423     {
01424         vloc.vcell_loc.virt_row = top;
01425         vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01426         if (vcell && vcell->cellblock && vcell->visible)
01427         {
01428             vloc.phys_row_offset = 0;
01429             vloc.phys_col_offset = 0;
01430 
01431             if (gnc_table_find_valid_cell_horiz (table, &vloc, FALSE))
01432                 break;
01433         }
01434 
01435         vloc.vcell_loc.virt_row = bottom;
01436         vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01437         if (vcell && vcell->cellblock && vcell->visible)
01438         {
01439             vloc.phys_row_offset = 0;
01440             vloc.phys_col_offset = 0;
01441 
01442             if (gnc_table_find_valid_cell_horiz (table, &vloc, FALSE))
01443                 break;
01444         }
01445 
01446         top--;
01447         bottom++;
01448     }
01449 
01450     if (!vcell || !vcell->cellblock || !vcell->visible)
01451         return FALSE;
01452 
01453     if (vloc.phys_row_offset < 0)
01454         vloc.phys_row_offset = 0;
01455     if (vloc.phys_row_offset >= vcell->cellblock->num_rows)
01456         vloc.phys_row_offset = vcell->cellblock->num_rows - 1;
01457 
01458     virt_loc->vcell_loc = vloc.vcell_loc;
01459 
01460     return TRUE;
01461 }
01462 
01463 static gboolean
01464 gnc_table_find_valid_cell_horiz (Table *table,
01465                                  VirtualLocation *virt_loc,
01466                                  gboolean exact_cell)
01467 {
01468     VirtualLocation vloc;
01469     VirtualCell *vcell;
01470     int left;
01471     int right;
01472 
01473     if (table == NULL)
01474         return FALSE;
01475 
01476     if (virt_loc == NULL)
01477         return FALSE;
01478 
01479     if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc->vcell_loc))
01480         return FALSE;
01481 
01482     if (gnc_table_virtual_loc_valid (table, *virt_loc, exact_cell))
01483         return TRUE;
01484 
01485     vloc = *virt_loc;
01486 
01487     vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01488     if (vcell == NULL)
01489         return FALSE;
01490     if (vcell->cellblock == NULL)
01491         return FALSE;
01492 
01493     if (vloc.phys_col_offset < 0)
01494         vloc.phys_col_offset = 0;
01495     if (vloc.phys_col_offset >= vcell->cellblock->num_cols)
01496         vloc.phys_col_offset = vcell->cellblock->num_cols - 1;
01497 
01498     left  = vloc.phys_col_offset - 1;
01499     right = vloc.phys_col_offset + 1;
01500 
01501     while (left >= 0 || right < vcell->cellblock->num_cols)
01502     {
01503         vloc.phys_col_offset = right;
01504         if (gnc_table_virtual_loc_valid(table, vloc, FALSE))
01505         {
01506             *virt_loc = vloc;
01507             return TRUE;
01508         }
01509 
01510         vloc.phys_col_offset = left;
01511         if (gnc_table_virtual_loc_valid(table, vloc, FALSE))
01512         {
01513             *virt_loc = vloc;
01514             return TRUE;
01515         }
01516 
01517         left--;
01518         right++;
01519     }
01520 
01521     return FALSE;
01522 }
01523 
01524 gboolean
01525 gnc_table_find_close_valid_cell (Table *table, VirtualLocation *virt_loc,
01526                                  gboolean exact_pointer)
01527 {
01528     if (!gnc_table_find_valid_row_vert (table, virt_loc))
01529         return FALSE;
01530 
01531     return gnc_table_find_valid_cell_horiz (table, virt_loc, exact_pointer);
01532 }
01533 
01534 void
01535 gnc_table_refresh_cursor_gui (Table * table,
01536                               VirtualCellLocation vcell_loc,
01537                               gboolean do_scroll)
01538 {
01539     g_return_if_fail (table != NULL);
01540     g_return_if_fail (table->gui_handlers.cursor_refresh != NULL);
01541 
01542     table->gui_handlers.cursor_refresh (table, vcell_loc, do_scroll);
01543 }
01544 
01545 gboolean
01546 gnc_table_move_tab (Table *table,
01547                     VirtualLocation *virt_loc,
01548                     gboolean move_right)
01549 {
01550     VirtualCell *vcell;
01551     VirtualLocation vloc;
01552     BasicCell *cell;
01553 
01554     if ((table == NULL) || (virt_loc == NULL))
01555         return FALSE;
01556 
01557     vloc = *virt_loc;
01558 
01559     vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01560     if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
01561         return FALSE;
01562 
01563     while (1)
01564     {
01565         CellIOFlags io_flags;
01566 
01567         if (move_right)
01568         {
01569             vloc.phys_col_offset++;
01570 
01571             if (vloc.phys_col_offset >= vcell->cellblock->num_cols)
01572             {
01573                 if (!gnc_table_move_vertical_position (table, &vloc, 1))
01574                     return FALSE;
01575 
01576                 vloc.phys_col_offset = 0;
01577             }
01578         }
01579         else
01580         {
01581             vloc.phys_col_offset--;
01582 
01583             if (vloc.phys_col_offset < 0)
01584             {
01585                 if (!gnc_table_move_vertical_position (table, &vloc, -1))
01586                     return FALSE;
01587 
01588                 vloc.phys_col_offset = vcell->cellblock->num_cols - 1;
01589             }
01590         }
01591 
01592         vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01593         if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
01594             return FALSE;
01595 
01596         cell = gnc_cellblock_get_cell (vcell->cellblock,
01597                                        vloc.phys_row_offset,
01598                                        vloc.phys_col_offset);
01599         if (!cell)
01600             continue;
01601 
01602         io_flags = gnc_table_get_io_flags (table, vloc);
01603 
01604         if (!(io_flags & XACC_CELL_ALLOW_INPUT))
01605             continue;
01606 
01607         if (io_flags & XACC_CELL_ALLOW_EXACT_ONLY)
01608             continue;
01609 
01610         break;
01611     }
01612 
01613     {
01614         gboolean changed = !virt_loc_equal (vloc, *virt_loc);
01615 
01616         *virt_loc = vloc;
01617 
01618         return changed;
01619     }
01620 }
01621 
01622 gboolean
01623 gnc_table_move_vertical_position (Table *table,
01624                                   VirtualLocation *virt_loc,
01625                                   int phys_row_offset)
01626 {
01627     VirtualLocation vloc;
01628     VirtualCell *vcell;
01629     gint last_visible_row;
01630 
01631     if ((table == NULL) || (virt_loc == NULL))
01632         return FALSE;
01633 
01634     vloc = *virt_loc;
01635     last_visible_row = vloc.vcell_loc.virt_row;
01636 
01637     vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01638     if ((vcell == NULL) || (vcell->cellblock == NULL))
01639         return FALSE;
01640 
01641     while (phys_row_offset != 0)
01642     {
01643         /* going up */
01644         if (phys_row_offset < 0)
01645         {
01646             phys_row_offset++;
01647 
01648             /* room left in the current cursor */
01649             if (vloc.phys_row_offset > 0)
01650             {
01651                 vloc.phys_row_offset--;
01652                 continue;
01653             }
01654 
01655             /* end of the line */
01656             if (vloc.vcell_loc.virt_row == 1)
01657                 break;
01658 
01659             do
01660             {
01661                 vloc.vcell_loc.virt_row--;
01662 
01663                 vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01664             }
01665             while (vcell && vcell->cellblock && !vcell->visible);
01666 
01667             if (!vcell || !vcell->cellblock)
01668                 break;
01669 
01670             last_visible_row = vloc.vcell_loc.virt_row;
01671             vloc.phys_row_offset = vcell->cellblock->num_rows - 1;
01672         }
01673         /* going down */
01674         else
01675         {
01676             phys_row_offset--;
01677 
01678             /* room left in the current cursor */
01679             if (vloc.phys_row_offset < (vcell->cellblock->num_rows - 1))
01680             {
01681                 vloc.phys_row_offset++;
01682                 continue;
01683             }
01684 
01685             /* end of the line */
01686             if (vloc.vcell_loc.virt_row == (table->num_virt_rows - 1))
01687                 break;
01688 
01689             do
01690             {
01691                 vloc.vcell_loc.virt_row++;
01692 
01693                 vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
01694             }
01695             while (vcell && vcell->cellblock && !vcell->visible);
01696 
01697             if (!vcell || !vcell->cellblock)
01698                 break;
01699 
01700             last_visible_row = vloc.vcell_loc.virt_row;
01701             vloc.phys_row_offset = 0;
01702         }
01703     }
01704 
01705     vloc.vcell_loc.virt_row = last_visible_row;
01706 
01707     {
01708         gboolean changed = !virt_loc_equal (vloc, *virt_loc);
01709 
01710         *virt_loc = vloc;
01711 
01712         return changed;
01713     }
01714 }
01715 
01716 gboolean
01717 gnc_table_traverse_update(Table *table,
01718                           VirtualLocation virt_loc,
01719                           gncTableTraversalDir dir,
01720                           VirtualLocation *dest_loc)
01721 {
01722     CellBlock *cb;
01723     gboolean abort_move;
01724 
01725     if ((table == NULL) || (dest_loc == NULL))
01726         return FALSE;
01727 
01728     cb = table->current_cursor;
01729 
01730     ENTER("proposed (%d %d) -> (%d %d)\n",
01731           virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_row,
01732           dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col);
01733 
01734     /* first, make sure our destination cell is valid. If it is out
01735      * of bounds report an error. I don't think this ever happens. */
01736     if (gnc_table_virtual_cell_out_of_bounds (table, dest_loc->vcell_loc))
01737     {
01738         PERR("destination (%d, %d) out of bounds (%d, %d)\n",
01739              dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col,
01740              table->num_virt_rows, table->num_virt_cols);
01741         LEAVE("");
01742         return TRUE;
01743     }
01744 
01745     /* next, check the current row and column.  If they are out of bounds
01746      * we can recover by treating the traversal as a mouse point. This can
01747      * occur whenever the register widget is resized smaller, maybe?. */
01748     if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE))
01749     {
01750         PINFO("source (%d, %d) out of bounds (%d, %d)\n",
01751               virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_col,
01752               table->num_virt_rows, table->num_virt_cols);
01753 
01754         dir = GNC_TABLE_TRAVERSE_POINTER;
01755     }
01756 
01757     /* process forward-moving traversals */
01758     switch (dir)
01759     {
01760     case GNC_TABLE_TRAVERSE_RIGHT:
01761     case GNC_TABLE_TRAVERSE_LEFT:
01762         gnc_table_find_valid_cell_horiz(table, dest_loc, FALSE);
01763 
01764         break;
01765 
01766     case GNC_TABLE_TRAVERSE_UP:
01767     case GNC_TABLE_TRAVERSE_DOWN:
01768     {
01769         VirtualLocation new_loc = *dest_loc;
01770         int increment;
01771 
01772         /* Keep going in the specified direction until we find a valid
01773          * row to land on, or we hit the end of the table. At the end,
01774          * turn around and go back until we find a valid row or we get
01775          * to where we started. If we still can't find anything, try
01776          * going left and right. */
01777         increment = (dir == GNC_TABLE_TRAVERSE_DOWN) ? 1 : -1;
01778 
01779         while (!gnc_table_virtual_loc_valid(table, new_loc, FALSE))
01780         {
01781             if (virt_loc_equal (new_loc, virt_loc))
01782             {
01783                 new_loc = *dest_loc;
01784                 gnc_table_find_valid_cell_horiz(table, &new_loc, FALSE);
01785                 break;
01786             }
01787 
01788             if (!gnc_table_move_vertical_position (table, &new_loc, increment))
01789             {
01790                 increment *= -1;
01791                 new_loc = *dest_loc;
01792             }
01793         }
01794 
01795         *dest_loc = new_loc;
01796     }
01797 
01798     if (!gnc_table_virtual_loc_valid(table, *dest_loc, FALSE))
01799     {
01800         LEAVE("");
01801         return TRUE;
01802     }
01803 
01804     break;
01805 
01806     case GNC_TABLE_TRAVERSE_POINTER:
01807         if (!gnc_table_find_valid_cell_horiz(table, dest_loc, TRUE))
01808         {
01809             LEAVE("");
01810             return TRUE;
01811         }
01812 
01813         break;
01814 
01815     default:
01816         g_return_val_if_fail (FALSE, TRUE);
01817         break;
01818     }
01819 
01820     /* Call the table traverse callback for any modifications. */
01821     if (table->control->traverse)
01822         abort_move = table->control->traverse (dest_loc, dir,
01823                                                table->control->user_data);
01824     else
01825         abort_move = FALSE;
01826 
01827     LEAVE("dest_row = %d, dest_col = %d\n",
01828           dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col);
01829 
01830     return abort_move;
01831 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines