1 /*
2 
3   toolbar-standalone-gtk.c: toolbar implementation with GTK+
4 
5   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
6 
7   All rights reserved.
8 
9   Redistribution and use in source and binary forms, with or without
10   modification, are permitted provided that the following conditions
11   are met:
12 
13   1. Redistributions of source code must retain the above copyright
14      notice, this list of conditions and the following disclaimer.
15   2. Redistributions in binary form must reproduce the above copyright
16      notice, this list of conditions and the following disclaimer in the
17      documentation and/or other materials provided with the distribution.
18   3. Neither the name of authors nor the names of its contributors
19      may be used to endorse or promote products derived from this software
20      without specific prior written permission.
21 
22   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
23   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
26   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32   SUCH DAMAGE.
33 
34 */
35 
36 #ifdef HAVE_CONFIG_H
37 #  include <config.h>
38 #endif
39 
40 #include <locale.h>
41 #include <uim/gettext.h>
42 #include <gtk/gtk.h>
43 #include "uim/uim.h"
44 
45 extern GtkWidget *uim_toolbar_standalone_new(void);
46 
47 static gboolean toolbar_dragging = FALSE;
48 static gint window_drag_start_x = -1, window_drag_start_y = -1;
49 static gint pointer_drag_start_x = -1, pointer_drag_start_y = -1;
50 static void size_request_cb(GtkWidget *widget, GtkRequisition *req,
51                             gpointer data);
52 
53 #if GLIB_CHECK_VERSION(2, 6, 0)
54 
55 static void
parse_options(gint argc,gchar ** argv)56 parse_options(gint argc, gchar **argv)
57 {
58 
59 }
60 
61 #endif
62 
63 static void
delete_event(GtkWidget * widget,gpointer data)64 delete_event(GtkWidget *widget, gpointer data)
65 {
66   gtk_main_quit();
67 }
68 
69 static gboolean
button_press_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer data)70 button_press_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
71 {
72   GdkCursor *cursor;
73   GtkWidget *toolbar;
74   gint height, width;
75 #if GTK_CHECK_VERSION(3, 0, 0)
76   GdkDevice *device = gtk_get_current_event_device();
77 #endif
78 
79   /* do nothing unless left mouse button is pressed */
80   if (event->button != 1)
81     return FALSE;
82 
83   switch (event->type) {
84   case GDK_BUTTON_PRESS:
85     cursor = gdk_cursor_new(GDK_FLEUR);
86 #if GTK_CHECK_VERSION(3, 0, 0)
87     gdk_device_grab(device, gtk_widget_get_window(widget),
88                      GDK_OWNERSHIP_NONE, FALSE,
89 #else
90     gdk_pointer_grab(gtk_widget_get_window(widget), FALSE,
91 #endif
92 		     GDK_BUTTON_RELEASE_MASK |
93 		     GDK_POINTER_MOTION_MASK,
94 #if !GTK_CHECK_VERSION(3, 0, 0)
95 		     NULL,
96 #endif
97 		     cursor, event->time);
98 #if GTK_CHECK_VERSION(3, 0, 0)
99     g_object_unref(cursor);
100 #else
101     gdk_cursor_unref(cursor);
102 #endif
103 
104     gtk_window_get_position(GTK_WINDOW(widget),
105 		    	    &window_drag_start_x,
106 			    &window_drag_start_y);
107     pointer_drag_start_x = (gint)event->x_root;
108     pointer_drag_start_y = (gint)event->y_root;
109     toolbar_dragging = TRUE;
110     break;
111   case GDK_2BUTTON_PRESS:
112     toolbar = GTK_WIDGET(data);
113 #if GTK_CHECK_VERSION(2, 18, 0)
114     if (gtk_widget_get_visible(toolbar)) {
115 #else
116     if (GTK_WIDGET_VISIBLE(toolbar)) {
117 #endif
118       gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
119       gtk_widget_hide(toolbar);
120     } else {
121       height = -1;
122       gtk_widget_show(toolbar);
123     }
124 #if GTK_CHECK_VERSION(2, 90, 0)
125     {
126       GtkRequisition minimum_size, natural_size;
127       gtk_widget_get_preferred_size(widget, &minimum_size, &natural_size);
128       if (height > 0)
129         natural_size.height = height;
130       else if (height == 0)
131         natural_size.height = minimum_size.height;
132       size_request_cb(widget, &natural_size, NULL);
133     }
134 #else
135     gtk_widget_set_size_request(widget, -1, height);
136 #endif
137     break;
138   default:
139     break;
140   }
141 
142   return FALSE;
143 }
144 
145 static void
146 helper_win_set_position(GtkWidget *window, gint x, gint y)
147 {
148   gint wx = x, wy = y;
149   gint w, h, sc_w, sc_h;
150 
151   sc_w = gdk_screen_width();
152   sc_h = gdk_screen_height();
153 
154 #if GTK_CHECK_VERSION(2, 90, 0)
155   w = gdk_window_get_width(gtk_widget_get_window(window));
156   h = gdk_window_get_height(gtk_widget_get_window(window));
157 #else
158   gdk_drawable_get_size(gtk_widget_get_window(window), &w, &h);
159 #endif
160 
161   if (wx < 0)
162     wx = 0;
163   else if (wx > sc_w - w)
164     wx = sc_w - w;
165   if (wy < 0)
166     wy = 0;
167   else if (wy > sc_h - h)
168     wy = sc_h -h;
169 
170   g_object_set_data(G_OBJECT(window), "position_x", GINT_TO_POINTER(wx));
171   g_object_set_data(G_OBJECT(window), "position_y", GINT_TO_POINTER(wy));
172   gtk_window_move(GTK_WINDOW(window), wx, wy);
173 }
174 
175 static gboolean
176 motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
177 {
178   if (toolbar_dragging) {
179     gint wx, wy;
180 
181     wx = window_drag_start_x + ((gint)event->x_root - pointer_drag_start_x);
182     wy = window_drag_start_y + ((gint)event->y_root - pointer_drag_start_y);
183 
184     helper_win_set_position(widget, wx, wy);
185 
186     return TRUE;
187   }
188 
189   return FALSE;
190 }
191 
192 static gboolean
193 button_release_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
194 {
195   if (!toolbar_dragging)
196     return FALSE;
197 
198 #if GTK_CHECK_VERSION(3, 0, 0)
199   gdk_device_ungrab(gtk_get_current_event_device(), event->time);
200 #else
201   gdk_pointer_ungrab(event->time);
202 #endif
203 
204   pointer_drag_start_x = -1;
205   pointer_drag_start_y = -1;
206   toolbar_dragging = FALSE;
207 
208   return FALSE;
209 }
210 
211 #if GTK_CHECK_VERSION(2, 90, 0)
212 static gboolean
213 handle_draw_cb(GtkWidget *widget, cairo_t *cr)
214 {
215   gtk_render_handle(gtk_widget_get_style_context(widget), cr,
216 		   0, 0,
217 		   gtk_widget_get_allocated_width(widget),
218 		   gtk_widget_get_allocated_height(widget));
219   return FALSE;
220 }
221 
222 #else
223 static gboolean
224 handle_expose_event_cb(GtkWidget *widget, GdkEventExpose *event)
225 {
226   GdkRectangle *rect = &event->area;
227 
228 #if GTK_CHECK_VERSION(2, 18, 0)
229   GtkAllocation allocation;
230   gtk_widget_get_allocation(widget, &allocation);
231   gtk_paint_handle(gtk_widget_get_style(widget), gtk_widget_get_window(widget),
232 		   GTK_STATE_NORMAL, GTK_SHADOW_OUT,
233 		   rect, widget, "handlebox",
234 		   allocation.x, allocation.y,
235 		   allocation.width, allocation.height,
236 		   GTK_ORIENTATION_VERTICAL);
237 #else
238   gtk_paint_handle(widget->style, widget->window,
239                    GTK_STATE_NORMAL, GTK_SHADOW_OUT,
240                    rect, widget, "handlebox",
241                    widget->allocation.x, widget->allocation.y,
242                    widget->allocation.width, widget->allocation.height,
243                    GTK_ORIENTATION_VERTICAL);
244 #endif
245 
246   return FALSE;
247 }
248 #endif
249 
250 static void
251 size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
252 {
253   gint x, y;
254 
255 #if GTK_CHECK_VERSION(2, 20, 0)
256   if (gtk_widget_get_mapped(widget)) {
257 #else
258   if (GTK_WIDGET_MAPPED(widget)) {
259 #endif
260     gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
261     helper_win_set_position(widget, x, y);
262   }
263 }
264 
265 static void
266 size_request_cb(GtkWidget *widget, GtkRequisition *req, gpointer data)
267 {
268 
269 #if GTK_CHECK_VERSION(2, 20, 0)
270   if (gtk_widget_get_mapped(widget)) {
271 #else
272   if (GTK_WIDGET_MAPPED(widget)) {
273 #endif
274     gint width, height;
275     gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
276 
277     if (width > req->width) {
278       gtk_window_resize(GTK_WINDOW(widget), req->width, req->height);
279     }
280   }
281 }
282 
283 int
284 main(int argc, char *argv[])
285 {
286   GtkWidget *toolbar;
287   GtkWidget *window;
288   GtkWidget *hbox;
289   GtkWidget *handle;
290   GtkWidget *frame;
291 
292   setlocale(LC_ALL, "");
293   bindtextdomain(PACKAGE, LOCALEDIR);
294   textdomain(PACKAGE);
295   bind_textdomain_codeset(PACKAGE, "UTF-8");
296 
297   uim_init();
298 
299   gtk_init(&argc, &argv);
300 
301   window = gtk_window_new(GTK_WINDOW_POPUP);
302 
303   gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DOCK);
304 
305   gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE);
306   gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
307   gtk_window_stick(GTK_WINDOW(window));
308 
309   gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
310 
311   frame = gtk_frame_new(NULL);
312   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
313   gtk_container_add(GTK_CONTAINER(window), frame);
314 
315 #if GTK_CHECK_VERSION(3, 2, 0)
316   hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
317 #else
318   hbox = gtk_hbox_new(FALSE, 0);
319 #endif
320   gtk_container_add(GTK_CONTAINER(frame), hbox);
321 
322   handle = gtk_drawing_area_new();
323   gtk_widget_set_size_request(handle, 8, -1);
324   gtk_box_pack_start(GTK_BOX(hbox), handle, FALSE, FALSE, 0);
325 
326   toolbar = (GtkWidget*)uim_toolbar_standalone_new();
327   gtk_box_pack_start(GTK_BOX(hbox), toolbar, FALSE, FALSE, 0);
328 
329 #if GTK_CHECK_VERSION(2, 90, 0)
330   g_signal_connect(G_OBJECT(handle), "draw",
331 		   G_CALLBACK(handle_draw_cb), NULL);
332 #else
333   g_signal_connect(G_OBJECT(handle), "expose-event",
334 		   G_CALLBACK(handle_expose_event_cb), NULL);
335 #endif
336 
337   g_signal_connect(G_OBJECT(window), "delete_event",
338 		   G_CALLBACK(delete_event), NULL);
339   g_signal_connect(G_OBJECT(window), "button-press-event",
340 		   G_CALLBACK(button_press_event_cb), toolbar);
341   g_signal_connect(G_OBJECT(window), "button-release-event",
342 		   G_CALLBACK(button_release_event_cb), NULL);
343   g_signal_connect(G_OBJECT(window), "motion-notify-event",
344 		   G_CALLBACK(motion_notify_event_cb), NULL);
345   g_signal_connect(G_OBJECT(window), "size-allocate",
346 		   G_CALLBACK(size_allocate_cb), NULL);
347 #if !GTK_CHECK_VERSION(2, 90, 0)
348   g_signal_connect(G_OBJECT(window), "size-request",
349 		   G_CALLBACK(size_request_cb), NULL);
350 #endif
351 
352   gtk_widget_show_all(GTK_WIDGET(window));
353 
354   if (argc > 1) {
355     gint x, y;
356     if (!gtk_window_parse_geometry(GTK_WINDOW(window), argv[1])) {
357 
358 #if GLIB_CHECK_VERSION(2, 6, 0)
359       parse_options(argc, argv);
360 #else
361       g_warning(_("Unable to parse the geometry string '%s'"), argv[1]);
362 #endif
363     }
364     gtk_window_get_position(GTK_WINDOW(window), &x, &y);
365     g_object_set_data(G_OBJECT(window), "position_x", GINT_TO_POINTER(x));
366     g_object_set_data(G_OBJECT(window), "position_y", GINT_TO_POINTER(y));
367   } else {
368     gint x, y, w, h, sc_w, sc_h;
369     gint panel_height = 32; /* FIXME! */
370 
371     gtk_window_get_size(GTK_WINDOW(window), &w, &h);
372     sc_w = gdk_screen_width();
373     sc_h = gdk_screen_height();
374 
375     x = sc_w - w;
376     y = sc_h - h - panel_height; /* FIXME! */
377     helper_win_set_position(window, x, y);
378   }
379 
380   gtk_main();
381 
382   uim_quit();
383 
384   return 0;
385 }
386