/* gnc_table_verify_cursor_position checks the location of the cursor * with respect to a virtual location, and repositions the cursor * if necessary. Returns true if the cell cursor was repositioned. */ gboolean gnc_table_verify_cursor_position (Table *table, VirtualLocation virt_loc) { gboolean do_move = FALSE; gboolean moved_cursor = FALSE; if (!table) return FALSE; /* Someone may be trying to intentionally invalidate the cursor, in * which case the physical addresses could be out of bounds. For * example, in order to unmap it in preparation for a reconfig. * So, if the specified location is out of bounds, then the cursor * MUST be moved. */ if (gnc_table_virtual_cell_out_of_bounds (table, virt_loc.vcell_loc)) do_move = TRUE; if (!virt_cell_loc_equal (virt_loc.vcell_loc, table->current_cursor_loc.vcell_loc)) do_move = TRUE; if (do_move) { gnc_table_move_cursor_gui (table, virt_loc); moved_cursor = TRUE; } else if (!virt_loc_equal (virt_loc, table->current_cursor_loc)) { table->current_cursor_loc = virt_loc; moved_cursor = TRUE; } return moved_cursor; }
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 gnc_table_refresh_cursor_gnome (Table * table, VirtualCellLocation vcell_loc, gboolean do_scroll) { GnucashSheet *sheet; if (!table || !table->ui_data) return; g_return_if_fail (GNUCASH_IS_SHEET (table->ui_data)); if (gnc_table_virtual_cell_out_of_bounds (table, vcell_loc)) return; sheet = GNUCASH_SHEET (table->ui_data); gnucash_sheet_cursor_set_from_table (sheet, do_scroll); if (gnucash_sheet_block_set_from_table (sheet, vcell_loc)) { gnucash_sheet_recompute_block_offsets (sheet); gnucash_sheet_set_scroll_region (sheet); gnucash_sheet_compute_visible_range (sheet); gnucash_sheet_redraw_all (sheet); } else gnucash_sheet_redraw_block (sheet, vcell_loc); }
void gnc_table_show_range (Table *table, VirtualCellLocation start_loc, VirtualCellLocation end_loc) { GnucashSheet *sheet; if (!table || !table->ui_data) return; g_return_if_fail (GNUCASH_IS_SHEET (table->ui_data)); if (gnc_table_virtual_cell_out_of_bounds (table, start_loc)) return; if (gnc_table_virtual_cell_out_of_bounds (table, end_loc)) return; sheet = GNUCASH_SHEET (table->ui_data); gnucash_sheet_show_range (sheet, start_loc, end_loc); }
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; }
static gboolean gnc_entry_ledger_traverse (VirtualLocation *p_new_virt_loc, gncTableTraversalDir dir, gpointer user_data) { GncEntryLedger *ledger = user_data; GncEntry *entry, *new_entry; gint response; VirtualLocation virt_loc; int changed; char const *cell_name; gboolean exact_traversal; if (!ledger) return FALSE; exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER); entry = gnc_entry_ledger_get_current_entry (ledger); if (!entry) return FALSE; /* no changes, make sure we aren't going off the end */ changed = gnc_table_current_cursor_changed (ledger->table, FALSE); if (!changed) return FALSE; virt_loc = *p_new_virt_loc; cell_name = gnc_table_get_current_cell_name (ledger->table); /* See if we are leaving the account field */ do { ComboCell *cell; char *name; char *cell_name = NULL; switch (ledger->type) { case GNCENTRY_INVOICE_ENTRY: case GNCENTRY_INVOICE_VIEWER: case GNCENTRY_CUST_CREDIT_NOTE_ENTRY: case GNCENTRY_CUST_CREDIT_NOTE_VIEWER: cell_name = ENTRY_IACCT_CELL; break; case GNCENTRY_BILL_ENTRY: case GNCENTRY_BILL_VIEWER: case GNCENTRY_EXPVOUCHER_ENTRY: case GNCENTRY_EXPVOUCHER_VIEWER: case GNCENTRY_VEND_CREDIT_NOTE_ENTRY: case GNCENTRY_VEND_CREDIT_NOTE_VIEWER: case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY: case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER: cell_name = ENTRY_BACCT_CELL; break; default: g_warning ("Unhandled ledger type"); break; } if (!cell_name) break; if (!gnc_cell_name_equal (cell_name, cell_name)) break; if (!gnc_table_layout_get_cell_changed (ledger->table->layout, cell_name, FALSE)) break; cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name); if (!cell) break; name = cell->cell.value; if (!name || *name == '\0') break; /* Create the account if necessary. Also checks for a placeholder */ if (!gnc_entry_ledger_get_account_by_name (ledger, (BasicCell *) cell, cell->cell.value, &ledger->full_refresh)) return TRUE; } while (FALSE); /* See if we are leaving the TaxTable field */ do { ComboCell *cell; GncTaxTable *table; char *name; if (!gnc_cell_name_equal (cell_name, ENTRY_TAXTABLE_CELL)) break; if (!gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_TAXTABLE_CELL, FALSE)) break; cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout, ENTRY_TAXTABLE_CELL); if (!cell) break; name = cell->cell.value; if (!name || *name == '\0') break; table = gncTaxTableLookupByName (ledger->book, cell->cell.value); if (table) break; { const char *format = _("The tax table %s does not exist. " "Would you like to create it?"); if (!gnc_verify_dialog (ledger->parent, TRUE, format, name)) break; } ledger->full_refresh = FALSE; table = gnc_ui_tax_table_new_from_name (ledger->book, name); if (!table) break; ledger->full_refresh = TRUE; name = (char *)gncTaxTableGetName (table); gnc_combo_cell_set_value (cell, name); gnc_basic_cell_set_changed (&cell->cell, TRUE); } while (FALSE); /* See if we are tabbing off the end of the very last line * (i.e. the blank entry) */ do { VirtualLocation virt_loc; if (!changed && !ledger->blank_entry_edited) break; if (dir != GNC_TABLE_TRAVERSE_RIGHT) break; virt_loc = ledger->table->current_cursor_loc; if (gnc_table_move_vertical_position (ledger->table, &virt_loc, 1)) break; virt_loc = ledger->table->current_cursor_loc; if (gnc_table_move_tab (ledger->table, &virt_loc, TRUE)) break; *p_new_virt_loc = ledger->table->current_cursor_loc; /* Yep, we're trying to leave the blank entry -- make sure * we are allowed to do so by verifying the current cursor. * If the current cursor is ok, then move on! */ /* Verify that the cursor is ok. If we can't save the cell, don't move! */ if (!gnc_entry_ledger_verify_can_save (ledger)) { return TRUE; } (p_new_virt_loc->vcell_loc.virt_row)++; p_new_virt_loc->phys_row_offset = 0; p_new_virt_loc->phys_col_offset = 0; ledger->traverse_to_new = TRUE; /* If we're here, we're tabbing off the end of the 'blank entry' */ return FALSE; } while (FALSE); /* Now see if we are changing cursors. If not, we may be able to * auto-complete. */ if (!gnc_table_virtual_cell_out_of_bounds (ledger->table, virt_loc.vcell_loc)) { if (gnc_entry_ledger_auto_completion (ledger, dir, p_new_virt_loc)) return FALSE; } /* Check for going off the end */ gnc_table_find_close_valid_cell (ledger->table, &virt_loc, exact_traversal); /* Same entry, no problem -- we're just moving backwards in the cursor */ new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc); if (entry == new_entry) { *p_new_virt_loc = virt_loc; return FALSE; } /* If we are here, then we are trying to leave the cursor. Make sure * the cursor we are leaving is valid. If so, ask the user if the * changes should be recorded. If not, don't go anywhere. */ /* Verify this cursor -- if it's not valid, don't let them move on */ if (!gnc_entry_ledger_verify_can_save (ledger)) { *p_new_virt_loc = ledger->table->current_cursor_loc; return TRUE; } /* * XXX GNCENTRY_INVOICE_EDIT processing to be added: * 1) check if the qty field changed. * 2) if so, check if this entry is part of an order. * 3) if so, ask if they want to change the entry or * split the entry into two parts. */ /* Ok, we are changing lines and the current entry has * changed. We only ask them what they want to do in * limited cases -- usually just let the change go through. */ { GtkWidget *dialog; const char *title = _("Save the current entry?"); const char *message = _("The current entry has been changed. However, this entry is " "part of an existing order. Would you like to record the change " "and effectively change your order?"); switch (ledger->type) { case GNCENTRY_INVOICE_ENTRY: case GNCENTRY_CUST_CREDIT_NOTE_ENTRY: if (gncEntryGetOrder (entry) != NULL) { dialog = gtk_message_dialog_new(GTK_WINDOW(ledger->parent), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", title); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", message); gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("_Don't Record"), GTK_RESPONSE_REJECT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, _("_Record"), GTK_RESPONSE_ACCEPT, NULL); response = gnc_dialog_run(GTK_DIALOG(dialog), "invoice_entry_changed"); gtk_widget_destroy(dialog); break; } /* FALL THROUGH */ default: response = GTK_RESPONSE_ACCEPT; break; } } switch (response) { case GTK_RESPONSE_ACCEPT: break; case GTK_RESPONSE_REJECT: { VirtualCellLocation vcell_loc; GncEntry *new_entry; new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc); gnc_entry_ledger_cancel_cursor_changes (ledger); if (gnc_entry_ledger_find_entry (ledger, new_entry, &vcell_loc)) virt_loc.vcell_loc = vcell_loc; gnc_table_find_close_valid_cell (ledger->table, &virt_loc, exact_traversal); *p_new_virt_loc = virt_loc; } break; case GTK_RESPONSE_CANCEL: default: return TRUE; } return FALSE; }