示例#1
0
static void
gnucash_cursor_get_pixel_coords (GnucashCursor *cursor,
                                 gint *x, gint *y,
                                 gint *w, gint *h)
{
    GnucashSheet *sheet = cursor->sheet;
    VirtualCellLocation vcell_loc;
    CellDimensions *cd;
    VirtualCell *vcell;
    SheetBlock *block;
    gint col;

    vcell_loc.virt_row = cursor->row;
    vcell_loc.virt_col = cursor->col;

    block = gnucash_sheet_get_block (sheet, vcell_loc);
    if (!block)
        return;

    vcell = gnc_table_get_virtual_cell (sheet->table, vcell_loc);
    if (!vcell)
        return;

    for (col = 0; col < vcell->cellblock->num_cols; col++)
    {
        BasicCell *cell;

        cell = gnc_cellblock_get_cell (vcell->cellblock, 0, col);
        if (cell && cell->cell_name)
            break;
    }

    *y = block->origin_y;

    cd = gnucash_style_get_cell_dimensions (block->style, 0, col);
    if (cd)
        *x = cd->origin_x;
    else
        *x = block->origin_x;

    for (col = vcell->cellblock->num_cols - 1; col >= 0; col--)
    {
        BasicCell *cell;

        cell = gnc_cellblock_get_cell (vcell->cellblock, 0, col);
        if (cell && cell->cell_name)
            break;
    }

    *h = block->style->dimensions->height;

    cd = gnucash_style_get_cell_dimensions (block->style, 0, col);
    if (cd)
        *w = cd->origin_x + cd->pixel_width - *x;
    else
        *w = block->style->dimensions->width - *x;
}
示例#2
0
int
gnc_cellblock_changed (CellBlock *cursor, gboolean include_conditional)
{
    int changed = 0;
    int r, c;

    if (!cursor)
        return FALSE;

    for (r = 0; r < cursor->num_rows; r++)
        for (c = 0; c < cursor->num_cols; c++)
        {
            BasicCell *cell;

            cell = gnc_cellblock_get_cell (cursor, r, c);
            if (cell == NULL)
                continue;

            if (gnc_basic_cell_get_changed (cell))
            {
                changed++;
                continue;
            }

            if (include_conditional &&
                    gnc_basic_cell_get_conditionally_changed (cell))
                changed++;
        }

    return changed;
}
示例#3
0
void
gnc_table_leave_update (Table *table, VirtualLocation virt_loc)
{
    CellLeaveFunc leave;
    BasicCell *cell;
    CellBlock *cb;
    int cell_row;
    int cell_col;

    if (table == NULL)
        return;

    cb = table->current_cursor;

    cell_row = virt_loc.phys_row_offset;
    cell_col = virt_loc.phys_col_offset;

    ENTER("proposed (%d %d) rel(%d %d)\n",
          virt_loc.vcell_loc.virt_row,
          virt_loc.vcell_loc.virt_col,
          cell_row, cell_col);

    /* OK, if there is a callback for this cell, call it */
    cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
    if (!cell)
    {
        LEAVE("no cell");
        return;
    }

    leave = cell->leave_cell;

    if (leave)
    {
        char * old_value;

        old_value = g_strdup (cell->value);

        leave (cell);

        if (g_strcmp0 (old_value, cell->value) != 0)
        {
            if (gnc_table_model_read_only (table->model))
            {
                PWARN ("leave update changed read-only table");
            }

            cell->changed = TRUE;
        }

        g_free (old_value);
    }
    LEAVE("");
}
示例#4
0
BasicCell *
gnc_table_get_cell (Table *table, VirtualLocation virt_loc)
{
    VirtualCell *vcell;

    if (!table)
        return NULL;

    vcell = gnc_table_get_virtual_cell (table, virt_loc.vcell_loc);
    if (!vcell)
        return NULL;

    return gnc_cellblock_get_cell (vcell->cellblock,
                                   virt_loc.phys_row_offset,
                                   virt_loc.phys_col_offset);
}
示例#5
0
static void
copy_cursor_row (TableLayout *layout, CellBlock *to, CellBlock *from, int row)
{
    int col;

    for (col = 0; col < from->num_cols; col++)
    {
        BasicCell *cell;

        cell = gnc_cellblock_get_cell (from, row, col);
        if (!cell || !cell->cell_name)
            continue;

        gnc_table_layout_set_cell (layout, to, cell->cell_name, row, col);
    }
}
示例#6
0
gboolean
gnc_table_get_cell_location (Table *table,
                             const char *cell_name,
                             VirtualCellLocation vcell_loc,
                             VirtualLocation *virt_loc)
{
    VirtualCell *vcell;
    CellBlock *cellblock;
    int cell_row, cell_col;

    if (table == NULL)
        return FALSE;

    vcell = gnc_table_get_virtual_cell (table, vcell_loc);
    if (vcell == NULL)
        return FALSE;

    cellblock = vcell->cellblock;

    for (cell_row = 0; cell_row < cellblock->num_rows; cell_row++)
        for (cell_col = 0; cell_col < cellblock->num_cols; cell_col++)
        {
            BasicCell *cell;

            cell = gnc_cellblock_get_cell (cellblock, cell_row, cell_col);
            if (!cell)
                continue;

            if (gnc_basic_cell_has_name (cell, cell_name))
            {
                if (virt_loc != NULL)
                {
                    virt_loc->vcell_loc = vcell_loc;

                    virt_loc->phys_row_offset = cell_row;
                    virt_loc->phys_col_offset = cell_col;
                }

                return TRUE;
            }
        }

    return FALSE;
}
示例#7
0
void
gnc_cellblock_clear_changes (CellBlock *cursor)
{
    int r, c;

    if (!cursor)
        return;

    for (r = 0; r < cursor->num_rows; r++)
        for (c = 0; c < cursor->num_cols; c++)
        {
            BasicCell *cell;

            cell = gnc_cellblock_get_cell (cursor, r, c);
            if (cell == NULL)
                continue;

            gnc_basic_cell_set_changed (cell, FALSE);
            gnc_basic_cell_set_conditionally_changed (cell, FALSE);
        }
}
示例#8
0
static void
gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
                 int x, int y, int width, int height)
{
    GncHeader *header = GNC_HEADER(item);
    SheetBlockStyle *style = header->style;
    Table *table = header->sheet->table;
    VirtualLocation virt_loc;
    VirtualCell *vcell;
    CellDimensions *cd;
    GdkColor *bg_color;
    int xpaint, ypaint;
    const char *text;
    CellBlock *cb;
    guint32 argb, color_type;
    int i, j;
    int w, h;
    PangoLayout *layout;

    virt_loc.vcell_loc.virt_row = 0;
    virt_loc.vcell_loc.virt_col = 0;
    virt_loc.phys_row_offset = 0;
    virt_loc.phys_col_offset = 0;

    if (header->sheet->use_theme_colors)
    {
        color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
                     NULL);
        bg_color = get_gtkrc_color(header->sheet, color_type);
    }
    else
    {
        argb = gnc_table_get_bg_color (table, virt_loc, NULL);
        bg_color = gnucash_color_argb_to_gdk (argb);
    }

    h = style->dimensions->height;
    h *= header->num_phys_rows;
    h /= header->style->nrows;

    gdk_gc_set_foreground (header->gc, bg_color);

    gdk_draw_rectangle (drawable, header->gc, TRUE, 0, 0,
                        style->dimensions->width, h);

    gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
    gdk_gc_set_foreground (header->gc, &gn_black);

    gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y,
                        style->dimensions->width, h);

    gdk_draw_line (drawable, header->gc, 0, h + 1,
                   style->dimensions->width, h + 1);

    gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
    gdk_gc_set_background (header->gc, &gn_white);
    gdk_gc_set_foreground (header->gc, &gn_black);
    /*font = gnucash_register_font;*/

    vcell = gnc_table_get_virtual_cell
            (table, table->current_cursor_loc.vcell_loc);
    cb = vcell ? vcell->cellblock : NULL;

    ypaint = -y;
    h = 0;

    for (i = 0; i < style->nrows; i++)
    {
        xpaint = -x;
        virt_loc.phys_row_offset = i;

        /* TODO: This routine is duplicated in several places.
           Can we abstract at least the cell drawing routine?
           That way we'll be sure everything is drawn
           consistently, and cut down on maintenance issues. */

        for (j = 0; j < style->ncols; j++)
        {
            /*                        gint x_offset, y_offset;*/
            GdkRectangle rect;
            BasicCell *cell;

            virt_loc.phys_col_offset = j;

            cd = gnucash_style_get_cell_dimensions (style, i, j);

            if (header->in_resize && (j == header->resize_col))
                w = header->resize_col_width;
            else
                w = cd->pixel_width;

            cell = gnc_cellblock_get_cell (cb, i, j);
            if (!cell || !cell->cell_name)
            {
                xpaint += w;
                continue;
            }

            h = cd->pixel_height;

            gdk_draw_rectangle (drawable, header->gc, FALSE,
                                xpaint, ypaint, w, h);

            virt_loc.vcell_loc =
                table->current_cursor_loc.vcell_loc;
            text = gnc_table_get_label (table, virt_loc);
            if (!text)
                text = "";

            layout = gtk_widget_create_pango_layout (GTK_WIDGET (header->sheet), text);

            /*y_offset = ((h / 2) +
                                    (((font->ascent + font->descent) / 2) -
                                     font->descent));
                        y_offset++;*/

            switch (gnc_table_get_align (table, virt_loc))
            {
            default:
            case CELL_ALIGN_LEFT:
                pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
                break;

            case CELL_ALIGN_RIGHT:
                pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
                break;

            case CELL_ALIGN_CENTER:
                pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
                break;
            }

            rect.x = xpaint + CELL_HPADDING;
            rect.y = ypaint + 1;
            rect.width = MAX (0, w - (2 * CELL_HPADDING));
            rect.height = h - 2;

            gdk_gc_set_clip_rectangle (header->gc, &rect);

            gdk_draw_layout (drawable,
                             header->gc,
                             xpaint + CELL_HPADDING,
                             ypaint + 1,
                             layout);

            g_object_unref (layout);

            gdk_gc_set_clip_rectangle (header->gc, NULL);

            xpaint += w;
        }

        ypaint += h;
    }
}
示例#9
0
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");
}
示例#10
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;
    }
}
示例#11
0
gboolean
gnc_table_direct_update (Table *table,
                         VirtualLocation virt_loc,
                         char **newval_ptr,
                         int *cursor_position,
                         int *start_selection,
                         int *end_selection,
                         gpointer gui_data)
{
    gboolean result;
    BasicCell *cell;
    CellBlock *cb;
    int cell_row;
    int cell_col;
    char * old_value;

    g_return_val_if_fail (table, FALSE);
    g_return_val_if_fail (table->model, FALSE);

    if (gnc_table_model_read_only (table->model))
    {
        PWARN ("input to read-only table");
        return FALSE;
    }

    cb = table->current_cursor;

    cell_row = virt_loc.phys_row_offset;
    cell_col = virt_loc.phys_col_offset;

    cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
    if (!cell)
        return FALSE;

    ENTER ("");

    if (cell->direct_update == NULL)
    {
        LEAVE("no direct update");
        return FALSE;
    }

    old_value = g_strdup (cell->value);

    result = cell->direct_update (cell, cursor_position, start_selection,
                                  end_selection, gui_data);

    if (g_strcmp0 (old_value, cell->value) != 0)
    {
        if (!gnc_table_confirm_change (table, virt_loc))
        {
            gnc_basic_cell_set_value (cell, old_value);
            *newval_ptr = NULL;
            result = TRUE;
        }
        else
        {
            cell->changed = TRUE;
            *newval_ptr = cell->value;
        }
    }
    else
        *newval_ptr = NULL;

    g_free (old_value);

    if (table->gui_handlers.redraw_help)
        table->gui_handlers.redraw_help (table);

    LEAVE("");
    return result;
}
示例#12
0
/* Returned result should not be touched by the caller.
 * NULL return value means the edit was rejected. */
