gtk table from rob browning

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@831 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 1998-04-22 03:08:02 +00:00
parent c67dc15470
commit f69c987698
2 changed files with 853 additions and 0 deletions

707
src/register/table-gtk.c Normal file
View File

@ -0,0 +1,707 @@
/*
* FILE:
* table-gtk.c
*
* FUNCTION:
* Implements the infrastructure for the displayed table.
* This is the Gtk implementation;
*
* HISTORY:
* Copyright (c) 1998 Linas Vepstas
* Copyright (c) 1998 Rob Browning <rlb@cs.utexas.edu>
*/
/********************************************************************\
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include "cellblock.h"
#include "table-allgui.h"
#include "table-gtk.h"
#if 0
static void enterCB (GtkWidget * mw, gpointer cd, gpointer cb);
static void leaveCB (GtkWidget * mw, gpointer cd, gpointer cb);
static void modifyCB (GtkWidget * mw, gpointer cd, gpointer cb);
static void traverseCB (GtkWidget * mw, gpointer cd, gpointer cb);
/* The XrmQuarks are used to figure out the direction of
* traversal from cell to cell */
//static XrmQuark QPointer, QLeft, QRight, QUp, QDown;
//static Boolean haveQuarks = False;
#endif
/* ==================================================== */
Table *
xaccMallocTable (void)
{
Table *table;
table = (Table *) malloc (sizeof (Table));
xaccInitTable (table);
return table;
}
/* ==================================================== */
void
xaccInitTable (Table * table)
{
table->table_widget = 0;
table->next_tab_group = 0;
table->num_phys_rows = -1;
table->num_phys_cols = -1;
table->num_virt_rows = -1;
table->num_virt_cols = -1;
table->current_cursor = NULL;
table->current_cursor_virt_row = -1;
table->current_cursor_virt_col = -1;
table->current_cursor_phys_row = -1;
table->current_cursor_phys_col = -1;
table->move_cursor = NULL;
table->client_data = NULL;
table->entries = NULL;
table->locators = NULL;
table->user_data = NULL;
table->handlers = NULL;
/* invalidate the "previous" traversed cell */
table->prev_phys_traverse_row = -1;
table->prev_phys_traverse_col = -1;
}
/* ==================================================== */
void
xaccDestroyTable (Table * table)
{
/* free the gui-independent parts */
xaccFreeTableEntries (table);
/* Let GTK know we're finished with this */
gtk_widget_unref(table->table_widget);
/* intialize vars to null value so that any access is voided. */
xaccInitTable (table);
free (table);
}
/* ==================================================== */
void
xaccSetTableSize (Table * table, int phys_rows, int phys_cols,
int virt_rows, int virt_cols)
{
xaccTableResize (table, phys_rows, phys_cols, virt_rows, virt_cols);
/* invalidate the "previous" traversed cell */
table->prev_phys_traverse_row = -1;
table->prev_phys_traverse_col = -1;
/* invalidate the current cursor position, if needed */
if ((table->current_cursor_virt_row >= table->num_virt_rows) ||
(table->current_cursor_virt_col >= table->num_virt_cols)) {
table->current_cursor_virt_row = -1;
table->current_cursor_virt_col = -1;
table->current_cursor_phys_row = -1;
table->current_cursor_phys_col = -1;
table->current_cursor = NULL;
}
}
/* ==================================================== */
void
xaccNextTabGroup (Table *table, GtkWidget * w)
{
table->next_tab_group = w;
}
#if 0
/* ==================================================== */
/* this routine calls the individual cell callbacks */
/* hack alert -- assumes that header is first cell */
static void
cellCB (GtkWidget *mw, gpointer cd, gpointer cb)
{
Table *table;
XbaeMatrixDefaultActionCallbackStruct *cbs;
int row, col;
int rel_row, rel_col;
CellBlock *arr, *header;
int invalid = 0;
table = (Table *) cd;
arr = table->current_cursor;
cbs = (XbaeMatrixDefaultActionCallbackStruct *) cb;
row = cbs->row;
col = cbs->column;
/* can't edit outside of the physical space */
invalid = (0 > row) || (0 > col) ;
invalid = invalid || (row >= table->num_phys_rows);
invalid = invalid || (col >= table->num_phys_cols);
/* header rows cannot be modified */
header = table->handlers[0][0];
invalid = invalid || (row < header->numRows);
/* compute the cell location */
rel_row = table->locators[row][col]->phys_row_offset;
rel_col = table->locators[row][col]->phys_col_offset;
/* check for a cell handler, but only if cell adress is valid */
if (arr && !invalid) {
if (! (arr->cells[rel_row][rel_col])) {
invalid = TRUE;
} else {
/* if cell is marked as output-only,
* then don't call callbacks */
if (0 == (arr->cells[rel_row][rel_col])->input_output) {
invalid = TRUE;
}
}
} else {
invalid = TRUE;
}
/* oops the callback failed for some reason ...
* reject the enter/edit/leave and return */
if (invalid) {
switch (cbs->reason) {
case XbaeEnterCellReason: {
XbaeMatrixEnterCellCallbackStruct *ecbs;
ecbs = (XbaeMatrixEnterCellCallbackStruct *) cbs;
ecbs->doit = False;
ecbs->map = False;
break;
}
case XbaeModifyVerifyReason: {
XbaeMatrixModifyVerifyCallbackStruct *mvcbs;
mvcbs = (XbaeMatrixModifyVerifyCallbackStruct *) cbs;
mvcbs->verify->doit = False;
break;
}
case XbaeLeaveCellReason: {
XbaeMatrixLeaveCellCallbackStruct *lcbs;
lcbs = (XbaeMatrixLeaveCellCallbackStruct *) cbs;
/* must set doit to true in order to be able to leave the cell */
lcbs->doit = True;
break;
}
default:
break;
}
return;
}
/* if we got to here, then there is a cell handler for
* this cell. Dispatch for processing. */
switch (cbs->reason) {
case XbaeEnterCellReason: {
xaccVerifyCursorPosition (table, row, col);
enterCB (mw, cd, cb);
break;
}
case XbaeModifyVerifyReason: {
modifyCB (mw, cd, cb);
break;
}
case XbaeLeaveCellReason: {
leaveCB (mw, cd, cb);
break;
}
default:
break;
}
}
/* ==================================================== */
/* this callback assumes that basic error checking has already
* been performed. */
static void
enterCB (GtkWidget * mw, gpointer cd, gpointer cb)
{
Table *table;
CellBlock *arr;
XbaeMatrixEnterCellCallbackStruct *cbs;
int row, col;
int rel_row, rel_col;
const char * (*enter) (BasicCell *, const char *);
table = (Table *) cd;
arr = table->current_cursor;
cbs = (XbaeMatrixEnterCellCallbackStruct *) cb;
/* compute the cell location */
row = cbs->row;
col = cbs->column;
rel_row = table->locators[row][col]->phys_row_offset;
rel_col = table->locators[row][col]->phys_col_offset;
printf ("enter %d %d \n", row, col);
/* since we are here, there must be a cell handler.
* therefore, we accept entry into the cell by default,
*/
cbs->doit = True;
cbs->map = True;
/* OK, if there is a callback for this cell, call it */
enter = arr->cells[rel_row][rel_col]->enter_cell;
if (enter) {
const char *val;
char *retval;
val = table->entries[row][col];
retval = (char *) enter (arr->cells[rel_row][rel_col], val);
if (NULL == retval) retval = (char *) val;
if (val != retval) {
if (table->entries[row][col]) free (table->entries[row][col]);
table->entries[row][col] = retval;
(arr->cells[rel_row][rel_col])->changed = 0xffffffff;
XbaeMatrixSetCell (mw, row, col, retval);
XbaeMatrixRefreshCell (mw, row, col);
/* don't map a text widget */
cbs->map = False;
cbs->doit = False;
}
}
/* record this position as the cell that will be
* traversed out of if a traverse even happens */
table->prev_phys_traverse_row = row;
table->prev_phys_traverse_col = col;
}
/* ==================================================== */
/* this routine calls the individual cell callbacks */
static void
modifyCB (GtkWidget * mw, gpointer cd, gpointer cb)
{
Table *table;
CellBlock *arr;
XbaeMatrixModifyVerifyCallbackStruct *cbs;
int row, col;
int rel_row, rel_col;
const char * (*mv) (BasicCell *,
const char *,
const char *,
const char *);
const char *oldval, *change;
char *newval;
const char *retval;
int len;
table = (Table *) cd;
arr = table->current_cursor;
cbs = (XbaeMatrixModifyVerifyCallbackStruct *) cb;
/* compute the cell location */
row = cbs->row;
col = cbs->column;
rel_row = table->locators[row][col]->phys_row_offset;
rel_col = table->locators[row][col]->phys_col_offset;
/* accept edits by default, unless the cell handler rejects them */
cbs->verify->doit = True;
oldval = cbs->prev_text;
change = cbs->verify->text->ptr;
/* first, compute the newval string */
len = 1;
if (oldval) len += strlen (oldval);
if (change) len += strlen (change);
newval = (char *) malloc (len);
/* text may be inserted, or deleted, or replaced ... */
newval[0] = 0;
strncat (newval, oldval, cbs->verify->startPos);
if (change) strcat (newval, change);
strcat (newval, &oldval[(cbs->verify->endPos)]);
/* OK, if there is a callback for this cell, call it */
mv = arr->cells[rel_row][rel_col]->modify_verify;
if (mv) {
retval = (*mv) (arr->cells[rel_row][rel_col], oldval, change, newval);
/* if the callback returned a non-null value, allow the edit */
if (retval) {
/* update data. bounds check done earlier */
free (table->entries[row][col]);
table->entries[row][col] = (char *) retval;
(arr->cells[rel_row][rel_col])->changed = 0xffffffff;
/* if the callback modified the display string,
* update the display cell as well */
if (retval != newval) {
XbaeMatrixSetCell (mw, row, col, (char *) retval);
XbaeMatrixRefreshCell (mw, row, col);
XbaeMatrixSetCursorPosition (mw, (cbs->verify->endPos) +1);
/* the default update has already been overridden,
* so don't allow Xbae to update */
cbs->verify->doit = False;
/* avoid wasting memory */
free (newval);
}
} else {
/* NULL return value means the edit was rejected */
cbs->verify->doit = False;
/* avoid wasting memory */
free(newval);
}
} else {
/* update data. bounds check done earlier */
free (table->entries[row][col]);
table->entries[row][col] = newval;
(arr->cells[rel_row][rel_col])->changed = 0xffffffff;
}
}
/* ==================================================== */
static void
leaveCB (GtkWidget * mw, gpointer cd, gpointer cb)
{
Table *table;
CellBlock *arr;
XbaeMatrixLeaveCellCallbackStruct *cbs;
int row, col;
int rel_row, rel_col;
const char * (*leave) (BasicCell *, const char *);
char * newval;
table = (Table *) cd;
arr = table->current_cursor;
cbs = (XbaeMatrixLeaveCellCallbackStruct *) cb;
/* compute the cell location */
row = cbs->row;
col = cbs->column;
rel_row = table->locators[row][col]->phys_row_offset;
rel_col = table->locators[row][col]->phys_col_offset;
printf ("leave %d %d \n", row, col);
/* by default, accept whatever the final proposed edit is */
cbs->doit = True;
/* OK, if there is a callback for this cell, call it */
leave = arr->cells[rel_row][rel_col]->leave_cell;
if (leave) {
const char *val, *retval;
val = cbs->value;
retval = leave (arr->cells[rel_row][rel_col], val);
newval = (char *) retval;
if (NULL == retval) newval = strdup (val);
if (val == retval) newval = strdup (val);
/* if the leave() routine declared a new string, lets use it */
if ( retval && (retval != val)) {
cbs->value = strdup (retval);
}
} else {
newval = strdup (cbs->value);
}
/* save whatever was returned; but lets check for
* changes to avoid roiling the cells too much */
if (table->entries[row][col]) {
if (strcmp (table->entries[row][col], newval)) {
free (table->entries[row][col]);
table->entries[row][col] = newval;
(arr->cells[rel_row][rel_col])->changed = 0xffffffff;
} else {
/* leave() allocated memory, which we will not be using ... */
free (newval);
}
} else {
table->entries[row][col] = newval;
(arr->cells[rel_row][rel_col])->changed = 0xffffffff;
}
}
/* ==================================================== */
static void
traverseCB (GtkWidget * mw, gpointer cd, gpointer cb)
{
Table *table;
CellBlock *arr;
XbaeMatrixTraverseCellCallbackStruct *cbs;
int row, col;
int rel_row, rel_col;
table = (Table *) cd;
arr = table->current_cursor;
cbs = (XbaeMatrixTraverseCellCallbackStruct *) cb;
row = cbs->row;
col = cbs->column;
/* If the quark is zero, then it is likely that we are
* here because we traversed out of a cell that had a
* ComboBox in it. The ComboCell is clever enough to put
* us back into the register after tabing out of it.
* However, its not (cannot be) clever enough to pretend
* that it was a tab group in the register. Thus,
* we will emulate that we left a tab group in the register
* to get here. To put it more simply, we just set the
* row and column to that of the ComboCell, which we had
* previously recorded, and continue on as if nothing
* happened.
* BTW -- note that we are emulating a normal, right-moving tab.
* Backwards tabs are broken.
*/
if (NULLQUARK == cbs->qparam) {
if ((0==row) && (0==col)) {
if ((0 <= table->prev_phys_traverse_row) &&
(0 <= table->prev_phys_traverse_col)) {
cbs->qparam = QRight;
row = table->prev_phys_traverse_row;
col = table->prev_phys_traverse_col;
}
}
}
xaccVerifyCursorPosition (table, row, col);
/* compute the cell location */
rel_row = table->locators[row][col]->phys_row_offset;
rel_col = table->locators[row][col]->phys_col_offset;
/* process right-moving traversals */
if (QRight == cbs->qparam) {
int next_row = arr->right_traverse_r[rel_row][rel_col];
int next_col = arr->right_traverse_c[rel_row][rel_col];
/* if we are at the end of the traversal chain,
* hop out of this tab group, and into the next.
*/
if ((0 > next_row) || (0 > next_col)) {
/* reverse the sign of next_row, col to be positive. */
cbs->next_row = row - rel_row - next_row -1;
cbs->next_column = col - rel_col - next_col -1;
cbs->qparam = NULLQUARK;
if (table->next_tab_group) {
XmProcessTraversal (table->next_tab_group,
XmTRAVERSE_CURRENT);
}
} else {
cbs->next_row = row - rel_row + next_row;
cbs->next_column = col - rel_col + next_col;
}
}
table->prev_phys_traverse_row = cbs->next_row;
table->prev_phys_traverse_col = cbs->next_column;
}
#endif
/* ==================================================== */
GtkWidget *
xaccCreateTable (Table *table, GtkWidget * parent)
{
CellBlock *curs;
unsigned char * alignments;
short * widths;
GtkWidget * reg;
int num_header_rows = 0;
int i;
if (!table) return 0;
#if 0
/* if quarks have not yet been initialized for this
* application, initialize them now. */
if (!haveQuarks) {
QPointer = XrmPermStringToQuark ("Pointer");
QLeft = XrmPermStringToQuark ("Left");
QRight = XrmPermStringToQuark ("Right");
QUp = XrmPermStringToQuark ("Up");
QDown = XrmPermStringToQuark ("Down");
haveQuarks = True;
}
#endif
/* The 0'th row of the handlers is defined as the header */
alignments = NULL;
widths = NULL;
curs = table->handlers[0][0];
alignments = curs->alignments;
widths = curs->widths;
num_header_rows = curs->numRows;
/* copy header data into entries cache */
xaccRefreshHeader (table);
/* create the matrix widget */
if(num_header_rows > 1) {
fprintf(stderr,
"gnucash: GTK implementation only supports one header row.\n");
num_header_rows = 1;
}
if(num_header_rows == 0) {
reg = gtk_clist_new(table->num_phys_cols);
} else {
reg = gtk_clist_new_with_titles(table->num_phys_cols, table->entries[0]);
gtk_clist_freeze(GTK_CLIST(reg));
for(i = 0; i < table->num_phys_cols; i++) {
/* Widths are in units of characters, not pixels, so we have
this hack. It should be fixed later... */
gtk_clist_set_column_width(GTK_CLIST(reg), i, widths[i] * 5);
}
gtk_clist_thaw(GTK_CLIST(reg));
}
gtk_clist_freeze(GTK_CLIST(reg));
for(i = num_header_rows; i < table->num_phys_rows; i++) {
gtk_clist_append(GTK_CLIST(reg), table->entries[i]);
}
gtk_clist_thaw(GTK_CLIST(reg));
/* size?
alignments?
grid type?
shadow type?
*/
gtk_container_add(GTK_CONTAINER(parent), reg);
gtk_widget_show(reg);
#if 0
/* add callbacks that handle cell editing */
XtAddCallback (reg, XmNenterCellCallback, cellCB, (gpointer)table);
XtAddCallback (reg, XmNleaveCellCallback, cellCB, (gpointer)table);
XtAddCallback (reg, XmNmodifyVerifyCallback, cellCB, (gpointer)table);
XtAddCallback (reg, XmNtraverseCellCallback, traverseCB, (gpointer)table);
#endif
table->table_widget = reg;
#if 0
/* if any of the cells have GUI specific components that need
* initialization, initialize them now. */
curs = table->current_cursor;
if (curs) {
int i,j;
for (i=0; i<curs->numRows; i++) {
for (j=0; j<curs->numCols; j++) {
BasicCell *cell;
cell = curs->cells[i][j];
if (cell) {
void (*xt_realize) (BasicCell *,
void *gui,
int pixel_width);
xt_realize = cell->realize;
if (xt_realize) {
int pixel_width;
pixel_width = XbaeMatrixGetColumnPixelWidth (reg, j);
xt_realize (cell, ((void *) reg), pixel_width);
}
}
}
}
}
#endif
return (reg);
}
/* ==================================================== */
void
xaccRefreshTableGUI (Table * table)
{
CellBlock *curs;
GtkWidget * reg;
int num_header_rows = 0;
int i, j;
printf("xaccRefreshTableGUI(%p)\n", table);
if (!table) return;
/* The 0'th row of the handlers is defined as the header */
curs = table->handlers[0][0];
num_header_rows = curs->numRows;
reg = table->table_widget;
printf (" refresh numphysrows=%d numphyscols=%d \n",
table->num_phys_rows,table->num_phys_cols);
for (i = num_header_rows; i < table->num_phys_rows; i++) {
printf ("cell %d act:%s descr: %s \n",
i, table->entries[i][2],
table->entries[i][3]);
}
gtk_clist_freeze(GTK_CLIST(reg));
while(GTK_CLIST(reg)->rows < table->num_phys_rows) {
gtk_clist_append(GTK_CLIST(reg), NULL);
}
for(i = num_header_rows; i < table->num_phys_rows; i++) {
for(j = 0; j < table->num_phys_cols; j++) {
gtk_clist_set_text(GTK_CLIST(reg), i, j, table->entries[i][j]);
}
}
gtk_clist_thaw(GTK_CLIST(reg));
}
/* ================== end of file ======================= */
/*
Local Variables:
tab-width: 2
indent-tabs-mode: nil
mode: c-mode
c-indentation-style: gnu
eval: (c-set-offset 'block-open '-)
End:
*/

