gEDA Developer Tips, tricks and hints

Data Structure of a Schematic

Internally, a schematic in gaf is implemented by number of doubly linked lists. The central type linked in the lists is OBJECT. It can represent a symbol, a line of text, a drawing primitive, a net, or an attribute.

An overview of the data structure of a schematic can be retrieved here. The sketch was drawn in 2005 by Stuart Brorson.

Doxygen Comments and Styles

Doxygen is a tool which extracts API documentation from comments in the source code. Markup can be placed in the comments, which is then extracted and rendered to HTML or LaTeX by Doxygen. This allows e.g. one function to link to another related function, and permits arguments and return values to have documentation associated with them.

Some sections of the gaf source have already been doxyfied. Currently, this includes libgeda, gschem, gnetlist, gsymcheck and gattrib. The Makefile in the docs dir of these tools contains a target “doxygen”. Alternatively, you can browse the output of doxygen online on this site, made available by Bert Timmerman.

If you want to see Doxygen's ideas on how to format documentation see the Doxygen website. The individual commands are documented here. There is also a very handy Doxygen quick reference card.

The following sections provide an introduction on how gschem and libgeda are customarily documented. Note that the QT style of /*! comment goes here */ for a Doxygen-enabled comment is preferred.

Documenting Files

When starting a new file, you obviously need to have the normal GNU license text. After the GNU License you should include a file comment describing what the file is for and any other descriptions that apply to the whole file.

  /*! \file <filename.ext>
      \brief Put a brief summary of what this file is for...
      \par Description
      A lengthier description of what the file is for (this is optional).
   */

Documenting Variables/Defines/Typedefs

Global variables in a file can be documented using the \var command or by just writing a comment with a \brief command right before the definition.

  /*! \brief fill style of objects like cirle, rect, path */
  typedef enum {FILLING_HOLLOW, FILLING_FILL, FILLING_MESH, FILLING_HATCH, FILLING_VOID} OBJECT_FILLING;

Documenting Functions

Functions can be documented in the same way as Variables, etc… Just use a comment block above the function it is documenting and use a \brief command to start it.

Usually an additional Function Description paragraph is used for the lengthy description of the function's purpose. Also \param commands are used with the [in] or [out] attributes to document if the parameter will be modified by the function.

  /*! \brief "Save" a file into a string buffer
   *  \par Function Description
   *  This function saves a whole schematic into a buffer in libgeda
   *  format. The buffer should be freed when no longer needed.
   *
   *  \param [in] toplevel    The current TOPLEVEL.
   *  \param [in] object_list The head of a GList of OBJECTs to save.
   *  \return a buffer containing schematic data or NULL on failure.
   */

Structure Documentation

Structures are documented the same as in the previous sections. Note that comments on the structure members can be documented inline or using the same \brief syntax as variables. Inline documentation requires the special comment starting with /*!< at the end of the line it applies to.

  /*! \brief Structure for connections between OBJECTs
   *
   * The st_conn structure contains a single connection
   * to another object.
   * The connection system in s_conn.c uses this struct
   */
  struct st_conn {
    OBJECT *other_object; /*!< The "other" object connected to this one */
    /*! \brief type of connection. Always in reference to how the "other"
        object is connected to the current one */
    int type;
    int x; /*!< x coord of the connection position */
    int y; /*!< y coord of the connection position */
    int whichone; /*!< which endpoint of the current object caused this connection */
    int other_whichone; /*!< which endpoint of the "other" object caused this connection */
  };

Bug/Todo Commands

\bug and \todo are useful for notating where there are defects or missing features in the code. These commands can be used anywhere within the Doxygen comments, and generate entries on special pages in the documentation so that they can easily be referred to.

Dialogs: Design and Behaviour

Dialog Design

There's a nice document from the gnome guys called Gnome HIG. There are several suggestions on how to design dialogs and how they should behave.

The dialog design is mostly a matter of taste:

Modal or Nonmodal dialogs

A modal dialog is required whenever the main application provides data for the dialog.

