1 /*
2 * Photos - access, organize and share your photos on GNOME
3 * Copyright © 2017 – 2019 Red Hat, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include <gio/gio.h>
25
26 #include "photos-utils.h"
27 #include "photos-zoom-controls.h"
28
29
30 struct _PhotosZoomControls
31 {
32 GtkBin parent_instance;
33 GAction *zoom_best_fit_action;
34 GAction *zoom_out_action;
35 GtkWidget *revealer;
36 GtkWidget *zoom_in_button;
37 GtkWidget *zoom_out_button;
38 GtkWidget *zoom_toggle_button;
39 gboolean hover;
40 guint notify_enabled_id;
41 };
42
43 enum
44 {
45 PROP_0,
46 PROP_HOVER
47 };
48
49
50 G_DEFINE_TYPE (PhotosZoomControls, photos_zoom_controls, GTK_TYPE_BIN);
51
52
53 static void
photos_zoom_controls_update_buttons(PhotosZoomControls * self)54 photos_zoom_controls_update_buttons (PhotosZoomControls *self)
55 {
56 GtkWidget *image;
57 gboolean zoom_best_fit_enabled;
58 gboolean zoom_out_enabled;
59 const gchar *icon_name;
60
61 zoom_best_fit_enabled = g_action_get_enabled (self->zoom_best_fit_action);
62 zoom_out_enabled = g_action_get_enabled (self->zoom_out_action);
63 g_return_if_fail (zoom_best_fit_enabled == zoom_out_enabled);
64
65 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), zoom_out_enabled);
66
67 gtk_actionable_set_action_name (GTK_ACTIONABLE (self->zoom_toggle_button), NULL);
68 gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->zoom_toggle_button), NULL);
69
70 if (zoom_out_enabled)
71 {
72 gtk_actionable_set_action_name (GTK_ACTIONABLE (self->zoom_toggle_button), "app.zoom-best-fit");
73 }
74 else
75 {
76 GVariant *target_value = NULL;
77
78 target_value = photos_utils_create_zoom_target_value (1.0, PHOTOS_ZOOM_EVENT_MOUSE_CLICK);
79 gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->zoom_toggle_button), target_value);
80 gtk_actionable_set_action_name (GTK_ACTIONABLE (self->zoom_toggle_button), "app.zoom-in");
81 }
82
83 icon_name = zoom_out_enabled ? "zoom-fit-best-symbolic" : "zoom-in-symbolic";
84 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_INVALID);
85 gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
86 gtk_button_set_image (GTK_BUTTON (self->zoom_toggle_button), image);
87 }
88
89
90 static gboolean
photos_zoom_controls_notify_idle(gpointer user_data)91 photos_zoom_controls_notify_idle (gpointer user_data)
92 {
93 PhotosZoomControls *self = PHOTOS_ZOOM_CONTROLS (user_data);
94
95 self->notify_enabled_id = 0;
96 photos_zoom_controls_update_buttons (self);
97 return G_SOURCE_REMOVE;
98 }
99
100
101 static void
photos_zoom_controls_notify_enabled(PhotosZoomControls * self)102 photos_zoom_controls_notify_enabled (PhotosZoomControls *self)
103 {
104 if (self->notify_enabled_id != 0)
105 return;
106
107 self->notify_enabled_id = g_idle_add (photos_zoom_controls_notify_idle, self);
108 }
109
110
111 static void
photos_zoom_controls_set_hover(PhotosZoomControls * self,gboolean hover)112 photos_zoom_controls_set_hover (PhotosZoomControls *self, gboolean hover)
113 {
114 if (self->hover == hover)
115 return;
116
117 self->hover = hover;
118 g_object_notify (G_OBJECT (self), "hover");
119 }
120
121
122 static gboolean
photos_zoom_controls_enter_notify(GtkWidget * widget,GdkEventCrossing * event)123 photos_zoom_controls_enter_notify (GtkWidget *widget, GdkEventCrossing *event)
124 {
125 PhotosZoomControls *self = PHOTOS_ZOOM_CONTROLS (widget);
126
127 if (event->detail != GDK_NOTIFY_INFERIOR)
128 photos_zoom_controls_set_hover (self, TRUE);
129
130 return GDK_EVENT_PROPAGATE;
131 }
132
133
134 static gboolean
photos_zoom_controls_leave_notify(GtkWidget * widget,GdkEventCrossing * event)135 photos_zoom_controls_leave_notify (GtkWidget *widget, GdkEventCrossing *event)
136 {
137 PhotosZoomControls *self = PHOTOS_ZOOM_CONTROLS (widget);
138
139 if (event->detail != GDK_NOTIFY_INFERIOR)
140 photos_zoom_controls_set_hover (self, FALSE);
141
142 return GDK_EVENT_PROPAGATE;
143 }
144
145
146 static void
photos_zoom_controls_realize(GtkWidget * widget)147 photos_zoom_controls_realize (GtkWidget *widget)
148 {
149 GdkVisual *visual;
150 g_autoptr (GdkWindow) window = NULL;
151 GdkWindow *parent_window;
152 GdkWindowAttr attributes;
153 GtkAllocation allocation;
154 gint attributes_mask;
155
156 gtk_widget_set_realized (widget, TRUE);
157
158 attributes.event_mask = gtk_widget_get_events (widget);
159 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK
160 | GDK_BUTTON_RELEASE_MASK
161 | GDK_ENTER_NOTIFY_MASK
162 | GDK_LEAVE_NOTIFY_MASK);
163
164 gtk_widget_get_allocation (widget, &allocation);
165 attributes.x = allocation.x;
166 attributes.y = allocation.y;
167 attributes.width = allocation.width;
168 attributes.height = allocation.height;
169
170 attributes.wclass = GDK_INPUT_OUTPUT;
171
172 visual = gtk_widget_get_visual (widget);
173 attributes.visual = visual;
174
175 attributes.window_type = GDK_WINDOW_CHILD;
176
177 attributes_mask = GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y;
178
179 parent_window = gtk_widget_get_parent_window (widget);
180 window = gdk_window_new (parent_window, &attributes, attributes_mask);
181 gtk_widget_set_window (widget, g_object_ref (window));
182 gtk_widget_register_window (widget, window);
183 }
184
185
186 static void
photos_zoom_controls_size_allocate(GtkWidget * widget,GtkAllocation * allocation)187 photos_zoom_controls_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
188 {
189 GdkWindow *window;
190
191 GTK_WIDGET_CLASS (photos_zoom_controls_parent_class)->size_allocate (widget, allocation);
192
193 if (!gtk_widget_get_realized (widget))
194 return;
195
196 window = gtk_widget_get_window (widget);
197 gdk_window_move_resize (window, allocation->x, allocation->y, allocation->width, allocation->height);
198 }
199
200
201 static void
photos_zoom_controls_dispose(GObject * object)202 photos_zoom_controls_dispose (GObject *object)
203 {
204 PhotosZoomControls *self = PHOTOS_ZOOM_CONTROLS (object);
205
206 if (self->notify_enabled_id != 0)
207 {
208 g_source_remove (self->notify_enabled_id);
209 self->notify_enabled_id = 0;
210 }
211
212 G_OBJECT_CLASS (photos_zoom_controls_parent_class)->dispose (object);
213 }
214
215
216 static void
photos_zoom_controls_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)217 photos_zoom_controls_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
218 {
219 PhotosZoomControls *self = PHOTOS_ZOOM_CONTROLS (object);
220
221 switch (prop_id)
222 {
223 case PROP_HOVER:
224 g_value_set_boolean (value, self->hover);
225 break;
226
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229 break;
230 }
231 }
232
233
234 static void
photos_zoom_controls_init(PhotosZoomControls * self)235 photos_zoom_controls_init (PhotosZoomControls *self)
236 {
237 GApplication *app;
238
239 app = g_application_get_default ();
240
241 gtk_widget_init_template (GTK_WIDGET (self));
242 gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
243
244 self->zoom_best_fit_action = g_action_map_lookup_action (G_ACTION_MAP (app), "zoom-best-fit");
245 g_signal_connect_swapped (self->zoom_best_fit_action,
246 "notify::enabled",
247 G_CALLBACK (photos_zoom_controls_notify_enabled),
248 self);
249
250 self->zoom_out_action = g_action_map_lookup_action (G_ACTION_MAP (app), "zoom-out");
251 g_signal_connect_swapped (self->zoom_out_action,
252 "notify::enabled",
253 G_CALLBACK (photos_zoom_controls_notify_enabled),
254 self);
255
256 photos_zoom_controls_update_buttons (self);
257 }
258
259
260 static void
photos_zoom_controls_class_init(PhotosZoomControlsClass * class)261 photos_zoom_controls_class_init (PhotosZoomControlsClass *class)
262 {
263 GObjectClass *object_class = G_OBJECT_CLASS (class);
264 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
265
266 object_class->dispose = photos_zoom_controls_dispose;
267 object_class->get_property = photos_zoom_controls_get_property;
268
269 widget_class->enter_notify_event = photos_zoom_controls_enter_notify;
270 widget_class->leave_notify_event = photos_zoom_controls_leave_notify;
271 widget_class->realize = photos_zoom_controls_realize;
272 widget_class->size_allocate = photos_zoom_controls_size_allocate;
273
274 g_object_class_install_property (object_class,
275 PROP_HOVER,
276 g_param_spec_boolean ("hover",
277 "Hover",
278 "Whether the widget is hovered",
279 FALSE,
280 G_PARAM_EXPLICIT_NOTIFY |
281 G_PARAM_READABLE |
282 G_PARAM_STATIC_STRINGS));
283
284 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Photos/zoom-controls.ui");
285 gtk_widget_class_bind_template_child (widget_class, PhotosZoomControls, revealer);
286 gtk_widget_class_bind_template_child (widget_class, PhotosZoomControls, zoom_in_button);
287 gtk_widget_class_bind_template_child (widget_class, PhotosZoomControls, zoom_out_button);
288 gtk_widget_class_bind_template_child (widget_class, PhotosZoomControls, zoom_toggle_button);
289 }
290
291
292 GtkWidget *
photos_zoom_controls_new(void)293 photos_zoom_controls_new (void)
294 {
295 return g_object_new (PHOTOS_TYPE_ZOOM_CONTROLS, NULL);
296 }
297
298
299 gboolean
photos_zoom_controls_get_hover(PhotosZoomControls * self)300 photos_zoom_controls_get_hover (PhotosZoomControls *self)
301 {
302 g_return_val_if_fail (PHOTOS_IS_ZOOM_CONTROLS (self), FALSE);
303 return self->hover;
304 }
305