Adding features to PSPPIRE ========================== There are many procedures in PSPP which don't currently have corresponding menus in the GUI. Consequently they can be executed only through the syntax window. This memo describes how to add a dialog box for your favourite procedure. This memo presumes that you have checked out the PSPP source code from CVS, configured and built it in the normal way, and that any problems installing or running the gui have been fixed. There are three basic things to do when adding a dialog box. 1) Create a menuitem for the dialog box; 2) Create the layout of the dialog box; 3) Define the behaviour of the dialog box. Creating the menuitem ===================== You'll probably need a menuitem from which your new dialog box will be invoked. The menus are defined in src/ui/gui/data-editor.glade which is an XML file which must be edited with glade. Glade-3 is required. Glade-2 will not work. Adding/Editing menus with glade should be self intuitive, so I'll not attempt to elaborate here. Creating the dialog box layout ============================== In order to factor out common features used in the GUI, there are some custom widgets which must be installed first. To install these widgets you must configure the pspp source code with ./configure --with-gui-tools. Then run make ; make install in the normal way. This will install the glade catalogue and modules in their respective places. Since they are installed in restricted directories, it's necessary to be root when doing this. It's not possible to install them in non-standard directories. Now you should be able to run glade-3, and read in src/ui/gui/psppire.glade and examine the existing dialog boxes there. The custom widgets ------------------ The Psppire custom widgets should be used wherever possible. They help to maintain a common look and feel of the user interface and save a lot of coding: PsppireDialog ............. Gtk+ provides a GtkDialog widget. However, this didn't satisfy the needs of Psppire, so there is a PsppireDialog widget which should be used instead. PsppireDialog differs from GtkDialog in that it can have either a horizontal or vertical setting, and that it returns a response according to its PsppireButtonBox (see below). Psppire[HV]ButtonBox .................... Each PsppireDialog widget should contain either a PsppireVButtonBox or a PsppireHButtonBox. These widgets automatically contain the buttons which used by most dialog boxes. You can change decide which buttons are included, by the "buttons" property. By default there are OK, PASTE, CANCEL, REFRESH and HELP buttons. PsppireSelector ............... A common device in Psppire dialog boxes features a list of variables (displayed in a GtkTreeView), from which the user may select one or more to participate in the the statistical procedure which is the subject of the dialog box. Normally the selected variables are entered into a GtkEntry or a GtkTreeView. The widget for selecting/deselecting the variables is the PsppireSelector widget. It appears as a button containing an arrowhead. PsppireKeypad ............. There is a PsppireKeypad widget which is useful for in dialogs which require algebraic expressions to be entered by the user. Defining the Dialog box's behaviour =================================== (Note: Much of the dialog box functionality should be re-factored into common functions. This however has not yet been done. So for now you'll have to use one of the existing src/ui/gui/*-dialog.c files as a template.) The exact behaviour of the dialog box depends, of course, on what it is supposed to do. However there are two things which you're likely to want to do with it: * restoring default state. * display it (pop it up) , * create a syntax fragment to run the procedure. * run the syntax. Restoring default state ----------------------- Most dialog boxes also have a RESET button, which puts the dialog's components back to their original state. Even if no such button is present, it's best to provide a handler for the dialog's "refresh" signal, since this signal occurs whenever the dialog box pops up. You should connect to the refresh signal with a line similar to: g_signal_connect (dialog, "refresh", G_CALLBACK (refresh), &scd); Displaying the dialog box ------------------------- You should provide a public function of the form void my_dialog (GObject *o, gpointer data); This function serves as a callback to the menuitem which invokes it. Thus, in src/ui/gui/data-editor.c, you need to place code similar to: g_signal_connect (my_menu_item, "activate", G_CALLBACK (my_dialog), de); The definition of my_dialog will generally contain the lines: GladeXML *xml = XML_NEW ("psppire.glade"); GtkWidget *dialog = get_widget_assert (xml, "my-dialog"); response = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); where the string "my-dialog" should be replaced with the name of the dialog widget in psppire.glade. Generating the syntax --------------------- You need to provide a function of the form gchar *generate_syntax (const void *); Which generates a syntax fragment based on the state your dialog's components. The string it returns should be allocated on the heap. It's the caller's responsibility to free it. GLib's GString object is useful for creating the string. If your fragment contains literal strings which need to be quoted, use the gen_quoted_string function from src/libpspp/syntax-gen.c Running the syntax ------------------ Generally you can cut and paste code from an exiting dialog to do this. Most dialog boxes should support the PASTE and OK responses, so the appropriate code is: switch (response) { case GTK_RESPONSE_OK: { gchar *syntax = generate_syntax (&scd); struct getl_interface *sss = create_syntax_string_source (syntax); execute_syntax (sss); g_free (syntax); } break; case PSPPIRE_RESPONSE_PASTE: { gchar *syntax = generate_syntax (&scd); struct syntax_editor *se = (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL); gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1); g_free (syntax); } break; default: break; } Handling PsppireSelector widgets and variable treeviews ------------------------------------------------------- Most dialogs have a GtkTreeView intended to display the variable lists. To make this widget actually do this, you need to call attach_dictionary_to_treeview (GTK_TREE_VIEW (treeview), dict, GTK_SELECTION_SINGLE, NULL); where treeview is the GtkTreeView widget, dict is the PsppireDict representing the dictionary. If you want the user to be able to select only a single variable, then pass GTK_SELECTION_SINGLE, otherwise GTK_SELECTION_MULTIPLE. The final argument is a predicate function (from src/data/variable.h) such as var_is_numeric which can be used to indicate that only certain variables should be displayed. In order to make your treeview, the selector and the destination widget interact, call: psppire_selector_set_subjects (PsppireSelector *selector, GtkWidget *source, GtkWidget *dest, SelectItemsFunc *select_func, FilterItemsFunc *filterfunc); The source widget must be the GtkTreeView, on which you have previously called attach_dictionary_to_treeview. The destination widget may be either a GtkEntry, a second GtkTreeView or a GtkTextView. SelectItemsFunc should normally correspond to the type of the destination widget. One of the existing: insert_source_row_into_entry; insert_source_row_into_tree_view ; or insert_source_row_into_text_view functions should suffice for most purposes. (This parameter may disappear in the future). The FilterItemsFunc parameter should be is_currently_in_entry if the destination widget is a GtkEntry, otherwise it should be NULL. (This parameter may also be removed. I can't remember why it was necessary). References ========== http://glade.gnome.org