Exemplo n.º 1
0
void
gnucash_register_goto_next_virt_row (GnucashRegister *reg)
{
    GnucashSheet *sheet;
    VirtualLocation virt_loc;
    int start_virt_row;

    g_return_if_fail (reg != NULL);
    g_return_if_fail (GNUCASH_IS_REGISTER(reg));

    sheet = GNUCASH_SHEET(reg->sheet);

    gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);

    /* Move down one physical row at a time until we
     * reach the next visible virtual cell. */
    start_virt_row = virt_loc.vcell_loc.virt_row;
    do
    {
        if (!gnc_table_move_vertical_position (sheet->table, &virt_loc, 1))
            return;
    }
    while (start_virt_row == virt_loc.vcell_loc.virt_row);

    if (virt_loc.vcell_loc.virt_row >= sheet->num_virt_rows)
        return;

    virt_loc.phys_row_offset = 0;
    virt_loc.phys_col_offset = 0;

    gnucash_sheet_goto_virt_loc (sheet, virt_loc);
}
Exemplo n.º 2
0
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);
    }
}
Exemplo n.º 3
0
void
gnucash_register_goto_next_matching_row (GnucashRegister *reg,
        VirtualLocationMatchFunc match,
        gpointer user_data)
{
    GnucashSheet *sheet;
    SheetBlockStyle *style;
    VirtualLocation virt_loc;

    g_return_if_fail (reg != NULL);
    g_return_if_fail (GNUCASH_IS_REGISTER(reg));
    g_return_if_fail (match != NULL);

    sheet = GNUCASH_SHEET (reg->sheet);

    gnucash_cursor_get_virt (GNUCASH_CURSOR(sheet->cursor), &virt_loc);

    do
    {
        if (!gnc_table_move_vertical_position (sheet->table,
                                               &virt_loc, 1))
            return;

        if (virt_loc.vcell_loc.virt_row >= sheet->num_virt_rows)
            return;

        style = gnucash_sheet_get_style (sheet, virt_loc.vcell_loc);
        if (!style || !style->cursor)
            return;
    }
    while (!match (virt_loc, user_data));

    virt_loc.phys_row_offset = 0;
    virt_loc.phys_col_offset = 0;

    gnucash_sheet_goto_virt_loc (sheet, virt_loc);
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
gboolean
gnc_table_move_tab (Table *table,
                    VirtualLocation *virt_loc,
                    gboolean move_right)
{
    VirtualCell *vcell;
    VirtualLocation vloc;
    BasicCell *cell;

    if ((table == NULL) || (virt_loc == NULL))
        return FALSE;

    vloc = *virt_loc;

    vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
    if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
        return FALSE;

    while (1)
    {
        CellIOFlags io_flags;

        if (move_right)
        {
            vloc.phys_col_offset++;

            if (vloc.phys_col_offset >= vcell->cellblock->num_cols)
            {
                if (!gnc_table_move_vertical_position (table, &vloc, 1))
                    return FALSE;

                vloc.phys_col_offset = 0;
            }
        }
        else
        {
            vloc.phys_col_offset--;

            if (vloc.phys_col_offset < 0)
            {
                if (!gnc_table_move_vertical_position (table, &vloc, -1))
                    return FALSE;

                vloc.phys_col_offset = vcell->cellblock->num_cols - 1;
            }
        }

        vcell = gnc_table_get_virtual_cell (table, vloc.vcell_loc);
        if ((vcell == NULL) || (vcell->cellblock == NULL) || !vcell->visible)
            return FALSE;

        cell = gnc_cellblock_get_cell (vcell->cellblock,
                                       vloc.phys_row_offset,
                                       vloc.phys_col_offset);
        if (!cell)
            continue;

        io_flags = gnc_table_get_io_flags (table, vloc);

        if (!(io_flags & XACC_CELL_ALLOW_INPUT))
            continue;

        if (io_flags & XACC_CELL_ALLOW_EXACT_ONLY)
            continue;

        break;
    }

    {
        gboolean changed = !virt_loc_equal (vloc, *virt_loc);

        *virt_loc = vloc;

        return changed;
    }
}
Exemplo n.º 6
0
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;
}