GTK-SHEET TUTORIAL

GTK-EXTRA Reference Manual Author: Victor Mierla aka Kornos <kornos@softhome.net> .
Copyright : See the COPYRIGHT file
GTK-EXTRA library author: Adrian Feiguin <adrian@ifir.edu.ar>

 

INDEX

     1. Introduction
     2. Creating a sheet
     3. Sheet signals
     4. Modifying cell contents
4.1 - functions that modify a cell 4.2 - functions that modify a range of cells 4.3 - functions that modify all cells from the sheet 4.4 - functions that set row/column titles , sheet title , etc
5. Modifying sheet state
6. Selections 7. Row/column operations
 Remark : This tutorial does not cover all functions from GtkSheet library.
                  It it only a reference to the most important(used) functions.
           This tutorial may contain errors or inaccuracies .Please mail me <kornos@softhome.net> in such a case.

 

 

 

1.Introduction



GtkSheet is a spreadsheet widget for Gtk (an Excel-like table) Internally, GtkSheet allocates memory for the sheet cells contents using an array of pointers to GtkSheetEntry widgets. A GtkSheetEntry widget( a cell in fact) is a GtkEntry subclass with the following differences : - It doesn't draw the border. - It is dinamically resized as the text is typed. - It allows right/left/center text justification (gtk_entry_size_allocate & entry_adjust_scroll) In a cell you may pack text , pictures or other widgets. Unfortunately, GtkEntry is a one text-line widget, so if you are searching for a Word-cell like behaviour(a multi -line cell) , you should probably consider checking other widgets(?!) or rewrite GtkSheet :-) The maximum number of rows/columns in a sheet is stored in a guint so in fact the maximum size of the sheet practically depends on the available memory in your system. 2. Creating a sheet The first step when you write a program using GtkSheet is to create the sheet widget and to pack it into a scrolled window. If you'll pack the sheet into a normal window you'll receive some warnings about adjustment signals.
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
...............................
window_box= gtk_vbox_new (FALSE,TRUE);

scrolled_window=gtk_scrolled_window_new(NULL, NULL);    
gtk_container_add (GTK_CONTAINER (window), window_box);         
gtk_box_pack_start (GTK_BOX (window_box), scrolled_window, 1,1,1);  
sheet=gtk_sheet_new(3,11,"Edit table");
GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_AUTORESIZE);  
gtk_container_add(GTK_CONTAINER(scrolled_window), sheet);


gtk_widget_show_all(window);
You should set the GTK_SHEET_AUTORESIZE flag so the cell would resize as you type.
GtkSheet can be created with a custom entry widget. You can use GtkEntry or a GtkEntry subclass 
(e.g. GtkSheetEntry, GtkSpinButton) or a box or table containing a GtkEntry subclass (e.g. GtkCombo)
gtk_sheet_new_with_custom_entry  (gint nrows, gint ncols, GtkType *entry_type);
if you want to construct a non-editable spreadsheet ( called "browser sheet") , just fow viewing purposes use:
gtk_sheet_construct_browser(GtkSheet *sheet,guint rows,guint columns, const gchar *title);

The lines and columns(white coloured by default) are numbered from 0 to max row/col number.
The gray row/col are numbered -1 .


Screenshot :


3. Sheet signals

The interaction between user and sheet is done(as in all Gtk programs) through signals.
In GtkSheet the next signals are implemented :
      
     SELECT_ROW, 
      SELECT_COLUMN, 
      SELECT_RANGE,
      CLIP_RANGE,
      RESIZE_RANGE,
      MOVE_RANGE,
      TRAVERSE, 
      DEACTIVATE, 
      ACTIVATE,
      SET_CELL,
      CLEAR_CELL,
      CHANGED,
      NEW_COL_WIDTH,
      NEW_ROW_HEIGHT,
      LAST_SIGNAL

If you're trying  to catch these signals , you should connect them to a callback(function that treats the signal/event).
If these notions seem strange to you , perhaps it's better to read the Gtk/Gnome documentation .

The most used signals (by me at least) are : ACTIVATE,DEACTIVATE,TRAVERSE.

Callback functions for different signals have different parameters.
For a correct use you should check out the Gtk-Extra Reference Manual , the examples or Gtk-Extra source code.
Incorrect use of the callback function parameters should have an undefined result(in the worst cases)

