1 /*
2 * Entangle: Tethered Camera Control & Capture
3 *
4 * Copyright (C) 2009-2017 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 <string.h>
22 #include <glib/gi18n.h>
23 #include <math.h>
24
25 #include "entangle-debug.h"
26 #include "entangle-control-panel.h"
27 #include "entangle-control-button.h"
28 #include "entangle-control-choice.h"
29 #include "entangle-control-date.h"
30 #include "entangle-control-group.h"
31 #include "entangle-control-range.h"
32 #include "entangle-control-text.h"
33 #include "entangle-control-toggle.h"
34
35 #define ENTANGLE_CONTROL_PANEL_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_CONTROL_PANEL, EntangleControlPanelPrivate))
37
38 struct _EntangleControlPanelPrivate {
39 EntangleCameraPreferences *cameraPrefs;
40 EntangleCamera *camera;
41
42 gulong sigCamera;
43 gboolean hasControls;
44 gboolean inUpdate;
45
46 GtkWidget *grid;
47 gsize rows;
48 };
49
50 G_DEFINE_TYPE(EntangleControlPanel, entangle_control_panel, GTK_TYPE_EXPANDER);
51
52 enum {
53 PROP_O,
54 PROP_CAMERA,
55 PROP_CAMERA_PREFS,
56 PROP_HAS_CONTROLS,
57 };
58
do_control_remove(GtkWidget * widget,gpointer data)59 static void do_control_remove(GtkWidget *widget,
60 gpointer data)
61 {
62 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
63
64 EntangleControlPanel *panel = data;
65 EntangleControlPanelPrivate *priv = panel->priv;
66
67 gtk_container_remove(GTK_CONTAINER(priv->grid), widget);
68 }
69
70
do_update_control_finish(GObject * src,GAsyncResult * res,gpointer data)71 static void do_update_control_finish(GObject *src,
72 GAsyncResult *res,
73 gpointer data)
74 {
75 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
76
77 GError *error = NULL;
78
79 if (!entangle_camera_save_controls_finish(ENTANGLE_CAMERA(src), res, &error)) {
80 GtkWidget *msg = gtk_message_dialog_new(NULL,
81 0,
82 GTK_MESSAGE_ERROR,
83 GTK_BUTTONS_OK,
84 _("Camera control update failed"));
85 gtk_window_set_title(GTK_WINDOW(msg),
86 _("Entangle: Camera control update failed"));
87 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg),
88 "%s",
89 error->message);
90 g_signal_connect_swapped(msg,
91 "response",
92 G_CALLBACK(gtk_widget_destroy),
93 msg);
94 gtk_widget_show_all(msg);
95 g_error_free(error);
96 }
97 }
98
99
do_refresh_control_entry_idle(gpointer data)100 static gboolean do_refresh_control_entry_idle(gpointer data)
101 {
102 GtkWidget *widget = GTK_WIDGET(data);
103 EntangleControlPanel *panel = g_object_get_data(G_OBJECT(widget), "panel");
104 GObject *control = g_object_get_data(G_OBJECT(widget), "control");
105 gchar *text;
106
107 panel->priv->inUpdate = TRUE;
108 g_object_get(control, "value", &text, NULL);
109 ENTANGLE_DEBUG("Notified control entry '%s' ('%s') with '%s'",
110 entangle_control_get_path(ENTANGLE_CONTROL(control)),
111 entangle_control_get_label(ENTANGLE_CONTROL(control)),
112 text);
113
114 if (GTK_IS_LABEL(widget))
115 gtk_label_set_text(GTK_LABEL(widget), text);
116 else
117 gtk_entry_set_text(GTK_ENTRY(widget), text);
118 g_free(text);
119 panel->priv->inUpdate = FALSE;
120 return FALSE;
121 }
122
do_refresh_control_entry(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)123 static void do_refresh_control_entry(GObject *object G_GNUC_UNUSED,
124 GParamSpec *pspec G_GNUC_UNUSED,
125 gpointer data)
126 {
127 g_idle_add(do_refresh_control_entry_idle, data);
128 }
129
do_update_control_entry(GtkWidget * widget,GdkEventFocus * ev G_GNUC_UNUSED,gpointer data)130 static void do_update_control_entry(GtkWidget *widget,
131 GdkEventFocus *ev G_GNUC_UNUSED,
132 gpointer data)
133 {
134 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
135
136 EntangleControlText *control = g_object_get_data(G_OBJECT(widget), "control");
137 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(data);
138 EntangleControlPanelPrivate *priv = panel->priv;
139 const char *text;
140
141 if (panel->priv->inUpdate)
142 return;
143
144 text = gtk_entry_get_text(GTK_ENTRY(widget));
145
146 ENTANGLE_DEBUG("Updated control entry '%s' ('%s') with '%s'",
147 entangle_control_get_path(ENTANGLE_CONTROL(control)),
148 entangle_control_get_label(ENTANGLE_CONTROL(control)),
149 text);
150 g_object_set(control, "value", text, NULL);
151
152 entangle_camera_save_controls_async(priv->camera,
153 NULL,
154 do_update_control_finish,
155 panel);
156 }
157
158
do_refresh_control_range_idle(gpointer data)159 static gboolean do_refresh_control_range_idle(gpointer data)
160 {
161 GtkWidget *widget = GTK_WIDGET(data);
162 EntangleControlPanel *panel = g_object_get_data(G_OBJECT(widget), "panel");
163 GObject *control = g_object_get_data(G_OBJECT(widget), "control");
164 gfloat val;
165
166 panel->priv->inUpdate = TRUE;
167 g_object_get(control, "value", &val, NULL);
168 ENTANGLE_DEBUG("Notified control range '%s' ('%s') with '%lf'",
169 entangle_control_get_path(ENTANGLE_CONTROL(control)),
170 entangle_control_get_label(ENTANGLE_CONTROL(control)),
171 (double)val);
172
173 if (GTK_IS_LABEL(widget)) {
174 gchar *text = g_strdup_printf("%0.02f", (double)val);
175 gtk_label_set_text(GTK_LABEL(widget), text);
176 g_free(text);
177 } else {
178 gtk_range_set_value(GTK_RANGE(widget), val);
179 }
180 panel->priv->inUpdate = FALSE;
181 return FALSE;
182 }
183
184
do_refresh_control_range(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)185 static void do_refresh_control_range(GObject *object G_GNUC_UNUSED,
186 GParamSpec *pspec G_GNUC_UNUSED,
187 gpointer data)
188 {
189 g_idle_add(do_refresh_control_range_idle, data);
190 }
191
192
do_update_control_range(GtkRange * widget G_GNUC_UNUSED,GtkScrollType scroll G_GNUC_UNUSED,gdouble value,gpointer data)193 static void do_update_control_range(GtkRange *widget G_GNUC_UNUSED,
194 GtkScrollType scroll G_GNUC_UNUSED,
195 gdouble value,
196 gpointer data)
197 {
198 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
199
200 EntangleControlRange *control = g_object_get_data(G_OBJECT(widget), "control");
201 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(data);
202 EntangleControlPanelPrivate *priv = panel->priv;
203
204 if (panel->priv->inUpdate)
205 return;
206
207 ENTANGLE_DEBUG("Updated control range '%s' ('%s') with '%lf'",
208 entangle_control_get_path(ENTANGLE_CONTROL(control)),
209 entangle_control_get_label(ENTANGLE_CONTROL(control)),
210 value);
211 g_object_set(control, "value", (double)value, NULL);
212
213 entangle_camera_save_controls_async(priv->camera,
214 NULL,
215 do_update_control_finish,
216 panel);
217 }
218
219
do_refresh_control_combo_idle(gpointer data)220 static gboolean do_refresh_control_combo_idle(gpointer data)
221 {
222 GtkWidget *widget = GTK_WIDGET(data);
223 EntangleControlPanel *panel = g_object_get_data(G_OBJECT(widget), "panel");
224 GObject *control = g_object_get_data(G_OBJECT(widget), "control");
225 gchar *text;
226
227 panel->priv->inUpdate = TRUE;
228 g_object_get(control, "value", &text, NULL);
229 ENTANGLE_DEBUG("Notified control combo '%s' ('%s') with '%s'",
230 entangle_control_get_path(ENTANGLE_CONTROL(control)),
231 entangle_control_get_label(ENTANGLE_CONTROL(control)),
232 text);
233
234 if (GTK_IS_LABEL(widget)) {
235 gtk_label_set_text(GTK_LABEL(widget), text);
236 } else {
237 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)));
238 int active = 0;
239 gtk_list_store_clear(store);
240 for (int n = 0; n < entangle_control_choice_entry_count(ENTANGLE_CONTROL_CHOICE(control)); n++) {
241 GtkTreeIter iter;
242 if (g_strcmp0(text, entangle_control_choice_entry_get(ENTANGLE_CONTROL_CHOICE(control), n)) == 0)
243 active = n;
244 gtk_list_store_append(store, &iter);
245 gtk_list_store_set(store, &iter, 0,
246 entangle_control_choice_entry_get(ENTANGLE_CONTROL_CHOICE(control), n),
247 -1);
248 }
249 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), active);
250 }
251 g_free(text);
252 panel->priv->inUpdate = FALSE;
253
254 return FALSE;
255 }
256
257
do_refresh_control_combo(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)258 static void do_refresh_control_combo(GObject *object G_GNUC_UNUSED,
259 GParamSpec *pspec G_GNUC_UNUSED,
260 gpointer data)
261 {
262 g_idle_add(do_refresh_control_combo_idle, data);
263 }
264
265
do_update_control_combo(GtkComboBox * widget,gpointer data)266 static void do_update_control_combo(GtkComboBox *widget,
267 gpointer data)
268 {
269 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
270
271 EntangleControlChoice *control = g_object_get_data(G_OBJECT(widget), "control");
272 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(data);
273 EntangleControlPanelPrivate *priv = panel->priv;
274 GtkTreeIter iter;
275 char *text = NULL;
276 GtkTreeModel *model = gtk_combo_box_get_model(widget);
277
278 if (panel->priv->inUpdate)
279 return;
280
281 if (gtk_combo_box_get_active_iter(widget, &iter))
282 gtk_tree_model_get(model, &iter, 0, &text, -1);
283
284 ENTANGLE_DEBUG("Updated control combo '%s' ('%s') with '%s'",
285 entangle_control_get_path(ENTANGLE_CONTROL(control)),
286 entangle_control_get_label(ENTANGLE_CONTROL(control)),
287 text);
288 g_object_set(control, "value", text, NULL);
289
290 g_free(text);
291
292 entangle_camera_save_controls_async(priv->camera,
293 NULL,
294 do_update_control_finish,
295 panel);
296 }
297
298
do_refresh_control_toggle_idle(gpointer data)299 static gboolean do_refresh_control_toggle_idle(gpointer data)
300 {
301 GtkWidget *widget = GTK_WIDGET(data);
302 EntangleControlPanel *panel = g_object_get_data(G_OBJECT(widget), "panel");
303 GObject *control = g_object_get_data(G_OBJECT(widget), "control");
304 gboolean state;
305
306 panel->priv->inUpdate = TRUE;
307 g_object_get(control, "value", &state, NULL);
308 ENTANGLE_DEBUG("Notified control toggle '%s' ('%s') with '%d'",
309 entangle_control_get_path(ENTANGLE_CONTROL(control)),
310 entangle_control_get_label(ENTANGLE_CONTROL(control)),
311 state);
312
313 if (GTK_IS_LABEL(widget))
314 gtk_label_set_text(GTK_LABEL(widget), state ? _("On") : _("Off"));
315 else
316 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
317 state);
318 panel->priv->inUpdate = FALSE;
319 return FALSE;
320 }
321
322
do_refresh_control_toggle(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)323 static void do_refresh_control_toggle(GObject *object G_GNUC_UNUSED,
324 GParamSpec *pspec G_GNUC_UNUSED,
325 gpointer data)
326 {
327 g_idle_add(do_refresh_control_toggle_idle, data);
328 }
329
330
do_update_control_toggle(GtkToggleButton * widget,gpointer data)331 static void do_update_control_toggle(GtkToggleButton *widget,
332 gpointer data)
333 {
334 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
335
336 EntangleControlChoice *control = g_object_get_data(G_OBJECT(widget), "control");
337 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(data);
338 EntangleControlPanelPrivate *priv = panel->priv;
339 gboolean active;
340
341 if (panel->priv->inUpdate)
342 return;
343
344 active = gtk_toggle_button_get_active(widget);
345 ENTANGLE_DEBUG("Updated control toggle '%s' ('%s') with '%d'",
346 entangle_control_get_path(ENTANGLE_CONTROL(control)),
347 entangle_control_get_label(ENTANGLE_CONTROL(control)),
348 active);
349 g_object_set(control, "value", active, NULL);
350
351 entangle_camera_save_controls_async(priv->camera,
352 NULL,
353 do_update_control_finish,
354 panel);
355 }
356
do_update_control_readonly_idle(gpointer data)357 static gboolean do_update_control_readonly_idle(gpointer data)
358 {
359 GtkWidget *widget = GTK_WIDGET(data);
360 GObject *control = g_object_get_data(G_OBJECT(widget), "control");
361 gboolean state;
362
363 g_object_get(control, "readonly", &state, NULL);
364 gtk_widget_set_sensitive(widget, !state);
365
366 return FALSE;
367 }
368
369
do_update_control_readonly(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)370 static void do_update_control_readonly(GObject *object G_GNUC_UNUSED,
371 GParamSpec *pspec G_GNUC_UNUSED,
372 gpointer data)
373 {
374 g_idle_add(do_update_control_readonly_idle, data);
375 }
376
377
do_setup_control(EntangleControlPanel * panel,EntangleControl * control,GtkContainer * box,gint row)378 static void do_setup_control(EntangleControlPanel *panel,
379 EntangleControl *control,
380 GtkContainer *box,
381 gint row)
382 {
383 GtkWidget *label = NULL;
384 GtkWidget *value = NULL;
385 gboolean needLabel = TRUE;
386
387 ENTANGLE_DEBUG("Build control %d %s",
388 entangle_control_get_id(control),
389 entangle_control_get_label(control));
390
391 if (ENTANGLE_IS_CONTROL_BUTTON(control)) {
392 needLabel = FALSE;
393 value = gtk_button_new_with_label(entangle_control_get_label(control));
394 if (entangle_control_get_readonly(control))
395 gtk_widget_set_sensitive(value, FALSE);
396 g_signal_connect(control, "notify::readonly",
397 G_CALLBACK(do_update_control_readonly), value);
398 } else if (ENTANGLE_IS_CONTROL_CHOICE(control)) {
399 GtkCellRenderer *cell;
400 GtkListStore *store;
401 char *text;
402 int active = -1;
403
404 /*
405 * Need todo better here
406 *
407 * If there's only two entries 0/1, turn into toggle
408 * If there's a continuous sequence of numbers turn
409 * into a spinbutton
410 *
411 * Some sequences of numbers are nonsene, and need to
412 * be turned in to real labels.
413 *
414 * eg Shutter speed 0.00025 should be presented 1/4000
415 */
416
417 store = gtk_list_store_new(1, G_TYPE_STRING);
418 value = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
419 g_object_unref(store);
420
421 cell = gtk_cell_renderer_text_new();
422 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(value), cell, TRUE);
423 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(value), cell,
424 "text", 0,
425 NULL);
426
427 g_object_get(control, "value", &text, NULL);
428 for (int n = 0; n < entangle_control_choice_entry_count(ENTANGLE_CONTROL_CHOICE(control)); n++) {
429 GtkTreeIter iter;
430 if (g_strcmp0(text, entangle_control_choice_entry_get(ENTANGLE_CONTROL_CHOICE(control), n)) == 0)
431 active = n;
432 gtk_list_store_append(store, &iter);
433 gtk_list_store_set(store, &iter, 0,
434 entangle_control_choice_entry_get(ENTANGLE_CONTROL_CHOICE(control), n),
435 -1);
436 }
437
438 if (entangle_control_get_readonly(control))
439 gtk_widget_set_sensitive(value, FALSE);
440 gtk_combo_box_set_active(GTK_COMBO_BOX(value), active);
441
442 g_signal_connect(value, "changed",
443 G_CALLBACK(do_update_control_combo), panel);
444 g_signal_connect(control, "notify::value",
445 G_CALLBACK(do_refresh_control_combo), value);
446 g_signal_connect(control, "notify::readonly",
447 G_CALLBACK(do_update_control_readonly), value);
448 } else if (ENTANGLE_IS_CONTROL_DATE(control)) {
449 int date;
450
451 value = gtk_entry_new();
452 g_object_get(control, "value", &date, NULL);
453 if (entangle_control_get_readonly(control))
454 gtk_widget_set_sensitive(value, FALSE);
455 //gtk_entry_set_text(GTK_ENTRY(value), text);
456 } else if (ENTANGLE_IS_CONTROL_RANGE(control)) {
457 gfloat offset;
458 gdouble min = entangle_control_range_get_min(ENTANGLE_CONTROL_RANGE(control));
459 gdouble max = entangle_control_range_get_max(ENTANGLE_CONTROL_RANGE(control));
460 gboolean forceReadonly = FALSE;
461
462 if (fabs(min-max) < 0.005) {
463 forceReadonly = TRUE;
464 max += 1;
465 }
466
467 value = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,
468 min, max,
469 entangle_control_range_get_step(ENTANGLE_CONTROL_RANGE(control)));
470 g_object_get(control, "value", &offset, NULL);
471 gtk_range_set_value(GTK_RANGE(value), offset);
472 if (entangle_control_get_readonly(control) || forceReadonly)
473 gtk_widget_set_sensitive(value, FALSE);
474 g_signal_connect(value, "change-value",
475 G_CALLBACK(do_update_control_range), panel);
476 g_signal_connect(control, "notify::value",
477 G_CALLBACK(do_refresh_control_range), value);
478 g_signal_connect(control, "notify::readonly",
479 G_CALLBACK(do_update_control_readonly), value);
480 } else if (ENTANGLE_IS_CONTROL_TEXT(control)) {
481 const char *text;
482
483 value = gtk_entry_new();
484 g_object_get(control, "value", &text, NULL);
485 gtk_entry_set_text(GTK_ENTRY(value), text);
486 if (entangle_control_get_readonly(control))
487 gtk_widget_set_sensitive(value, FALSE);
488 g_signal_connect(value, "focus-out-event",
489 G_CALLBACK(do_update_control_entry), panel);
490 g_signal_connect(control, "notify::value",
491 G_CALLBACK(do_refresh_control_entry), value);
492 g_signal_connect(control, "notify::readonly",
493 G_CALLBACK(do_update_control_readonly), value);
494 } else if (ENTANGLE_IS_CONTROL_TOGGLE(control)) {
495 gboolean active;
496 needLabel = FALSE;
497 value = gtk_check_button_new_with_label(entangle_control_get_label(control));
498 g_object_get(control, "value", &active, NULL);
499 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(value), active);
500 if (entangle_control_get_readonly(control))
501 gtk_widget_set_sensitive(value, FALSE);
502 g_signal_connect(value, "toggled",
503 G_CALLBACK(do_update_control_toggle), panel);
504 g_signal_connect(control, "notify::value",
505 G_CALLBACK(do_refresh_control_toggle), value);
506 g_signal_connect(control, "notify::readonly",
507 G_CALLBACK(do_update_control_readonly), value);
508 }
509
510 if (needLabel) {
511 label = gtk_label_new(entangle_control_get_label(control));
512 gtk_widget_set_tooltip_text(label, entangle_control_get_info(control));
513 gtk_widget_set_halign(label, GTK_ALIGN_FILL);
514 gtk_grid_attach(GTK_GRID(box), label, 0, row, 1, 1);
515
516 g_object_set_data(G_OBJECT(label), "panel", panel);
517 g_object_set_data(G_OBJECT(label), "control", control);
518 gtk_widget_show(label);
519
520 gtk_widget_set_hexpand(value, TRUE);
521 gtk_widget_set_halign(value, GTK_ALIGN_FILL);
522 gtk_grid_attach(GTK_GRID(box), value, 1, row, 1, 1);
523
524 g_object_set_data(G_OBJECT(value), "panel", panel);
525 g_object_set_data(G_OBJECT(value), "control", control);
526 gtk_widget_show(value);
527 } else {
528 gtk_widget_set_hexpand(value, TRUE);
529 gtk_widget_set_halign(value, GTK_ALIGN_FILL);
530 gtk_grid_attach(GTK_GRID(box), value, 0, row, 2, 1);
531
532 g_object_set_data(G_OBJECT(value), "panel", panel);
533 g_object_set_data(G_OBJECT(value), "control", control);
534 gtk_widget_show(value);
535 }
536 }
537
538
entangle_control_panel_get_default_controls(EntangleControlGroup * root)539 static gchar **entangle_control_panel_get_default_controls(EntangleControlGroup *root)
540 {
541 gchar **controls = NULL;
542 gsize ncontrols = 0;
543
544 if (entangle_control_group_get_by_path(root,
545 "/main/capturesettings/f-number")) {
546 controls = g_renew(gchar *, controls, ncontrols + 1);
547 controls[ncontrols++] = g_strdup("/main/capturesettings/f-number");
548 } else if (entangle_control_group_get_by_path(root,
549 "/main/capturesettings/aperture")) {
550 controls = g_renew(gchar *, controls, ncontrols + 1);
551 controls[ncontrols++] = g_strdup("/main/capturesettings/aperture");
552 }
553
554 if (entangle_control_group_get_by_path(root,
555 "/main/capturesettings/shutterspeed2")) {
556 controls = g_renew(gchar *, controls, ncontrols + 1);
557 controls[ncontrols++] = g_strdup("/main/capturesettings/shutterspeed2");
558 } else if (entangle_control_group_get_by_path(root,
559 "/main/capturesettings/shutterspeed")) {
560 controls = g_renew(gchar *, controls, ncontrols + 1);
561 controls[ncontrols++] = g_strdup("/main/capturesettings/shutterspeed");
562 }
563
564 if (entangle_control_group_get_by_path(root,
565 "/main/imgsettings/iso")) {
566 controls = g_renew(gchar *, controls, ncontrols + 1);
567 controls[ncontrols++] = g_strdup("/main/imgsettings/iso");
568 }
569
570 if (entangle_control_group_get_by_path(root,
571 "/main/imgsettings/whitebalance")) {
572 controls = g_renew(gchar *, controls, ncontrols + 1);
573 controls[ncontrols++] = g_strdup("/main/imgsettings/whitebalance");
574 }
575
576 if (entangle_control_group_get_by_path(root,
577 "/main/capturesettings/imagequality")) {
578 controls = g_renew(gchar *, controls, ncontrols + 1);
579 controls[ncontrols++] = g_strdup("/main/capturesettings/imagequality");
580 } else if (entangle_control_group_get_by_path(root,
581 "/main/imgsettings/imageformat")) {
582 controls = g_renew(gchar *, controls, ncontrols + 1);
583 controls[ncontrols++] = g_strdup("/main/imgsettings/imageformat");
584 }
585
586 if (entangle_control_group_get_by_path(root,
587 "/main/imgsettings/imagesize")) {
588 controls = g_renew(gchar *, controls, ncontrols + 1);
589 controls[ncontrols++] = g_strdup("/main/imgsettings/imagesize");
590 }
591
592 controls = g_renew(gchar *, controls, ncontrols + 1);
593 controls[ncontrols++] = NULL;
594
595 return controls;
596 }
597
do_get_control_list(EntangleControlGroup * group)598 static GList *do_get_control_list(EntangleControlGroup *group)
599 {
600 gsize i;
601 GList *controls = NULL;
602
603 for (i = 0; i < entangle_control_group_count(group); i++) {
604 EntangleControl *control = entangle_control_group_get(group, i);
605
606 if (ENTANGLE_IS_CONTROL_GROUP(control)) {
607 GList *children = do_get_control_list(ENTANGLE_CONTROL_GROUP(control));
608
609 controls = g_list_concat(controls, children);
610 } else {
611 controls = g_list_append(controls, control);
612 }
613 }
614
615 return controls;
616 }
617
compare_control(gconstpointer a,gconstpointer b)618 static int compare_control(gconstpointer a, gconstpointer b)
619 {
620 EntangleControl *ac = (EntangleControl *)a;
621 EntangleControl *bc = (EntangleControl *)b;
622
623 return strcmp(entangle_control_get_label(ac),
624 entangle_control_get_label(bc));
625 }
626
627
is_control_enabled(gchar ** controls,const gchar * check)628 static gboolean is_control_enabled(gchar **controls,
629 const gchar *check)
630 {
631 gsize i;
632
633 if (!controls)
634 return FALSE;
635
636 for (i = 0; controls[i] != NULL; i++)
637 if (g_str_equal(controls[i], check))
638 return TRUE;
639 return FALSE;
640 }
641
642 static void do_setup_controls(EntangleControlPanel *panel);
643
do_reset_controls(GtkWidget * src G_GNUC_UNUSED,EntangleControlPanel * panel)644 static void do_reset_controls(GtkWidget *src G_GNUC_UNUSED,
645 EntangleControlPanel *panel)
646 {
647 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(panel));
648
649 EntangleControlPanelPrivate *priv = panel->priv;
650
651 gtk_container_foreach(GTK_CONTAINER(priv->grid), do_control_remove, panel);
652 priv->rows = 0;
653 entangle_camera_preferences_set_controls(priv->cameraPrefs,
654 NULL);
655 do_setup_controls(panel);
656 }
657
658
do_update_control_prefs(EntangleControlPanel * panel)659 static void do_update_control_prefs(EntangleControlPanel *panel)
660 {
661 EntangleControlPanelPrivate *priv = panel->priv;
662 const gchar **controlnames = g_new0(const gchar *, priv->rows + 1);
663 gsize i;
664
665 for (i = 0; i < priv->rows; i++) {
666 GtkWidget *widget = gtk_grid_get_child_at(GTK_GRID(priv->grid), 0, i);
667 EntangleControl *control = g_object_get_data(G_OBJECT(widget), "control");
668 controlnames[i] = entangle_control_get_path(control);
669 }
670 controlnames[priv->rows] = NULL;
671 entangle_camera_preferences_set_controls(priv->cameraPrefs,
672 (const gchar *const *)controlnames);
673 g_free(controlnames);
674 }
675
do_add_control(EntangleControlPanel * panel,EntangleControl * control)676 static void do_add_control(EntangleControlPanel *panel,
677 EntangleControl *control)
678 {
679 EntangleControlPanelPrivate *priv = panel->priv;
680 gsize i;
681
682 for (i = 0; i < priv->rows; i++) {
683 GtkWidget *widget = gtk_grid_get_child_at(GTK_GRID(priv->grid),
684 0, i);
685 EntangleControl *that = g_object_get_data(G_OBJECT(widget), "control");
686
687 if (that == control)
688 return;
689 }
690
691 gtk_grid_insert_row(GTK_GRID(priv->grid), priv->rows);
692 do_setup_control(panel, control, GTK_CONTAINER(priv->grid), priv->rows++);
693 do_update_control_prefs(panel);
694 }
695
do_remove_control(EntangleControlPanel * panel,EntangleControl * control)696 static void do_remove_control(EntangleControlPanel *panel,
697 EntangleControl *control)
698 {
699 EntangleControlPanelPrivate *priv = panel->priv;
700 gsize i;
701
702 for (i = 0; i < priv->rows; i++) {
703 GtkWidget *widget = gtk_grid_get_child_at(GTK_GRID(priv->grid),
704 0, i);
705 EntangleControl *that = g_object_get_data(G_OBJECT(widget), "control");
706
707 if (that == control) {
708 gtk_grid_remove_row(GTK_GRID(priv->grid), i);
709 priv->rows--;
710 break;
711 }
712 }
713 do_update_control_prefs(panel);
714 }
715
716
do_addremove_control(GtkWidget * src,EntangleControlPanel * panel)717 static void do_addremove_control(GtkWidget *src,
718 EntangleControlPanel *panel)
719 {
720 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(panel));
721
722 EntangleControl *control;
723
724 control = g_object_get_data(G_OBJECT(src), "control");
725 g_return_if_fail(ENTANGLE_IS_CONTROL(control));
726
727 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(src)))
728 do_add_control(panel, control);
729 else
730 do_remove_control(panel, control);
731 }
732
733
do_create_control_menu(EntangleControlPanel * panel,EntangleControlGroup * group,gchar ** controlnames)734 static GtkWidget *do_create_control_menu(EntangleControlPanel *panel,
735 EntangleControlGroup *group,
736 gchar **controlnames)
737 {
738 GList *controls = do_get_control_list(group);
739 GList *tmp;
740 GtkWidget *menu = gtk_menu_new();
741 GtkWidget *item;
742
743 tmp = controls = g_list_sort(controls, compare_control);
744
745 while (tmp) {
746 EntangleControl *control = tmp->data;
747 item = gtk_check_menu_item_new_with_label(entangle_control_get_label(control));
748
749 g_object_set_data(G_OBJECT(item), "control", control);
750 g_signal_connect(item, "toggled",
751 G_CALLBACK(do_addremove_control), panel);
752
753 gtk_container_add(GTK_CONTAINER(menu),
754 item);
755
756 if (is_control_enabled(controlnames,
757 entangle_control_get_path(control)))
758 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
759 TRUE);
760
761 tmp = tmp->next;
762 }
763
764 g_list_free(controls);
765
766 item = gtk_separator_menu_item_new();
767 gtk_container_add(GTK_CONTAINER(menu), item);
768
769 item = gtk_menu_item_new_with_label(_("Reset controls"));
770 gtk_container_add(GTK_CONTAINER(menu), item);
771 g_signal_connect(item, "activate", G_CALLBACK(do_reset_controls), panel);
772
773 gtk_widget_show_all(menu);
774
775 return menu;
776 }
777
778
do_setup_controls(EntangleControlPanel * panel)779 static void do_setup_controls(EntangleControlPanel *panel)
780 {
781 EntangleControlPanelPrivate *priv = panel->priv;
782 EntangleControlGroup *root;
783 GtkWidget *settingsButton;
784 gchar **controls;
785 GtkWidget *menu;
786 gsize i;
787
788 root = entangle_camera_get_controls(priv->camera, NULL);
789
790 controls = entangle_camera_preferences_get_controls(priv->cameraPrefs);
791 if (!controls || !controls[0]) {
792 controls = entangle_control_panel_get_default_controls(root);
793 entangle_camera_preferences_set_controls(priv->cameraPrefs,
794 (const char *const *)controls);
795 }
796
797
798 for (i = 0; controls[i] != NULL; i++) {
799 EntangleControl *control = entangle_control_group_get_by_path(root,
800 controls[i]);
801 if (control)
802 do_setup_control(panel, control, GTK_CONTAINER(priv->grid), priv->rows++);
803 }
804
805 menu = do_create_control_menu(panel,
806 root,
807 controls);
808
809 settingsButton = gtk_menu_button_new();
810 gtk_container_add(GTK_CONTAINER(settingsButton),
811 gtk_image_new_from_icon_name("emblem-system-symbolic",
812 GTK_ICON_SIZE_SMALL_TOOLBAR));
813 gtk_menu_button_set_popup(GTK_MENU_BUTTON(settingsButton),
814 menu);
815 gtk_widget_set_hexpand(settingsButton, TRUE);
816 gtk_widget_set_halign(settingsButton, GTK_ALIGN_END);
817 #if GTK_CHECK_VERSION(3, 12, 0)
818 gtk_widget_set_margin_end(settingsButton, 6);
819 #else
820 gtk_widget_set_margin_right(settingsButton, 6);
821 #endif
822
823
824 gtk_grid_attach(GTK_GRID(priv->grid), settingsButton, 1, priv->rows, 2, 1);
825
826 gtk_widget_show_all(GTK_WIDGET(panel));
827 g_object_unref(root);
828 g_strfreev(controls);
829 }
830
do_setup_camera(EntangleControlPanel * panel)831 static void do_setup_camera(EntangleControlPanel *panel)
832 {
833 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(panel));
834
835 EntangleControlPanelPrivate *priv = panel->priv;
836
837 gtk_container_foreach(GTK_CONTAINER(priv->grid), do_control_remove, panel);
838 priv->rows = 0;
839
840 if (!priv->camera) {
841 GtkWidget *label = gtk_label_new(_("No camera connected"));
842
843 gtk_widget_set_hexpand(label, TRUE);
844 gtk_widget_set_halign(label, GTK_ALIGN_FILL);
845 gtk_grid_attach(GTK_GRID(priv->grid), label, 0, 0, 2, 1);
846 gtk_widget_show_all(GTK_WIDGET(panel));
847 } else if (entangle_camera_get_controls(priv->camera, NULL) == NULL) {
848 GtkWidget *label = gtk_label_new(_("No controls available"));
849 gtk_widget_set_hexpand(label, TRUE);
850 gtk_widget_set_halign(label, GTK_ALIGN_FILL);
851 gtk_grid_attach(GTK_GRID(priv->grid), label, 0, 0, 2, 1);
852 gtk_widget_show_all(GTK_WIDGET(panel));
853 } else {
854 do_setup_controls(panel);
855 }
856 }
857
858
do_update_camera(GObject * object G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,gpointer data)859 static void do_update_camera(GObject *object G_GNUC_UNUSED,
860 GParamSpec *pspec G_GNUC_UNUSED,
861 gpointer data)
862 {
863 g_return_if_fail(ENTANGLE_IS_CONTROL_PANEL(data));
864
865 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(data);
866 EntangleControlPanelPrivate *priv = panel->priv;
867
868 if (priv->camera) {
869 g_object_unref(priv->camera);
870 priv->camera = NULL;
871 }
872 priv->camera = entangle_camera_preferences_get_camera(priv->cameraPrefs);
873 if (priv->camera)
874 g_object_ref(priv->camera);
875
876 do_setup_camera(panel);
877 }
878
879
entangle_control_panel_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)880 static void entangle_control_panel_get_property(GObject *object,
881 guint prop_id,
882 GValue *value,
883 GParamSpec *pspec)
884 {
885 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(object);
886 EntangleControlPanelPrivate *priv = panel->priv;
887
888 switch (prop_id)
889 {
890 case PROP_CAMERA_PREFS:
891 g_value_set_object(value, priv->cameraPrefs);
892 break;
893
894 case PROP_CAMERA:
895 g_value_set_object(value, priv->camera);
896 break;
897
898 case PROP_HAS_CONTROLS:
899 g_value_set_boolean(value, priv->hasControls);
900 break;
901
902 default:
903 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
904 }
905 }
906
907
entangle_control_panel_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)908 static void entangle_control_panel_set_property(GObject *object,
909 guint prop_id,
910 const GValue *value,
911 GParamSpec *pspec)
912 {
913 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(object);
914 EntangleControlPanelPrivate *priv = panel->priv;
915
916 ENTANGLE_DEBUG("Set prop on control panel %d", prop_id);
917
918 switch (prop_id)
919 {
920 case PROP_CAMERA_PREFS:
921 priv->cameraPrefs = g_value_get_object(value);
922 priv->sigCamera = g_signal_connect(priv->cameraPrefs, "notify::camera",
923 G_CALLBACK(do_update_camera), panel);
924 break;
925
926 default:
927 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
928 }
929 }
930
931
entangle_control_panel_finalize(GObject * object)932 static void entangle_control_panel_finalize(GObject *object)
933 {
934 EntangleControlPanel *panel = ENTANGLE_CONTROL_PANEL(object);
935 EntangleControlPanelPrivate *priv = panel->priv;
936
937 if (priv->camera)
938 g_object_unref(priv->camera);
939
940 if (priv->cameraPrefs) {
941 g_signal_handler_disconnect(priv->cameraPrefs, priv->sigCamera);
942 g_object_unref(priv->cameraPrefs);
943 }
944
945 G_OBJECT_CLASS(entangle_control_panel_parent_class)->finalize(object);
946 }
947
948
entangle_control_panel_class_init(EntangleControlPanelClass * klass)949 static void entangle_control_panel_class_init(EntangleControlPanelClass *klass)
950 {
951 GObjectClass *object_class = G_OBJECT_CLASS(klass);
952
953 object_class->finalize = entangle_control_panel_finalize;
954 object_class->get_property = entangle_control_panel_get_property;
955 object_class->set_property = entangle_control_panel_set_property;
956
957 g_object_class_install_property(object_class,
958 PROP_CAMERA_PREFS,
959 g_param_spec_object("camera-prefs",
960 "Camera prefs",
961 "Camera preferences to manage",
962 ENTANGLE_TYPE_CAMERA_PREFERENCES,
963 G_PARAM_READWRITE |
964 G_PARAM_CONSTRUCT_ONLY |
965 G_PARAM_STATIC_NAME |
966 G_PARAM_STATIC_NICK |
967 G_PARAM_STATIC_BLURB));
968
969 g_object_class_install_property(object_class,
970 PROP_CAMERA,
971 g_param_spec_object("camera",
972 "Camera",
973 "Camera to manage",
974 ENTANGLE_TYPE_CAMERA,
975 G_PARAM_READABLE |
976 G_PARAM_STATIC_NAME |
977 G_PARAM_STATIC_NICK |
978 G_PARAM_STATIC_BLURB));
979
980 g_object_class_install_property(object_class,
981 PROP_HAS_CONTROLS,
982 g_param_spec_boolean("has-controls",
983 "Has Controls",
984 "Has Controls",
985 FALSE,
986 G_PARAM_READABLE |
987 G_PARAM_STATIC_NAME |
988 G_PARAM_STATIC_NICK |
989 G_PARAM_STATIC_BLURB));
990
991 g_type_class_add_private(klass, sizeof(EntangleControlPanelPrivate));
992 }
993
994
entangle_control_panel_new(EntangleCameraPreferences * prefs)995 EntangleControlPanel *entangle_control_panel_new(EntangleCameraPreferences *prefs)
996 {
997 return ENTANGLE_CONTROL_PANEL(g_object_new(ENTANGLE_TYPE_CONTROL_PANEL,
998 "camera-prefs", prefs,
999 "label", "Camera settings",
1000 "expanded", TRUE,
1001 NULL));
1002 }
1003
1004
entangle_control_panel_init(EntangleControlPanel * panel)1005 static void entangle_control_panel_init(EntangleControlPanel *panel)
1006 {
1007 EntangleControlPanelPrivate *priv = panel->priv;
1008
1009 priv = panel->priv = ENTANGLE_CONTROL_PANEL_GET_PRIVATE(panel);
1010
1011 gtk_container_set_border_width(GTK_CONTAINER(panel), 0);
1012
1013 priv->grid = gtk_grid_new();
1014 gtk_grid_set_row_spacing(GTK_GRID(priv->grid), 6);
1015 gtk_grid_set_column_spacing(GTK_GRID(priv->grid), 6);
1016 gtk_container_set_border_width(GTK_CONTAINER(priv->grid), 6);
1017 gtk_widget_set_hexpand(priv->grid, TRUE);
1018 gtk_widget_set_halign(priv->grid, GTK_ALIGN_FILL);
1019
1020 gtk_container_add(GTK_CONTAINER(panel), priv->grid);
1021
1022 do_setup_camera(panel);
1023 }
1024
1025
1026 /**
1027 * entangle_control_panel_get_camera_preferences:
1028 * @panel: the control widget
1029 *
1030 * Get the camera preferences whose controls are displayed
1031 *
1032 * Returns: (transfer none): the camera preferences or NULL
1033 */
entangle_control_panel_get_camera_preferences(EntangleControlPanel * panel)1034 EntangleCameraPreferences *entangle_control_panel_get_camera_preferences(EntangleControlPanel *panel)
1035 {
1036 g_return_val_if_fail(ENTANGLE_IS_CONTROL_PANEL(panel), NULL);
1037
1038 EntangleControlPanelPrivate *priv = panel->priv;
1039
1040 return priv->cameraPrefs;
1041 }
1042
1043
entangle_control_panel_get_has_controls(EntangleControlPanel * panel)1044 gboolean entangle_control_panel_get_has_controls(EntangleControlPanel *panel)
1045 {
1046 g_return_val_if_fail(ENTANGLE_IS_CONTROL_PANEL(panel), FALSE);
1047
1048 EntangleControlPanelPrivate *priv = panel->priv;
1049
1050 return priv->hasControls;
1051 }
1052
1053
1054 /*
1055 * Local variables:
1056 * c-indent-level: 4
1057 * c-basic-offset: 4
1058 * indent-tabs-mode: nil
1059 * tab-width: 8
1060 * End:
1061 */
1062