146
src/register/table-gtk.h Normal file
View File

@ -0,0 +1,146 @@
/*
* FILE:
* table-gtk.h
*
* FUNCTION:
* The table is the complete, displayed table.
* It consists of a header, followed by a simple
* list of repeated entries.
*
* It provides the mechanism to handle tab-traversal.
*
* HISTORY:
* Copyright (c) 1998 Linas Vepstas
* Copyright (c) 1998 Rob Browning <rlb@cs.utexas.edu>
*/
/********************************************************************\
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
#ifndef __XACC_TABLE_H__
#define __XACC_TABLE_H__
#include <gtk/gtk.h>
#include "basiccell.h"
#include "cellblock.h"
/* the Locator structure is used provide a mapping from
* the physical array of cells to the logical array of
* virtual cell blocks.
*
* There is one instance of Locator for each physical cell.
* The virt_row and virt_col members identify the corresponding
* cellblock/virtual cell that this physical cell is a member of.
* The two phys_offsets provide the location of the physical cell
* as an offset from the cell block origin. That is, the offsets
* should never be less than zero, or greater than the size of
* the cell block.
*/
struct _Locator {
short phys_row_offset;
short phys_col_offset;
short virt_row;
short virt_col;
};
typedef struct _Locator Locator;
typedef struct _Table Table;
struct _Table {
/* The number of "physical" rows/cols is the number
* of displayed one-line gui rows/cols in the table.
* The number of physical rows can differ from the
* number of "virtual" rows because each virtual row
* consist of one or more physical rows.
*/
int num_phys_rows;
int num_phys_cols;
int num_virt_rows;
int num_virt_cols;
/* the current cursor row/col is the virt row/col */
CellBlock *current_cursor;
int current_cursor_phys_row;
int current_cursor_phys_col;
int current_cursor_virt_row;
int current_cursor_virt_col;
/* callback that is called when the cursor is moved */
/* hack alert -- this should be a callback list, actually */
void (*move_cursor) (Table *, void *client_data);
void * client_data;
/* string values for each cell,
* of dimension num_phys_rows * num_phys_cols */
char ***entries;
/* handler locators for each cell,
* of dimension num_phys_rows * num_phys_cols */
Locator ***locators;
/* user hooks, of dimension num_virt_rows * num_virt_cols */
void ***user_data;
/* cell blocks, of dimension num_virt_rows * num_virt_cols */
CellBlock ***handlers;
/* private data, caches, etc. */
/* This is black-box stuff that no user of this class
* should ever want to access */
/* This class implements tab-key and arrow key
* traversal through the cells of the table.
* To perform this traversal, the location
* of the "previous" cell having input focus
* is required.
*/
int prev_phys_traverse_row;
int prev_phys_traverse_col;
/* Gtk-only date below, gui-independent data above */
/* protected data -- vital for the implementation,
* but not something we want to generally expose */
GtkWidget *table_widget; /* the CList */
GtkWidget *next_tab_group; /* where to traverse in the end */
};
Table * xaccMallocTable (void);
void xaccInitTable (Table *);
void xaccDestroyTable (Table *);
/* rsize the table to the indicated dimensions.
* calls the gui-independent xaccTableResize() routine,
* and then does some gtk-specific cleanup.
*/
void xaccSetTableSize (Table * table, int phys_rows, int phys_cols,
int virt_rows, int virt_cols);
/* create the GtkWidget */
GtkWidget *xaccCreateTable (Table *, GtkWidget *parent);
void xaccNextTabGroup (Table *, GtkWidget*);
/* redraw the table GUI */
void xaccRefreshTableGUI (Table *);
#endif __XACC_TABLE_H__
/* ================== end of file ======================= */