Example:
  The dialog is called with a selection list and the dialog only should operate on this selection.

A modal dialog is OK too, if the dialog is only called very seldom. The file open dialog could be nonmodal because it does not require any input from the application.

A modal dialog is not OK if there is a lot of user interaction with the dialog. The component selection is a good example.

Where to place the dialog

A dialog can be put on different places in on the screen. A list of possible places can be found in the GtkReference

The current dialogs are placed either on the mouse position (GTK_WIN_POS_MOUSE) or at no preset position (GTK_WIN_POS_NONE). The Gnome HID does not say anything about that topic.

The default setting is GTK_WIN_POS_NONE for GtkWindow see GtkWindow. The default for GtkDialog is GTK_WIN_POS_CENTER_ON_PARENT ( taken from the GtkDialog source).

Placing dialogs in front of their parent window

Most of the dialogs are placed in front of their parent window using the transient_for property (see. GtkReference). This property should be set for all modal dialogs.

For nonmodal dialogs the setting of transient_for property is not obvious. While in gschem for example the coord dialog should stay above the parent window, the log window does not need to stay in front of it.

Note: There is an older mechanism that keeps the the dialogs in front of gschem. If the raise-dialog-boxes-on-expose variable is set to enable in one of gschem's configuration files, it may cause problems with some window managers. If dialogs are flickering at 100% CPU load, then disable that setting.

; raise-dialog-boxes-on-expose string
;
; Controls if dialog boxes are raised whenever an expose event happens
; Default is enabled
;
;(raise-dialog-boxes-on-expose "enabled")
(raise-dialog-boxes-on-expose "disabled")

Button order in dialogs

Button order at the bottom of the dialog depends on which operating system the user is using. GTK handles this automatically, but requires the developers set the alternative button order. For more information, check the GTK documentation here.

The alternative button order is set with just one call to a GTK function:

/* Set the alternative button order (ok, no, cancel, help) for other systems */
gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
			                GTK_RESPONSE_OK,
					GTK_RESPONSE_NO,
					GTK_RESPONSE_CANCEL,
					GTK_RESPONSE_HELP,
					-1);

This should be done for every new dialog created, before running it.

Dialog design of the current dialogs

Source template for simple dialogs

This template is not intented to compile, but you can easily copy the code block that you need.