The ACTIVATE signal should be connected if you want to do something when a cell is clicked:
gtk_signal_connect(GTK_OBJECT(sheet),
                   "activate",
                   (GtkSignalFunc) alarm_activate,
                    NULL);

The callback function should have these parameters:

gint alarm_activate(GtkWidget *sheet, 
                    gint row, gint col, gpointer data)
{
................
}
If you have different functions that require the same cell to behave in a different way , I suggest that 
instead of writing "n" alarm_activate()
callbacks , write just one callback function that looks like this:
gtk_signal_connect(GTK_OBJECT(sheet),
                   "activate",
                   (GtkSignalFunc) alarm_activate,
                    (gpointer)function_name);

When the signal is connected , pass the function name (for example) as the last parameter.


gint alarm_activate(GtkWidget *sheet, 
                    gint row, gint col, gpointer function_name)
{
     if(strcmp(function_name,"table_new")==0){
               .....do_some_stuf()
     }
     else if(strcmp(function_name,"table_edit")==0){
               .....do_other_stuf()
     }
}

In this way , you'll have a much cleaner code , with as few functions as possible.
It is better to have a single callback function that treats all posible behaviour cases ,
instead of many functions which make the code harder to read.
A very important signal is TRAVERSE.
gtk_signal_connect(GTK_OBJECT(sheet),
                   "traverse",
                   (GtkSignalFunc) alarm_traverse,
                    NULL);

The callback function should have these parameters:


gint alarm_traverse(GtkWidget *widget,
                    gint row, gint col, gint *new_row, gint *new_col,
                    gpointer data)

{
...........
}
The "traverse" signal  is emited before "deactivate_cell" and allows to veto the movement.
 In such case, the entry will remain in the site, and the other signals will not be emited.

The traverse signal is useful when you need to know in what cell have you previously clicked or what was selected.    

The explanation for the the other signals is quite straightforward so if you want more details you should check 
GtkExtra Reference Manual or the GtkExtra source code.
REMARK: Adrian told me that a callback function should ALWAYS return TRUE or FALSE. Otherwise BUGZ may occur. 4. Modifying cell contents As previously stated , a cell may contain text, picture or other widgets. GtkSheet functions can be categorized after the number of modified cells: 4.1 - functions that modify a cell 4.2 - functions that modify a range of cells 4.3 - functions that modify all cells from the sheet 4.4 - functions that set row/column titles , sheet title , etc 4.1 Functions that modify a cell * A cell text is set by:
/* Set a cell text with justification */

void  gtk_sheet_set_cell  (GtkSheet *sheet, 
                           gint row, gint col,
                           GtkJustification justification,
                           const gchar *text);

/* No justification is made */
void gtk_sheet_set_cell_text (GtkSheet *sheet, 
                              gint row, gint col, 
                              const gchar  *text); 
The text string will be strdup()'ed by the gtkextra library so you may free your string after this line.
Do not free the internally allocated string !
This will be done by the library at the destruction of the sheet or when the gtk_sheet_set_cell is called again.

The text justification in the cell may be : GTK_JUSTIFY_LEFT, GTK_JUSTIFY_RIGHT, GTK_JUSTIFY_CENTER



* A cell text may be read by :
gchar *  gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col);

Screenshot:


* A cell text may be deleted by :
void gtk_sheet_cell_clear(GtkSheet *sheet, gint row, gint col); - delete cell contents
void gtk_sheet_cell_delete(GGtSheet *sheet, gint row, gint col); - delete + remove links

* You may change background/foreground color of cell (a cell,  a range of cells or the whole sheet).
See the example in the manual .


* You can pack a xpm into a cell like this :
GtkWidget *sheet,*bullet;
GdkPixmap *pixmap;
GdkColormap *colormap;
GdkBitmap *mask; ............
sheet=gtk_sheet_new(...);
.......
colormap = gdk_colormap_get_system();
pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, colormap, &mask, NULL,
bullet_xpm);
bullet = gtk_pixmap_new(pixmap, mask);
gtk_sheet_attach(GTK_SHEET(sheet), bullet, 1, 1, 0,0 ,.5, .5);
........
gtk_widget_show_all (bullet); // don't forget this!!
gtk_widget_show_all (window); See the full example here(xpm.c).
Screenshot:

