1 /*
2
3 Copyright (c) 2005-2013 uim Project https://github.com/uim/uim
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. Neither the name of authors nor the names of its contributors
17 may be used to endorse or promote products derived from this software
18 without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31 */
32
33 #include <config.h>
34
35 #include <gtk/gtk.h>
36 #include <string.h>
37
38 #include "uim/uim.h"
39 #include "uim/uim-helper.h"
40 #include "uim/gettext.h"
41
42 #include "caret-state-indicator.h"
43 /*
44 * caret state indicator is a state indicator nearby the caret.
45 */
46
47 #define DEFAULT_WINDOW_WIDTH 20
48 #define DEFAULT_WINDOW_HEIGHT 20
49
50 static gint get_current_time(void);
51 static gint caret_state_indicator_timeout(gpointer data);
52
53 /* This function is not correct, size of tv_sec is glong, not gint */
54 static gint
get_current_time(void)55 get_current_time(void)
56 {
57 GTimeVal result;
58
59 g_get_current_time(&result);
60 return result.tv_sec;
61 }
62
63 static gint
caret_state_indicator_timeout(gpointer data)64 caret_state_indicator_timeout(gpointer data)
65 {
66 GtkWidget *window = GTK_WIDGET(data);
67 gint timeout, called_time, current_time;
68
69 timeout = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "timeout"));
70 called_time = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window),
71 "called_time"));
72 current_time = get_current_time();
73
74 if ((current_time - called_time) * 1000 >= timeout)
75 gtk_widget_hide(window);
76
77 g_object_set_data(G_OBJECT(window), "timeout-tag", GUINT_TO_POINTER(0));
78
79 return FALSE;
80 }
81
82 static gint
83 #if GTK_CHECK_VERSION(2, 90, 0)
caret_state_indicator_paint_window(GtkWidget * window,cairo_t * cr)84 caret_state_indicator_paint_window(GtkWidget *window, cairo_t *cr)
85 #else
86 caret_state_indicator_paint_window(GtkWidget *window)
87 #endif
88 {
89 #if GTK_CHECK_VERSION(2, 90, 0)
90 gtk_render_frame(gtk_widget_get_style_context(window), cr,
91 0, 0,
92 gtk_widget_get_allocated_width(window),
93 gtk_widget_get_allocated_height(window));
94 #else
95 gtk_paint_flat_box(gtk_widget_get_style(window),
96 gtk_widget_get_window(window),
97 GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, GTK_WIDGET(window),
98 "tooltip", 0, 0, -1, -1);
99 #endif
100
101 return FALSE;
102 }
103
104 static gint
caret_state_indicator_destroy_cb(GtkWidget * window)105 caret_state_indicator_destroy_cb(GtkWidget *window)
106 {
107 GList *label_list, *frame_list;
108
109 label_list = g_object_get_data(G_OBJECT(window), "labels");
110 frame_list = g_object_get_data(G_OBJECT(window), "frames");
111
112 g_list_free(label_list);
113 g_list_free(frame_list);
114
115 return FALSE;
116 }
117
118 GtkWidget *
caret_state_indicator_new(void)119 caret_state_indicator_new(void)
120 {
121 GtkWidget *window, *label, *hbox, *frame;
122 GList *label_list = NULL, *frame_list = NULL;
123
124 window = gtk_window_new(GTK_WINDOW_POPUP);
125 label = gtk_label_new("");
126 frame = gtk_frame_new(NULL);
127 gtk_container_add(GTK_CONTAINER(frame), label);
128 #if GTK_CHECK_VERSION(3, 2, 0)
129 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
130 #else
131 hbox = gtk_hbox_new(TRUE, 0);
132 #endif
133 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
134 gtk_container_add(GTK_CONTAINER(window), hbox);
135
136 gtk_window_set_default_size(GTK_WINDOW(window),
137 DEFAULT_WINDOW_WIDTH,
138 DEFAULT_WINDOW_HEIGHT);
139 gtk_widget_set_app_paintable(window, TRUE);
140
141 #if GTK_CHECK_VERSION(2, 90, 0)
142 g_signal_connect(window, "draw",
143 G_CALLBACK(caret_state_indicator_paint_window),
144 NULL);
145 #else
146 g_signal_connect(window, "expose_event",
147 G_CALLBACK(caret_state_indicator_paint_window),
148 NULL);
149 #endif
150 g_signal_connect(window, "destroy",
151 G_CALLBACK(caret_state_indicator_destroy_cb),
152 NULL);
153
154 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
155
156 label_list = g_list_append(label_list, label);
157 frame_list = g_list_append(frame_list, frame);
158 g_object_set_data(G_OBJECT(window), "frames", frame_list);
159 g_object_set_data(G_OBJECT(window), "labels", label_list);
160 g_object_set_data(G_OBJECT(window), "hbox", hbox);
161
162 return window;
163 }
164
165 void
caret_state_indicator_update(GtkWidget * window,gint topwin_x,gint topwin_y,const gchar * str)166 caret_state_indicator_update(GtkWidget *window, gint topwin_x, gint topwin_y, const gchar *str)
167 {
168 gint cursor_x, cursor_y;
169
170 g_return_if_fail(window != NULL);
171
172 cursor_x = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "cursor_x"));
173 cursor_y = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "cursor_y"));
174
175 if (str) {
176 gchar **cols;
177 GtkWidget *label, *hbox, *frame;
178 GList *label_list, *frame_list, *list1, *list2;
179 gint i;
180
181 list1 = label_list = g_object_get_data(G_OBJECT(window), "labels");
182 list2 = frame_list = g_object_get_data(G_OBJECT(window), "frames");
183 hbox = g_object_get_data(G_OBJECT(window), "hbox");
184
185 cols = g_strsplit(str, "\t", 0);
186 for (i = 0; cols[i] && strcmp("", cols[i]); i++) {
187 if (label_list) {
188 label = label_list->data;
189 gtk_label_set_text(GTK_LABEL(label), cols[i]);
190 } else {
191 label = gtk_label_new(cols[i]);
192 frame = gtk_frame_new(NULL);
193 gtk_container_add(GTK_CONTAINER(frame), label);
194 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
195 list1 = g_list_append(list1, label);
196 label_list = g_list_find(list1, label);
197 list2 = g_list_append(list2, frame);
198 frame_list = g_list_find(list2, frame);
199 }
200 label_list = label_list->next;
201 frame_list = frame_list->next;
202 }
203
204 while (label_list) {
205 label = label_list->data;
206 frame = frame_list->data;
207 label_list = label_list->next;
208 frame_list = frame_list->next;
209 gtk_container_remove(GTK_CONTAINER(frame), label);
210 gtk_container_remove(GTK_CONTAINER(hbox), frame);
211 list1 = g_list_remove(list1, label);
212 list2 = g_list_remove(list2, frame);
213 }
214 g_object_set_data(G_OBJECT(window), "labels", list1);
215 g_object_set_data(G_OBJECT(window), "frames", list2);
216
217 g_strfreev(cols);
218 }
219
220 gtk_window_move(GTK_WINDOW(window), topwin_x + cursor_x,
221 topwin_y + cursor_y + 3);
222 }
223
224 void
caret_state_indicator_set_cursor_location(GtkWidget * window,GdkRectangle * cursor_location)225 caret_state_indicator_set_cursor_location(GtkWidget *window, GdkRectangle *cursor_location)
226 {
227 g_return_if_fail(window != NULL);
228
229 g_object_set_data(G_OBJECT(window), "cursor_x",
230 GINT_TO_POINTER(cursor_location->x));
231 g_object_set_data(G_OBJECT(window), "cursor_y",
232 GINT_TO_POINTER(cursor_location->y +
233 cursor_location->height));
234 }
235
236 void
caret_state_indicator_set_timeout(GtkWidget * window,gint timeout)237 caret_state_indicator_set_timeout(GtkWidget *window, gint timeout)
238 {
239 gint current_time;
240 guint tag, oldtag;
241
242 g_return_if_fail(window != NULL);
243
244 oldtag = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(window), "timeout-tag"));
245
246 if (oldtag > 0)
247 g_source_remove(oldtag);
248
249 current_time = get_current_time();
250 tag = g_timeout_add(timeout, caret_state_indicator_timeout, (gpointer)window);
251
252 g_object_set_data(G_OBJECT(window), "timeout-tag", GUINT_TO_POINTER(tag));
253 g_object_set_data(G_OBJECT(window), "timeout", GINT_TO_POINTER(timeout));
254 /* "called_time" stores the latest time when this function is called */
255 g_object_set_data(G_OBJECT(window), "called_time",
256 GINT_TO_POINTER(current_time));
257 }
258