2 PSPPIRE --- A Graphical User Interface for PSPP
3 Copyright (C) 2006 Free Software Foundation
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 /* This module describes the behaviour of the Sort Cases dialog box. */
23 /* This code is rather quick and dirty. Some of the issues are:
25 1. Character set conversion when displaying dictionary tree view.
27 2. The interaction between the dictionary treeview, the criteria
28 list treeview and the button, needs to be abstracted and made
29 available as an external interface.
31 3. There's no destroy function for this dialog.
33 4. Some of the functionality might be better implemented with
36 5. Double clicking the tree view rows should insert/delete them
37 from the criteria list.
39 6. Changing the Ascending/Descending flag ought to be possible for
40 a criteria already in the criteria tree view.
42 7. Variables which are in the criteria tree view should not be
43 shown in the dictionary treeview.
45 8. The dialog box structure itself ought to be a GtkWindow and
52 #include "sort-cases-dialog.h"
53 #include "psppire-dict.h"
54 #include <math/sort.h>
57 #define _(msgid) gettext (msgid)
58 #define N_(msgid) msgid
61 enum {CRIT_TVM_IDX = 0, CRIT_TVM_DIR};
63 /* Occurs when the dictionary tree view selection changes */
65 dictionary_selection_changed (GtkTreeSelection *selection,
68 GtkTreeSelection *otherselection ;
69 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
71 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
74 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
75 dialog->button_state = VAR_SELECT;
77 otherselection = gtk_tree_view_get_selection(dialog->criteria_view);
79 gtk_tree_selection_unselect_all(otherselection);
83 /* Occurs when the sort criteria tree view selection changes */
85 criteria_selection_changed (GtkTreeSelection *selection,
88 GtkTreeSelection *otherselection ;
89 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
91 if ( 0 == gtk_tree_selection_count_selected_rows(selection) )
94 otherselection = gtk_tree_view_get_selection(dialog->dict_view);
96 gtk_arrow_set(dialog->arrow, GTK_ARROW_LEFT, GTK_SHADOW_OUT);
97 dialog->button_state = VAR_DESELECT;
99 gtk_tree_selection_unselect_all(otherselection);
103 /* Occurs when the dialog box is deleted (eg: closed via the title bar) */
105 delete_event_callback(GtkWidget *widget,
109 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
111 g_main_loop_quit(dialog->loop);
113 gtk_widget_hide_on_delete(widget);
115 dialog->response = GTK_RESPONSE_DELETE_EVENT;
120 /* Occurs when the cancel button is clicked */
122 sort_cases_cancel_callback(GObject *obj, gpointer data)
124 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
126 gtk_widget_hide(dialog->window);
128 g_main_loop_quit(dialog->loop);
130 dialog->response = GTK_RESPONSE_CANCEL;
133 /* Occurs when the reset button is clicked */
135 sort_cases_reset_callback(GObject *obj, gpointer data)
137 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
139 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
140 dialog->button_state = VAR_SELECT;
142 gtk_list_store_clear(dialog->criteria_list);
146 /* Add variables currently selected in the dictionary tree view to the
149 select_criteria(GtkTreeModel *model, GtkTreePath *path,
150 GtkTreeIter *iter, gpointer data)
152 GtkTreeIter new_iter;
155 struct variable *variable;
156 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
158 /* Get the variable from the dictionary */
159 gtk_tree_model_get (model, iter,
160 DICT_TVM_COL_VAR, &variable,
163 index = var_get_dict_index (variable);
165 dir = gtk_toggle_button_get_active (dialog->ascending_button) ?
166 SRT_ASCEND:SRT_DESCEND;
168 /* Append to the list of criteria */
169 gtk_list_store_append(dialog->criteria_list, &new_iter);
170 gtk_list_store_set(dialog->criteria_list,
171 &new_iter, CRIT_TVM_IDX, index, -1);
172 gtk_list_store_set(dialog->criteria_list,
173 &new_iter, CRIT_TVM_DIR, dir, -1);
176 /* Create a list of the RowRefs which are to be removed from the
179 path_to_row_ref(GtkTreeModel *model, GtkTreePath *path,
180 GtkTreeIter *iter, gpointer data)
182 GList **rrlist = data;
183 GtkTreeRowReference *rowref = gtk_tree_row_reference_new(model, path);
185 *rrlist = g_list_append(*rrlist, rowref);
189 /* Remove a row from the list of criteria */
191 deselect_criteria(gpointer data,
195 GtkTreeRowReference *row_ref = data;
197 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) user_data;
199 path = gtk_tree_row_reference_get_path(row_ref);
201 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->criteria_list), &iter, path);
203 gtk_list_store_remove(dialog->criteria_list, &iter);
205 gtk_tree_row_reference_free(row_ref);
210 /* Callback which occurs when the button to remove variables from the list
211 of criteria is clicked. */
213 sort_cases_button_callback(GObject *obj, gpointer data)
215 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
217 if ( dialog->button_state == VAR_SELECT) /* Right facing arrow */
219 GtkTreeSelection *selection =
220 gtk_tree_view_get_selection(dialog->dict_view);
222 gtk_tree_selection_selected_foreach(selection, select_criteria, dialog);
224 else /* Left facing arrow */
226 GList *selectedRows = NULL;
227 GtkTreeSelection *selection =
228 gtk_tree_view_get_selection(dialog->criteria_view);
230 /* Make a list of rows to be deleted */
231 gtk_tree_selection_selected_foreach(selection, path_to_row_ref,
234 /* ... and delete them */
235 g_list_foreach(selectedRows, deselect_criteria, dialog);
237 g_list_free(selectedRows);
242 /* Callback which occurs when the OK button is clicked */
244 sort_cases_ok_callback(GObject *obj, gpointer data)
246 struct sort_cases_dialog *dialog = (struct sort_cases_dialog*) data;
248 gtk_widget_hide(dialog->window);
249 g_main_loop_quit(dialog->loop);
251 dialog->response = GTK_RESPONSE_OK;
255 /* This function is responsible for rendering a criterion in the
258 criteria_render_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
259 GtkTreeModel *model, GtkTreeIter *iter,
263 struct variable *variable ;
267 PsppireDict *dict = data;
269 gtk_tree_model_get(model, iter,
270 CRIT_TVM_IDX, &var_index,
271 CRIT_TVM_DIR, &direction, -1);
273 variable = psppire_dict_get_variable(dict, var_index);
275 varname = pspp_locale_to_utf8 (var_get_name(variable),
278 if ( direction == SRT_ASCEND)
279 buf = g_strdup_printf("%s: %s", varname, _("Ascending"));
281 buf = g_strdup_printf("%s: %s", varname, _("Descending"));
285 g_object_set(renderer, "text", buf, NULL);
291 /* Create the dialog */
292 struct sort_cases_dialog *
293 sort_cases_dialog_create(GladeXML *xml)
295 struct sort_cases_dialog *dialog = g_malloc(sizeof(*dialog));
297 dialog->loop = g_main_loop_new(NULL, FALSE);
299 dialog->window = get_widget_assert(xml, "sort-cases-dialog");
301 dialog->dict_view = GTK_TREE_VIEW(get_widget_assert
302 (xml, "sort-cases-treeview-dict"));
303 dialog->criteria_view = GTK_TREE_VIEW(get_widget_assert
304 (xml, "sort-cases-treeview-criteria"));
306 dialog->arrow = GTK_ARROW(get_widget_assert(xml, "sort-cases-arrow"));
307 dialog->button = GTK_BUTTON(get_widget_assert(xml, "sort-cases-button"));
309 dialog->ascending_button =
310 GTK_TOGGLE_BUTTON(get_widget_assert(xml, "sort-cases-button-ascending"));
312 g_signal_connect(dialog->window, "delete-event",
313 G_CALLBACK(delete_event_callback), dialog);
315 g_signal_connect(get_widget_assert(xml, "sort-cases-cancel"),
316 "clicked", G_CALLBACK(sort_cases_cancel_callback), dialog);
318 g_signal_connect(get_widget_assert(xml, "sort-cases-ok"),
319 "clicked", G_CALLBACK(sort_cases_ok_callback), dialog);
322 g_signal_connect(get_widget_assert(xml, "sort-cases-reset"),
323 "clicked", G_CALLBACK(sort_cases_reset_callback), dialog);
326 g_signal_connect(get_widget_assert(xml, "sort-cases-button"),
327 "clicked", G_CALLBACK(sort_cases_button_callback), dialog);
331 /* Set up the dictionary treeview */
332 GtkTreeViewColumn *col;
334 GtkTreeSelection *selection =
335 gtk_tree_view_get_selection(dialog->dict_view);
337 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
339 col = gtk_tree_view_column_new_with_attributes(_("Var"),
345 /* FIXME: make this a value in terms of character widths */
346 g_object_set(col, "min-width", 100, NULL);
348 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
350 gtk_tree_view_append_column(dialog->dict_view, col);
352 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
354 g_signal_connect(selection, "changed",
355 G_CALLBACK(dictionary_selection_changed), dialog);
359 /* Set up the variable list treeview */
360 GtkTreeSelection *selection =
361 gtk_tree_view_get_selection(dialog->criteria_view);
363 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
365 dialog->crit_renderer = gtk_cell_renderer_text_new();
367 dialog->crit_col = gtk_tree_view_column_new_with_attributes(_("Criteria"),
368 dialog->crit_renderer,
373 gtk_tree_view_column_set_sizing (dialog->crit_col, GTK_TREE_VIEW_COLUMN_FIXED);
375 gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->criteria_view),
378 g_signal_connect(selection, "changed",
379 G_CALLBACK(criteria_selection_changed), dialog);
383 /* Create the list of criteria */
384 dialog->criteria_list = gtk_list_store_new(2,
385 G_TYPE_INT, /* index of the variable */
386 G_TYPE_INT /* Ascending/Descending */
389 gtk_tree_view_set_model(dialog->criteria_view,
390 GTK_TREE_MODEL(dialog->criteria_list));
393 dialog->response = GTK_RESPONSE_NONE;
400 convert_list_store_to_criteria(GtkListStore *list,
402 struct sort_criteria *criteria);
406 If the return value is GTK_RESPONSE_OK, then CRITERIA gets filled
407 with a valid sort criteria which can be used to sort the data.
408 This structure and its contents must be freed by the caller. */
410 sort_cases_dialog_run(struct sort_cases_dialog *dialog,
412 struct sort_criteria *criteria
415 g_assert(! g_main_loop_is_running(dialog->loop));
417 gtk_tree_view_set_model(GTK_TREE_VIEW(dialog->dict_view),
418 GTK_TREE_MODEL(dict));
421 gtk_tree_view_column_set_cell_data_func(dialog->crit_col,
422 dialog->crit_renderer,
423 criteria_render_func, dict, 0);
425 gtk_list_store_clear(dialog->criteria_list);
427 gtk_arrow_set(dialog->arrow, GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
428 dialog->button_state = VAR_SELECT;
430 gtk_widget_show(dialog->window);
432 g_main_loop_run(dialog->loop);
434 if ( GTK_RESPONSE_OK == dialog->response)
435 convert_list_store_to_criteria(dialog->criteria_list,
438 return dialog->response;
443 /* Convert the GtkListStore to a struct sort_criteria*/
445 convert_list_store_to_criteria(GtkListStore *list,
447 struct sort_criteria *criteria)
453 GtkTreeModel *model = GTK_TREE_MODEL(list);
455 criteria->crit_cnt = gtk_tree_model_iter_n_children (model, NULL);
457 criteria->crits = g_malloc(sizeof(struct sort_criterion) *
460 for(valid = gtk_tree_model_get_iter_first(model, &iter);
462 valid = gtk_tree_model_iter_next(model, &iter))
464 struct variable *variable;
466 struct sort_criterion *scn = &criteria->crits[n];
467 g_assert ( n < criteria->crit_cnt);
470 gtk_tree_model_get(model, &iter,
471 CRIT_TVM_IDX, &index,
472 CRIT_TVM_DIR, &scn->dir,
475 variable = psppire_dict_get_variable(dict, index);
477 scn->fv = var_get_case_index (variable);
478 scn->width = var_get_width(variable);