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
54 #include "sort-cases-dialog.h"
55 #include "psppire-dict.h"
56 #include <math/sort.h>
57 #include "psppire-variable.h"
60 #define _(msgid) gettext (msgid)
61 #define N_(msgid) msgid
64 enum {CRIT_TVM_IDX = 0, CRIT_TVM_DIR};
66 /* Occurs when the dictionary tree view selection changes */
68 dictionary_selection_changed (GtkTreeSelection *selection,
71 GtkTreeSelection *otherselection ;
72 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
74 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
77 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
78 dialog->button_state = VAR_SELECT;
80 otherselection = gtk_tree_view_get_selection(dialog->criteria_view);
82 gtk_tree_selection_unselect_all(otherselection);
86 /* Occurs when the sort criteria tree view selection changes */
88 criteria_selection_changed (GtkTreeSelection *selection,
91 GtkTreeSelection *otherselection ;
92 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
94 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
97 otherselection = gtk_tree_view_get_selection(dialog->dict_view);
99 gtk_arrow_set(dialog->arrow, GTK_ARROW_LEFT, GTK_SHADOW_OUT);
100 dialog->button_state = VAR_DESELECT;
102 gtk_tree_selection_unselect_all(otherselection);
106 /* Occurs when the dialog box is deleted (eg: closed via the title bar) */
108 delete_event_callback(GtkWidget *widget,
112 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
114 g_main_loop_quit(dialog->loop);
116 gtk_widget_hide_on_delete(widget);
118 dialog->response = GTK_RESPONSE_DELETE_EVENT;
123 /* Occurs when the cancel button is clicked */
125 sort_cases_cancel_callback(GObject *obj, gpointer data)
127 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
129 gtk_widget_hide(dialog->window);
131 g_main_loop_quit(dialog->loop);
133 dialog->response = GTK_RESPONSE_CANCEL;
136 /* Occurs when the reset button is clicked */
138 sort_cases_reset_callback(GObject *obj, gpointer data)
140 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
142 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
143 dialog->button_state = VAR_SELECT;
145 gtk_list_store_clear(dialog->criteria_list);
149 /* Add variables currently selected in the dictionary tree view to the
152 select_criteria(GtkTreeModel *model, GtkTreePath *path,
153 GtkTreeIter *iter, gpointer data)
155 GtkTreeIter new_iter;
158 struct PsppireVariable *variable;
159 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
161 /* Get the variable from the dictionary */
162 gtk_tree_model_get (model, iter,
163 DICT_TVM_COL_VAR, &variable,
166 index = psppire_variable_get_index(variable);
168 dir = gtk_toggle_button_get_active (dialog->ascending_button) ?
169 SRT_ASCEND:SRT_DESCEND;
171 /* Append to the list of criteria */
172 gtk_list_store_append(dialog->criteria_list, &new_iter);
173 gtk_list_store_set(dialog->criteria_list,
174 &new_iter, CRIT_TVM_IDX, index, -1);
175 gtk_list_store_set(dialog->criteria_list,
176 &new_iter, CRIT_TVM_DIR, dir, -1);
179 /* Create a list of the RowRefs which are to be removed from the
182 path_to_row_ref(GtkTreeModel *model, GtkTreePath *path,
183 GtkTreeIter *iter, gpointer data)
185 GList **rrlist = data;
186 GtkTreeRowReference *rowref = gtk_tree_row_reference_new(model, path);
188 *rrlist = g_list_append(*rrlist, rowref);
192 /* Remove a row from the list of criteria */
194 deselect_criteria(gpointer data,
198 GtkTreeRowReference *row_ref = data;
200 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) user_data;
202 path = gtk_tree_row_reference_get_path(row_ref);
204 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->criteria_list), &iter, path);
206 gtk_list_store_remove(dialog->criteria_list, &iter);
208 gtk_tree_row_reference_free(row_ref);
213 /* Callback which occurs when the button to remove variables from the list
214 of criteria is clicked. */
216 sort_cases_button_callback(GObject *obj, gpointer data)
218 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
220 if ( dialog->button_state == VAR_SELECT) /* Right facing arrow */
222 GtkTreeSelection *selection =
223 gtk_tree_view_get_selection(dialog->dict_view);
225 gtk_tree_selection_selected_foreach(selection, select_criteria, dialog);
227 else /* Left facing arrow */
229 GList *selectedRows = NULL;
230 GtkTreeSelection *selection =
231 gtk_tree_view_get_selection(dialog->criteria_view);
233 /* Make a list of rows to be deleted */
234 gtk_tree_selection_selected_foreach(selection, path_to_row_ref,
237 /* ... and delete them */
238 g_list_foreach(selectedRows, deselect_criteria, dialog);
240 g_list_free(selectedRows);
245 /* Callback which occurs when the OK button is clicked */
247 sort_cases_ok_callback(GObject *obj, gpointer data)
249 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
251 gtk_widget_hide(dialog->window);
252 g_main_loop_quit(dialog->loop);
254 dialog->response = GTK_RESPONSE_OK;
258 /* This function is responsible for rendering a criterion in the
261 criteria_render_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
262 GtkTreeModel *model, GtkTreeIter *iter,
266 struct PsppireVariable *variable ;
270 PsppireDict *dict = data;
272 gtk_tree_model_get(model, iter,
273 CRIT_TVM_IDX, &var_index,
274 CRIT_TVM_DIR, &direction, -1);
276 variable = psppire_dict_get_variable(dict, var_index);
278 varname = pspp_locale_to_utf8(psppire_variable_get_name(variable),
281 if ( direction == SRT_ASCEND)
282 buf = g_strdup_printf("%s: %s", varname, _("Ascending"));
284 buf = g_strdup_printf("%s: %s", varname, _("Descending"));
288 g_object_set(renderer, "text", buf, NULL);
294 /* Create the dialog */
295 struct sort_cases_dialog *
296 sort_cases_dialog_create(GladeXML *xml)
298 struct sort_cases_dialog *dialog = g_malloc(sizeof(*dialog));
300 dialog->loop = g_main_loop_new(NULL, FALSE);
302 dialog->window = get_widget_assert(xml, "sort-cases-dialog");
304 dialog->dict_view = GTK_TREE_VIEW(get_widget_assert
305 (xml, "sort-cases-treeview-dict"));
306 dialog->criteria_view = GTK_TREE_VIEW(get_widget_assert
307 (xml, "sort-cases-treeview-criteria"));
309 dialog->arrow = GTK_ARROW(get_widget_assert(xml, "sort-cases-arrow"));
310 dialog->button = GTK_BUTTON(get_widget_assert(xml, "sort-cases-button"));
312 dialog->ascending_button =
313 GTK_TOGGLE_BUTTON(get_widget_assert(xml, "sort-cases-button-ascending"));
315 g_signal_connect(dialog->window, "delete-event",
316 G_CALLBACK(delete_event_callback), dialog);
318 g_signal_connect(get_widget_assert(xml, "sort-cases-cancel"),
319 "clicked", G_CALLBACK(sort_cases_cancel_callback), dialog);
321 g_signal_connect(get_widget_assert(xml, "sort-cases-ok"),
322 "clicked", G_CALLBACK(sort_cases_ok_callback), dialog);
325 g_signal_connect(get_widget_assert(xml, "sort-cases-reset"),
326 "clicked", G_CALLBACK(sort_cases_reset_callback), dialog);
329 g_signal_connect(get_widget_assert(xml, "sort-cases-button"),
330 "clicked", G_CALLBACK(sort_cases_button_callback), dialog);
334 /* Set up the dictionary treeview */
335 GtkTreeViewColumn *col;
337 GtkTreeSelection *selection =
338 gtk_tree_view_get_selection(dialog->dict_view);
340 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
342 col = gtk_tree_view_column_new_with_attributes(_("Var"),
348 /* FIXME: make this a value in terms of character widths */
349 g_object_set(col, "min-width", 100, NULL);
351 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
353 gtk_tree_view_append_column(dialog->dict_view, col);
355 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
357 g_signal_connect(selection, "changed",
358 G_CALLBACK(dictionary_selection_changed), dialog);
362 /* Set up the variable list treeview */
363 GtkTreeSelection *selection =
364 gtk_tree_view_get_selection(dialog->criteria_view);
366 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
368 dialog->crit_renderer = gtk_cell_renderer_text_new();
370 dialog->crit_col = gtk_tree_view_column_new_with_attributes(_("Criteria"),
371 dialog->crit_renderer,
376 gtk_tree_view_column_set_sizing (dialog->crit_col, GTK_TREE_VIEW_COLUMN_FIXED);
378 gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->criteria_view),
381 g_signal_connect(selection, "changed",
382 G_CALLBACK(criteria_selection_changed), dialog);
386 /* Create the list of criteria */
387 dialog->criteria_list = gtk_list_store_new(2,
388 G_TYPE_INT, /* index of the variable */
389 G_TYPE_INT /* Ascending/Descending */
392 gtk_tree_view_set_model(dialog->criteria_view,
393 GTK_TREE_MODEL(dialog->criteria_list));
396 dialog->response = GTK_RESPONSE_NONE;
403 convert_list_store_to_criteria(GtkListStore *list,
405 struct sort_criteria *criteria);
409 If the return value is GTK_RESPONSE_OK, then CRITERIA gets filled
410 with a valid sort criteria which can be used to sort the data.
411 This structure and its contents must be freed by the caller. */
413 sort_cases_dialog_run(struct sort_cases_dialog *dialog,
415 struct sort_criteria *criteria
418 g_assert(! g_main_loop_is_running(dialog->loop));
420 gtk_tree_view_set_model(GTK_TREE_VIEW(dialog->dict_view),
421 GTK_TREE_MODEL(dict));
424 gtk_tree_view_column_set_cell_data_func(dialog->crit_col,
425 dialog->crit_renderer,
426 criteria_render_func, dict, 0);
428 gtk_list_store_clear(dialog->criteria_list);
430 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
431 dialog->button_state = VAR_SELECT;
433 gtk_widget_show(dialog->window);
435 g_main_loop_run(dialog->loop);
437 if ( GTK_RESPONSE_OK == dialog->response)
438 convert_list_store_to_criteria(dialog->criteria_list,
441 return dialog->response;
446 /* Convert the GtkListStore to a struct sort_criteria*/
448 convert_list_store_to_criteria(GtkListStore *list,
450 struct sort_criteria *criteria)
456 GtkTreeModel *model = GTK_TREE_MODEL(list);
458 criteria->crit_cnt = gtk_tree_model_iter_n_children (model, NULL);
460 criteria->crits = g_malloc(sizeof(struct sort_criterion) *
463 for(valid = gtk_tree_model_get_iter_first(model, &iter);
465 valid = gtk_tree_model_iter_next(model, &iter))
467 struct PsppireVariable *variable;
469 struct sort_criterion *scn = &criteria->crits[n];
470 g_assert ( n < criteria->crit_cnt);
473 gtk_tree_model_get(model, &iter,
474 CRIT_TVM_IDX, &index,
475 CRIT_TVM_DIR, &scn->dir,
478 variable = psppire_dict_get_variable(dict, index);
480 scn->fv = psppire_variable_get_fv(variable);
481 scn->width = psppire_variable_get_width(variable);