]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplication.c
Add GtkApplication
[~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   char *appid;
88   GtkActionGroup *main_actions;
89
90   GtkWindow *default_window;
91   GSList *windows;
92 };
93
94 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
95
96 static gboolean
97 gtk_application_default_quit (GApplication *application,
98                               guint         timestamp)
99 {
100   gtk_main_quit ();
101   return TRUE;
102 }
103
104 static void
105 gtk_application_default_run (GApplication *application)
106 {
107   gtk_main ();
108 }
109
110 static void
111 gtk_application_default_prepare_activation (GApplication *application,
112                                             GVariant     *arguments,
113                                             GVariant     *platform_data)
114 {
115   GVariantIter iter;
116   gchar *key;
117   GVariant *value;
118
119   g_variant_iter_init (&iter, platform_data);
120   while (g_variant_iter_next (&iter, "{sv}", &key, &value))
121     {
122       if (strcmp (key, "startup-notification-id") == 0 &&
123           strcmp (g_variant_get_type_string (value), "s") == 0)
124         gdk_notify_startup_complete_with_id (g_variant_get_string (value, NULL));
125       g_free (key);
126       g_variant_unref (value);
127     }
128   
129   g_signal_emit (G_OBJECT (application), gtk_application_signals[ACTIVATED], 0, arguments);
130 }
131
132 static void
133 gtk_application_default_activated (GApplication *application,
134                                    GVariant     *arguments)
135 {
136   GtkApplication *app = GTK_APPLICATION (application);
137
138   /* TODO: should we raise the last focused window instead ? */
139   if (app->priv->default_window != NULL)
140     gtk_window_present (app->priv->default_window);
141 }
142
143 static void
144 gtk_application_default_action (GApplication *application,
145                                 const gchar  *action,
146                                 guint         timestamp)
147 {
148   GtkApplication *app = GTK_APPLICATION (application);
149   GList *actions, *iter;
150
151   actions = gtk_action_group_list_actions (app->priv->main_actions);
152   for (iter = actions; iter; iter = iter->next)
153     {
154       GtkAction *gtkaction = iter->data;
155       if (strcmp (action, gtk_action_get_name (gtkaction)) == 0)
156         {
157           /* TODO set timestamp */
158           gtk_action_activate (gtkaction);
159           break;
160         }
161     }
162   g_list_free (actions);
163 }
164
165 static GVariant *
166 gtk_application_format_activation_data (void)
167 {
168   const gchar *startup_id = NULL;
169   GdkDisplay *display = gdk_display_get_default ();
170   GVariantBuilder builder;
171
172   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
173
174   /* try and get the startup notification id from GDK, the environment
175    * or, if everything else failed, fake one.
176    */
177 #ifdef GDK_WINDOWING_X11
178   startup_id = gdk_x11_display_get_startup_notification_id (display);
179 #endif /* GDK_WINDOWING_X11 */
180
181   if (startup_id)
182     g_variant_builder_add (&builder, "{sv}", "startup-notification-id",
183                            g_variant_new ("s", startup_id));
184   return g_variant_builder_end (&builder);
185 }
186
187 /**
188  * gtk_application_new:
189  * @argc: (allow-none) (inout): System argument count
190  * @argv: (allow-none) (inout): System argument vector
191  * @appid: System-dependent application identifier
192  *
193  * Create a new #GtkApplication, or if one has already been initialized
194  * in this process, return the existing instance. This function will as
195  * a side effect initialize the display system; see gtk_init().
196  *
197  * For the behavior if this application is running in another process,
198  * see g_application_new().
199  *
200  * Returns: (transfer full): A newly-referenced #GtkApplication
201  *
202  * Since: 3.0
203  */
204 GtkApplication*
205 gtk_application_new (gint          *argc,
206                      gchar       ***argv,
207                      const gchar   *appid)
208 {
209   GtkApplication *app;
210   gint argc_for_app;
211   gchar **argv_for_app;
212   GVariant *platform_data;
213
214   gtk_init (argc, argv);
215
216   if (argc)
217     argc_for_app = *argc;
218   else
219     argc_for_app = 0;
220   if (argv)
221     argv_for_app = *argv;
222   else
223     argv_for_app = NULL;
224
225   app = g_object_new (GTK_TYPE_APPLICATION, "appid", appid, NULL);
226
227   platform_data = gtk_application_format_activation_data ();
228   g_application_register_with_data (G_APPLICATION (app), argc_for_app, argv_for_app,
229                                     platform_data);
230   g_variant_unref (platform_data);
231
232   return app;
233 }
234
235 static void
236 on_action_sensitive (GtkAction      *action,
237                      GParamSpec     *pspec,
238                      GtkApplication *app)
239 {
240   g_application_set_action_enabled (G_APPLICATION (app),
241                                     gtk_action_get_name (action),
242                                     gtk_action_get_sensitive (action));
243 }
244
245 /**
246  * gtk_application_set_action_group:
247  * @app: A #GtkApplication
248  * @group: A #GtkActionGroup
249  *
250  * Set @group as this application's global action group.
251  * This will ensure the operating system interface uses
252  * these actions as follows:
253  *
254  * <itemizedlist>
255  *   <listitem>In GNOME 2 this exposes the actions for scripting.</listitem>
256  *   <listitem>In GNOME 3, this function populates the application menu.</listitem>
257  *   <listitem>In Windows prior to version 7, this function does nothing.</listitem>
258  *   <listitem>In Windows 7, this function adds "Tasks" to the Jump List.</listitem>
259  *   <listitem>In Mac OS X, this function extends the Dock menu.</listitem>
260  * </itemizedlist>
261  *
262  * It is an error to call this function more than once.
263  *
264  * Since: 3.0
265  */
266 void
267 gtk_application_set_action_group (GtkApplication *app,
268                                   GtkActionGroup *group)
269 {
270   GList *actions, *iter;
271
272   g_return_if_fail (GTK_IS_APPLICATION (app));
273   g_return_if_fail (app->priv->main_actions == NULL);
274
275   app->priv->main_actions = g_object_ref (group);
276   actions = gtk_action_group_list_actions (group);
277   for (iter = actions; iter; iter = iter->next)
278     {
279       GtkAction *action = iter->data;
280       g_application_add_action (G_APPLICATION (app),
281                                 gtk_action_get_name (action),
282                                 gtk_action_get_tooltip (action));
283       g_signal_connect (action, "notify::sensitive",
284                         G_CALLBACK (on_action_sensitive), app);
285     }
286   g_list_free (actions);
287 }
288
289 static gboolean
290 gtk_application_on_window_destroy (GtkWidget *window,
291                                    gpointer   user_data)
292 {
293   GtkApplication *app = GTK_APPLICATION (user_data);
294
295   app->priv->windows = g_slist_remove (app->priv->windows, window);
296
297   if (app->priv->windows == NULL)
298     gtk_application_quit (app);
299
300   return FALSE;
301 }
302
303 static gchar *default_title;
304
305 /**
306  * gtk_application_add_window:
307  * @app: a #GtkApplication
308  * @window: a toplevel window to add to @app
309  *
310  * Adds a window to the #GtkApplication.
311  *
312  * If the user closes all of the windows added to @app, the default
313  * behaviour is to call gtk_application_quit().
314  *
315  * If your application uses only a single toplevel window, you can
316  * use gtk_application_get_window().
317  *
318  * Since: 3.0
319  */
320 void
321 gtk_application_add_window (GtkApplication *app,
322                             GtkWindow      *window)
323 {
324   app->priv->windows = g_slist_prepend (app->priv->windows, window);
325
326   if (gtk_window_get_title (window) == NULL && default_title != NULL)
327     gtk_window_set_title (window, default_title);
328
329   g_signal_connect (window, "destroy",
330                     G_CALLBACK (gtk_application_on_window_destroy), app);
331 }
332
333 /**
334  * gtk_application_get_window:
335  * @app: a #GtkApplication
336  *
337  * A simple #GtkApplication has a "default window". This window should
338  * act as the primary user interaction point with your application.
339  * The window returned by this function is of type #GTK_WINDOW_TYPE_TOPLEVEL
340  * and its properties such as "title" and "icon-name" will be initialized
341  * as appropriate for the platform.
342  *
343  * If the user closes this window, and your application hasn't created
344  * any other windows, the default action will be to call gtk_application_quit().
345  *
346  * If your application has more than one toplevel window (e.g. an
347  * single-document-interface application with multiple open documents),
348  * or if you are constructing your toplevel windows yourself (e.g. using
349  * #GtkBuilder), use gtk_application_add_window() instead.
350  *
351  * Returns: (transfer none): The default #GtkWindow for this application
352  *
353  * Since: 3.0
354  */
355 GtkWindow *
356 gtk_application_get_window (GtkApplication *app)
357 {
358   if (app->priv->default_window != NULL)
359     return app->priv->default_window;
360
361   app->priv->default_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
362   g_object_ref_sink (app->priv->default_window);
363
364   gtk_application_add_window (app, app->priv->default_window);
365
366   return app->priv->default_window;
367 }
368
369 /**
370  * gtk_application_run:
371  * @app: a #GtkApplication
372  *
373  * Runs the main loop; see g_application_run().
374  * The default implementation for #GtkApplication uses gtk_main().
375  *
376  * Since: 3.0
377  */
378 void
379 gtk_application_run (GtkApplication *app)
380 {
381   g_application_run (G_APPLICATION (app));
382 }
383
384 /**
385  * gtk_application_quit:
386  * @app: a #GtkApplication
387  *
388  * Request the application exit.
389  * By default, this method will exit the main loop; see gtk_main_quit().
390  *
391  * Since: 3.0
392  */
393 void
394 gtk_application_quit (GtkApplication *app)
395 {
396   g_application_quit (G_APPLICATION (app), gtk_get_current_event_time ());
397 }
398
399 static void
400 gtk_application_get_property (GObject    *object,
401                               guint       prop_id,
402                               GValue     *value,
403                               GParamSpec *pspec)
404 {
405   GtkApplication *app = GTK_APPLICATION (object);
406
407   switch (prop_id)
408     {
409       case PROP_WINDOW:
410         g_value_set_object (value, gtk_application_get_window (app));
411         break;
412       default:
413         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
414     }
415 }
416
417 static void
418 gtk_application_set_property (GObject      *object,
419                               guint         prop_id,
420                               const GValue *value,
421                               GParamSpec   *pspec)
422 {
423   GtkApplication *app = GTK_APPLICATION (object);
424
425   g_assert (app != NULL);
426
427   switch (prop_id)
428     {
429       default:
430         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
431     }
432 }
433
434 static void
435 setup_default_window_decorations (void)
436 {
437   const gchar *pid;
438   const gchar *filename;
439   GKeyFile *keyfile;
440   gchar *title;
441   gchar *icon_name;
442
443   pid = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID");
444   filename = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
445
446   keyfile = g_key_file_new ();
447
448   if (pid != NULL && filename != NULL && atoi (pid) == getpid () &&
449       g_key_file_load_from_file (keyfile, filename, 0, NULL))
450     {
451       title = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
452       icon_name = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
453
454       g_print ("default title: %s\n", title);
455       g_print ("default icon: %s\n", icon_name);
456
457       if (default_title == NULL)
458         default_title = title;
459
460       if (gtk_window_get_default_icon_name () == NULL)
461         gtk_window_set_default_icon_name (icon_name);
462
463       g_free (icon_name);
464     }
465
466   g_key_file_free (keyfile);
467 }
468
469 static void
470 gtk_application_init (GtkApplication *application)
471 {
472   application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application, GTK_TYPE_APPLICATION, GtkApplicationPrivate);
473
474   setup_default_window_decorations ();
475 }
476
477
478 static GObject*
479 gtk_application_constructor (GType                  type,
480                              guint                  n_construct_properties,
481                              GObjectConstructParam *construct_params)
482 {
483   GObject *object;
484
485   /* Last ditch effort here */
486   gtk_init (0, NULL);
487
488   object = (* G_OBJECT_CLASS (gtk_application_parent_class)->constructor) (type,
489                                                                            n_construct_properties,
490                                                                            construct_params);
491
492   return object;
493 }
494
495 static void
496 gtk_application_class_init (GtkApplicationClass *klass)
497 {
498   GObjectClass *gobject_class;
499   GApplicationClass *application_class;
500
501   gobject_class = G_OBJECT_CLASS (klass);
502   application_class = G_APPLICATION_CLASS (klass);
503
504   gobject_class->constructor = gtk_application_constructor;
505   gobject_class->get_property = gtk_application_get_property;
506   gobject_class->set_property = gtk_application_set_property;
507
508   application_class->run = gtk_application_default_run;
509   application_class->quit = gtk_application_default_quit;
510   application_class->action = gtk_application_default_action;
511   application_class->prepare_activation = gtk_application_default_prepare_activation;
512
513   klass->activated = gtk_application_default_activated;
514
515   /**
516    * GtkApplication::activated:
517    * @arguments: A #GVariant with the signature "aay"
518    *
519    * This signal is emitted when a non-primary process for a given
520    * application is invoked while your application is running; for
521    * example, when a file browser launches your program to open a
522    * file.  The raw operating system arguments are passed in the
523    * variant @arguments.
524    */
525
526   gtk_application_signals[ACTIVATED] =
527     g_signal_new (g_intern_static_string ("activated"),
528                   G_OBJECT_CLASS_TYPE (klass),
529                   G_SIGNAL_RUN_LAST,
530                   G_STRUCT_OFFSET (GtkApplicationClass, activated),
531                   NULL, NULL,
532                   g_cclosure_marshal_VOID__BOXED,
533                   G_TYPE_NONE, 1,
534                   G_TYPE_VARIANT);
535
536   g_type_class_add_private (gobject_class, sizeof (GtkApplicationPrivate));
537 }
538
539 #define __GTK_APPLICATION_C__
540 #include "gtkaliasdef.c"