1 /* Copyright (C) 2005-2006 Emmanuele Bassi <ebassi@gmail.com>
2 * Copyright (C) 2012-2021 MATE Developers
3 *
4 * Ported from Seth Nickell's Python class.
5 * Copyright (C) 2003 Seth Nickell
6 *
7 * This file is part of MATE Utils.
8 *
9 * MATE Utils is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MATE Utils is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MATE Utils. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32 #include <gdk/gdkx.h>
33
34 #include "gdict-aligned-window.h"
35
36 struct _GdictAlignedWindowPrivate
37 {
38 GtkWidget *align_widget;
39
40 guint motion_id;
41 };
42
43 enum
44 {
45 PROP_0,
46
47 PROP_ALIGN_WIDGET
48 };
49
50 static void gdict_aligned_window_finalize (GObject *object);
51 static void gdict_aligned_window_get_property (GObject *object,
52 guint prop_id,
53 GValue *value,
54 GParamSpec *pspec);
55 static void gdict_aligned_window_set_property (GObject *object,
56 guint prop_id,
57 const GValue *value,
58 GParamSpec *pspec);
59
60 static void gdict_aligned_window_realize (GtkWidget *widget);
61 static void gdict_aligned_window_show (GtkWidget *widget);
62
63 static gboolean gdict_aligned_window_motion_notify_cb (GtkWidget *widget,
64 GdkEventMotion *event,
65 GdictAlignedWindow *aligned_window);
66
67
68 G_DEFINE_TYPE_WITH_PRIVATE (GdictAlignedWindow, gdict_aligned_window, GTK_TYPE_WINDOW);
69
70
71
72 static void
gdict_aligned_window_class_init(GdictAlignedWindowClass * klass)73 gdict_aligned_window_class_init (GdictAlignedWindowClass *klass)
74 {
75 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
76 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
77
78 gobject_class->set_property = gdict_aligned_window_set_property;
79 gobject_class->get_property = gdict_aligned_window_get_property;
80 gobject_class->finalize = gdict_aligned_window_finalize;
81
82 widget_class->realize = gdict_aligned_window_realize;
83 widget_class->show = gdict_aligned_window_show;
84
85 g_object_class_install_property (gobject_class, PROP_ALIGN_WIDGET,
86 g_param_spec_object ("align-widget",
87 "Align Widget",
88 "The widget the window should align to",
89 GTK_TYPE_WIDGET,
90 G_PARAM_READWRITE));
91 }
92
93 static void
gdict_aligned_window_init(GdictAlignedWindow * aligned_window)94 gdict_aligned_window_init (GdictAlignedWindow *aligned_window)
95 {
96 GdictAlignedWindowPrivate *priv = gdict_aligned_window_get_instance_private (aligned_window);
97 GtkWindow *window = GTK_WINDOW (aligned_window);
98
99 aligned_window->priv = priv;
100
101 priv->align_widget = NULL;
102 priv->motion_id = 0;
103
104 /* set window properties */
105 #if 0
106 gtk_window_set_modal (window, TRUE);
107 #endif
108 gtk_window_set_decorated (window, FALSE);
109 gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_DOCK);
110 }
111
112 static void
gdict_aligned_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)113 gdict_aligned_window_get_property (GObject *object,
114 guint prop_id,
115 GValue *value,
116 GParamSpec *pspec)
117 {
118 GdictAlignedWindow *aligned_window = GDICT_ALIGNED_WINDOW (object);
119
120 switch (prop_id)
121 {
122 case PROP_ALIGN_WIDGET:
123 g_value_set_object (value, aligned_window->priv->align_widget);
124 break;
125 default:
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127 break;
128 }
129 }
130
131 static void
gdict_aligned_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)132 gdict_aligned_window_set_property (GObject *object,
133 guint prop_id,
134 const GValue *value,
135 GParamSpec *pspec)
136 {
137 GdictAlignedWindow *aligned_window = GDICT_ALIGNED_WINDOW (object);
138
139 switch (prop_id)
140 {
141 case PROP_ALIGN_WIDGET:
142 gdict_aligned_window_set_widget (aligned_window,
143 g_value_get_object (value));
144 break;
145 default:
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 break;
148 }
149 }
150
151 static void
gdict_aligned_window_position(GdictAlignedWindow * window)152 gdict_aligned_window_position (GdictAlignedWindow *window)
153 {
154 GdictAlignedWindowPrivate *priv;
155 GtkWidget *align_widget;
156 gint our_width, our_height;
157 gint entry_x, entry_y, entry_width, entry_height;
158 gint x, y;
159 GdkGravity gravity = GDK_GRAVITY_NORTH_WEST;
160 GdkWindow *gdk_window;
161 GdkDisplay *display;
162
163 g_assert (GDICT_IS_ALIGNED_WINDOW (window));
164 priv = window->priv;
165
166 if (!priv->align_widget)
167 return;
168
169 align_widget = priv->align_widget;
170 gdk_window = gtk_widget_get_window (align_widget);
171
172 display = gdk_display_get_default ();
173 gdk_display_flush (display);
174
175 gdk_window_get_geometry (gtk_widget_get_window (GTK_WIDGET (window)), NULL, NULL, &our_width, &our_height);
176
177 /* stick, skip taskbar and pager */
178 gtk_window_stick (GTK_WINDOW (window));
179 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
180 gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
181
182 /* make sure the align_widget is realized before we do anything */
183 gtk_widget_realize (align_widget);
184
185 /* get the positional and dimensional attributes of the align widget */
186 gdk_window_get_origin (gdk_window,
187 &entry_x,
188 &entry_y);
189 gdk_window_get_geometry (gdk_window, NULL, NULL, &entry_width, &entry_height);
190
191 if (entry_x + our_width < WidthOfScreen (gdk_x11_screen_get_xscreen (gdk_screen_get_default ())))
192 x = entry_x + 1;
193 else
194 {
195 x = entry_x + entry_width - our_width - 1;
196
197 gravity = GDK_GRAVITY_NORTH_EAST;
198 }
199
200 if (entry_y + entry_height + our_height < HeightOfScreen (gdk_x11_screen_get_xscreen (gdk_screen_get_default ())))
201 y = entry_y + entry_height - 1;
202 else
203 {
204 y = entry_y - our_height + 1;
205
206 if (gravity == GDK_GRAVITY_NORTH_EAST)
207 gravity = GDK_GRAVITY_SOUTH_EAST;
208 else
209 gravity = GDK_GRAVITY_SOUTH_WEST;
210 }
211
212 gtk_window_set_gravity (GTK_WINDOW (window), gravity);
213 gtk_window_move (GTK_WINDOW (window), x, y);
214 }
215
216 static void
gdict_aligned_window_realize(GtkWidget * widget)217 gdict_aligned_window_realize (GtkWidget *widget)
218 {
219 GTK_WIDGET_CLASS (gdict_aligned_window_parent_class)->realize (widget);
220
221 gdict_aligned_window_position (GDICT_ALIGNED_WINDOW (widget));
222 }
223
224 static void
gdict_aligned_window_show(GtkWidget * widget)225 gdict_aligned_window_show (GtkWidget *widget)
226 {
227 gdict_aligned_window_position (GDICT_ALIGNED_WINDOW (widget));
228
229 GTK_WIDGET_CLASS (gdict_aligned_window_parent_class)->show (widget);
230 }
231
232 static void
gdict_aligned_window_finalize(GObject * object)233 gdict_aligned_window_finalize (GObject *object)
234 {
235 G_OBJECT_CLASS (gdict_aligned_window_parent_class)->finalize (object);
236 }
237
238 static gboolean
gdict_aligned_window_motion_notify_cb(GtkWidget * widget,GdkEventMotion * event,GdictAlignedWindow * aligned_window)239 gdict_aligned_window_motion_notify_cb (GtkWidget *widget,
240 GdkEventMotion *event,
241 GdictAlignedWindow *aligned_window)
242 {
243 GtkAllocation alloc;
244 GdkRectangle rect;
245
246 gtk_widget_get_allocation (GTK_WIDGET (aligned_window), &alloc);
247
248 rect.x = 0;
249 rect.y = 0;
250 rect.width = alloc.width;
251 rect.height = alloc.height;
252
253 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (aligned_window)),
254 &rect,
255 FALSE);
256
257 return FALSE;
258 }
259
260
261 /**
262 * gdict_aligned_window_new:
263 * @align_widget: a #GtkWidget to which the window should align
264 *
265 * Creates a new window, aligned to a previously created widget.
266 *
267 * Return value: a new #GdictAlignedWindow
268 */
269 GtkWidget *
gdict_aligned_window_new(GtkWidget * align_widget)270 gdict_aligned_window_new (GtkWidget *align_widget)
271 {
272 return g_object_new (GDICT_TYPE_ALIGNED_WINDOW,
273 "align-widget", align_widget,
274 NULL);
275 }
276
277 /**
278 * gdict_aligned_window_set_widget:
279 * @aligned_window: a #GdictAlignedWindow
280 * @align_widget: the #GtkWidget @aligned_window should align to
281 *
282 * Sets @align_widget as the #GtkWidget to which @aligned_window should
283 * align.
284 *
285 * Note that @align_widget must have a #GdkWindow in order to
286 * #GdictAlignedWindow to work.
287 */
288 void
gdict_aligned_window_set_widget(GdictAlignedWindow * aligned_window,GtkWidget * align_widget)289 gdict_aligned_window_set_widget (GdictAlignedWindow *aligned_window,
290 GtkWidget *align_widget)
291 {
292 GdictAlignedWindowPrivate *priv;
293
294 g_return_if_fail (GDICT_IS_ALIGNED_WINDOW (aligned_window));
295 g_return_if_fail (GTK_IS_WIDGET (align_widget));
296
297 #if 0
298 if (GTK_WIDGET_NO_WINDOW (align_widget))
299 {
300 g_warning ("Attempting to set a widget of class '%s' as the "
301 "align widget, but widgets of this class does not "
302 "have a GdkWindow.",
303 g_type_name (G_OBJECT_TYPE (align_widget)));
304
305 return;
306 }
307 #endif
308
309 priv = gdict_aligned_window_get_instance_private (aligned_window);
310
311 if (priv->align_widget)
312 {
313 g_signal_handler_disconnect (priv->align_widget, priv->motion_id);
314 priv->align_widget = NULL;
315 }
316
317 priv->align_widget = align_widget;
318 priv->motion_id = g_signal_connect (priv->align_widget, "motion-notify-event",
319 G_CALLBACK (gdict_aligned_window_motion_notify_cb),
320 aligned_window);
321 }
322
323 /**
324 * gdict_aligned_window_get_widget:
325 * @aligned_window: a #GdictAlignedWindow
326 *
327 * Retrieves the #GtkWidget to which @aligned_window is aligned to.
328 *
329 * Return value: the align widget.
330 */
331 GtkWidget *
gdict_aligned_window_get_widget(GdictAlignedWindow * aligned_window)332 gdict_aligned_window_get_widget (GdictAlignedWindow *aligned_window)
333 {
334 g_return_val_if_fail (GDICT_IS_ALIGNED_WINDOW (aligned_window), NULL);
335
336 return aligned_window->priv->align_widget;
337 }
338