From ded899933c50a976d10144f526ffd6a5e58e6ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sat, 3 Sep 2022 21:10:39 +0200 Subject: [PATCH] sdgui: make Ctrl+click put hovered word in search --- README.adoc | 2 +- src/sdgui.c | 11 + src/stardict-view.c | 551 ++++++++++++++++++++++++++++++-------------- 3 files changed, 384 insertions(+), 180 deletions(-) diff --git a/README.adoc b/README.adoc index d5bee2c..9e95237 100644 --- a/README.adoc +++ b/README.adoc @@ -96,7 +96,7 @@ should be addressed before including the software in regular Linux and/or BSD distributions: - The tab bar doesn't handle overflows well in the TUI. - - The GUI has a few usability issues, and is awkward to configure. + - The GUI is awkward to configure. - Lacking configuration, standard StarDict locations should be scanned. Given all issues with the file format, it might be better to start anew. diff --git a/src/sdgui.c b/src/sdgui.c index 457e72e..72fdaac 100644 --- a/src/sdgui.c +++ b/src/sdgui.c @@ -127,6 +127,15 @@ on_changed (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer data) search (g_ptr_array_index (g.dictionaries, g.dictionary)); } +static void +on_send (G_GNUC_UNUSED StardictView *view, + const char *word, G_GNUC_UNUSED gpointer data) +{ + GtkEntryBuffer *buf = gtk_entry_get_buffer (GTK_ENTRY (g.entry)); + gtk_entry_buffer_set_text (buf, word, -1); + gtk_editable_select_region (GTK_EDITABLE (g.entry), 0, -1); +} + static void on_selection_received (G_GNUC_UNUSED GtkClipboard *clipboard, const gchar *text, G_GNUC_UNUSED gpointer data) @@ -529,6 +538,8 @@ main (int argc, char *argv[]) gtk_drag_dest_add_uri_targets (g.view); g_signal_connect (g.view, "drag-data-received", G_CALLBACK (on_drag_data_received), NULL); + g_signal_connect (g.view, "send", + G_CALLBACK (on_send), NULL); gtk_widget_show_all (g.window); gtk_main (); diff --git a/src/stardict-view.c b/src/stardict-view.c index d073b16..056c240 100644 --- a/src/stardict-view.c +++ b/src/stardict-view.c @@ -149,15 +149,51 @@ struct view_entry_render_ctx PangoLayout *selection_layout; int selection_begin; int selection_end; + PangoLayout *hover_layout; + int hover_begin; + int hover_end; }; +static PangoLayout * +view_entry_adjust_layout (ViewEntryRenderCtx *ctx, PangoLayout *layout) +{ + if (layout != ctx->hover_layout) + return g_object_ref (layout); + + layout = pango_layout_copy (layout); + PangoAttrList *attrs = pango_layout_get_attributes (layout); + attrs = attrs + ? pango_attr_list_copy (attrs) + : pango_attr_list_new (); + + PangoAttribute *u = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + u->start_index = ctx->hover_begin; + u->end_index = ctx->hover_end; + pango_attr_list_change (attrs, u); + + PangoAttribute *uc = pango_attr_underline_color_new (0, 0, 0xffff); + uc->start_index = ctx->hover_begin; + uc->end_index = ctx->hover_end; + pango_attr_list_change (attrs, uc); + + PangoAttribute *c = pango_attr_foreground_new (0, 0, 0xffff); + c->start_index = ctx->hover_begin; + c->end_index = ctx->hover_end; + pango_attr_list_change (attrs, c); + + pango_layout_set_attributes (layout, attrs); + pango_attr_list_unref (attrs); + return layout; +} + static void view_entry_render (ViewEntryRenderCtx *ctx, gdouble x, gdouble y, PangoLayout *layout) { - gtk_render_layout (ctx->style, ctx->cr, x, y, layout); + PangoLayout *adjusted = view_entry_adjust_layout (ctx, layout); + gtk_render_layout (ctx->style, ctx->cr, x, y, adjusted); if (layout != ctx->selection_layout) - return; + goto out; gtk_style_context_save (ctx->style); gtk_style_context_set_state (ctx->style, GTK_STATE_FLAG_SELECTED); @@ -166,16 +202,18 @@ view_entry_render (ViewEntryRenderCtx *ctx, gdouble x, gdouble y, int ranges[2] = { MIN (ctx->selection_begin, ctx->selection_end), MAX (ctx->selection_begin, ctx->selection_end) }; cairo_region_t *region - = gdk_pango_layout_get_clip_region (layout, x, y, ranges, 1); + = gdk_pango_layout_get_clip_region (adjusted, x, y, ranges, 1); gdk_cairo_region (ctx->cr, region); cairo_clip (ctx->cr); cairo_region_destroy (region); gtk_render_background (ctx->style, ctx->cr, 0, 0, ctx->width, ctx->height); - gtk_render_layout (ctx->style, ctx->cr, x, y, layout); + gtk_render_layout (ctx->style, ctx->cr, x, y, adjusted); cairo_restore (ctx->cr); gtk_style_context_restore (ctx->style); +out: + g_object_unref (adjusted); } static gint @@ -272,6 +310,10 @@ struct _StardictView GWeakRef selection; ///< Selected PangoLayout, if any int selection_begin; ///< Start index within `selection` int selection_end; ///< End index within `selection` + + GWeakRef hover; ///< Hovered PangoLayout, if any + int hover_begin; ///< Word start index within `hover` + int hover_end; ///< Word end index within `hover` }; static ViewEntry * @@ -283,6 +325,23 @@ make_entry (StardictView *self, StardictIterator *iterator) return ve; } +static void +reset_hover (StardictView *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + PangoLayout *hover = g_weak_ref_get (&self->hover); + if (hover) + { + g_object_unref (hover); + g_weak_ref_set (&self->hover, NULL); + self->hover_begin = self->hover_end = -1; + gtk_widget_queue_draw (widget); + } + + if (gtk_widget_get_realized (widget)) + gdk_window_set_cursor (gtk_widget_get_window (widget), NULL); +} + static void adjust_for_height (StardictView *self) { @@ -314,15 +373,16 @@ adjust_for_height (StardictView *self) } g_object_unref (iterator); - self->entries = g_list_concat (self->entries, g_list_reverse (append)); - gtk_widget_queue_draw (widget); - // Also handling this for adjust_for_offset(), which calls this. PangoLayout *selection = g_weak_ref_get (&self->selection); if (selection) g_object_unref (selection); else self->selection_begin = self->selection_end = -1; + + reset_hover (self); + self->entries = g_list_concat (self->entries, g_list_reverse (append)); + gtk_widget_queue_draw (widget); } static void @@ -378,6 +438,7 @@ reload (StardictView *self) // For consistency, and the check in make_context_menu() self->selection_begin = self->selection_end = -1; + reset_hover (self); if (gtk_widget_get_realized (widget) && self->dict) adjust_for_height (self); @@ -393,131 +454,7 @@ natural_row_size (GtkWidget *widget) return height; } -// --- Boilerplate ------------------------------------------------------------- - -G_DEFINE_TYPE (StardictView, stardict_view, GTK_TYPE_WIDGET) - -static void -stardict_view_finalize (GObject *gobject) -{ - StardictView *self = STARDICT_VIEW (gobject); - g_clear_object (&self->dict); - - g_list_free_full (self->entries, (GDestroyNotify) view_entry_destroy); - self->entries = NULL; - - g_object_unref (self->selection_gesture); - g_weak_ref_clear (&self->selection); - - g_free (self->matched); - self->matched = NULL; - - G_OBJECT_CLASS (stardict_view_parent_class)->finalize (gobject); -} - -static void -stardict_view_get_preferred_height (GtkWidget *widget, - gint *minimum, gint *natural) -{ - // There isn't any value that would make any real sense - if (!STARDICT_VIEW (widget)->dict) - *natural = *minimum = 0; - else - *natural = *minimum = natural_row_size (widget); -} - -static void -stardict_view_get_preferred_width (GtkWidget *widget, - gint *minimum, gint *natural) -{ - GtkStyleContext *style = gtk_widget_get_style_context (widget); - GtkBorder padding = view_entry_get_padding (style); - *natural = *minimum = 2 * (padding.left + 1 * padding.right); -} - -static void -stardict_view_realize (GtkWidget *widget) -{ - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - - GdkWindowAttr attributes = - { - .window_type = GDK_WINDOW_CHILD, - .x = allocation.x, - .y = allocation.y, - .width = allocation.width, - .height = allocation.height, - - // Input-only would presumably also work (as in GtkPathBar, e.g.), - // but it merely seems to involve more work. - .wclass = GDK_INPUT_OUTPUT, - .visual = gtk_widget_get_visual (widget), - .event_mask = gtk_widget_get_events (widget) | GDK_SCROLL_MASK - | GDK_SMOOTH_SCROLL_MASK | GDK_BUTTON_PRESS_MASK, - }; - - // We need this window to receive input events at all. - // TODO: see if we don't want GDK_WA_CURSOR for setting a text cursor - GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL); - - // The default background colour of the GDK window is transparent - gtk_widget_register_window (widget, window); - gtk_widget_set_window (widget, window); - gtk_widget_set_realized (widget, TRUE); -} - -static gboolean -stardict_view_draw (GtkWidget *widget, cairo_t *cr) -{ - StardictView *self = STARDICT_VIEW (widget); - - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - - GtkStyleContext *style = gtk_widget_get_style_context (widget); - gtk_render_background (style, cr, - 0, 0, allocation.width, allocation.height); - gtk_render_frame (style, cr, - 0, 0, allocation.width, allocation.height); - - ViewEntryRenderCtx ctx = - { - .style = style, - .cr = cr, - .width = allocation.width, - .height = 0, - .selection_layout = g_weak_ref_get (&self->selection), - .selection_begin = self->selection_begin, - .selection_end = self->selection_end, - }; - - gint offset = -self->top_offset; - gint i = self->top_position; - for (GList *iter = self->entries; iter; iter = iter->next) - { - // Style regions would be appropriate, if they weren't deprecated. - // GTK+ CSS gadgets/nodes are an internal API. We don't want to turn - // this widget into a container, to avoid needless complexity. - // - // gtk_style_context_{get,set}_path() may be misused by adding the same - // GType with gtk_widget_path_append_type() and changing its name - // using gtk_widget_path_iter_set_name()... but that is ugly. - gtk_style_context_save (style); - gtk_style_context_add_class (style, (i++ & 1) ? "even" : "odd"); - - cairo_save (cr); - cairo_translate (cr, 0, offset); - // TODO: later exclude clipped entries, but it's not that important - offset += view_entry_draw (iter->data, &ctx); - cairo_restore (cr); - - gtk_style_context_restore (style); - } - g_clear_object (&ctx.selection_layout); - return TRUE; -} +// --- Figuring out where stuff is---------------------------------------------- /// Figure out which layout is at given widget coordinates, and translate them. static PangoLayout * @@ -590,6 +527,242 @@ layout_coords (StardictView *self, PangoLayout *layout, int *x, int *y) return FALSE; } +static int +layout_index_at (PangoLayout *layout, int x, int y) +{ + int index = 0, trailing = 0; + (void) pango_layout_xy_to_index (layout, + x * PANGO_SCALE, + y * PANGO_SCALE, + &index, + &trailing); + + const char *text = pango_layout_get_text (layout) + index; + while (trailing--) + { + int len = g_utf8_next_char (text) - text; + text += len; + index += len; + } + return index; +} + +static PangoLayout * +locate_word_at (StardictView *self, int x, int y, int *beginpos, int *endpos) +{ + *beginpos = -1; + *endpos = -1; + PangoLayout *layout = layout_at (self, &x, &y); + if (!layout) + return NULL; + + const char *text = pango_layout_get_text (layout), *p = NULL; + const char *begin = text + layout_index_at (layout, x, y), *end = begin; + while ((p = g_utf8_find_prev_char (text, begin)) + && !g_unichar_isspace (g_utf8_get_char (p))) + begin = p; + gunichar c; + while ((c = g_utf8_get_char (end)) && !g_unichar_isspace (c)) + end = g_utf8_next_char (end); + + *beginpos = begin - text; + *endpos = end - text; + return layout; +} + +// --- Boilerplate ------------------------------------------------------------- + +G_DEFINE_TYPE (StardictView, stardict_view, GTK_TYPE_WIDGET) + +enum { + SEND, + LAST_SIGNAL, +}; + +static guint view_signals[LAST_SIGNAL]; + +static void +stardict_view_finalize (GObject *gobject) +{ + StardictView *self = STARDICT_VIEW (gobject); + g_clear_object (&self->dict); + + g_list_free_full (self->entries, (GDestroyNotify) view_entry_destroy); + self->entries = NULL; + + g_object_unref (self->selection_gesture); + g_weak_ref_clear (&self->selection); + + g_free (self->matched); + self->matched = NULL; + + G_OBJECT_CLASS (stardict_view_parent_class)->finalize (gobject); +} + +static void +stardict_view_get_preferred_height (GtkWidget *widget, + gint *minimum, gint *natural) +{ + // There isn't any value that would make any real sense + if (!STARDICT_VIEW (widget)->dict) + *natural = *minimum = 0; + else + *natural = *minimum = natural_row_size (widget); +} + +static void +stardict_view_get_preferred_width (GtkWidget *widget, + gint *minimum, gint *natural) +{ + GtkStyleContext *style = gtk_widget_get_style_context (widget); + GtkBorder padding = view_entry_get_padding (style); + *natural = *minimum = 2 * (padding.left + 1 * padding.right); +} + +static void +stardict_view_realize (GtkWidget *widget) +{ + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + + GdkWindowAttr attributes = + { + .window_type = GDK_WINDOW_CHILD, + .x = allocation.x, + .y = allocation.y, + .width = allocation.width, + .height = allocation.height, + + // Input-only would presumably also work (as in GtkPathBar, e.g.), + // but it merely seems to involve more work. + .wclass = GDK_INPUT_OUTPUT, + .visual = gtk_widget_get_visual (widget), + .event_mask = gtk_widget_get_events (widget) | GDK_SCROLL_MASK + | GDK_SMOOTH_SCROLL_MASK | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK, + }; + + // We need this window to receive input events at all. + GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL); + + // The default background colour of the GDK window is transparent + gtk_widget_register_window (widget, window); + gtk_widget_set_window (widget, window); + gtk_widget_set_realized (widget, TRUE); +} + +static void +reset_hover_for_event (StardictView *self, guint state, int x, int y) +{ + reset_hover (self); + if ((state &= gtk_accelerator_get_default_mod_mask ()) != GDK_CONTROL_MASK) + return; + + g_weak_ref_set (&self->hover, + locate_word_at (self, x, y, &self->hover_begin, &self->hover_end)); + gtk_widget_queue_draw (GTK_WIDGET (self)); + + GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (self)); + GdkCursor *cursor = gdk_cursor_new_from_name + (gdk_window_get_display (window), "pointer"); + gdk_window_set_cursor (window, cursor); + g_object_unref (cursor); +} + +static void +on_keymap_state_changed (G_GNUC_UNUSED GdkKeymap *keymap, StardictView *self) +{ + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (self)); + GdkSeat *seat = gdk_display_get_default_seat (display); + GdkDevice *pointer = gdk_seat_get_pointer (seat); + + int x = -1, y = -1; + GdkModifierType state = 0; + GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (self)); + gdk_window_get_device_position (window, pointer, &x, &y, &state); + reset_hover_for_event (self, state, x, y); +} + +static void +stardict_view_map (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (stardict_view_parent_class)->map (widget); + + GdkWindow *window = gtk_widget_get_window (widget); + GdkDisplay *display = gdk_window_get_display (window); + GdkKeymap *keymap = gdk_keymap_get_for_display (display); + g_signal_connect (keymap, "state-changed", + G_CALLBACK (on_keymap_state_changed), widget); +} + +static void +stardict_view_unmap (GtkWidget *widget) +{ + GdkWindow *window = gtk_widget_get_window (widget); + GdkDisplay *display = gdk_window_get_display (window); + GdkKeymap *keymap = gdk_keymap_get_for_display (display); + g_signal_handlers_disconnect_by_data (keymap, widget); + + GTK_WIDGET_CLASS (stardict_view_parent_class)->unmap (widget); +} + +static gboolean +stardict_view_draw (GtkWidget *widget, cairo_t *cr) +{ + StardictView *self = STARDICT_VIEW (widget); + + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + + GtkStyleContext *style = gtk_widget_get_style_context (widget); + gtk_render_background (style, cr, + 0, 0, allocation.width, allocation.height); + gtk_render_frame (style, cr, + 0, 0, allocation.width, allocation.height); + + ViewEntryRenderCtx ctx = + { + .style = style, + .cr = cr, + .width = allocation.width, + .height = 0, + + .selection_layout = g_weak_ref_get (&self->selection), + .selection_begin = self->selection_begin, + .selection_end = self->selection_end, + .hover_layout = g_weak_ref_get (&self->hover), + .hover_begin = self->hover_begin, + .hover_end = self->hover_end, + }; + + gint offset = -self->top_offset; + gint i = self->top_position; + for (GList *iter = self->entries; iter; iter = iter->next) + { + // Style regions would be appropriate, if they weren't deprecated. + // GTK+ CSS gadgets/nodes are an internal API. We don't want to turn + // this widget into a container, to avoid needless complexity. + // + // gtk_style_context_{get,set}_path() may be misused by adding the same + // GType with gtk_widget_path_append_type() and changing its name + // using gtk_widget_path_iter_set_name()... but that is ugly. + gtk_style_context_save (style); + gtk_style_context_add_class (style, (i++ & 1) ? "even" : "odd"); + + cairo_save (cr); + cairo_translate (cr, 0, offset); + // TODO: later exclude clipped entries, but it's not that important + offset += view_entry_draw (iter->data, &ctx); + cairo_restore (cr); + + gtk_style_context_restore (style); + } + g_clear_object (&ctx.selection_layout); + g_clear_object (&ctx.hover_layout); + return TRUE; +} + static void stardict_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { @@ -662,31 +835,11 @@ stardict_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) } } -static int -layout_index_at (PangoLayout *layout, int x, int y) -{ - int index = 0, trailing = 0; - (void) pango_layout_xy_to_index (layout, - x * PANGO_SCALE, - y * PANGO_SCALE, - &index, - &trailing); - - const char *text = pango_layout_get_text (layout) + index; - while (trailing--) - { - int len = g_utf8_next_char(text) - text; - text += len; - index += len; - } - return index; -} - static void publish_selection (StardictView *self, GdkAtom target) { PangoLayout *layout = g_weak_ref_get (&self->selection); - if (!layout || self->selection_begin == self->selection_end) + if (!layout) return; // Unlike GtkLabel, we don't place the selection in PRIMARY immediately. @@ -694,7 +847,7 @@ publish_selection (StardictView *self, GdkAtom target) int len = strlen (text), s1 = MIN (self->selection_begin, self->selection_end), s2 = MAX (self->selection_begin, self->selection_end); - if (s1 >= 0 && s1 <= len && s2 >= 0 && s2 <= len) + if (s1 != s2 && s1 >= 0 && s1 <= len && s2 >= 0 && s2 <= len) gtk_clipboard_set_text (gtk_clipboard_get (target), text + s1, s2 - s1); g_object_unref (layout); } @@ -702,22 +855,9 @@ publish_selection (StardictView *self, GdkAtom target) static void select_word_at (StardictView *self, int x, int y) { - PangoLayout *layout = layout_at (self, &x, &y); - if (!layout) - return; + g_weak_ref_set (&self->selection, locate_word_at (self, + x, y, &self->selection_begin, &self->selection_end)); - const char *text = pango_layout_get_text (layout), *p = NULL; - const char *begin = text + layout_index_at (layout, x, y), *end = begin; - while ((p = g_utf8_find_prev_char (text, begin)) - && !g_unichar_isspace (g_utf8_get_char (p))) - begin = p; - gunichar c; - while ((c = g_utf8_get_char (end)) && !g_unichar_isspace (c)) - end = g_utf8_next_char (end); - - g_weak_ref_set (&self->selection, layout); - self->selection_begin = begin - text; - self->selection_end = end - text; gtk_widget_queue_draw (GTK_WIDGET (self)); publish_selection (self, GDK_SELECTION_PRIMARY); } @@ -779,7 +919,7 @@ static gboolean stardict_view_button_press_event (GtkWidget *widget, GdkEventButton *event) { StardictView *self = STARDICT_VIEW (widget); - if (gdk_event_triggers_context_menu((const GdkEvent *) event)) + if (gdk_event_triggers_context_menu ((const GdkEvent *) event)) { gtk_menu_popup_at_pointer (make_context_menu (self), (const GdkEvent *) event); @@ -806,6 +946,23 @@ stardict_view_button_press_event (GtkWidget *widget, GdkEventButton *event) ->button_press_event (widget, event); } +static gboolean +stardict_view_motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +{ + StardictView *self = STARDICT_VIEW (widget); + reset_hover_for_event (self, event->state, event->x, event->y); + return GTK_WIDGET_CLASS (stardict_view_parent_class) + ->motion_notify_event (widget, event); +} + +static gboolean +stardict_view_leave_notify_event + (GtkWidget *widget, G_GNUC_UNUSED GdkEventCrossing *event) +{ + reset_hover (STARDICT_VIEW (widget)); + return GDK_EVENT_PROPAGATE; +} + static void on_drag_begin (GtkGestureDrag *drag, G_GNUC_UNUSED gdouble start_x, G_GNUC_UNUSED gdouble start_y, gpointer user_data) @@ -838,21 +995,49 @@ on_drag_update (G_GNUC_UNUSED GtkGestureDrag *drag, self->drag_last_offset = offset_y; } +static gboolean +send_hover (StardictView *self) +{ + PangoLayout *layout = g_weak_ref_get (&self->hover); + if (!layout) + return FALSE; + + const char *text = pango_layout_get_text (layout); + int len = strlen (text), + s1 = MIN (self->hover_begin, self->hover_end), + s2 = MAX (self->hover_begin, self->hover_end); + if (s1 != s2 && s1 >= 0 && s1 <= len && s2 >= 0 && s2 <= len) + { + gchar *word = g_strndup (text + s1, s2 - s1); + g_signal_emit (self, view_signals[SEND], 0, word); + g_free (word); + } + g_object_unref (layout); + return TRUE; +} + static void on_select_begin (GtkGestureDrag *drag, gdouble start_x, gdouble start_y, gpointer user_data) { - // Despite our two gestures not being grouped up, claiming one doesn't - // deny the other, and :exclusive isn't the opposite of :touch-only. - // A non-NULL sequence indicates a touch event. + // We probably don't need to check modifiers and mouse position again. + StardictView *self = STARDICT_VIEW (user_data); GtkGesture *gesture = GTK_GESTURE (drag); - if (gtk_gesture_get_last_updated_sequence (gesture)) + if (send_hover (self)) { - gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); + return; + } + + // Despite our two gestures not being grouped up, claiming one doesn't + // deny the other, and :exclusive isn't the opposite of :touch-only. + // A non-NULL sequence indicates a touch event. + if (gtk_gesture_get_last_updated_sequence (gesture)) + { + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); return; } - StardictView *self = STARDICT_VIEW (user_data); g_weak_ref_set (&self->selection, NULL); self->selection_begin = -1; self->selection_end = -1; @@ -862,7 +1047,7 @@ on_select_begin (GtkGestureDrag *drag, gdouble start_x, gdouble start_y, PangoLayout *layout = layout_at (self, &layout_x, &layout_y); if (!layout) { - gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); return; } @@ -881,7 +1066,7 @@ on_select_update (GtkGestureDrag *drag, gdouble offset_x, gdouble offset_y, PangoLayout *layout = g_weak_ref_get (&self->selection); if (!layout) { - gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); return; } @@ -892,7 +1077,7 @@ on_select_update (GtkGestureDrag *drag, gdouble offset_x, gdouble offset_y, if (!layout_coords (self, layout, &x, &y)) { g_warning ("internal error: weakly referenced layout not found"); - gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); goto out; } @@ -916,6 +1101,10 @@ on_select_end (G_GNUC_UNUSED GtkGestureDrag *drag, static void stardict_view_class_init (StardictViewClass *klass) { + view_signals[SEND] = g_signal_new ("send", + G_TYPE_FROM_CLASS (klass), 0, 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = stardict_view_finalize; @@ -923,11 +1112,15 @@ stardict_view_class_init (StardictViewClass *klass) widget_class->get_preferred_height = stardict_view_get_preferred_height; widget_class->get_preferred_width = stardict_view_get_preferred_width; widget_class->realize = stardict_view_realize; + widget_class->map = stardict_view_map; + widget_class->unmap = stardict_view_unmap; widget_class->draw = stardict_view_draw; widget_class->size_allocate = stardict_view_size_allocate; widget_class->screen_changed = stardict_view_screen_changed; widget_class->scroll_event = stardict_view_scroll_event; widget_class->button_press_event = stardict_view_button_press_event; + widget_class->motion_notify_event = stardict_view_motion_notify_event; + widget_class->leave_notify_event = stardict_view_leave_notify_event; gtk_widget_class_set_css_name (widget_class, "stardict-view"); }