<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <HTML ><HEAD ><TITLE >Views</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK REL="HOME" TITLE="Gaby's documentation" HREF="index.html"><LINK REL="UP" TITLE="Gaby Developers' Guide" HREF="p489.html"><LINK REL="PREVIOUS" TITLE="Structures" HREF="x570.html"><LINK REL="NEXT" TITLE="Actions" HREF="c782.html"></HEAD ><BODY CLASS="CHAPTER" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >Gaby's documentation</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="x570.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="c782.html" ACCESSKEY="N" >Next</A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="CHAPTER" ><H1 ><A NAME="PLUGIN-VIEW" ></A >Chapter 9. Views</H1 ><DIV CLASS="IMPORTANT" ><P ></P ><TABLE CLASS="IMPORTANT" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="../images/important.gif" HSPACE="5" ALT="Important"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >I changed the view plug-in API recently and you should skip this chapter waiting I rewrote it</P ></TD ></TR ></TABLE ></DIV ><P >There is a sample plugin logically called 'hello' which is a minimal implementation of a classic (it only shows one record at the same time) plugin.</P ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="AEN684" >9.1. Initialization</A ></H1 ><P >When Gaby starts it reads the description of the database to be used (which is guess from the name you used to invoke Gaby or from the --as flags). I'll not talk about this file here but somewhere it will have a line like this : <B CLASS="COMMAND" >viewable as form,list,xlist.</B > from which Gaby "guess" it has to load form, list and xlist plugins. The first step is to initialize those plugins <A NAME="AEN688" HREF="#FTN.AEN688" ><SPAN CLASS="footnote" >[1]</SPAN ></A > which is done looking for a function like this in the plugin :</P ><DIV CLASS="FUNCSYNOPSIS" ><P ></P ><A NAME="AEN690" ></A ><PRE CLASS="FUNCSYNOPSISINFO" >#include <gaby.h></PRE ><CODE CLASS="FUNCDEF" >int init_view_plugin</CODE >(ViewPluginData *vpd);<P ></P ></DIV ><DIV CLASS="IMPORTANT" ><P ></P ><TABLE CLASS="IMPORTANT" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="../images/important.gif" HSPACE="5" ALT="Important"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P >If you want your plug-in to support being part of the huge binary produced when configuring with <SPAN CLASS="QUOTE" >"Miguel wishes"</SPAN > you'll have to enclose the function in a <VAR CLASS="LITERAL" >#ifndef FOLLOW_MIGUEL</VAR >.</P ></TD ></TR ></TABLE ></DIV ><P >The ViewPluginData structure is defined in <TT CLASS="FILENAME" >gaby.h</TT > (like all structures used in Gaby) : <PRE CLASS="PROGRAMLISTING" >typedef struct _ViewPluginData ViewPluginData; struct _ViewPluginData { GModule *handle; int (*init_plugin) ( ViewPluginData *vpd ); void (*view_create) (gabywindow *win, gboolean first); void (*view_fill) (gabywindow *win); void (*view_save) (gabywindow *win); #ifndef NO_GTK GtkWidget* (*configure) (ViewPluginData *vpd); #endif gchar *name; gchar *i18n_name; enum { ALL_RECORDS = 1 << 0, ONE_RECORD = 1 << 1, FILTER = 1 << 2 } type; enum { NONE = 0, EDITABLE = 1 << 0, FILTERABLE = 1 << 1, } capabilities; };</PRE > </P ><P >I believe you already have a good picture of what will have its place in <CODE CLASS="FUNCTION" >init_view_plugin</CODE >. To confirm : <DIV CLASS="EXAMPLE" ><A NAME="AEN705" ></A ><P ><B >Example 9-1. Initializing a view plug-in</B ></P ><PRE CLASS="PROGRAMLISTING" >int init_view_plugin(ViewPluginData *vpd) { vpd->view_create = hello_create; vpd->view_fill = hello_fill; vpd->configure = NULL; vpd->name = "hello"; vpd->i18n_name = _("Hello"); vpd->type = ONE_RECORD; vpd->capabilities = NONE; return 0; }</PRE ></DIV > </P ><P ><SPAN CLASS="emphasis" ><I CLASS="EMPHASIS" >FIXME</I ></SPAN >: a few comments would be welcomed.</P ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="AEN710" >9.1. View creation</A ></H1 ><P >It is the <B CLASS="COMMAND" >vpd->view_create = hello_create;</B > with <B CLASS="COMMAND" >hello_create</B > being a function defined like this : <DIV CLASS="FUNCSYNOPSIS" ><P ></P ><A NAME="AEN715" ></A ><CODE CLASS="FUNCDEF" >mstatic void* hello_create</CODE >(gabywindow* window, gboolean first);<P ></P ></DIV > <DIV CLASS="IMPORTANT" ><P ></P ><TABLE CLASS="IMPORTANT" WIDTH="100%" BORDER="0" ><TR ><TD WIDTH="25" ALIGN="CENTER" VALIGN="TOP" ><IMG SRC="../images/important.gif" HSPACE="5" ALT="Important"></TD ><TD ALIGN="LEFT" VALIGN="TOP" ><P ><CODE CLASS="FUNCTION" >hello_create</CODE > is defined as a <SPAN CLASS="QUOTE" >"mstatic"</SPAN > function, this means that it will be static <SPAN CLASS="emphasis" ><I CLASS="EMPHASIS" >excepted</I ></SPAN > when compiled with Miguel wishes.</P ></TD ></TR ></TABLE ></DIV > Its purpose is to create a widget which will then be put either in the main window of Gaby either in a separate window. This widget will then be recorded at <CODE CLASS="STRUCTNAME" >window->widget</CODE >. This means that the widget has <SPAN CLASS="emphasis" ><I CLASS="EMPHASIS" >not</I ></SPAN > to be a window with title bar and the like. It will commonly be a <B CLASS="COMMAND" >Gtk[V,H]Box</B > but any GTK container will work. </P ><P >Let's have an example : <DIV CLASS="EXAMPLE" ><A NAME="AEN731" ></A ><P ><B >Example 9-2. Creating a view</B ></P ><PRE CLASS="PROGRAMLISTING" >mstatic void hello_create ( gabywindow *window, gboolean first ) { GtkWidget *vbox; GtkWidget *label; GtkWidget *hbb; GtkWidget *button; int *id = &(window->id); record *r; r = table_first(v->subtable->table, -1); *id = ( r == NULL ? 0 : r->id ); r = table_first(window->view->subtable->table, -1); *id = ( r == NULL ? 0 : r->id ); vbox = gtk_vbox_new(FALSE,0); window->widget = vbox; label = gtk_label_new(""); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); hbb = gtk_hbutton_box_new(); gtk_widget_show(hbb); gtk_box_pack_start(GTK_BOX(vbox), hbb, FALSE, TRUE, 0); button = gtk_button_new_with_label(_("Previous")); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", \ GTK_SIGNAL_FUNC(previous_clicked), window); gtk_container_add(GTK_CONTAINER(hbb), button); button = gtk_button_new_with_label(_("Next")); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", \ GTK_SIGNAL_FUNC(next_clicked), window); gtk_container_add(GTK_CONTAINER(hbb), button); gtk_widget_show(vbox); return; }</PRE ></DIV > Most lines are typical GTK as described in GTK documentation. The interesting lines are : <PRE CLASS="PROGRAMLISTING" > int *id = &(window->id); record *r; r = table_first(v->subtable->table, -1); *id = ( r == NULL ? 0 : r->id );</PRE > This sets the record you're currently on to the first record of the table; or to 0 if the table is empty. </P ><P >Note that unlike the previous version of the API you don't have to play with lots of <CODE CLASS="FUNCTION" >gtk_object_get_data</CODE > in your plug-ins, making them cleaner.</P ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="AEN737" >9.1. View filling</A ></H1 ><P >As I just said (and as the title says) this is the function which will take datas from a table to show them in the beautiful widgets you just created.</P ><P >Here is the code : <DIV CLASS="EXAMPLE" ><A NAME="AEN741" ></A ><P ><B >Example 9-3. Filling a view</B ></P ><PRE CLASS="PROGRAMLISTING" >mstatic void hello_fill ( gabywindow *window ) { view *v = window->view; int *id = &(window->id); GtkWidget *label = gtk_object_get_data(GTK_OBJECT(win), "label"); GString *str; if ( *id == 0 ) { gtk_label_set_text(GTK_LABEL(label), _("Hello, world !")); return; } str = get_subtable_stringed_field_id(v->subtable, *id, 0 ); str = g_string_prepend(str, _("Hello, ")); gtk_label_set_text(GTK_LABEL(label), str->str); g_string_free(str, 1); }</PRE ></DIV > <P >The first thing is to retrieve the label widget we'll use to show the information : <PRE CLASS="PROGRAMLISTING" > GtkWidget *label = gtk_object_get_data(GTK_OBJECT(win), "label");</PRE > </P > <P >Then we check if we have a record to show, since record ids start at 1, 0 means no record to show : <PRE CLASS="PROGRAMLISTING" > if ( *id == 0 ) { gtk_label_set_text(GTK_LABEL(label), _("Hello, world !")); return; }</PRE ></P > <P >The easy case is done. Now we know that we have a real record to show. Since this is just an example, we'll just show one field and to be sure it will work with any tables we'll show the first one. <PRE CLASS="PROGRAMLISTING" > str = get_subtable_stringed_field_id(v->subtable, *id, 0 );</PRE > It is now easy as adding "Hello" before the string we got and showing it in a widget : <PRE CLASS="PROGRAMLISTING" > str = g_string_prepend(str, _("Hello, ")); gtk_label_set_text(GTK_LABEL(label), str->str);</PRE > It is now finished, we free the string and leave. </P > <DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="AEN751" >9.1. Moving between records</A ></H1 ><P > We have two buttons in our view (namely "Previous" and "Next"), their purpose is evident, the way it is done also (hem hem). </P ><P > <PRE CLASS="PROGRAMLISTING" >static void previous_clicked(GtkWidget *button, gabywindow *window) { record *r; view *v = window->view; int *id = &(window->id); r = get_record_no(v->subtable->table, *id); r = table_prev(v->subtable->table, r, -1); *id = ( r == NULL ) ? 0 : r->id; hello_fill(win); update_bound_windows(window); }</PRE > The interesting is <CODE CLASS="FUNCTION" >r = table_prev(v->subtable->table, r, -1);</CODE >. </P ><P ><SPAN CLASS="emphasis" ><I CLASS="EMPHASIS" >FIXME</I ></SPAN >: here is a few notes, they should be moved in a section with table_{first,last,next,prev} and get_record_no.</P ><P ><CODE CLASS="FUNCTION" >table_prev</CODE > takes 3 arguments : <P ></P ><UL ><LI ><P >table which is the table in which the record is,</P ></LI ><LI ><P >r which is the record we were at,</P ></LI ><LI ><P >sort_by which is a number explaining with which kind of sorting we want to move (-1 means no sorting otherwise it is a field number).</P ></LI ></UL > </P ><P >Then we fill again the view and tell gaby about this change so bound windows are updated (with <CODE CLASS="FUNCTION" >update_bound_windows</CODE >).</P ><P > <CODE CLASS="FUNCTION" >next_clicked</CODE > is the same with <CODE CLASS="FUNCTION" >table_next</CODE > instead of <CODE CLASS="FUNCTION" >table_prev</CODE >. Note that you don't need to check if you're on the first (or latest) record, it is handled by Gaby (previous when on first and you'll stay on it, same with next on latest). </P ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="AEN774" >9.1. Configuring</A ></H1 ><P >In <CODE CLASS="FUNCTION" >init_view_plugin</CODE > we affected something to <CODE CLASS="STRUCTFIELD" >vpd->configure</CODE >. It is a function defined as : <CODE CLASS="FUNCTION" >mstatic GtkWidget* configure(ViewPluginData *vpd);</CODE > It is used in the 'configure' box inside Gaby. Since you don't have anything to configure you may simply type (or affect <VAR CLASS="LITERAL" >NULL</VAR > to the variable : <PRE CLASS="PROGRAMLISTING" >mstatic GtkWidget* configure(ViewPluginData *vpd) { return NULL; }</PRE > </P ></DIV ></DIV ></P ></DIV ></DIV ></DIV ></DIV ><H3 CLASS="FOOTNOTES" >Notes</H3 ><TABLE BORDER="0" CLASS="FOOTNOTES" WIDTH="100%" ><TR ><TD ALIGN="LEFT" VALIGN="TOP" WIDTH="5%" ><A NAME="FTN.AEN688" HREF="c679.html#AEN688" ><SPAN CLASS="footnote" >[1]</SPAN ></A ></TD ><TD ALIGN="LEFT" VALIGN="TOP" WIDTH="95%" ><P >Note that this could be done only when the user asks for them but since it is not implemented this way ...</P ></TD ></TR ></TABLE ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="x570.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="index.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="c782.html" ACCESSKEY="N" >Next</A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Structures</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="p489.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Actions</TD ></TR ></TABLE ></DIV ></BODY ></HTML >