summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimptagpopup.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimptagpopup.c')
-rw-r--r--app/widgets/gimptagpopup.c1518
1 files changed, 1518 insertions, 0 deletions
diff --git a/app/widgets/gimptagpopup.c b/app/widgets/gimptagpopup.c
new file mode 100644
index 0000000..cc76670
--- /dev/null
+++ b/app/widgets/gimptagpopup.c
@@ -0,0 +1,1518 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptagentry.c
+ * Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "widgets-types.h"
+
+#include "core/gimpcontainer.h"
+#include "core/gimpcontext.h"
+#include "core/gimptag.h"
+#include "core/gimptagged.h"
+#include "core/gimptaggedcontainer.h"
+#include "core/gimpviewable.h"
+
+#include "gimpcombotagentry.h"
+#include "gimptagentry.h"
+#include "gimptagpopup.h"
+
+#include "gimp-intl.h"
+
+
+#define MENU_SCROLL_STEP1 8
+#define MENU_SCROLL_STEP2 15
+#define MENU_SCROLL_FAST_ZONE 8
+#define MENU_SCROLL_TIMEOUT1 50
+#define MENU_SCROLL_TIMEOUT2 20
+
+#define GIMP_TAG_POPUP_MARGIN 5
+#define GIMP_TAG_POPUP_PADDING 2
+#define GIMP_TAG_POPUP_LINE_SPACING 2
+
+enum
+{
+ PROP_0,
+ PROP_OWNER
+};
+
+struct _PopupTagData
+{
+ GimpTag *tag;
+ GdkRectangle bounds;
+ GtkStateType state;
+};
+
+
+static void gimp_tag_popup_constructed (GObject *object);
+static void gimp_tag_popup_dispose (GObject *object);
+static void gimp_tag_popup_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_tag_popup_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_tag_popup_border_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpTagPopup *popup);
+static gboolean gimp_tag_popup_list_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpTagPopup *popup);
+static gboolean gimp_tag_popup_border_event (GtkWidget *widget,
+ GdkEvent *event);
+static gboolean gimp_tag_popup_list_event (GtkWidget *widget,
+ GdkEvent *event,
+ GimpTagPopup *popup);
+static gboolean gimp_tag_popup_is_in_tag (PopupTagData *tag_data,
+ gint x,
+ gint y);
+static void gimp_tag_popup_queue_draw_tag (GimpTagPopup *widget,
+ PopupTagData *tag_data);
+static void gimp_tag_popup_toggle_tag (GimpTagPopup *popup,
+ PopupTagData *tag_data);
+static void gimp_tag_popup_check_can_toggle (GimpTagged *tagged,
+ GimpTagPopup *popup);
+static gint gimp_tag_popup_layout_tags (GimpTagPopup *popup,
+ gint width);
+static gboolean gimp_tag_popup_scroll_timeout (gpointer data);
+static void gimp_tag_popup_remove_scroll_timeout (GimpTagPopup *popup);
+static gboolean gimp_tag_popup_scroll_timeout_initial (gpointer data);
+static void gimp_tag_popup_start_scrolling (GimpTagPopup *popup);
+static void gimp_tag_popup_stop_scrolling (GimpTagPopup *popup);
+static void gimp_tag_popup_scroll_by (GimpTagPopup *popup,
+ gint step);
+static void gimp_tag_popup_handle_scrolling (GimpTagPopup *popup,
+ gint x,
+ gint y,
+ gboolean enter,
+ gboolean motion);
+
+static gboolean gimp_tag_popup_button_scroll (GimpTagPopup *popup,
+ GdkEventButton *event);
+
+static void get_arrows_visible_area (GimpTagPopup *combo_entry,
+ GdkRectangle *border,
+ GdkRectangle *upper,
+ GdkRectangle *lower,
+ gint *arrow_space);
+static void get_arrows_sensitive_area (GimpTagPopup *popup,
+ GdkRectangle *upper,
+ GdkRectangle *lower);
+
+
+G_DEFINE_TYPE (GimpTagPopup, gimp_tag_popup, GTK_TYPE_WINDOW);
+
+#define parent_class gimp_tag_popup_parent_class
+
+
+static void
+gimp_tag_popup_class_init (GimpTagPopupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_tag_popup_constructed;
+ object_class->dispose = gimp_tag_popup_dispose;
+ object_class->set_property = gimp_tag_popup_set_property;
+ object_class->get_property = gimp_tag_popup_get_property;
+
+ g_object_class_install_property (object_class, PROP_OWNER,
+ g_param_spec_object ("owner", NULL, NULL,
+ GIMP_TYPE_COMBO_TAG_ENTRY,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_tag_popup_init (GimpTagPopup *popup)
+{
+ GtkWidget *widget = GTK_WIDGET (popup);
+
+ popup->upper_arrow_state = GTK_STATE_NORMAL;
+ popup->lower_arrow_state = GTK_STATE_NORMAL;
+
+ gtk_widget_add_events (widget,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_SCROLL_MASK);
+
+ popup->frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (popup->frame), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (popup), popup->frame);
+ gtk_widget_show (popup->frame);
+
+ popup->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+ gtk_container_add (GTK_CONTAINER (popup->frame), popup->alignment);
+ gtk_widget_show (popup->alignment);
+
+ popup->tag_area = gtk_drawing_area_new ();
+ gtk_widget_add_events (popup->tag_area,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK);
+ gtk_container_add (GTK_CONTAINER (popup->alignment), popup->tag_area);
+ gtk_widget_show (popup->tag_area);
+
+ g_signal_connect (popup->alignment, "expose-event",
+ G_CALLBACK (gimp_tag_popup_border_expose),
+ popup);
+ g_signal_connect (popup, "event",
+ G_CALLBACK (gimp_tag_popup_border_event),
+ NULL);
+ g_signal_connect (popup->tag_area, "expose-event",
+ G_CALLBACK (gimp_tag_popup_list_expose),
+ popup);
+ g_signal_connect (popup->tag_area, "event",
+ G_CALLBACK (gimp_tag_popup_list_event),
+ popup);
+}
+
+static void
+gimp_tag_popup_constructed (GObject *object)
+{
+ GimpTagPopup *popup = GIMP_TAG_POPUP (object);
+ GimpTaggedContainer *container;
+ GtkWidget *entry;
+ GtkAllocation entry_allocation;
+ GtkStyle *frame_style;
+ gint x;
+ gint y;
+ gint width;
+ gint height;
+ gint popup_height;
+ GHashTable *tag_hash;
+ GList *tag_list;
+ GList *tag_iterator;
+ gint i;
+ gint max_height;
+ gint screen_height;
+ gchar **current_tags;
+ gint current_count;
+ GdkRectangle popup_rects[2]; /* variants of popup placement */
+ GdkRectangle popup_rect; /* best popup rect in screen coordinates */
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ entry = GTK_WIDGET (popup->combo_entry);
+
+ gtk_window_set_screen (GTK_WINDOW (popup), gtk_widget_get_screen (entry));
+
+ popup->context = gtk_widget_create_pango_context (GTK_WIDGET (popup));
+ popup->layout = pango_layout_new (popup->context);
+
+ gtk_widget_get_allocation (entry, &entry_allocation);
+
+ gtk_widget_style_get (GTK_WIDGET (popup),
+ "scroll-arrow-vlength", &popup->scroll_arrow_height,
+ NULL);
+
+ pango_layout_set_attributes (popup->layout,
+ popup->combo_entry->normal_item_attr);
+
+ current_tags = gimp_tag_entry_parse_tags (GIMP_TAG_ENTRY (popup->combo_entry));
+ current_count = g_strv_length (current_tags);
+
+ container = GIMP_TAG_ENTRY (popup->combo_entry)->container;
+
+ tag_hash = container->tag_ref_counts;
+ tag_list = g_hash_table_get_keys (tag_hash);
+ tag_list = g_list_sort (tag_list, gimp_tag_compare_func);
+
+ popup->tag_count = g_list_length (tag_list);
+ popup->tag_data = g_new0 (PopupTagData, popup->tag_count);
+
+ for (i = 0, tag_iterator = tag_list;
+ i < popup->tag_count;
+ i++, tag_iterator = g_list_next (tag_iterator))
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+ gint j;
+
+ tag_data->tag = tag_iterator->data;
+ tag_data->state = GTK_STATE_NORMAL;
+
+ g_object_ref (tag_data->tag);
+
+ for (j = 0; j < current_count; j++)
+ {
+ if (! gimp_tag_compare_with_string (tag_data->tag, current_tags[j]))
+ {
+ tag_data->state = GTK_STATE_SELECTED;
+ break;
+ }
+ }
+ }
+
+ g_list_free (tag_list);
+ g_strfreev (current_tags);
+
+ if (GIMP_TAG_ENTRY (popup->combo_entry)->mode == GIMP_TAG_ENTRY_MODE_QUERY)
+ {
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ if (popup->tag_data[i].state != GTK_STATE_SELECTED)
+ {
+ popup->tag_data[i].state = GTK_STATE_INSENSITIVE;
+ }
+ }
+
+ gimp_container_foreach (GIMP_CONTAINER (container),
+ (GFunc) gimp_tag_popup_check_can_toggle,
+ popup);
+ }
+
+ frame_style = gtk_widget_get_style (popup->frame);
+
+ width = (entry_allocation.width -
+ 2 * frame_style->xthickness);
+ height = (gimp_tag_popup_layout_tags (popup, width) +
+ 2 * frame_style->ythickness);
+
+ gdk_window_get_origin (gtk_widget_get_window (entry), &x, &y);
+
+ max_height = entry_allocation.height * 10;
+
+ screen_height = gdk_screen_get_height (gtk_widget_get_screen (entry));
+
+ popup_height = MIN (height, max_height);
+
+ popup_rects[0].x = x;
+ popup_rects[0].y = 0;
+ popup_rects[0].width = entry_allocation.width;
+ popup_rects[0].height = y + entry_allocation.height;
+
+ popup_rects[1].x = x;
+ popup_rects[1].y = y;
+ popup_rects[1].width = popup_rects[0].width;
+ popup_rects[1].height = screen_height - popup_rects[0].height;
+
+ if (popup_rects[0].height >= popup_height)
+ {
+ popup_rect = popup_rects[0];
+ popup_rect.y += popup_rects[0].height - popup_height;
+ popup_rect.height = popup_height;
+ }
+ else if (popup_rects[1].height >= popup_height)
+ {
+ popup_rect = popup_rects[1];
+ popup_rect.height = popup_height;
+ }
+ else
+ {
+ if (popup_rects[0].height >= popup_rects[1].height)
+ {
+ popup_rect = popup_rects[0];
+ popup_rect.y += popup->scroll_arrow_height + frame_style->ythickness;
+ }
+ else
+ {
+ popup_rect = popup_rects[1];
+ popup_rect.y -= popup->scroll_arrow_height + frame_style->ythickness;
+ }
+
+ popup_height = popup_rect.height;
+ }
+
+ if (popup_height < height)
+ {
+ popup->arrows_visible = TRUE;
+ popup->upper_arrow_state = GTK_STATE_INSENSITIVE;
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (popup->alignment),
+ popup->scroll_arrow_height + 2,
+ popup->scroll_arrow_height + 2, 0, 0);
+
+ popup_height -= 2 * popup->scroll_arrow_height + 4;
+
+ popup->scroll_height = height - popup_height;
+ popup->scroll_y = 0;
+ popup->scroll_step = 0;
+ }
+
+ gtk_widget_set_size_request (popup->tag_area, width, popup_height);
+
+ gtk_window_move (GTK_WINDOW (popup), popup_rect.x, popup_rect.y);
+ gtk_window_resize (GTK_WINDOW (popup), popup_rect.width, popup_rect.height);
+}
+
+static void
+gimp_tag_popup_dispose (GObject *object)
+{
+ GimpTagPopup *popup = GIMP_TAG_POPUP (object);
+
+ gimp_tag_popup_remove_scroll_timeout (popup);
+
+ g_clear_object (&popup->combo_entry);
+ g_clear_object (&popup->layout);
+ g_clear_object (&popup->context);
+
+ if (popup->tag_data)
+ {
+ gint i;
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ g_object_unref (popup->tag_data[i].tag);
+ }
+
+ g_clear_pointer (&popup->tag_data, g_free);
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_tag_popup_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTagPopup *popup = GIMP_TAG_POPUP (object);
+
+ switch (property_id)
+ {
+ case PROP_OWNER:
+ popup->combo_entry = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_tag_popup_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTagPopup *popup = GIMP_TAG_POPUP (object);
+
+ switch (property_id)
+ {
+ case PROP_OWNER:
+ g_value_set_object (value, popup->combo_entry);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_tag_popup_new:
+ * @combo_entry: #GimpComboTagEntry which is owner of the popup window.
+ *
+ * Tag popup widget is only useful for for #GimpComboTagEntry and
+ * should not be used elsewhere.
+ *
+ * Return value: a newly created #GimpTagPopup widget.
+ **/
+GtkWidget *
+gimp_tag_popup_new (GimpComboTagEntry *combo_entry)
+{
+ g_return_val_if_fail (GIMP_IS_COMBO_TAG_ENTRY (combo_entry), NULL);
+
+ return g_object_new (GIMP_TYPE_TAG_POPUP,
+ "type", GTK_WINDOW_POPUP,
+ "owner", combo_entry,
+ NULL);
+}
+
+/**
+ * gimp_tag_popup_show:
+ * @tag_popup: an instance of #GimpTagPopup
+ *
+ * Show tag popup widget. If mouse grab cannot be obtained for widget,
+ * it is destroyed.
+ **/
+void
+gimp_tag_popup_show (GimpTagPopup *popup)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (GIMP_IS_TAG_POPUP (popup));
+
+ widget = GTK_WIDGET (popup);
+
+ gtk_widget_show (widget);
+
+ gtk_grab_add (widget);
+ gtk_widget_grab_focus (widget);
+
+ if (gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, NULL,
+ GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
+ {
+ /* pointer grab must be attained otherwise user would have
+ * problems closing the popup window.
+ */
+ gtk_grab_remove (widget);
+ gtk_widget_destroy (widget);
+ }
+}
+
+static gint
+gimp_tag_popup_layout_tags (GimpTagPopup *popup,
+ gint width)
+{
+ PangoFontMetrics *font_metrics;
+ gint x;
+ gint y;
+ gint height = 0;
+ gint i;
+ gint line_height;
+ gint space_width;
+
+ x = GIMP_TAG_POPUP_MARGIN;
+ y = GIMP_TAG_POPUP_MARGIN;
+
+ font_metrics = pango_context_get_metrics (popup->context,
+ pango_context_get_font_description (popup->context),
+ NULL);
+
+ line_height = PANGO_PIXELS ((pango_font_metrics_get_ascent (font_metrics) +
+ pango_font_metrics_get_descent (font_metrics)));
+ space_width = PANGO_PIXELS (pango_font_metrics_get_approximate_char_width (font_metrics));
+
+ pango_font_metrics_unref (font_metrics);
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+ gint w, h;
+
+ pango_layout_set_text (popup->layout,
+ gimp_tag_get_name (tag_data->tag), -1);
+ pango_layout_get_pixel_size (popup->layout, &w, &h);
+
+ tag_data->bounds.width = w + 2 * GIMP_TAG_POPUP_PADDING;
+ tag_data->bounds.height = h + 2 * GIMP_TAG_POPUP_PADDING;
+
+ if (x + space_width + tag_data->bounds.width +
+ GIMP_TAG_POPUP_MARGIN - 1 > width)
+ {
+ x = GIMP_TAG_POPUP_MARGIN;
+ y += line_height + 2 * GIMP_TAG_POPUP_PADDING + GIMP_TAG_POPUP_LINE_SPACING;
+ }
+
+ tag_data->bounds.x = x;
+ tag_data->bounds.y = y;
+
+ x += tag_data->bounds.width + space_width;
+ }
+
+ if (gtk_widget_get_direction (GTK_WIDGET (popup)) == GTK_TEXT_DIR_RTL)
+ {
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+
+ tag_data->bounds.x = (width -
+ tag_data->bounds.x -
+ tag_data->bounds.width);
+ }
+ }
+
+ height = y + line_height + GIMP_TAG_POPUP_MARGIN;
+
+ return height;
+}
+
+static gboolean
+gimp_tag_popup_border_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpTagPopup *popup)
+{
+ GdkWindow *window = gtk_widget_get_window (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GdkRectangle border;
+ GdkRectangle upper;
+ GdkRectangle lower;
+ gint arrow_space;
+ gint arrow_size;
+
+ if (event->window != gtk_widget_get_window (widget))
+ return FALSE;
+
+ get_arrows_visible_area (popup, &border, &upper, &lower, &arrow_space);
+
+ arrow_size = 0.7 * arrow_space;
+
+ gtk_paint_box (style, window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu",
+ 0, 0, -1, -1);
+
+ if (popup->arrows_visible)
+ {
+ /* upper arrow */
+
+ gtk_paint_box (style, window,
+ popup->upper_arrow_state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu",
+ upper.x,
+ upper.y,
+ upper.width,
+ upper.height);
+
+ gtk_paint_arrow (style, window,
+ popup->upper_arrow_state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu_scroll_arrow_up",
+ GTK_ARROW_UP,
+ TRUE,
+ upper.x + (upper.width - arrow_size) / 2,
+ upper.y + style->ythickness + (arrow_space - arrow_size) / 2,
+ arrow_size, arrow_size);
+
+ /* lower arrow */
+
+ gtk_paint_box (style, window,
+ popup->lower_arrow_state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu",
+ lower.x,
+ lower.y,
+ lower.width,
+ lower.height);
+
+ gtk_paint_arrow (style, window,
+ popup->lower_arrow_state,
+ GTK_SHADOW_OUT,
+ &event->area, widget, "menu_scroll_arrow_down",
+ GTK_ARROW_DOWN,
+ TRUE,
+ lower.x + (lower.width - arrow_size) / 2,
+ lower.y + style->ythickness + (arrow_space - arrow_size) / 2,
+ arrow_size, arrow_size);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_tag_popup_border_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GimpTagPopup *popup = GIMP_TAG_POPUP (widget);
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ GdkEventButton *button_event = (GdkEventButton *) event;
+ GtkAllocation allocation;
+ gint x;
+ gint y;
+
+ if (button_event->window == gtk_widget_get_window (widget) &&
+ gimp_tag_popup_button_scroll (popup, button_event))
+ {
+ return TRUE;
+ }
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gdk_window_get_pointer (gtk_widget_get_window (widget), &x, &y, NULL);
+
+ if (button_event->window != gtk_widget_get_window (popup->tag_area) &&
+ (x < allocation.y ||
+ y < allocation.x ||
+ x > allocation.x + allocation.width ||
+ y > allocation.y + allocation.height))
+ {
+ /* user has clicked outside the popup area,
+ * which means it should be hidden.
+ */
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ GDK_CURRENT_TIME);
+ gtk_widget_destroy (widget);
+ }
+ }
+ else if (event->type == GDK_MOTION_NOTIFY)
+ {
+ GdkEventMotion *motion_event = (GdkEventMotion *) event;
+ gint x, y;
+
+ gdk_window_get_pointer (gtk_widget_get_window (widget), &x, &y, NULL);
+
+ gimp_tag_popup_handle_scrolling (popup, x, y,
+ motion_event->window ==
+ gtk_widget_get_window (widget),
+ TRUE);
+ }
+ else if (event->type == GDK_BUTTON_RELEASE)
+ {
+ GdkEventButton *button_event = (GdkEventButton *) event;
+
+ popup->single_select_disabled = TRUE;
+
+ if (button_event->window == gtk_widget_get_window (widget) &&
+ gimp_tag_popup_button_scroll (popup, button_event))
+ {
+ return TRUE;
+ }
+ }
+ else if (event->type == GDK_GRAB_BROKEN)
+ {
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ GDK_CURRENT_TIME);
+ gtk_widget_destroy (widget);
+ }
+ else if (event->type == GDK_KEY_PRESS)
+ {
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ GDK_CURRENT_TIME);
+ gtk_widget_destroy (widget);
+ }
+ else if (event->type == GDK_SCROLL)
+ {
+ GdkEventScroll *scroll_event = (GdkEventScroll *) event;
+
+ switch (scroll_event->direction)
+ {
+ case GDK_SCROLL_RIGHT:
+ case GDK_SCROLL_DOWN:
+ gimp_tag_popup_scroll_by (popup, MENU_SCROLL_STEP2);
+ return TRUE;
+
+ case GDK_SCROLL_LEFT:
+ case GDK_SCROLL_UP:
+ gimp_tag_popup_scroll_by (popup, - MENU_SCROLL_STEP2);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_tag_popup_list_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpTagPopup *popup)
+{
+ GdkWindow *window = gtk_widget_get_window (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ cairo_t *cr;
+ PangoAttribute *attribute;
+ PangoAttrList *attributes;
+ gint i;
+
+ cr = gdk_cairo_create (event->window);
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+
+ pango_layout_set_text (popup->layout,
+ gimp_tag_get_name (tag_data->tag), -1);
+
+ switch (tag_data->state)
+ {
+ case GTK_STATE_SELECTED:
+ attributes = pango_attr_list_copy (popup->combo_entry->selected_item_attr);
+ break;
+
+ case GTK_STATE_INSENSITIVE:
+ attributes = pango_attr_list_copy (popup->combo_entry->insensitive_item_attr);
+ break;
+
+ default:
+ attributes = pango_attr_list_copy (popup->combo_entry->normal_item_attr);
+ break;
+ }
+
+ if (tag_data == popup->prelight &&
+ tag_data->state != GTK_STATE_INSENSITIVE)
+ {
+ attribute = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+ pango_attr_list_insert (attributes, attribute);
+ }
+
+ pango_layout_set_attributes (popup->layout, attributes);
+ pango_attr_list_unref (attributes);
+
+ if (tag_data->state == GTK_STATE_SELECTED)
+ {
+ gdk_cairo_set_source_color (cr,
+ &popup->combo_entry->selected_item_color);
+
+ cairo_rectangle (cr,
+ tag_data->bounds.x - 1,
+ tag_data->bounds.y - popup->scroll_y,
+ tag_data->bounds.width + 2,
+ tag_data->bounds.height);
+ cairo_fill (cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+
+ cairo_move_to (cr,
+ tag_data->bounds.x,
+ tag_data->bounds.y - popup->scroll_y - 1);
+ cairo_line_to (cr,
+ tag_data->bounds.x + tag_data->bounds.width - 1,
+ tag_data->bounds.y - popup->scroll_y - 1);
+
+ cairo_move_to (cr,
+ tag_data->bounds.x,
+ tag_data->bounds.y - popup->scroll_y + tag_data->bounds.height);
+ cairo_line_to (cr,
+ tag_data->bounds.x + tag_data->bounds.width - 1,
+ tag_data->bounds.y - popup->scroll_y + tag_data->bounds.height);
+
+ cairo_stroke (cr);
+
+ cairo_translate (cr, -0.5, -0.5);
+ }
+
+ cairo_move_to (cr,
+ (tag_data->bounds.x +
+ GIMP_TAG_POPUP_PADDING),
+ (tag_data->bounds.y -
+ popup->scroll_y +
+ GIMP_TAG_POPUP_PADDING));
+
+ pango_cairo_show_layout (cr, popup->layout);
+
+ if (tag_data == popup->prelight &&
+ tag_data->state != GTK_STATE_INSENSITIVE &&
+ ! popup->single_select_disabled)
+ {
+ gtk_paint_focus (style, window,
+ tag_data->state,
+ &event->area, widget, NULL,
+ tag_data->bounds.x,
+ tag_data->bounds.y - popup->scroll_y,
+ tag_data->bounds.width,
+ tag_data->bounds.height);
+ }
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static gboolean
+gimp_tag_popup_list_event (GtkWidget *widget,
+ GdkEvent *event,
+ GimpTagPopup *popup)
+{
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ GdkEventButton *button_event = (GdkEventButton *) event;
+ gint x;
+ gint y;
+ gint i;
+
+ popup->single_select_disabled = TRUE;
+
+ x = button_event->x;
+ y = button_event->y + popup->scroll_y;
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+
+ if (gimp_tag_popup_is_in_tag (tag_data, x, y))
+ {
+ gimp_tag_popup_toggle_tag (popup, tag_data);
+ gtk_widget_queue_draw (widget);
+ break;
+ }
+ }
+ }
+ else if (event->type == GDK_MOTION_NOTIFY)
+ {
+ GdkEventMotion *motion_event = (GdkEventMotion *) event;
+ PopupTagData *prelight = NULL;
+ gint x;
+ gint y;
+ gint i;
+
+ x = motion_event->x;
+ y = motion_event->y + popup->scroll_y;
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+
+ if (gimp_tag_popup_is_in_tag (tag_data, x, y))
+ {
+ prelight = tag_data;
+ break;
+ }
+ }
+
+ if (prelight != popup->prelight)
+ {
+ if (popup->prelight)
+ gimp_tag_popup_queue_draw_tag (popup, popup->prelight);
+
+ popup->prelight = prelight;
+
+ if (popup->prelight)
+ gimp_tag_popup_queue_draw_tag (popup, popup->prelight);
+ }
+ }
+ else if (event->type == GDK_BUTTON_RELEASE &&
+ ! popup->single_select_disabled)
+ {
+ GdkEventButton *button_event = (GdkEventButton *) event;
+ gint x;
+ gint y;
+ gint i;
+
+ popup->single_select_disabled = TRUE;
+
+ x = button_event->x;
+ y = button_event->y + popup->scroll_y;
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ PopupTagData *tag_data = &popup->tag_data[i];
+
+ if (gimp_tag_popup_is_in_tag (tag_data, x, y))
+ {
+ gimp_tag_popup_toggle_tag (popup, tag_data);
+ gtk_widget_destroy (GTK_WIDGET (popup));
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_tag_popup_is_in_tag (PopupTagData *tag_data,
+ gint x,
+ gint y)
+{
+ if (x >= tag_data->bounds.x &&
+ y >= tag_data->bounds.y &&
+ x < tag_data->bounds.x + tag_data->bounds.width &&
+ y < tag_data->bounds.y + tag_data->bounds.height)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_tag_popup_queue_draw_tag (GimpTagPopup *popup,
+ PopupTagData *tag_data)
+{
+ gtk_widget_queue_draw_area (popup->tag_area,
+ tag_data->bounds.x,
+ tag_data->bounds.y - popup->scroll_y,
+ tag_data->bounds.width,
+ tag_data->bounds.height);
+}
+
+static void
+gimp_tag_popup_toggle_tag (GimpTagPopup *popup,
+ PopupTagData *tag_data)
+{
+ gchar **current_tags;
+ GString *tag_str;
+ gint length;
+ gint i;
+ gboolean tag_toggled_off = FALSE;
+
+ if (tag_data->state == GTK_STATE_NORMAL)
+ {
+ tag_data->state = GTK_STATE_SELECTED;
+ }
+ else if (tag_data->state == GTK_STATE_SELECTED)
+ {
+ tag_data->state = GTK_STATE_NORMAL;
+ }
+ else
+ {
+ return;
+ }
+
+ current_tags = gimp_tag_entry_parse_tags (GIMP_TAG_ENTRY (popup->combo_entry));
+ tag_str = g_string_new ("");
+ length = g_strv_length (current_tags);
+ for (i = 0; i < length; i++)
+ {
+ if (! gimp_tag_compare_with_string (tag_data->tag, current_tags[i]))
+ {
+ tag_toggled_off = TRUE;
+ }
+ else
+ {
+ if (tag_str->len)
+ {
+ g_string_append (tag_str, gimp_tag_entry_get_separator ());
+ g_string_append_c (tag_str, ' ');
+ }
+
+ g_string_append (tag_str, current_tags[i]);
+ }
+ }
+
+ if (! tag_toggled_off)
+ {
+ /* this tag was not selected yet, so it needs to be toggled on */
+
+ if (tag_str->len)
+ {
+ g_string_append (tag_str, gimp_tag_entry_get_separator ());
+ g_string_append_c (tag_str, ' ');
+ }
+
+ g_string_append (tag_str, gimp_tag_get_name (tag_data->tag));
+ }
+
+ gimp_tag_entry_set_tag_string (GIMP_TAG_ENTRY (popup->combo_entry),
+ tag_str->str);
+
+ g_string_free (tag_str, TRUE);
+ g_strfreev (current_tags);
+
+ if (GIMP_TAG_ENTRY (popup->combo_entry)->mode == GIMP_TAG_ENTRY_MODE_QUERY)
+ {
+ GimpTaggedContainer *container;
+
+ container = GIMP_TAG_ENTRY (popup->combo_entry)->container;
+
+ for (i = 0; i < popup->tag_count; i++)
+ {
+ if (popup->tag_data[i].state != GTK_STATE_SELECTED)
+ {
+ popup->tag_data[i].state = GTK_STATE_INSENSITIVE;
+ }
+ }
+
+ gimp_container_foreach (GIMP_CONTAINER (container),
+ (GFunc) gimp_tag_popup_check_can_toggle,
+ popup);
+ }
+}
+
+static int
+gimp_tag_popup_data_compare (const void *a,
+ const void *b)
+{
+ return gimp_tag_compare_func (((PopupTagData *) a)->tag,
+ ((PopupTagData *) b)->tag);
+}
+
+static void
+gimp_tag_popup_check_can_toggle (GimpTagged *tagged,
+ GimpTagPopup *popup)
+{
+ GList *iterator;
+
+ for (iterator = gimp_tagged_get_tags (tagged);
+ iterator;
+ iterator = g_list_next (iterator))
+ {
+ PopupTagData search_key;
+ PopupTagData *search_result;
+
+ search_key.tag = iterator->data;
+
+ search_result =
+ (PopupTagData *) bsearch (&search_key,
+ popup->tag_data, popup->tag_count,
+ sizeof (PopupTagData),
+ gimp_tag_popup_data_compare);
+
+ if (search_result)
+ {
+ if (search_result->state == GTK_STATE_INSENSITIVE)
+ {
+ search_result->state = GTK_STATE_NORMAL;
+ }
+ }
+ }
+}
+
+static gboolean
+gimp_tag_popup_scroll_timeout (gpointer data)
+{
+ GimpTagPopup *popup = data;
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ gimp_tag_popup_scroll_by (popup, popup->scroll_step);
+
+ return TRUE;
+}
+
+static void
+gimp_tag_popup_remove_scroll_timeout (GimpTagPopup *popup)
+{
+ if (popup->scroll_timeout_id)
+ {
+ g_source_remove (popup->scroll_timeout_id);
+ popup->scroll_timeout_id = 0;
+ }
+}
+
+static gboolean
+gimp_tag_popup_scroll_timeout_initial (gpointer data)
+{
+ GimpTagPopup *popup = data;
+ guint timeout;
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-timeout-repeat", &timeout,
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ gimp_tag_popup_scroll_by (popup, popup->scroll_step);
+
+ gimp_tag_popup_remove_scroll_timeout (popup);
+
+ popup->scroll_timeout_id =
+ gdk_threads_add_timeout (timeout,
+ gimp_tag_popup_scroll_timeout,
+ popup);
+
+ return FALSE;
+}
+
+static void
+gimp_tag_popup_start_scrolling (GimpTagPopup *popup)
+{
+ guint timeout;
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-timeout-repeat", &timeout,
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ gimp_tag_popup_scroll_by (popup, popup->scroll_step);
+
+ popup->scroll_timeout_id =
+ gdk_threads_add_timeout (timeout,
+ gimp_tag_popup_scroll_timeout_initial,
+ popup);
+}
+
+static void
+gimp_tag_popup_stop_scrolling (GimpTagPopup *popup)
+{
+ gboolean touchscreen_mode;
+
+ gimp_tag_popup_remove_scroll_timeout (popup);
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ if (! touchscreen_mode)
+ {
+ popup->upper_arrow_prelight = FALSE;
+ popup->lower_arrow_prelight = FALSE;
+ }
+}
+
+static void
+gimp_tag_popup_scroll_by (GimpTagPopup *popup,
+ gint step)
+{
+ GtkStateType arrow_state;
+ gint new_scroll_y = popup->scroll_y + step;
+
+ arrow_state = popup->upper_arrow_state;
+
+ if (new_scroll_y < 0)
+ {
+ new_scroll_y = 0;
+
+ if (arrow_state != GTK_STATE_INSENSITIVE)
+ gimp_tag_popup_stop_scrolling (popup);
+
+ arrow_state = GTK_STATE_INSENSITIVE;
+ }
+ else
+ {
+ arrow_state = (popup->upper_arrow_prelight ?
+ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+ }
+
+ if (arrow_state != popup->upper_arrow_state)
+ {
+ popup->upper_arrow_state = arrow_state;
+ gtk_widget_queue_draw (GTK_WIDGET (popup));
+ }
+
+ arrow_state = popup->lower_arrow_state;
+
+ if (new_scroll_y >= popup->scroll_height)
+ {
+ new_scroll_y = popup->scroll_height - 1;
+
+ if (arrow_state != GTK_STATE_INSENSITIVE)
+ gimp_tag_popup_stop_scrolling (popup);
+
+ arrow_state = GTK_STATE_INSENSITIVE;
+ }
+ else
+ {
+ arrow_state = (popup->lower_arrow_prelight ?
+ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+ }
+
+ if (arrow_state != popup->lower_arrow_state)
+ {
+ popup->lower_arrow_state = arrow_state;
+ gtk_widget_queue_draw (GTK_WIDGET (popup));
+ }
+
+ if (new_scroll_y != popup->scroll_y)
+ {
+ popup->scroll_y = new_scroll_y;
+
+ gdk_window_scroll (gtk_widget_get_window (popup->tag_area), 0, -step);
+ }
+}
+
+static void
+gimp_tag_popup_handle_scrolling (GimpTagPopup *popup,
+ gint x,
+ gint y,
+ gboolean enter,
+ gboolean motion)
+{
+ GdkRectangle rect;
+ gboolean in_arrow;
+ gboolean scroll_fast = FALSE;
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ /* upper arrow handling */
+
+ get_arrows_sensitive_area (popup, &rect, NULL);
+
+ in_arrow = FALSE;
+ if (popup->arrows_visible &&
+ x >= rect.x &&
+ x < rect.x + rect.width &&
+ y >= rect.y &&
+ y < rect.y + rect.height)
+ {
+ in_arrow = TRUE;
+ }
+
+ if (touchscreen_mode)
+ popup->upper_arrow_prelight = in_arrow;
+
+ if (popup->upper_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ gboolean arrow_pressed = FALSE;
+
+ if (popup->arrows_visible)
+ {
+ if (touchscreen_mode)
+ {
+ if (enter && popup->upper_arrow_prelight)
+ {
+ if (popup->scroll_timeout_id == 0)
+ {
+ gimp_tag_popup_remove_scroll_timeout (popup);
+ popup->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
+
+ if (! motion)
+ {
+ /* Only do stuff on click. */
+ gimp_tag_popup_start_scrolling (popup);
+ arrow_pressed = TRUE;
+ }
+ }
+ else
+ {
+ arrow_pressed = TRUE;
+ }
+ }
+ else if (! enter)
+ {
+ gimp_tag_popup_stop_scrolling (popup);
+ }
+ }
+ else /* !touchscreen_mode */
+ {
+ scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
+
+ if (enter && in_arrow &&
+ (! popup->upper_arrow_prelight ||
+ popup->scroll_fast != scroll_fast))
+ {
+ popup->upper_arrow_prelight = TRUE;
+ popup->scroll_fast = scroll_fast;
+
+ gimp_tag_popup_remove_scroll_timeout (popup);
+ popup->scroll_step = (scroll_fast ?
+ -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1);
+
+ popup->scroll_timeout_id =
+ gdk_threads_add_timeout (scroll_fast ?
+ MENU_SCROLL_TIMEOUT2 :
+ MENU_SCROLL_TIMEOUT1,
+ gimp_tag_popup_scroll_timeout,
+ popup);
+ }
+ else if (! enter && ! in_arrow && popup->upper_arrow_prelight)
+ {
+ gimp_tag_popup_stop_scrolling (popup);
+ }
+ }
+ }
+
+ /* gimp_tag_popup_start_scrolling() might have hit the top of the
+ * tag_popup, so check if the button isn't insensitive before
+ * changing it to something else.
+ */
+ if (popup->upper_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ GtkStateType arrow_state = GTK_STATE_NORMAL;
+
+ if (arrow_pressed)
+ arrow_state = GTK_STATE_ACTIVE;
+ else if (popup->upper_arrow_prelight)
+ arrow_state = GTK_STATE_PRELIGHT;
+
+ if (arrow_state != popup->upper_arrow_state)
+ {
+ popup->upper_arrow_state = arrow_state;
+
+ gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (popup)),
+ &rect, FALSE);
+ }
+ }
+ }
+
+ /* lower arrow handling */
+
+ get_arrows_sensitive_area (popup, NULL, &rect);
+
+ in_arrow = FALSE;
+ if (popup->arrows_visible &&
+ x >= rect.x &&
+ x < rect.x + rect.width &&
+ y >= rect.y &&
+ y < rect.y + rect.height)
+ {
+ in_arrow = TRUE;
+ }
+
+ if (touchscreen_mode)
+ popup->lower_arrow_prelight = in_arrow;
+
+ if (popup->lower_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ gboolean arrow_pressed = FALSE;
+
+ if (popup->arrows_visible)
+ {
+ if (touchscreen_mode)
+ {
+ if (enter && popup->lower_arrow_prelight)
+ {
+ if (popup->scroll_timeout_id == 0)
+ {
+ gimp_tag_popup_remove_scroll_timeout (popup);
+ popup->scroll_step = MENU_SCROLL_STEP2; /* always fast */
+
+ if (! motion)
+ {
+ /* Only do stuff on click. */
+ gimp_tag_popup_start_scrolling (popup);
+ arrow_pressed = TRUE;
+ }
+ }
+ else
+ {
+ arrow_pressed = TRUE;
+ }
+ }
+ else if (! enter)
+ {
+ gimp_tag_popup_stop_scrolling (popup);
+ }
+ }
+ else /* !touchscreen_mode */
+ {
+ scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
+
+ if (enter && in_arrow &&
+ (! popup->lower_arrow_prelight ||
+ popup->scroll_fast != scroll_fast))
+ {
+ popup->lower_arrow_prelight = TRUE;
+ popup->scroll_fast = scroll_fast;
+
+ gimp_tag_popup_remove_scroll_timeout (popup);
+ popup->scroll_step = (scroll_fast ?
+ MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1);
+
+ popup->scroll_timeout_id =
+ gdk_threads_add_timeout (scroll_fast ?
+ MENU_SCROLL_TIMEOUT2 :
+ MENU_SCROLL_TIMEOUT1,
+ gimp_tag_popup_scroll_timeout,
+ popup);
+ }
+ else if (! enter && ! in_arrow && popup->lower_arrow_prelight)
+ {
+ gimp_tag_popup_stop_scrolling (popup);
+ }
+ }
+ }
+
+ /* gimp_tag_popup_start_scrolling() might have hit the bottom of the
+ * popup, so check if the button isn't insensitive before
+ * changing it to something else.
+ */
+ if (popup->lower_arrow_state != GTK_STATE_INSENSITIVE)
+ {
+ GtkStateType arrow_state = GTK_STATE_NORMAL;
+
+ if (arrow_pressed)
+ arrow_state = GTK_STATE_ACTIVE;
+ else if (popup->lower_arrow_prelight)
+ arrow_state = GTK_STATE_PRELIGHT;
+
+ if (arrow_state != popup->lower_arrow_state)
+ {
+ popup->lower_arrow_state = arrow_state;
+
+ gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (popup)),
+ &rect, FALSE);
+ }
+ }
+ }
+}
+
+static gboolean
+gimp_tag_popup_button_scroll (GimpTagPopup *popup,
+ GdkEventButton *event)
+{
+ if (popup->upper_arrow_prelight || popup->lower_arrow_prelight)
+ {
+ gboolean touchscreen_mode;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
+ "gtk-touchscreen-mode", &touchscreen_mode,
+ NULL);
+
+ if (touchscreen_mode)
+ gimp_tag_popup_handle_scrolling (popup,
+ event->x_root,
+ event->y_root,
+ event->type == GDK_BUTTON_PRESS,
+ FALSE);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+get_arrows_visible_area (GimpTagPopup *popup,
+ GdkRectangle *border,
+ GdkRectangle *upper,
+ GdkRectangle *lower,
+ gint *arrow_space)
+{
+ GtkWidget *widget = GTK_WIDGET (popup->alignment);
+ guint padding_top;
+ guint padding_bottom;
+ guint padding_left;
+ guint padding_right;
+
+ gtk_alignment_get_padding (GTK_ALIGNMENT (popup->alignment),
+ &padding_top, &padding_bottom,
+ &padding_left, &padding_right);
+
+ gtk_widget_get_allocation (widget, border);
+
+ upper->x = border->x + padding_left;
+ upper->y = border->y;
+ upper->width = border->width - padding_left - padding_right;
+ upper->height = padding_top;
+
+ lower->x = border->x + padding_left;
+ lower->y = border->y + border->height - padding_bottom;
+ lower->width = border->width - padding_left - padding_right;
+ lower->height = padding_bottom;
+
+ *arrow_space = popup->scroll_arrow_height;
+}
+
+static void
+get_arrows_sensitive_area (GimpTagPopup *popup,
+ GdkRectangle *upper,
+ GdkRectangle *lower)
+{
+ GdkRectangle tmp_border;
+ GdkRectangle tmp_upper;
+ GdkRectangle tmp_lower;
+ gint tmp_arrow_space;
+
+ get_arrows_visible_area (popup,
+ &tmp_border, &tmp_upper, &tmp_lower, &tmp_arrow_space);
+
+ if (upper)
+ *upper = tmp_upper;
+
+ if (lower)
+ *lower = tmp_lower;
+}