--- /dev/null
+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?
+
+