static gboolean gnc_table_find_valid_cell_horiz (Table *table, VirtualLocation *virt_loc, gboolean exact_cell) { VirtualLocation vloc; VirtualCell *vcell; int left; int right; if (table == NULL) return FALSE; if (virt_loc == NULL) return FALSE; if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc->vcell_loc)) return FALSE; if (gnc_table_virtual_loc_valid (table, *virt_loc, exact_cell)) return TRUE; vloc = *virt_loc; vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc); if (vcell == NULL) return FALSE; if (vcell->cellblock == NULL) return FALSE; if (vloc.phys_col_offset < 0) vloc.phys_col_offset = 0; if (vloc.phys_col_offset >= vcell->cellblock->num_cols) vloc.phys_col_offset = vcell->cellblock->num_cols - 1; left = vloc.phys_col_offset - 1; right = vloc.phys_col_offset + 1; while (left >= 0 || right < vcell->cellblock->num_cols) { vloc.phys_col_offset = right; if (gnc_table_virtual_loc_valid(table, vloc, FALSE)) { *virt_loc = vloc; return TRUE; } vloc.phys_col_offset = left; if (gnc_table_virtual_loc_valid(table, vloc, FALSE)) { *virt_loc = vloc; return TRUE; } left--; right++; } return FALSE; }
static void get_cell_borders (GnucashSheet *sheet, VirtualLocation virt_loc, PhysicalCellBorders *borders) { VirtualLocation v_loc; PhysicalCellBorders neighbor; gnucash_sheet_get_borders (sheet, virt_loc, borders); /* top */ v_loc = virt_loc; if (gnc_table_move_vertical_position (sheet->table, &v_loc, -1)) { gnucash_sheet_get_borders (sheet, v_loc, &neighbor); borders->top = MAX (borders->top, neighbor.bottom); } /* bottom */ v_loc = virt_loc; if (gnc_table_move_vertical_position (sheet->table, &v_loc, 1)) { gnucash_sheet_get_borders (sheet, v_loc, &neighbor); borders->bottom = MAX (borders->bottom, neighbor.top); } /* left */ v_loc = virt_loc; v_loc.phys_col_offset--; if (gnc_table_virtual_loc_valid (sheet->table, v_loc, TRUE)) { gnucash_sheet_get_borders (sheet, v_loc, &neighbor); borders->left = MAX (borders->left, neighbor.right); } /* right */ v_loc = virt_loc; v_loc.phys_col_offset++; if (gnc_table_virtual_loc_valid (sheet->table, v_loc, TRUE)) { gnucash_sheet_get_borders (sheet, v_loc, &neighbor); borders->right = MAX (borders->right, neighbor.left); } }
static void gnc_table_move_cursor_internal (Table *table, VirtualLocation new_virt_loc, gboolean do_move_gui) { int cell_row, cell_col; VirtualLocation virt_loc; VirtualCell *vcell; CellBlock *curs; ENTER("new_virt=(%d %d) do_move_gui=%d\n", new_virt_loc.vcell_loc.virt_row, new_virt_loc.vcell_loc.virt_col, do_move_gui); /* call the callback, allowing the app to commit any changes * associated with the current location of the cursor. Note that * this callback may recursively call this routine. */ if (table->control->move_cursor && table->control->allow_move) { table->control->move_cursor (&new_virt_loc, table->control->user_data); /* The above callback can cause this routine to be called * recursively. As a result of this recursion, the cursor may * have gotten repositioned. We need to make sure we make * passive again. */ if (do_move_gui) gnc_table_refresh_current_cursor_gui (table, FALSE); } /* invalidate the cursor for now; we'll fix it back up below */ gnc_virtual_location_init (&table->current_cursor_loc); curs = table->current_cursor; table->current_cursor = NULL; /* check for out-of-bounds conditions (which may be deliberate) */ if ((new_virt_loc.vcell_loc.virt_row < 0) || (new_virt_loc.vcell_loc.virt_col < 0)) { /* if the location is invalid, then we should take this * as a command to unmap the cursor gui. */ if (do_move_gui && curs) { for (cell_row = 0; cell_row < curs->num_rows; cell_row++) for (cell_col = 0; cell_col < curs->num_cols; cell_col++) { BasicCell *cell; cell = gnc_cellblock_get_cell (curs, cell_row, cell_col); if (cell) { cell->changed = FALSE; cell->conditionally_changed = FALSE; if (cell->gui_move) cell->gui_move (cell); } } } LEAVE("out of bounds\n"); return; } if (!gnc_table_virtual_loc_valid (table, new_virt_loc, TRUE)) { PWARN("bad table location"); LEAVE(""); return; } /* ok, we now have a valid position. Find the new cursor to use, * and initialize its cells */ vcell = gnc_table_get_virtual_cell (table, new_virt_loc.vcell_loc); curs = vcell->cellblock; table->current_cursor = curs; /* record the new position */ table->current_cursor_loc = new_virt_loc; virt_loc.vcell_loc = new_virt_loc.vcell_loc; /* update the cell values to reflect the new position */ for (cell_row = 0; cell_row < curs->num_rows; cell_row++) for (cell_col = 0; cell_col < curs->num_cols; cell_col++) { BasicCell *cell; CellIOFlags io_flags; virt_loc.phys_row_offset = cell_row; virt_loc.phys_col_offset = cell_col; cell = gnc_cellblock_get_cell(curs, cell_row, cell_col); if (cell) { /* if a cell has a GUI, move that first, before setting * the cell value. Otherwise, we'll end up putting the * new values in the old cell locations, and that would * lead to confusion of all sorts. */ if (do_move_gui && cell->gui_move) cell->gui_move (cell); /* OK, now copy the string value from the table at large * into the cell handler. */ io_flags = gnc_table_get_io_flags (table, virt_loc); if (io_flags & XACC_CELL_ALLOW_SHADOW) { const char *entry; gboolean conditionally_changed = FALSE; entry = gnc_table_get_entry_internal (table, virt_loc, &conditionally_changed); gnc_basic_cell_set_value (cell, entry); cell->changed = FALSE; cell->conditionally_changed = conditionally_changed; } } } LEAVE("did move\n"); }
gboolean gnc_table_traverse_update(Table *table, VirtualLocation virt_loc, gncTableTraversalDir dir, VirtualLocation *dest_loc) { gboolean abort_move; if ((table == NULL) || (dest_loc == NULL)) return FALSE; ENTER("proposed (%d %d) -> (%d %d)\n", virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_row, dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col); /* first, make sure our destination cell is valid. If it is out * of bounds report an error. I don't think this ever happens. */ if (gnc_table_virtual_cell_out_of_bounds (table, dest_loc->vcell_loc)) { PERR("destination (%d, %d) out of bounds (%d, %d)\n", dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col, table->num_virt_rows, table->num_virt_cols); LEAVE(""); return TRUE; } /* next, check the current row and column. If they are out of bounds * we can recover by treating the traversal as a mouse point. This can * occur whenever the register widget is resized smaller, maybe?. */ if (!gnc_table_virtual_loc_valid (table, virt_loc, TRUE)) { PINFO("source (%d, %d) out of bounds (%d, %d)\n", virt_loc.vcell_loc.virt_row, virt_loc.vcell_loc.virt_col, table->num_virt_rows, table->num_virt_cols); dir = GNC_TABLE_TRAVERSE_POINTER; } /* process forward-moving traversals */ switch (dir) { case GNC_TABLE_TRAVERSE_RIGHT: case GNC_TABLE_TRAVERSE_LEFT: gnc_table_find_valid_cell_horiz(table, dest_loc, FALSE); break; case GNC_TABLE_TRAVERSE_UP: case GNC_TABLE_TRAVERSE_DOWN: { VirtualLocation new_loc = *dest_loc; int increment; int col_offset = 0; gboolean second_traversal = FALSE; /* Keep going in the specified direction until we find a valid * row to land on, or we hit the end of the table. At the end, * turn around and go back until we find a valid row or we get * to where we started. If we still can't find anything, try * going left and right. */ increment = (dir == GNC_TABLE_TRAVERSE_DOWN) ? 1 : -1; while (!gnc_table_virtual_loc_valid(table, new_loc, FALSE)) { if (virt_loc_equal (new_loc, virt_loc)) { new_loc = *dest_loc; gnc_table_find_valid_cell_horiz(table, &new_loc, FALSE); break; } if (!gnc_table_move_vertical_position (table, &new_loc, increment)) { /* Special case: if there is no valid cell at all in the column * we are scanning, (both up and down directions didn't work) * attempt to do the same in the next column. * Hack alert: there is no check to see if there really is a * valid next column. However this situation so far only happens * after a pagedown/pageup key event in the SX transaction editor * which always tests the first column to start (which has no * editable cells) and in that situation there is a valid next column. */ if (!second_traversal) second_traversal = TRUE; else { second_traversal = FALSE; col_offset++; } increment *= -1; new_loc = *dest_loc; new_loc.phys_col_offset = new_loc.phys_col_offset + col_offset; } } *dest_loc = new_loc; } if (!gnc_table_virtual_loc_valid(table, *dest_loc, FALSE)) { LEAVE(""); return TRUE; } break; case GNC_TABLE_TRAVERSE_POINTER: if (!gnc_table_find_valid_cell_horiz(table, dest_loc, TRUE)) { LEAVE(""); return TRUE; } break; default: g_return_val_if_fail (FALSE, TRUE); break; } /* Call the table traverse callback for any modifications. */ if (table->control->traverse) abort_move = table->control->traverse (dest_loc, dir, table->control->user_data); else abort_move = FALSE; LEAVE("dest_row = %d, dest_col = %d\n", dest_loc->vcell_loc.virt_row, dest_loc->vcell_loc.virt_col); return abort_move; }