* You can pack a combo box into a cell like this ( my way,it needs improvement ) :
GtkWidget *sheet,*combo;
GList *combo_items;
..................
combo=gtk_combo_new(); combo_items=NULL;
combo_items=g_list_append (combo_items, "NULL");
combo_items=g_list_append (combo_items, "NOT NULL");
gtk_combo_set_popdown_strings (GTK_COMBO (nul_combo), combo_items);
g_list_free(combo_items);

// From GtkExtra 1.0 the cell may be resized automatically to fit the widget
//You can thank me for bugging Adrian with this ;-)
gtk_sheet_attach_default (GTK_SHEET(sheet), current_combo, row, col,0,0); // Until GtkExtra 0.99.17 you must resize it yourself
gtk_sheet_set_column_width(GTK_SHEET(sheet),col,170); // 170 is the width of the combo box.
gtk_widget_show(combo);
Screenshot :


The widgets can be added to a cell using these  functions.
Some of them expand/shrink cell depending of widget size ,others don't.
See the GtkSheet manual for details
/* Attach widget at (row x,col y) starting at the upper left of the cell */
GtkSheetChild *gtk_sheet_put (GtkSheet *sheet, GtkWidget *widget, gint x, gint y); void gtk_sheet_attach (GtkSheet *sheet,GtkWidget *widget,gint row, gint col,gint xoptions,gint yoptions,gint xpadding,gint ypadding);
void gtk_sheet_attach_default (GtkSheet *sheet,GtkWidget *widget,gint row, gint col);
void gtk_sheet_attach_floating (GtkSheet *sheet,GtkWidget *widget,gint row, gint col);

 

* You can set a link for each cell. This is in fact a pointer you want to be associated to the cell.  

void gtk_sheet_link_cell(GtkSheet *sheet, gint row, gint col,gpointer link);
gpointer gtk_sheet_get_link(GtkSheet *sheet, gint row, gint col);
void gtk_sheet_remove_link(GtkSheet *sheet, gint row, gint col);



4.2 Functions that modify a range of cells

 

A range of cells is in fact :

struct _GtkSheetRange
{
gint row0,col0; /* upper-left cell */
gint rowi,coli; /* lower-right cell */
};

Some functions operating on range of cells:

void  gtk_sheet_get_visible_range (GtkSheet *sheet,GtkSheetRange *range); 
void gtk_sheet_range_set_background (GtkSheet *sheet, const GtkSheetRange *range, const GdkColor *color);
void gtk_sheet_range_set_foreground (GtkSheet *sheet, const GtkSheetRange *range,const GdkColor *color);
void gtk_sheet_select_range (GtkSheet *sheet, const GtkSheetRange *range);
void gtk_sheet_unselect_range (GtkSheet *sheet); void gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range);
void gtk_sheet_range_delete (GtkSheet *sheet, const GtkSheetRange *range); void gtk_sheet_range_set_editable (GtkSheet *sheet, const GtkSheetRange *range, gint editable);
void gtk_sheet_range_set_visible (GtkSheet *sheet, const GtkSheetRange *range, gboolean visible);
void gtk_sheet_range_set_border (GtkSheet *sheet, const GtkSheetRange *range, gint mask, guint width, gint line_style);
void gtk_sheet_range_set_border_color GtkSheet *sheet, const GtkSheetRange *range, const GdkColor *color);

Remark:

/* When you declare a range like this:*/

......
GtkSheetRange range; ................. /* You must call the range functions like this : */
void gtk_sheet_range_clear (sheet, &range);

Screenshot: Modifying the background color of a range

 

4.3 Functions that modify all cells from the sheet

 

These functions are used if you want the cells to contain the same widget.
These are some of them:

GtkWidget *gtk_sheet_new_with_custom_entry (guint rows, guint columns, const gchar *title,GtkType entry_type); 
void gtk_sheet_set_foreground (GtkSheet *sheet,GdkColor *fg_color);
void gtk_sheet_set_background (GtkSheet *sheet,GdkColor *bg_color);

 

4.4 Functions that set titles

 

* A row/column title can be set with:

void gtk_sheet_row_button_add_label	(GtkSheet *sheet, gint row, const gchar *label); 
void gtk_sheet_column_button_add_label (GtkSheet *sheet, gint row, const gchar *label);

