From 9c52158826c0f171e3d000ccbf07632c412e65e1 Mon Sep 17 00:00:00 2001 From: John Darrington Date: Sun, 31 May 2015 15:34:47 +0200 Subject: [PATCH] New file for developers: memorandum.txt This short text very briefly describes the steps to add a new GUI dialog to Psppire. --- src/ui/gui/memorandum.txt | 320 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 src/ui/gui/memorandum.txt diff --git a/src/ui/gui/memorandum.txt b/src/ui/gui/memorandum.txt new file mode 100644 index 0000000000..d316c3a35c --- /dev/null +++ b/src/ui/gui/memorandum.txt @@ -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 +#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. + + + + + uimanager1 + some-menu_foobar + _Foobar + somemenu-foobar + + + + +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? + + -- 2.30.2