1 /*
2 * tooltip.c
3 *
4 * Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "common.h"
24
25 /* Wordchars defining possible signs in an expression. */
26 #define SCOPE_EXPR_WORDCHARS "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.->[]"
27
tooltip_trigger(void)28 static void tooltip_trigger(void)
29 {
30 GdkDisplay *display = gdk_display_get_default();
31 #if GTK_CHECK_VERSION(3, 0, 0)
32 GdkDeviceManager *manager = gdk_display_get_device_manager(display);
33 GdkDevice *device = gdk_device_manager_get_client_pointer(manager);
34 GdkWindow *window = gdk_device_get_window_at_position(device, NULL, NULL);
35 #else
36 GdkWindow *window = gdk_display_get_window_at_pointer(display, NULL, NULL);
37 #endif
38 GeanyDocument *doc = document_get_current();
39
40 if (doc && window)
41 {
42 GtkWidget *event_widget;
43
44 gdk_window_get_user_data(window, (void **) &event_widget);
45 /* if you know a better working way, do not hesistate to tell me */
46 if (event_widget &&
47 gtk_widget_is_ancestor(event_widget, GTK_WIDGET(doc->editor->sci)))
48 {
49 gtk_tooltip_trigger_tooltip_query(display);
50 }
51 }
52 }
53
54 static gchar *last_expr = NULL;
55 static gchar *output = NULL;
56 static gint last_pos = -1;
57 static gint peek_pos = -1;
58 static gboolean show;
59
tooltip_set(gchar * text)60 static void tooltip_set(gchar *text)
61 {
62 show = text != NULL;
63 g_free(output);
64 output = text;
65 last_pos = peek_pos;
66
67 if (show)
68 {
69 if (pref_tooltips_length && strlen(output) > (size_t) pref_tooltips_length + 3)
70 strcpy(output + pref_tooltips_length, "...");
71
72 tooltip_trigger();
73 }
74 }
75
tooltip_set_expr(gchar * expr,gchar * text)76 static void tooltip_set_expr(gchar *expr, gchar *text)
77 {
78 show = text != NULL;
79 g_free(output);
80 output = g_strdup_printf ("%s =\n %s", expr, text);
81 g_free(text);
82 g_free(expr);
83 last_pos = peek_pos;
84
85 if (show)
86 {
87 if (pref_tooltips_length && strlen(output) > (size_t) pref_tooltips_length + 3)
88 strcpy(output + pref_tooltips_length, "...");
89
90 tooltip_trigger();
91 }
92 }
93
94 static gint scid_gen = 0;
95
on_tooltip_error(GArray * nodes)96 void on_tooltip_error(GArray *nodes)
97 {
98 if (atoi(parse_grab_token(nodes)) == scid_gen)
99 {
100 if (pref_tooltips_fail_action == 1)
101 tooltip_set(parse_get_error(nodes));
102 else
103 {
104 tooltip_set(NULL);
105 if (pref_tooltips_fail_action)
106 plugin_blink();
107 }
108 }
109 }
110
111 static char *input = NULL;
112
on_tooltip_value(GArray * nodes)113 void on_tooltip_value(GArray *nodes)
114 {
115 if (atoi(parse_grab_token(nodes)) == scid_gen)
116 {
117 tooltip_set_expr(last_expr, parse_get_display_from_7bit(parse_lead_value(nodes),
118 parse_mode_get(input, MODE_HBIT), parse_mode_get(input, MODE_MEMBER)));
119 }
120 }
121
122 static guint query_id = 0;
123
tooltip_launch(gpointer gdata)124 static gboolean tooltip_launch(gpointer gdata)
125 {
126 GeanyDocument *doc = document_get_current();
127
128 if (doc && utils_source_document(doc) && doc->editor == gdata &&
129 (debug_state() & DS_SENDABLE))
130 {
131 ScintillaObject *sci = doc->editor->sci;
132 gchar *expr;
133 if (sci_get_selection_mode(sci) == SC_SEL_STREAM &&
134 peek_pos >= sci_get_selection_start(sci) &&
135 peek_pos < sci_get_selection_end(sci))
136 {
137 expr = editor_get_default_selection(doc->editor, FALSE, NULL);
138 }
139 else
140 {
141 expr = utils_read_evaluate_expr(doc->editor, peek_pos);
142 }
143
144 if ((expr = utils_verify_selection(expr)) != NULL)
145 {
146 g_free(input);
147 input = debug_send_evaluate('3', scid_gen, expr);
148 last_expr = expr;
149 }
150 else
151 tooltip_set(NULL);
152 }
153 else
154 tooltip_set(NULL);
155
156 query_id = 0;
157 return FALSE;
158 }
159
on_query_tooltip(G_GNUC_UNUSED GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,GeanyEditor * editor)160 static gboolean on_query_tooltip(G_GNUC_UNUSED GtkWidget *widget, gint x, gint y,
161 gboolean keyboard_mode, GtkTooltip *tooltip, GeanyEditor *editor)
162 {
163 gint pos = keyboard_mode ? sci_get_current_position(editor->sci) :
164 scintilla_send_message(editor->sci, SCI_POSITIONFROMPOINT, x, y);
165
166 if (pos >= 0)
167 {
168 if (pos == last_pos)
169 {
170 gtk_tooltip_set_text(tooltip, output);
171 return show;
172 }
173 else if (pos != peek_pos)
174 {
175 if (query_id)
176 g_source_remove(query_id);
177 else
178 scid_gen++;
179
180 peek_pos = pos;
181 query_id = plugin_timeout_add(geany_plugin, pref_tooltips_send_delay * 10,
182 tooltip_launch, editor);
183 }
184 }
185
186 return FALSE;
187 }
188
tooltip_attach(GeanyEditor * editor)189 void tooltip_attach(GeanyEditor *editor)
190 {
191 if (option_editor_tooltips)
192 {
193 gtk_widget_set_has_tooltip(GTK_WIDGET(editor->sci), TRUE);
194 g_signal_connect(editor->sci, "query-tooltip", G_CALLBACK(on_query_tooltip), editor);
195 }
196 }
197
tooltip_remove(GeanyEditor * editor)198 void tooltip_remove(GeanyEditor *editor)
199 {
200 GtkWidget *widget = GTK_WIDGET(editor->sci);
201
202 if (gtk_widget_get_has_tooltip(widget))
203 {
204 gulong query_tooltip_id = g_signal_handler_find(widget, G_SIGNAL_MATCH_ID,
205 g_signal_lookup("query-tooltip", GTK_TYPE_WIDGET), 0, NULL, NULL, NULL);
206
207 if (query_tooltip_id)
208 g_signal_handler_disconnect(widget, query_tooltip_id);
209 gtk_widget_set_has_tooltip(widget, FALSE);
210 }
211 }
212
tooltip_clear(void)213 void tooltip_clear(void)
214 {
215 scid_gen = 0;
216 last_pos = -1;
217 peek_pos = -1;
218 }
219
tooltip_update(void)220 gboolean tooltip_update(void)
221 {
222 if (option_editor_tooltips)
223 {
224 last_pos = -1;
225 peek_pos = -1;
226 tooltip_trigger();
227 }
228 return TRUE;
229 }
230
tooltip_finalize(void)231 void tooltip_finalize(void)
232 {
233 g_free(output);
234 g_free(input);
235 }
236