New file for developers: memorandum.txt
authorJohn Darrington <john@darrington.wattle.id.au>
Sun, 31 May 2015 13:34:47 +0000 (15:34 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Sun, 31 May 2015 13:34:47 +0000 (15:34 +0200)
This short text very briefly describes the steps to add a new GUI dialog to Psppire.

src/ui/gui/memorandum.txt [new file with mode: 0644]

diff --git a/src/ui/gui/memorandum.txt b/src/ui/gui/memorandum.txt
new file mode 100644 (file)
index 0000000..d316c3a
--- /dev/null
@@ -0,0 +1,320 @@
+This is a mini howto, describing the steps to add a new dialog box
+to psppire.   This is document is only tested for the master branch.
+Gtk3 is uncharted territory ....
+
+
+How to add a new dialog to Psppire
+==================================
+
+
+Before you start.
+
+1. You will need to install Glade version 3.8.4 ---    ANY OTHER VERSION
+IS UNLIKELY TO WORK  --- I know that 3.18.x does not!  If your
+distro  doesn't have this version, you will need to download it and
+build it from source.
+
+
+
+2. Build and install the lastest pspp master HEAD, with the
+   "--with-gui-tools" option passed to configure.  Thus:
+
+   ./configure --with-gui-tools
+   make
+   sudo make install
+
+   This installs the pspp specific libraries and widgets that glade needs.
+
+   For this step, you must build inside the pspp source directory, and
+   you must install to the default directory.  DO NOT build from
+   outside the source, and do not pass a --prefix to configure.
+
+
+Having done the above, you should be able to edit an existing dialog
+definition, thus:
+
+           glade-3 src/ui/gui/descriptives.ui
+
+You will probably get a lot of Gtk-Warnings/Criticals  most of them
+are harmless.
+
+
+If this works, now you are in a position to start ....
+
+There are two basic steps.  Firstly, you need to define the layout of
+the dialog.  This is done using glade, which you have installed as
+described above.  The second step is to define the behaviour of the
+dialog.  For this, you need to define a new object deriving from
+PsppireDialogAction
+
+
+
+Example:  Adding a new dialog: foobar
+-------------------------------------
+
+
+Defining the layout
+...................
+
+Start glade and create a new GtkBuilder file, thus:
+      glade-3 src/ui/gui/foobar.ui
+
+All dialogs need a top level window.  For Psppire, this must be an
+instance of PsppireDialog.
+On the left hand side of the Glade window, you will see a category
+headed "Psppire".  Click on the first item in that category and a new
+(empty) dialog will be created.
+
+Most dialogs have a vertical button box on the right hand side, so
+click on the "Vertical Button Box" icon.  Then on the right hand panel
+of the new dialog.  You will be prompted to enter the number of items
+in the button box.  For this example, chooose 5.
+
+Note, that by default, the dialog box was created with an "internal
+child" GtkVbox whose "Number of items" property is 2.  Change this
+to 4.  You should see the extra empty panels appear.
+
+Typically a dialog box needs a DictionaryTreeview in the left hand
+panel.  But we normally put that inside a scrolled window, which in
+turn is inside a Frame.
+
+Under the "Containers" category there is a "Frame" item.  Click on
+this.  Then on the leftmost panel of the dialog box.  The new GtkFrame
+widget should appear.  Again in the "Containers" category click on
+"Scrolled Window" and then in the frame you have just created.
+This will put a new GtkScrolledWindow inside the GtkFrame.
+
+Now click on the "Dictionary Treeview" widget (unde the "Psppire"
+category, and put that inside the Scrolled window.
+
+You should now have  a half complete dialog looking something like
+this:
+
++---------------+-----------------+----------------+-----------------+
+|Frame1                |                 |                | +--------------+|
+|+-------------+|                |                | |     OK       ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Paste      ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Cancel     ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Reset      ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Help       ||
+|+-------------+|                |                | +--------------+|
++---------------+-----------------+----------------+-----------------+
+
+In the second panel from the left, we might want a selector button.
+However, we don't want to to fill the entire panel.
+So we put it in inside a GtkAlignment.  From the "Containers" category
+click "Alignment". and then the panel.   Such a widget is of course
+not visible,  but you will note its presence from the widget hierarchy
+in the top right hand corner of the glade window.
+Now, from the "Psppire" category, click on "Selector Button" and put
+it inside the new GtkAlignment.  It will appear to occupy the entire
+panel.  To fix this, select the GtkAlignment and change its Horizontal
+Scale and Vertical Scale properties to zero.
+
+If all is well, the dialog box now looks like this:
+
+
++---------------+-----------------+----------------+-----------------+
+|Frame1                |                 |                | +--------------+|
+|+-------------+|                |                | |     OK       ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Paste      ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||                    ||       |\        |                | +--------------+|
+||                    ||       | \       |                | |   Cancel     ||
+||            ||       | /       |                | +--------------+|
+||            ||       |/        |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Reset      ||
+||            ||                 |                | +--------------+|
+||            ||                 |                |                 |
+||            ||                 |                | +--------------+|
+||            ||                 |                | |   Help       ||
+|+-------------+|                |                | +--------------+|
++---------------+-----------------+----------------+-----------------+
+
+
+However you will notice that upon resizing the dialog, the selector
+button's panel expands with it, which is probably not what we want.
+To fix this, select the GtkAlignment which we created above, and set
+its "Expand" packing-property to false.
+
+
+We final panel might require a PsppireVarView widget, again inside a
+GtkScrolled window (and possibly a GtkFrame).  Create this, in a
+similar way to which you did the PsppireDictionaryTreeview above.
+
+Now you have a fully populated dialog box.  It might need tweaking
+which we can do later.  But we are now in a position to display our
+defined dialog box.
+
+
+Displaying the Dialog box in Psppire
+....................................
+
+
+1. Define a new PsppireDialogAction Class
+
+Create a new object class derived from PsppireDialogAction  (note that
+PsppireDialogAction itself derives from GtkAction).  It's probably
+best if you use an existing example as a template.  The minimum you
+require is:
+
+
+#include <config.h>
+#include "psppire-dialog-action-foobar.h"
+#include "psppire-var-view.h"
+#include "psppire-dialog.h"
+#include "builder-wrapper.h"
+
+static void psppire_dialog_action_foobar_init            (PsppireDialogActionFoobar      *act);
+static void psppire_dialog_action_foobar_class_init      (PsppireDialogActionFoobarClass *class);
+
+G_DEFINE_TYPE (PsppireDialogActionFoobar, psppire_dialog_action_foobar, PSPPIRE_TYPE_DIALOG_ACTION);
+
+static gboolean
+dialog_state_valid (gpointer data)
+{
+  PsppireDialogActionFoobar *ud = PSPPIRE_DIALOG_ACTION_FOOBAR (data);
+
+  // This function is a predicate to determine if the dialog box has
+  //   been set to a state where is is appropriate to click OK  /
+  //  Paste.
+
+  // If it returns FALSE, the OK and PASTE buttons are insensitive
+
+
+  return ....;
+}
+
+static void
+refresh (PsppireDialogAction *rd_)
+{
+  PsppireDialogActionFoobar *uv = PSPPIRE_DIALOG_ACTION_FOOBAR (rd_);
+
+  // This function is called when the Reset Button is clicked.
+  // It sets the dialog to its default state
+}
+
+
+// This function is called when the menuitem is activated.
+// It is what pops up the dialog 
+static void
+psppire_dialog_action_foobar_activate (GtkAction *a)
+{
+  PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
+  PsppireDialogActionFoobar *act = PSPPIRE_DIALOG_ACTION_FOOBAR (a);
+
+  // These three lines are (almost) always required
+  GtkBuilder *xml = builder_new ("foobar.ui");
+  pda->dialog = get_widget_assert   (xml, "foobar-dialog");
+  pda->source = get_widget_assert   (xml, "dict-view");
+
+  // ... here you can load any widgets that your dialog uses
+  act->this_widget = get_widget_assert (xml, "this_widget");
+  act->that_widget = get_widget_assert (xml, "that_widget");
+
+  // ... you will most probably need to have these here
+  psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
+  psppire_dialog_action_set_refresh (pda, refresh);
+
+
+  // Everything below this line is necessary.
+  g_object_unref (xml);
+
+  if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_foobar_parent_class)->activate)
+    PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_foobar_parent_class)->activate (pda);
+}
+
+
+// Most often this function will not need any changes
+static void
+psppire_dialog_action_foobar_class_init (PsppireDialogActionFoobarClass *class)
+{
+  GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+  action_class->activate = psppire_dialog_action_foobar_activate;
+  PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
+}
+
+
+static void
+psppire_dialog_action_foobar_init (PsppireDialogActionFoobar *act)
+{
+ // often, this function can be empty
+}
+
+
+
+static char *
+generate_syntax (PsppireDialogAction *act)
+{
+  PsppireDialogActionFoobar *uvd = PSPPIRE_DIALOG_ACTION_FOOBAR (act);
+
+  gchar *text = NULL;
+  GString *str = g_string_new ("FOOBAR ");
+
+  // ... this function generates the syntax to be interpreted by
+  //   PSPP's backend
+
+  g_string_append (str, ".\n");
+  text = str->str;
+  g_string_free (str, FALSE);
+
+  return text;
+}
+
+2. Declare the new PsppireDialogAction Class
+
+You will also need to define the corresponding .h header file.  Best
+to copy and search replace on an existing one.
+
+
+3. Adding the entry point for the dialog.
+
+Edit src/ui/gui/data-editor.ui  It is possible to do this with Glade,
+but frankly is easier with a text editor.  Something like the
+following is typical.
+
+
+        <child>
+          <object class="PsppireDialogActionFoobar" id="some-menu_foobar">
+            <property name="manager">uimanager1</property>
+            <property name="name">some-menu_foobar</property>
+            <property name="label" translatable="yes">_Foobar</property>
+           <property name="stock-id">somemenu-foobar</property>
+          </object>
+        </child>
+
+
+4. Announce the new object to GtkBuilder.
+
+Add the new PsppireDialogAction's get_type function to  to
+src/ui/gui/widgets.c  Just have a look there and follow the pattern.
+This is necessary to that GtkBuilder knows about the new object class.
+
+5.  Putting it all together.
+
+Don't forget to put any new files you've created in
+src/ui/gui/automake.mk and to rerun make ; make install
+
+
+
+That's about it, I think.  Did I forget anything?
+
+