1 /* GTK - The GIMP Toolkit
2 * Copyright © 2012 Carlos Garnacho <carlosg@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 #include "gtkprivatetypebuiltins.h"
20 #include "gtktexthandleprivate.h"
21 #include "gtkmarshalers.h"
22 #include "gtkprivate.h"
27 typedef struct _GtkTextHandlePrivate GtkTextHandlePrivate;
28 typedef struct _HandleWindow HandleWindow;
45 GdkRectangle pointing_to;
49 guint mode_visible : 1;
50 guint user_visible : 1;
54 struct _GtkTextHandlePrivate
56 HandleWindow windows[2];
58 GdkWindow *relative_to;
59 GtkStyleContext *style_context;
61 gulong draw_signal_id;
62 gulong event_signal_id;
63 gulong style_updated_id;
64 gulong composited_changed_id;
69 G_DEFINE_TYPE (GtkTextHandle, _gtk_text_handle, G_TYPE_OBJECT)
71 static guint signals[LAST_SIGNAL] = { 0 };
74 _gtk_text_handle_get_size (GtkTextHandle *handle,
78 GtkTextHandlePrivate *priv;
83 gtk_widget_style_get (priv->parent,
84 "text-handle-width", &w,
85 "text-handle-height", &h,
95 _gtk_text_handle_draw (GtkTextHandle *handle,
97 GtkTextHandlePosition pos)
99 GtkTextHandlePrivate *priv;
105 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
106 cairo_set_source_rgba (cr, 0, 0, 0, 0);
109 if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
110 cairo_translate (cr, 0, priv->windows[pos].pointing_to.height);
112 gtk_style_context_save (priv->style_context);
113 gtk_style_context_add_class (priv->style_context,
114 GTK_STYLE_CLASS_CURSOR_HANDLE);
116 if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
118 gtk_style_context_add_class (priv->style_context,
119 GTK_STYLE_CLASS_BOTTOM);
121 if (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
122 gtk_style_context_add_class (priv->style_context,
123 GTK_STYLE_CLASS_INSERTION_CURSOR);
126 gtk_style_context_add_class (priv->style_context,
127 GTK_STYLE_CLASS_TOP);
129 _gtk_text_handle_get_size (handle, &width, &height);
130 gtk_render_background (priv->style_context, cr, 0, 0, width, height);
132 gtk_style_context_restore (priv->style_context);
137 _gtk_text_handle_update_shape (GtkTextHandle *handle,
139 GtkTextHandlePosition pos)
141 GtkTextHandlePrivate *priv;
142 cairo_rectangle_int_t rect;
143 cairo_surface_t *surface;
144 cairo_region_t *region;
150 gdk_window_create_similar_surface (window,
151 CAIRO_CONTENT_COLOR_ALPHA,
152 gdk_window_get_width (window),
153 gdk_window_get_height (window));
155 cr = cairo_create (surface);
156 _gtk_text_handle_draw (handle, cr, pos);
159 region = gdk_cairo_region_create_from_surface (surface);
161 if (gtk_widget_is_composited (priv->parent))
162 gdk_window_shape_combine_region (window, NULL, 0, 0);
164 gdk_window_shape_combine_region (window, region, 0, 0);
166 cairo_region_get_extents (region, &rect);
167 cairo_region_destroy (region);
169 /* Preserve x/width, but extend input shape
170 * vertically to all window height */
172 rect.height = gdk_window_get_height (window);
173 region = cairo_region_create_rectangle (&rect);
175 gdk_window_input_shape_combine_region (window, region, 0, 0);
177 cairo_surface_destroy (surface);
178 cairo_region_destroy (region);
182 _gtk_text_handle_create_window (GtkTextHandle *handle,
183 GtkTextHandlePosition pos)
185 GtkTextHandlePrivate *priv;
186 GdkRGBA bg = { 0, 0, 0, 0 };
187 GdkWindowAttr attributes;
196 _gtk_text_handle_get_size (handle, &attributes.width, &attributes.height);
197 attributes.window_type = GDK_WINDOW_TEMP;
198 attributes.wclass = GDK_INPUT_OUTPUT;
199 attributes.event_mask = (GDK_EXPOSURE_MASK |
200 GDK_BUTTON_PRESS_MASK |
201 GDK_BUTTON_RELEASE_MASK |
202 GDK_BUTTON1_MOTION_MASK);
204 mask = GDK_WA_X | GDK_WA_Y;
206 visual = gdk_screen_get_rgba_visual (gtk_widget_get_screen (priv->parent));
210 attributes.visual = visual;
211 mask |= GDK_WA_VISUAL;
214 window = gdk_window_new (gtk_widget_get_root_window (priv->parent),
216 gtk_widget_register_window (priv->parent, window);
217 gdk_window_set_background_rgba (window, &bg);
219 _gtk_text_handle_update_shape (handle, window, pos);
225 gtk_text_handle_widget_draw (GtkWidget *widget,
227 GtkTextHandle *handle)
229 GtkTextHandlePrivate *priv;
230 GtkTextHandlePosition pos;
231 HandleWindow *handle_window;
238 if (gtk_cairo_should_draw_window (cr, priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window))
239 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
240 else if (gtk_cairo_should_draw_window (cr, priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window))
241 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
245 handle_window = &priv->windows[pos];
246 if (gdk_window_is_visible (handle_window->window))
247 _gtk_text_handle_draw (handle, cr, pos);
253 gtk_text_handle_widget_event (GtkWidget *widget,
255 GtkTextHandle *handle)
257 GtkTextHandlePrivate *priv;
258 GtkTextHandlePosition pos;
262 if (event->any.window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
263 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
264 else if (event->any.window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
265 pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
269 if (event->type == GDK_BUTTON_PRESS)
271 priv->windows[pos].dx = event->button.x;
272 priv->windows[pos].dy = event->button.y;
273 priv->windows[pos].dragged = TRUE;
275 else if (event->type == GDK_BUTTON_RELEASE)
277 g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
278 priv->windows[pos].dx = priv->windows[pos].dy = 0;
279 priv->windows[pos].dragged = FALSE;
281 else if (event->type == GDK_MOTION_NOTIFY && priv->windows[pos].dragged)
283 gint x, y, width, height;
285 _gtk_text_handle_get_size (handle, &width, &height);
286 gdk_window_get_origin (priv->relative_to, &x, &y);
288 x = event->motion.x_root - priv->windows[pos].dx + (width / 2) - x;
289 y = event->motion.y_root - priv->windows[pos].dy - y;
291 if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START)
294 y += priv->windows[pos].pointing_to.height / 2;
296 g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
303 _gtk_text_handle_update_window_state (GtkTextHandle *handle,
304 GtkTextHandlePosition pos)
306 GtkTextHandlePrivate *priv;
307 HandleWindow *handle_window;
310 handle_window = &priv->windows[pos];
312 if (!handle_window->window)
315 if (handle_window->has_point &&
316 handle_window->mode_visible && handle_window->user_visible)
318 gint x, y, width, height;
320 x = handle_window->pointing_to.x;
321 y = handle_window->pointing_to.y;
322 _gtk_text_handle_get_size (handle, &width, &height);
324 if (pos != GTK_TEXT_HANDLE_POSITION_CURSOR)
327 height += handle_window->pointing_to.height;
330 gdk_window_move_resize (handle_window->window, x, y, width, height);
331 gdk_window_show (handle_window->window);
334 gdk_window_hide (handle_window->window);
338 _gtk_text_handle_update_window (GtkTextHandle *handle,
339 GtkTextHandlePosition pos,
342 GtkTextHandlePrivate *priv;
343 HandleWindow *handle_window;
346 handle_window = &priv->windows[pos];
348 if (!handle_window->window)
353 gtk_widget_unregister_window (priv->parent, handle_window->window);
354 gdk_window_destroy (handle_window->window);
355 handle_window->window = _gtk_text_handle_create_window (handle, pos);
358 _gtk_text_handle_update_window_state (handle, pos);
362 _gtk_text_handle_update_windows (GtkTextHandle *handle)
364 GtkTextHandlePrivate *priv = handle->priv;
366 gtk_style_context_invalidate (priv->style_context);
367 _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, FALSE);
368 _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, FALSE);
372 _gtk_text_handle_composited_changed (GtkTextHandle *handle)
374 _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, TRUE);
375 _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, TRUE);
379 gtk_text_handle_constructed (GObject *object)
381 GtkTextHandlePrivate *priv;
383 priv = GTK_TEXT_HANDLE (object)->priv;
384 g_assert (priv->parent != NULL);
386 priv->draw_signal_id =
387 g_signal_connect (priv->parent, "draw",
388 G_CALLBACK (gtk_text_handle_widget_draw),
390 priv->event_signal_id =
391 g_signal_connect (priv->parent, "event",
392 G_CALLBACK (gtk_text_handle_widget_event),
394 priv->composited_changed_id =
395 g_signal_connect_swapped (priv->parent, "composited-changed",
396 G_CALLBACK (_gtk_text_handle_composited_changed),
398 priv->style_updated_id =
399 g_signal_connect_swapped (priv->parent, "style-updated",
400 G_CALLBACK (_gtk_text_handle_update_windows),
405 gtk_text_handle_finalize (GObject *object)
407 GtkTextHandlePrivate *priv;
409 priv = GTK_TEXT_HANDLE (object)->priv;
411 if (priv->relative_to)
412 g_object_unref (priv->relative_to);
414 if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
416 gtk_widget_unregister_window (priv->parent,
417 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
418 gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
421 if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
423 gtk_widget_unregister_window (priv->parent,
424 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
425 gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
428 if (g_signal_handler_is_connected (priv->parent, priv->draw_signal_id))
429 g_signal_handler_disconnect (priv->parent, priv->draw_signal_id);
431 if (g_signal_handler_is_connected (priv->parent, priv->event_signal_id))
432 g_signal_handler_disconnect (priv->parent, priv->event_signal_id);
434 if (g_signal_handler_is_connected (priv->parent, priv->composited_changed_id))
435 g_signal_handler_disconnect (priv->parent, priv->composited_changed_id);
437 if (g_signal_handler_is_connected (priv->parent, priv->style_updated_id))
438 g_signal_handler_disconnect (priv->parent, priv->style_updated_id);
440 g_object_unref (priv->style_context);
442 G_OBJECT_CLASS (_gtk_text_handle_parent_class)->finalize (object);
446 gtk_text_handle_set_property (GObject *object,
451 GtkTextHandlePrivate *priv;
452 GtkTextHandle *handle;
454 handle = GTK_TEXT_HANDLE (object);
460 priv->parent = g_value_get_object (value);
462 case PROP_RELATIVE_TO:
463 _gtk_text_handle_set_relative_to (handle,
464 g_value_get_object (value));
467 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
472 gtk_text_handle_get_property (GObject *object,
477 GtkTextHandlePrivate *priv;
479 priv = GTK_TEXT_HANDLE (object)->priv;
484 g_value_set_object (value, priv->parent);
486 case PROP_RELATIVE_TO:
487 g_value_set_object (value, priv->relative_to);
490 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
495 _gtk_text_handle_class_init (GtkTextHandleClass *klass)
497 GObjectClass *object_class = G_OBJECT_CLASS (klass);
499 object_class->constructed = gtk_text_handle_constructed;
500 object_class->finalize = gtk_text_handle_finalize;
501 object_class->set_property = gtk_text_handle_set_property;
502 object_class->get_property = gtk_text_handle_get_property;
504 signals[HANDLE_DRAGGED] =
505 g_signal_new (I_("handle-dragged"),
506 G_OBJECT_CLASS_TYPE (object_class),
508 G_STRUCT_OFFSET (GtkTextHandleClass, handle_dragged),
510 _gtk_marshal_VOID__ENUM_INT_INT,
512 GTK_TYPE_TEXT_HANDLE_POSITION,
513 G_TYPE_INT, G_TYPE_INT);
514 signals[DRAG_FINISHED] =
515 g_signal_new (I_("drag-finished"),
516 G_OBJECT_CLASS_TYPE (object_class),
517 G_SIGNAL_RUN_LAST, 0,
519 g_cclosure_marshal_VOID__ENUM,
521 GTK_TYPE_TEXT_HANDLE_POSITION);
523 g_object_class_install_property (object_class,
525 g_param_spec_object ("parent",
529 GTK_PARAM_READWRITE |
530 G_PARAM_CONSTRUCT_ONLY));
531 g_object_class_install_property (object_class,
533 g_param_spec_object ("relative-to",
535 P_("Window the coordinates are based upon"),
537 GTK_PARAM_READWRITE));
539 g_type_class_add_private (object_class, sizeof (GtkTextHandlePrivate));
543 _gtk_text_handle_init (GtkTextHandle *handle)
545 GtkTextHandlePrivate *priv;
548 handle->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (handle,
549 GTK_TYPE_TEXT_HANDLE,
550 GtkTextHandlePrivate);
552 path = gtk_widget_path_new ();
553 gtk_widget_path_append_type (path, GTK_TYPE_TEXT_HANDLE);
555 priv->style_context = gtk_style_context_new ();
556 gtk_style_context_set_path (priv->style_context, path);
557 gtk_widget_path_free (path);
561 _gtk_text_handle_new (GtkWidget *parent)
563 return g_object_new (GTK_TYPE_TEXT_HANDLE,
569 _gtk_text_handle_set_relative_to (GtkTextHandle *handle,
572 GtkTextHandlePrivate *priv;
574 g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
575 g_return_if_fail (!window || GDK_IS_WINDOW (window));
579 if (priv->relative_to)
581 gtk_widget_unregister_window (priv->parent,
582 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
583 gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
584 gtk_widget_unregister_window (priv->parent,
585 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
586 gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
587 g_object_unref (priv->relative_to);
592 priv->relative_to = g_object_ref (window);
593 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window =
594 _gtk_text_handle_create_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
595 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window =
596 _gtk_text_handle_create_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
597 priv->realized = TRUE;
601 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window = NULL;
602 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window = NULL;
603 priv->relative_to = NULL;
604 priv->realized = FALSE;
607 g_object_notify (G_OBJECT (handle), "relative-to");
611 _gtk_text_handle_set_mode (GtkTextHandle *handle,
612 GtkTextHandleMode mode)
614 GtkTextHandlePrivate *priv;
616 g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
620 if (priv->mode == mode)
627 case GTK_TEXT_HANDLE_MODE_CURSOR:
628 priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].mode_visible = TRUE;
629 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = FALSE;
631 case GTK_TEXT_HANDLE_MODE_SELECTION:
632 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = TRUE;
633 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].mode_visible = TRUE;
635 case GTK_TEXT_HANDLE_MODE_NONE:
637 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = FALSE;
638 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].mode_visible = FALSE;
642 _gtk_text_handle_update_shape (handle,
643 priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].window,
644 GTK_TEXT_HANDLE_POSITION_CURSOR);
645 _gtk_text_handle_update_shape (handle,
646 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window,
647 GTK_TEXT_HANDLE_POSITION_SELECTION_START);
649 _gtk_text_handle_update_window_state (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
650 _gtk_text_handle_update_window_state (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
654 _gtk_text_handle_get_mode (GtkTextHandle *handle)
656 GtkTextHandlePrivate *priv;
658 g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), GTK_TEXT_HANDLE_MODE_NONE);
665 _gtk_text_handle_set_position (GtkTextHandle *handle,
666 GtkTextHandlePosition pos,
669 GtkTextHandlePrivate *priv;
670 HandleWindow *handle_window;
671 gboolean size_changed;
673 g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
676 pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
677 GTK_TEXT_HANDLE_POSITION_SELECTION_START);
678 handle_window = &priv->windows[pos];
683 if (priv->mode == GTK_TEXT_HANDLE_MODE_NONE ||
684 (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
685 pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
688 size_changed = (rect->width != handle_window->pointing_to.width ||
689 rect->height != handle_window->pointing_to.height);
691 handle_window->pointing_to = *rect;
692 handle_window->has_point = TRUE;
693 gdk_window_get_root_coords (priv->relative_to,
695 &handle_window->pointing_to.x,
696 &handle_window->pointing_to.y);
698 _gtk_text_handle_update_window_state (handle, pos);
701 _gtk_text_handle_update_shape (handle, handle_window->window, pos);
705 _gtk_text_handle_set_visible (GtkTextHandle *handle,
706 GtkTextHandlePosition pos,
709 GtkTextHandlePrivate *priv;
712 g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
715 pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
716 GTK_TEXT_HANDLE_POSITION_SELECTION_START);
721 window = priv->windows[pos].window;
726 if (!gdk_window_is_visible (window))
727 _gtk_text_handle_update_shape (handle, window, pos);
729 priv->windows[pos].user_visible = visible;
730 _gtk_text_handle_update_window_state (handle, pos);
734 _gtk_text_handle_get_is_dragged (GtkTextHandle *handle,
735 GtkTextHandlePosition pos)
737 GtkTextHandlePrivate *priv;
739 g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), FALSE);
742 pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
743 GTK_TEXT_HANDLE_POSITION_SELECTION_START);
745 return priv->windows[pos].dragged;