Set default window icon for application
[entangle:entangle.git] / src / frontend / entangle-application.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 <glib.h>
24 #include <glib/gi18n.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "entangle-debug.h"
29 #include "entangle-application.h"
30 #include "entangle-preferences.h"
31 #include "entangle-camera-manager.h"
32
33 /**
34  * SECTION:entangle-application
35  * @Short_description: Global application state base class
36  * @Title: EntangleApplication
37  *
38  * <para>
39  * EntangleApplication maintains some global application state. At this time,
40  * the list of currently attached cameras, the application preferences
41  * and the plugin manager.
42  * </para>
43  *
44  * <para>
45  * This class will normally be sub-classed when creating a Entangle based
46  * application, typically in order to add in UI state.
47  * </para>
48  */
49
50
51 #define ENTANGLE_APPLICATION_GET_PRIVATE(obj)                           \
52     (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_APPLICATION, EntangleApplicationPrivate))
53
54 struct _EntangleApplicationPrivate {
55     EntangleCameraList *activeCameras;
56     EntangleCameraList *supportedCameras;
57
58     EntanglePreferences *preferences;
59
60     PeasEngine *pluginEngine;
61     PeasExtensionSet *pluginExt;
62 };
63
64 G_DEFINE_TYPE(EntangleApplication, entangle_application, GTK_TYPE_APPLICATION);
65
66 enum {
67     PROP_0,
68     PROP_ACTIVE_CAMERAS,
69     PROP_SUPPORTED_CAMERAS,
70     PROP_PREFERENCES,
71 };
72
73 static void entangle_application_get_property(GObject *object,
74                                               guint prop_id,
75                                               GValue *value,
76                                               GParamSpec *pspec)
77 {
78     EntangleApplication *app = ENTANGLE_APPLICATION(object);
79     EntangleApplicationPrivate *priv = app->priv;
80
81     switch (prop_id) {
82     case PROP_ACTIVE_CAMERAS:
83         g_value_set_object(value, priv->activeCameras);
84         break;
85
86     case PROP_SUPPORTED_CAMERAS:
87         g_value_set_object(value, priv->supportedCameras);
88         break;
89
90     case PROP_PREFERENCES:
91         g_value_set_object(value, priv->preferences);
92         break;
93
94     default:
95         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
96     }
97 }
98
99
100 static void entangle_application_set_property(GObject *object,
101                                               guint prop_id,
102                                               const GValue *value,
103                                               GParamSpec *pspec)
104 {
105     EntangleApplication *app = ENTANGLE_APPLICATION(object);
106     EntangleApplicationPrivate *priv = app->priv;
107
108     switch (prop_id) {
109     case PROP_ACTIVE_CAMERAS:
110         if (priv->activeCameras)
111             g_object_unref(priv->activeCameras);
112         priv->activeCameras = g_value_get_object(value);
113         g_object_ref(priv->activeCameras);
114         break;
115
116     case PROP_SUPPORTED_CAMERAS:
117         if (priv->supportedCameras)
118             g_object_unref(priv->supportedCameras);
119         priv->supportedCameras = g_value_get_object(value);
120         g_object_ref(priv->supportedCameras);
121         break;
122
123     case PROP_PREFERENCES:
124         if (priv->preferences)
125             g_object_unref(priv->preferences);
126         priv->preferences = g_value_get_object(value);
127         g_object_ref(priv->preferences);
128         break;
129
130     default:
131         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
132     }
133 }
134
135
136 static void entangle_application_finalize(GObject *object)
137 {
138     EntangleApplication *app = ENTANGLE_APPLICATION(object);
139     EntangleApplicationPrivate *priv = app->priv;
140
141     ENTANGLE_DEBUG("Finalize application %p", object);
142
143     if (priv->activeCameras)
144         g_object_unref(priv->activeCameras);
145     if (priv->supportedCameras)
146         g_object_unref(priv->supportedCameras);
147     if (priv->preferences)
148         g_object_unref(priv->preferences);
149     if (priv->pluginEngine)
150         g_object_unref(priv->pluginEngine);
151     if (priv->pluginExt)
152         g_object_unref(priv->pluginExt);
153
154     G_OBJECT_CLASS(entangle_application_parent_class)->finalize(object);
155 }
156
157
158 static void entangle_application_activate(GApplication *gapp)
159 {
160     GList *wins = gtk_application_get_windows(GTK_APPLICATION(gapp));
161     while (wins) {
162         GtkWindow *win = wins->data;
163         gtk_widget_show(GTK_WIDGET(win));
164         gtk_window_present(win);
165         wins = wins->next;
166     }
167 }
168
169 static void entangle_application_startup(GApplication *gapp)
170 {
171     g_return_if_fail(ENTANGLE_IS_APPLICATION(gapp));
172
173     EntangleApplication *app = ENTANGLE_APPLICATION(gapp);
174     EntangleApplicationPrivate *priv = app->priv;
175     GList *cameras = NULL, *tmp = NULL;
176     gboolean gotcamera = FALSE;
177
178     (*G_APPLICATION_CLASS(entangle_application_parent_class)->startup)(gapp);
179
180     gtk_window_set_default_icon_name("entangle");
181
182     if (entangle_preferences_interface_get_auto_connect(priv->preferences))
183         cameras = tmp = entangle_camera_list_get_cameras(priv->activeCameras);
184
185     while (tmp) {
186         EntangleCamera *cam = ENTANGLE_CAMERA(tmp->data);
187
188         ENTANGLE_DEBUG("Opening window for %s",
189                        entangle_camera_get_port(cam));
190
191         if (entangle_camera_get_has_capture(cam)) {
192             EntangleCameraManager *manager = entangle_camera_manager_new();
193             gtk_application_add_window(GTK_APPLICATION(gapp), GTK_WINDOW(manager));
194             gtk_widget_show(GTK_WIDGET(manager));
195             entangle_camera_manager_set_camera(manager, cam);
196             gotcamera = TRUE;
197         }
198
199         tmp = tmp->next;
200     }
201     g_list_free(cameras);
202
203     if (!gotcamera) {
204         EntangleCameraManager *manager = entangle_camera_manager_new();
205         gtk_application_add_window(GTK_APPLICATION(gapp), GTK_WINDOW(manager));
206         gtk_widget_show(GTK_WIDGET(manager));
207     }
208 }
209
210
211 static void entangle_application_class_init(EntangleApplicationClass *klass)
212 {
213     GObjectClass *object_class = G_OBJECT_CLASS(klass);
214     GApplicationClass *app_class = G_APPLICATION_CLASS(klass);
215
216     object_class->finalize = entangle_application_finalize;
217     object_class->get_property = entangle_application_get_property;
218     object_class->set_property = entangle_application_set_property;
219
220     app_class->activate = entangle_application_activate;
221     app_class->startup = entangle_application_startup;
222
223     g_object_class_install_property(object_class,
224                                     PROP_ACTIVE_CAMERAS,
225                                     g_param_spec_object("active-cameras",
226                                                         "Active cameras",
227                                                         "List of active cameras",
228                                                         ENTANGLE_TYPE_CAMERA_LIST,
229                                                         G_PARAM_READABLE |
230                                                         G_PARAM_STATIC_NAME |
231                                                         G_PARAM_STATIC_NICK |
232                                                         G_PARAM_STATIC_BLURB));
233
234     g_object_class_install_property(object_class,
235                                     PROP_SUPPORTED_CAMERAS,
236                                     g_param_spec_object("supported-cameras",
237                                                         "Supported cameras",
238                                                         "List of supported cameras",
239                                                         ENTANGLE_TYPE_CAMERA_LIST,
240                                                         G_PARAM_READABLE |
241                                                         G_PARAM_STATIC_NAME |
242                                                         G_PARAM_STATIC_NICK |
243                                                         G_PARAM_STATIC_BLURB));
244
245     g_object_class_install_property(object_class,
246                                     PROP_PREFERENCES,
247                                     g_param_spec_object("preferences",
248                                                         "Preferences",
249                                                         "Application preferences",
250                                                         ENTANGLE_TYPE_PREFERENCES,
251                                                         G_PARAM_READABLE |
252                                                         G_PARAM_STATIC_NAME |
253                                                         G_PARAM_STATIC_NICK |
254                                                         G_PARAM_STATIC_BLURB));
255
256     g_type_class_add_private(klass, sizeof(EntangleApplicationPrivate));
257 }
258
259
260 EntangleApplication *entangle_application_new(void)
261 {
262     return ENTANGLE_APPLICATION(g_object_new(ENTANGLE_TYPE_APPLICATION,
263                                              "application-id", "org.entangle_photo.Manager",
264                                              NULL));
265 }
266
267
268 static void
269 on_extension_added(PeasExtensionSet *set G_GNUC_UNUSED,
270                    PeasPluginInfo *info G_GNUC_UNUSED,
271                    PeasExtension *exten,
272                    gpointer opaque G_GNUC_UNUSED)
273 {
274     peas_activatable_activate(PEAS_ACTIVATABLE(exten));
275 }
276
277
278 static void
279 on_extension_removed(PeasExtensionSet *set G_GNUC_UNUSED,
280                      PeasPluginInfo *info G_GNUC_UNUSED,
281                      PeasExtension *exten,
282                      gpointer opaque G_GNUC_UNUSED)
283 {
284     peas_activatable_deactivate(PEAS_ACTIVATABLE(exten));
285 }
286
287
288 static void
289 on_plugin_load(PeasEngine *engine G_GNUC_UNUSED,
290                PeasPluginInfo *info,
291                gpointer data)
292 {
293     g_return_if_fail(ENTANGLE_IS_APPLICATION(data));
294
295     EntangleApplication *app = data;
296     EntangleApplicationPrivate *priv = app->priv;
297
298     entangle_preferences_interface_add_plugin(priv->preferences,
299                                               peas_plugin_info_get_module_name(info));
300 }
301
302
303 static void
304 on_plugin_unload(PeasEngine *engine G_GNUC_UNUSED,
305                  PeasPluginInfo *info,
306                  gpointer data)
307 {
308     g_return_if_fail(ENTANGLE_IS_APPLICATION(data));
309
310     EntangleApplication *app = data;
311     EntangleApplicationPrivate *priv = app->priv;
312
313     entangle_preferences_interface_remove_plugin(priv->preferences,
314                                                  peas_plugin_info_get_module_name(info));
315 }
316
317
318 static void entangle_application_init(EntangleApplication *app)
319 {
320     EntangleApplicationPrivate *priv;
321     int i;
322     gchar *userdir;
323     gchar **plugins;
324     GtkSettings *gtk_settings;
325
326     gtk_settings = gtk_settings_get_default();
327     g_object_set(G_OBJECT(gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL);
328
329     priv = app->priv = ENTANGLE_APPLICATION_GET_PRIVATE(app);
330
331     priv->preferences = entangle_preferences_new();
332     priv->activeCameras = entangle_camera_list_new_active();
333     priv->supportedCameras = entangle_camera_list_new_supported();
334
335     g_irepository_require(g_irepository_get_default(),
336                           "Peas", "1.0", 0, NULL);
337
338     userdir = g_build_filename(g_get_user_config_dir(), "entangle/plugins", NULL);
339     g_mkdir_with_parents(userdir, 0777);
340
341     priv->pluginEngine = peas_engine_get_default();
342     peas_engine_enable_loader(priv->pluginEngine, "python3");
343
344     peas_engine_add_search_path(priv->pluginEngine,
345                                 userdir, userdir);
346     g_free(userdir);
347     if (access("./entangle-256x256.png", R_OK) == 0)
348         peas_engine_add_search_path(priv->pluginEngine,
349                                     "./plugins",
350                                     "./plugins");
351     else
352         peas_engine_add_search_path(priv->pluginEngine,
353                                     LIBDIR "/entangle/plugins",
354                                     DATADIR "/entangle/plugins");
355     peas_engine_rescan_plugins(priv->pluginEngine);
356
357     priv->pluginExt = peas_extension_set_new(priv->pluginEngine,
358                                              PEAS_TYPE_ACTIVATABLE,
359                                              "object", app,
360                                              NULL);
361
362     peas_extension_set_foreach(priv->pluginExt,
363                                (PeasExtensionSetForeachFunc)on_extension_added,
364                                NULL);
365
366     g_signal_connect(priv->pluginExt, "extension-added",
367                      G_CALLBACK(on_extension_added), NULL);
368     g_signal_connect(priv->pluginExt, "extension-removed",
369                      G_CALLBACK(on_extension_removed), NULL);
370
371     g_signal_connect(priv->pluginEngine, "load-plugin",
372                      G_CALLBACK(on_plugin_load), app);
373     g_signal_connect(priv->pluginEngine, "unload-plugin",
374                      G_CALLBACK(on_plugin_unload), app);
375
376     plugins = entangle_preferences_interface_get_plugins(priv->preferences);
377     for (i = 0; plugins[i] != NULL; i++) {
378         PeasPluginInfo *plugin = peas_engine_get_plugin_info(priv->pluginEngine,
379                                                              plugins[i]);
380         if (plugin) {
381             ENTANGLE_DEBUG("Plugin %p %s", plugin, plugins[i]);
382             peas_engine_load_plugin(priv->pluginEngine, plugin);
383         }
384     }
385     g_strfreev(plugins);
386 }
387
388
389 /**
390  * entangle_application_get_active_cameras:
391  *
392  * Retrieve the active camera list
393  *
394  * Returns: (transfer none): the camera list
395  */
396 EntangleCameraList *entangle_application_get_active_cameras(EntangleApplication *app)
397 {
398     g_return_val_if_fail(ENTANGLE_IS_APPLICATION(app), NULL);
399
400     EntangleApplicationPrivate *priv = app->priv;
401     return priv->activeCameras;
402 }
403
404
405 /**
406  * entangle_application_get_supported_cameras:
407  *
408  * Retrieve the supported camera list
409  *
410  * Returns: (transfer none): the camera list
411  */
412 EntangleCameraList *entangle_application_get_supported_cameras(EntangleApplication *app)
413 {
414     g_return_val_if_fail(ENTANGLE_IS_APPLICATION(app), NULL);
415
416     EntangleApplicationPrivate *priv = app->priv;
417     return priv->supportedCameras;
418 }
419
420
421 /**
422  * entangle_application_get_preferences:
423  *
424  * Retrieve the application preferences object
425  *
426  * Returns: (transfer none): the application preferences
427  */
428 EntanglePreferences *entangle_application_get_preferences(EntangleApplication *app)
429 {
430     g_return_val_if_fail(ENTANGLE_IS_APPLICATION(app), NULL);
431
432     EntangleApplicationPrivate *priv = app->priv;
433     return priv->preferences;
434 }
435
436 /**
437  * entangle_application_get_plugin_engine:
438  *
439  * Retrieve the plugin manager
440  *
441  * Returns: (transfer none): the plugin engine
442  */
443 PeasEngine *entangle_application_get_plugin_engine(EntangleApplication *app)
444 {
445     g_return_val_if_fail(ENTANGLE_IS_APPLICATION(app), NULL);
446
447     EntangleApplicationPrivate *priv = app->priv;
448     return priv->pluginEngine;
449 }
450
451
452 /*
453  * Local variables:
454  *  c-indent-level: 4
455  *  c-basic-offset: 4
456  *  indent-tabs-mode: nil
457  *  tab-width: 8
458  * End:
459  */