gtk_sheet_set_row_title() is used for setting an internal label associated with a row.
Some people told that this is useless but i find it very usefull in some cases.
And one more thing : please don't ask again on the mailing list what this function does.
The secret is unveiled now :-)


* The sheet title may be set with :

void gtk_sheet_set_title (GtkSheet *sheet, const gchar *title);


* The dimensions of the row/column titles may be set with the next functions.
These functions exist because you may want to resize the width of a column or height of a row,
unless you set the autoresize flag, which will do it automaticaly for you.

void gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width); 
void gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height);


* Row/column title justification is done with:

void gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width); 
void gtk_sheet_set_column_titles_height(GtkSheet *sheet, guint height);

5. Modifying sheet state


* You can freeze/unfreeze the sheet with:

void gtk_sheet_freeze (GtkSheet *sheet); 
void gtk_sheet_thaw (GtkSheet *sheet);
* Set if cell contents can be edited  or not in the given range: 
void gtk_sheet_range_set_editable	(GtkSheet *sheet, const GtkSheetRange *range, gint editable);

*REMARK : From GtkExtra 1.0 sheet state will be set with functions:

void gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize);
gboolean gtk_sheet_autoresize (GtkSheet *sheet);
void gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll);
gboolean  gtk_sheet_autoscroll(GtkSheet *sheet);
void gtk_sheet_set_clip_text  (GtkSheet *sheet, gboolean clip_text);
gboolean gtk_sheet_clip_text  (GtkSheet *sheet);
void gtk_sheet_set_locked     (GtkSheet *sheet, gboolean lock);
void gtk_sheet_locked         (GtkSheet *sheet);
void gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable);
gboolean gtk_sheet_rows_resizable (GtkSheet *sheet);
void gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable);
gboolean  gtk_sheet_columns_resizable (GtkSheet *sheet);
void gtk_sheet_show_column_titles (GtkSheet *sheet);
void gtk_sheet_show_row_titles (GtkSheet *sheet);
void gtk_sheet_hide_column_titles (GtkSheet *sheet);
void gtk_sheet_hide_row_titles (GtkSheet *sheet);
gboolean gtk_sheet_column_titles_visible (GtkSheet *sheet);
gboolean gtk_sheet_row_titles_visible (GtkSheet *sheet);
void gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify);
gboolean gtk_sheet_justify_entry (GtkSheet *sheet);

*Up to (including) GtkExtra 0.99.17 Sheet state is mainly set by a set of macros:
 GTK_SHEET_AUTORESIZE(sheet)
GTK_SHEET_ROW_FROZEN(sheet)
GTK_SHEET_COLUMN_FROZEN(sheet)
GTK_SHEET_CLIP_TEXT(sheet)
GTK_SHEET_AUTO_SCROLL(sheet)
GTK_SHEET_JUSTIFY_ENTRY(sheet)
GTK_SHEET_ROW_TITLES_VISIBLE(sheet)
GTK_SHEET_COL_TITLES_VISIBLE(sheet)

 

6. Selections

 

* Select a row/column:
void gtk_sheet_select_row (GtkSheet * sheet,gint row);
void gtk_sheet_select_column (GtkSheet * sheet,gint column);


* (Un)Select a range:

void gtk_sheet_select_range (GtkSheet *sheet, const GtkSheetRange *range); 
void gtk_sheet_unselect_range (GtkSheet *sheet);

A row/column selection can  be catched using "select_row" and "select_column" signals: 
void (*select_row) (GtkSheet *sheet, gint row);
void (*select_column) (GtkSheet *sheet, gint column);

 

7. Row/column operations

 

The most common row/column operations are:
- insertion
- deletion
- finding the number of row/cols
void gtk_sheet_add_column (GtkSheet *sheet, guint ncols);
void gtk_sheet_add_row (GtkSheet *sheet, guint nrows);
void gtk_sheet_insert_rows (GtkSheet *sheet, guint row, guint nrows);
void gtk_sheet_insert_columns (GtkSheet *sheet, guint col, guint ncols);
void gtk_sheet_delete_rows (GtkSheet *sheet, guint row, guint nrows);
void gtk_sheet_delete_columns (GtkSheet *sheet, guint col, guint ncols);
guint gtk_sheet_get_columns_count (GtkSheet *sheet);
guint gtk_sheet_get_rows_count (GtkSheet *sheet);

 

 

 

Home