DWI Example Code
Below follows example code for a simple note-taking application.
This example includes almost all of the formal interface documentation.
Once you go through this example, you will know just about everything
needed to write a DWI application.
<?xml version="1.0"?>
<!--
- FILE: testbasic.dui
-
- FUNCTION:
- This is an example DWI database-to-glade scripting file.
- It contains extensive documentation as to the DWI XML syntax.
-
- To actually run this example, you will need to create a database,
- create an ODBC dataset description for it, and initialize the
- database with the tables in the "testbasic.sql" file. Finally,
- you might want to change the username and password (below)
- to something reasonable; or review the "testlogin.dui" example
- to see a better way of handling user logins.
-->
<!-- The <dwi> element is used to indicate the beginning of a new
- DWI application. An application consists of one or more
- <database> sections, each of which consists of one or more
- <window> sections. The application windows themselves
- interact with the user as a <report>, displaying data,
- and/or as a <form>, collecting user input.
-
- A DWI application contains a rudimentary mechanism for
- handling application state: a global set of key-value
- pairs (implemented as a hash table). You can create
- a key-value pair at any time, and later use the values
- in database queries or in reports.
- There are a reserved set of keys. These all start with /system/
- The currently defined keys are:
- /system/datetime - the current date and time (iso8859)
-
- The keyword "name" identifies the application name. This
- keyword is optional. Its not used for much at this time.
-->
<dwi name="testbasic">
<!-- The <database> element is used to identify the database that
- will be used for the fetching and storage of data. A database
- must be specified; otherwise the whole point of this is pointless.
- Note that the host, dbname, username and authentication need not
- be hard-coded in a DWI file; they can be obtained from the GUI.
- See the "testlogin.dui" example to see how to do this.
-
- The keyword "provider" identifies the database driver. Currently
- only 'odbc' is supported.
- The keyword "dbname" identifies the database to connect to. It
- must already exist and have tables defined within it.
- The keyword "host" identifies the tcp/ip host that has the database
- server running on it.
- The keyword "username" identifies the username under which to log
- into the database.
- The keyword "passwd" provides an authentication token. Whether this
- is a password or other token depends on how the database server
- is configured.
-->
<database provider="odbc" dbname="testbasic" host="localhost" username="linas" passwd="asdf">
<!-- The <window> element is used to identify an application window and
- the file which describes the window's graphical layout.
-
- The keyword "provider" identifies the graphical layout engine.
- Currently, the only supported values are "glade" and "self".
- (Other providers e.g. for web/PHP are envisioned.)
- The "glade" provider requires a glade filename that defines
- the user interface, and the root widget name.
- The "self" provider i
- The keyword "name" identifies this window for the purpose of having
- forms and reports reference this window to read from and write
- to it.
- The keyword "filename" specifies the filepath to the Glade XML file.
- The keyword "root_widget" specifies a root widget (typically a top
- level window) under which the other widgets can be found.
- The keyword "main" indicates whether or not this window is the main
- application window. The use of this keyword is optional; if its
- value is "yes", then this window will be displayed on application
- startup.
-->
<window provider="glade" name="home page" main="yes"
filename="testbasic.glade" root_widget="main window">
<!-- The <form> element is used specify how a set of data
- values are obtained when a widget signal is received.
- Typically, the widget signal is the press of the "submit"
- button on a form, and the data values are a result of
- a database query that this form specifies. Note, however
- that the form is general enough that any signal, not
- just a button press, will do. Note that forms are
- general enough that a variety of actions can be performed;
- these do not always cause SQL/database statements to be
- issued.
-
- The example below specifies a database query statement
- that is created out of the values obtained from a set of
- input widgets. Specifically, it will cause an SQL SELECT
- statement to be generated when the "search button" widget
- is "clicked".
-
- The keyword "name" identifies this form by name. It is
- used in other places to identify this form.
- The keyword "type" identifies the SQL query type. Valid values
- are "select" "insert" "update", "delete", "tables" or
- "columns". The first four values
- correspond to thier matching SQL statements. The fifth,
- "tables", performs a database-specific query to list all
- of the tables in this database. The sixth, "columns",
- performs a database-specific query to list the columns
- in a table.
- The keyword "tables" identifies one or more SQL tables. These are
- used in the SELECT..FROM tables construct. The list must be
- comma-separated.
- The keyword "tables_key" identifies a KVP key which contains the
- table name to use.
- The keyword "report" indicates the name of the form that will
- be used to report the query results.
-
- The below will generate the SQL statement
- SELECT notes.summary, notes.id FROM notes WHERE
-->
<form type="select" tables="notes" report="result list">
<!-- The <submit> element is used to hook up widget signals to
- this form. This allows the database query to be generated
- and submitted to the database server whenever some widget
- is clicked (or some particular signal is received).
-
- The keyword "widget" identifies a widget (by name) that,
- when clicked, launches the actual select operation.
- The widget doesn't have to be a clickable button,
- it can be any widget with a signal.
- The keyword "signal" identifies the widget signal that
- actually launches the select. It can be any valid
- Gtk signal for the specified 'submit' widget.
-->
<submit widget="search button" signal="clicked"/>
<!-- The <select> element specifies what fields will be retrieved
- from the database.
- The 'field' keyword can be 'table.fieldname' or simply 'fieldname',
- or 'field AS othername'
-->
<select field="notes.summary" />
<select field="notes.id" />
<select field="fromdate" />
<select field="todate" />
<!-- The <where> element is used to control database record
- matching. It determines the WHERE part of an SQL statement
- "SELECT ... FROM ... WHERE ...". This part of an SQL statement
- has the form of "field" "op" "value" where "field" is the
- name of an SQL field (column label), "op" is an operator
- (such as equals, less-than, etc.), and "value" is used by the
- SQL server to find matching records. With DWI, the "value"
- can be obtained from a widget: indeed, this is the central point
- of DWI: that values can be obtained from widgets. Any of
- the common value-specifying keywords can be used to specify
- the value (see below, "COMMON VALUE SPECIFYING KEYWORDS").
- In addition to these, the following are also needed:
-
- The keyword "field" specifies the database field that will
- be matched. This field is mandatory.
- The keyword "op" specifies the relational operator to use
- when matching. It can be any of the accepted SQL
- operators appropriate for this field. If not specified,
- it is assumed to be "=". Note that the current XML parser
- does not allow "<" so you gotta "<" if you want "less
- than".
-
-
- COMMON VALUE SPECIFYING KEYWORDS
- - - - - - - - - - - - - - - - - -
- In general, a value can be obtained directly from a widget,
- or from a global key-value store, or hard-coded in DWI.
- Once a value has been obtained, it can be filtered before
- it is used. Filters typically can check for null values,
- change capitalization, change date strings and number
- representations, etc.
-
- The following keywords are used to specify how to get a value:
-
- The keyword "widget" specifies the widget whose value
- will be used. Either this, or the "key" keyword,
- or the "value" keyword, must be present.
- The keyword "column" is used to identify which column of
- the widget contains the string. This keyword only
- makes sense with widgets that have multiple columns
- (such as GtkCList).
- The keyword "data", if present, will cause the value to be
- fetched not from the widget directly, but from the
- GtkObject's data (using the gtk_object_get_data() call).
- This allows arbitrary string data previously stored with
- the widget to be used as a value.
- The keyword "key" specifies the key of a key-value pair
- in the global key-value store. A value must have been
- previously stored under this key (else a null string will
- be returned). Either this keyword, or the "value" keyword,
- or the "widget" keyword must be present.
- The keyword "value" specifies a hard-coded value that will
- be used, in place of a value from a widget. Either
- this keyword, or the "key" keyword, or the "widget"
- keyword must be present.
- The keyword "filter" names a string-handling function that
- will be applied to the resultant value (fetched from
- the widget or by KVP key or the hardcoded value).
- The filter transforms the string in some way. There
- are a number of pre-defined filters.
-
-->
<where widget="ID text" field="notes.id" />
<where widget="From text" field="fromdate" op=">" />
<where widget="To text" field="todate" op="<" />
</form>
<!-- This is a quickie little hack to hook up the clear button
- so that the text fields are cleared when it is pressed.
- This hack depends on reports (explained further below):
- basically, we create a report, all of whose fields are
- blanks. We then "report" these blanks on the finder tab.
-->
<form report="finder">
<submit widget="clear button" signal="clicked"/>
</form>
<report name="finder">
<entry widget="ID text" value=""/>
<entry widget="From text" value=""/>
<entry widget="To text" value=""/>
</report>
<!-- Another example of clearing fields in a window; this time
- with some fancier tricks.
- trick 1: we don't clear all the fields on this form.
- trick 2: we use a non-null value to initialize the 'notes' widget.
- trick 3: we use the '/system/datetime' key to auto-fill
- in the current time.
- trick 4: we use the 'show' signal on the application
- main window to automatically run this report
- on application startup. If we wanted to, we could
- even launch an SQL query on startup in this way.
-->
<form report="new tab">
<submit widget="clear new button" signal="clicked"/>
<submit widget="main window" signal="show" />
</form>
<report name="new tab">
<entry widget="new notes" value="Enter a Note Here!"/>
<entry widget="new to date" key="/system/datetime"/>
</report>
<!-- Another example of a form. This form allows the user to create
- a new record in the database.
-
- The <insert> element is used to specify which widgets will
- provide data for an SQL INSERT INTO ... VALUES ... statement.
- Any of the "COMMON VALUE SPECIFYING KEYWORDS" (described above)
- can be used. In addition to these, the following are needed:
-
- The keyword "field" specifies the database field that will
- be matched. This field is mandatory.
-
- The <refresh> element is used to indicate other windows that need
- to be redrawn when the form is activated. This is typically needed
- when a form is modifying data that might be visible in other windows.
- Without a refresh, the other windows will continue to show old data,
- until the user manually refreshed them.
-
- The keyword "window" gives the name of a window that will be redrawn.
-
- The <chain> element is used to chain together several actions.
- The first action in a chain to actually 'do something', i.e.
- to generate a report that puts up a GUI, breaks the chain. At
- this time, this is intended primarily for implementing 'prereq'
- dialogs, to check for a missing or incorrect field before the
- 'true' form is run. The example below illustrates this.
-
- The keyword "form" gives the name of the form that will be
- run first, before this form.
-->
<form type="insert" tables="notes" report="submit ok">
<submit widget="submit button" signal="clicked"/>
<insert widget="new from date" field="fromdate" />
<insert widget="new to date" field="todate" />
<insert widget="new summary" field="summary" />
<insert widget="new notes" field="notes" />
<chain form="check for null" />
<refresh window="result list" />
</form>
<!-- Another example of a form. This form provides an example of
- copying from a widget to the global KVP (key-value pair) table.
-
- The <wtokey> element is used to specify that a value is to be
- gotten from the specified widget, and placed into the global
- KVP (key-value-pair) table. Any of the "COMMON VALUE SPECIFYING
- KEYWORDS" (describerd above) can be used. In addtion to these,
- the following is also needed:
-
- The keyword "dst_key" specifies a key under which the resulting
- value will be stored.
-
-->
<form name="check for null" report="null from date">
<wtokey widget="new from date" dst_key="nulldate" />
</form>
</window>
<!-- The following is a very simple error message dialog to
- state that the 'from date' field has been left blank.
- It fetches the value associated with key "nulldate",
- and applies the filter "is_whitespace_or_null" to it.
- If it is, it will return true, and the "null msg"
- popup will remind the user to not leave the date field
- blank. If its not, the popup will not come up. Because
- this is the result of a form that was chained, the next
- form in the chain will run, accepting the user's input.
-->
<window provider="glade" name="null from date"
filename="testbasic.glade" root_widget="null fromdate msg">
<report>
<entry widget="null msg" key="nulldate" filter="is_whitespace_or_null"/>
</report>
</window>
<!-- ============================================================ -->
<!-- ============================================================ -->
<!-- Move on to the next window. This window will serve primarily as
- a place to display the results of queries specified by the previous
- forms.
-->
<window provider = "glade" name="result list"
filename="testbasic.glade" root_widget="query results">
<!-- The <report> element determines how data from the database
- is shown on the screen. It is one-half of a report-form pair:
- the form (shown previously) determines how the database is queried
- for data.
-
- A report can show data in tabular format (by using, e.g.
- the GtkCList or GtkCTree widgets) or individual widgets
- (e.g. GtkLabel, or filling in a default value for GtkEntry).
-
- The keyword "name" identifies the report by name. It can be any
- user-defined value (it does not need to be a widget name).
- If the name isn't specified, the name of the parent window
- is used to identify the report.
-->
<report>
<!-- The "row" element is used to define a row iterator within
- a report. The use of this element is optional; however,
- without it, only one recordset (one 'row') of results can
- be reported. Currently, only the GtkCList and the GtkCTree
- widgets are supported.
-
- To use GtkCList, only the widgetname is needed.
-
- To use GtkCTree, a considerable number of extra keywords
- are needed to correctly set up the hierarchical arrangement
- of the tree. Basically, the idea is to find the parent
- node of a set of child rows/nodes by matching a recordset
- field to a value in one of the columns in the parent node.
- If there's a match, then that node is used as the parent.
- Note that parent rows must be defined before child rows.
- (This is a temp limitation maybe ???)
-
- The keyword "widget" specifies a widget that can report
- multiple rows of data. This keyword is required to be
- present.
- The keyword "name" identifies the row by name. The name
- can be any user-defined name. When using a tree display,
- this keyword is mandatory: it is used by the <form>
- element to identify this row as the target of a query.
- The keyword "nest" identifies a row definition that will
- serve as the topmost, root row definition. Set
- to zero to indicate its root, else non-zero ...
- The keyword "match_column" specifies the tree column that
- contains a value that will be matched to identify the
- parent tree node. When using the tree display, this
- keyword is mandatory.
- The keyword "match_field" specifies the database recordset
- field that contains the value that will be matched to
- the tree column to identify the parent tree node.
- When using a tree display, then either this keyword,
- or the "match_key" keyword, or the "match_value" keyword
- must be present.
- The keyword "match_key" specifies the global hash table
- key that stores the value that will be matched to the
- tree column to identify the parent tree node. Either
- this, or the "match_field" or the "match_value" keywords
- must be present.
- The keyword "match_val" specifies a constant value that
- is used to match a column value to find the parent tree
- node. Either this, or the "match_field" or the "match_key"
- keywords must be present when using a tree display.
-->
<row widget="query clist">
<!-- The <entry> element is used to link specific database
- fields to specific widgets or columns. In the example
- below, database fields are hooked up to clist columns.
- A different example comes a bit later in this file.
-
- The keyword "widget" specifies a widget that will report
- the data. This keyword is optional if the entry
- is nested inside a <row> element.
- The keyword "column" specifies which column the value
- will appear in. This keyword is optional, and
- only makes sense when used with widgets that
- support columns. If a column is not specified,
- then a "widget" must be.
- The keyword "data", if present, will cause the value
- to not be displayed in the widget. Instead,
- it will be stored with the widget (using the
- gtk_object_set_data() call). The data keyword
- specifies the key under which the value will
- be stored. (In the case of multi-row widgets,
- such as GtkCTree and GtkCList, which do not
- support keyed data in rows, instead a hash
- table will be created and stored as the row
- data. The key-value will then be placed in
- that row's hash table.)
- The keyword "arg", if present, will cause the value
- to not be displayed in the widget. Instead,
- it will set the named widget arg (using the
- gtk_object_set() call). Widget args allow
- arbitrary widget characteristics to be set.
- For example, the arg "GtkWidget::height"
- can be used to set the widget height.
- The keyword "field" specifies a database field from
- which the reported data will be fetched. This
- keyword is optional, however, if its absent, then
- the either the "key" or the "value" keyword must
- be present.
- The keyword "key" specifies the key of a key-value
- pair in the application hash table. The value
- currently associated with that key will be used
- to fill in the report widget.
- The keyword "value" specifies a constant value that
- this widget will report. Handy for blanking
- or setting a default value in a widget (see
- example below).
-->
<entry widget="query clist" column="0" field="notes.id"/>
<entry widget="query clist" column="1" field="notes.fromdate"/>
<entry widget="query clist" column="2" field="todate"/>
<entry widget="query clist" column="3" field="summary"/>
</row>
</report>
<!-- A listing of records is useless if one can't delete them
- or modify them. The next <form> deletes a record from the
- database. The deleted record must have a matching "notes.id"
- which is read from column 1 of the highlighted row in the
- clist widget. The SQL query generated by this form is
- DELETE FROM notes WHERE notes.id='xxx';
-->
<form type="delete" tables="notes" report="submit ok">
<submit widget="qdelete button" signal="clicked"/>
<where widget="query clist" column="0" field="notes.id" />
<refresh window="result list" />
</form>
<!-- View the details of the currently highlighted row.
- This performs the following SQL query:
- SELECT * FROM notes WHERE notes.id='xxx';
- The results of the query will be displayed in the
- 'record viewer' window.
-->
<form type="select" tables="notes" report="record viewer">
<submit widget="qview button" signal="clicked"/>
<select field="*" />
<where widget="query clist" column="0" field="notes.id" />
</form>
<!-- The <sigaction> element is a generic signal escape mechanism.
- It causes the indicated 'action' to be performed when the
- indicated 'signal' on the 'widget' is received. Currently,
- the supported actions are:
- "close_window" closes the current window.
- "main_quit" quits the application main loop
-->
<sigaction widget="qclose button" signal="clicked" action="close_window"/>
</window>
<!-- The following is a very simple "OK" dialog to confirm a
- database update.
-->
<window provider="glade" name="submit ok"
filename="testbasic.glade" root_widget="submit msg">
<report>
<entry widget="submit msg" value="New Record Created"/>
</report>
</window>
<!-- The Record viewer/editor window
- Note how the report form doesn't use a <row> element. This is because we
- assume that only one record/row is returned by the database query.
-->
<window provider="glade" name="record viewer"
filename="testbasic.glade" root_widget="details window">
<report>
<entry widget="record id label" field="id" />
<entry widget="fromdate entry" field="fromdate" />
<entry widget="todate entry" field="todate" />
<entry widget="summary entry" field="summary" />
<entry widget="notes text" field="notes" />
</report>
<form type="update" tables="notes" report="submit ok">
<submit widget="edit submit button" signal="clicked"/>
<insert widget="fromdate entry" field="fromdate" />
<insert widget="todate entry" field="todate" />
<insert widget="summary entry" field="summary" />
<insert widget="notes text" field="notes" />
<where widget="record id label" field="id" />
<refresh window="result list" />
</form>
</window>
</database>
</dwi>
<!-- ===================== END OF FILE ========================== -->