]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplication.c
docs: Document the GtkApplication::quit signal behavior
[~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 "gtkmarshalers.h"
38 #include "gtkintl.h"
39 #include "gtkprivate.h"
40
41 #include "gtkalias.h"
42
43 #include <gdk/gdk.h>
44 #ifdef GDK_WINDOWING_X11
45 #include <gdk/x11/gdkx.h>
46 #endif
47
48 /**
49  * SECTION:gtkapplication
50  * @title: GtkApplication
51  * @short_description: Application class
52  *
53  * #GtkApplication is a class that handles many important aspects
54  * of a GTK+ application in a convenient fashion, without enforcing
55  * a one-size-fits-all application model.
56  *
57  * Currently, GtkApplication handles application uniqueness, provides
58  * some basic scriptability by exporting 'actions', implements some
59  * standard actions itself (such as 'Quit') and provides a main window
60  * whose life-cycle is automatically tied to the life-cycle of your
61  * application.
62  *
63  * <example id="gtkapplication"><title>A simple application</title>
64  * <programlisting>
65  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gtk/tests/gtk-example-application.c">
66  *  <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
67  * </xi:include>
68  * </programlisting>
69  * </example>
70  */
71 enum
72 {
73   PROP_0,
74
75   PROP_WINDOW
76 };
77
78 enum
79 {
80   ACTIVATED,
81   QUIT,
82   ACTION,
83
84   LAST_SIGNAL
85 };
86
87 static guint gtk_application_signals[LAST_SIGNAL] = { 0 };
88
89 struct _GtkApplicationPrivate
90 {
91   GtkActionGroup *main_actions;
92
93   GtkWindow *default_window;
94   GSList *windows;
95 };
96
97 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
98
99 static void
100 process_timestamp_from_platform_data (GVariant *platform_data)
101 {
102   /* TODO - extract timestamp from here, update GDK time */
103 }
104
105 static gboolean
106 gtk_application_default_quit (GtkApplication *application)
107 {
108   gtk_main_quit ();
109   return TRUE;
110 }
111
112 static gboolean
113 gtk_application_default_quit_with_data (GApplication *application,
114                                         GVariant     *platform_data)
115 {
116   gboolean result;
117
118   process_timestamp_from_platform_data (platform_data);
119   
120   g_signal_emit (application, gtk_application_signals[QUIT], 0, &result);
121
122   return result;
123 }
124
125 static void
126 gtk_application_default_run (GApplication *application)
127 {
128   gtk_main ();
129 }
130
131 static void
132 gtk_application_default_prepare_activation (GApplication *application,
133                                             GVariant     *arguments,
134                                             GVariant     *platform_data)
135 {
136   GVariantIter iter;
137   const gchar *key;
138   GVariant *value;
139
140   g_variant_iter_init (&iter, platform_data);
141   while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
142     {
143       if (strcmp (key, "startup-notification-id") == 0 &&
144           g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
145         gdk_notify_startup_complete_with_id (g_variant_get_string (value, NULL));
146       g_variant_unref (value);
147     }
148   
149   g_signal_emit (G_OBJECT (application), gtk_application_signals[ACTIVATED], 0, arguments);
150 }
151
152 static void
153 gtk_application_default_activated (GtkApplication *application,
154                                    GVariant       *arguments)
155 {
156   GtkApplicationPrivate *priv = application->priv;
157
158   /* TODO: should we raise the last focused window instead ? */
159   if (priv->default_window != NULL)
160     gtk_window_present (priv->default_window);
161 }
162
163 static void
164 gtk_application_default_action (GtkApplication *application,
165                                 const gchar    *action_name)
166 {
167   GtkApplicationPrivate *priv = application->priv;
168   GtkAction *action;
169
170   action = gtk_action_group_get_action (priv->main_actions, action_name);
171   if (action)
172     gtk_action_activate (action);
173 }
174
175 static GtkWindow *
176 gtk_application_default_create_window (GtkApplication *application)
177 {
178   return GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
179 }
180
181 static void
182 gtk_application_default_action_with_data (GApplication *application,
183                                           const gchar  *action_name,
184                                           GVariant     *platform_data)
185 {
186   process_timestamp_from_platform_data (platform_data);
187
188   g_signal_emit (application, gtk_application_signals[ACTION], g_quark_from_string (action_name));
189 }
190
191 static GVariant *
192 gtk_application_format_activation_data (void)
193 {
194   const gchar *startup_id = NULL;
195   GdkDisplay *display = gdk_display_get_default ();
196   GVariantBuilder builder;
197
198   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
199
200   /* try and get the startup notification id from GDK, the environment
201    * or, if everything else failed, fake one.
202    */
203 #ifdef GDK_WINDOWING_X11
204   startup_id = gdk_x11_display_get_startup_notification_id (display);
205 #endif /* GDK_WINDOWING_X11 */
206
207   if (startup_id)
208     g_variant_builder_add (&builder, "{sv}", "startup-notification-id",
209                            g_variant_new ("s", startup_id));
210   return g_variant_builder_end (&builder);
211 }
212
213 /**
214  * gtk_application_new:
215  * @appid: System-dependent application identifier
216  * @argc: (allow-none) (inout): System argument count
217  * @argv: (allow-none) (inout): System argument vector
218  *
219  * Create a new #GtkApplication, or if one has already been initialized
220  * in this process, return the existing instance. This function will as
221  * a side effect initialize the display system; see gtk_init().
222  *
223  * For the behavior if this application is running in another process,
224  * see g_application_new().
225  *
226  * Returns: (transfer full): A newly-referenced #GtkApplication
227  *
228  * Since: 3.0
229  */
230 GtkApplication*
231 gtk_application_new (const gchar   *appid,
232                      gint          *argc,
233                      gchar       ***argv)
234 {
235   GtkApplication *app;
236   gint argc_for_app;
237   const gchar **argv_for_app;
238   GVariant *argv_variant;
239   GError *error = NULL;
240
241   gtk_init (argc, argv);
242
243   if (argc)
244     argc_for_app = *argc;
245   else
246     argc_for_app = 0;
247
248   if (argv)
249     argv_for_app = (const gchar **) *argv;
250   else
251     argv_for_app = NULL;
252
253   argv_variant = g_variant_new_bytestring_array (argv_for_app, argc_for_app);
254
255   app = g_initable_new (GTK_TYPE_APPLICATION, 
256                         NULL,
257                         &error,
258                         "application-id", appid, 
259                         "argv", argv_variant, 
260                         NULL);
261   if (app == NULL)
262     {
263       g_error ("%s", error->message);
264       g_clear_error (&error);
265       return NULL;
266     }
267
268   return app;
269 }
270
271 static void
272 on_action_sensitive (GtkAction      *action,
273                      GParamSpec     *pspec,
274                      GtkApplication *app)
275 {
276   g_application_set_action_enabled (G_APPLICATION (app),
277                                     gtk_action_get_name (action),
278                                     gtk_action_get_sensitive (action));
279 }
280
281 /**
282  * gtk_application_set_action_group:
283  * @app: A #GtkApplication
284  * @group: A #GtkActionGroup
285  *
286  * Set @group as this application's global action group.
287  * This will ensure the operating system interface uses
288  * these actions as follows:
289  *
290  * <itemizedlist>
291  *   <listitem>In GNOME 2 this exposes the actions for scripting.</listitem>
292  *   <listitem>In GNOME 3, this function populates the application menu.</listitem>
293  *   <listitem>In Windows prior to version 7, this function does nothing.</listitem>
294  *   <listitem>In Windows 7, this function adds "Tasks" to the Jump List.</listitem>
295  *   <listitem>In Mac OS X, this function extends the Dock menu.</listitem>
296  * </itemizedlist>
297  *
298  * It is an error to call this function more than once.
299  *
300  * Since: 3.0
301  */
302 void
303 gtk_application_set_action_group (GtkApplication *app,
304                                   GtkActionGroup *group)
305 {
306   GList *actions, *iter;
307
308   g_return_if_fail (GTK_IS_APPLICATION (app));
309   g_return_if_fail (GTK_IS_ACTION_GROUP (group));
310   g_return_if_fail (app->priv->main_actions == NULL);
311
312   app->priv->main_actions = g_object_ref (group);
313   actions = gtk_action_group_list_actions (group);
314   for (iter = actions; iter; iter = iter->next)
315     {
316       GtkAction *action = iter->data;
317       g_application_add_action (G_APPLICATION (app),
318                                 gtk_action_get_name (action),
319                                 gtk_action_get_tooltip (action));
320       g_signal_connect (action, "notify::sensitive",
321                         G_CALLBACK (on_action_sensitive), app);
322     }
323   g_list_free (actions);
324 }
325
326 static gboolean
327 gtk_application_on_window_destroy (GtkWidget *window,
328                                    gpointer   user_data)
329 {
330   GtkApplication *app = GTK_APPLICATION (user_data);
331
332   app->priv->windows = g_slist_remove (app->priv->windows, window);
333
334   if (app->priv->windows == NULL)
335     gtk_application_quit (app);
336
337   return FALSE;
338 }
339
340 static gchar *default_title;
341
342 /**
343  * gtk_application_add_window:
344  * @app: a #GtkApplication
345  * @window: a toplevel window to add to @app
346  *
347  * Adds a window to the #GtkApplication.
348  *
349  * If all the windows managed by #GtkApplication are closed, the
350  * #GtkApplication will call gtk_application_quit(), and quit
351  * the application.
352  *
353  * If your application uses only a single toplevel window, you can
354  * use gtk_application_get_window(). If you are using a sub-class
355  * of #GtkApplication you should call gtk_application_create_window()
356  * to let the #GtkApplication instance create a #GtkWindow and add
357  * it to the list of toplevels of the application. You should call
358  * this function only to add #GtkWindow<!-- -->s that you created
359  * directly using gtk_window_new().
360  *
361  * Since: 3.0
362  */
363 void
364 gtk_application_add_window (GtkApplication *app,
365                             GtkWindow      *window)
366 {
367   GtkApplicationPrivate *priv;
368
369   g_return_if_fail (GTK_IS_APPLICATION (app));
370   g_return_if_fail (GTK_IS_WINDOW (window));
371
372   priv = app->priv;
373
374   if (g_slist_find (priv->windows, window) != NULL)
375     return;
376
377   priv->windows = g_slist_prepend (priv->windows, window);
378
379   if (priv->default_window == NULL)
380     priv->default_window = window;
381
382   if (gtk_window_get_title (window) == NULL && default_title != NULL)
383     gtk_window_set_title (window, default_title);
384
385   g_signal_connect (window, "destroy",
386                     G_CALLBACK (gtk_application_on_window_destroy),
387                     app);
388 }
389
390 /**
391  * gtk_application_get_window:
392  * @app: a #GtkApplication
393  *
394  * A simple #GtkApplication has a "default window". This window should
395  * act as the primary user interaction point with your application.
396  * The window returned by this function is of type #GTK_WINDOW_TYPE_TOPLEVEL
397  * and its properties such as "title" and "icon-name" will be initialized
398  * as appropriate for the platform.
399  *
400  * If the user closes this window, and your application hasn't created
401  * any other windows, the default action will be to call gtk_application_quit().
402  *
403  * If your application has more than one toplevel window (e.g. an
404  * single-document-interface application with multiple open documents),
405  * or if you are constructing your toplevel windows yourself (e.g. using
406  * #GtkBuilder), use gtk_application_create_window() or
407  * gtk_application_add_window() instead.
408  *
409  * Returns: (transfer none): The default #GtkWindow for this application
410  *
411  * Since: 3.0
412  */
413 GtkWindow *
414 gtk_application_get_window (GtkApplication *app)
415 {
416   GtkApplicationPrivate *priv;
417
418   g_return_val_if_fail (GTK_IS_APPLICATION (app), NULL);
419
420   priv = app->priv;
421
422   if (priv->default_window != NULL)
423     return priv->default_window;
424
425   return gtk_application_create_window (app);
426 }
427
428 /**
429  * gtk_application_create_window:
430  * @app: a #GtkApplication
431  *
432  * Creates a new #GtkWindow for the application.
433  *
434  * This function calls the #GtkApplication::create_window() virtual function,
435  * which can be overridden by sub-classes, for instance to use #GtkBuilder to
436  * create the user interface. After creating a new #GtkWindow instance, it will
437  * be added to the list of toplevels associated to the application.
438  *
439  * Return value: (transfer none): the newly created application #GtkWindow
440  *
441  * Since: 3.0
442  */
443 GtkWindow *
444 gtk_application_create_window (GtkApplication *app)
445 {
446   GtkWindow *window;
447
448   g_return_val_if_fail (GTK_IS_APPLICATION (app), NULL);
449
450   window = GTK_APPLICATION_GET_CLASS (app)->create_window (app);
451   gtk_application_add_window (app, window);
452
453   return window;
454 }
455
456 /**
457  * gtk_application_run:
458  * @app: a #GtkApplication
459  *
460  * Runs the main loop; see g_application_run().
461  * The default implementation for #GtkApplication uses gtk_main().
462  *
463  * Since: 3.0
464  */
465 void
466 gtk_application_run (GtkApplication *app)
467 {
468   g_application_run (G_APPLICATION (app));
469 }
470
471 /**
472  * gtk_application_quit:
473  * @app: a #GtkApplication
474  *
475  * Request the application exit.  This function invokes
476  * g_application_quit_with_data(), which normally will
477  * in turn cause @app to emit #GtkApplication::quit.
478  *
479  * To control an application's quit behavior (for example, to ask for
480  * files to be saved), connect to the #GtkApplication::quit signal
481  * handler.
482  *
483  * Since: 3.0
484  */
485 void
486 gtk_application_quit (GtkApplication *app)
487 {
488   GVariantBuilder builder;
489   GVariant *platform_data;
490
491   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
492   g_variant_builder_add (&builder, "{sv}",
493                          "timestamp",
494                          g_variant_new ("u", gtk_get_current_event_time ()));
495   platform_data = g_variant_builder_end (&builder);
496
497   g_application_quit_with_data (G_APPLICATION (app), platform_data);
498
499   g_variant_unref (platform_data);
500 }
501
502 static void
503 gtk_application_get_property (GObject    *object,
504                               guint       prop_id,
505                               GValue     *value,
506                               GParamSpec *pspec)
507 {
508   GtkApplication *app = GTK_APPLICATION (object);
509
510   switch (prop_id)
511     {
512       case PROP_WINDOW:
513         g_value_set_object (value, gtk_application_get_window (app));
514         break;
515       default:
516         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
517     }
518 }
519
520 static void
521 gtk_application_set_property (GObject      *object,
522                               guint         prop_id,
523                               const GValue *value,
524                               GParamSpec   *pspec)
525 {
526   GtkApplication *app = GTK_APPLICATION (object);
527
528   g_assert (app != NULL);
529
530   switch (prop_id)
531     {
532       default:
533         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
534     }
535 }
536
537 static void
538 setup_default_window_decorations (void)
539 {
540   const gchar *pid;
541   const gchar *filename;
542   GKeyFile *keyfile;
543   gchar *title;
544   gchar *icon_name;
545
546   pid = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID");
547   filename = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
548
549   keyfile = g_key_file_new ();
550
551   if (pid != NULL && filename != NULL && atoi (pid) == getpid () &&
552       g_key_file_load_from_file (keyfile, filename, 0, NULL))
553     {
554       title = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
555       icon_name = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
556
557       if (default_title == NULL)
558         default_title = title;
559
560       if (gtk_window_get_default_icon_name () == NULL)
561         gtk_window_set_default_icon_name (icon_name);
562
563       g_free (icon_name);
564     }
565
566   g_key_file_free (keyfile);
567 }
568
569 static void
570 gtk_application_init (GtkApplication *application)
571 {
572   application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application, GTK_TYPE_APPLICATION, GtkApplicationPrivate);
573
574   g_object_set (application, "platform-data", gtk_application_format_activation_data (), NULL);
575
576   setup_default_window_decorations ();
577 }
578
579
580 static GObject*
581 gtk_application_constructor (GType                  type,
582                              guint                  n_construct_properties,
583                              GObjectConstructParam *construct_params)
584 {
585   GObject *object;
586
587   /* Last ditch effort here */
588   gtk_init (0, NULL);
589
590   object = (* G_OBJECT_CLASS (gtk_application_parent_class)->constructor) (type,
591                                                                            n_construct_properties,
592                                                                            construct_params);
593
594   return object;
595 }
596
597 static void
598 gtk_application_class_init (GtkApplicationClass *klass)
599 {
600   GObjectClass *gobject_class;
601   GApplicationClass *application_class;
602
603   gobject_class = G_OBJECT_CLASS (klass);
604   application_class = G_APPLICATION_CLASS (klass);
605
606   gobject_class->constructor = gtk_application_constructor;
607   gobject_class->get_property = gtk_application_get_property;
608   gobject_class->set_property = gtk_application_set_property;
609
610   application_class->run = gtk_application_default_run;
611   application_class->quit_with_data = gtk_application_default_quit_with_data;
612   application_class->action_with_data = gtk_application_default_action_with_data;
613   application_class->prepare_activation = gtk_application_default_prepare_activation;
614
615   klass->quit = gtk_application_default_quit;
616   klass->action = gtk_application_default_action;
617   klass->create_window = gtk_application_default_create_window;
618
619   klass->activated = gtk_application_default_activated;
620
621   /**
622    * GtkApplication::activated:
623    * @arguments: A #GVariant with the signature "aay"
624    *
625    * This signal is emitted when a non-primary process for a given
626    * application is invoked while your application is running; for
627    * example, when a file browser launches your program to open a
628    * file.  The raw operating system arguments are passed in the
629    * variant @arguments.
630    *
631    * Since: 3.0
632    */
633   gtk_application_signals[ACTIVATED] =
634     g_signal_new (g_intern_static_string ("activated"),
635                   G_OBJECT_CLASS_TYPE (klass),
636                   G_SIGNAL_RUN_LAST,
637                   G_STRUCT_OFFSET (GtkApplicationClass, activated),
638                   NULL, NULL,
639                   g_cclosure_marshal_VOID__VARIANT,
640                   G_TYPE_NONE, 1,
641                   G_TYPE_VARIANT);
642
643   /**
644    * GtkApplication::quit:
645    * @application: the object on which the signal is emitted
646    *
647    * This signal is emitted when a quit is initiated.  See also
648    * the #GApplication::quit-with-data signal which may in
649    * turn trigger this signal.
650    *
651    * The default handler for this signal exits the mainloop of the
652    * application. It is possible to override the default handler
653    * by simply returning %TRUE from a callback, e.g.:
654    *
655    * |[
656    * static gboolean
657    * my_application_quit (GtkApplication *application)
658    * {
659    *   /&ast; if some_condition is TRUE, do not quit &ast;/
660    *   if (some_condition)
661    *     return TRUE;
662    *
663    *   /&ast; this will cause the application to quit &ast;
664    *   return FALSE;
665    * }
666    *
667    *   g_signal_connect (application, "quit",
668    *                     G_CALLBACK (my_application_quit),
669    *                     NULL);
670    * ]|
671    *
672    * Returns: %TRUE if the signal has been handled, %FALSE to continue
673    *   signal emission
674    *
675    * Since: 3.0
676    */
677   gtk_application_signals[QUIT] =
678     g_signal_new (g_intern_static_string ("quit"),
679                   G_OBJECT_CLASS_TYPE (klass),
680                   G_SIGNAL_RUN_LAST,
681                   G_STRUCT_OFFSET (GtkApplicationClass, quit),
682                   g_signal_accumulator_true_handled, NULL,
683                   _gtk_marshal_BOOLEAN__VOID,
684                   G_TYPE_BOOLEAN, 0);
685
686   /**
687    * GtkApplication::action:
688    * @application: the object on which the signal is emitted
689    * @name: The name of the activated action
690    *
691    * This signal is emitted when an action is activated. The action name
692    * is passed as the first argument, but also as signal detail, so it
693    * is possible to connect to this signal for individual actions.
694    *
695    * See also the #GApplication::action-with-data signal which may in
696    * turn trigger this signal.
697    *
698    * The signal is never emitted for disabled actions.
699    *
700    * Since: 3.0
701    */
702   gtk_application_signals[ACTION] =
703     g_signal_new (g_intern_static_string ("action"),
704                   G_OBJECT_CLASS_TYPE (klass),
705                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED,
706                   G_STRUCT_OFFSET (GtkApplicationClass, action),
707                   NULL, NULL,
708                   g_cclosure_marshal_VOID__STRING,
709                   G_TYPE_NONE, 1,
710                   G_TYPE_STRING);
711                  
712   g_type_class_add_private (gobject_class, sizeof (GtkApplicationPrivate));
713 }
714
715 #define __GTK_APPLICATION_C__
716 #include "gtkaliasdef.c"