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 "&lt;" 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="&lt;" />
    
    </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 ========================== -->