2 PSPPIRE --- A Graphical User Interface for PSPP
3 Copyright (C) 2006 Free Software Foundation
4 Written by John Darrington
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 /* This module describes the behaviour of the Sort Cases dialog box. */
24 /* This code is rather quick and dirty. Some of the issues are:
26 1. Character set conversion when displaying dictionary tree view.
28 2. The interaction between the dictionary treeview, the criteria
29 list treeview and the button, needs to be abstracted and made
30 available as an external interface.
32 3. There's no destroy function for this dialog.
34 4. Some of the functionality might be better implemented with
37 5. Double clicking the tree view rows should insert/delete them
38 from the criteria list.
40 6. Changing the Ascending/Descending flag ought to be possible for
41 a criteria already in the criteria tree view.
43 7. Variables which are in the criteria tree view should not be
44 shown in the dictionary treeview.
46 8. The dialog box structure itself ought to be a GtkWindow and
53 #include "sort-cases-dialog.h"
54 #include "psppire-dict.h"
55 #include <math/sort.h>
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
62 enum {CRIT_TVM_IDX = 0, CRIT_TVM_DIR};
64 /* Occurs when the dictionary tree view selection changes */
66 dictionary_selection_changed (GtkTreeSelection *selection,
69 GtkTreeSelection *otherselection ;
70 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
72 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
75 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
76 dialog->button_state = VAR_SELECT;
78 otherselection = gtk_tree_view_get_selection(dialog->criteria_view);
80 gtk_tree_selection_unselect_all(otherselection);
84 /* Occurs when the sort criteria tree view selection changes */
86 criteria_selection_changed (GtkTreeSelection *selection,
89 GtkTreeSelection *otherselection ;
90 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
92 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
95 otherselection = gtk_tree_view_get_selection(dialog->dict_view);
97 gtk_arrow_set(dialog->arrow, GTK_ARROW_LEFT, GTK_SHADOW_OUT);
98 dialog->button_state = VAR_DESELECT;
100 gtk_tree_selection_unselect_all(otherselection);
104 /* Occurs when the dialog box is deleted (eg: closed via the title bar) */
106 delete_event_callback(GtkWidget *widget,
110 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
112 g_main_loop_quit(dialog->loop);
114 gtk_widget_hide_on_delete(widget);
116 dialog->response = GTK_RESPONSE_DELETE_EVENT;
121 /* Occurs when the cancel button is clicked */
123 sort_cases_cancel_callback(GObject *obj, gpointer data)
125 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
127 gtk_widget_hide(dialog->window);
129 g_main_loop_quit(dialog->loop);
131 dialog->response = GTK_RESPONSE_CANCEL;
134 /* Occurs when the reset button is clicked */
136 sort_cases_reset_callback(GObject *obj, gpointer data)
138 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
140 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
141 dialog->button_state = VAR_SELECT;
143 gtk_list_store_clear(dialog->criteria_list);
147 /* Add variables currently selected in the dictionary tree view to the
150 select_criteria(GtkTreeModel *model, GtkTreePath *path,
151 GtkTreeIter *iter, gpointer data)
153 GtkTreeIter new_iter;
156 struct variable *variable;
157 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
159 /* Get the variable from the dictionary */
160 gtk_tree_model_get (model, iter,
161 DICT_TVM_COL_VAR, &variable,
164 index = var_get_dict_index (variable);
166 dir = gtk_toggle_button_get_active (dialog->ascending_button) ?
167 SRT_ASCEND:SRT_DESCEND;
169 /* Append to the list of criteria */
170 gtk_list_store_append(dialog->criteria_list, &new_iter);
171 gtk_list_store_set(dialog->criteria_list,
172 &new_iter, CRIT_TVM_IDX, index, -1);
173 gtk_list_store_set(dialog->criteria_list,
174 &new_iter, CRIT_TVM_DIR, dir, -1);
177 /* Create a list of the RowRefs which are to be removed from the
180 path_to_row_ref(GtkTreeModel *model, GtkTreePath *path,
181 GtkTreeIter *iter, gpointer data)
183 GList **rrlist = data;
184 GtkTreeRowReference *rowref = gtk_tree_row_reference_new(model, path);
186 *rrlist = g_list_append(*rrlist, rowref);
190 /* Remove a row from the list of criteria */
192 deselect_criteria(gpointer data,
196 GtkTreeRowReference *row_ref = data;
198 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) user_data;
200 path = gtk_tree_row_reference_get_path(row_ref);
202 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->criteria_list), &iter, path);
204 gtk_list_store_remove(dialog->criteria_list, &iter);
206 gtk_tree_row_reference_free(row_ref);
211 /* Callback which occurs when the button to remove variables from the list
212 of criteria is clicked. */
214 sort_cases_button_callback(GObject *obj, gpointer data)
216 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
218 if ( dialog->button_state == VAR_SELECT) /* Right facing arrow */
220 GtkTreeSelection *selection =
221 gtk_tree_view_get_selection(dialog->dict_view);
223 gtk_tree_selection_selected_foreach(selection, select_criteria, dialog);
225 else /* Left facing arrow */
227 GList *selectedRows = NULL;
228 GtkTreeSelection *selection =
229 gtk_tree_view_get_selection(dialog->criteria_view);
231 /* Make a list of rows to be deleted */
232 gtk_tree_selection_selected_foreach(selection, path_to_row_ref,
235 /* ... and delete them */
236 g_list_foreach(selectedRows, deselect_criteria, dialog);
238 g_list_free(selectedRows);
243 /* Callback which occurs when the OK button is clicked */
245 sort_cases_ok_callback(GObject *obj, gpointer data)
247 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
249 gtk_widget_hide(dialog->window);
250 g_main_loop_quit(dialog->loop);
252 dialog->response = GTK_RESPONSE_OK;
256 /* This function is responsible for rendering a criterion in the
259 criteria_render_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
260 GtkTreeModel *model, GtkTreeIter *iter,
264 struct variable *variable ;
268 PsppireDict *dict = data;
270 gtk_tree_model_get(model, iter,
271 CRIT_TVM_IDX, &var_index,
272 CRIT_TVM_DIR, &direction, -1);
274 variable = psppire_dict_get_variable(dict, var_index);
276 varname = pspp_locale_to_utf8 (var_get_name(variable),
279 if ( direction == SRT_ASCEND)
280 buf = g_strdup_printf("%s: %s", varname, _("Ascending"));
282 buf = g_strdup_printf("%s: %s", varname, _("Descending"));
286 g_object_set(renderer, "text", buf, NULL);
292 /* Create the dialog */
293 struct sort_cases_dialog *
294 sort_cases_dialog_create(GladeXML *xml)
296 struct sort_cases_dialog *dialog = g_malloc(sizeof(*dialog));
298 dialog->loop = g_main_loop_new(NULL, FALSE);
300 dialog->window = get_widget_assert(xml, "sort-cases-dialog");
302 dialog->dict_view = GTK_TREE_VIEW(get_widget_assert
303 (xml, "sort-cases-treeview-dict"));
304 dialog->criteria_view = GTK_TREE_VIEW(get_widget_assert
305 (xml, "sort-cases-treeview-criteria"));
307 dialog->arrow = GTK_ARROW(get_widget_assert(xml, "sort-cases-arrow"));
308 dialog->button = GTK_BUTTON(get_widget_assert(xml, "sort-cases-button"));
310 dialog->ascending_button =
311 GTK_TOGGLE_BUTTON(get_widget_assert(xml, "sort-cases-button-ascending"));
313 g_signal_connect(dialog->window, "delete-event",
314 G_CALLBACK(delete_event_callback), dialog);
316 g_signal_connect(get_widget_assert(xml, "sort-cases-cancel"),
317 "clicked", G_CALLBACK(sort_cases_cancel_callback), dialog);
319 g_signal_connect(get_widget_assert(xml, "sort-cases-ok"),
320 "clicked", G_CALLBACK(sort_cases_ok_callback), dialog);
323 g_signal_connect(get_widget_assert(xml, "sort-cases-reset"),
324 "clicked", G_CALLBACK(sort_cases_reset_callback), dialog);
327 g_signal_connect(get_widget_assert(xml, "sort-cases-button"),
328 "clicked", G_CALLBACK(sort_cases_button_callback), dialog);
332 /* Set up the dictionary treeview */
333 GtkTreeViewColumn *col;
335 GtkTreeSelection *selection =
336 gtk_tree_view_get_selection(dialog->dict_view);
338 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
340 col = gtk_tree_view_column_new_with_attributes(_("Var"),
346 /* FIXME: make this a value in terms of character widths */
347 g_object_set(col, "min-width", 100, NULL);
349 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
351 gtk_tree_view_append_column(dialog->dict_view, col);
353 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
355 g_signal_connect(selection, "changed",
356 G_CALLBACK(dictionary_selection_changed), dialog);
360 /* Set up the variable list treeview */
361 GtkTreeSelection *selection =
362 gtk_tree_view_get_selection(dialog->criteria_view);
364 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
366 dialog->crit_renderer = gtk_cell_renderer_text_new();
368 dialog->crit_col = gtk_tree_view_column_new_with_attributes(_("Criteria"),
369 dialog->crit_renderer,
374 gtk_tree_view_column_set_sizing (dialog->crit_col, GTK_TREE_VIEW_COLUMN_FIXED);
376 gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->criteria_view),
379 g_signal_connect(selection, "changed",
380 G_CALLBACK(criteria_selection_changed), dialog);
384 /* Create the list of criteria */
385 dialog->criteria_list = gtk_list_store_new(2,
386 G_TYPE_INT, /* index of the variable */
387 G_TYPE_INT /* Ascending/Descending */
390 gtk_tree_view_set_model(dialog->criteria_view,
391 GTK_TREE_MODEL(dialog->criteria_list));
394 dialog->response = GTK_RESPONSE_NONE;
401 convert_list_store_to_criteria(GtkListStore *list,
403 struct sort_criteria *criteria);
407 If the return value is GTK_RESPONSE_OK, then CRITERIA gets filled
408 with a valid sort criteria which can be used to sort the data.
409 This structure and its contents must be freed by the caller. */
411 sort_cases_dialog_run(struct sort_cases_dialog *dialog,
413 struct sort_criteria *criteria
416 g_assert(! g_main_loop_is_running(dialog->loop));
418 gtk_tree_view_set_model(GTK_TREE_VIEW(dialog->dict_view),
419 GTK_TREE_MODEL(dict));
422 gtk_tree_view_column_set_cell_data_func(dialog->crit_col,
423 dialog->crit_renderer,
424 criteria_render_func, dict, 0);
426 gtk_list_store_clear(dialog->criteria_list);
428 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
429 dialog->button_state = VAR_SELECT;
431 gtk_widget_show(dialog->window);
433 g_main_loop_run(dialog->loop);
435 if ( GTK_RESPONSE_OK == dialog->response)
436 convert_list_store_to_criteria(dialog->criteria_list,
439 return dialog->response;
444 /* Convert the GtkListStore to a struct sort_criteria*/
446 convert_list_store_to_criteria(GtkListStore *list,
448 struct sort_criteria *criteria)
454 GtkTreeModel *model = GTK_TREE_MODEL(list);
456 criteria->crit_cnt = gtk_tree_model_iter_n_children (model, NULL);
458 criteria->crits = g_malloc(sizeof(struct sort_criterion) *
461 for(valid = gtk_tree_model_get_iter_first(model, &iter);
463 valid = gtk_tree_model_iter_next(model, &iter))
465 struct variable *variable;
467 struct sort_criterion *scn = &criteria->crits[n];
468 g_assert ( n < criteria->crit_cnt);
471 gtk_tree_model_get(model, &iter,
472 CRIT_TVM_IDX, &index,
473 CRIT_TVM_DIR, &scn->dir,
476 variable = psppire_dict_get_variable(dict, index);
478 scn->fv = var_get_case_index (variable);
479 scn->width = var_get_width(variable);