]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplication.c
Update to GApplication api changes
[~andy/gtk] / gtk / gtkapplication.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Colin Walters <walters@verbum.org>
21  */
22
23 /*
24  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28  */
29
30 #include "config.h"
31
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #include "gtkapplication.h"
36 #include "gtkmain.h"
37 #include "gtkintl.h"
38 #include "gtkprivate.h"
39
40 #include "gtkalias.h"
41
42 #include <gdk/gdk.h>
43 #ifdef GDK_WINDOWING_X11
44 #include <gdk/x11/gdkx.h>
45 #endif
46
47 /**
48  * SECTION:gtkapplication
49  * @title: GtkApplication
50  * @short_description: Application class
51  *
52  * #GtkApplication is a class that handles many important aspects
53  * of a GTK+ application in a convenient fashion, without enforcing
54  * a one-size-fits-all application model.
55  *
56  * Currently, GtkApplication handles application uniqueness, provides
57  * some basic scriptability by exporting 'actions', implements some
58  * standard actions itself (such as 'Quit') and provides a main window
59  * whose life-cycle is automatically tied to the life-cycle of your
60  * application.
61  *
62  * <example id="gtkapplication"><title>A simple application</title>
63  * <programlisting>
64  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gtk/tests/gtk-example-application.c">
65  *  <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
66  * </xi:include>
67  * </programlisting>
68  * </example>
69  */
70 enum
71 {
72   PROP_0,
73   PROP_WINDOW
74 };
75
76 enum
77 {
78   ACTIVATED,
79
80   LAST_SIGNAL
81 };
82
83 static guint gtk_application_signals[LAST_SIGNAL] = { 0 };
84
85 struct _GtkApplicationPrivate
86 {
87   GtkActionGroup *main_actions;
88
89   GtkWindow *default_window;
90   GSList *windows;
91 };
92
93 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
94
95 static gboolean
96 gtk_application_default_quit (GApplication *application,
97                               guint         timestamp)
98 {
99   gtk_main_quit ();
100   return TRUE;
101 }
102
103 static void
104 gtk_application_default_run (GApplication *application)
105 {
106   gtk_main ();
107 }
108
109 static void
110 gtk_application_default_prepare_activation (GApplication *application,
111                                             GVariant     *arguments,
112                                             GVariant     *platform_data)
113 {
114   GVariantIter iter;
115   gchar *key;
116   GVariant *value;
117
118   g_variant_iter_init (&iter, platform_data);
119   while (g_variant_iter_next (&iter, "{sv}", &key, &value))
120     {
121       if (strcmp (key, "startup-notification-id") == 0 &&
122           strcmp (g_variant_get_type_string (value), "s") == 0)
123         gdk_notify_startup_complete_with_id (g_variant_get_string (value, NULL));
124       g_free (key);
125       g_variant_unref (value);
126     }
127   
128   g_signal_emit (G_OBJECT (application), gtk_application_signals[ACTIVATED], 0, arguments);
129 }
130
131 static void
132 gtk_application_default_activated (GApplication *application,
133                                    GVariant     *arguments)
134 {
135   GtkApplication *app = GTK_APPLICATION (application);
136
137   /* TODO: should we raise the last focused window instead ? */
138   if (app->priv->default_window != NULL)
139     gtk_window_present (app->priv->default_window);
140 }
141
142 static void
143 gtk_application_default_action (GApplication *application,
144                                 const gchar  *action,
145                                 guint         timestamp)
146 {
147   GtkApplication *app = GTK_APPLICATION (application);
148   GList *actions, *iter;
149
150   actions = gtk_action_group_list_actions (app->priv->main_actions);
151   for (iter = actions; iter; iter = iter->next)
152     {
153       GtkAction *gtkaction = iter->data;
154       if (strcmp (action, gtk_action_get_name (gtkaction)) == 0)
155         {
156           /* TODO set timestamp */
157           gtk_action_activate (gtkaction);
158           break;
159         }
160     }
161   g_list_free (actions);
162 }
163
164 static GVariant *
165 gtk_application_format_activation_data (void)
166 {
167   const gchar *startup_id = NULL;
168   GdkDisplay *display = gdk_display_get_default ();
169   GVariantBuilder builder;
170
171   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
172
173   /* try and get the startup notification id from GDK, the environment
174    * or, if everything else failed, fake one.
175    */
176 #ifdef GDK_WINDOWING_X11
177   startup_id = gdk_x11_display_get_startup_notification_id (display);
178 #endif /* GDK_WINDOWING_X11 */
179
180   if (startup_id)
181     g_variant_builder_add (&builder, "{sv}", "startup-notification-id",
182                            g_variant_new ("s", startup_id));
183   return g_variant_builder_end (&builder);
184 }
185
186 /**
187  * gtk_application_new:
188  * @argc: (allow-none) (inout): System argument count
189  * @argv: (allow-none) (inout): System argument vector
190  * @appid: System-dependent application identifier
191  *
192  * Create a new #GtkApplication, or if one has already been initialized
193  * in this process, return the existing instance. This function will as
194  * a side effect initialize the display system; see gtk_init().
195  *
196  * For the behavior if this application is running in another process,
197  * see g_application_new().
198  *
199  * Returns: (transfer full): A newly-referenced #GtkApplication
200  *
201  * Since: 3.0
202  */
203 GtkApplication*
204 gtk_application_new (gint          *argc,
205                      gchar       ***argv,
206                      const gchar   *appid)
207 {
208   GtkApplication *app;
209   gint argc_for_app;
210   gchar **argv_for_app;
211   GVariant *platform_data;
212
213   gtk_init (argc, argv);
214
215   if (argc)
216     argc_for_app = *argc;
217   else
218     argc_for_app = 0;
219   if (argv)
220     argv_for_app = *argv;
221   else
222     argv_for_app = NULL;
223
224   app = g_object_new (GTK_TYPE_APPLICATION, "application-id", appid, NULL);
225
226   platform_data = gtk_application_format_activation_data ();
227   g_application_register_with_data (G_APPLICATION (app), argc_for_app, argv_for_app,
228                                     platform_data);
229   g_variant_unref (platform_data);
230
231   return app;
232 }
233
234 static void
235 on_action_sensitive (GtkAction      *action,
236                      GParamSpec     *pspec,
237                      GtkApplication *app)
238 {
239   g_application_set_action_enabled (G_APPLICATION (app),
240                                     gtk_action_get_name (action),
241                                     gtk_action_get_sensitive (action));
242 }
243
244 /**
245  * gtk_application_set_action_group:
246  * @app: A #GtkApplication
247  * @group: A #GtkActionGroup
248  *
249  * Set @group as this application's global action group.
250  * This will ensure the operating system interface uses
251  * these actions as follows:
252  *
253  * <itemizedlist>
254  *   <listitem>In GNOME 2 this exposes the actions for scripting.</listitem>
255  *   <listitem>In GNOME 3, this function populates the application menu.</listitem>
256  *   <listitem>In Windows prior to version 7, this function does nothing.</listitem>
257  *   <listitem>In Windows 7, this function adds "Tasks" to the Jump List.</listitem>
258  *   <listitem>In Mac OS X, this function extends the Dock menu.</listitem>
259  * </itemizedlist>
260  *
261  * It is an error to call this function more than once.
262  *
263  * Since: 3.0
264  */
265 void
266 gtk_application_set_action_group (GtkApplication *app,
267                                   GtkActionGroup *group)
268 {
269   GList *actions, *iter;
270
271   g_return_if_fail (GTK_IS_APPLICATION (app));
272   g_return_if_fail (app->priv->main_actions == NULL);
273
274   app->priv->main_actions = g_object_ref (group);
275   actions = gtk_action_group_list_actions (group);
276   for (iter = actions; iter; iter = iter->next)
277     {
278       GtkAction *action = iter->data;
279       g_application_add_action (G_APPLICATION (app),
280                                 gtk_action_get_name (action),
281                                 gtk_action_get_tooltip (action));
282       g_signal_connect (action, "notify::sensitive",
283                         G_CALLBACK (on_action_sensitive), app);
284     }
285   g_list_free (actions);
286 }
287
288 static gboolean
289 gtk_application_on_window_destroy (GtkWidget *window,
290                                    gpointer   user_data)
291 {
292   GtkApplication *app = GTK_APPLICATION (user_data);
293
294   app->priv->windows = g_slist_remove (app->priv->windows, window);
295
296   if (app->priv->windows == NULL)
297     gtk_application_quit (app);
298
299   return FALSE;
300 }
301
302 static gchar *default_title;
303
304 /**
305  * gtk_application_add_window:
306  * @app: a #GtkApplication
307  * @window: a toplevel window to add to @app
308  *
309  * Adds a window to the #GtkApplication.
310  *
311  * If the user closes all of the windows added to @app, the default
312  * behaviour is to call gtk_application_quit().
313  *
314  * If your application uses only a single toplevel window, you can
315  * use gtk_application_get_window().
316  *
317  * Since: 3.0
318  */
319 void
320 gtk_application_add_window (GtkApplication *app,
321                             GtkWindow      *window)
322 {
323   app->priv->windows = g_slist_prepend (app->priv->windows, window);
324
325   if (gtk_window_get_title (window) == NULL && default_title != NULL)
326     gtk_window_set_title (window, default_title);
327
328   g_signal_connect (window, "destroy",
329                     G_CALLBACK (gtk_application_on_window_destroy), app);
330 }
331
332 /**
333  * gtk_application_get_window:
334  * @app: a #GtkApplication
335  *
336  * A simple #GtkApplication has a "default window". This window should
337  * act as the primary user interaction point with your application.
338  * The window returned by this function is of type #GTK_WINDOW_TYPE_TOPLEVEL
339  * and its properties such as "title" and "icon-name" will be initialized
340  * as appropriate for the platform.
341  *
342  * If the user closes this window, and your application hasn't created
343  * any other windows, the default action will be to call gtk_application_quit().
344  *
345  * If your application has more than one toplevel window (e.g. an
346  * single-document-interface application with multiple open documents),
347  * or if you are constructing your toplevel windows yourself (e.g. using
348  * #GtkBuilder), use gtk_application_add_window() instead.
349  *
350  * Returns: (transfer none): The default #GtkWindow for this application
351  *
352  * Since: 3.0
353  */
354 GtkWindow *
355 gtk_application_get_window (GtkApplication *app)
356 {
357   if (app->priv->default_window != NULL)
358     return app->priv->default_window;
359
360   app->priv->default_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
361   g_object_ref_sink (app->priv->default_window);
362
363   gtk_application_add_window (app, app->priv->default_window);
364
365   return app->priv->default_window;
366 }
367
368 /**
369  * gtk_application_run:
370  * @app: a #GtkApplication
371  *
372  * Runs the main loop; see g_application_run().
373  * The default implementation for #GtkApplication uses gtk_main().
374  *
375  * Since: 3.0
376  */
377 void
378 gtk_application_run (GtkApplication *app)
379 {
380   g_application_run (G_APPLICATION (app));
381 }
382
383 /**
384  * gtk_application_quit:
385  * @app: a #GtkApplication
386  *
387  * Request the application exit.
388  * By default, this method will exit the main loop; see gtk_main_quit().
389  *
390  * Since: 3.0
391  */
392 void
393 gtk_application_quit (GtkApplication *app)
394 {
395   g_application_quit (G_APPLICATION (app), gtk_get_current_event_time ());
396 }
397
398 static void
399 gtk_application_get_property (GObject    *object,
400                               guint       prop_id,
401                               GValue     *value,
402                               GParamSpec *pspec)
403 {
404   GtkApplication *app = GTK_APPLICATION (object);
405
406   switch (prop_id)
407     {
408       case PROP_WINDOW:
409         g_value_set_object (value, gtk_application_get_window (app));
410         break;
411       default:
412         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
413     }
414 }
415
416 static void
417 gtk_application_set_property (GObject      *object,
418                               guint         prop_id,
419                               const GValue *value,
420                               GParamSpec   *pspec)
421 {
422   GtkApplication *app = GTK_APPLICATION (object);
423
424   g_assert (app != NULL);
425
426   switch (prop_id)
427     {
428       default:
429         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430     }
431 }
432
433 static void
434 setup_default_window_decorations (void)
435 {
436   const gchar *pid;
437   const gchar *filename;
438   GKeyFile *keyfile;
439   gchar *title;
440   gchar *icon_name;
441
442   pid = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID");
443   filename = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
444
445   keyfile = g_key_file_new ();
446
447   if (pid != NULL && filename != NULL && atoi (pid) == getpid () &&
448       g_key_file_load_from_file (keyfile, filename, 0, NULL))
449     {
450       title = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
451       icon_name = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
452
453       g_print ("default title: %s\n", title);
454       g_print ("default icon: %s\n", icon_name);
455
456       if (default_title == NULL)
457         default_title = title;
458
459       if (gtk_window_get_default_icon_name () == NULL)
460         gtk_window_set_default_icon_name (icon_name);
461
462       g_free (icon_name);
463     }
464
465   g_key_file_free (keyfile);
466 }
467
468 static void
469 gtk_application_init (GtkApplication *application)
470 {
471   application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application, GTK_TYPE_APPLICATION, GtkApplicationPrivate);
472
473   setup_default_window_decorations ();
474 }
475
476
477 static GObject*
478 gtk_application_constructor (GType                  type,
479                              guint                  n_construct_properties,
480                              GObjectConstructParam *construct_params)
481 {
482   GObject *object;
483
484   /* Last ditch effort here */
485   gtk_init (0, NULL);
486
487   object = (* G_OBJECT_CLASS (gtk_application_parent_class)->constructor) (type,
488                                                                            n_construct_properties,
489                                                                            construct_params);
490
491   return object;
492 }
493
494 static void
495 gtk_application_class_init (GtkApplicationClass *klass)
496 {
497   GObjectClass *gobject_class;
498   GApplicationClass *application_class;
499
500   gobject_class = G_OBJECT_CLASS (klass);
501   application_class = G_APPLICATION_CLASS (klass);
502
503   gobject_class->constructor = gtk_application_constructor;
504   gobject_class->get_property = gtk_application_get_property;
505   gobject_class->set_property = gtk_application_set_property;
506
507   application_class->run = gtk_application_default_run;
508   application_class->quit = gtk_application_default_quit;
509   application_class->action = gtk_application_default_action;
510   application_class->prepare_activation = gtk_application_default_prepare_activation;
511
512   klass->activated = gtk_application_default_activated;
513
514   /**
515    * GtkApplication::activated:
516    * @arguments: A #GVariant with the signature "aay"
517    *
518    * This signal is emitted when a non-primary process for a given
519    * application is invoked while your application is running; for
520    * example, when a file browser launches your program to open a
521    * file.  The raw operating system arguments are passed in the
522    * variant @arguments.
523    */
524
525   gtk_application_signals[ACTIVATED] =
526     g_signal_new (g_intern_static_string ("activated"),
527                   G_OBJECT_CLASS_TYPE (klass),
528                   G_SIGNAL_RUN_LAST,
529                   G_STRUCT_OFFSET (GtkApplicationClass, activated),
530                   NULL, NULL,
531                   g_cclosure_marshal_VOID__BOXED,
532                   G_TYPE_NONE, 1,
533                   G_TYPE_VARIANT);
534
535   g_type_class_add_private (gobject_class, sizeof (GtkApplicationPrivate));
536 }
537
538 #define __GTK_APPLICATION_C__
539 #include "gtkaliasdef.c"