1 /* GTK - The GIMP Toolkit
2  * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 #include "gtk/gtk.h"
20 #include "gtkmagnifierprivate.h"
21 #include "gtkintl.h"
22 
23 enum {
24   PROP_INSPECTED = 1,
25   PROP_RESIZE,
26   PROP_MAGNIFICATION
27 };
28 
29 typedef struct _GtkMagnifierPrivate GtkMagnifierPrivate;
30 
31 struct _GtkMagnifierPrivate
32 {
33   GtkWidget *inspected;
34   gdouble magnification;
35   gint x;
36   gint y;
37   gboolean resize;
38   gulong draw_handler;
39   gulong resize_handler;
40 };
41 
G_DEFINE_TYPE_WITH_PRIVATE(GtkMagnifier,_gtk_magnifier,GTK_TYPE_WIDGET)42 G_DEFINE_TYPE_WITH_PRIVATE (GtkMagnifier, _gtk_magnifier,
43                             GTK_TYPE_WIDGET)
44 
45 static void
46 _gtk_magnifier_set_property (GObject      *object,
47                              guint         param_id,
48                              const GValue *value,
49                              GParamSpec   *pspec)
50 {
51   switch (param_id)
52     {
53     case PROP_INSPECTED:
54       _gtk_magnifier_set_inspected (GTK_MAGNIFIER (object),
55                                     g_value_get_object (value));
56       break;
57     case PROP_MAGNIFICATION:
58       _gtk_magnifier_set_magnification (GTK_MAGNIFIER (object),
59                                         g_value_get_double (value));
60       break;
61     case PROP_RESIZE:
62       _gtk_magnifier_set_resize (GTK_MAGNIFIER (object),
63                                  g_value_get_boolean (value));
64       break;
65     default:
66       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
67     }
68 }
69 
70 static void
_gtk_magnifier_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)71 _gtk_magnifier_get_property (GObject    *object,
72                              guint       param_id,
73                              GValue     *value,
74                              GParamSpec *pspec)
75 {
76   GtkMagnifier *magnifier;
77   GtkMagnifierPrivate *priv;
78 
79   magnifier = GTK_MAGNIFIER (object);
80   priv = _gtk_magnifier_get_instance_private (magnifier);
81 
82   switch (param_id)
83     {
84     case PROP_INSPECTED:
85       g_value_set_object (value, priv->inspected);
86       break;
87     case PROP_MAGNIFICATION:
88       g_value_set_double (value, priv->magnification);
89       break;
90     case PROP_RESIZE:
91       g_value_set_boolean (value, priv->resize);
92       break;
93     default:
94       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
95     }
96 }
97 
98 static gboolean
_gtk_magnifier_draw(GtkWidget * widget,cairo_t * cr)99 _gtk_magnifier_draw (GtkWidget *widget,
100                      cairo_t   *cr)
101 {
102   GtkAllocation allocation, inspected_alloc;
103   GtkMagnifier *magnifier;
104   GtkMagnifierPrivate *priv;
105   gdouble x, y;
106 
107   magnifier = GTK_MAGNIFIER (widget);
108   priv = _gtk_magnifier_get_instance_private (magnifier);
109 
110   if (priv->inspected == NULL)
111     return FALSE;
112 
113   if (!gtk_widget_is_visible (priv->inspected))
114     return FALSE;
115 
116   gtk_widget_get_allocation (widget, &allocation);
117   gtk_widget_get_allocation (priv->inspected, &inspected_alloc);
118 
119   if (!priv->resize)
120     cairo_translate (cr, allocation.width / 2, allocation.height / 2);
121 
122   x = CLAMP (priv->x, 0, inspected_alloc.width);
123   y = CLAMP (priv->y, 0, inspected_alloc.height);
124 
125   cairo_save (cr);
126   cairo_scale (cr, priv->magnification, priv->magnification);
127   cairo_translate (cr, -x, -y);
128   g_signal_handler_block (priv->inspected, priv->draw_handler);
129   gtk_widget_draw (priv->inspected, cr);
130   g_signal_handler_unblock (priv->inspected, priv->draw_handler);
131   cairo_restore (cr);
132 
133   return TRUE;
134 }
135 
136 static void
gtk_magnifier_get_preferred_width(GtkWidget * widget,gint * minimum_width,gint * natural_width)137 gtk_magnifier_get_preferred_width (GtkWidget *widget,
138                                    gint      *minimum_width,
139                                    gint      *natural_width)
140 {
141   GtkMagnifier *magnifier;
142   GtkMagnifierPrivate *priv;
143   gint width;
144 
145   magnifier = GTK_MAGNIFIER (widget);
146   priv = _gtk_magnifier_get_instance_private (magnifier);
147 
148   if (priv->resize && priv->inspected)
149     width = priv->magnification * gtk_widget_get_allocated_width (priv->inspected);
150   else
151     width = 0;
152 
153   *minimum_width = width;
154   *natural_width = width;
155 }
156 
157 static void
gtk_magnifier_get_preferred_height(GtkWidget * widget,gint * minimum_height,gint * natural_height)158 gtk_magnifier_get_preferred_height (GtkWidget *widget,
159                                     gint      *minimum_height,
160                                     gint      *natural_height)
161 {
162   GtkMagnifier *magnifier;
163   GtkMagnifierPrivate *priv;
164   gint height;
165 
166   magnifier = GTK_MAGNIFIER (widget);
167   priv = _gtk_magnifier_get_instance_private (magnifier);
168 
169   if (priv->resize && priv->inspected)
170     height = priv->magnification * gtk_widget_get_allocated_height (priv->inspected);
171   else
172     height = 0;
173 
174   *minimum_height = height;
175   *natural_height = height;
176 }
177 
178 static void
resize_handler(GtkWidget * widget,GtkAllocation * alloc,GtkWidget * magnifier)179 resize_handler (GtkWidget     *widget,
180                 GtkAllocation *alloc,
181                 GtkWidget     *magnifier)
182 {
183   gtk_widget_queue_resize (magnifier);
184 }
185 
186 static void
connect_resize_handler(GtkMagnifier * magnifier)187 connect_resize_handler (GtkMagnifier *magnifier)
188 {
189   GtkMagnifierPrivate *priv;
190 
191   priv = _gtk_magnifier_get_instance_private (magnifier);
192 
193   if (priv->inspected && priv->resize)
194     priv->resize_handler = g_signal_connect (priv->inspected, "size-allocate",
195                                              G_CALLBACK (resize_handler), magnifier);
196 }
197 
198 static void
disconnect_resize_handler(GtkMagnifier * magnifier)199 disconnect_resize_handler (GtkMagnifier *magnifier)
200 {
201   GtkMagnifierPrivate *priv;
202 
203   priv = _gtk_magnifier_get_instance_private (magnifier);
204 
205   if (priv->resize_handler)
206     {
207       if (priv->inspected)
208         g_signal_handler_disconnect (priv->inspected, priv->resize_handler);
209       priv->resize_handler = 0;
210     }
211 }
212 
213 static gboolean
draw_handler(GtkWidget * widget,cairo_t * cr,GtkWidget * magnifier)214 draw_handler (GtkWidget     *widget,
215               cairo_t       *cr,
216               GtkWidget     *magnifier)
217 {
218   gtk_widget_queue_draw (magnifier);
219   return FALSE;
220 }
221 
222 static void
connect_draw_handler(GtkMagnifier * magnifier)223 connect_draw_handler (GtkMagnifier *magnifier)
224 {
225   GtkMagnifierPrivate *priv;
226 
227   priv = _gtk_magnifier_get_instance_private (magnifier);
228 
229   if (!priv->draw_handler)
230     {
231       if (priv->inspected)
232         priv->draw_handler = g_signal_connect (priv->inspected, "draw",
233                                                G_CALLBACK (draw_handler), magnifier);
234     }
235 }
236 
237 static void
disconnect_draw_handler(GtkMagnifier * magnifier)238 disconnect_draw_handler (GtkMagnifier *magnifier)
239 {
240   GtkMagnifierPrivate *priv;
241 
242   priv = _gtk_magnifier_get_instance_private (magnifier);
243 
244   if (priv->draw_handler)
245     {
246       if (priv->inspected)
247         g_signal_handler_disconnect (priv->inspected, priv->draw_handler);
248       priv->draw_handler = 0;
249     }
250 }
251 
252 static void
_gtk_magnifier_destroy(GtkWidget * widget)253 _gtk_magnifier_destroy (GtkWidget *widget)
254 {
255   _gtk_magnifier_set_inspected (GTK_MAGNIFIER (widget), NULL);
256 
257   GTK_WIDGET_CLASS (_gtk_magnifier_parent_class)->destroy (widget);
258 }
259 
260 static void
gtk_magnifier_map(GtkWidget * widget)261 gtk_magnifier_map (GtkWidget *widget)
262 {
263   connect_draw_handler (GTK_MAGNIFIER (widget));
264 
265   GTK_WIDGET_CLASS (_gtk_magnifier_parent_class)->map (widget);
266 }
267 
268 static void
gtk_magnifier_unmap(GtkWidget * widget)269 gtk_magnifier_unmap (GtkWidget *widget)
270 {
271   GTK_WIDGET_CLASS (_gtk_magnifier_parent_class)->unmap (widget);
272 
273   disconnect_draw_handler (GTK_MAGNIFIER (widget));
274 }
275 
276 static void
_gtk_magnifier_class_init(GtkMagnifierClass * klass)277 _gtk_magnifier_class_init (GtkMagnifierClass *klass)
278 {
279   GObjectClass *object_class = G_OBJECT_CLASS (klass);
280   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
281 
282   object_class->set_property = _gtk_magnifier_set_property;
283   object_class->get_property = _gtk_magnifier_get_property;
284 
285   widget_class->destroy = _gtk_magnifier_destroy;
286   widget_class->draw = _gtk_magnifier_draw;
287   widget_class->get_preferred_width = gtk_magnifier_get_preferred_width;
288   widget_class->get_preferred_height = gtk_magnifier_get_preferred_height;
289   widget_class->map = gtk_magnifier_map;
290   widget_class->unmap = gtk_magnifier_unmap;
291 
292   g_object_class_install_property (object_class,
293                                    PROP_INSPECTED,
294                                    g_param_spec_object ("inspected",
295                                                         P_("Inspected"),
296                                                         P_("Inspected widget"),
297                                                         GTK_TYPE_WIDGET,
298                                                         G_PARAM_READWRITE));
299   g_object_class_install_property (object_class,
300                                    PROP_MAGNIFICATION,
301                                    g_param_spec_double ("magnification",
302                                                         P_("magnification"),
303                                                         P_("magnification"),
304                                                         1, G_MAXDOUBLE, 1,
305                                                         G_PARAM_READWRITE));
306   g_object_class_install_property (object_class,
307                                    PROP_RESIZE,
308                                    g_param_spec_boolean ("resize",
309                                                          P_("resize"),
310                                                          P_("resize"),
311                                                          FALSE,
312                                                          G_PARAM_READWRITE));
313 }
314 
315 static void
_gtk_magnifier_init(GtkMagnifier * magnifier)316 _gtk_magnifier_init (GtkMagnifier *magnifier)
317 {
318   GtkWidget *widget = GTK_WIDGET (magnifier);
319   GtkMagnifierPrivate *priv;
320 
321   priv = _gtk_magnifier_get_instance_private (magnifier);
322 
323   gtk_widget_set_events (widget,
324                          gtk_widget_get_events (widget));
325 
326   gtk_widget_set_has_window (widget, FALSE);
327   priv->magnification = 1;
328   priv->resize = FALSE;
329 }
330 
331 GtkWidget *
_gtk_magnifier_new(GtkWidget * inspected)332 _gtk_magnifier_new (GtkWidget *inspected)
333 {
334   g_return_val_if_fail (GTK_IS_WIDGET (inspected), NULL);
335 
336   return g_object_new (GTK_TYPE_MAGNIFIER,
337                        "inspected", inspected,
338                        NULL);
339 }
340 
341 GtkWidget *
_gtk_magnifier_get_inspected(GtkMagnifier * magnifier)342 _gtk_magnifier_get_inspected (GtkMagnifier *magnifier)
343 {
344   GtkMagnifierPrivate *priv;
345 
346   g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), NULL);
347 
348   priv = _gtk_magnifier_get_instance_private (magnifier);
349 
350   return priv->inspected;
351 }
352 
353 void
_gtk_magnifier_set_inspected(GtkMagnifier * magnifier,GtkWidget * inspected)354 _gtk_magnifier_set_inspected (GtkMagnifier *magnifier,
355                               GtkWidget    *inspected)
356 {
357   GtkMagnifierPrivate *priv;
358 
359   g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
360 
361   priv = _gtk_magnifier_get_instance_private (magnifier);
362 
363   if (priv->inspected == inspected)
364     return;
365 
366   disconnect_draw_handler (magnifier);
367   disconnect_resize_handler (magnifier);
368 
369   if (priv->inspected)
370     g_object_remove_weak_pointer (G_OBJECT (priv->inspected),
371                                   (gpointer *) &priv->inspected);
372   priv->inspected = inspected;
373   if (priv->inspected)
374     g_object_add_weak_pointer (G_OBJECT (priv->inspected),
375                                (gpointer *) &priv->inspected);
376 
377   if (gtk_widget_get_mapped (GTK_WIDGET (magnifier)))
378     connect_draw_handler (magnifier);
379   connect_resize_handler (magnifier);
380 
381   g_object_notify (G_OBJECT (magnifier), "inspected");
382 }
383 
384 void
_gtk_magnifier_set_coords(GtkMagnifier * magnifier,gdouble x,gdouble y)385 _gtk_magnifier_set_coords (GtkMagnifier *magnifier,
386                            gdouble       x,
387                            gdouble       y)
388 {
389   GtkMagnifierPrivate *priv;
390 
391   g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
392 
393   priv = _gtk_magnifier_get_instance_private (magnifier);
394 
395   if (priv->x == x && priv->y == y)
396     return;
397 
398   priv->x = x;
399   priv->y = y;
400 
401   if (gtk_widget_is_visible (GTK_WIDGET (magnifier)))
402     gtk_widget_queue_draw (GTK_WIDGET (magnifier));
403 }
404 
405 void
_gtk_magnifier_get_coords(GtkMagnifier * magnifier,gdouble * x,gdouble * y)406 _gtk_magnifier_get_coords (GtkMagnifier *magnifier,
407                            gdouble      *x,
408                            gdouble      *y)
409 {
410   GtkMagnifierPrivate *priv;
411 
412   g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
413 
414   priv = _gtk_magnifier_get_instance_private (magnifier);
415 
416   if (x)
417     *x = priv->x;
418 
419   if (y)
420     *y = priv->y;
421 }
422 
423 void
_gtk_magnifier_set_magnification(GtkMagnifier * magnifier,gdouble magnification)424 _gtk_magnifier_set_magnification (GtkMagnifier *magnifier,
425                                   gdouble       magnification)
426 {
427   GtkMagnifierPrivate *priv;
428 
429   g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
430 
431   priv = _gtk_magnifier_get_instance_private (magnifier);
432 
433   if (priv->magnification == magnification)
434     return;
435 
436   priv->magnification = magnification;
437   g_object_notify (G_OBJECT (magnifier), "magnification");
438 
439   if (priv->resize)
440     gtk_widget_queue_resize (GTK_WIDGET (magnifier));
441 
442   if (gtk_widget_is_visible (GTK_WIDGET (magnifier)))
443     gtk_widget_queue_draw (GTK_WIDGET (magnifier));
444 }
445 
446 gdouble
_gtk_magnifier_get_magnification(GtkMagnifier * magnifier)447 _gtk_magnifier_get_magnification (GtkMagnifier *magnifier)
448 {
449   GtkMagnifierPrivate *priv;
450 
451   g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), 1);
452 
453   priv = _gtk_magnifier_get_instance_private (magnifier);
454 
455   return priv->magnification;
456 }
457 
458 void
_gtk_magnifier_set_resize(GtkMagnifier * magnifier,gboolean resize)459 _gtk_magnifier_set_resize (GtkMagnifier *magnifier,
460                            gboolean      resize)
461 {
462   GtkMagnifierPrivate *priv;
463 
464   g_return_if_fail (GTK_IS_MAGNIFIER (magnifier));
465 
466   priv = _gtk_magnifier_get_instance_private (magnifier);
467 
468   if (priv->resize == resize)
469     return;
470 
471   priv->resize = resize;
472 
473   gtk_widget_queue_resize (GTK_WIDGET (magnifier));
474   if (resize)
475     connect_resize_handler (magnifier);
476   else
477     disconnect_resize_handler (magnifier);
478 }
479 
480 gboolean
_gtk_magnifier_get_resize(GtkMagnifier * magnifier)481 _gtk_magnifier_get_resize (GtkMagnifier *magnifier)
482 {
483   GtkMagnifierPrivate *priv;
484 
485   g_return_val_if_fail (GTK_IS_MAGNIFIER (magnifier), FALSE);
486 
487   priv = _gtk_magnifier_get_instance_private (magnifier);
488 
489   return priv->resize;
490 }
491