X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpsppire-selector.c;h=5ec051bb9a98c8f83ed4a87f67bf4eab4e74399c;hb=1abba60b3018ce7eb3d85930ad591e4d6ad39813;hp=453fee8ca006d8aba5d1db4b928da0cef973d46a;hpb=50f8d6d2dbf599b410c9bc346a47cb577fba8fe1;p=pspp-builds.git diff --git a/src/ui/gui/psppire-selector.c b/src/ui/gui/psppire-selector.c index 453fee8c..5ec051bb 100644 --- a/src/ui/gui/psppire-selector.c +++ b/src/ui/gui/psppire-selector.c @@ -1,10 +1,9 @@ -/* - PSPPIRE --- A Graphical User Interface for PSPP +/* PSPPIRE - a graphical user interface for PSPP. Copyright (C) 2007 Free Software Foundation - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -13,9 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ /* This module provides a widget, PsppireSelector derived from @@ -68,6 +65,7 @@ #include #include +#include #include static void psppire_selector_base_finalize (PsppireSelectorClass *, gpointer); @@ -75,6 +73,10 @@ static void psppire_selector_base_init (PsppireSelectorClass *class); static void psppire_selector_class_init (PsppireSelectorClass *class); static void psppire_selector_init (PsppireSelector *selector); + +static void set_direction (PsppireSelector *, enum psppire_selector_dir); + + enum {SELECTED, /* Emitted when an item is inserted into dest */ DE_SELECTED, /* Emitted when an item is removed from dest */ n_SIGNALS}; @@ -116,11 +118,76 @@ psppire_selector_finalize (GObject *object) { } +/* Properties */ +enum +{ + PROP_0, + PROP_ORIENTATION +}; + + +static void +psppire_selector_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PsppireSelector *selector = PSPPIRE_SELECTOR (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + selector->orientation = g_value_get_enum (value); + set_direction (selector, selector->direction); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + + +static void +psppire_selector_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireSelector *selector = PSPPIRE_SELECTOR (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, selector->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + }; +} + static void psppire_selector_class_init (PsppireSelectorClass *class) { + GObjectClass *object_class = G_OBJECT_CLASS (class); + GParamSpec *orientation_spec = + g_param_spec_enum ("orientation", + "Orientation", + "Where the selector is relative to its subjects", + G_TYPE_PSPPIRE_SELECTOR_ORIENTATION, + PSPPIRE_SELECT_SOURCE_BEFORE_DEST /* default value */, + G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE); + + + object_class->set_property = psppire_selector_set_property; + object_class->get_property = psppire_selector_get_property; + + g_object_class_install_property (object_class, + PROP_ORIENTATION, + orientation_spec); + signals [SELECTED] = g_signal_new ("selected", G_TYPE_FROM_CLASS (class), @@ -140,8 +207,6 @@ psppire_selector_class_init (PsppireSelectorClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - } @@ -175,9 +240,7 @@ psppire_selector_init (PsppireSelector *selector) gtk_widget_show (selector->arrow); - /* FIXME: This shouldn't be necessary, but Glade interfaces seem to - need it. */ - gtk_widget_show (GTK_WIDGET (selector)); + selector->selecting = FALSE; } @@ -196,9 +259,48 @@ set_direction (PsppireSelector *selector, enum psppire_selector_dir d) /* FIXME: Need to reverse the arrow direction if an RTL locale is in effect */ if ( d == PSPPIRE_SELECTOR_SOURCE_TO_DEST ) - g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL); + { + switch (selector->orientation) + { + case PSPPIRE_SELECT_SOURCE_BEFORE_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL); + break; + case PSPPIRE_SELECT_SOURCE_AFTER_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL); + break; + case PSPPIRE_SELECT_SOURCE_ABOVE_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL); + break; + case PSPPIRE_SELECT_SOURCE_BELOW_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL); + break; + default: + g_assert_not_reached (); + break; + }; + } else - g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL); + { + switch (selector->orientation) + { + case PSPPIRE_SELECT_SOURCE_BEFORE_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL); + break; + case PSPPIRE_SELECT_SOURCE_AFTER_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL); + break; + case PSPPIRE_SELECT_SOURCE_ABOVE_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL); + break; + case PSPPIRE_SELECT_SOURCE_BELOW_DEST: + g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL); + break; + default: + g_assert_not_reached (); + break; + }; + + } } /* Callback for when the source selection changes */ @@ -291,6 +393,8 @@ de_select_selection_tree_view (PsppireSelector *selector) static void de_select_selection (PsppireSelector *selector) { + selector->selecting = TRUE; + if ( GTK_IS_TREE_VIEW (selector->dest ) ) de_select_selection_tree_view (selector); @@ -300,6 +404,8 @@ de_select_selection (PsppireSelector *selector) else g_assert_not_reached (); + selector->selecting = FALSE; + gtk_tree_model_filter_refilter (selector->filtered_source); g_signal_emit (selector, signals [DE_SELECTED], 0); @@ -322,6 +428,7 @@ select_selection (PsppireSelector *selector) g_return_if_fail (selector->select_items); + selector->selecting = TRUE; for (item = g_list_first (selected_rows); item != NULL; @@ -350,9 +457,11 @@ select_selection (PsppireSelector *selector) gtk_tree_model_filter_refilter (selector->filtered_source); g_signal_emit (selector, signals [SELECTED], 0); + + selector->selecting = FALSE; } -/* Callback fro then the source treeview is activated (double clicked) */ +/* Callback for when the source treeview is activated (double clicked) */ static void on_row_activate (GtkTreeView *tree_view, GtkTreePath *path, @@ -450,8 +559,8 @@ is_source_item_visible (GtkTreeModel *childmodel, { PsppireSelector *selector = list->data; - if ( selector->filter (childmodel, iter, selector)) - return FALSE; + if ( selector->filter && selector->filter (childmodel, iter, selector)) + return FALSE; list = list->next; } @@ -511,26 +620,68 @@ set_tree_view_source (PsppireSelector *selector, } +/* + Callback for when the destination treeview's data changes + */ +static void +on_dest_data_change (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + PsppireSelector *selector = user_data; + + if ( selector->selecting) return; + + gtk_tree_model_filter_refilter (selector->filtered_source); +} + + +static void +on_dest_data_delete (GtkTreeModel *tree_model, + GtkTreePath *path, + gpointer user_data) +{ + PsppireSelector *selector = user_data; + + if ( selector->selecting ) return; + + gtk_tree_model_filter_refilter (selector->filtered_source); +} + + + + /* Set the destination widget to DEST */ static void set_tree_view_dest (PsppireSelector *selector, GtkTreeView *dest) { GtkTreeSelection* selection = gtk_tree_view_get_selection (dest); + GtkTreeModel *model = gtk_tree_view_get_model (dest); gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select), selector); + + g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change), + selector); + + g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete), + selector); + } -/* Callback for when the DEST GtkEntry is activated (Enter is pressed) */ -static void -on_entry_activate (GtkEntry *w, gpointer data) +/* Callback which causes the filter to be refiltered. + Called when the DEST GtkEntry is activated (Enter is pressed), or when it + looses focus. +*/ +static gboolean +refilter (PsppireSelector *selector) { - PsppireSelector * selector = data; - gtk_tree_model_filter_refilter (selector->filtered_source); + return FALSE; } /* Callback for when the DEST GtkEntry is selected (clicked) */ @@ -544,16 +695,53 @@ on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data) return FALSE; } + + +/* Callback for when an item disappears from the source list. + By implication, this means that the item has been inserted into the + destination. + */ +static void +on_row_deleted (PsppireSelector *selector) +{ + g_signal_emit (selector, signals [SELECTED], 0); +} + +/* Callback for when a new item appears in the source list. + By implication, this means that an item has been deleted from the + destination. + */ +static void +on_row_inserted (PsppireSelector *selector) +{ + g_signal_emit (selector, signals [DE_SELECTED], 0); +} + + + /* Set DEST to be the destination GtkEntry widget */ static void set_entry_dest (PsppireSelector *selector, GtkEntry *dest) { - g_signal_connect (dest, "activate", G_CALLBACK (on_entry_activate), + g_signal_connect_swapped (dest, "activate", G_CALLBACK (refilter), + selector); + + g_signal_connect_swapped (dest, "changed", G_CALLBACK (refilter), selector); g_signal_connect (dest, "focus-in-event", G_CALLBACK (on_entry_dest_select), selector); + + g_signal_connect_swapped (dest, "focus-out-event", G_CALLBACK (refilter), + selector); + + + g_signal_connect_swapped (selector->filtered_source, "row-deleted", + G_CALLBACK (on_row_deleted), selector); + + g_signal_connect_swapped (selector->filtered_source, "row-inserted", + G_CALLBACK (on_row_inserted), selector); } @@ -566,6 +754,8 @@ psppire_selector_set_subjects (PsppireSelector *selector, SelectItemsFunc *select_func, FilterItemsFunc *filter_func ) { + g_assert(selector); + selector->filter = filter_func ; selector->source = source; @@ -592,8 +782,32 @@ psppire_selector_set_subjects (PsppireSelector *selector, else if ( GTK_IS_ENTRY (dest)) set_entry_dest (selector, GTK_ENTRY (dest)); + else if (GTK_IS_TEXT_VIEW (dest)) + { + /* Nothing to be done */ + } + else g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (dest)); selector->select_items = select_func; } + + + +GType +psppire_selector_orientation_get_type (void) +{ + static GType etype = 0; + if (etype == 0) { + static const GEnumValue values[] = { + { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" }, + { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" }, + { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" }, + { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values); + } + return etype; +}