const char *
gnc_table_modify_update (Table *table,
                         VirtualLocation virt_loc,
                         const char *change,
                         int change_len,
                         const char *newval,
                         int newval_len,
                         int *cursor_position,
                         int *start_selection,
                         int *end_selection,
                         gboolean *cancelled)
{
    gboolean changed = FALSE;
    CellModifyVerifyFunc mv;
    BasicCell *cell;
    CellBlock *cb;
    int cell_row;
    int cell_col;
    char * old_value;

    g_return_val_if_fail (table, NULL);
    g_return_val_if_fail (table->model, NULL);

    if (gnc_table_model_read_only (table->model))
    {
        PWARN ("change to read-only table");
        return NULL;
    }

    cb = table->current_cursor;

    cell_row = virt_loc.phys_row_offset;
    cell_col = virt_loc.phys_col_offset;

    ENTER ("");

    if (!gnc_table_confirm_change (table, virt_loc))
    {
        if (cancelled)
            *cancelled = TRUE;

        LEAVE("change cancelled");
        return NULL;
    }

    if (cancelled)
        *cancelled = FALSE;

    /* OK, if there is a callback for this cell, call it */
    cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
    if (!cell)
    {
        LEAVE("no cell");
        return NULL;
    }

    mv = cell->modify_verify;

    old_value = g_strdup (cell->value);

    if (mv)
    {
        mv (cell, change, change_len, newval, newval_len,
            cursor_position, start_selection, end_selection);
    }
    else
    {
        gnc_basic_cell_set_value (cell, newval);
    }

    if (g_strcmp0 (old_value, cell->value) != 0)
    {
        changed = TRUE;
        cell->changed = TRUE;
    }

    g_free (old_value);

    if (table->gui_handlers.redraw_help)
        table->gui_handlers.redraw_help (table);

    LEAVE ("change %d %d (relrow=%d relcol=%d) val=%s\n",
           virt_loc.vcell_loc.virt_row,
           virt_loc.vcell_loc.virt_col,
           cell_row, cell_col,
           cell->value ? cell->value : "(null)");

    if (changed)
        return cell->value;
    else
        return NULL;
}
示例#13
0
/* Handle the non gui-specific parts of a cell enter callback */
gboolean
gnc_table_enter_update (Table *table,
                        VirtualLocation virt_loc,
                        int *cursor_position,
                        int *start_selection,
                        int *end_selection)
{
    gboolean can_edit = TRUE;
    CellEnterFunc enter;
    BasicCell *cell;
    CellBlock *cb;
    int cell_row;
    int cell_col;
    CellIOFlags io_flags;

    if (table == NULL)
        return FALSE;

    cb = table->current_cursor;

    cell_row = virt_loc.phys_row_offset;
    cell_col = virt_loc.phys_col_offset;

    ENTER("enter %d %d (relrow=%d relcol=%d)",
          virt_loc.vcell_loc.virt_row,
          virt_loc.vcell_loc.virt_col,
          cell_row, cell_col);

    /* OK, if there is a callback for this cell, call it */
    cell = gnc_cellblock_get_cell (cb, cell_row, cell_col);
    if (!cell)
    {
        LEAVE("no cell");
        return FALSE;
    }

    io_flags = gnc_table_get_io_flags (table, virt_loc);
    if (io_flags == XACC_CELL_ALLOW_READ_ONLY)
    {
        LEAVE("read only cell");
        return FALSE;
    }

    enter = cell->enter_cell;

    if (enter)
    {
        char * old_value;

        DEBUG("gnc_table_enter_update(): %d %d has enter handler\n",
              cell_row, cell_col);

        old_value = g_strdup (cell->value);

        can_edit = enter (cell, cursor_position, start_selection, end_selection);

        if (g_strcmp0 (old_value, cell->value) != 0)
        {
            if (gnc_table_model_read_only (table->model))
            {
                PWARN ("enter update changed read-only table");
            }

            cell->changed = TRUE;
        }

        g_free (old_value);
    }

    if (table->gui_handlers.redraw_help)
        table->gui_handlers.redraw_help (table);

    LEAVE("return %d\n", can_edit);
    return can_edit;
}
示例#14
0
static void
gnc_header_draw_offscreen (GncHeader *header)
{
    SheetBlockStyle *style = header->style;
    Table *table = header->sheet->table;
    VirtualLocation virt_loc;
    VirtualCell *vcell;
    guint32 color_type;
    GtkStyleContext *stylectxt = gtk_widget_get_style_context (GTK_WIDGET(header));
    GdkRGBA color;
    int row_offset;
    CellBlock *cb;
    int i;
    cairo_t *cr;

    virt_loc.vcell_loc.virt_row = 0;
    virt_loc.vcell_loc.virt_col = 0;
    virt_loc.phys_row_offset = 0;
    virt_loc.phys_col_offset = 0;

    gtk_style_context_save (stylectxt);

    // Get the background color type and apply the css class
    color_type = gnc_table_get_bg_color (table, virt_loc, NULL);
    gnucash_get_style_classes (header->sheet, stylectxt, color_type);

    if (header->surface)
        cairo_surface_destroy (header->surface);
    header->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                                header->width,
                                                header->height);

    cr = cairo_create (header->surface);

    // Fill background color of header
    gtk_render_background (stylectxt, cr, 0, 0, header->width, header->height);

    gdk_rgba_parse (&color, "black");
    cairo_set_source_rgb (cr, color.red, color.green, color.blue);
    cairo_rectangle (cr, 0.5, 0.5, header->width - 1.0, header->height - 1.0);
    cairo_set_line_width (cr, 1.0);
    cairo_stroke (cr);

    // Draw bottom horizontal line, makes bottom line thicker
    cairo_move_to (cr, 0.5, header->height - 1.5);
    cairo_line_to (cr, header->width - 1.0, header->height - 1.5);
    cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
    cairo_set_line_width (cr, 1.0);
    cairo_stroke (cr);

    /*font = gnucash_register_font;*/

    vcell = gnc_table_get_virtual_cell
            (table, table->current_cursor_loc.vcell_loc);
    cb = vcell ? vcell->cellblock : NULL;
    row_offset = 0;

    for (i = 0; i < style->nrows; i++)
    {
        int col_offset = 0;
        int h = 0, j;
        virt_loc.phys_row_offset = i;

        /* TODO: This routine is duplicated in several places.
           Can we abstract at least the cell drawing routine?
           That way we'll be sure everything is drawn
           consistently, and cut down on maintenance issues. */

        for (j = 0; j < style->ncols; j++)
        {
            CellDimensions *cd;
            double text_x, text_y, text_w, text_h;
            BasicCell *cell;
            const char *text;
            int w;
            PangoLayout *layout;

            virt_loc.phys_col_offset = j;

            cd = gnucash_style_get_cell_dimensions (style, i, j);
            h = cd->pixel_height;
            if (header->in_resize && (j == header->resize_col))
                w = header->resize_col_width;
            else
                w = cd->pixel_width;

            cell = gnc_cellblock_get_cell (cb, i, j);
            if (!cell || !cell->cell_name)
            {
                col_offset += w;
                continue;
            }

            cairo_rectangle (cr, col_offset - 0.5, row_offset + 0.5, w, h);
            cairo_set_line_width (cr, 1.0);
            cairo_stroke (cr);

            virt_loc.vcell_loc =
                table->current_cursor_loc.vcell_loc;
            text = gnc_table_get_label (table, virt_loc);
            if (!text)
                text = "";

            layout = gtk_widget_create_pango_layout (GTK_WIDGET (header->sheet), text);
            switch (gnc_table_get_align (table, virt_loc))
            {
            default:
            case CELL_ALIGN_LEFT:
                pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
                break;

            case CELL_ALIGN_RIGHT:
                pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
                break;

            case CELL_ALIGN_CENTER:
                pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
                break;
            }

            text_x = col_offset + CELL_HPADDING;
            text_y = row_offset + 1;
            text_w = MAX (0, w - (2 * CELL_HPADDING));
            text_h = h - 2;
            cairo_save (cr);
            cairo_rectangle (cr, text_x, text_y, text_w, text_h);
            cairo_clip (cr);

            gtk_render_layout (stylectxt, cr, text_x, text_y, layout);

            cairo_restore (cr);
            g_object_unref (layout);

            col_offset += w;
        }
        row_offset += h;
    }
    gtk_style_context_restore (stylectxt);

    cairo_destroy (cr);
}