1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimptagentry.c
5 * Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28
29 #include "widgets-types.h"
30
31 #include "core/gimpcontainer.h"
32 #include "core/gimpcontext.h"
33 #include "core/gimptag.h"
34 #include "core/gimptagged.h"
35 #include "core/gimptaggedcontainer.h"
36 #include "core/gimpviewable.h"
37
38 #include "gimpcombotagentry.h"
39 #include "gimptagentry.h"
40 #include "gimptagpopup.h"
41
42 #include "gimp-intl.h"
43
44
45 #define MENU_SCROLL_STEP1 8
46 #define MENU_SCROLL_STEP2 15
47 #define MENU_SCROLL_FAST_ZONE 8
48 #define MENU_SCROLL_TIMEOUT1 50
49 #define MENU_SCROLL_TIMEOUT2 20
50
51 #define GIMP_TAG_POPUP_MARGIN 5
52 #define GIMP_TAG_POPUP_PADDING 2
53 #define GIMP_TAG_POPUP_LINE_SPACING 2
54
55 enum
56 {
57 PROP_0,
58 PROP_OWNER
59 };
60
61 struct _PopupTagData
62 {
63 GimpTag *tag;
64 GdkRectangle bounds;
65 GtkStateType state;
66 };
67
68
69 static void gimp_tag_popup_constructed (GObject *object);
70 static void gimp_tag_popup_dispose (GObject *object);
71 static void gimp_tag_popup_set_property (GObject *object,
72 guint property_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void gimp_tag_popup_get_property (GObject *object,
76 guint property_id,
77 GValue *value,
78 GParamSpec *pspec);
79
80 static gboolean gimp_tag_popup_border_expose (GtkWidget *widget,
81 GdkEventExpose *event,
82 GimpTagPopup *popup);
83 static gboolean gimp_tag_popup_list_expose (GtkWidget *widget,
84 GdkEventExpose *event,
85 GimpTagPopup *popup);
86 static gboolean gimp_tag_popup_border_event (GtkWidget *widget,
87 GdkEvent *event);
88 static gboolean gimp_tag_popup_list_event (GtkWidget *widget,
89 GdkEvent *event,
90 GimpTagPopup *popup);
91 static gboolean gimp_tag_popup_is_in_tag (PopupTagData *tag_data,
92 gint x,
93 gint y);
94 static void gimp_tag_popup_queue_draw_tag (GimpTagPopup *widget,
95 PopupTagData *tag_data);
96 static void gimp_tag_popup_toggle_tag (GimpTagPopup *popup,
97 PopupTagData *tag_data);
98 static void gimp_tag_popup_check_can_toggle (GimpTagged *tagged,
99 GimpTagPopup *popup);
100 static gint gimp_tag_popup_layout_tags (GimpTagPopup *popup,
101 gint width);
102 static gboolean gimp_tag_popup_scroll_timeout (gpointer data);
103 static void gimp_tag_popup_remove_scroll_timeout (GimpTagPopup *popup);
104 static gboolean gimp_tag_popup_scroll_timeout_initial (gpointer data);
105 static void gimp_tag_popup_start_scrolling (GimpTagPopup *popup);
106 static void gimp_tag_popup_stop_scrolling (GimpTagPopup *popup);
107 static void gimp_tag_popup_scroll_by (GimpTagPopup *popup,
108 gint step);
109 static void gimp_tag_popup_handle_scrolling (GimpTagPopup *popup,
110 gint x,
111 gint y,
112 gboolean enter,
113 gboolean motion);
114
115 static gboolean gimp_tag_popup_button_scroll (GimpTagPopup *popup,
116 GdkEventButton *event);
117
118 static void get_arrows_visible_area (GimpTagPopup *combo_entry,
119 GdkRectangle *border,
120 GdkRectangle *upper,
121 GdkRectangle *lower,
122 gint *arrow_space);
123 static void get_arrows_sensitive_area (GimpTagPopup *popup,
124 GdkRectangle *upper,
125 GdkRectangle *lower);
126
127
128 G_DEFINE_TYPE (GimpTagPopup, gimp_tag_popup, GTK_TYPE_WINDOW);
129
130 #define parent_class gimp_tag_popup_parent_class
131
132
133 static void
gimp_tag_popup_class_init(GimpTagPopupClass * klass)134 gimp_tag_popup_class_init (GimpTagPopupClass *klass)
135 {
136 GObjectClass *object_class = G_OBJECT_CLASS (klass);
137
138 object_class->constructed = gimp_tag_popup_constructed;
139 object_class->dispose = gimp_tag_popup_dispose;
140 object_class->set_property = gimp_tag_popup_set_property;
141 object_class->get_property = gimp_tag_popup_get_property;
142
143 g_object_class_install_property (object_class, PROP_OWNER,
144 g_param_spec_object ("owner", NULL, NULL,
145 GIMP_TYPE_COMBO_TAG_ENTRY,
146 GIMP_PARAM_READWRITE |
147 G_PARAM_CONSTRUCT_ONLY));
148 }
149
150 static void
gimp_tag_popup_init(GimpTagPopup * popup)151 gimp_tag_popup_init (GimpTagPopup *popup)
152 {
153 GtkWidget *widget = GTK_WIDGET (popup);
154
155 popup->upper_arrow_state = GTK_STATE_NORMAL;
156 popup->lower_arrow_state = GTK_STATE_NORMAL;
157
158 gtk_widget_add_events (widget,
159 GDK_BUTTON_PRESS_MASK |
160 GDK_BUTTON_RELEASE_MASK |
161 GDK_POINTER_MOTION_MASK |
162 GDK_KEY_RELEASE_MASK |
163 GDK_SCROLL_MASK);
164
165 popup->frame = gtk_frame_new (NULL);
166 gtk_frame_set_shadow_type (GTK_FRAME (popup->frame), GTK_SHADOW_OUT);
167 gtk_container_add (GTK_CONTAINER (popup), popup->frame);
168 gtk_widget_show (popup->frame);
169
170 popup->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
171 gtk_container_add (GTK_CONTAINER (popup->frame), popup->alignment);
172 gtk_widget_show (popup->alignment);
173
174 popup->tag_area = gtk_drawing_area_new ();
175 gtk_widget_add_events (popup->tag_area,
176 GDK_BUTTON_PRESS_MASK |
177 GDK_BUTTON_RELEASE_MASK |
178 GDK_POINTER_MOTION_MASK);
179 gtk_container_add (GTK_CONTAINER (popup->alignment), popup->tag_area);
180 gtk_widget_show (popup->tag_area);
181
182 g_signal_connect (popup->alignment, "expose-event",
183 G_CALLBACK (gimp_tag_popup_border_expose),
184 popup);
185 g_signal_connect (popup, "event",
186 G_CALLBACK (gimp_tag_popup_border_event),
187 NULL);
188 g_signal_connect (popup->tag_area, "expose-event",
189 G_CALLBACK (gimp_tag_popup_list_expose),
190 popup);
191 g_signal_connect (popup->tag_area, "event",
192 G_CALLBACK (gimp_tag_popup_list_event),
193 popup);
194 }
195
196 static void
gimp_tag_popup_constructed(GObject * object)197 gimp_tag_popup_constructed (GObject *object)
198 {
199 GimpTagPopup *popup = GIMP_TAG_POPUP (object);
200 GimpTaggedContainer *container;
201 GtkWidget *entry;
202 GtkAllocation entry_allocation;
203 GtkStyle *frame_style;
204 gint x;
205 gint y;
206 gint width;
207 gint height;
208 gint popup_height;
209 GHashTable *tag_hash;
210 GList *tag_list;
211 GList *tag_iterator;
212 gint i;
213 gint max_height;
214 gint screen_height;
215 gchar **current_tags;
216 gint current_count;
217 GdkRectangle popup_rects[2]; /* variants of popup placement */
218 GdkRectangle popup_rect; /* best popup rect in screen coordinates */
219
220 G_OBJECT_CLASS (parent_class)->constructed (object);
221
222 entry = GTK_WIDGET (popup->combo_entry);
223
224 gtk_window_set_screen (GTK_WINDOW (popup), gtk_widget_get_screen (entry));
225
226 popup->context = gtk_widget_create_pango_context (GTK_WIDGET (popup));
227 popup->layout = pango_layout_new (popup->context);
228
229 gtk_widget_get_allocation (entry, &entry_allocation);
230
231 gtk_widget_style_get (GTK_WIDGET (popup),
232 "scroll-arrow-vlength", &popup->scroll_arrow_height,
233 NULL);
234
235 pango_layout_set_attributes (popup->layout,
236 popup->combo_entry->normal_item_attr);
237
238 current_tags = gimp_tag_entry_parse_tags (GIMP_TAG_ENTRY (popup->combo_entry));
239 current_count = g_strv_length (current_tags);
240
241 container = GIMP_TAG_ENTRY (popup->combo_entry)->container;
242
243 tag_hash = container->tag_ref_counts;
244 tag_list = g_hash_table_get_keys (tag_hash);
245 tag_list = g_list_sort (tag_list, gimp_tag_compare_func);
246
247 popup->tag_count = g_list_length (tag_list);
248 popup->tag_data = g_new0 (PopupTagData, popup->tag_count);
249
250 for (i = 0, tag_iterator = tag_list;
251 i < popup->tag_count;
252 i++, tag_iterator = g_list_next (tag_iterator))
253 {
254 PopupTagData *tag_data = &popup->tag_data[i];
255 gint j;
256
257 tag_data->tag = tag_iterator->data;
258 tag_data->state = GTK_STATE_NORMAL;
259
260 g_object_ref (tag_data->tag);
261
262 for (j = 0; j < current_count; j++)
263 {
264 if (! gimp_tag_compare_with_string (tag_data->tag, current_tags[j]))
265 {
266 tag_data->state = GTK_STATE_SELECTED;
267 break;
268 }
269 }
270 }
271
272 g_list_free (tag_list);
273 g_strfreev (current_tags);
274
275 if (GIMP_TAG_ENTRY (popup->combo_entry)->mode == GIMP_TAG_ENTRY_MODE_QUERY)
276 {
277 for (i = 0; i < popup->tag_count; i++)
278 {
279 if (popup->tag_data[i].state != GTK_STATE_SELECTED)
280 {
281 popup->tag_data[i].state = GTK_STATE_INSENSITIVE;
282 }
283 }
284
285 gimp_container_foreach (GIMP_CONTAINER (container),
286 (GFunc) gimp_tag_popup_check_can_toggle,
287 popup);
288 }
289
290 frame_style = gtk_widget_get_style (popup->frame);
291
292 width = (entry_allocation.width -
293 2 * frame_style->xthickness);
294 height = (gimp_tag_popup_layout_tags (popup, width) +
295 2 * frame_style->ythickness);
296
297 gdk_window_get_origin (gtk_widget_get_window (entry), &x, &y);
298
299 max_height = entry_allocation.height * 10;
300
301 screen_height = gdk_screen_get_height (gtk_widget_get_screen (entry));
302
303 popup_height = MIN (height, max_height);
304
305 popup_rects[0].x = x;
306 popup_rects[0].y = 0;
307 popup_rects[0].width = entry_allocation.width;
308 popup_rects[0].height = y + entry_allocation.height;
309
310 popup_rects[1].x = x;
311 popup_rects[1].y = y;
312 popup_rects[1].width = popup_rects[0].width;
313 popup_rects[1].height = screen_height - popup_rects[0].height;
314
315 if (popup_rects[0].height >= popup_height)
316 {
317 popup_rect = popup_rects[0];
318 popup_rect.y += popup_rects[0].height - popup_height;
319 popup_rect.height = popup_height;
320 }
321 else if (popup_rects[1].height >= popup_height)
322 {
323 popup_rect = popup_rects[1];
324 popup_rect.height = popup_height;
325 }
326 else
327 {
328 if (popup_rects[0].height >= popup_rects[1].height)
329 {
330 popup_rect = popup_rects[0];
331 popup_rect.y += popup->scroll_arrow_height + frame_style->ythickness;
332 }
333 else
334 {
335 popup_rect = popup_rects[1];
336 popup_rect.y -= popup->scroll_arrow_height + frame_style->ythickness;
337 }
338
339 popup_height = popup_rect.height;
340 }
341
342 if (popup_height < height)
343 {
344 popup->arrows_visible = TRUE;
345 popup->upper_arrow_state = GTK_STATE_INSENSITIVE;
346
347 gtk_alignment_set_padding (GTK_ALIGNMENT (popup->alignment),
348 popup->scroll_arrow_height + 2,
349 popup->scroll_arrow_height + 2, 0, 0);
350
351 popup_height -= 2 * popup->scroll_arrow_height + 4;
352
353 popup->scroll_height = height - popup_height;
354 popup->scroll_y = 0;
355 popup->scroll_step = 0;
356 }
357
358 gtk_widget_set_size_request (popup->tag_area, width, popup_height);
359
360 gtk_window_move (GTK_WINDOW (popup), popup_rect.x, popup_rect.y);
361 gtk_window_resize (GTK_WINDOW (popup), popup_rect.width, popup_rect.height);
362 }
363
364 static void
gimp_tag_popup_dispose(GObject * object)365 gimp_tag_popup_dispose (GObject *object)
366 {
367 GimpTagPopup *popup = GIMP_TAG_POPUP (object);
368
369 gimp_tag_popup_remove_scroll_timeout (popup);
370
371 g_clear_object (&popup->combo_entry);
372 g_clear_object (&popup->layout);
373 g_clear_object (&popup->context);
374
375 if (popup->tag_data)
376 {
377 gint i;
378
379 for (i = 0; i < popup->tag_count; i++)
380 {
381 g_object_unref (popup->tag_data[i].tag);
382 }
383
384 g_clear_pointer (&popup->tag_data, g_free);
385 }
386
387 G_OBJECT_CLASS (parent_class)->dispose (object);
388 }
389
390 static void
gimp_tag_popup_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)391 gimp_tag_popup_set_property (GObject *object,
392 guint property_id,
393 const GValue *value,
394 GParamSpec *pspec)
395 {
396 GimpTagPopup *popup = GIMP_TAG_POPUP (object);
397
398 switch (property_id)
399 {
400 case PROP_OWNER:
401 popup->combo_entry = g_value_dup_object (value);
402 break;
403
404 default:
405 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
406 break;
407 }
408 }
409
410 static void
gimp_tag_popup_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)411 gimp_tag_popup_get_property (GObject *object,
412 guint property_id,
413 GValue *value,
414 GParamSpec *pspec)
415 {
416 GimpTagPopup *popup = GIMP_TAG_POPUP (object);
417
418 switch (property_id)
419 {
420 case PROP_OWNER:
421 g_value_set_object (value, popup->combo_entry);
422 break;
423
424 default:
425 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
426 break;
427 }
428 }
429
430 /**
431 * gimp_tag_popup_new:
432 * @combo_entry: #GimpComboTagEntry which is owner of the popup window.
433 *
434 * Tag popup widget is only useful for for #GimpComboTagEntry and
435 * should not be used elsewhere.
436 *
437 * Return value: a newly created #GimpTagPopup widget.
438 **/
439 GtkWidget *
gimp_tag_popup_new(GimpComboTagEntry * combo_entry)440 gimp_tag_popup_new (GimpComboTagEntry *combo_entry)
441 {
442 g_return_val_if_fail (GIMP_IS_COMBO_TAG_ENTRY (combo_entry), NULL);
443
444 return g_object_new (GIMP_TYPE_TAG_POPUP,
445 "type", GTK_WINDOW_POPUP,
446 "owner", combo_entry,
447 NULL);
448 }
449
450 /**
451 * gimp_tag_popup_show:
452 * @tag_popup: an instance of #GimpTagPopup
453 *
454 * Show tag popup widget. If mouse grab cannot be obtained for widget,
455 * it is destroyed.
456 **/
457 void
gimp_tag_popup_show(GimpTagPopup * popup)458 gimp_tag_popup_show (GimpTagPopup *popup)
459 {
460 GtkWidget *widget;
461
462 g_return_if_fail (GIMP_IS_TAG_POPUP (popup));
463
464 widget = GTK_WIDGET (popup);
465
466 gtk_widget_show (widget);
467
468 gtk_grab_add (widget);
469 gtk_widget_grab_focus (widget);
470
471 if (gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
472 GDK_BUTTON_PRESS_MASK |
473 GDK_BUTTON_RELEASE_MASK |
474 GDK_POINTER_MOTION_MASK,
475 NULL, NULL,
476 GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
477 {
478 /* pointer grab must be attained otherwise user would have
479 * problems closing the popup window.
480 */
481 gtk_grab_remove (widget);
482 gtk_widget_destroy (widget);
483 }
484 }
485
486 static gint
gimp_tag_popup_layout_tags(GimpTagPopup * popup,gint width)487 gimp_tag_popup_layout_tags (GimpTagPopup *popup,
488 gint width)
489 {
490 PangoFontMetrics *font_metrics;
491 gint x;
492 gint y;
493 gint height = 0;
494 gint i;
495 gint line_height;
496 gint space_width;
497
498 x = GIMP_TAG_POPUP_MARGIN;
499 y = GIMP_TAG_POPUP_MARGIN;
500
501 font_metrics = pango_context_get_metrics (popup->context,
502 pango_context_get_font_description (popup->context),
503 NULL);
504
505 line_height = PANGO_PIXELS ((pango_font_metrics_get_ascent (font_metrics) +
506 pango_font_metrics_get_descent (font_metrics)));
507 space_width = PANGO_PIXELS (pango_font_metrics_get_approximate_char_width (font_metrics));
508
509 pango_font_metrics_unref (font_metrics);
510
511 for (i = 0; i < popup->tag_count; i++)
512 {
513 PopupTagData *tag_data = &popup->tag_data[i];
514 gint w, h;
515
516 pango_layout_set_text (popup->layout,
517 gimp_tag_get_name (tag_data->tag), -1);
518 pango_layout_get_pixel_size (popup->layout, &w, &h);
519
520 tag_data->bounds.width = w + 2 * GIMP_TAG_POPUP_PADDING;
521 tag_data->bounds.height = h + 2 * GIMP_TAG_POPUP_PADDING;
522
523 if (x + space_width + tag_data->bounds.width +
524 GIMP_TAG_POPUP_MARGIN - 1 > width)
525 {
526 x = GIMP_TAG_POPUP_MARGIN;
527 y += line_height + 2 * GIMP_TAG_POPUP_PADDING + GIMP_TAG_POPUP_LINE_SPACING;
528 }
529
530 tag_data->bounds.x = x;
531 tag_data->bounds.y = y;
532
533 x += tag_data->bounds.width + space_width;
534 }
535
536 if (gtk_widget_get_direction (GTK_WIDGET (popup)) == GTK_TEXT_DIR_RTL)
537 {
538 for (i = 0; i < popup->tag_count; i++)
539 {
540 PopupTagData *tag_data = &popup->tag_data[i];
541
542 tag_data->bounds.x = (width -
543 tag_data->bounds.x -
544 tag_data->bounds.width);
545 }
546 }
547
548 height = y + line_height + GIMP_TAG_POPUP_MARGIN;
549
550 return height;
551 }
552
553 static gboolean
gimp_tag_popup_border_expose(GtkWidget * widget,GdkEventExpose * event,GimpTagPopup * popup)554 gimp_tag_popup_border_expose (GtkWidget *widget,
555 GdkEventExpose *event,
556 GimpTagPopup *popup)
557 {
558 GdkWindow *window = gtk_widget_get_window (widget);
559 GtkStyle *style = gtk_widget_get_style (widget);
560 GdkRectangle border;
561 GdkRectangle upper;
562 GdkRectangle lower;
563 gint arrow_space;
564 gint arrow_size;
565
566 if (event->window != gtk_widget_get_window (widget))
567 return FALSE;
568
569 get_arrows_visible_area (popup, &border, &upper, &lower, &arrow_space);
570
571 arrow_size = 0.7 * arrow_space;
572
573 gtk_paint_box (style, window,
574 GTK_STATE_NORMAL,
575 GTK_SHADOW_OUT,
576 &event->area, widget, "menu",
577 0, 0, -1, -1);
578
579 if (popup->arrows_visible)
580 {
581 /* upper arrow */
582
583 gtk_paint_box (style, window,
584 popup->upper_arrow_state,
585 GTK_SHADOW_OUT,
586 &event->area, widget, "menu",
587 upper.x,
588 upper.y,
589 upper.width,
590 upper.height);
591
592 gtk_paint_arrow (style, window,
593 popup->upper_arrow_state,
594 GTK_SHADOW_OUT,
595 &event->area, widget, "menu_scroll_arrow_up",
596 GTK_ARROW_UP,
597 TRUE,
598 upper.x + (upper.width - arrow_size) / 2,
599 upper.y + style->ythickness + (arrow_space - arrow_size) / 2,
600 arrow_size, arrow_size);
601
602 /* lower arrow */
603
604 gtk_paint_box (style, window,
605 popup->lower_arrow_state,
606 GTK_SHADOW_OUT,
607 &event->area, widget, "menu",
608 lower.x,
609 lower.y,
610 lower.width,
611 lower.height);
612
613 gtk_paint_arrow (style, window,
614 popup->lower_arrow_state,
615 GTK_SHADOW_OUT,
616 &event->area, widget, "menu_scroll_arrow_down",
617 GTK_ARROW_DOWN,
618 TRUE,
619 lower.x + (lower.width - arrow_size) / 2,
620 lower.y + style->ythickness + (arrow_space - arrow_size) / 2,
621 arrow_size, arrow_size);
622 }
623
624 return FALSE;
625 }
626
627 static gboolean
gimp_tag_popup_border_event(GtkWidget * widget,GdkEvent * event)628 gimp_tag_popup_border_event (GtkWidget *widget,
629 GdkEvent *event)
630 {
631 GimpTagPopup *popup = GIMP_TAG_POPUP (widget);
632
633 if (event->type == GDK_BUTTON_PRESS)
634 {
635 GdkEventButton *button_event = (GdkEventButton *) event;
636 GtkAllocation allocation;
637 gint x;
638 gint y;
639
640 if (button_event->window == gtk_widget_get_window (widget) &&
641 gimp_tag_popup_button_scroll (popup, button_event))
642 {
643 return TRUE;
644 }
645
646 gtk_widget_get_allocation (widget, &allocation);
647
648 gdk_window_get_pointer (gtk_widget_get_window (widget), &x, &y, NULL);
649
650 if (button_event->window != gtk_widget_get_window (popup->tag_area) &&
651 (x < allocation.y ||
652 y < allocation.x ||
653 x > allocation.x + allocation.width ||
654 y > allocation.y + allocation.height))
655 {
656 /* user has clicked outside the popup area,
657 * which means it should be hidden.
658 */
659 gtk_grab_remove (widget);
660 gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
661 GDK_CURRENT_TIME);
662 gtk_widget_destroy (widget);
663 }
664 }
665 else if (event->type == GDK_MOTION_NOTIFY)
666 {
667 GdkEventMotion *motion_event = (GdkEventMotion *) event;
668 gint x, y;
669
670 gdk_window_get_pointer (gtk_widget_get_window (widget), &x, &y, NULL);
671
672 gimp_tag_popup_handle_scrolling (popup, x, y,
673 motion_event->window ==
674 gtk_widget_get_window (widget),
675 TRUE);
676 }
677 else if (event->type == GDK_BUTTON_RELEASE)
678 {
679 GdkEventButton *button_event = (GdkEventButton *) event;
680
681 popup->single_select_disabled = TRUE;
682
683 if (button_event->window == gtk_widget_get_window (widget) &&
684 gimp_tag_popup_button_scroll (popup, button_event))
685 {
686 return TRUE;
687 }
688 }
689 else if (event->type == GDK_GRAB_BROKEN)
690 {
691 gtk_grab_remove (widget);
692 gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
693 GDK_CURRENT_TIME);
694 gtk_widget_destroy (widget);
695 }
696 else if (event->type == GDK_KEY_PRESS)
697 {
698 gtk_grab_remove (widget);
699 gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
700 GDK_CURRENT_TIME);
701 gtk_widget_destroy (widget);
702 }
703 else if (event->type == GDK_SCROLL)
704 {
705 GdkEventScroll *scroll_event = (GdkEventScroll *) event;
706
707 switch (scroll_event->direction)
708 {
709 case GDK_SCROLL_RIGHT:
710 case GDK_SCROLL_DOWN:
711 gimp_tag_popup_scroll_by (popup, MENU_SCROLL_STEP2);
712 return TRUE;
713
714 case GDK_SCROLL_LEFT:
715 case GDK_SCROLL_UP:
716 gimp_tag_popup_scroll_by (popup, - MENU_SCROLL_STEP2);
717 return TRUE;
718 }
719 }
720
721 return FALSE;
722 }
723
724 static gboolean
gimp_tag_popup_list_expose(GtkWidget * widget,GdkEventExpose * event,GimpTagPopup * popup)725 gimp_tag_popup_list_expose (GtkWidget *widget,
726 GdkEventExpose *event,
727 GimpTagPopup *popup)
728 {
729 GdkWindow *window = gtk_widget_get_window (widget);
730 GtkStyle *style = gtk_widget_get_style (widget);
731 cairo_t *cr;
732 PangoAttribute *attribute;
733 PangoAttrList *attributes;
734 gint i;
735
736 cr = gdk_cairo_create (event->window);
737
738 gdk_cairo_region (cr, event->region);
739 cairo_clip (cr);
740
741 cairo_set_line_width (cr, 1.0);
742 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
743
744 for (i = 0; i < popup->tag_count; i++)
745 {
746 PopupTagData *tag_data = &popup->tag_data[i];
747
748 pango_layout_set_text (popup->layout,
749 gimp_tag_get_name (tag_data->tag), -1);
750
751 switch (tag_data->state)
752 {
753 case GTK_STATE_SELECTED:
754 attributes = pango_attr_list_copy (popup->combo_entry->selected_item_attr);
755 break;
756
757 case GTK_STATE_INSENSITIVE:
758 attributes = pango_attr_list_copy (popup->combo_entry->insensitive_item_attr);
759 break;
760
761 default:
762 attributes = pango_attr_list_copy (popup->combo_entry->normal_item_attr);
763 break;
764 }
765
766 if (tag_data == popup->prelight &&
767 tag_data->state != GTK_STATE_INSENSITIVE)
768 {
769 attribute = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
770 pango_attr_list_insert (attributes, attribute);
771 }
772
773 pango_layout_set_attributes (popup->layout, attributes);
774 pango_attr_list_unref (attributes);
775
776 if (tag_data->state == GTK_STATE_SELECTED)
777 {
778 gdk_cairo_set_source_color (cr,
779 &popup->combo_entry->selected_item_color);
780
781 cairo_rectangle (cr,
782 tag_data->bounds.x - 1,
783 tag_data->bounds.y - popup->scroll_y,
784 tag_data->bounds.width + 2,
785 tag_data->bounds.height);
786 cairo_fill (cr);
787
788 cairo_translate (cr, 0.5, 0.5);
789
790 cairo_move_to (cr,
791 tag_data->bounds.x,
792 tag_data->bounds.y - popup->scroll_y - 1);
793 cairo_line_to (cr,
794 tag_data->bounds.x + tag_data->bounds.width - 1,
795 tag_data->bounds.y - popup->scroll_y - 1);
796
797 cairo_move_to (cr,
798 tag_data->bounds.x,
799 tag_data->bounds.y - popup->scroll_y + tag_data->bounds.height);
800 cairo_line_to (cr,
801 tag_data->bounds.x + tag_data->bounds.width - 1,
802 tag_data->bounds.y - popup->scroll_y + tag_data->bounds.height);
803
804 cairo_stroke (cr);
805
806 cairo_translate (cr, -0.5, -0.5);
807 }
808
809 cairo_move_to (cr,
810 (tag_data->bounds.x +
811 GIMP_TAG_POPUP_PADDING),
812 (tag_data->bounds.y -
813 popup->scroll_y +
814 GIMP_TAG_POPUP_PADDING));
815
816 pango_cairo_show_layout (cr, popup->layout);
817
818 if (tag_data == popup->prelight &&
819 tag_data->state != GTK_STATE_INSENSITIVE &&
820 ! popup->single_select_disabled)
821 {
822 gtk_paint_focus (style, window,
823 tag_data->state,
824 &event->area, widget, NULL,
825 tag_data->bounds.x,
826 tag_data->bounds.y - popup->scroll_y,
827 tag_data->bounds.width,
828 tag_data->bounds.height);
829 }
830 }
831
832 cairo_destroy (cr);
833
834 return FALSE;
835 }
836
837 static gboolean
gimp_tag_popup_list_event(GtkWidget * widget,GdkEvent * event,GimpTagPopup * popup)838 gimp_tag_popup_list_event (GtkWidget *widget,
839 GdkEvent *event,
840 GimpTagPopup *popup)
841 {
842 if (event->type == GDK_BUTTON_PRESS)
843 {
844 GdkEventButton *button_event = (GdkEventButton *) event;
845 gint x;
846 gint y;
847 gint i;
848
849 popup->single_select_disabled = TRUE;
850
851 x = button_event->x;
852 y = button_event->y + popup->scroll_y;
853
854 for (i = 0; i < popup->tag_count; i++)
855 {
856 PopupTagData *tag_data = &popup->tag_data[i];
857
858 if (gimp_tag_popup_is_in_tag (tag_data, x, y))
859 {
860 gimp_tag_popup_toggle_tag (popup, tag_data);
861 gtk_widget_queue_draw (widget);
862 break;
863 }
864 }
865 }
866 else if (event->type == GDK_MOTION_NOTIFY)
867 {
868 GdkEventMotion *motion_event = (GdkEventMotion *) event;
869 PopupTagData *prelight = NULL;
870 gint x;
871 gint y;
872 gint i;
873
874 x = motion_event->x;
875 y = motion_event->y + popup->scroll_y;
876
877 for (i = 0; i < popup->tag_count; i++)
878 {
879 PopupTagData *tag_data = &popup->tag_data[i];
880
881 if (gimp_tag_popup_is_in_tag (tag_data, x, y))
882 {
883 prelight = tag_data;
884 break;
885 }
886 }
887
888 if (prelight != popup->prelight)
889 {
890 if (popup->prelight)
891 gimp_tag_popup_queue_draw_tag (popup, popup->prelight);
892
893 popup->prelight = prelight;
894
895 if (popup->prelight)
896 gimp_tag_popup_queue_draw_tag (popup, popup->prelight);
897 }
898 }
899 else if (event->type == GDK_BUTTON_RELEASE &&
900 ! popup->single_select_disabled)
901 {
902 GdkEventButton *button_event = (GdkEventButton *) event;
903 gint x;
904 gint y;
905 gint i;
906
907 popup->single_select_disabled = TRUE;
908
909 x = button_event->x;
910 y = button_event->y + popup->scroll_y;
911
912 for (i = 0; i < popup->tag_count; i++)
913 {
914 PopupTagData *tag_data = &popup->tag_data[i];
915
916 if (gimp_tag_popup_is_in_tag (tag_data, x, y))
917 {
918 gimp_tag_popup_toggle_tag (popup, tag_data);
919 gtk_widget_destroy (GTK_WIDGET (popup));
920 break;
921 }
922 }
923 }
924
925 return FALSE;
926 }
927
928 static gboolean
gimp_tag_popup_is_in_tag(PopupTagData * tag_data,gint x,gint y)929 gimp_tag_popup_is_in_tag (PopupTagData *tag_data,
930 gint x,
931 gint y)
932 {
933 if (x >= tag_data->bounds.x &&
934 y >= tag_data->bounds.y &&
935 x < tag_data->bounds.x + tag_data->bounds.width &&
936 y < tag_data->bounds.y + tag_data->bounds.height)
937 {
938 return TRUE;
939 }
940
941 return FALSE;
942 }
943
944 static void
gimp_tag_popup_queue_draw_tag(GimpTagPopup * popup,PopupTagData * tag_data)945 gimp_tag_popup_queue_draw_tag (GimpTagPopup *popup,
946 PopupTagData *tag_data)
947 {
948 gtk_widget_queue_draw_area (popup->tag_area,
949 tag_data->bounds.x,
950 tag_data->bounds.y - popup->scroll_y,
951 tag_data->bounds.width,
952 tag_data->bounds.height);
953 }
954
955 static void
gimp_tag_popup_toggle_tag(GimpTagPopup * popup,PopupTagData * tag_data)956 gimp_tag_popup_toggle_tag (GimpTagPopup *popup,
957 PopupTagData *tag_data)
958 {
959 gchar **current_tags;
960 GString *tag_str;
961 gint length;
962 gint i;
963 gboolean tag_toggled_off = FALSE;
964
965 if (tag_data->state == GTK_STATE_NORMAL)
966 {
967 tag_data->state = GTK_STATE_SELECTED;
968 }
969 else if (tag_data->state == GTK_STATE_SELECTED)
970 {
971 tag_data->state = GTK_STATE_NORMAL;
972 }
973 else
974 {
975 return;
976 }
977
978 current_tags = gimp_tag_entry_parse_tags (GIMP_TAG_ENTRY (popup->combo_entry));
979 tag_str = g_string_new ("");
980 length = g_strv_length (current_tags);
981 for (i = 0; i < length; i++)
982 {
983 if (! gimp_tag_compare_with_string (tag_data->tag, current_tags[i]))
984 {
985 tag_toggled_off = TRUE;
986 }
987 else
988 {
989 if (tag_str->len)
990 {
991 g_string_append (tag_str, gimp_tag_entry_get_separator ());
992 g_string_append_c (tag_str, ' ');
993 }
994
995 g_string_append (tag_str, current_tags[i]);
996 }
997 }
998
999 if (! tag_toggled_off)
1000 {
1001 /* this tag was not selected yet, so it needs to be toggled on */
1002
1003 if (tag_str->len)
1004 {
1005 g_string_append (tag_str, gimp_tag_entry_get_separator ());
1006 g_string_append_c (tag_str, ' ');
1007 }
1008
1009 g_string_append (tag_str, gimp_tag_get_name (tag_data->tag));
1010 }
1011
1012 gimp_tag_entry_set_tag_string (GIMP_TAG_ENTRY (popup->combo_entry),
1013 tag_str->str);
1014
1015 g_string_free (tag_str, TRUE);
1016 g_strfreev (current_tags);
1017
1018 if (GIMP_TAG_ENTRY (popup->combo_entry)->mode == GIMP_TAG_ENTRY_MODE_QUERY)
1019 {
1020 GimpTaggedContainer *container;
1021
1022 container = GIMP_TAG_ENTRY (popup->combo_entry)->container;
1023
1024 for (i = 0; i < popup->tag_count; i++)
1025 {
1026 if (popup->tag_data[i].state != GTK_STATE_SELECTED)
1027 {
1028 popup->tag_data[i].state = GTK_STATE_INSENSITIVE;
1029 }
1030 }
1031
1032 gimp_container_foreach (GIMP_CONTAINER (container),
1033 (GFunc) gimp_tag_popup_check_can_toggle,
1034 popup);
1035 }
1036 }
1037
1038 static int
gimp_tag_popup_data_compare(const void * a,const void * b)1039 gimp_tag_popup_data_compare (const void *a,
1040 const void *b)
1041 {
1042 return gimp_tag_compare_func (((PopupTagData *) a)->tag,
1043 ((PopupTagData *) b)->tag);
1044 }
1045
1046 static void
gimp_tag_popup_check_can_toggle(GimpTagged * tagged,GimpTagPopup * popup)1047 gimp_tag_popup_check_can_toggle (GimpTagged *tagged,
1048 GimpTagPopup *popup)
1049 {
1050 GList *iterator;
1051
1052 for (iterator = gimp_tagged_get_tags (tagged);
1053 iterator;
1054 iterator = g_list_next (iterator))
1055 {
1056 PopupTagData search_key;
1057 PopupTagData *search_result;
1058
1059 search_key.tag = iterator->data;
1060
1061 search_result =
1062 (PopupTagData *) bsearch (&search_key,
1063 popup->tag_data, popup->tag_count,
1064 sizeof (PopupTagData),
1065 gimp_tag_popup_data_compare);
1066
1067 if (search_result)
1068 {
1069 if (search_result->state == GTK_STATE_INSENSITIVE)
1070 {
1071 search_result->state = GTK_STATE_NORMAL;
1072 }
1073 }
1074 }
1075 }
1076
1077 static gboolean
gimp_tag_popup_scroll_timeout(gpointer data)1078 gimp_tag_popup_scroll_timeout (gpointer data)
1079 {
1080 GimpTagPopup *popup = data;
1081 gboolean touchscreen_mode;
1082
1083 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1084 "gtk-touchscreen-mode", &touchscreen_mode,
1085 NULL);
1086
1087 gimp_tag_popup_scroll_by (popup, popup->scroll_step);
1088
1089 return TRUE;
1090 }
1091
1092 static void
gimp_tag_popup_remove_scroll_timeout(GimpTagPopup * popup)1093 gimp_tag_popup_remove_scroll_timeout (GimpTagPopup *popup)
1094 {
1095 if (popup->scroll_timeout_id)
1096 {
1097 g_source_remove (popup->scroll_timeout_id);
1098 popup->scroll_timeout_id = 0;
1099 }
1100 }
1101
1102 static gboolean
gimp_tag_popup_scroll_timeout_initial(gpointer data)1103 gimp_tag_popup_scroll_timeout_initial (gpointer data)
1104 {
1105 GimpTagPopup *popup = data;
1106 guint timeout;
1107 gboolean touchscreen_mode;
1108
1109 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1110 "gtk-timeout-repeat", &timeout,
1111 "gtk-touchscreen-mode", &touchscreen_mode,
1112 NULL);
1113
1114 gimp_tag_popup_scroll_by (popup, popup->scroll_step);
1115
1116 gimp_tag_popup_remove_scroll_timeout (popup);
1117
1118 popup->scroll_timeout_id =
1119 gdk_threads_add_timeout (timeout,
1120 gimp_tag_popup_scroll_timeout,
1121 popup);
1122
1123 return FALSE;
1124 }
1125
1126 static void
gimp_tag_popup_start_scrolling(GimpTagPopup * popup)1127 gimp_tag_popup_start_scrolling (GimpTagPopup *popup)
1128 {
1129 guint timeout;
1130 gboolean touchscreen_mode;
1131
1132 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1133 "gtk-timeout-repeat", &timeout,
1134 "gtk-touchscreen-mode", &touchscreen_mode,
1135 NULL);
1136
1137 gimp_tag_popup_scroll_by (popup, popup->scroll_step);
1138
1139 popup->scroll_timeout_id =
1140 gdk_threads_add_timeout (timeout,
1141 gimp_tag_popup_scroll_timeout_initial,
1142 popup);
1143 }
1144
1145 static void
gimp_tag_popup_stop_scrolling(GimpTagPopup * popup)1146 gimp_tag_popup_stop_scrolling (GimpTagPopup *popup)
1147 {
1148 gboolean touchscreen_mode;
1149
1150 gimp_tag_popup_remove_scroll_timeout (popup);
1151
1152 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1153 "gtk-touchscreen-mode", &touchscreen_mode,
1154 NULL);
1155
1156 if (! touchscreen_mode)
1157 {
1158 popup->upper_arrow_prelight = FALSE;
1159 popup->lower_arrow_prelight = FALSE;
1160 }
1161 }
1162
1163 static void
gimp_tag_popup_scroll_by(GimpTagPopup * popup,gint step)1164 gimp_tag_popup_scroll_by (GimpTagPopup *popup,
1165 gint step)
1166 {
1167 GtkStateType arrow_state;
1168 gint new_scroll_y = popup->scroll_y + step;
1169
1170 arrow_state = popup->upper_arrow_state;
1171
1172 if (new_scroll_y < 0)
1173 {
1174 new_scroll_y = 0;
1175
1176 if (arrow_state != GTK_STATE_INSENSITIVE)
1177 gimp_tag_popup_stop_scrolling (popup);
1178
1179 arrow_state = GTK_STATE_INSENSITIVE;
1180 }
1181 else
1182 {
1183 arrow_state = (popup->upper_arrow_prelight ?
1184 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
1185 }
1186
1187 if (arrow_state != popup->upper_arrow_state)
1188 {
1189 popup->upper_arrow_state = arrow_state;
1190 gtk_widget_queue_draw (GTK_WIDGET (popup));
1191 }
1192
1193 arrow_state = popup->lower_arrow_state;
1194
1195 if (new_scroll_y >= popup->scroll_height)
1196 {
1197 new_scroll_y = popup->scroll_height - 1;
1198
1199 if (arrow_state != GTK_STATE_INSENSITIVE)
1200 gimp_tag_popup_stop_scrolling (popup);
1201
1202 arrow_state = GTK_STATE_INSENSITIVE;
1203 }
1204 else
1205 {
1206 arrow_state = (popup->lower_arrow_prelight ?
1207 GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
1208 }
1209
1210 if (arrow_state != popup->lower_arrow_state)
1211 {
1212 popup->lower_arrow_state = arrow_state;
1213 gtk_widget_queue_draw (GTK_WIDGET (popup));
1214 }
1215
1216 if (new_scroll_y != popup->scroll_y)
1217 {
1218 popup->scroll_y = new_scroll_y;
1219
1220 gdk_window_scroll (gtk_widget_get_window (popup->tag_area), 0, -step);
1221 }
1222 }
1223
1224 static void
gimp_tag_popup_handle_scrolling(GimpTagPopup * popup,gint x,gint y,gboolean enter,gboolean motion)1225 gimp_tag_popup_handle_scrolling (GimpTagPopup *popup,
1226 gint x,
1227 gint y,
1228 gboolean enter,
1229 gboolean motion)
1230 {
1231 GdkRectangle rect;
1232 gboolean in_arrow;
1233 gboolean scroll_fast = FALSE;
1234 gboolean touchscreen_mode;
1235
1236 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1237 "gtk-touchscreen-mode", &touchscreen_mode,
1238 NULL);
1239
1240 /* upper arrow handling */
1241
1242 get_arrows_sensitive_area (popup, &rect, NULL);
1243
1244 in_arrow = FALSE;
1245 if (popup->arrows_visible &&
1246 x >= rect.x &&
1247 x < rect.x + rect.width &&
1248 y >= rect.y &&
1249 y < rect.y + rect.height)
1250 {
1251 in_arrow = TRUE;
1252 }
1253
1254 if (touchscreen_mode)
1255 popup->upper_arrow_prelight = in_arrow;
1256
1257 if (popup->upper_arrow_state != GTK_STATE_INSENSITIVE)
1258 {
1259 gboolean arrow_pressed = FALSE;
1260
1261 if (popup->arrows_visible)
1262 {
1263 if (touchscreen_mode)
1264 {
1265 if (enter && popup->upper_arrow_prelight)
1266 {
1267 if (popup->scroll_timeout_id == 0)
1268 {
1269 gimp_tag_popup_remove_scroll_timeout (popup);
1270 popup->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
1271
1272 if (! motion)
1273 {
1274 /* Only do stuff on click. */
1275 gimp_tag_popup_start_scrolling (popup);
1276 arrow_pressed = TRUE;
1277 }
1278 }
1279 else
1280 {
1281 arrow_pressed = TRUE;
1282 }
1283 }
1284 else if (! enter)
1285 {
1286 gimp_tag_popup_stop_scrolling (popup);
1287 }
1288 }
1289 else /* !touchscreen_mode */
1290 {
1291 scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
1292
1293 if (enter && in_arrow &&
1294 (! popup->upper_arrow_prelight ||
1295 popup->scroll_fast != scroll_fast))
1296 {
1297 popup->upper_arrow_prelight = TRUE;
1298 popup->scroll_fast = scroll_fast;
1299
1300 gimp_tag_popup_remove_scroll_timeout (popup);
1301 popup->scroll_step = (scroll_fast ?
1302 -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1);
1303
1304 popup->scroll_timeout_id =
1305 gdk_threads_add_timeout (scroll_fast ?
1306 MENU_SCROLL_TIMEOUT2 :
1307 MENU_SCROLL_TIMEOUT1,
1308 gimp_tag_popup_scroll_timeout,
1309 popup);
1310 }
1311 else if (! enter && ! in_arrow && popup->upper_arrow_prelight)
1312 {
1313 gimp_tag_popup_stop_scrolling (popup);
1314 }
1315 }
1316 }
1317
1318 /* gimp_tag_popup_start_scrolling() might have hit the top of the
1319 * tag_popup, so check if the button isn't insensitive before
1320 * changing it to something else.
1321 */
1322 if (popup->upper_arrow_state != GTK_STATE_INSENSITIVE)
1323 {
1324 GtkStateType arrow_state = GTK_STATE_NORMAL;
1325
1326 if (arrow_pressed)
1327 arrow_state = GTK_STATE_ACTIVE;
1328 else if (popup->upper_arrow_prelight)
1329 arrow_state = GTK_STATE_PRELIGHT;
1330
1331 if (arrow_state != popup->upper_arrow_state)
1332 {
1333 popup->upper_arrow_state = arrow_state;
1334
1335 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (popup)),
1336 &rect, FALSE);
1337 }
1338 }
1339 }
1340
1341 /* lower arrow handling */
1342
1343 get_arrows_sensitive_area (popup, NULL, &rect);
1344
1345 in_arrow = FALSE;
1346 if (popup->arrows_visible &&
1347 x >= rect.x &&
1348 x < rect.x + rect.width &&
1349 y >= rect.y &&
1350 y < rect.y + rect.height)
1351 {
1352 in_arrow = TRUE;
1353 }
1354
1355 if (touchscreen_mode)
1356 popup->lower_arrow_prelight = in_arrow;
1357
1358 if (popup->lower_arrow_state != GTK_STATE_INSENSITIVE)
1359 {
1360 gboolean arrow_pressed = FALSE;
1361
1362 if (popup->arrows_visible)
1363 {
1364 if (touchscreen_mode)
1365 {
1366 if (enter && popup->lower_arrow_prelight)
1367 {
1368 if (popup->scroll_timeout_id == 0)
1369 {
1370 gimp_tag_popup_remove_scroll_timeout (popup);
1371 popup->scroll_step = MENU_SCROLL_STEP2; /* always fast */
1372
1373 if (! motion)
1374 {
1375 /* Only do stuff on click. */
1376 gimp_tag_popup_start_scrolling (popup);
1377 arrow_pressed = TRUE;
1378 }
1379 }
1380 else
1381 {
1382 arrow_pressed = TRUE;
1383 }
1384 }
1385 else if (! enter)
1386 {
1387 gimp_tag_popup_stop_scrolling (popup);
1388 }
1389 }
1390 else /* !touchscreen_mode */
1391 {
1392 scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
1393
1394 if (enter && in_arrow &&
1395 (! popup->lower_arrow_prelight ||
1396 popup->scroll_fast != scroll_fast))
1397 {
1398 popup->lower_arrow_prelight = TRUE;
1399 popup->scroll_fast = scroll_fast;
1400
1401 gimp_tag_popup_remove_scroll_timeout (popup);
1402 popup->scroll_step = (scroll_fast ?
1403 MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1);
1404
1405 popup->scroll_timeout_id =
1406 gdk_threads_add_timeout (scroll_fast ?
1407 MENU_SCROLL_TIMEOUT2 :
1408 MENU_SCROLL_TIMEOUT1,
1409 gimp_tag_popup_scroll_timeout,
1410 popup);
1411 }
1412 else if (! enter && ! in_arrow && popup->lower_arrow_prelight)
1413 {
1414 gimp_tag_popup_stop_scrolling (popup);
1415 }
1416 }
1417 }
1418
1419 /* gimp_tag_popup_start_scrolling() might have hit the bottom of the
1420 * popup, so check if the button isn't insensitive before
1421 * changing it to something else.
1422 */
1423 if (popup->lower_arrow_state != GTK_STATE_INSENSITIVE)
1424 {
1425 GtkStateType arrow_state = GTK_STATE_NORMAL;
1426
1427 if (arrow_pressed)
1428 arrow_state = GTK_STATE_ACTIVE;
1429 else if (popup->lower_arrow_prelight)
1430 arrow_state = GTK_STATE_PRELIGHT;
1431
1432 if (arrow_state != popup->lower_arrow_state)
1433 {
1434 popup->lower_arrow_state = arrow_state;
1435
1436 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (popup)),
1437 &rect, FALSE);
1438 }
1439 }
1440 }
1441 }
1442
1443 static gboolean
gimp_tag_popup_button_scroll(GimpTagPopup * popup,GdkEventButton * event)1444 gimp_tag_popup_button_scroll (GimpTagPopup *popup,
1445 GdkEventButton *event)
1446 {
1447 if (popup->upper_arrow_prelight || popup->lower_arrow_prelight)
1448 {
1449 gboolean touchscreen_mode;
1450
1451 g_object_get (gtk_widget_get_settings (GTK_WIDGET (popup)),
1452 "gtk-touchscreen-mode", &touchscreen_mode,
1453 NULL);
1454
1455 if (touchscreen_mode)
1456 gimp_tag_popup_handle_scrolling (popup,
1457 event->x_root,
1458 event->y_root,
1459 event->type == GDK_BUTTON_PRESS,
1460 FALSE);
1461
1462 return TRUE;
1463 }
1464
1465 return FALSE;
1466 }
1467
1468 static void
get_arrows_visible_area(GimpTagPopup * popup,GdkRectangle * border,GdkRectangle * upper,GdkRectangle * lower,gint * arrow_space)1469 get_arrows_visible_area (GimpTagPopup *popup,
1470 GdkRectangle *border,
1471 GdkRectangle *upper,
1472 GdkRectangle *lower,
1473 gint *arrow_space)
1474 {
1475 GtkWidget *widget = GTK_WIDGET (popup->alignment);
1476 guint padding_top;
1477 guint padding_bottom;
1478 guint padding_left;
1479 guint padding_right;
1480
1481 gtk_alignment_get_padding (GTK_ALIGNMENT (popup->alignment),
1482 &padding_top, &padding_bottom,
1483 &padding_left, &padding_right);
1484
1485 gtk_widget_get_allocation (widget, border);
1486
1487 upper->x = border->x + padding_left;
1488 upper->y = border->y;
1489 upper->width = border->width - padding_left - padding_right;
1490 upper->height = padding_top;
1491
1492 lower->x = border->x + padding_left;
1493 lower->y = border->y + border->height - padding_bottom;
1494 lower->width = border->width - padding_left - padding_right;
1495 lower->height = padding_bottom;
1496
1497 *arrow_space = popup->scroll_arrow_height;
1498 }
1499
1500 static void
get_arrows_sensitive_area(GimpTagPopup * popup,GdkRectangle * upper,GdkRectangle * lower)1501 get_arrows_sensitive_area (GimpTagPopup *popup,
1502 GdkRectangle *upper,
1503 GdkRectangle *lower)
1504 {
1505 GdkRectangle tmp_border;
1506 GdkRectangle tmp_upper;
1507 GdkRectangle tmp_lower;
1508 gint tmp_arrow_space;
1509
1510 get_arrows_visible_area (popup,
1511 &tmp_border, &tmp_upper, &tmp_lower, &tmp_arrow_space);
1512
1513 if (upper)
1514 *upper = tmp_upper;
1515
1516 if (lower)
1517 *lower = tmp_lower;
1518 }
1519