blob: 137c8ac3c33898b3e5897448600ac40bc922279b [file] [log] [blame]
Nicolas Jagerb413b302016-05-06 11:41:32 -04001/*
2 * Copyright (C) 2016 Savoir-faire Linux Inc.
3 * Author: Nicolas Jager <nicolas.jager@savoirfairelinux.com>
4 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "avatarmanipulation.h"
22
23/* LRC */
24#include <globalinstances.h>
25#include <person.h>
26#include <profile.h>
27#include <profilemodel.h>
28#include <video/configurationproxy.h>
29#include <video/previewmanager.h>
30#include <video/devicemodel.h>
31
32/* client */
33#include "native/pixbufmanipulator.h"
34#include "video/video_widget.h"
Stepan Salenikovich5c54b352016-09-14 14:28:19 -040035#include "cc-crop-area.h"
Nicolas Jagerb413b302016-05-06 11:41:32 -040036
37/* system */
38#include <glib/gi18n.h>
Nicolas Jagerb413b302016-05-06 11:41:32 -040039
40/* size of avatar */
41static constexpr int AVATAR_WIDTH = 100; /* px */
42static constexpr int AVATAR_HEIGHT = 100; /* px */
43
44/* size of video widget */
45static constexpr int VIDEO_WIDTH = 300; /* px */
46static constexpr int VIDEO_HEIGHT = 200; /* px */
47
Nicolas Jagerb413b302016-05-06 11:41:32 -040048struct _AvatarManipulation
49{
50 GtkBox parent;
51};
52
53struct _AvatarManipulationClass
54{
55 GtkBoxClass parent_class;
56};
57
58typedef struct _AvatarManipulationPrivate AvatarManipulationPrivate;
59
60struct _AvatarManipulationPrivate
61{
62 GtkWidget *stack_avatar_manipulation;
63 GtkWidget *video_widget;
64 GtkWidget *box_views_and_controls;
65 GtkWidget *box_controls;
Stepan Salenikovichae92efd2016-07-28 18:51:27 -040066
67 GtkWidget *button_box_current;
68 GtkWidget *button_box_photo;
69 GtkWidget *button_box_edit;
70
71 GtkWidget *button_start_camera;
Nicolas Jagerb413b302016-05-06 11:41:32 -040072 GtkWidget *button_choose_picture;
Stepan Salenikovichae92efd2016-07-28 18:51:27 -040073 GtkWidget *button_take_photo;
74 GtkWidget *button_return_photo;
Nicolas Jagerb413b302016-05-06 11:41:32 -040075 GtkWidget *button_set_avatar;
Stepan Salenikovichae92efd2016-07-28 18:51:27 -040076 GtkWidget *button_return_edit;
77
Stepan Salenikovich5c54b352016-09-14 14:28:19 -040078 // GtkWidget *selector_widget;
Nicolas Jagerb413b302016-05-06 11:41:32 -040079 GtkWidget *stack_views;
80 GtkWidget *image_avatar;
Stepan Salenikovich5c54b352016-09-14 14:28:19 -040081 GtkWidget *vbox_crop_area;
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -040082 GtkWidget *frame_video;
Nicolas Jagerb413b302016-05-06 11:41:32 -040083
Nicolas Jagerb413b302016-05-06 11:41:32 -040084 AvatarManipulationState state;
Stepan Salenikovichae92efd2016-07-28 18:51:27 -040085 AvatarManipulationState last_state;
86
87 /* this is used to keep track of the state of the preview when the camera is used to take a
88 * photo; if a call is in progress, then the preview should already be started and we don't want
89 * to stop it when the settings are closed, in this case
90 */
91 gboolean video_started_by_avatar_manipulation;
Stepan Salenikovich5c54b352016-09-14 14:28:19 -040092
93 GtkWidget *crop_area;
Nicolas Jagerb413b302016-05-06 11:41:32 -040094};
95
96G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX);
97
98#define AVATAR_MANIPULATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), AVATAR_MANIPULATION_TYPE, \
99 AvatarManipulationPrivate))
100
101static void set_state(AvatarManipulation *self, AvatarManipulationState state);
102
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400103static void start_camera(AvatarManipulation *self);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400104static void take_a_photo(AvatarManipulation *self);
105static void choose_picture(AvatarManipulation *self);
Stepan Salenikovichd9a38302016-09-14 15:08:11 -0400106static void return_to_previous(AvatarManipulation *self);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400107static void update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400108static void set_avatar(AvatarManipulation *self);
109static void got_snapshot(AvatarManipulation *parent);
110
Nicolas Jagerb413b302016-05-06 11:41:32 -0400111static void
112avatar_manipulation_dispose(GObject *object)
113{
114 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(object);
115
116 /* make sure we stop the preview and the video widget */
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400117 if (priv->video_started_by_avatar_manipulation)
118 Video::PreviewManager::instance().stopPreview();
Nicolas Jagerb413b302016-05-06 11:41:32 -0400119 if (priv->video_widget) {
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -0400120 gtk_container_remove(GTK_CONTAINER(priv->frame_video), priv->video_widget);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400121 priv->video_widget = NULL;
122 }
123
124 G_OBJECT_CLASS(avatar_manipulation_parent_class)->dispose(object);
125}
126
127static void
128avatar_manipulation_finalize(GObject *object)
129{
Nicolas Jagerb413b302016-05-06 11:41:32 -0400130 G_OBJECT_CLASS(avatar_manipulation_parent_class)->finalize(object);
131}
132
133GtkWidget*
134avatar_manipulation_new(void)
135{
136 // a profile must exist
137 g_return_val_if_fail(ProfileModel::instance().selectedProfile(), NULL);
138
139 return (GtkWidget *)g_object_new(AVATAR_MANIPULATION_TYPE, NULL);
140}
141
142GtkWidget*
143avatar_manipulation_new_from_wizard(void)
144{
145 auto self = avatar_manipulation_new();
146
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400147 /* in this mode, we want to automatically go to the PHOTO avatar state, unless one already exists */
148 if (!ProfileModel::instance().selectedProfile()->person()->photo().isValid()) {
149 // check if there is a camera
150 if (Video::DeviceModel::instance().rowCount() > 0)
151 set_state(AVATAR_MANIPULATION(self), AVATAR_MANIPULATION_STATE_PHOTO);
152 }
Nicolas Jagerb413b302016-05-06 11:41:32 -0400153
154 return self;
155}
156
157static void
158avatar_manipulation_class_init(AvatarManipulationClass *klass)
159{
160 G_OBJECT_CLASS(klass)->finalize = avatar_manipulation_finalize;
161 G_OBJECT_CLASS(klass)->dispose = avatar_manipulation_dispose;
162
163 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass), "/cx/ring/RingGnome/avatarmanipulation.ui");
164
165 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_views_and_controls);
166 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_controls);
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400167 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_start_camera);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400168 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_choose_picture);
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400169 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_take_photo);
170 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_photo);
171 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_set_avatar);
172 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_edit);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400173 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, stack_views);
174 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, image_avatar);
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -0400175 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, frame_video);
Stepan Salenikovich5c54b352016-09-14 14:28:19 -0400176 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, vbox_crop_area);
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400177 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_current);
178 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_photo);
179 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_edit);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400180}
181
182static void
183avatar_manipulation_init(AvatarManipulation *self)
184{
185 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
186 gtk_widget_init_template(GTK_WIDGET(self));
187
Nicolas Jagerb413b302016-05-06 11:41:32 -0400188 /* our desired size for the image area */
189 gtk_widget_set_size_request(priv->stack_views, VIDEO_WIDTH, VIDEO_HEIGHT);
190
Nicolas Jagerb413b302016-05-06 11:41:32 -0400191 /* signals */
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400192 g_signal_connect_swapped(priv->button_start_camera, "clicked", G_CALLBACK(start_camera), self);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400193 g_signal_connect_swapped(priv->button_choose_picture, "clicked", G_CALLBACK(choose_picture), self);
194 g_signal_connect_swapped(priv->button_take_photo, "clicked", G_CALLBACK(take_a_photo), self);
Stepan Salenikovichd9a38302016-09-14 15:08:11 -0400195 g_signal_connect_swapped(priv->button_return_photo, "clicked", G_CALLBACK(return_to_previous), self);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400196 g_signal_connect_swapped(priv->button_set_avatar, "clicked", G_CALLBACK(set_avatar), self);
Stepan Salenikovichd9a38302016-09-14 15:08:11 -0400197 g_signal_connect_swapped(priv->button_return_edit, "clicked", G_CALLBACK(return_to_previous), self);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400198
199 set_state(self, AVATAR_MANIPULATION_STATE_CURRENT);
200
201 gtk_widget_show_all(priv->stack_views);
202}
203
204static void
205set_state(AvatarManipulation *self, AvatarManipulationState state)
206{
207 // note: this funciton does not check if the state transition is valid, this is assumed to have
208 // been done by the caller
209 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
210
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400211 // save prev state
212 priv->last_state = priv->state;
213
Nicolas Jagerb413b302016-05-06 11:41:32 -0400214 switch (state) {
215 case AVATAR_MANIPULATION_STATE_CURRENT:
216 {
217 /* get the current or default profile avatar */
218 auto photo = GlobalInstances::pixmapManipulator().contactPhoto(
219 ProfileModel::instance().selectedProfile()->person(),
220 QSize(AVATAR_WIDTH, AVATAR_HEIGHT),
221 false);
222 std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>();
223
224 if (photo.isValid()) {
225 gtk_image_set_from_pixbuf (GTK_IMAGE(priv->image_avatar), pixbuf_photo.get());
226 } else {
227 g_warning("invlid pixbuf");
228 }
229
230 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_avatar");
Nicolas Jagerb413b302016-05-06 11:41:32 -0400231
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400232 /* available actions: start camera (if available) or choose image */
233 if (Video::DeviceModel::instance().rowCount() > 0) {
234 // TODO: update if a video device gets inserted while in this state
235 gtk_widget_set_visible(priv->button_start_camera, true);
236 }
237 gtk_widget_set_visible(priv->button_box_current, true);
238 gtk_widget_set_visible(priv->button_box_photo, false);
239 gtk_widget_set_visible(priv->button_box_edit, false);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400240
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400241 /* make sure video widget and camera is not running */
242 if (priv->video_started_by_avatar_manipulation)
243 Video::PreviewManager::instance().stopPreview();
Nicolas Jagerb413b302016-05-06 11:41:32 -0400244 if (priv->video_widget) {
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -0400245 gtk_container_remove(GTK_CONTAINER(priv->frame_video), priv->video_widget);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400246 priv->video_widget = NULL;
247 }
248 }
249 break;
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400250 case AVATAR_MANIPULATION_STATE_PHOTO:
Nicolas Jagerb413b302016-05-06 11:41:32 -0400251 {
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400252 // start the video; if its not available we should not be in this state
253 priv->video_widget = video_widget_new();
254 g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self);
255 gtk_widget_set_vexpand_set(priv->video_widget, FALSE);
256 gtk_widget_set_hexpand_set(priv->video_widget, FALSE);
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -0400257 gtk_container_add(GTK_CONTAINER(priv->frame_video), priv->video_widget);
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400258 gtk_widget_set_visible(priv->video_widget, true);
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400259 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth");
Nicolas Jagerb413b302016-05-06 11:41:32 -0400260
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400261
262 /* local renderer, but set as "remote" so that it takes up the whole screen */
263 video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
264 Video::PreviewManager::instance().previewRenderer(),
265 VIDEO_RENDERER_REMOTE);
266
267 if (!Video::PreviewManager::instance().isPreviewing()) {
268 priv->video_started_by_avatar_manipulation = TRUE;
Nicolas Jagerb413b302016-05-06 11:41:32 -0400269 Video::PreviewManager::instance().startPreview();
Nicolas Jagerb413b302016-05-06 11:41:32 -0400270 } else {
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400271 priv->video_started_by_avatar_manipulation = FALSE;
Nicolas Jagerb413b302016-05-06 11:41:32 -0400272 }
273
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400274 /* available actions: take snapshot, return*/
275 gtk_widget_set_visible(priv->button_box_current, false);
276 gtk_widget_set_visible(priv->button_box_photo, true);
277 gtk_widget_set_visible(priv->button_box_edit, false);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400278 }
279 break;
280 case AVATAR_MANIPULATION_STATE_EDIT:
281 {
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400282 /* make sure video widget and camera is not running */
283 if (priv->video_started_by_avatar_manipulation)
284 Video::PreviewManager::instance().stopPreview();
Nicolas Jagerb413b302016-05-06 11:41:32 -0400285 if (priv->video_widget) {
Stepan Salenikovich1c1b0d82016-07-29 08:13:59 -0400286 gtk_container_remove(GTK_CONTAINER(priv->frame_video), priv->video_widget);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400287 priv->video_widget = NULL;
288 }
289
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400290 /* available actions: set avatar, return */
291 gtk_widget_set_visible(priv->button_box_current, false);
292 gtk_widget_set_visible(priv->button_box_photo, false);
293 gtk_widget_set_visible(priv->button_box_edit, true);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400294
295 gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_edit_view");
296 }
297 break;
298 }
299
300 priv->state = state;
301}
302
303static void
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400304start_camera(AvatarManipulation *self)
305{
306 set_state(self, AVATAR_MANIPULATION_STATE_PHOTO);
307}
308
309static void
Nicolas Jagerb413b302016-05-06 11:41:32 -0400310take_a_photo(AvatarManipulation *self)
311{
312 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
313 video_widget_take_snapshot(VIDEO_WIDGET(priv->video_widget));
314}
315
316static void
Nicolas Jagerb413b302016-05-06 11:41:32 -0400317set_avatar(AvatarManipulation *self)
318{
319 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
320
321 gchar* png_buffer_signed = nullptr;
322 gsize png_buffer_size;
323 GError* error = nullptr;
324
Stepan Salenikovich5c54b352016-09-14 14:28:19 -0400325 /* get the cropped area */
326 GdkPixbuf *selector_pixbuf = cc_crop_area_get_picture(CC_CROP_AREA(priv->crop_area));
Nicolas Jagerb413b302016-05-06 11:41:32 -0400327
328 /* scale it */
329 GdkPixbuf* pixbuf_frame_resized = gdk_pixbuf_scale_simple(selector_pixbuf, AVATAR_WIDTH, AVATAR_HEIGHT,
330 GDK_INTERP_HYPER);
331
332 /* save the png in memory */
333 gdk_pixbuf_save_to_buffer(pixbuf_frame_resized, &png_buffer_signed, &png_buffer_size, "png", &error, NULL);
334 if (!png_buffer_signed) {
335 g_warning("(set_avatar) failed to save pixbuffer to png: %s\n", error->message);
336 g_error_free(error);
337 return;
338 }
339
340 /* convert buffer to QByteArray in base 64*/
341 QByteArray png_q_byte_array = QByteArray::fromRawData(png_buffer_signed, png_buffer_size).toBase64();
342
343 /* save in profile */
344 QVariant photo = GlobalInstances::pixmapManipulator().personPhoto(png_q_byte_array);
345 ProfileModel::instance().selectedProfile()->person()->setPhoto(photo);
346 ProfileModel::instance().selectedProfile()->save();
347
348 g_free(png_buffer_signed);
349 g_object_unref(selector_pixbuf);
350 g_object_unref(pixbuf_frame_resized);
351
352 set_state(self, AVATAR_MANIPULATION_STATE_CURRENT);
353}
354
355static void
Stepan Salenikovichd9a38302016-09-14 15:08:11 -0400356return_to_previous(AvatarManipulation *self)
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400357{
358 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
Stepan Salenikovichd9a38302016-09-14 15:08:11 -0400359
360 if (priv->state == AVATAR_MANIPULATION_STATE_PHOTO) {
361 // from photo we alway go back to current
362 set_state(self, AVATAR_MANIPULATION_STATE_CURRENT);
363 } else {
364 // otherwise, if we were in edit state, we may have come from photo or current state
365 set_state(self, priv->last_state);
366 }
Stepan Salenikovichae92efd2016-07-28 18:51:27 -0400367}
368
369static void
Nicolas Jagerb413b302016-05-06 11:41:32 -0400370choose_picture(AvatarManipulation *self)
371{
372 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
373 GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
374 GtkFileFilter *filter = gtk_file_filter_new ();
375 gint res;
376
377 auto preview = gtk_image_new();
378
379 GtkWidget *ring_main_window = gtk_widget_get_toplevel(GTK_WIDGET(self));
380
381 auto dialog = gtk_file_chooser_dialog_new (_("Open Avatar Image"),
382 GTK_WINDOW(ring_main_window),
383 action,
384 _("_Cancel"),
385 GTK_RESPONSE_CANCEL,
386 _("_Open"),
387 GTK_RESPONSE_ACCEPT,
388 NULL);
389
390 /* add filters */
391 gtk_file_filter_add_pattern (filter,"*.png");
392 gtk_file_filter_add_pattern (filter,"*.jpg");
393 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),filter);
394
395 /* add an image preview inside the file choose */
396 gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER(dialog), preview);
397 g_signal_connect (GTK_FILE_CHOOSER(dialog), "update-preview", G_CALLBACK (update_preview_cb), preview);
398
399 /* start the file chooser */
400 res = gtk_dialog_run (GTK_DIALOG(dialog)); /* blocks until the dialog is closed */
401
402 if (res == GTK_RESPONSE_ACCEPT) {
403 if(auto filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog))) {
404 GError* error = nullptr; /* initialising to null avoid trouble... */
405
Stepan Salenikovichd8a9ce22016-09-15 13:36:05 -0400406 auto picture = gdk_pixbuf_new_from_file_at_size (filename, VIDEO_WIDTH, VIDEO_HEIGHT, &error);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400407
408 if (!error) {
Stepan Salenikovichd8a9ce22016-09-15 13:36:05 -0400409 /* reset crop area */
410 if (priv->crop_area)
411 gtk_container_remove(GTK_CONTAINER(priv->vbox_crop_area), priv->crop_area);
412 priv->crop_area = cc_crop_area_new();
413 gtk_widget_show(priv->crop_area);
414 gtk_box_pack_start(GTK_BOX(priv->vbox_crop_area), priv->crop_area, TRUE, TRUE, 0);
415 cc_crop_area_set_picture(CC_CROP_AREA(priv->crop_area), picture);
416 g_object_unref(picture);
417
Nicolas Jagerb413b302016-05-06 11:41:32 -0400418 set_state(self, AVATAR_MANIPULATION_STATE_EDIT);
419 } else {
420 g_warning("(choose_picture) failed to load pixbuf from file: %s", error->message);
421 g_error_free(error);
422 }
423
424 g_free(filename);
425 } else {
426 g_warning("(choose_picture) filename empty");
427 }
428 }
429
430 gtk_widget_destroy(dialog);
431}
432
433static void
Nicolas Jagerb413b302016-05-06 11:41:32 -0400434update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview)
435{
436 gboolean have_preview = FALSE;
437 if (auto filename = gtk_file_chooser_get_preview_filename(file_chooser)) {
438 GError* error = nullptr;
439 auto pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, &error);
440 if (!error) {
441 gtk_image_set_from_pixbuf(GTK_IMAGE(preview), pixbuf);
442 g_object_unref(pixbuf);
443 have_preview = TRUE;
444 } else {
445 // nothing to do, the file is probably not a picture
446 }
447 g_free (filename);
448 }
449 gtk_file_chooser_set_preview_widget_active(file_chooser, have_preview);
450}
451
Nicolas Jagerb413b302016-05-06 11:41:32 -0400452static void
453got_snapshot(AvatarManipulation *self)
454{
455 AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
456 GdkPixbuf* pix = video_widget_get_snapshot(VIDEO_WIDGET(priv->video_widget));
457
Stepan Salenikovichd8a9ce22016-09-15 13:36:05 -0400458 if (priv->crop_area)
459 gtk_container_remove(GTK_CONTAINER(priv->vbox_crop_area), priv->crop_area);
460 priv->crop_area = cc_crop_area_new();
461 gtk_widget_show(priv->crop_area);
462 gtk_box_pack_start(GTK_BOX(priv->vbox_crop_area), priv->crop_area, TRUE, TRUE, 0);
Stepan Salenikovich5c54b352016-09-14 14:28:19 -0400463 cc_crop_area_set_picture(CC_CROP_AREA(priv->crop_area), pix);
Nicolas Jagerb413b302016-05-06 11:41:32 -0400464
465 set_state(self, AVATAR_MANIPULATION_STATE_EDIT);
466}