void dialog (TOPLEVEL *w_current)
{
  GtkWidget *vbox, *label, *alignment, *table;
  GtkWidget *dialog;
 
  /* only create the dialog if it is not there yet. This usually is a
     widget pointer in the w_current structure:
     dialog = w_current->tewindow */
  if (!dialog) {
    dialog = gtk_dialog_new_with_buttons(_("Dialog title"),
					 /* the parent window or NULL */
					 GTK_WINDOW(w_current->main_window),
					 /* dialog properties */
					 GTK_DIALOG_MODAL, /* 0 for nonmodal dialogs */
					 /* dialog buttons and response signals */
					 GTK_STOCK_CANCEL,
					 GTK_RESPONSE_REJECT,
					 GTK_STOCK_OK,
					 GTK_RESPONSE_ACCEPT,
					 NULL);
 
    /* Set the alternative button order (ok, no, cancel, help) for other systems */
    gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
	  				    GTK_RESPONSE_OK,
					    GTK_RESPONSE_NO,
					    GTK_RESPONSE_CANCEL,
					    GTK_RESPONSE_HELP,
					    -1);
 
    /* set default response signal. This is usually triggered by the
       "Return" key */
    gtk_dialog_set_default_response(GTK_DIALOG(dialog),
				    GTK_RESPONSE_ACCEPT);
 
    /* set the function for handling the button responses and dialog close
       for nonmodal dialogs you can use dialog_run() too.*/
    gtk_signal_connect(GTK_OBJECT(dialog), "response",
		       GTK_SIGNAL_FUNC(dialog_response), w_current);
 
    /* where to place the dialog: GTK_WIN_POS_MOUSE or GTK_WIN_POS_NONE */
    gtk_window_position(GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
 
    /* set the border spacing and the vbox spacing of the dialog */
    vbox = GTK_DIALOG(dialog)->vbox;
    gtk_container_set_border_width(GTK_CONTAINER(dialog),DIALOG_BORDER_SPACING);
    gtk_box_set_spacing(GTK_BOX(vbox), DIALOG_V_SPACING);
 
    /* create a label (with markup) and pack it into the dialog box */
    label = gtk_label_new(_("<b>Section label</b>"));
    gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
    gtk_misc_set_alignment(GTK_MISC(label),0,0);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
    /* create a alignment container with the DIALOG_INDENTATION on the left */
    alignment = gtk_alignment_new(0,0,1,1);
    gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
			      DIALOG_INDENTATION, 0);
    gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
 
    /* a table can store several entries. It is stored in the aligment container.
       Note: the vertical and horizontal cell spacings */
    table = gtk_table_new (3, 2, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), DIALOG_V_SPACING);
    gtk_table_set_col_spacings(GTK_TABLE(table), DIALOG_H_SPACING);
    gtk_container_add(GTK_CONTAINER(alignment), table);
 
    /* a simple text label in one table cell with left alignment.
       Note: the GTK_FILL in the third line is required */
    label = gtk_label_new(_("Text:"));
    gtk_misc_set_alignment(GTK_MISC(label),0,0);
    gtk_table_attach(GTK_TABLE(table), label, 0,1,0,1, GTK_FILL,0,0,0);
 
    /* a simple text entry completes the option row */
    textentry = gtk_entry_new_with_max_length (10);
    gtk_table_attach_defaults(GTK_TABLE(table), textentry, 1,2,0,1);
    gtk_entry_set_activates_default(GTK_ENTRY(textentry), TRUE);
 
    /* ..... more table rows with options, or new sections */
 
    /* create references to all widgets that you need later */
    GLADE_HOOKUP_OBJECT(dialog, sizeentry,"textentry");
 
    /* show all widgets recursivly */
    gtk_widget_show_all(dialog);
  }
 
  else {
    /* Dialog is already there. Present it to the user.
       This is only required if you have a nonmodal dialog */
    gtk_window_present(GTK_WINDOW(dialog));
  }
 
  /* always set the current values to the dialog
     If you're placing that part at the end of the dialog function you can
     easily create dialogs that can be called, even if they are already open */
  textentry = g_object_get_data(G_OBJECT(dialog), "textentry");
  gtk_entry_set_text(GTK_ENTRY(textentry), string);
  /* select the text region that the user usually likes to overwrite */
  gtk_entry_select_region(GTK_ENTRY(textentry), 0, strlen(string));
}

The response function for such a dialog may look like this:

void dialog_response(GtkWidget *widget, gint response, TOPLEVEL *w_current)
{
  switch (response) {
  case GTK_RESPONSE_ACCEPT:
    /* apply the dialog settings:
       just insert your code here if it is short
       call an extra apply function if the required code is long */
    break;
  case GTK_RESPONSE_REJECT:
  case GTK_RESPONSE_DELETE_EVENT:
    /* for modal dialogs just do nothing,
       for nonmodal dialogs, destroy the dialog and clean up */
    break;
  default:
    /* catch wrong signals signals (paranoid error checking ;-)) */
    printf("dialog_response(): strange signal %d\n", response);
  }
 
  /* for nonmodal dialogs just do nothing,
     for modal dialogs, always destroy the dialog and clean up */
}

Current Dialog Issues in Gschem

Here's a list of things that could be improved:

Print Dialog
Write Image
Execute Script
Edit Text
Color Dialog
Line Width and Type
Fill Type
Translate Symbol
Page Manager
Component Selector
Single Attribut Editor
Multi Attribute Editor
Add Text
Arc Params
Insert Picture
Picture Replace
Find Text
Hide Text
Show Text
Autonumber Text
Text Size
Snap Grid Spacing
Coord Dialog
About Dialog
Hotkeys