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:

  • alignment of elements. See Window Layout
  • right alignment of dialog buttons
  • some spacing around the dialog (but how much?)
  • some spacing between the elements (vertical and horizontal)
  • option groups with frames or indentation?
  • frame labels or bold headlines?

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

  • There is some space around the whole dialog (DIALOG_BORDER_SPACING).
  • Some indentation to show the topic group (DIALOG_INDENTATION) below it's bold headline.
  • The vertikal and the horizontal separation is done with DIALOG_H_SPACING and DIALOG_V_SPACING.

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

  • every dialog has it's own design
  • dialog placement: mouse position or no predefined position?
  • dialogs do not remember their last size, position and contents.
  • missing keyboard shortcuts

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

Print Dialog
  • change design?
Write Image
  • In the fileselect dialog the default filename is missing if the file does not exist
  • Return key does not work in the filename entry
Execute Script
Edit Text
  • some missing keyboard shortcuts
  • add *unmodified* tags if there are multiple selections
  • maybe add some color pixbufs for the color
  • maybe replace the text alignment with nine ratio buttons, toggle buttons with icons or …
Color Dialog
  • maybe add some color pixbufs for the color
Line Width and Type
  • keyboard shortcuts missing
  • icons for the line type
Fill Type
  • keyboard shortcuts missing
  • icons in the fill type
Translate Symbol
Page Manager
  • wrong button order? Depends on whether you think the refresh button is the main action button or just an extra button.
  • Maybe the “Return” key should trigger Refresh
Component Selector
  • strange edit widget when typing and the tree is selected (It's a search helper widget, disable it?!)
Single Attribut Editor
  • when multiple elements are selected and you call edit (ee) there are a few problems:
    • if the first object is text, then this dialog is opened (but with the wrong list parameter
    • if the first object is complex then the multiple attribute editor is called
Multi Attribute Editor
Add Text
Arc Params
  • add the diameter to the dialog, but select the start angle entry (increment = grid)
  • let “ee” call that dialog if only a single arc is selected
  • maybe add a section label
Insert Picture
Picture Replace
  • the new picture uses the aspect ratio of the old picture
  • the dialog has lots in common with the Insert Picture dialog. They could use some code together
Find Text
  • manipulates the mouse pointer (maybe the zooming code is the culprit). Just press “Return” to trigger a FindNext
  • if you select hierarchy and the found text is in a different schematic, then the filename in the title is not updated
  • maybe add an option: “Select all matching text objects”, disable hierarchy for that feature!
  • FIXME gschem hangs if you use that dialog with hierarchical schematics that have dependancy loops (e.g. the autonumber test schematics)
  • add an option “search for visible text only”
  • maybe use regular expressions instead of the substring for the searchtext
Hide Text
  • use regular expressions instead of starting substring
Show Text
  • use regular expressions instead of starting substring
  • Maybe merge that dialog together with the “Hide Text” dialog
Autonumber Text
  • Maybe disable the skip option if the renumber scope is “selection”. The other skip options (Page and Hierarchy) are really stupid.
Text Size
Snap Grid Spacing
Coord Dialog
  • maybe move the world coordinates to the main window status bar
About Dialog
Hotkeys
geda/devel-tips.txt · Last modified: 2012/10/25 02:42 by vzh
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki