diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 8d9d36fa88..377616d143 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -331,10 +331,8 @@ static gboolean gtk_calendar_query_tooltip (GtkWidget *widget, gboolean keyboard_mode, GtkTooltip *tooltip); -static gboolean gtk_calendar_drag_motion (GtkDropTarget *dest, +static gboolean gtk_calendar_drag_accept (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, GtkCalendar *calendar); static void gtk_calendar_drag_leave (GtkDropTarget *dest, GdkDrop *drop, @@ -792,7 +790,7 @@ gtk_calendar_init (GtkCalendar *calendar) dest = gtk_drop_target_new (formats, GDK_ACTION_COPY); gdk_content_formats_unref (formats); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_motion), calendar); + g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_accept), calendar); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_calendar_drag_leave), calendar); g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_calendar_drag_drop), calendar); @@ -2984,7 +2982,8 @@ got_text (GObject *source, GAsyncResult *result, gpointer data) { - GtkCalendar *calendar = GTK_CALENDAR (data); + GtkDropTarget *dest = GTK_DROP_TARGET (data); + GtkCalendar *calendar = GTK_CALENDAR (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest))); GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar); GdkDrop *drop = GDK_DROP (source); guint day, month, year; @@ -3010,8 +3009,9 @@ got_text (GObject *source, } else suggested_action = 0; - gdk_drop_status (drop, suggested_action); + if (suggested_action == 0) + gtk_drop_target_deny_drop (dest, drop); return; } @@ -3027,6 +3027,7 @@ got_text (GObject *source, g_warning ("Received invalid date data"); g_date_free (date); gdk_drop_finish (drop, 0); + gtk_drop_target_deny_drop (dest, drop); return; } @@ -3046,23 +3047,23 @@ got_text (GObject *source, } static gboolean -gtk_calendar_drag_motion (GtkDropTarget *dest, +gtk_calendar_drag_accept (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, GtkCalendar *calendar) { GdkAtom target; target = gtk_drop_target_find_mimetype (dest); if (!target || gdk_drop_get_actions (drop) == 0) - gdk_drop_status (drop, 0); + { + gdk_drop_status (drop, 0); + return FALSE; + } else if (get_status_pending (drop) == 0) { set_status_pending (drop, gdk_drop_get_actions (drop)); - gdk_drop_read_text_async (drop, NULL, got_text, calendar); + gdk_drop_read_text_async (drop, NULL, got_text, dest); } - return TRUE; } @@ -3079,7 +3080,7 @@ gtk_calendar_drag_drop (GtkDropTarget *dest, if (target != NULL) { set_status_pending (drop, 0); - gdk_drop_read_text_async (drop, NULL, got_text, calendar); + gdk_drop_read_text_async (drop, NULL, got_text, dest); return TRUE; } diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c index 68d0a19652..f1a38acc48 100644 --- a/gtk/gtkexpander.c +++ b/gtk/gtkexpander.c @@ -192,10 +192,8 @@ static void gtk_expander_size_allocate (GtkWidget *widget, int baseline); static gboolean gtk_expander_focus (GtkWidget *widget, GtkDirectionType direction); -static gboolean gtk_expander_drag_motion (GtkDropTarget *dest, +static gboolean gtk_expander_drag_accept (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, GtkExpander *expander); static void gtk_expander_drag_leave (GtkDropTarget *dest, GdkDrop *drop, @@ -379,7 +377,7 @@ gtk_expander_init (GtkExpander *expander) formats = gdk_content_formats_new (NULL, 0); dest = gtk_drop_target_new (formats, 0); gdk_content_formats_unref (formats); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_expander_drag_motion), expander); + g_signal_connect (dest, "accept", G_CALLBACK (gtk_expander_drag_accept), expander); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_expander_drag_leave), expander); gtk_widget_add_controller (GTK_WIDGET (expander), GTK_EVENT_CONTROLLER (dest)); @@ -549,10 +547,8 @@ expand_timeout (gpointer data) } static gboolean -gtk_expander_drag_motion (GtkDropTarget *dest, +gtk_expander_drag_accept (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, GtkExpander *expander) { GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander); diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 38fc967dfb..4aecc911c7 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -2046,10 +2046,8 @@ file_list_drag_drop_cb (GtkDropTarget *dest, /* Disable the normal tree drag motion handler, it makes it look like you're dropping the dragged item onto a tree item */ static gboolean -file_list_drag_motion_cb (GtkDropTarget *dest, +file_list_drag_accept_cb (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, GtkFileChooserWidget *impl) { g_signal_stop_emission_by_name (dest, "accept"); @@ -8497,7 +8495,7 @@ post_process_ui (GtkFileChooserWidget *impl) GDK_ACTION_COPY | GDK_ACTION_MOVE); dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_motion_cb), impl); + g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_accept_cb), impl); g_signal_connect (dest, "drag-drop", G_CALLBACK (file_list_drag_drop_cb), impl); gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (dest)); gdk_content_formats_unref (formats); diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 94e30bee6e..c662156488 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -292,7 +292,7 @@ static GBytes * gtk_icon_view_drag_data_get (const char *mime_type, static void gtk_icon_view_drag_leave (GtkDropTarget *dest, GdkDrop *drop, GtkIconView *icon_view); -static gboolean gtk_icon_view_drag_motion (GtkDropTarget *dest, +static void gtk_icon_view_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y, @@ -6179,7 +6179,7 @@ gtk_icon_view_drag_leave (GtkDropTarget *dest, remove_scroll_timeout (icon_view); } -static gboolean +static void gtk_icon_view_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, @@ -6193,10 +6193,10 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest, gboolean empty; if (!set_destination (icon_view, dest, x, y, &suggested_action, &target)) - return FALSE; - - icon_view->priv->event_last_x = x; - icon_view->priv->event_last_y = y; + { + gdk_drop_status (drop, 0); + return; + } gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); @@ -6233,8 +6233,6 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest, if (path) gtk_tree_path_free (path); - - return TRUE; } static gboolean @@ -6457,7 +6455,7 @@ gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, icon_view->priv->dest = gtk_drop_target_new (formats, actions); g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view); - g_signal_connect (icon_view->priv->dest, "accept", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); + g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); g_signal_connect (icon_view->priv->dest, "drag-drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view); gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index a160ccb4eb..c8fa3c94fd 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -703,7 +703,7 @@ static void gtk_notebook_dnd_finished_cb (GdkDrag *drag, static void gtk_notebook_drag_cancel_cb (GdkDrag *drag, GdkDragCancelReason reason, GtkWidget *widget); -static gboolean gtk_notebook_drag_motion (GtkDropTarget *dest, +static void gtk_notebook_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y); @@ -1349,7 +1349,7 @@ gtk_notebook_init (GtkNotebook *notebook) targets = gdk_content_formats_new (dst_notebook_targets, G_N_ELEMENTS (dst_notebook_targets)); dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_notebook_drag_motion), NULL); + g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_notebook_drag_motion), NULL); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_notebook_drag_leave), NULL); g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_notebook_drag_drop), NULL); gtk_widget_add_controller (GTK_WIDGET (priv->tabs_widget), GTK_EVENT_CONTROLLER (dest)); @@ -3188,7 +3188,7 @@ gtk_notebook_switch_tab_timeout (gpointer data) return FALSE; } -static gboolean +static void gtk_notebook_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, @@ -3202,7 +3202,6 @@ gtk_notebook_drag_motion (GtkDropTarget *dest, GtkNotebookArrow arrow; GdkAtom target, tab_target; GList *tab; - gboolean retval = FALSE; arrow = gtk_notebook_get_arrow (notebook, x, y); if (arrow != ARROW_NONE) @@ -3211,7 +3210,6 @@ gtk_notebook_drag_motion (GtkDropTarget *dest, gtk_notebook_set_scroll_timer (notebook); gdk_drop_status (drop, 0); - retval = TRUE; goto out; } @@ -3225,8 +3223,6 @@ gtk_notebook_drag_motion (GtkDropTarget *dest, GtkWidget *source_child; GdkDrag *drag = gdk_drop_get_drag (drop); - retval = TRUE; - if (!drag) { gdk_drop_status (drop, 0); @@ -3264,8 +3260,6 @@ gtk_notebook_drag_motion (GtkDropTarget *dest, priv->mouse_x = x; priv->mouse_y = y; - retval = TRUE; - if (tab != priv->switch_tab) remove_switch_tab_timer (notebook); @@ -3282,8 +3276,7 @@ gtk_notebook_drag_motion (GtkDropTarget *dest, remove_switch_tab_timer (notebook); } - out: - return retval; + out:; } static void diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index aad2385b60..82c6ddad83 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -1723,12 +1723,12 @@ create_placeholder_row (GtkPlacesSidebar *sidebar) return g_object_new (GTK_TYPE_SIDEBAR_ROW, "placeholder", TRUE, NULL); } -static gboolean +static void drag_motion_callback (GtkDropTarget *dest, GdkDrop *drop, - gint x, - gint y, - gpointer user_data) + gint x, + gint y, + gpointer user_data) { GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data); gint action; @@ -1850,10 +1850,7 @@ drag_motion_callback (GtkDropTarget *dest, out: start_drop_feedback (sidebar, GTK_SIDEBAR_ROW (row), drag); - gdk_drop_status (drop, action); - - return TRUE; } /* Takes an array of URIs and turns it into a list of GFile */ @@ -4088,7 +4085,7 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar) formats = gdk_content_formats_builder_free_to_formats (builder); dest = gtk_drop_target_new (formats, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); gdk_content_formats_unref (formats); - g_signal_connect (dest, "accept", G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (dest, "drag-motion", G_CALLBACK (drag_motion_callback), sidebar); g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop_callback), sidebar); g_signal_connect (dest, "drag-leave", G_CALLBACK (drag_leave_callback), sidebar); gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (dest)); diff --git a/gtk/gtksearchlistmodel.c b/gtk/gtksearchlistmodel.c new file mode 100644 index 0000000000..7a50c28833 --- /dev/null +++ b/gtk/gtksearchlistmodel.c @@ -0,0 +1,544 @@ +/* + * Copyright © 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#include "config.h" + +#include "gtksearchlistmodel.h" + +#include "gtkintl.h" +#include "gtkselectionmodel.h" + +/** + * SECTION:gtksearchlistmodel + * @Short_description: A selection model that allows incremental searching + * @Title: GtkSearchListModel + * @see_also: #GtkSelectionModel + * + * GtkSearchListModel is an implementation of the #GtkSelectionModel interface + * that allows selecting a single element. The selected element can be determined + * interactively via a filter. + */ +struct _GtkSearchListModel +{ + GObject parent_instance; + + GListModel *model; + guint selected; + gpointer selected_item; + + GtkFilter *filter; +}; + +struct _GtkSearchListModelClass +{ + GObjectClass parent_class; +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_FILTER, + PROP_SELECTED, + PROP_SELECTED_ITEM, + + N_PROPS +}; + +static GParamSpec *properties[N_PROPS] = { NULL, }; + +static GType +gtk_search_list_model_get_item_type (GListModel *list) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list); + + return g_list_model_get_item_type (self->model); +} + +static guint +gtk_search_list_model_get_n_items (GListModel *list) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list); + + return g_list_model_get_n_items (self->model); +} + +static gpointer +gtk_search_list_model_get_item (GListModel *list, + guint position) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list); + + return g_list_model_get_item (self->model, position); +} + +static void +gtk_search_list_model_list_model_init (GListModelInterface *iface) +{ + iface->get_item_type = gtk_search_list_model_get_item_type; + iface->get_n_items = gtk_search_list_model_get_n_items; + iface->get_item = gtk_search_list_model_get_item; +} + +static gboolean +gtk_search_list_model_is_selected (GtkSelectionModel *model, + guint position) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (model); + + return self->selected == position; +} + +static void +gtk_search_list_model_query_range (GtkSelectionModel *model, + guint position, + guint *start_range, + guint *n_range, + gboolean *selected) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (model); + guint n_items; + + n_items = g_list_model_get_n_items (self->model); + + if (position >= n_items) + { + *start_range = position; + *n_range = 0; + *selected = FALSE; + } + else if (self->selected == GTK_INVALID_LIST_POSITION) + { + *start_range = 0; + *n_range = n_items; + *selected = FALSE; + } + else if (position < self->selected) + { + *start_range = 0; + *n_range = self->selected; + *selected = FALSE; + } + else if (position > self->selected) + { + *start_range = self->selected + 1; + *n_range = n_items - *start_range; + *selected = FALSE; + } + else + { + *start_range = self->selected; + *n_range = 1; + *selected = TRUE; + } +} + +static void +gtk_search_list_model_selection_model_init (GtkSelectionModelInterface *iface) +{ + iface->is_selected = gtk_search_list_model_is_selected; + iface->query_range = gtk_search_list_model_query_range; +} + +G_DEFINE_TYPE_EXTENDED (GtkSearchListModel, gtk_search_list_model, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, + gtk_search_list_model_list_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL, + gtk_search_list_model_selection_model_init)) + +static void +gtk_search_list_model_items_changed_cb (GListModel *model, + guint position, + guint removed, + guint added, + GtkSearchListModel *self) +{ + g_object_freeze_notify (G_OBJECT (self)); + + if (self->selected_item == NULL) + { + /* nothing to do */ + } + else if (self->selected < position) + { + /* unchanged */ + } + else if (self->selected >= position + removed) + { + self->selected += added - removed; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]); + } + else + { + guint i; + + for (i = 0; i < added; i++) + { + gpointer item = g_list_model_get_item (model, position + i); + if (item == self->selected_item) + { + /* the item moved */ + //TODO refilter + if (self->selected != position + i) + { + self->selected = position + i; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]); + } + break; + } + } + if (i == added) + { + /* the item really was deleted */ + g_clear_object (&self->selected_item); + self->selected = GTK_INVALID_LIST_POSITION; + //TODO refilter + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]); + } + } + + g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +set_selected (GtkSearchListModel *self, + guint position) +{ + gpointer new_selected = NULL; + guint old_position; + + if (self->selected == position) + return; + + new_selected = g_list_model_get_item (self->model, position); + + if (new_selected == NULL) + position = GTK_INVALID_LIST_POSITION; + + if (self->selected == position) + return; + + old_position = self->selected; + self->selected = position; + g_clear_object (&self->selected_item); + self->selected_item = new_selected; + + if (old_position == GTK_INVALID_LIST_POSITION) + gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, 1); + else if (position == GTK_INVALID_LIST_POSITION) + gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, 1); + else if (position < old_position) + gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, old_position - position + 1); + else + gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, position - old_position + 1); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]); +} + +static gboolean +match_item (GtkSearchListModel *self, + guint position) +{ + gpointer item; + gboolean result; + + item = g_list_model_get_item (self->model, position); + result = gtk_filter_match (self->filter, item); + g_object_unref (item); + + return result; +} + +static guint +find_next_match (GtkSearchListModel *self, + guint position, + gboolean forward) +{ + guint i; + + if (position == GTK_INVALID_LIST_POSITION) + position = 0; + + g_print ("search %s from %u\n", forward ? "forward" : "backward", position); + if (forward) + for (i = position; i < g_list_model_get_n_items (self->model); i++) + { + if (match_item (self, i)) + return i; + } + else + for (i = position; ; i--) + { + if (match_item (self, i)) + return i; + if (i == 0) + break; + } + + return GTK_INVALID_LIST_POSITION; +} + +static void +gtk_search_list_model_filter_changed_cb (GtkFilter *filter, + GtkFilterChange change, + GtkSearchListModel *self) +{ + guint position; + +g_print ("filter changed: change %d, strictness %d\n", change, gtk_filter_get_strictness (self->filter)); + + if (gtk_filter_get_strictness (self->filter) == GTK_FILTER_MATCH_NONE) + position = GTK_INVALID_LIST_POSITION; + else + switch (change) + { + case GTK_FILTER_CHANGE_DIFFERENT: + case GTK_FILTER_CHANGE_LESS_STRICT: + position = find_next_match (self, 0, TRUE); + break; + case GTK_FILTER_CHANGE_MORE_STRICT: + position = find_next_match (self, self->selected, TRUE); + break; + default: + g_assert_not_reached (); + } + + g_print ("select %u\n", position); + set_selected (self, position); +} + +static void +gtk_search_list_model_clear_model (GtkSearchListModel *self) +{ + if (self->model == NULL) + return; + + g_signal_handlers_disconnect_by_func (self->model, + gtk_search_list_model_items_changed_cb, + self); + g_clear_object (&self->model); +} + +static void +gtk_search_list_model_clear_filter (GtkSearchListModel *self) +{ + if (self->filter == NULL) + return; + + g_signal_handlers_disconnect_by_func (self->filter, + gtk_search_list_model_filter_changed_cb, + self); + + g_clear_object (&self->filter); +} + +static void +gtk_search_list_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object); + + switch (prop_id) + { + case PROP_MODEL: + self->model = g_value_dup_object (value); + g_signal_connect (self->model, "items-changed", + G_CALLBACK (gtk_search_list_model_items_changed_cb), self); + break; + + case PROP_FILTER: + self->filter = g_value_dup_object (value); + g_signal_connect (self->filter, "changed", + G_CALLBACK (gtk_search_list_model_filter_changed_cb), self); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_search_list_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, self->model); + break; + + case PROP_FILTER: + g_value_set_object (value, self->filter); + break; + + case PROP_SELECTED: + g_value_set_uint (value, self->selected); + break; + + case PROP_SELECTED_ITEM: + g_value_set_object (value, self->selected_item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_search_list_model_dispose (GObject *object) +{ + GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object); + + gtk_search_list_model_clear_model (self); + gtk_search_list_model_clear_filter (self); + + self->selected = GTK_INVALID_LIST_POSITION; + g_clear_object (&self->selected_item); + + G_OBJECT_CLASS (gtk_search_list_model_parent_class)->dispose (object); +} + +static void +gtk_search_list_model_class_init (GtkSearchListModelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = gtk_search_list_model_get_property; + gobject_class->set_property = gtk_search_list_model_set_property; + gobject_class->dispose = gtk_search_list_model_dispose; + + /** + * GtkSearchListModel:selected: + * + * Position of the selected item + */ + properties[PROP_SELECTED] = + g_param_spec_uint ("selected", + P_("Selected"), + P_("Position of the selected item"), + 0, G_MAXUINT, GTK_INVALID_LIST_POSITION, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GtkSearchListModel:selected-item: + * + * The selected item + */ + properties[PROP_SELECTED_ITEM] = + g_param_spec_object ("selected-item", + P_("Selected Item"), + P_("The selected item"), + G_TYPE_OBJECT, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * GtkSearchListModel:model: + * + * The model being managed + */ + properties[PROP_MODEL] = + g_param_spec_object ("model", + P_("The model"), + P_("The model being managed"), + G_TYPE_LIST_MODEL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * GtkSearchListModel:filter: + * + * The filter determining the selected item + */ + properties[PROP_FILTER] = + g_param_spec_object ("filter", + P_("The filter"), + P_("The filter being used"), + GTK_TYPE_FILTER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPS, properties); +} + +static void +gtk_search_list_model_init (GtkSearchListModel *self) +{ + self->selected = GTK_INVALID_LIST_POSITION; +} + +/** + * gtk_search_list_model_new: + * @model: (transfer none): the #GListModel to manage + * @filter: (transfer none): the #GtkFilter to use + * + * Creates a new selection to handle @model. + * + * Returns: (transfer full) (type GtkSearchListModel): a new #GtkSearchListModel + **/ +GtkSearchListModel * +gtk_search_list_model_new (GListModel *model, + GtkFilter *filter) +{ + g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL); + + return g_object_new (GTK_TYPE_SEARCH_LIST_MODEL, + "model", model, + "filter", filter, + NULL); +} + +gboolean +gtk_search_list_model_next_match (GtkSearchListModel *self) +{ + guint position; + + position = find_next_match (self, self->selected, TRUE); + if (position == GTK_INVALID_LIST_POSITION) + return FALSE; + + set_selected (self, position); + + return TRUE; +} + +gboolean +gtk_search_list_model_previous_match (GtkSearchListModel *self) +{ + guint position; + + position = find_next_match (self, self->selected, FALSE); + if (position == GTK_INVALID_LIST_POSITION) + return FALSE; + + set_selected (self, position); + + return TRUE; +} diff --git a/gtk/gtksearchlistmodel.h b/gtk/gtksearchlistmodel.h new file mode 100644 index 0000000000..324449bbcd --- /dev/null +++ b/gtk/gtksearchlistmodel.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GTK_SEARCH_LIST_MODEL_H__ +#define __GTK_SEARCH_LIST_MODEL_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + + +G_BEGIN_DECLS + +#define GTK_TYPE_SEARCH_LIST_MODEL (gtk_search_list_model_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkSearchListModel, gtk_search_list_model, GTK, SEARCH_LIST_MODEL, GObject) + +GDK_AVAILABLE_IN_ALL +GtkSearchListModel * gtk_search_list_model_new (GListModel *model, + GtkFilter *filter); +GDK_AVAILABLE_IN_ALL +gboolean gtk_search_list_model_next_match (GtkSearchListModel *self); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_search_list_model_previous_match (GtkSearchListModel *self); + +G_END_DECLS + +#endif /* __GTK_SEARCH_LIST_MODEL_H__ */ diff --git a/gtk/gtkstackswitcher.c b/gtk/gtkstackswitcher.c index a5f3bbaeb1..c9d30fecf1 100644 --- a/gtk/gtkstackswitcher.c +++ b/gtk/gtkstackswitcher.c @@ -95,7 +95,10 @@ enum { static void gtk_stack_switcher_drag_leave (GtkDropTarget *dest, GdkDrop *drop, GtkStackSwitcher *self); -static gboolean gtk_stack_switcher_drag_motion (GtkDropTarget *dest, +static gboolean gtk_stack_switcher_drag_accept (GtkDropTarget *dest, + GdkDrop *drop, + GtkStackSwitcher *self); +static void gtk_stack_switcher_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y, @@ -121,7 +124,8 @@ gtk_stack_switcher_init (GtkStackSwitcher *switcher) dest = gtk_drop_target_new (formats, 0); gdk_content_formats_unref (formats); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_stack_switcher_drag_leave), switcher); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_stack_switcher_drag_motion), switcher); + g_signal_connect (dest, "accept", G_CALLBACK (gtk_stack_switcher_drag_accept), switcher); + g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_stack_switcher_drag_motion), switcher); gtk_widget_add_controller (GTK_WIDGET (switcher), GTK_EVENT_CONTROLLER (dest)); } @@ -264,6 +268,14 @@ gtk_stack_switcher_switch_timeout (gpointer data) } static gboolean +gtk_stack_switcher_drag_accept (GtkDropTarget *dest, + GdkDrop *drop, + GtkStackSwitcher *self) +{ + return TRUE; +} + +static void gtk_stack_switcher_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, @@ -274,7 +286,6 @@ gtk_stack_switcher_drag_motion (GtkDropTarget *dest, GtkWidget *button; GHashTableIter iter; gpointer value; - gboolean retval = FALSE; button = NULL; g_hash_table_iter_init (&iter, priv->buttons); @@ -285,7 +296,6 @@ gtk_stack_switcher_drag_motion (GtkDropTarget *dest, if (gtk_widget_contains (GTK_WIDGET (value), cx, cy)) { button = GTK_WIDGET (value); - retval = TRUE; break; } } @@ -302,8 +312,6 @@ gtk_stack_switcher_drag_motion (GtkDropTarget *dest, self); g_source_set_name_by_id (priv->switch_timer, "[gtk] gtk_stack_switcher_switch_timeout"); } - - return retval; } static void diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 0c775301ec..45b9b3c82c 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -339,7 +339,10 @@ static gboolean gtk_text_drag_drop (GtkDropTarget *dest, int x, int y, GtkText *text); -static gboolean gtk_text_drag_motion (GtkDropTarget *dest, +static gboolean gtk_text_drag_accept (GtkDropTarget *dest, + GdkDrop *drop, + GtkText *self); +static void gtk_text_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y, @@ -1732,7 +1735,8 @@ gtk_text_init (GtkText *self) formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING); dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_motion), self); + g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_accept), self); + g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_drag_motion), self); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_drag_leave), self); g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_drag_drop), self); gdk_content_formats_unref (formats); @@ -6230,6 +6234,29 @@ gtk_text_drag_drop (GtkDropTarget *dest, } static gboolean +gtk_text_drag_accept (GtkDropTarget *dest, + GdkDrop *drop, + GtkText *self) +{ + GtkTextPrivate *priv = gtk_text_get_instance_private (self); + GdkDragAction suggested_action; + + if (priv->editable && + gtk_drop_target_find_mimetype (dest) != NULL) + { + suggested_action = GDK_ACTION_COPY | GDK_ACTION_MOVE; + } + else + { + /* Entry not editable, or no text */ + suggested_action = 0; + } + + gdk_drop_status (drop, suggested_action); + return suggested_action != 0; +} + +static void gtk_text_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, @@ -6237,8 +6264,6 @@ gtk_text_drag_motion (GtkDropTarget *dest, GtkText *self) { GtkTextPrivate *priv = gtk_text_get_instance_private (self); - GtkWidget *widget = GTK_WIDGET (self); - GdkDragAction suggested_action; int new_position, old_position; old_position = priv->dnd_position; @@ -6247,8 +6272,6 @@ gtk_text_drag_motion (GtkDropTarget *dest, if (priv->editable && gtk_drop_target_find_mimetype (dest) != NULL) { - suggested_action = GDK_ACTION_COPY | GDK_ACTION_MOVE; - if (priv->selection_bound == priv->current_pos || new_position < priv->selection_bound || new_position > priv->current_pos) @@ -6263,16 +6286,11 @@ gtk_text_drag_motion (GtkDropTarget *dest, else { /* Entry not editable, or no text */ - suggested_action = 0; priv->dnd_position = -1; } - gdk_drop_status (drop, suggested_action); - if (priv->dnd_position != old_position) - gtk_widget_queue_draw (widget); - - return TRUE; + gtk_widget_queue_draw (GTK_WIDGET (self)); } /* We display the cursor when diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 21d0635a05..ed5f6ad90d 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -1638,7 +1638,7 @@ gtk_text_view_init (GtkTextView *text_view) dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE); gdk_content_formats_unref (formats); g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_view_drag_leave), text_view); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_view_drag_motion), text_view); + g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_view_drag_motion), text_view); g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_view_drag_drop), text_view); gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest)); diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 6dc7fa4004..003204cf1b 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -695,7 +695,7 @@ static GBytes *gtk_tree_view_drag_data_get (const char *mimetype, static void gtk_tree_view_drag_leave (GtkDropTarget *dest, GdkDrop *drop, GtkTreeView *tree_view); -static gboolean gtk_tree_view_drag_motion (GtkDropTarget *dest, +static void gtk_tree_view_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, int y, @@ -7244,7 +7244,7 @@ gtk_tree_view_drag_leave (GtkDropTarget *dest, } -static gboolean +static void gtk_tree_view_drag_motion (GtkDropTarget *dest, GdkDrop *drop, int x, @@ -7258,7 +7258,10 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest, GdkAtom target; if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target)) - return FALSE; + { + gdk_drop_status (drop, 0); + return; + } tree_view->event_last_x = x; tree_view->event_last_y = y; @@ -7305,8 +7308,6 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest, if (path) gtk_tree_path_free (path); - - return TRUE; } @@ -12924,7 +12925,7 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, di->dest = gtk_drop_target_new (formats, actions); g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view); - g_signal_connect (di->dest, "accept", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); + g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); g_signal_connect (di->dest, "drag-drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view); gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); g_object_ref (di->dest); diff --git a/tests/testdnd.c b/tests/testdnd.c index 42d55589bb..54df81423e 100644 --- a/tests/testdnd.c +++ b/tests/testdnd.c @@ -445,9 +445,7 @@ popdown_cb (gpointer data) gboolean popup_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) + GdkDrop *drop) { gdk_drop_status (drop, GDK_ACTION_COPY); return TRUE; diff --git a/tests/testdnd2.c b/tests/testdnd2.c index 09cefbfae9..ecf5a95551 100644 --- a/tests/testdnd2.c +++ b/tests/testdnd2.c @@ -179,8 +179,6 @@ delayed_deny (gpointer data) static gboolean image_drag_motion (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, gpointer data) { GtkWidget *image = data; diff --git a/tests/testdnd3.c b/tests/testdnd3.c index 5f2bb0e92b..f9315569fc 100644 --- a/tests/testdnd3.c +++ b/tests/testdnd3.c @@ -221,9 +221,7 @@ item_drag_drop (GtkDropTarget *dest, static gboolean item_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) + GdkDrop *drop) { if (gtk_drop_target_find_mimetype (dest) != NULL) {