Keep the progress/cancel toolbar permanently visible
[entangle:entangle.git] / src / frontend / entangle-camera-manager.c
1 /*
2  *  Entangle: Tethered Camera Control & Capture
3  *
4  *  Copyright (C) 2009-2012 Daniel P. Berrange
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, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <config.h>
22
23 #include <string.h>
24 #include <math.h>
25 #include <gtk/gtk.h>
26 #include <glib/gi18n.h>
27 #include <unistd.h>
28 #include <gdk/gdkx.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include "entangle-debug.h"
33 #include "entangle-camera-manager.h"
34 #include "entangle-camera-list.h"
35 #include "entangle-camera-info.h"
36 #include "entangle-camera-support.h"
37 #include "entangle-camera-picker.h"
38 #include "entangle-session.h"
39 #include "entangle-image-display.h"
40 #include "entangle-image-statusbar.h"
41 #include "entangle-image-loader.h"
42 #include "entangle-thumbnail-loader.h"
43 #include "entangle-image-popup.h"
44 #include "entangle-image-histogram.h"
45 #include "entangle-help-about.h"
46 #include "entangle-session-browser.h"
47 #include "entangle-control-panel.h"
48 #include "entangle-colour-profile.h"
49 #include "entangle-preferences-display.h"
50 #include "entangle-progress.h"
51 #include "entangle-dpms.h"
52 #include "entangle-window.h"
53 #include "entangle-auto-drawer.h"
54
55 #define ENTANGLE_CAMERA_MANAGER_GET_PRIVATE(obj)                        \
56     (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_CAMERA_MANAGER, EntangleCameraManagerPrivate))
57
58 struct _EntangleCameraManagerPrivate {
59     EntangleCamera *camera;
60     gboolean cameraReady;
61     gboolean cameraChanged;
62     EntangleSession *session;
63
64     EntangleCameraPicker *picker;
65     EntangleHelpAbout *about;
66
67     EntangleCameraInfo *summary;
68     EntangleCameraInfo *manual;
69     EntangleCameraInfo *driver;
70     EntangleCameraSupport *supported;
71
72     EntangleImageLoader *imageLoader;
73     EntangleThumbnailLoader *thumbLoader;
74     EntangleColourProfileTransform *colourTransform;
75     GtkScrolledWindow *imageScroll;
76     EntangleImageDisplay *imageDisplay;
77     EntangleImageStatusbar *imageStatusbar;
78     EntangleAutoDrawer *imageDrawer;
79     gulong imageDrawerTimer;
80     EntangleSessionBrowser *sessionBrowser;
81     GtkMenu *sessionBrowserMenu;
82     EntangleImage *sessionBrowserImage;
83     EntangleControlPanel *controlPanel;
84     EntanglePreferencesDisplay *prefsDisplay;
85     EntangleImageHistogram *imageHistogram;
86
87     EntangleImage *currentImage;
88
89     EntangleImagePopup *imagePresentation;
90     gint presentationMonitor;
91     GHashTable *popups;
92
93     gdouble imageScrollVOffset;
94     gdouble imageScrollHOffset;
95     gboolean imageScrollRestored;
96
97     int zoomLevel;
98
99     gulong sigFileDownload;
100     gulong sigFilePreview;
101     gulong sigFileAdd;
102     gulong sigChanged;
103     gulong sigPrefsNotify;
104
105     GCancellable *monitorCancel;
106     GCancellable *taskCancel;
107     GCancellable *taskConfirm;
108     gboolean taskCapture;
109     gboolean taskPreview;
110     gboolean taskActive;
111     gboolean taskProcessEvents;
112     float taskTarget;
113     char *deleteImageDup;
114
115     GtkBuilder *builder;
116 };
117
118
119 typedef struct _EntangleCameraFileTaskData EntangleCameraFileTaskData;
120 struct _EntangleCameraFileTaskData {
121     EntangleCameraManager *manager;
122     EntangleCameraFile *file;
123 };
124
125
126 static void entangle_camera_progress_interface_init(gpointer g_iface,
127                                                     gpointer iface_data);
128 static void entangle_camera_manager_window_interface_init(gpointer g_iface,
129                                                           gpointer iface_data);
130 static void do_select_image(EntangleCameraManager *manager,
131                             EntangleImage *image);
132
133 G_DEFINE_TYPE_EXTENDED(EntangleCameraManager, entangle_camera_manager, GTK_TYPE_WINDOW, 0,
134                        G_IMPLEMENT_INTERFACE(ENTANGLE_TYPE_PROGRESS, entangle_camera_progress_interface_init)
135                        G_IMPLEMENT_INTERFACE(ENTANGLE_TYPE_WINDOW, entangle_camera_manager_window_interface_init));
136
137 enum {
138     PROP_O,
139     PROP_CAMERA,
140 };
141
142
143 void do_menu_help_summary(GtkMenuItem *src,
144                           EntangleCameraManager *manager);
145 void do_menu_help_manual(GtkMenuItem *src,
146                          EntangleCameraManager *manager);
147 void do_menu_help_driver(GtkMenuItem *src,
148                          EntangleCameraManager *manager);
149 void do_menu_help_supported(GtkMenuItem *src,
150                             EntangleCameraManager *manager);
151 void do_menu_new_window(GtkImageMenuItem *src,
152                         EntangleCameraManager *manager);
153 void do_menu_select_session(GtkImageMenuItem *src,
154                             EntangleCameraManager *manager);
155 void do_menu_settings_toggled(GtkCheckMenuItem *src,
156                               EntangleCameraManager *manager);
157 void do_menu_zoom_in(GtkImageMenuItem *src,
158                      EntangleCameraManager *manager);
159 void do_menu_zoom_out(GtkImageMenuItem *src,
160                       EntangleCameraManager *manager);
161 void do_menu_zoom_normal(GtkImageMenuItem *src,
162                          EntangleCameraManager *manager);
163 void do_menu_zoom_best(GtkImageMenuItem *src,
164                        EntangleCameraManager *manager);
165 void do_menu_fullscreen(GtkCheckMenuItem *src,
166                         EntangleCameraManager *manager);
167 void do_menu_presentation(GtkCheckMenuItem *src,
168                           EntangleCameraManager *manager);
169 void do_menu_preferences(GtkCheckMenuItem *src,
170                          EntangleCameraManager *manager);
171 void do_toolbar_select_session(GtkFileChooserButton *src,
172                                EntangleCameraManager *manager);
173 void do_toolbar_settings(GtkToggleToolButton *src,
174                          EntangleCameraManager *manager);
175 void do_toolbar_cancel_clicked(GtkToolButton *src,
176                                EntangleCameraManager *manager);
177 void do_toolbar_capture(GtkToolButton *src,
178                         EntangleCameraManager *manager);
179 void do_toolbar_preview(GtkToggleToolButton *src,
180                         EntangleCameraManager *manager);
181 void do_toolbar_zoom_in(GtkToolButton *src,
182                         EntangleCameraManager *manager);
183 void do_toolbar_zoom_out(GtkToolButton *src,
184                          EntangleCameraManager *manager);
185 void do_toolbar_zoom_normal(GtkToolButton *src,
186                             EntangleCameraManager *manager);
187 void do_toolbar_zoom_best(GtkToolButton *src,
188                           EntangleCameraManager *manager);
189 void do_toolbar_fullscreen(GtkToggleToolButton *src,
190                            EntangleCameraManager *manager);
191 void do_menu_close(GtkMenuItem *src,
192                    EntangleCameraManager *manager);
193 void do_menu_quit(GtkMenuItem *src,
194                   EntangleCameraManager *manager);
195 void do_menu_help_about(GtkMenuItem *src,
196                         EntangleCameraManager *manager);
197 void do_menu_connect(GtkMenuItem *src,
198                      EntangleCameraManager *manager);
199 void do_menu_disconnect(GtkMenuItem *src,
200                         EntangleCameraManager *manager);
201 void do_menu_capture(GtkMenuItem *src,
202                      EntangleCameraManager *manager);
203 void do_menu_preview(GtkMenuItem *src,
204                      EntangleCameraManager *manager);
205 void do_menu_cancel(GtkMenuItem *src,
206                     EntangleCameraManager *manager);
207 gboolean do_manager_key_release(GtkWidget *widget G_GNUC_UNUSED,
208                                 GdkEventKey *ev,
209                                 gpointer data);
210 void do_menu_session_open_activate(GtkMenuItem *item G_GNUC_UNUSED,
211                                    EntangleCameraManager *manager);
212 void do_menu_session_delete_activate(GtkMenuItem *item G_GNUC_UNUSED,
213                                      EntangleCameraManager *manager);
214
215 static EntanglePreferences *entangle_camera_manager_get_preferences(EntangleCameraManager *manager)
216 {
217     GtkApplication *gapp = gtk_window_get_application(GTK_WINDOW(manager));
218     EntangleApplication *app = ENTANGLE_APPLICATION(gapp);
219     return entangle_application_get_preferences(app);
220 }
221
222 static EntangleColourProfile *entangle_camera_manager_monitor_profile(GtkWindow *window)
223 {
224     GdkScreen *screen;
225     GByteArray *profileData;
226     gchar *atom;
227     int monitor = 0;
228     GdkAtom type = GDK_NONE;
229     gint format = 0;
230     gint nitems = 0;
231     guint8 *data = NULL;
232     EntangleColourProfile *profile = NULL;
233
234     gtk_widget_realize(GTK_WIDGET(window));
235
236     screen = gtk_widget_get_screen(GTK_WIDGET(window));
237     monitor = gdk_screen_get_monitor_at_window(screen,
238                                                gtk_widget_get_window(GTK_WIDGET(window)));
239
240     if (monitor == 0)
241         atom = g_strdup("_ICC_PROFILE");
242     else
243         atom = g_strdup_printf("_ICC_PROFILE_%d", monitor);
244
245     if (!gdk_property_get(gdk_screen_get_root_window(screen),
246                           gdk_atom_intern(atom, FALSE),
247                           GDK_NONE,
248                           0, 64 * 1024 * 1024, FALSE,
249                           &type, &format, &nitems, &data) || nitems <= 0)
250         goto cleanup;
251
252     profileData = g_byte_array_new();
253     g_byte_array_append(profileData, data, nitems);
254
255     profile = entangle_colour_profile_new_data(profileData);
256     g_byte_array_unref(profileData);
257
258  cleanup:
259     g_free(data);
260     g_free(atom);
261
262     return profile;
263 }
264
265
266 static EntangleColourProfileTransform *entangle_camera_manager_colour_transform(EntangleCameraManager *manager)
267 {
268     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager), NULL);
269
270     EntangleColourProfileTransform *transform = NULL;
271     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
272
273     if (entangle_preferences_cms_get_enabled(prefs)) {
274         EntangleColourProfile *rgbProfile;
275         EntangleColourProfile *monitorProfile;
276         EntangleColourProfileIntent intent;
277
278         rgbProfile = entangle_preferences_cms_get_rgb_profile(prefs);
279         intent = entangle_preferences_cms_get_rendering_intent(prefs);
280         if (entangle_preferences_cms_get_detect_system_profile(prefs)) {
281             monitorProfile = entangle_camera_manager_monitor_profile(GTK_WINDOW(manager));
282         } else {
283             monitorProfile = entangle_preferences_cms_get_monitor_profile(prefs);
284         }
285
286         if (monitorProfile) {
287             transform = entangle_colour_profile_transform_new(rgbProfile, monitorProfile, intent);
288             g_object_unref(monitorProfile);
289         }
290         g_object_unref(rgbProfile);
291     }
292
293     return transform;
294 }
295
296
297 static void entangle_camera_manager_update_colour_transform(EntangleCameraManager *manager)
298 {
299     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
300
301     EntangleCameraManagerPrivate *priv = manager->priv;
302
303     if (priv->colourTransform)
304         g_object_unref(priv->colourTransform);
305
306     priv->colourTransform = entangle_camera_manager_colour_transform(manager);
307     if (priv->imageLoader)
308         entangle_pixbuf_loader_set_colour_transform(ENTANGLE_PIXBUF_LOADER(priv->imageLoader),
309                                                     priv->colourTransform);
310     if (priv->thumbLoader)
311         entangle_pixbuf_loader_set_colour_transform(ENTANGLE_PIXBUF_LOADER(priv->thumbLoader),
312                                                     priv->colourTransform);
313 }
314
315
316 static void entangle_camera_manager_update_aspect_ratio(EntangleCameraManager *manager)
317 {
318     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
319
320     EntangleCameraManagerPrivate *priv = manager->priv;
321     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
322     const gchar *aspect = entangle_preferences_img_get_aspect_ratio(prefs);
323
324     if (!aspect) {
325         entangle_image_display_set_aspect_ratio(priv->imageDisplay, 1.33);
326     } else {
327         gdouble d;
328         gchar *end;
329         d = g_ascii_strtod(aspect, &end);
330         if (end == aspect || (errno == ERANGE))
331             entangle_image_display_set_aspect_ratio(priv->imageDisplay, 1.33);
332         else
333             entangle_image_display_set_aspect_ratio(priv->imageDisplay, d);
334     }
335 }
336
337
338 static void entangle_camera_manager_update_mask_opacity(EntangleCameraManager *manager)
339 {
340     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
341
342     EntangleCameraManagerPrivate *priv = manager->priv;
343     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
344     gint opacity = entangle_preferences_img_get_mask_opacity(prefs);
345
346     entangle_image_display_set_mask_opacity(priv->imageDisplay, opacity / 100.0);
347 }
348
349
350 static void entangle_camera_manager_update_mask_enabled(EntangleCameraManager *manager)
351 {
352     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
353
354     EntangleCameraManagerPrivate *priv = manager->priv;
355     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
356     gboolean enabled = entangle_preferences_img_get_mask_enabled(prefs);
357
358     entangle_image_display_set_mask_enabled(priv->imageDisplay, enabled);
359 }
360
361
362 static void entangle_camera_manager_update_histogram_linear(EntangleCameraManager *manager)
363 {
364     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
365
366     EntangleCameraManagerPrivate *priv = manager->priv;
367     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
368     gboolean histogram_linear = entangle_preferences_interface_get_histogram_linear(prefs);
369
370     entangle_image_histogram_set_histogram_linear(priv->imageHistogram, histogram_linear);
371 }
372
373
374 static void do_presentation_monitor_toggled(GtkCheckMenuItem *menu, gpointer data)
375 {
376     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
377
378     EntangleCameraManager *manager = data;
379     EntangleCameraManagerPrivate *priv = manager->priv;
380     gpointer mon = g_object_get_data(G_OBJECT(menu), "monitor");
381
382     priv->presentationMonitor = GPOINTER_TO_INT(mon);
383
384     ENTANGLE_DEBUG("Set monitor %d", priv->presentationMonitor);
385
386     if (priv->imagePresentation)
387         entangle_image_popup_move_to_monitor(priv->imagePresentation,
388                                              priv->presentationMonitor);
389 }
390
391
392 static GtkWidget *entangle_camera_manager_monitor_menu(EntangleCameraManager *manager)
393 {
394     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager), NULL);
395
396     EntangleCameraManagerPrivate *priv = manager->priv;
397     GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(manager));
398     GtkWidget *menu = gtk_menu_new();
399     GSList *group = NULL;
400 #ifdef gdk_screen_get_primary_monitor
401     int active = gdk_screen_get_primary_monitor(screen);
402 #else
403     int active = 0;
404 #endif
405
406     for (int i = 0; i < gdk_screen_get_n_monitors(screen); i++) {
407         const gchar *name = gdk_screen_get_monitor_plug_name(screen, i);
408         GtkWidget *submenu = gtk_radio_menu_item_new_with_label(group, name);
409         g_object_set_data(G_OBJECT(submenu), "monitor", GINT_TO_POINTER(i));
410         group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(submenu));
411
412         gtk_menu_shell_append(GTK_MENU_SHELL(menu), submenu);
413
414         if (i == active)
415             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(submenu), TRUE);
416
417         g_signal_connect(submenu, "toggled",
418                          G_CALLBACK(do_presentation_monitor_toggled), manager);
419     }
420
421     priv->presentationMonitor = active;
422     gtk_widget_show_all(menu);
423
424     return menu;
425 }
426
427
428 static void entangle_camera_manager_update_viewfinder(EntangleCameraManager *manager)
429 {
430     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
431
432     EntangleCameraManagerPrivate *priv = manager->priv;
433
434     if (priv->taskPreview) {
435         EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
436         gint gridLines = entangle_preferences_img_get_grid_lines(prefs);
437         gboolean focusPoint = entangle_preferences_img_get_focus_point(prefs);
438
439         entangle_image_display_set_focus_point(priv->imageDisplay, focusPoint);
440         entangle_image_display_set_grid_display(priv->imageDisplay, gridLines);
441     } else {
442         entangle_image_display_set_focus_point(priv->imageDisplay, FALSE);
443         entangle_image_display_set_grid_display(priv->imageDisplay,
444                                                 ENTANGLE_IMAGE_DISPLAY_GRID_NONE);
445     }
446 }
447
448
449 static void entangle_camera_manager_update_image_loader(EntangleCameraManager *manager)
450 {
451     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
452
453     EntangleCameraManagerPrivate *priv = manager->priv;
454
455     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
456     gboolean embeddedPreview = entangle_preferences_img_get_embedded_preview(prefs);
457     entangle_image_loader_set_embedded_preview(priv->imageLoader, embeddedPreview);
458 }
459
460
461 static void entangle_camera_manager_prefs_changed(GObject *object G_GNUC_UNUSED,
462                                                   GParamSpec *spec,
463                                                   gpointer data)
464 {
465     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
466
467     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
468
469     if (g_str_equal(spec->name, "interface-histogram-linear")) {
470         entangle_camera_manager_update_histogram_linear(manager);
471     } else if (g_str_equal(spec->name, "cms-enabled") ||
472         g_str_equal(spec->name, "cms-rgb-profile") ||
473         g_str_equal(spec->name, "cms-monitor-profile") ||
474         g_str_equal(spec->name, "cms-detect-system-profile") ||
475         g_str_equal(spec->name, "cms-rendering-intent")) {
476         entangle_camera_manager_update_colour_transform(manager);
477     } else if (g_str_equal(spec->name, "img-aspect-ratio")) {
478         entangle_camera_manager_update_aspect_ratio(manager);
479     } else if (g_str_equal(spec->name, "img-mask-opacity")) {
480         entangle_camera_manager_update_mask_opacity(manager);
481     } else if (g_str_equal(spec->name, "img-mask-enabled")) {
482         entangle_camera_manager_update_mask_enabled(manager);
483     } else if (g_str_equal(spec->name, "img-focus-point") ||
484                g_str_equal(spec->name, "img-grid-lines")) {
485         entangle_camera_manager_update_viewfinder(manager);
486     } else if (g_str_equal(spec->name, "img-embedded-preview")) {
487         entangle_camera_manager_update_image_loader(manager);
488     } else if (g_str_equal(spec->name, "img-onion-skin") ||
489                g_str_equal(spec->name, "img-onion-layers")) {
490         EntangleCameraManagerPrivate *priv = manager->priv;
491         do_select_image(manager, priv->currentImage);
492     }
493 }
494
495
496 static void do_capture_widget_sensitivity(EntangleCameraManager *manager)
497 {
498     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
499
500     EntangleCameraManagerPrivate *priv = manager->priv;
501     GtkWidget *toolCapture;
502     GtkWidget *toolPreview;
503     GtkWidget *toolCancel;
504     GtkWidget *menuCapture;
505     GtkWidget *menuPreview;
506     GtkWidget *menuCancel;
507
508     GtkWidget *toolSession;
509     GtkWidget *menuSession;
510     GtkWidget *menuConnect;
511     GtkWidget *menuDisconnect;
512     GtkWidget *menuHelp;
513
514
515     toolCapture = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-capture"));
516     toolPreview = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-preview"));
517     toolCancel = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-cancel"));
518     menuCapture = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-capture"));
519     menuPreview = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-preview"));
520     menuCancel = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-cancel"));
521
522     toolSession = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-session"));
523     menuSession = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-session"));
524     menuConnect = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-connect"));
525     menuDisconnect = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-disconnect"));
526     menuHelp = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-help-camera"));
527
528
529     gtk_widget_set_sensitive(toolCapture,
530                              priv->cameraReady && !priv->taskCapture && priv->camera &&
531                              entangle_camera_get_has_capture(priv->camera) ? TRUE : FALSE);
532     gtk_widget_set_sensitive(toolPreview,
533                              priv->cameraReady && !priv->taskCapture && priv->camera &&
534                              entangle_camera_get_has_capture(priv->camera) &&
535                              entangle_camera_get_has_preview(priv->camera) &&
536                              !priv->taskCapture ? TRUE : FALSE);
537     gtk_widget_set_sensitive(menuCapture,
538                              priv->cameraReady && !priv->taskCapture && priv->camera &&
539                              entangle_camera_get_has_capture(priv->camera) ? TRUE : FALSE);
540     gtk_widget_set_sensitive(menuPreview,
541                              priv->cameraReady && !priv->taskCapture && priv->camera &&
542                              entangle_camera_get_has_capture(priv->camera) &&
543                              entangle_camera_get_has_preview(priv->camera) &&
544                              !priv->taskCapture ? TRUE : FALSE);
545
546     gtk_widget_set_sensitive(toolSession,
547                              !priv->taskActive);
548     gtk_widget_set_sensitive(menuSession,
549                              !priv->taskActive);
550     gtk_widget_set_sensitive(menuConnect,
551                              priv->camera ? FALSE : TRUE);
552     gtk_widget_set_sensitive(menuDisconnect,
553                              priv->camera && priv->cameraReady ? TRUE : FALSE);
554     gtk_widget_set_sensitive(menuHelp,
555                              priv->camera ? TRUE : FALSE);
556
557     gtk_widget_set_tooltip_text(toolCapture, _("Capture an image"));
558     gtk_widget_set_tooltip_text(toolPreview, _("Continuous capture preview"));
559
560     if (priv->camera) {
561         if (!entangle_camera_get_has_capture(priv->camera))
562             gtk_widget_set_tooltip_text(toolCapture, _("This camera does not support image capture"));
563         if (!entangle_camera_get_has_capture(priv->camera) ||
564             !entangle_camera_get_has_preview(priv->camera))
565             gtk_widget_set_tooltip_text(toolPreview, _("This camera does not support image preview"));
566     }
567
568 #if 0
569     if (priv->camera &&
570         hasControls) {
571         gtk_widget_show(settingsScroll);
572         gtk_widget_set_sensitive(settingsScroll, priv->cameraReady);
573     } else {
574         gtk_widget_hide(settingsScroll);
575     }
576 #endif
577
578     if (priv->taskCapture) {
579         gtk_widget_set_sensitive(toolCancel, True);
580         gtk_widget_set_sensitive(menuCancel, True);
581     } else {
582         gtk_widget_set_sensitive(toolCancel, False);
583         gtk_widget_set_sensitive(menuCancel, False);
584     }
585
586     entangle_camera_manager_update_viewfinder(manager);
587 }
588
589
590 static void do_restore_scroll(GtkWidget *widget,
591                               GdkRectangle *allocation G_GNUC_UNUSED,
592                               EntangleCameraManager *manager)
593 {
594     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
595     GtkAdjustment *hadjust;
596     GtkAdjustment *vadjust;
597     EntangleCameraManagerPrivate *priv = manager->priv;
598
599     if (!entangle_image_display_get_loaded(ENTANGLE_IMAGE_DISPLAY(widget)))
600         return;
601
602     hadjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(priv->imageScroll));
603     vadjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(priv->imageScroll));
604
605     gtk_adjustment_set_value(hadjust,
606                              priv->imageScrollHOffset);
607     gtk_adjustment_set_value(vadjust,
608                              priv->imageScrollVOffset);
609     priv->imageScrollRestored = TRUE;
610 }
611
612
613 static void do_select_image(EntangleCameraManager *manager,
614                             EntangleImage *image)
615 {
616     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
617     g_return_if_fail(!image || ENTANGLE_IS_IMAGE(image));
618     GList *newimages = NULL;
619     GList *oldimages;
620     GList *tmp;
621     GtkAdjustment *hadjust;
622     GtkAdjustment *vadjust;
623
624     EntangleCameraManagerPrivate *priv = manager->priv;
625     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
626
627     ENTANGLE_DEBUG("Selected image %p %s", image,
628                    image ? entangle_image_get_filename(image) : "<none>");
629
630     if (image) {
631         if (entangle_preferences_img_get_onion_skin(prefs))
632             newimages = entangle_session_browser_earlier_images
633                 (priv->sessionBrowser,
634                  entangle_image_get_filename(image) == NULL,
635                  entangle_preferences_img_get_onion_layers(prefs));
636
637         newimages = g_list_prepend(newimages, g_object_ref(image));
638     }
639
640     /* Load all new images first */
641     tmp = newimages;
642     while (tmp) {
643         EntangleImage *thisimage = tmp->data;
644         ENTANGLE_DEBUG("New image %p %s", thisimage,
645                        entangle_image_get_filename(thisimage));
646
647         if (entangle_image_get_filename(thisimage))
648             entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->imageLoader),
649                                         thisimage);
650
651         tmp = tmp->next;
652     }
653
654     /* Now unload old images */
655     tmp = oldimages = entangle_image_display_get_image_list(priv->imageDisplay);
656     while (tmp) {
657         EntangleImage *thisimage = tmp->data;
658
659         ENTANGLE_DEBUG("Old %p %s", thisimage,
660                        entangle_image_get_filename(thisimage));
661
662         if (entangle_image_get_filename(thisimage))
663             entangle_pixbuf_loader_unload(ENTANGLE_PIXBUF_LOADER(priv->imageLoader),
664                                           thisimage);
665
666         tmp = tmp->next;
667     }
668
669     hadjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(priv->imageScroll));
670     vadjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(priv->imageScroll));
671
672     if (priv->imageScrollRestored) {
673         priv->imageScrollHOffset = gtk_adjustment_get_value(hadjust);
674         priv->imageScrollVOffset = gtk_adjustment_get_value(vadjust);
675         priv->imageScrollRestored = FALSE;
676     }
677
678     entangle_image_display_set_image_list(priv->imageDisplay, newimages);
679
680     if (image)
681         g_object_ref(image);
682     if (priv->currentImage)
683         g_object_unref(priv->currentImage);
684     priv->currentImage = image;
685
686     entangle_image_statusbar_set_image(priv->imageStatusbar, priv->currentImage);
687     entangle_image_histogram_set_image(priv->imageHistogram, priv->currentImage);
688     if (priv->imagePresentation)
689         entangle_image_popup_set_image(priv->imagePresentation, priv->currentImage);
690
691     g_list_foreach(oldimages, (GFunc)g_object_unref, NULL);
692     g_list_free(oldimages);
693     g_list_foreach(newimages, (GFunc)g_object_unref, NULL);
694     g_list_free(newimages);
695 }
696
697
698 static void do_camera_task_error(EntangleCameraManager *manager,
699                                  const char *label, GError *error)
700 {
701     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
702
703     GtkWidget *msg = gtk_message_dialog_new(NULL,
704                                             0,
705                                             GTK_MESSAGE_ERROR,
706                                             GTK_BUTTONS_OK,
707                                             _("Operation: %s"), label);
708     gtk_window_set_title(GTK_WINDOW(msg),
709                          _("Entangle: Operation failed"));
710     gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
711                                              "%s",
712                                              error->message);
713     g_signal_connect_swapped(msg,
714                              "response",
715                              G_CALLBACK(gtk_widget_destroy),
716                              msg);
717     gtk_widget_show_all(msg);
718 }
719
720
721 static void do_camera_process_events(EntangleCameraManager *manager);
722
723
724 static void do_camera_load_controls_refresh_finish(GObject *source,
725                                                    GAsyncResult *result,
726                                                    gpointer data)
727 {
728     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
729
730     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
731     EntangleCameraManagerPrivate *priv = manager->priv;
732     EntangleCamera *camera = ENTANGLE_CAMERA(source);
733     GError *error = NULL;
734
735     if (!entangle_camera_load_controls_finish(camera, result, &error)) {
736         GtkWidget *msg = gtk_message_dialog_new(NULL,
737                                                 0,
738                                                 GTK_MESSAGE_ERROR,
739                                                 GTK_BUTTONS_OK,
740                                                 _("Camera load controls failed"));
741         gtk_window_set_title(GTK_WINDOW(msg),
742                              _("Entangle: Camera load controls failed"));
743         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
744                                                  "%s",
745                                                  error->message);
746         g_signal_connect_swapped(msg,
747                                  "response",
748                                  G_CALLBACK(gtk_widget_destroy),
749                                  msg);
750         gtk_widget_show_all(msg);
751
752         g_error_free(error);
753     }
754
755     g_cancellable_reset(priv->monitorCancel);
756
757     do_camera_process_events(manager);
758 }
759
760
761 static void do_camera_process_events_finish(GObject *src,
762                                             GAsyncResult *result,
763                                             gpointer data)
764 {
765     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
766
767     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
768     EntangleCameraManagerPrivate *priv = manager->priv;
769     EntangleCamera *camera = ENTANGLE_CAMERA(src);
770     GError *error = NULL;
771
772     priv->taskProcessEvents = FALSE;
773
774     if (!entangle_camera_process_events_finish(camera,
775                                                result,
776                                                &error)) {
777         if (g_cancellable_is_cancelled(priv->monitorCancel)) {
778             g_cancellable_reset(priv->monitorCancel);
779         } else {
780             do_camera_task_error(manager, _("Monitor"), error);
781         }
782         g_error_free(error);
783         return;
784     }
785
786     if (!priv->cameraReady) {
787         priv->cameraReady = TRUE;
788         do_capture_widget_sensitivity(manager);
789     }
790
791     if (!priv->camera)
792         return;
793
794     if (priv->cameraChanged) {
795         priv->cameraChanged = FALSE;
796         entangle_camera_load_controls_async(priv->camera,
797                                             NULL,
798                                             do_camera_load_controls_refresh_finish,
799                                             manager);
800     } else {
801         do_camera_process_events(manager);
802     }
803 }
804
805
806 static void do_camera_process_events(EntangleCameraManager *manager)
807 {
808     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
809
810     EntangleCameraManagerPrivate *priv = manager->priv;
811
812     if (!priv->camera)
813         return;
814
815     if (priv->taskProcessEvents)
816         return;
817
818     entangle_camera_process_events_async(priv->camera, 500, priv->monitorCancel,
819                                          do_camera_process_events_finish, manager);
820
821     priv->taskProcessEvents = TRUE;
822 }
823
824 static void do_camera_preview_image_finish(GObject *src,
825                                            GAsyncResult *res,
826                                            gpointer opaque);
827
828
829 static EntangleCameraFileTaskData *do_camera_task_begin(EntangleCameraManager *manager)
830 {
831     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager), NULL);
832
833     EntangleCameraManagerPrivate *priv = manager->priv;
834     EntangleCameraFileTaskData *data;
835
836     if (priv->taskActive)
837         return NULL;
838
839     data = g_new0(EntangleCameraFileTaskData, 1);
840     data->manager = g_object_ref(manager);
841
842     g_cancellable_cancel(priv->monitorCancel);
843     g_cancellable_reset(priv->taskConfirm);
844     g_cancellable_reset(priv->taskCancel);
845     priv->taskActive = TRUE;
846
847     g_free(priv->deleteImageDup);
848     priv->deleteImageDup = NULL;
849
850     return data;
851 }
852
853
854 static void do_camera_task_complete(EntangleCameraFileTaskData *data)
855 {
856     EntangleCameraManagerPrivate *priv = data->manager->priv;
857     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(data->manager);
858     GtkWidget *preview;
859
860     if (data->file) {
861         g_object_unref(data->file);
862         data->file = NULL;
863     }
864
865     if (priv->taskPreview &&
866         priv->camera &&
867         !g_cancellable_is_cancelled(priv->taskCancel) &&
868         entangle_preferences_capture_get_continuous_preview(prefs)) {
869         entangle_camera_preview_image_async(priv->camera,
870                                             priv->taskCancel,
871                                             do_camera_preview_image_finish,
872                                             data);
873     } else {
874
875         preview = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-preview"));
876         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(preview), FALSE);
877
878         priv->taskActive = priv->taskPreview = priv->taskCapture = FALSE;
879
880         do_capture_widget_sensitivity(data->manager);
881
882         g_cancellable_reset(priv->taskConfirm);
883         g_cancellable_reset(priv->taskCancel);
884         g_cancellable_reset(priv->monitorCancel);
885
886         do_camera_process_events(data->manager);
887         g_object_unref(data->manager);
888         g_free(data);
889     }
890 }
891
892
893 static void do_camera_file_download(EntangleCamera *cam G_GNUC_UNUSED, EntangleCameraFile *file, void *data)
894 {
895     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
896
897     EntangleCameraManager *manager = data;
898     EntangleCameraManagerPrivate *priv = manager->priv;
899     EntangleImage *image;
900     gchar *localpath;
901
902     ENTANGLE_DEBUG("File download %p %p %p", cam, file, data);
903
904     localpath = entangle_session_next_filename(priv->session, file);
905
906     if (!entangle_camera_file_save_path(file, localpath, NULL)) {
907         ENTANGLE_DEBUG("Failed save path");
908         goto cleanup;
909     }
910     ENTANGLE_DEBUG("Saved to %s", localpath);
911     image = entangle_image_new_file(localpath);
912
913     entangle_session_add(priv->session, image);
914     do_select_image(manager, image);
915
916     g_object_unref(image);
917
918  cleanup:
919     g_free(localpath);
920 }
921
922
923 static void do_camera_delete_file_finish(GObject *src,
924                                          GAsyncResult *res,
925                                          gpointer opaque)
926 {
927     EntangleCameraFileTaskData *data = opaque;
928     EntangleCamera *camera = ENTANGLE_CAMERA(src);
929     GError *error = NULL;
930
931     if (!entangle_camera_delete_file_finish(camera, res, &error)) {
932         do_camera_task_error(data->manager, _("Delete"), error);
933         g_error_free(error);
934         /* Fallthrough to unref */
935     }
936
937     do_camera_task_complete(data);
938 }
939
940
941 static void do_camera_download_file_finish(GObject *src,
942                                            GAsyncResult *res,
943                                            gpointer opaque)
944 {
945     EntangleCameraFileTaskData *data = opaque;
946     EntangleCamera *camera = ENTANGLE_CAMERA(src);
947     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(data->manager);
948     GError *error = NULL;
949
950     if (!entangle_camera_download_file_finish(camera, res, &error)) {
951         do_camera_task_error(data->manager, _("Download"), error);
952         g_error_free(error);
953         /* Fallthrough to delete anyway */
954     }
955
956     if (entangle_preferences_capture_get_delete_file(prefs)) {
957         entangle_camera_delete_file_async(camera,
958                                           data->file,
959                                           NULL,
960                                           do_camera_delete_file_finish,
961                                           data);
962     } else {
963         do_camera_task_complete(data);
964     }
965 }
966
967
968 static void do_camera_capture_image_finish(GObject *src,
969                                            GAsyncResult *res,
970                                            gpointer opaque)
971 {
972     EntangleCameraFileTaskData *data = opaque;
973     EntangleCameraManagerPrivate *priv = data->manager->priv;
974     EntangleCamera *camera = ENTANGLE_CAMERA(src);
975     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(data->manager);
976     GError *error = NULL;
977
978     if (entangle_preferences_interface_get_screen_blank(prefs))
979         entangle_dpms_set_blanking(FALSE, NULL);
980
981     if (!(data->file = entangle_camera_capture_image_finish(camera, res, &error))) {
982         do_camera_task_error(data->manager, _("Capture"), error);
983         do_camera_task_complete(data);
984         g_error_free(error);
985         return;
986     }
987
988     if (g_cancellable_is_cancelled(priv->taskCancel)) {
989         if (entangle_preferences_capture_get_delete_file(prefs)) {
990             entangle_camera_delete_file_async(camera,
991                                               data->file,
992                                               NULL,
993                                               do_camera_delete_file_finish,
994                                               data);
995         } else {
996             do_camera_task_complete(data);
997         }
998     } else {
999         entangle_camera_download_file_async(camera,
1000                                             data->file,
1001                                             NULL,
1002                                             do_camera_download_file_finish,
1003                                             data);
1004     }
1005 }
1006
1007
1008 static void do_camera_capture_image_discard_finish(GObject *src,
1009                                                    GAsyncResult *res,
1010                                                    gpointer opaque)
1011 {
1012     EntangleCameraFileTaskData *data = opaque;
1013     EntangleCameraManagerPrivate *priv = data->manager->priv;
1014     EntangleCamera *camera = ENTANGLE_CAMERA(src);
1015     GError *error = NULL;
1016     char *filename;
1017     char *tmp;
1018
1019     if (!(data->file = entangle_camera_capture_image_finish(camera, res, &error))) {
1020         do_camera_task_error(data->manager, _("Capture"), error);
1021         do_camera_task_complete(data);
1022         g_error_free(error);
1023         return;
1024     }
1025
1026     filename = g_strdup(entangle_camera_file_get_name(data->file));
1027     if ((tmp = strrchr(filename, '.')))
1028         *tmp = '\0';
1029     priv->deleteImageDup = filename;
1030
1031     entangle_camera_delete_file_async(camera,
1032                                       data->file,
1033                                       NULL,
1034                                       do_camera_delete_file_finish,
1035                                       data);
1036 }
1037
1038
1039 static void do_camera_set_viewfinder_finish(GObject *src,
1040                                             GAsyncResult *res,
1041                                             gpointer opaque)
1042 {
1043     EntangleCameraFileTaskData *data = opaque;
1044     EntangleCamera *camera = ENTANGLE_CAMERA(src);
1045     GError *error = NULL;
1046
1047     if (!entangle_camera_set_viewfinder_finish(camera, res, &error)) {
1048         do_camera_task_error(data->manager, _("Capture"), error);
1049         do_camera_task_complete(data);
1050         g_error_free(error);
1051         return;
1052     }
1053
1054     do_camera_task_complete(data);
1055 }
1056
1057
1058 static void do_camera_preview_image_finish(GObject *src,
1059                                            GAsyncResult *res,
1060                                            gpointer opaque)
1061 {
1062     EntangleCameraFileTaskData *data = opaque;
1063     EntangleCameraManagerPrivate *priv = data->manager->priv;
1064     EntangleCamera *camera = ENTANGLE_CAMERA(src);
1065     EntangleCameraFile *file;
1066     GError *error = NULL;
1067
1068     if (!(file = entangle_camera_preview_image_finish(camera, res, &error))) {
1069         if (g_cancellable_is_cancelled(priv->taskCancel) && priv->camera) {
1070             if (entangle_camera_get_has_viewfinder(priv->camera))
1071                 entangle_camera_set_viewfinder_async(priv->camera,
1072                                                      FALSE,
1073                                                      NULL,
1074                                                      do_camera_set_viewfinder_finish,
1075                                                      data);
1076             else
1077                 entangle_camera_capture_image_async(priv->camera,
1078                                                     NULL,
1079                                                     do_camera_capture_image_discard_finish,
1080                                                     data);
1081         } else {
1082             priv->taskPreview = FALSE;
1083             do_camera_task_error(data->manager, _("Preview"), error);
1084             do_camera_task_complete(data);
1085         }
1086         g_error_free(error);
1087         return;
1088     }
1089
1090     g_object_unref(file);
1091
1092     if (g_cancellable_is_cancelled(priv->taskCancel)) {
1093         if (entangle_camera_get_has_viewfinder(priv->camera))
1094             entangle_camera_set_viewfinder_async(priv->camera,
1095                                                  FALSE,
1096                                                  NULL,
1097                                                  do_camera_set_viewfinder_finish,
1098                                                  data);
1099         else
1100             entangle_camera_capture_image_async(priv->camera,
1101                                                 NULL,
1102                                                 do_camera_capture_image_discard_finish,
1103                                                 data);
1104     } else if (g_cancellable_is_cancelled(priv->taskConfirm)) {
1105         EntanglePreferences *prefs = entangle_camera_manager_get_preferences(data->manager);
1106
1107         if (entangle_preferences_interface_get_screen_blank(prefs))
1108             entangle_dpms_set_blanking(TRUE, NULL);
1109
1110         g_cancellable_reset(priv->taskConfirm);
1111         entangle_camera_capture_image_async(priv->camera,
1112                                             priv->taskCancel,
1113                                             do_camera_capture_image_finish,
1114                                             data);
1115     } else {
1116         entangle_camera_preview_image_async(priv->camera,
1117                                             priv->taskCancel,
1118                                             do_camera_preview_image_finish,
1119                                             data);
1120     }
1121 }
1122
1123
1124 static void do_camera_file_add(EntangleCamera *camera, EntangleCameraFile *file, void *opaque)
1125 {
1126     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(opaque));
1127     g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1128
1129     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(opaque);
1130     EntangleCameraFileTaskData *data = g_new0(EntangleCameraFileTaskData, 1);
1131     EntangleCameraManagerPrivate *priv = manager->priv;
1132
1133     ENTANGLE_DEBUG("File add %p %p %p", camera, file, data);
1134
1135     data->manager = g_object_ref(manager);
1136     data->file = g_object_ref(file);
1137
1138     if (priv->deleteImageDup) {
1139         gsize len = strlen(priv->deleteImageDup);
1140         if (strncmp(entangle_camera_file_get_name(file),
1141                     priv->deleteImageDup,
1142                     len) == 0) {
1143             g_free(priv->deleteImageDup);
1144             priv->deleteImageDup = NULL;
1145             entangle_camera_delete_file_async(camera,
1146                                               data->file,
1147                                               NULL,
1148                                               do_camera_delete_file_finish,
1149                                               data);
1150             return;
1151         }
1152     }
1153
1154     entangle_camera_download_file_async(camera,
1155                                         data->file,
1156                                         NULL,
1157                                         do_camera_download_file_finish,
1158                                         data);
1159 }
1160
1161
1162 static void do_camera_file_preview(EntangleCamera *cam G_GNUC_UNUSED, EntangleCameraFile *file, void *data)
1163 {
1164     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1165     g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1166
1167     EntangleCameraManager *manager = data;
1168     EntangleCameraManagerPrivate *priv = manager->priv;
1169     GdkPixbuf *pixbuf;
1170     GByteArray *bytes;
1171     GInputStream *is;
1172     EntangleImage *image;
1173
1174     if (priv->taskPreview &&
1175         priv->taskCancel  &&
1176         !g_cancellable_is_cancelled(priv->taskCancel)) {
1177         ENTANGLE_DEBUG("File preview %p %p %p", cam, file, data);
1178
1179         bytes = entangle_camera_file_get_data(file);
1180         is = g_memory_input_stream_new_from_data(bytes->data, bytes->len, NULL);
1181
1182         pixbuf = gdk_pixbuf_new_from_stream(is, NULL, NULL);
1183
1184         image = entangle_image_new_pixbuf(pixbuf);
1185
1186         do_select_image(manager, image);
1187
1188         g_object_unref(pixbuf);
1189         g_object_unref(is);
1190         g_object_unref(image);
1191     }
1192 }
1193
1194
1195 static void do_entangle_camera_progress_start(EntangleProgress *iface, float target, const char *msg)
1196 {
1197     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(iface));
1198
1199     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(iface);
1200     EntangleCameraManagerPrivate *priv = manager->priv;
1201     GtkWidget *mtr;
1202
1203     priv->taskTarget = target;
1204     mtr = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-progress"));
1205
1206     gtk_widget_set_tooltip_text(mtr, msg);
1207     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtr), msg);
1208     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtr), 0);
1209 }
1210
1211
1212 static void do_entangle_camera_progress_update(EntangleProgress *iface, float current)
1213 {
1214     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(iface));
1215
1216     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(iface);
1217     EntangleCameraManagerPrivate *priv = manager->priv;
1218     GtkWidget *mtr;
1219
1220     mtr = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-progress"));
1221
1222     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtr), current / priv->taskTarget);
1223 }
1224
1225
1226 static void do_entangle_camera_progress_stop(EntangleProgress *iface)
1227 {
1228     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(iface));
1229
1230     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(iface);
1231     EntangleCameraManagerPrivate *priv = manager->priv;
1232     GtkWidget *mtr;
1233
1234     mtr = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-progress"));
1235
1236     gtk_widget_set_tooltip_text(mtr, "");
1237     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtr), "");
1238     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtr), 0);
1239 }
1240
1241
1242 static void entangle_camera_progress_interface_init(gpointer g_iface,
1243                                                     gpointer iface_data G_GNUC_UNUSED)
1244 {
1245     EntangleProgressInterface *iface = g_iface;
1246     iface->start = do_entangle_camera_progress_start;
1247     iface->update = do_entangle_camera_progress_update;
1248     iface->stop = do_entangle_camera_progress_stop;
1249 }
1250
1251
1252 static void do_entangle_camera_manager_set_builder(EntangleWindow *window,
1253                                                    GtkBuilder *builder);
1254
1255 static void entangle_camera_manager_window_interface_init(gpointer g_iface,
1256                                                           gpointer iface_data G_GNUC_UNUSED)
1257 {
1258     EntangleWindowInterface *iface = g_iface;
1259     iface->set_builder = do_entangle_camera_manager_set_builder;
1260 }
1261
1262 static void do_remove_camera(EntangleCameraManager *manager)
1263 {
1264     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1265
1266     EntangleCameraManagerPrivate *priv = manager->priv;
1267     GtkWidget *mtr;
1268
1269     g_cancellable_cancel(priv->monitorCancel);
1270     g_cancellable_cancel(priv->taskCancel);
1271
1272     gtk_window_set_title(GTK_WINDOW(manager), _("Camera Manager - Entangle"));
1273
1274     entangle_control_panel_set_camera(priv->controlPanel, NULL);
1275     entangle_camera_set_progress(priv->camera, NULL);
1276
1277     g_signal_handler_disconnect(priv->camera, priv->sigFilePreview);
1278     g_signal_handler_disconnect(priv->camera, priv->sigFileDownload);
1279     g_signal_handler_disconnect(priv->camera, priv->sigFileAdd);
1280
1281     if (priv->imagePresentation) {
1282         gtk_widget_hide(GTK_WIDGET(priv->imagePresentation));
1283         g_object_unref(priv->imagePresentation);
1284         priv->imagePresentation = NULL;
1285     }
1286
1287     mtr = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-progress"));
1288     gtk_widget_set_tooltip_text(mtr, "");
1289     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(mtr), "");
1290     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(mtr), 0);
1291 }
1292
1293
1294 static void do_camera_load_controls_finish(GObject *source,
1295                                            GAsyncResult *result,
1296                                            gpointer data)
1297 {
1298     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1299
1300     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
1301     EntangleCameraManagerPrivate *priv = manager->priv;
1302
1303     EntangleCamera *cam = ENTANGLE_CAMERA(source);
1304     GError *error = NULL;
1305
1306     if (entangle_camera_load_controls_finish(cam, result, &error)) {
1307         do_capture_widget_sensitivity(manager);
1308         entangle_control_panel_set_camera(priv->controlPanel, priv->camera);
1309     } else {
1310         GtkWidget *msg = gtk_message_dialog_new(NULL,
1311                                                 0,
1312                                                 GTK_MESSAGE_ERROR,
1313                                                 GTK_BUTTONS_OK,
1314                                                 _("Camera load controls failed"));
1315         gtk_window_set_title(GTK_WINDOW(msg),
1316                              _("Entangle: Camera load controls failed"));
1317         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
1318                                                  "%s",
1319                                                  error->message);
1320         g_signal_connect_swapped(msg,
1321                                  "response",
1322                                  G_CALLBACK(gtk_widget_destroy),
1323                                  msg);
1324         gtk_widget_show_all(msg);
1325
1326         g_error_free(error);
1327     }
1328
1329     g_cancellable_reset(priv->monitorCancel);
1330     do_camera_process_events(manager);
1331 }
1332
1333
1334 static void do_camera_connect_finish(GObject *source,
1335                                      GAsyncResult *result,
1336                                      gpointer data)
1337 {
1338     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1339
1340     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
1341     EntangleCameraManagerPrivate *priv = manager->priv;
1342     EntangleCamera *cam = ENTANGLE_CAMERA(source);
1343     GError *error = NULL;
1344
1345     if (entangle_camera_connect_finish(cam, result, &error)) {
1346         entangle_camera_load_controls_async(priv->camera,
1347                                             NULL,
1348                                             do_camera_load_controls_finish,
1349                                             manager);
1350     } else {
1351         int response;
1352         GtkWidget *msg = gtk_message_dialog_new(NULL,
1353                                                 GTK_DIALOG_MODAL,
1354                                                 GTK_MESSAGE_ERROR,
1355                                                 GTK_BUTTONS_NONE,
1356                                                 _("Unable to connect to camera: %s"),
1357                                                 error->message);
1358
1359         gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(msg),
1360                                                    "%s",
1361                                                    _("Check that the camera is not\n\n"
1362                                                      " - opened by another photo <b>application</b>\n"
1363                                                      " - mounted as a <b>filesystem</b> on the desktop\n"
1364                                                      " - in <b>sleep mode</b> to save battery power\n"));
1365
1366         gtk_dialog_add_button(GTK_DIALOG(msg), _("Cancel"), GTK_RESPONSE_CANCEL);
1367         gtk_dialog_add_button(GTK_DIALOG(msg), _("Retry"), GTK_RESPONSE_ACCEPT);
1368         gtk_dialog_set_default_response(GTK_DIALOG(msg), GTK_RESPONSE_ACCEPT);
1369         response = gtk_dialog_run(GTK_DIALOG(msg));
1370
1371         gtk_widget_destroy(msg);
1372
1373         if (response == GTK_RESPONSE_CANCEL) {
1374             entangle_camera_manager_set_camera(manager, NULL);
1375         } else {
1376             entangle_camera_connect_async(cam,
1377                                           NULL,
1378                                           do_camera_connect_finish,
1379                                           manager);
1380         }
1381         g_error_free(error);
1382     }
1383 }
1384
1385
1386 static gboolean need_camera_unmount(EntangleCamera *cam)
1387 {
1388     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1389
1390     if (entangle_camera_is_mounted(cam)) {
1391         int response;
1392         GtkWidget *msg = gtk_message_dialog_new(NULL,
1393                                                 GTK_DIALOG_MODAL,
1394                                                 GTK_MESSAGE_ERROR,
1395                                                 GTK_BUTTONS_NONE,
1396                                                 "%s",
1397                                                 _("Camera is in use"));
1398
1399         gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(msg),
1400                                                    "%s",
1401                                                    _("The camera cannot be opened because it is "
1402                                                      "currently mounted as a filesystem. Do you "
1403                                                      "wish to umount it now ?"));
1404
1405         gtk_dialog_add_button(GTK_DIALOG(msg), _("No"), GTK_RESPONSE_NO);
1406         gtk_dialog_add_button(GTK_DIALOG(msg), _("Yes"), GTK_RESPONSE_YES);
1407         gtk_dialog_set_default_response(GTK_DIALOG(msg), GTK_RESPONSE_YES);
1408
1409         response = gtk_dialog_run(GTK_DIALOG(msg));
1410
1411         gtk_widget_destroy(msg);
1412
1413         if (response == GTK_RESPONSE_YES)
1414             return TRUE;
1415     }
1416     return FALSE;
1417 }
1418
1419
1420 static void do_camera_unmount_finish(GObject *source,
1421                                      GAsyncResult *result,
1422                                      gpointer data)
1423 {
1424     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1425
1426     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
1427     EntangleCameraManagerPrivate *priv = manager->priv;
1428     EntangleCamera *cam = ENTANGLE_CAMERA(source);
1429     GError *error = NULL;
1430
1431     if (entangle_camera_unmount_finish(cam, result, &error)) {
1432         entangle_camera_connect_async(priv->camera,
1433                                       NULL,
1434                                       do_camera_connect_finish,
1435                                       manager);
1436     } else {
1437         GtkWidget *msg = gtk_message_dialog_new(NULL,
1438                                                 0,
1439                                                 GTK_MESSAGE_ERROR,
1440                                                 GTK_BUTTONS_OK,
1441                                                 _("Camera connect failed"));
1442         gtk_window_set_title(GTK_WINDOW(msg),
1443                              _("Entangle: Camera connect failed"));
1444         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
1445                                                  "%s",
1446                                                  error->message);
1447         g_signal_connect_swapped(msg,
1448                                  "response",
1449                                  G_CALLBACK(gtk_widget_destroy),
1450                                  msg);
1451         gtk_widget_show_all(msg);
1452         entangle_camera_manager_set_camera(manager, NULL);
1453
1454         g_error_free(error);
1455     }
1456 }
1457
1458
1459 static void do_camera_control_changed(EntangleCamera *cam G_GNUC_UNUSED,
1460                                       gpointer data)
1461 {
1462     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1463
1464     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
1465     EntangleCameraManagerPrivate *priv = manager->priv;
1466
1467     priv->cameraChanged = TRUE;
1468 }
1469
1470
1471 static void do_add_camera(EntangleCameraManager *manager)
1472 {
1473     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1474
1475     EntangleCameraManagerPrivate *priv = manager->priv;
1476     char *title;
1477
1478     title = g_strdup_printf(_("%s Camera Manager - Entangle"),
1479                             entangle_camera_get_model(priv->camera));
1480
1481     gtk_window_set_title(GTK_WINDOW(manager), title);
1482     g_free(title);
1483
1484     priv->sigFilePreview = g_signal_connect(priv->camera, "camera-file-previewed",
1485                                             G_CALLBACK(do_camera_file_preview), manager);
1486     priv->sigFileDownload = g_signal_connect(priv->camera, "camera-file-downloaded",
1487                                              G_CALLBACK(do_camera_file_download), manager);
1488     priv->sigFileAdd = g_signal_connect(priv->camera, "camera-file-added",
1489                                         G_CALLBACK(do_camera_file_add), manager);
1490     priv->sigChanged = g_signal_connect(priv->camera, "camera-controls-changed",
1491                                         G_CALLBACK(do_camera_control_changed), manager);
1492
1493     entangle_camera_set_progress(priv->camera, ENTANGLE_PROGRESS(manager));
1494
1495     if (need_camera_unmount(priv->camera)) {
1496         entangle_camera_unmount_async(priv->camera,
1497                                       NULL,
1498                                       do_camera_unmount_finish,
1499                                       manager);
1500     } else {
1501         entangle_camera_connect_async(priv->camera,
1502                                       NULL,
1503                                       do_camera_connect_finish,
1504                                       manager);
1505     }
1506 }
1507
1508
1509 static void entangle_camera_manager_get_property(GObject *object,
1510                                                  guint prop_id,
1511                                                  GValue *value,
1512                                                  GParamSpec *pspec)
1513 {
1514     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(object);
1515     EntangleCameraManagerPrivate *priv = manager->priv;
1516
1517     switch (prop_id)
1518         {
1519         case PROP_CAMERA:
1520             g_value_set_object(value, priv->camera);
1521             break;
1522
1523         default:
1524             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1525         }
1526 }
1527
1528
1529 static void do_camera_removed(EntangleCameraList *cameras G_GNUC_UNUSED,
1530                               EntangleCamera *camera,
1531                               gpointer data)
1532 {
1533     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
1534
1535     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
1536     EntangleCameraManagerPrivate *priv = manager->priv;
1537
1538     if (priv->camera == camera)
1539         entangle_camera_manager_set_camera(manager, NULL);
1540 }
1541
1542
1543 static void do_entangle_camera_manager_set_app(GObject *object,
1544                                                GParamSpec *spec G_GNUC_UNUSED)
1545 {
1546     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(object));
1547
1548     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(object);
1549     EntangleCameraManagerPrivate *priv = manager->priv;
1550     EntanglePreferences *prefs;
1551     EntangleCameraList *cameras;
1552     gchar *directory;
1553     gchar *pattern;
1554     GtkWidget *chooser;
1555     EntangleApplication *app;
1556
1557     app = ENTANGLE_APPLICATION(gtk_window_get_application(GTK_WINDOW(manager)));
1558
1559     prefs = entangle_application_get_preferences(app);
1560     priv->sigPrefsNotify = g_signal_connect(prefs,
1561                                             "notify",
1562                                             G_CALLBACK(entangle_camera_manager_prefs_changed),
1563                                             manager);
1564     cameras = entangle_application_get_cameras(app);
1565     g_signal_connect(cameras, "camera-removed", G_CALLBACK(do_camera_removed), manager);
1566     directory = entangle_preferences_capture_get_last_session(prefs);
1567     pattern = entangle_preferences_capture_get_filename_pattern(prefs);
1568
1569     entangle_camera_manager_update_histogram_linear(manager);
1570     entangle_camera_manager_update_colour_transform(manager);
1571     entangle_camera_manager_update_aspect_ratio(manager);
1572     entangle_camera_manager_update_mask_opacity(manager);
1573     entangle_camera_manager_update_mask_enabled(manager);
1574     entangle_camera_manager_update_image_loader(manager);
1575
1576     priv->session = entangle_session_new(directory, pattern);
1577
1578     chooser = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-session-button"));
1579     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), directory);
1580
1581     entangle_session_load(priv->session);
1582     entangle_session_browser_set_session(priv->sessionBrowser, priv->session);
1583
1584     g_free(directory);
1585     g_free(pattern);
1586 }
1587
1588 static void entangle_camera_manager_set_property(GObject *object,
1589                                                  guint prop_id,
1590                                                  const GValue *value,
1591                                                  GParamSpec *pspec)
1592 {
1593     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(object);
1594
1595     ENTANGLE_DEBUG("Set prop %d", prop_id);
1596
1597     switch (prop_id)
1598         {
1599         case PROP_CAMERA:
1600             entangle_camera_manager_set_camera(manager, g_value_get_object(value));
1601             break;
1602
1603         default:
1604             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1605         }
1606 }
1607
1608
1609
1610 static void entangle_camera_manager_finalize(GObject *object)
1611 {
1612     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(object);
1613     EntangleCameraManagerPrivate *priv = manager->priv;
1614
1615     ENTANGLE_DEBUG("Finalize manager");
1616
1617     g_free(priv->deleteImageDup);
1618
1619     g_object_unref(priv->monitorCancel);
1620     g_object_unref(priv->taskCancel);
1621     g_object_unref(priv->taskConfirm);
1622
1623     if (priv->imageLoader)
1624         g_object_unref(priv->imageLoader);
1625     if (priv->thumbLoader)
1626         g_object_unref(priv->thumbLoader);
1627     if (priv->colourTransform)
1628         g_object_unref(priv->colourTransform);
1629     if (priv->camera)
1630         g_object_unref(priv->camera);
1631     if (priv->prefsDisplay)
1632         g_object_unref(priv->prefsDisplay);
1633     if (priv->picker)
1634         g_object_unref(priv->picker);
1635
1636     if (priv->imagePresentation)
1637         g_object_unref(priv->imagePresentation);
1638
1639     if (priv->builder)
1640         g_object_unref(priv->builder);
1641
1642     g_hash_table_destroy(priv->popups);
1643
1644     G_OBJECT_CLASS(entangle_camera_manager_parent_class)->finalize(object);
1645 }
1646
1647
1648 static void entangle_camera_manager_class_init(EntangleCameraManagerClass *klass)
1649 {
1650     GObjectClass *object_class = G_OBJECT_CLASS(klass);
1651
1652     object_class->finalize = entangle_camera_manager_finalize;
1653     object_class->get_property = entangle_camera_manager_get_property;
1654     object_class->set_property = entangle_camera_manager_set_property;
1655
1656     g_signal_new("closed",
1657                  G_TYPE_FROM_CLASS(klass),
1658                  G_SIGNAL_RUN_FIRST,
1659                  G_STRUCT_OFFSET(EntangleCameraManagerClass, manager_connect),
1660                  NULL, NULL,
1661                  g_cclosure_marshal_VOID__VOID,
1662                  G_TYPE_NONE,
1663                  0);
1664
1665     g_object_class_install_property(object_class,
1666                                     PROP_CAMERA,
1667                                     g_param_spec_object("camera",
1668                                                         "Camera",
1669                                                         "Camera to be managed",
1670                                                         ENTANGLE_TYPE_CAMERA,
1671                                                         G_PARAM_READWRITE |
1672                                                         G_PARAM_STATIC_NAME |
1673                                                         G_PARAM_STATIC_NICK |
1674                                                         G_PARAM_STATIC_BLURB));
1675
1676     g_type_class_add_private(klass, sizeof(EntangleCameraManagerPrivate));
1677 }
1678
1679
1680 void do_menu_help_summary(GtkMenuItem *src G_GNUC_UNUSED,
1681                           EntangleCameraManager *manager)
1682 {
1683     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1684
1685     EntangleCameraManagerPrivate *priv = manager->priv;
1686
1687     if (!priv->summary) {
1688         priv->summary = entangle_camera_info_new();
1689         entangle_camera_info_set_camera(priv->summary, priv->camera);
1690         entangle_camera_info_set_data(priv->summary, ENTANGLE_CAMERA_INFO_DATA_SUMMARY);
1691         gtk_window_set_transient_for(GTK_WINDOW(priv->summary),
1692                                      GTK_WINDOW(manager));
1693     }
1694
1695     gtk_widget_show(GTK_WIDGET(priv->summary));
1696 }
1697
1698 void do_menu_help_manual(GtkMenuItem *src G_GNUC_UNUSED,
1699                          EntangleCameraManager *manager)
1700 {
1701     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1702
1703     EntangleCameraManagerPrivate *priv = manager->priv;
1704
1705     if (!priv->manual) {
1706         priv->manual = entangle_camera_info_new();
1707         entangle_camera_info_set_camera(priv->manual, priv->camera);
1708         entangle_camera_info_set_data(priv->manual, ENTANGLE_CAMERA_INFO_DATA_MANUAL);
1709         gtk_window_set_transient_for(GTK_WINDOW(priv->manual),
1710                                      GTK_WINDOW(manager));
1711     }
1712
1713     gtk_widget_show(GTK_WIDGET(priv->manual));
1714 }
1715
1716 void do_menu_help_driver(GtkMenuItem *src G_GNUC_UNUSED,
1717                          EntangleCameraManager *manager)
1718 {
1719     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1720
1721     EntangleCameraManagerPrivate *priv = manager->priv;
1722
1723     if (!priv->driver) {
1724         priv->driver = entangle_camera_info_new();
1725         entangle_camera_info_set_camera(priv->driver, priv->camera);
1726         entangle_camera_info_set_data(priv->driver, ENTANGLE_CAMERA_INFO_DATA_DRIVER);
1727         gtk_window_set_transient_for(GTK_WINDOW(priv->driver),
1728                                      GTK_WINDOW(manager));
1729     }
1730
1731     gtk_widget_show(GTK_WIDGET(priv->driver));
1732 }
1733
1734 void do_menu_help_supported(GtkMenuItem *src G_GNUC_UNUSED,
1735                             EntangleCameraManager *manager)
1736 {
1737     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1738
1739     EntangleCameraManagerPrivate *priv = manager->priv;
1740
1741     if (!priv->supported) {
1742         EntangleApplication *app = ENTANGLE_APPLICATION(gtk_window_get_application(GTK_WINDOW(manager)));
1743         priv->supported = entangle_camera_support_new();
1744         entangle_camera_support_set_camera_list(priv->supported, entangle_application_get_cameras(app));
1745         gtk_window_set_transient_for(GTK_WINDOW(priv->supported),
1746                                      GTK_WINDOW(manager));
1747     }
1748
1749     gtk_widget_show(GTK_WIDGET(priv->supported));
1750 }
1751
1752
1753 void do_menu_new_window(GtkImageMenuItem *src G_GNUC_UNUSED,
1754                         EntangleCameraManager *manager)
1755 {
1756     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1757
1758     GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
1759     EntangleCameraManager *newmanager = entangle_camera_manager_new();
1760     gtk_window_set_application(GTK_WINDOW(newmanager), app);
1761
1762     gtk_widget_show(GTK_WIDGET(newmanager));
1763     gtk_window_present(GTK_WINDOW(newmanager));
1764 }
1765
1766
1767 void do_toolbar_select_session(GtkFileChooserButton *src,
1768                                EntangleCameraManager *manager)
1769 {
1770     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1771
1772     EntangleCameraManagerPrivate *priv = manager->priv;
1773     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
1774     EntangleSession *session;
1775     gchar *pattern;
1776     gchar *dir;
1777
1778     do_select_image(manager, NULL);
1779     dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(src));
1780     pattern = entangle_preferences_capture_get_filename_pattern(prefs);
1781     session = entangle_session_new(dir, pattern);
1782     entangle_preferences_capture_set_last_session(prefs, dir);
1783     g_free(dir);
1784     g_free(pattern);
1785     entangle_session_load(session);
1786     if (priv->session)
1787         g_object_unref(priv->session);
1788     priv->session = session;
1789     entangle_session_browser_set_session(priv->sessionBrowser, session);
1790     g_hash_table_remove_all(priv->popups);
1791     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(src), dir);
1792 }
1793
1794
1795 void do_menu_select_session(GtkImageMenuItem *src G_GNUC_UNUSED,
1796                             EntangleCameraManager *manager)
1797 {
1798     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1799
1800     EntangleCameraManagerPrivate *priv = manager->priv;
1801     GtkWidget *chooser;
1802     gchar *dir;
1803     EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
1804
1805     chooser = gtk_file_chooser_dialog_new(_("Select a folder"),
1806                                           GTK_WINDOW(manager),
1807                                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1808                                           GTK_STOCK_CANCEL,
1809                                           GTK_RESPONSE_REJECT,
1810                                           GTK_STOCK_OK,
1811                                           GTK_RESPONSE_ACCEPT,
1812                                           NULL);
1813     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chooser), TRUE);
1814     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE);
1815
1816     dir = entangle_preferences_capture_get_last_session(prefs);
1817     g_mkdir_with_parents(dir, 0777);
1818
1819     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), dir);
1820     g_free(dir);
1821
1822     gtk_widget_hide(chooser);
1823
1824     if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
1825         GtkWidget *toolchooser = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-session-button"));
1826         EntangleSession *session;
1827         gchar *pattern;
1828         do_select_image(manager, NULL);
1829         dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1830         pattern = entangle_preferences_capture_get_filename_pattern(prefs);
1831         session = entangle_session_new(dir, pattern);
1832         entangle_preferences_capture_set_last_session(prefs, dir);
1833         g_free(dir);
1834         g_free(pattern);
1835         entangle_session_load(session);
1836         if (priv->session)
1837             g_object_unref(priv->session);
1838         priv->session = session;
1839         entangle_session_browser_set_session(priv->sessionBrowser, session);
1840         g_hash_table_remove_all(priv->popups);
1841         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(toolchooser), dir);
1842     }
1843
1844     gtk_widget_destroy(chooser);
1845 }
1846
1847
1848 void do_menu_settings_toggled(GtkCheckMenuItem *src,
1849                               EntangleCameraManager *manager)
1850 {
1851     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1852
1853     EntangleCameraManagerPrivate *priv = manager->priv;
1854     GtkWidget *settings;
1855     GtkWidget *toolbar;
1856     gboolean active;
1857
1858     settings = GTK_WIDGET(gtk_builder_get_object(priv->builder, "settings-box"));
1859     toolbar = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-settings"));
1860
1861     active = gtk_check_menu_item_get_active(src);
1862     gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolbar), active);
1863
1864     if (active)
1865         gtk_widget_show(settings);
1866     else
1867         gtk_widget_hide(settings);
1868 }
1869
1870
1871 void do_toolbar_settings(GtkToggleToolButton *src,
1872                          EntangleCameraManager *manager)
1873 {
1874     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1875
1876     EntangleCameraManagerPrivate *priv = manager->priv;
1877     GtkWidget *settings;
1878     GtkWidget *menu;
1879     gboolean active;
1880
1881     settings = GTK_WIDGET(gtk_builder_get_object(priv->builder, "settings-box"));
1882     menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-settings"));
1883
1884     active = gtk_toggle_tool_button_get_active(src);
1885     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), active);
1886
1887     if (active)
1888         gtk_widget_show(settings);
1889     else
1890         gtk_widget_hide(settings);
1891 }
1892
1893
1894 void do_toolbar_cancel_clicked(GtkToolButton *src G_GNUC_UNUSED,
1895                                EntangleCameraManager *manager)
1896 {
1897     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1898
1899     EntangleCameraManagerPrivate *priv = manager->priv;
1900
1901     if (priv->taskCancel)
1902         g_cancellable_cancel(priv->taskCancel);
1903 }
1904
1905
1906 static void do_camera_manager_capture(EntangleCameraManager *manager)
1907 {
1908     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1909
1910     EntangleCameraManagerPrivate *priv = manager->priv;
1911     GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(priv->builder, "menu-sync-windows"));
1912
1913     if (gtk_check_menu_item_get_active(item)) {
1914         GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
1915         GList *windows = gtk_application_get_windows(app);
1916
1917         while (windows) {
1918             GtkWindow *window = windows->data;
1919
1920             if (ENTANGLE_IS_CAMERA_MANAGER(window))
1921                 entangle_camera_manager_capture(ENTANGLE_CAMERA_MANAGER(window));
1922
1923             windows = windows->next;
1924         }
1925     } else {
1926         entangle_camera_manager_capture(manager);
1927     }
1928 }
1929
1930
1931 static void do_camera_manager_preview_begin(EntangleCameraManager *manager)
1932 {
1933     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1934
1935     EntangleCameraManagerPrivate *priv = manager->priv;
1936     GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(priv->builder, "menu-sync-windows"));
1937
1938     if (gtk_check_menu_item_get_active(item)) {
1939         GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
1940         GList *windows = gtk_application_get_windows(app);
1941
1942         while (windows) {
1943             GtkWindow *window = windows->data;
1944             if (ENTANGLE_IS_CAMERA_MANAGER(window))
1945                 entangle_camera_manager_preview_begin(ENTANGLE_CAMERA_MANAGER(window));
1946
1947             windows = windows->next;
1948         }
1949     } else {
1950         entangle_camera_manager_preview_begin(manager);
1951     }
1952 }
1953
1954
1955 static void do_camera_manager_preview_cancel(EntangleCameraManager *manager)
1956 {
1957     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1958
1959     EntangleCameraManagerPrivate *priv = manager->priv;
1960     GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(priv->builder, "menu-sync-windows"));
1961
1962     if (gtk_check_menu_item_get_active(item)) {
1963         GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
1964         GList *windows = gtk_application_get_windows(app);
1965
1966         while (windows) {
1967             GtkWindow *window = windows->data;
1968             if (ENTANGLE_IS_CAMERA_MANAGER(window))
1969                 entangle_camera_manager_preview_cancel(ENTANGLE_CAMERA_MANAGER(window));
1970
1971             windows = windows->next;
1972         }
1973     } else {
1974         entangle_camera_manager_preview_cancel(manager);
1975     }
1976 }
1977
1978
1979 void entangle_camera_manager_capture(EntangleCameraManager *manager)
1980 {
1981     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
1982
1983     EntangleCameraManagerPrivate *priv = manager->priv;
1984     ENTANGLE_DEBUG("starting capture operation");
1985
1986     if (!priv->camera)
1987         return;
1988
1989     if (priv->taskPreview) {
1990         g_cancellable_cancel(priv->taskConfirm);
1991     } else {
1992         EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
1993         EntangleCameraFileTaskData *data;
1994         if (!(data = do_camera_task_begin(manager)))
1995             return;
1996
1997         priv->taskCapture = TRUE;
1998         do_capture_widget_sensitivity(manager);
1999
2000         if (entangle_preferences_interface_get_screen_blank(prefs))
2001             entangle_dpms_set_blanking(TRUE, NULL);
2002
2003         entangle_camera_capture_image_async(priv->camera,
2004                                             priv->taskCancel,
2005                                             do_camera_capture_image_finish,
2006                                             data);
2007     }
2008 }
2009
2010
2011 void entangle_camera_manager_preview_begin(EntangleCameraManager *manager)
2012 {
2013     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2014
2015     EntangleCameraManagerPrivate *priv = manager->priv;
2016     EntangleCameraFileTaskData *data;
2017
2018     if (!priv->camera)
2019         return;
2020
2021     ENTANGLE_DEBUG("starting preview operation");
2022     if (!(data = do_camera_task_begin(manager)))
2023         return;
2024
2025     priv->taskPreview = TRUE;
2026     do_capture_widget_sensitivity(manager);
2027     entangle_camera_preview_image_async(priv->camera,
2028                                         priv->taskCancel,
2029                                         do_camera_preview_image_finish,
2030                                         data);
2031 }
2032
2033
2034 void entangle_camera_manager_preview_cancel(EntangleCameraManager *manager)
2035 {
2036     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2037
2038     EntangleCameraManagerPrivate *priv = manager->priv;
2039     EntangleImage *img;
2040
2041     if (!priv->camera)
2042         return;
2043
2044     if (priv->taskPreview) {
2045         ENTANGLE_DEBUG("Cancelling capture operation");
2046         g_cancellable_cancel(priv->taskCancel);
2047
2048         img = entangle_session_browser_selected_image(priv->sessionBrowser);
2049         if (img) {
2050             do_select_image(manager, img);
2051             g_object_unref(img);
2052         }
2053     }
2054 }
2055
2056
2057 void do_toolbar_capture(GtkToolButton *src G_GNUC_UNUSED,
2058                         EntangleCameraManager *manager)
2059 {
2060     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2061
2062     do_camera_manager_capture(manager);
2063 }
2064
2065
2066 void do_toolbar_preview(GtkToggleToolButton *src,
2067                         EntangleCameraManager *manager)
2068 {
2069     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2070
2071     EntangleCameraManagerPrivate *priv = manager->priv;
2072     GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(priv->builder, "menu-preview"));
2073
2074     if (gtk_toggle_tool_button_get_active(src)) {
2075         if (!gtk_check_menu_item_get_active(item)) {
2076             gtk_check_menu_item_set_active(item, True);
2077             do_camera_manager_preview_begin(manager);
2078         }
2079     } else {
2080         if (gtk_check_menu_item_get_active(item)) {
2081             gtk_check_menu_item_set_active(item, False);
2082             do_camera_manager_preview_cancel(manager);
2083         }
2084     }
2085 }
2086
2087
2088 void do_menu_capture(GtkMenuItem *src G_GNUC_UNUSED,
2089                      EntangleCameraManager *manager)
2090 {
2091     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2092
2093     do_camera_manager_capture(manager);
2094 }
2095
2096
2097 void do_menu_preview(GtkMenuItem *src,
2098                      EntangleCameraManager *manager)
2099 {
2100     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2101
2102     EntangleCameraManagerPrivate *priv = manager->priv;
2103     GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(priv->builder, "toolbar-preview"));
2104
2105     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(src))) {
2106         if (!gtk_toggle_tool_button_get_active(button)) {
2107             gtk_toggle_tool_button_set_active(button, True);
2108             do_camera_manager_preview_begin(manager);
2109         }
2110     } else {
2111         if (gtk_toggle_tool_button_get_active(button)) {
2112             gtk_toggle_tool_button_set_active(button, False);
2113             do_camera_manager_preview_cancel(manager);
2114         }
2115     }
2116 }
2117
2118
2119 void do_menu_cancel(GtkMenuItem *src G_GNUC_UNUSED,
2120                     EntangleCameraManager *manager)
2121 {
2122     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2123
2124     EntangleCameraManagerPrivate *priv = manager->priv;
2125
2126     if (priv->taskCancel)
2127         g_cancellable_cancel(priv->taskCancel);
2128 }
2129
2130
2131 static void do_camera_autofocus_finish(GObject *source,
2132                                        GAsyncResult *result,
2133                                        gpointer data)
2134 {
2135     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
2136
2137     EntangleCamera *camera = ENTANGLE_CAMERA(source);
2138     GError *error = NULL;
2139
2140     if (!entangle_camera_autofocus_finish(camera, result, &error)) {
2141         GtkWidget *msg = gtk_message_dialog_new(NULL,
2142                                                 0,
2143                                                 GTK_MESSAGE_ERROR,
2144                                                 GTK_BUTTONS_OK,
2145                                                 _("Autofocus failed"));
2146         gtk_window_set_title(GTK_WINDOW(msg),
2147                              _("Entangle: Camera autofocus failed"));
2148         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
2149                                                  "%s",
2150                                                  error->message);
2151         g_signal_connect_swapped(msg,
2152                                  "response",
2153                                  G_CALLBACK(gtk_widget_destroy),
2154                                  msg);
2155         gtk_widget_show_all(msg);
2156
2157         g_error_free(error);
2158     }
2159 }
2160
2161
2162 static void do_camera_manualfocus_finish(GObject *source,
2163                                          GAsyncResult *result,
2164                                          gpointer data)
2165 {
2166     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
2167
2168     EntangleCamera *camera = ENTANGLE_CAMERA(source);
2169     GError *error = NULL;
2170
2171     if (!entangle_camera_manualfocus_finish(camera, result, &error)) {
2172         GtkWidget *msg = gtk_message_dialog_new(NULL,
2173                                                 0,
2174                                                 GTK_MESSAGE_ERROR,
2175                                                 GTK_BUTTONS_OK,
2176                                                 _("Manual focus failed"));
2177         gtk_window_set_title(GTK_WINDOW(msg),
2178                              _("Entangle: Camera manual focus failed"));
2179         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
2180                                                  "%s",
2181                                                  error->message);
2182         g_signal_connect_swapped(msg,
2183                                  "response",
2184                                  G_CALLBACK(gtk_widget_destroy),
2185                                  msg);
2186         gtk_widget_show_all(msg);
2187
2188         g_error_free(error);
2189     }
2190 }
2191
2192
2193 gboolean do_manager_key_release(GtkWidget *widget G_GNUC_UNUSED,
2194                                 GdkEventKey *ev,
2195                                 gpointer data)
2196 {
2197     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data), FALSE);
2198
2199     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
2200     EntangleCameraManagerPrivate *priv = manager->priv;
2201
2202     switch (ev->keyval) {
2203     case GDK_KEY_m: {
2204         EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
2205         gboolean enabled = entangle_preferences_img_get_mask_enabled(prefs);
2206         entangle_preferences_img_set_mask_enabled(prefs, !enabled);
2207     }   break;
2208
2209     case GDK_KEY_h: {
2210         EntanglePreferences *prefs = entangle_camera_manager_get_preferences(manager);
2211         gboolean linear = entangle_preferences_interface_get_histogram_linear(prefs);
2212         entangle_preferences_interface_set_histogram_linear(prefs, !linear);
2213     }   break;
2214
2215     case GDK_KEY_a: {
2216         if (priv->taskPreview) {
2217             entangle_camera_autofocus_async(priv->camera,
2218                                             NULL,
2219                                             do_camera_autofocus_finish,
2220                                             manager);
2221         }
2222     }   break;
2223
2224     case GDK_KEY_comma: {
2225         if (priv->taskPreview) {
2226             entangle_camera_manualfocus_async(priv->camera,
2227                                               -64,
2228                                               NULL,
2229                                               do_camera_manualfocus_finish,
2230                                               manager);
2231         }
2232     }   break;
2233
2234     case GDK_KEY_period: {
2235         if (priv->taskPreview) {
2236             entangle_camera_manualfocus_async(priv->camera,
2237                                               64,
2238                                               NULL,
2239                                               do_camera_manualfocus_finish,
2240                                               manager);
2241         }
2242     }   break;
2243
2244     case GDK_KEY_less: {
2245         if (priv->taskPreview) {
2246             entangle_camera_manualfocus_async(priv->camera,
2247                                               -512,
2248                                               NULL,
2249                                               do_camera_manualfocus_finish,
2250                                               manager);
2251         }
2252     }   break;
2253
2254     case GDK_KEY_greater: {
2255         if (priv->taskPreview) {
2256             entangle_camera_manualfocus_async(priv->camera,
2257                                               512,
2258                                               NULL,
2259                                               do_camera_manualfocus_finish,
2260                                               manager);
2261         }
2262     }   break;
2263
2264     default:
2265         break;
2266     }
2267
2268     return FALSE;
2269 }
2270
2271
2272 static void do_zoom_widget_sensitivity(EntangleCameraManager *manager)
2273 {
2274     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2275
2276     EntangleCameraManagerPrivate *priv = manager->priv;
2277     GtkWidget *toolzoomnormal = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-zoom-normal"));
2278     GtkWidget *toolzoombest = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-zoom-best"));
2279     GtkWidget *toolzoomin = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-zoom-in"));
2280     GtkWidget *toolzoomout = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-zoom-out"));
2281
2282     GtkWidget *menuzoomnormal = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-zoom-normal"));
2283     GtkWidget *menuzoombest = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-zoom-best"));
2284     GtkWidget *menuzoomin = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-zoom-in"));
2285     GtkWidget *menuzoomout = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-zoom-out"));
2286     gboolean autoscale;
2287
2288     autoscale = entangle_image_display_get_autoscale(priv->imageDisplay);
2289
2290     if (autoscale) {
2291         gtk_widget_set_sensitive(toolzoombest, FALSE);
2292         gtk_widget_set_sensitive(toolzoomnormal, TRUE);
2293         gtk_widget_set_sensitive(toolzoomin, FALSE);
2294         gtk_widget_set_sensitive(toolzoomout, FALSE);
2295
2296         gtk_widget_set_sensitive(menuzoombest, FALSE);
2297         gtk_widget_set_sensitive(menuzoomnormal, TRUE);
2298         gtk_widget_set_sensitive(menuzoomin, FALSE);
2299         gtk_widget_set_sensitive(menuzoomout, FALSE);
2300     } else {
2301         gtk_widget_set_sensitive(toolzoombest, TRUE);
2302         gtk_widget_set_sensitive(toolzoomnormal, priv->zoomLevel == 0 ? FALSE : TRUE);
2303         gtk_widget_set_sensitive(toolzoomin, priv->zoomLevel == 10 ? FALSE : TRUE);
2304         gtk_widget_set_sensitive(toolzoomout, priv->zoomLevel == -10 ? FALSE : TRUE);
2305
2306         gtk_widget_set_sensitive(menuzoombest, TRUE);
2307         gtk_widget_set_sensitive(menuzoomnormal, priv->zoomLevel == 0 ? FALSE : TRUE);
2308         gtk_widget_set_sensitive(menuzoomin, priv->zoomLevel == 10 ? FALSE : TRUE);
2309         gtk_widget_set_sensitive(menuzoomout, priv->zoomLevel == -10 ? FALSE : TRUE);
2310     }
2311 }
2312
2313
2314 static void entangle_camera_manager_update_zoom(EntangleCameraManager *manager)
2315 {
2316     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2317
2318     EntangleCameraManagerPrivate *priv = manager->priv;
2319
2320     if (priv->zoomLevel > 0)
2321         entangle_image_display_set_scale(priv->imageDisplay, 1.0+priv->zoomLevel);
2322     else if (priv->zoomLevel < 0)
2323         entangle_image_display_set_scale(priv->imageDisplay, 1.0/pow(1.5, -priv->zoomLevel));
2324     else
2325         entangle_image_display_set_scale(priv->imageDisplay, 0.0);
2326     do_zoom_widget_sensitivity(manager);
2327 }
2328
2329
2330 static void entangle_camera_manager_zoom_in(EntangleCameraManager *manager)
2331 {
2332     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2333
2334     EntangleCameraManagerPrivate *priv = manager->priv;
2335
2336     if (priv->zoomLevel < 10)
2337         priv->zoomLevel += 1;
2338
2339     entangle_camera_manager_update_zoom(manager);
2340 }
2341
2342
2343 static void entangle_camera_manager_zoom_out(EntangleCameraManager *manager)
2344 {
2345     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2346
2347     EntangleCameraManagerPrivate *priv = manager->priv;
2348
2349     if (priv->zoomLevel > -10)
2350         priv->zoomLevel -= 1;
2351
2352     entangle_camera_manager_update_zoom(manager);
2353 }
2354
2355 static void entangle_camera_manager_zoom_normal(EntangleCameraManager *manager)
2356 {
2357     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2358
2359     EntangleCameraManagerPrivate *priv = manager->priv;
2360
2361     priv->zoomLevel = 0;
2362     entangle_image_display_set_scale(priv->imageDisplay, 0.0);
2363     entangle_image_display_set_autoscale(priv->imageDisplay, FALSE);
2364     do_zoom_widget_sensitivity(manager);
2365 }
2366
2367
2368 static void entangle_camera_manager_zoom_best(EntangleCameraManager *manager)
2369 {
2370     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2371
2372     EntangleCameraManagerPrivate *priv = manager->priv;
2373
2374     priv->zoomLevel = 0;
2375     entangle_image_display_set_autoscale(priv->imageDisplay, TRUE);
2376     do_zoom_widget_sensitivity(manager);
2377 }
2378
2379
2380 void do_toolbar_zoom_in(GtkToolButton *src G_GNUC_UNUSED,
2381                         EntangleCameraManager *manager)
2382 {
2383     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2384
2385     entangle_camera_manager_zoom_in(manager);
2386 }
2387
2388
2389 void do_toolbar_zoom_out(GtkToolButton *src G_GNUC_UNUSED,
2390                          EntangleCameraManager *manager)
2391 {
2392     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2393
2394     entangle_camera_manager_zoom_out(manager);
2395 }
2396
2397
2398 void do_toolbar_zoom_normal(GtkToolButton *src G_GNUC_UNUSED,
2399                             EntangleCameraManager *manager)
2400 {
2401     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2402
2403     entangle_camera_manager_zoom_normal(manager);
2404 }
2405
2406
2407 void do_toolbar_zoom_best(GtkToolButton *src G_GNUC_UNUSED,
2408                           EntangleCameraManager *manager)
2409 {
2410     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2411
2412     entangle_camera_manager_zoom_best(manager);
2413 }
2414
2415
2416 void do_menu_zoom_in(GtkImageMenuItem *src G_GNUC_UNUSED,
2417                      EntangleCameraManager *manager)
2418 {
2419     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2420
2421     entangle_camera_manager_zoom_in(manager);
2422 }
2423
2424
2425 void do_menu_zoom_out(GtkImageMenuItem *src G_GNUC_UNUSED,
2426                       EntangleCameraManager *manager)
2427 {
2428     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2429
2430     entangle_camera_manager_zoom_out(manager);
2431 }
2432
2433 void do_menu_zoom_normal(GtkImageMenuItem *src G_GNUC_UNUSED,
2434                          EntangleCameraManager *manager)
2435 {
2436     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2437
2438     entangle_camera_manager_zoom_normal(manager);
2439 }
2440
2441 void do_menu_zoom_best(GtkImageMenuItem *src G_GNUC_UNUSED,
2442                        EntangleCameraManager *manager)
2443 {
2444     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2445
2446     entangle_camera_manager_zoom_best(manager);
2447 }
2448
2449
2450 void do_toolbar_fullscreen(GtkToggleToolButton *src,
2451                            EntangleCameraManager *manager)
2452 {
2453     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2454
2455     EntangleCameraManagerPrivate *priv = manager->priv;
2456     GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-fullscreen"));
2457     GtkWidget *menubar = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menubar"));
2458
2459     if (gtk_toggle_tool_button_get_active(src)) {
2460         if (0) gtk_widget_hide(menubar);
2461         gtk_window_fullscreen(GTK_WINDOW(manager));
2462     } else {
2463         gtk_window_unfullscreen(GTK_WINDOW(manager));
2464         if (0) gtk_widget_show(menubar);
2465     }
2466
2467     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)) !=
2468         gtk_toggle_tool_button_get_active(src))
2469         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu),
2470                                        gtk_toggle_tool_button_get_active(src));
2471 }
2472
2473
2474 void do_menu_fullscreen(GtkCheckMenuItem *src,
2475                         EntangleCameraManager *manager)
2476 {
2477     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2478
2479     EntangleCameraManagerPrivate *priv = manager->priv;
2480     GtkWidget *tool = GTK_WIDGET(gtk_builder_get_object(priv->builder, "toolbar-fullscreen"));
2481     GtkWidget *menubar = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menubar"));
2482
2483     if (gtk_check_menu_item_get_active(src)) {
2484         gtk_widget_hide(menubar);
2485         gtk_window_fullscreen(GTK_WINDOW(manager));
2486     } else {
2487         gtk_window_unfullscreen(GTK_WINDOW(manager));
2488         gtk_widget_show(menubar);
2489     }
2490
2491     if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(tool)) !=
2492         gtk_check_menu_item_get_active(src))
2493         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(tool),
2494                                           gtk_check_menu_item_get_active(src));
2495 }
2496
2497
2498 static void do_presentation_end(EntangleImagePopup *popup G_GNUC_UNUSED,
2499                                 EntangleCameraManager *manager)
2500 {
2501     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2502
2503     EntangleCameraManagerPrivate *priv = manager->priv;
2504     GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-presentation"));
2505
2506     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE);
2507 }
2508
2509 void do_menu_presentation(GtkCheckMenuItem *src,
2510                           EntangleCameraManager *manager)
2511 {
2512     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2513
2514     EntangleCameraManagerPrivate *priv = manager->priv;
2515
2516     if (gtk_check_menu_item_get_active(src)) {
2517         if (!priv->imagePresentation) {
2518             priv->imagePresentation = entangle_image_popup_new();
2519             g_signal_connect(priv->imagePresentation, "popup-close", G_CALLBACK(do_presentation_end), manager);
2520         }
2521         entangle_image_popup_set_image(priv->imagePresentation, priv->currentImage);
2522         entangle_image_popup_show_on_monitor(priv->imagePresentation,
2523                                              priv->presentationMonitor);
2524     } else if (priv->imagePresentation) {
2525         gtk_widget_hide(GTK_WIDGET(priv->imagePresentation));
2526         g_object_unref(priv->imagePresentation);
2527         priv->imagePresentation = NULL;
2528     }
2529 }
2530
2531
2532 void do_menu_preferences(GtkCheckMenuItem *src G_GNUC_UNUSED,
2533                          EntangleCameraManager *manager)
2534 {
2535     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2536
2537     EntangleCameraManagerPrivate *priv = manager->priv;
2538     if (priv->prefsDisplay == NULL) {
2539         GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
2540         priv->prefsDisplay = entangle_preferences_display_new();
2541         gtk_application_add_window(app, GTK_WINDOW(priv->prefsDisplay));
2542         gtk_window_set_transient_for(GTK_WINDOW(priv->prefsDisplay),
2543                                      GTK_WINDOW(manager));
2544     }
2545
2546     gtk_widget_show(GTK_WIDGET(priv->prefsDisplay));
2547 }
2548
2549
2550 void do_menu_close(GtkMenuItem *src G_GNUC_UNUSED,
2551                    EntangleCameraManager *manager)
2552 {
2553     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2554
2555     gtk_widget_destroy(GTK_WIDGET(manager));
2556 }
2557
2558
2559 void do_menu_quit(GtkMenuItem *src G_GNUC_UNUSED,
2560                   EntangleCameraManager *manager)
2561 {
2562     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2563
2564     GtkApplication *app = gtk_window_get_application(GTK_WINDOW(manager));
2565     GList *windows = gtk_application_get_windows(app);
2566     GList *tmp = g_list_copy(windows);
2567
2568     while (tmp) {
2569         GtkWidget *window = GTK_WIDGET(tmp->data);
2570         gtk_widget_destroy(window);
2571         tmp = tmp->next;
2572     }
2573     g_list_free(tmp);
2574 }
2575
2576
2577 void do_menu_help_about(GtkMenuItem *src G_GNUC_UNUSED,
2578                         EntangleCameraManager *manager)
2579 {
2580     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2581
2582     EntangleCameraManagerPrivate *priv = manager->priv;
2583
2584     if (!priv->about) {
2585         priv->about = entangle_help_about_new();
2586         gtk_window_set_transient_for(GTK_WINDOW(priv->about),
2587                                      GTK_WINDOW(manager));
2588     }
2589
2590     gtk_widget_show(GTK_WIDGET(priv->about));
2591 }
2592
2593
2594 static void do_picker_refresh(EntangleCameraPicker *picker G_GNUC_UNUSED, EntangleCameraManager *manager)
2595 {
2596     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2597
2598     EntangleApplication *app = ENTANGLE_APPLICATION(gtk_window_get_application(GTK_WINDOW(manager)));
2599     EntangleCameraList *list = entangle_application_get_cameras(app);
2600     entangle_camera_list_refresh(list, NULL);
2601 }
2602
2603
2604 static void do_picker_connect(EntangleCameraPicker *picker G_GNUC_UNUSED,
2605                               EntangleCamera *cam,
2606                               gpointer *data)
2607 {
2608     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
2609     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2610
2611     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(data);
2612     EntangleCameraManagerPrivate *priv = manager->priv;
2613 #if 0
2614     GError *error = NULL;
2615     ENTANGLE_DEBUG("emit connect %p %s", cam, entangle_camera_get_model(cam));
2616     if (need_camera_unmount(cam))
2617         do_camera_unmount(cam);
2618
2619     while (!entangle_camera_connect(cam, &error)) {
2620         int response;
2621         GtkWidget *msg = gtk_message_dialog_new(NULL,
2622                                                 GTK_DIALOG_MODAL,
2623                                                 GTK_MESSAGE_ERROR,
2624                                                 GTK_BUTTONS_NONE,
2625                                                 _("Unable to connect to camera: %s"),
2626                                                 error->message);
2627         g_error_free(error);
2628
2629         gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(msg),
2630                                                    "%s",
2631                                                    _("Check that the camera is not\n\n"
2632                                                      " - opened by another photo <b>application</b>\n"
2633                                                      " - mounted as a <b>filesystem</b> on the desktop\n"
2634                                                      " - in <b>sleep mode</b> to save battery power\n"));
2635
2636         gtk_dialog_add_button(GTK_DIALOG(msg), _("Cancel"), GTK_RESPONSE_CANCEL);
2637         gtk_dialog_add_button(GTK_DIALOG(msg), _("Retry"), GTK_RESPONSE_ACCEPT);
2638         gtk_dialog_set_default_response(GTK_DIALOG(msg), GTK_RESPONSE_ACCEPT);
2639
2640         response = gtk_dialog_run(GTK_DIALOG(msg));
2641
2642         gtk_widget_destroy(msg);
2643
2644         if (response == GTK_RESPONSE_CANCEL)
2645             return;
2646     }
2647 #endif
2648     entangle_camera_manager_set_camera(manager, cam);
2649     gtk_widget_hide(GTK_WIDGET(priv->picker));
2650 }
2651
2652
2653 static void do_camera_connect(EntangleCameraManager *manager)
2654 {
2655     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2656
2657     EntangleCameraManagerPrivate *priv = manager->priv;
2658     EntangleApplication *app = ENTANGLE_APPLICATION(gtk_window_get_application(GTK_WINDOW(manager)));
2659     EntangleCameraList *cameras = entangle_application_get_cameras(app);
2660
2661     if (!priv->picker) {
2662         priv->picker = entangle_camera_picker_new();
2663         entangle_camera_picker_set_camera_list(priv->picker, cameras);
2664         gtk_window_set_transient_for(GTK_WINDOW(priv->picker),
2665                                      GTK_WINDOW(manager));
2666         g_signal_connect(priv->picker, "picker-refresh", G_CALLBACK(do_picker_refresh), manager);
2667         g_signal_connect(priv->picker, "picker-connect", G_CALLBACK(do_picker_connect), manager);
2668     }
2669
2670     gtk_widget_show(GTK_WIDGET(priv->picker));
2671 }
2672
2673
2674 void do_menu_connect(GtkMenuItem *src G_GNUC_UNUSED,
2675                      EntangleCameraManager *manager)
2676 {
2677     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2678
2679     do_camera_connect(manager);
2680 }
2681
2682
2683 void do_menu_disconnect(GtkMenuItem *src G_GNUC_UNUSED,
2684                         EntangleCameraManager *manager)
2685 {
2686     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2687
2688     entangle_camera_manager_set_camera(manager, NULL);
2689 }
2690
2691
2692 static void do_session_image_selected(GtkIconView *view G_GNUC_UNUSED,
2693                                       gpointer data)
2694 {
2695     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
2696
2697     EntangleCameraManager *manager = data;
2698     EntangleCameraManagerPrivate *priv = manager->priv;
2699     EntangleImage *img = entangle_session_browser_selected_image(priv->sessionBrowser);
2700
2701     ENTANGLE_DEBUG("Image selection changed");
2702     if (img) {
2703         ENTANGLE_DEBUG("Try load");
2704         do_select_image(manager, img);
2705         g_object_unref(img);
2706     }
2707 }
2708
2709
2710 void do_menu_session_open_activate(GtkMenuItem *item G_GNUC_UNUSED,
2711                                    EntangleCameraManager *manager)
2712 {
2713     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2714
2715     EntangleCameraManagerPrivate *priv = manager->priv;
2716     gchar *ctype = NULL;
2717     GList *files = NULL;
2718     GAppInfo *info = NULL;
2719     const gchar *filename;
2720
2721     if (!priv->sessionBrowserImage)
2722         return;
2723
2724     filename = entangle_image_get_filename(priv->sessionBrowserImage);
2725     ctype = g_content_type_guess(filename,
2726                                  NULL, 0, NULL);
2727     if (!ctype)
2728         return;
2729
2730     info = g_app_info_get_default_for_type(ctype, FALSE);
2731     g_free(ctype);
2732     if (!info)
2733         return;
2734
2735     files = g_list_append(files, g_file_new_for_path(filename));
2736
2737     g_app_info_launch(info, files, NULL, NULL);
2738
2739     g_list_foreach(files, (GFunc)g_object_unref, NULL);
2740     g_list_free(files);
2741 }
2742
2743
2744 void do_menu_session_delete_activate(GtkMenuItem *item G_GNUC_UNUSED,
2745                                      EntangleCameraManager *manager)
2746 {
2747     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2748
2749     EntangleCameraManagerPrivate *priv = manager->priv;
2750     GError *error = NULL;
2751
2752     if (!priv->sessionBrowserImage)
2753         return;
2754
2755     if (!entangle_image_delete(priv->sessionBrowserImage, &error)) {
2756         do_camera_task_error(manager, _("Delete"), error);
2757         return;
2758     }
2759     entangle_session_remove(priv->session, priv->sessionBrowserImage);
2760 }
2761
2762
2763 static void do_session_browser_open_with_app(GtkMenuItem *src,
2764                                              EntangleCameraManager *manager)
2765 {
2766     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2767
2768     EntangleCameraManagerPrivate *priv = manager->priv;
2769     GAppInfo *info = g_object_get_data(G_OBJECT(src), "appinfo");
2770     GList *files = NULL;
2771     const gchar *filename;
2772
2773     if (!info)
2774         return;
2775
2776     if (!priv->sessionBrowserImage)
2777         return;
2778
2779     filename = entangle_image_get_filename(priv->sessionBrowserImage);
2780     files = g_list_append(files, g_file_new_for_path(filename));
2781
2782     g_app_info_launch(info, files, NULL, NULL);
2783
2784     g_list_foreach(files, (GFunc)g_object_unref, NULL);
2785     g_list_free(files);
2786 }
2787
2788
2789 static void do_session_browser_open_with_select(GtkMenuItem *src G_GNUC_UNUSED,
2790                                                 EntangleCameraManager *manager)
2791 {
2792     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2793
2794     EntangleCameraManagerPrivate *priv = manager->priv;
2795     GtkDialog *dialog;
2796     GFile *file;
2797     GAppInfo *info;
2798     GList *files = NULL;
2799
2800     if (!priv->sessionBrowserImage)
2801         return;
2802
2803     file = g_file_new_for_path(entangle_image_get_filename(priv->sessionBrowserImage));
2804     dialog = GTK_DIALOG(gtk_app_chooser_dialog_new(GTK_WINDOW(manager),
2805                                                    GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
2806                                                    file));
2807
2808     gtk_dialog_run(dialog);
2809
2810     info = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(dialog));
2811
2812     files = g_list_append(files, file);
2813
2814     if (info)
2815         g_app_info_launch(info, files, NULL, NULL);
2816
2817     g_list_foreach(files, (GFunc)g_object_unref, NULL);
2818     g_list_free(files);
2819     gtk_widget_destroy(GTK_WIDGET(dialog));
2820 }
2821
2822
2823 static gboolean do_session_browser_popup(EntangleSessionBrowser *browser,
2824                                          GdkEventButton  *event,
2825                                          EntangleCameraManager *manager)
2826 {
2827     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager), FALSE);
2828
2829     EntangleCameraManagerPrivate *priv = manager->priv;
2830     GtkWidget *open;
2831     GtkWidget *openWith;
2832     GtkMenu *appList;
2833     GtkMenuItem *child;
2834     gchar *ctype = NULL;
2835     GList *appInfoList = NULL;
2836     const gchar *filename;
2837
2838     if (event->type != GDK_BUTTON_PRESS)
2839         return FALSE;
2840     if (event->button != 3)
2841         return FALSE;
2842
2843     priv->sessionBrowserImage = entangle_session_browser_get_image_at_coords(browser,
2844                                                                              event->x,
2845                                                                              event->y);
2846
2847     if (!priv->sessionBrowserImage)
2848         return FALSE;
2849
2850     filename = entangle_image_get_filename(priv->sessionBrowserImage);
2851
2852     open = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-session-open"));
2853     openWith = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-session-open-with"));
2854
2855     ctype = g_content_type_guess(filename,
2856                                  NULL, 0, NULL);
2857     if (!ctype)
2858         goto cleanup;
2859
2860     appInfoList = g_app_info_get_all_for_type(ctype);
2861
2862     appList = GTK_MENU(gtk_menu_new());
2863
2864     if (appInfoList) {
2865         GList *tmp = appInfoList;
2866         while (tmp) {
2867             GAppInfo *appInfo = tmp->data;
2868
2869             child = GTK_MENU_ITEM(gtk_menu_item_new_with_label
2870                                   (g_app_info_get_display_name(appInfo)));
2871             g_signal_connect(child, "activate",
2872                              G_CALLBACK(do_session_browser_open_with_app), manager);
2873             g_object_set_data_full(G_OBJECT(child), "appinfo",
2874                                    appInfo, (GDestroyNotify)g_object_unref);
2875             gtk_container_add(GTK_CONTAINER(appList), GTK_WIDGET(child));
2876
2877             tmp = tmp->next;
2878         }
2879         g_list_free(appInfoList);
2880
2881         child = GTK_MENU_ITEM(gtk_separator_menu_item_new());
2882         gtk_container_add(GTK_CONTAINER(appList), GTK_WIDGET(child));
2883     }
2884
2885     child = GTK_MENU_ITEM(gtk_menu_item_new_with_label(_("Select application...")));
2886     g_signal_connect(child, "activate",
2887                      G_CALLBACK(do_session_browser_open_with_select), manager);
2888     gtk_container_add(GTK_CONTAINER(appList), GTK_WIDGET(child));
2889
2890     gtk_widget_show_all(GTK_WIDGET(appList));
2891
2892     gtk_menu_item_set_submenu(GTK_MENU_ITEM(openWith), GTK_WIDGET(appList));
2893
2894     gtk_menu_popup(priv->sessionBrowserMenu,
2895                    NULL, NULL, NULL, NULL,
2896                    event->button, event->time);
2897
2898  cleanup:
2899     gtk_widget_set_sensitive(GTK_WIDGET(open), ctype && appInfoList ? TRUE : FALSE);
2900     return TRUE;
2901 }
2902
2903
2904 static void do_popup_close(EntangleImagePopup *popup,
2905                            EntangleCameraManager *manager)
2906 {
2907     g_return_if_fail(ENTANGLE_IS_IMAGE_POPUP(popup));
2908     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
2909
2910     EntangleCameraManagerPrivate *priv = manager->priv;
2911     EntangleImage *img = entangle_image_popup_get_image(popup);
2912     const char *filename = entangle_image_get_filename(img);
2913
2914     g_hash_table_remove(priv->popups, filename);
2915
2916     entangle_pixbuf_loader_unload(ENTANGLE_PIXBUF_LOADER(priv->imageLoader),
2917                                   img);
2918 }
2919
2920
2921 static void do_session_browser_drag_failed(GtkWidget *widget G_GNUC_UNUSED,
2922                                            GdkDragContext *ctx G_GNUC_UNUSED,
2923                                            GtkDragResult res,
2924                                            gpointer data)
2925 {
2926     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
2927
2928     EntangleCameraManager *manager = data;
2929     EntangleCameraManagerPrivate *priv = manager->priv;
2930
2931     if (res == GTK_DRAG_RESULT_NO_TARGET) {
2932         EntangleImage *img = entangle_session_browser_selected_image(priv->sessionBrowser);
2933         if (img) {
2934             GdkScreen *screen;
2935             GdkDevice *dev;
2936             GdkDeviceManager *devmgr;
2937             int x, y;
2938
2939             devmgr = gdk_display_get_device_manager(gtk_widget_get_display(widget));
2940             dev = gdk_device_manager_get_client_pointer(devmgr);
2941             gdk_device_get_position(dev,
2942                                     &screen,
2943                                     &x, &y);
2944
2945             const gchar *filename = entangle_image_get_filename(img);
2946             EntangleImagePopup *pol;
2947             if (!(pol = g_hash_table_lookup(priv->popups, filename))) {
2948                 pol = entangle_image_popup_new();
2949                 entangle_image_popup_set_image(pol, img);
2950                 g_signal_connect(pol, "hide", G_CALLBACK(do_popup_close), manager);
2951                 g_hash_table_insert(priv->popups, g_strdup(filename), pol);
2952                 entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->imageLoader),
2953                                             img);
2954             }
2955             ENTANGLE_DEBUG("Popup %p for %s", pol, filename);
2956             entangle_image_popup_show(pol, GTK_WINDOW(manager), x, y);
2957             g_object_unref(img);
2958         }
2959     }
2960 }
2961
2962
2963 static void do_pixbuf_loaded(EntanglePixbufLoader *loader,
2964                              EntangleImage *image)
2965 {
2966     g_return_if_fail(ENTANGLE_IS_IMAGE(image));
2967
2968     GdkPixbuf *pixbuf = entangle_pixbuf_loader_get_pixbuf(loader, image);
2969
2970     entangle_image_set_pixbuf(image, pixbuf);
2971 }
2972
2973
2974 static void do_metadata_loaded(EntanglePixbufLoader *loader,
2975                                EntangleImage *image)
2976 {
2977     g_return_if_fail(ENTANGLE_IS_IMAGE(image));
2978
2979     GExiv2Metadata *metadata = entangle_pixbuf_loader_get_metadata(loader, image);
2980
2981     entangle_image_set_metadata(image, metadata);
2982 }
2983
2984
2985 static void do_pixbuf_unloaded(EntanglePixbufLoader *loader G_GNUC_UNUSED,
2986                                EntangleImage *image)
2987 {
2988     g_return_if_fail(ENTANGLE_IS_IMAGE(image));
2989
2990     entangle_image_set_pixbuf(image, NULL);
2991 }
2992
2993
2994 static void do_metadata_unloaded(EntanglePixbufLoader *loader G_GNUC_UNUSED,
2995                                  EntangleImage *image)
2996 {
2997     g_return_if_fail(ENTANGLE_IS_IMAGE(image));
2998
2999     entangle_image_set_metadata(image, NULL);
3000 }
3001
3002
3003 static gboolean do_image_status_hide(gpointer data)
3004 {
3005     g_return_val_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data), FALSE);
3006
3007     EntangleCameraManager *manager = data;
3008     EntangleCameraManagerPrivate *priv = manager->priv;
3009
3010     entangle_auto_drawer_set_pinned(ENTANGLE_AUTO_DRAWER(priv->imageDrawer), FALSE);
3011     priv->imageDrawerTimer = 0;
3012     return FALSE;
3013 }
3014
3015
3016 static void do_image_status_show(GtkWidget *widget G_GNUC_UNUSED,
3017                                  GdkEvent *event G_GNUC_UNUSED,
3018                                  gpointer data)
3019 {
3020     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(data));
3021
3022     EntangleCameraManager *manager = data;
3023     EntangleCameraManagerPrivate *priv = manager->priv;
3024
3025     entangle_auto_drawer_set_pinned(ENTANGLE_AUTO_DRAWER(priv->imageDrawer), TRUE);
3026     if (priv->imageDrawerTimer)
3027         g_source_remove(priv->imageDrawerTimer);
3028     priv->imageDrawerTimer = g_timeout_add_seconds(3,
3029                                                    do_image_status_hide,
3030                                                    manager);
3031 }
3032
3033
3034 static void do_entangle_camera_manager_hide(EntangleCameraManager *manager)
3035 {
3036     g_return_if_fail(ENTANGLE_IS_CAMERA_MANAGER(manager));
3037
3038     EntangleCameraManagerPrivate *priv = manager->priv;
3039
3040     entangle_camera_manager_set_camera(manager, NULL);
3041
3042     ENTANGLE_DEBUG("Removing all popups");
3043     g_hash_table_remove_all(priv->popups);
3044 }
3045
3046
3047 static void do_entangle_camera_manager_set_builder(EntangleWindow *window,
3048                                                    GtkBuilder *builder)
3049 {
3050     EntangleCameraManager *manager = ENTANGLE_CAMERA_MANAGER(window);
3051     EntangleCameraManagerPrivate *priv = manager->priv;
3052     GtkWidget *display;
3053     GtkWidget *iconScroll;
3054     GtkWidget *settingsBox;
3055     GtkWidget *settingsViewport;
3056     GtkWidget *menu;
3057     GtkWidget *monitorMenu;
3058     GtkWidget *imageViewport;
3059
3060     priv->builder = g_object_ref(builder);
3061
3062     menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-monitor"));
3063     monitorMenu = entangle_camera_manager_monitor_menu(manager);
3064     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), monitorMenu);
3065
3066     imageViewport = gtk_viewport_new(NULL, NULL);
3067     priv->imageScroll = GTK_SCROLLED_WINDOW
3068         (gtk_scrolled_window_new(gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(imageViewport)),
3069                                  gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(imageViewport))));
3070
3071     gtk_container_add(GTK_CONTAINER(priv->imageScroll), imageViewport);
3072
3073     priv->imageLoader = entangle_image_loader_new();
3074     priv->thumbLoader = entangle_thumbnail_loader_new(140, 140);
3075
3076     g_signal_connect(priv->imageLoader, "pixbuf-loaded", G_CALLBACK(do_pixbuf_loaded), NULL);
3077     g_signal_connect(priv->imageLoader, "metadata-loaded", G_CALLBACK(do_metadata_loaded), NULL);
3078     g_signal_connect(priv->imageLoader, "pixbuf-unloaded", G_CALLBACK(do_pixbuf_unloaded), NULL);
3079     g_signal_connect(priv->imageLoader, "metadata-unloaded", G_CALLBACK(do_metadata_unloaded), NULL);
3080
3081     priv->imageDisplay = entangle_image_display_new();
3082     priv->imageStatusbar = entangle_image_statusbar_new();
3083     priv->imageDrawer = entangle_auto_drawer_new();
3084     priv->sessionBrowser = entangle_session_browser_new();
3085     priv->sessionBrowserMenu = GTK_MENU(gtk_builder_get_object(priv->builder, "menu-session-browser"));
3086     priv->controlPanel = entangle_control_panel_new();
3087     priv->imageHistogram = entangle_image_histogram_new();
3088     gtk_widget_show(GTK_WIDGET(priv->imageHistogram));
3089
3090     g_object_set(priv->sessionBrowser, "thumbnail-loader", priv->thumbLoader, NULL);
3091
3092     g_signal_connect(priv->imageDisplay, "size-allocate",
3093                      G_CALLBACK(do_restore_scroll), manager);
3094
3095     g_signal_connect(priv->sessionBrowser, "selection-changed",
3096                      G_CALLBACK(do_session_image_selected), manager);
3097     g_signal_connect(priv->sessionBrowser, "button-press-event",
3098                      G_CALLBACK(do_session_browser_popup), manager);
3099     g_signal_connect(priv->sessionBrowser, "drag-failed",
3100                      G_CALLBACK(do_session_browser_drag_failed), manager);
3101
3102     settingsBox = GTK_WIDGET(gtk_builder_get_object(priv->builder, "settings-box"));
3103     settingsViewport = GTK_WIDGET(gtk_builder_get_object(priv->builder, "settings-viewport"));
3104     display = GTK_WIDGET(gtk_builder_get_object(priv->builder, "display-panel"));
3105
3106     iconScroll = gtk_scrolled_window_new(NULL, NULL);
3107     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(iconScroll),
3108                                    GTK_POLICY_AUTOMATIC,
3109                                    GTK_POLICY_NEVER);
3110
3111     /* XXX match icon size + padding + scrollbar needs */
3112     gtk_widget_set_size_request(settingsBox, 300, 100);
3113     gtk_widget_set_size_request(iconScroll, 140, 170);
3114