]> Pileus Git - ~andy/gtk/blob - gtk/gtkaboutdialog.c
about: Add :license-type property
[~andy/gtk] / gtk / gtkaboutdialog.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2001 CodeFactory AB
3  * Copyright (C) 2001, 2002 Anders Carlsson
4  * Copyright (C) 2003, 2004 Matthias Clasen <mclasen@redhat.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Author: Anders Carlsson <andersca@gnome.org>
24  *
25  * Modified by the GTK+ Team and others 1997-2004.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29  */
30
31 #include "config.h"
32
33 #include <string.h>
34
35 #include <gdk/gdkkeysyms.h>
36
37 #include "gtkaboutdialog.h"
38 #include "gtkbutton.h"
39 #include "gtkbbox.h"
40 #include "gtkdialog.h"
41 #include "gtkhbox.h"
42 #include "gtkimage.h"
43 #include "gtklabel.h"
44 #include "gtklinkbutton.h"
45 #include "gtkmarshalers.h"
46 #include "gtknotebook.h"
47 #include "gtkscrolledwindow.h"
48 #include "gtkstock.h"
49 #include "gtktextview.h"
50 #include "gtkvbox.h"
51 #include "gtkiconfactory.h"
52 #include "gtkshow.h"
53 #include "gtkmain.h"
54 #include "gtkmessagedialog.h"
55 #include "gtkprivate.h"
56 #include "gtkintl.h"
57
58
59 /**
60  * SECTION:gtkaboutdialog
61  * @Short_description: Display information about an application
62  * @Title: GtkAboutDialog
63  * @See_also:#GTK_STOCK_ABOUT
64  *
65  * The #GtkAboutDialog offers a simple way to display information about
66  * a program like its logo, name, copyright, website and license. It is
67  * also possible to give credits to the authors, documenters, translators
68  * and artists who have worked on the program. An about dialog is typically
69  * opened when the user selects the <literal>About</literal> option from
70  * the <literal>Help</literal> menu. All parts of the dialog are optional.
71  *
72  * About dialog often contain links and email addresses. #GtkAboutDialog
73  * supports this by offering global hooks, which are called when the user
74  * clicks on a link or email address, see gtk_about_dialog_set_email_hook()
75  * and gtk_about_dialog_set_url_hook(). Email addresses in the
76  * authors, documenters and artists properties are recognized by looking for
77  * <literal>&lt;user@<!-- -->host&gt;</literal>, URLs are
78  * recognized by looking for <literal>http://url</literal>, with
79  * <literal>url</literal> extending to the next space, tab or line break.
80  *
81  * <para id="gtk-about-dialog-hook-setup">
82  * Since 2.18 #GtkAboutDialog provides default website and email hooks that
83  * use gtk_show_uri().
84  * </para>
85  *
86  * If you want provide your own hooks overriding the default ones, it is
87  * important to do so before setting the website and email URL properties,
88  * like this:
89  * <informalexample><programlisting>
90  * gtk_about_dialog_set_url_hook (GTK_ABOUT_DIALOG (dialog), launch_url, NULL, NULL);
91  * gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog), app_url);
92  * </programlisting></informalexample>
93  * To disable the default hooks, you can pass %NULL as the hook func. Then,
94  * the #GtkAboutDialog widget will not display the website or the
95  * email addresses as clickable.
96  *
97  * To make constructing a #GtkAboutDialog as convenient as possible, you can
98  * use the function gtk_show_about_dialog() which constructs and shows a dialog
99  * and keeps it around so that it can be shown again.
100  *
101  * Note that GTK+ sets a default title of <literal>_("About &percnt;s")</literal>
102  * on the dialog window (where &percnt;s is replaced by the name of the
103  * application, but in order to ensure proper translation of the title,
104  * applications should set the title property explicitly when constructing
105  * a #GtkAboutDialog, as shown in the following example:
106  * <informalexample><programlisting>
107  * gtk_show_about_dialog (NULL,
108  *                        "program-name", "ExampleCode",
109  *                        "logo", example_logo,
110  *                        "title" _("About ExampleCode"),
111  *                        NULL);
112  * </programlisting></informalexample>
113  * Note that prior to GTK+ 2.12, the #GtkAboutDialog:program-name property
114  * was called "name". This was changed to avoid the conflict with the
115  * #GtkWidget:name property.
116  */
117
118 static GdkColor default_link_color = { 0, 0, 0, 0xeeee };
119 static GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
120
121 /* Translators: this is the license preamble; the string at the end
122  * contains the URL of the license.
123  */
124 static const gchar *gtk_license_preamble = N_("This program comes with ABSOLUTELY NO WARRANTY; for details, visit %s");
125
126 /* URLs for each GtkLicense type; keep in the same order as the enumeration */
127 static const gchar *gtk_license_urls[] = {
128   NULL,
129   NULL,
130
131   "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
132   "http://www.gnu.org/licenses/gpl.html",
133
134   "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html",
135   "http://www.gnu.org/licenses/lgpl.html",
136
137   "http://opensource.org/licenses/bsd-license.php",
138   "http://opensource.org/licenses/mit-license.php",
139
140   "http://opensource.org/licenses/artistic-license-2.0.php"
141 };
142
143 struct _GtkAboutDialogPrivate 
144 {
145   gchar *name;
146   gchar *version;
147   gchar *copyright;
148   gchar *comments;
149   gchar *website_url;
150   gchar *website_text;
151   gchar *translator_credits;
152   gchar *license;
153
154   gchar **authors;
155   gchar **documenters;
156   gchar **artists;
157
158   GtkWidget *logo_image;
159   GtkWidget *name_label;
160   GtkWidget *comments_label;
161   GtkWidget *copyright_label;
162   GtkWidget *website_label;
163
164   GtkWidget *credits_button;
165   GtkWidget *credits_dialog;
166   GtkWidget *license_button;
167   GtkWidget *license_dialog;
168
169   GdkCursor *hand_cursor;
170   GdkCursor *regular_cursor;
171
172   GSList *visited_links;
173
174   GtkLicense license_type;
175
176   guint hovering_over_link : 1;
177   guint wrap_license : 1;
178 };
179
180 #define GTK_ABOUT_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ABOUT_DIALOG, GtkAboutDialogPrivate))
181
182
183 enum
184 {
185   PROP_0,
186   PROP_NAME,
187   PROP_VERSION,
188   PROP_COPYRIGHT,
189   PROP_COMMENTS,
190   PROP_WEBSITE,
191   PROP_WEBSITE_LABEL,
192   PROP_LICENSE,
193   PROP_AUTHORS,
194   PROP_DOCUMENTERS,
195   PROP_TRANSLATOR_CREDITS,
196   PROP_ARTISTS,
197   PROP_LOGO,
198   PROP_LOGO_ICON_NAME,
199   PROP_WRAP_LICENSE,
200   PROP_LICENSE_TYPE
201 };
202
203 static void                 gtk_about_dialog_finalize       (GObject            *object);
204 static void                 gtk_about_dialog_get_property   (GObject            *object,
205                                                              guint               prop_id,
206                                                              GValue             *value,
207                                                              GParamSpec         *pspec);
208 static void                 gtk_about_dialog_set_property   (GObject            *object,
209                                                              guint               prop_id,
210                                                              const GValue       *value,
211                                                              GParamSpec         *pspec);
212 static void                 gtk_about_dialog_show           (GtkWidget          *widge);
213 static void                 update_name_version             (GtkAboutDialog     *about);
214 static GtkIconSet *         icon_set_new_from_pixbufs       (GList              *pixbufs);
215 static void                 follow_if_link                  (GtkAboutDialog     *about,
216                                                              GtkTextView        *text_view,
217                                                              GtkTextIter        *iter);
218 static void                 set_cursor_if_appropriate       (GtkAboutDialog     *about,
219                                                              GtkTextView        *text_view,
220                                                              GdkDevice          *device,
221                                                              gint                x,
222                                                              gint                y);
223 static void                 display_credits_dialog          (GtkWidget          *button,
224                                                              gpointer            data);
225 static void                 display_license_dialog          (GtkWidget          *button,
226                                                              gpointer            data);
227 static void                 close_cb                        (GtkAboutDialog     *about);
228 static void                 default_url_hook                (GtkAboutDialog     *about,
229                                                              const gchar        *uri,
230                                                              gpointer            user_data);
231 static void                 default_email_hook              (GtkAboutDialog     *about,
232                                                              const gchar        *email_address,
233                                                              gpointer            user_data);
234
235 static gboolean activate_email_hook_set = FALSE;
236 static GtkAboutDialogActivateLinkFunc activate_email_hook = NULL;
237 static gpointer activate_email_hook_data = NULL;
238 static GDestroyNotify activate_email_hook_destroy = NULL;
239
240 static gboolean activate_url_hook_set = FALSE;
241 static GtkAboutDialogActivateLinkFunc activate_url_hook = NULL;
242 static gpointer activate_url_hook_data = NULL;
243 static GDestroyNotify activate_url_hook_destroy = NULL;
244
245 static void
246 default_url_hook (GtkAboutDialog *about,
247                   const gchar    *uri,
248                   gpointer        user_data)
249 {
250   GdkScreen *screen;
251   GError *error = NULL;
252
253   screen = gtk_widget_get_screen (GTK_WIDGET (about));
254
255   if (!gtk_show_uri (screen, uri, gtk_get_current_event_time (), &error))
256     {
257       GtkWidget *dialog;
258
259       dialog = gtk_message_dialog_new (GTK_WINDOW (about),
260                                        GTK_DIALOG_DESTROY_WITH_PARENT |
261                                        GTK_DIALOG_MODAL,
262                                        GTK_MESSAGE_ERROR,
263                                        GTK_BUTTONS_CLOSE,
264                                        "%s", _("Could not show link"));
265       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
266                                                 "%s", error->message);
267       g_error_free (error);
268
269       g_signal_connect (dialog, "response",
270                         G_CALLBACK (gtk_widget_destroy), NULL);
271
272       gtk_window_present (GTK_WINDOW (dialog));
273     }
274 }
275
276 static void
277 default_email_hook (GtkAboutDialog *about,
278                     const gchar    *email_address,
279                     gpointer        user_data)
280 {
281   char *escaped, *uri;
282
283   escaped = g_uri_escape_string (email_address, NULL, FALSE);
284   uri = g_strdup_printf ("mailto:%s", escaped);
285   g_free (escaped);
286
287   default_url_hook (about, uri, user_data);
288   g_free (uri);
289 }
290
291 G_DEFINE_TYPE (GtkAboutDialog, gtk_about_dialog, GTK_TYPE_DIALOG)
292
293 static void
294 gtk_about_dialog_class_init (GtkAboutDialogClass *klass)
295 {
296   GObjectClass *object_class;
297   GtkWidgetClass *widget_class;
298
299   object_class = (GObjectClass *)klass;
300   widget_class = (GtkWidgetClass *)klass;
301
302   object_class->set_property = gtk_about_dialog_set_property;
303   object_class->get_property = gtk_about_dialog_get_property;
304
305   object_class->finalize = gtk_about_dialog_finalize;
306
307   widget_class->show = gtk_about_dialog_show;
308
309   /**
310    * GtkAboutDialog:program-name:
311    *
312    * The name of the program.
313    * If this is not set, it defaults to g_get_application_name().
314    *
315    * Since: 2.12
316    */
317   g_object_class_install_property (object_class,
318                                    PROP_NAME,
319                                    g_param_spec_string ("program-name",
320                                                         P_("Program name"),
321                                                         P_("The name of the program. If this is not set, it defaults to g_get_application_name()"),
322                                                         NULL,
323                                                         GTK_PARAM_READWRITE));
324
325   /**
326    * GtkAboutDialog:version:
327    *
328    * The version of the program.
329    *
330    * Since: 2.6
331    */
332   g_object_class_install_property (object_class,
333                                    PROP_VERSION,
334                                    g_param_spec_string ("version",
335                                                         P_("Program version"),
336                                                         P_("The version of the program"),
337                                                         NULL,
338                                                         GTK_PARAM_READWRITE));
339
340   /**
341    * GtkAboutDialog:copyright:
342    *
343    * Copyright information for the program.
344    *
345    * Since: 2.6
346    */
347   g_object_class_install_property (object_class,
348                                    PROP_COPYRIGHT,
349                                    g_param_spec_string ("copyright",
350                                                         P_("Copyright string"),
351                                                         P_("Copyright information for the program"),
352                                                         NULL,
353                                                         GTK_PARAM_READWRITE));
354         
355
356   /**
357    * GtkAboutDialog:comments:
358    *
359    * Comments about the program. This string is displayed in a label
360    * in the main dialog, thus it should be a short explanation of
361    * the main purpose of the program, not a detailed list of features.
362    *
363    * Since: 2.6
364    */
365   g_object_class_install_property (object_class,
366                                    PROP_COMMENTS,
367                                    g_param_spec_string ("comments",
368                                                         P_("Comments string"),
369                                                         P_("Comments about the program"),
370                                                         NULL,
371                                                         GTK_PARAM_READWRITE));
372
373   /**
374    * GtkAboutDialog:license:
375    *
376    * The license of the program. This string is displayed in a
377    * text view in a secondary dialog, therefore it is fine to use
378    * a long multi-paragraph text. Note that the text is only wrapped
379    * in the text view if the "wrap-license" property is set to %TRUE;
380    * otherwise the text itself must contain the intended linebreaks.
381    * When setting this property to a non-%NULL value, the
382    * #GtkAboutDialog:license-type property is set to %GTK_LICENSE_CUSTOM
383    * as a side effect.
384    *
385    * Since: 2.6
386    */
387   g_object_class_install_property (object_class,
388                                    PROP_LICENSE,
389                                    g_param_spec_string ("license",
390                                                         _("License"),
391                                                         _("The license of the program"),
392                                                         NULL,
393                                                         GTK_PARAM_READWRITE));
394
395   /**
396    * GtkAboutDialog:license-type:
397    *
398    * The license of the program, as a value of the %GtkLicense enumeration.
399    *
400    * The #GtkAboutDialog will automatically fill out a standard disclaimer
401    * and link the user to the appropriate online resource for the license
402    * text.
403    *
404    * If %GTK_LICENSE_UNKNOWN is used, the link used will be the same
405    * specified in the #GtkAboutDialog:website property.
406    *
407    * If %GTK_LICENSE_CUSTOM is used, the current contents of the
408    * #GtkAboutDialog:license property are used.
409    *
410    * For any other #GtkLicense value, the contents of the
411    * #GtkAboutDialog:license property are also set by this property as
412    * a side effect.
413    *
414    * Since: 3.0
415    */
416   g_object_class_install_property (object_class,
417                                    PROP_LICENSE_TYPE,
418                                    g_param_spec_enum ("license-type",
419                                                       P_("License Type"),
420                                                       P_("The license type of the program"),
421                                                       GTK_TYPE_LICENSE,
422                                                       GTK_LICENSE_UNKNOWN,
423                                                       GTK_PARAM_READWRITE));
424
425   /**
426    * GtkAboutDialog:website:
427    *
428    * The URL for the link to the website of the program.
429    * This should be a string starting with "http://.
430    *
431    * Since: 2.6
432    */
433   g_object_class_install_property (object_class,
434                                    PROP_WEBSITE,
435                                    g_param_spec_string ("website",
436                                                         P_("Website URL"),
437                                                         P_("The URL for the link to the website of the program"),
438                                                         NULL,
439                                                         GTK_PARAM_READWRITE));
440
441   /**
442    * GtkAboutDialog:website-label:
443    *
444    * The label for the link to the website of the program. If this is not set,
445    * it defaults to the URL specified in the #GtkAboutDialog:website property.
446    *
447    * Since: 2.6
448    */
449   g_object_class_install_property (object_class,
450                                    PROP_WEBSITE_LABEL,
451                                    g_param_spec_string ("website-label",
452                                                         P_("Website label"),
453                                                         P_("The label for the link to the website of the program. If this is not set, it defaults to the URL"),
454                                                         NULL,
455                                                         GTK_PARAM_READWRITE));
456
457   /**
458    * GtkAboutDialog:authors:
459    *
460    * The authors of the program, as a %NULL-terminated array of strings.
461    * Each string may contain email addresses and URLs, which will be displayed
462    * as links, see the introduction for more details.
463    *
464    * Since: 2.6
465    */
466   g_object_class_install_property (object_class,
467                                    PROP_AUTHORS,
468                                    g_param_spec_boxed ("authors",
469                                                        P_("Authors"),
470                                                        P_("List of authors of the program"),
471                                                        G_TYPE_STRV,
472                                                        GTK_PARAM_READWRITE));
473
474   /**
475    * GtkAboutDialog:documenters:
476    *
477    * The people documenting the program, as a %NULL-terminated array of strings.
478    * Each string may contain email addresses and URLs, which will be displayed
479    * as links, see the introduction for more details.
480    *
481    * Since: 2.6
482    */
483   g_object_class_install_property (object_class,
484                                    PROP_DOCUMENTERS,
485                                    g_param_spec_boxed ("documenters",
486                                                        P_("Documenters"),
487                                                        P_("List of people documenting the program"),
488                                                        G_TYPE_STRV,
489                                                        GTK_PARAM_READWRITE));
490
491   /**
492    * GtkAboutDialog:artists:
493    *
494    * The people who contributed artwork to the program, as a %NULL-terminated
495    * array of strings. Each string may contain email addresses and URLs, which
496    * will be displayed as links, see the introduction for more details.
497    *
498    * Since: 2.6
499    */
500   g_object_class_install_property (object_class,
501                                    PROP_ARTISTS,
502                                    g_param_spec_boxed ("artists",
503                                                        P_("Artists"),
504                                                        P_("List of people who have contributed artwork to the program"),
505                                                        G_TYPE_STRV,
506                                                        GTK_PARAM_READWRITE));
507
508
509   /**
510    * GtkAboutDialog:translator-credits:
511    *
512    * Credits to the translators. This string should be marked as translatable.
513    * The string may contain email addresses and URLs, which will be displayed
514    * as links, see the introduction for more details.
515    *
516    * Since: 2.6
517    */
518   g_object_class_install_property (object_class,
519                                    PROP_TRANSLATOR_CREDITS,
520                                    g_param_spec_string ("translator-credits",
521                                                         P_("Translator credits"),
522                                                         P_("Credits to the translators. This string should be marked as translatable"),
523                                                         NULL,
524                                                         GTK_PARAM_READWRITE));
525         
526   /**
527    * GtkAboutDialog:logo:
528    *
529    * A logo for the about box. If this is not set, it defaults to
530    * gtk_window_get_default_icon_list().
531    *
532    * Since: 2.6
533    */
534   g_object_class_install_property (object_class,
535                                    PROP_LOGO,
536                                    g_param_spec_object ("logo",
537                                                         P_("Logo"),
538                                                         P_("A logo for the about box. If this is not set, it defaults to gtk_window_get_default_icon_list()"),
539                                                         GDK_TYPE_PIXBUF,
540                                                         GTK_PARAM_READWRITE));
541
542   /**
543    * GtkAboutDialog:logo-icon-name:
544    *
545    * A named icon to use as the logo for the about box. This property
546    * overrides the #GtkAboutDialog:logo property.
547    *
548    * Since: 2.6
549    */
550   g_object_class_install_property (object_class,
551                                    PROP_LOGO_ICON_NAME,
552                                    g_param_spec_string ("logo-icon-name",
553                                                         P_("Logo Icon Name"),
554                                                         P_("A named icon to use as the logo for the about box."),
555                                                         NULL,
556                                                         GTK_PARAM_READWRITE));
557   /**
558    * GtkAboutDialog:wrap-license:
559    *
560    * Whether to wrap the text in the license dialog.
561    *
562    * Since: 2.8
563    */
564   g_object_class_install_property (object_class,
565                                    PROP_WRAP_LICENSE,
566                                    g_param_spec_boolean ("wrap-license",
567                                                          P_("Wrap license"),
568                                                          P_("Whether to wrap the license text."),
569                                                          FALSE,
570                                                          GTK_PARAM_READWRITE));
571
572
573   g_type_class_add_private (object_class, sizeof (GtkAboutDialogPrivate));
574 }
575
576 static gboolean
577 website_clicked (GtkLabel       *label,
578                  const gchar    *uri,
579                  GtkAboutDialog *about)
580 {
581   GtkAboutDialogActivateLinkFunc url_hook;
582   gpointer url_hook_data;
583
584   if (activate_url_hook_set)
585     {
586       url_hook = activate_url_hook;
587       url_hook_data = activate_url_hook_data;
588     }
589   else
590     {
591       url_hook = default_url_hook;
592       url_hook_data = NULL;
593     }
594
595   url_hook (about, uri, url_hook_data);
596
597   return TRUE;
598 }
599
600 static void
601 gtk_about_dialog_init (GtkAboutDialog *about)
602 {
603   GtkDialog *dialog = GTK_DIALOG (about);
604   GtkAboutDialogPrivate *priv;
605   GtkWidget *vbox, *hbox, *button, *close_button, *image;
606   GtkWidget *content_area, *action_area;
607
608   /* Data */
609   priv = GTK_ABOUT_DIALOG_GET_PRIVATE (about);
610   about->priv = priv;
611
612   priv->name = NULL;
613   priv->version = NULL;
614   priv->copyright = NULL;
615   priv->comments = NULL;
616   priv->website_url = NULL;
617   priv->website_text = NULL;
618   priv->translator_credits = NULL;
619   priv->license = NULL;
620   priv->authors = NULL;
621   priv->documenters = NULL;
622   priv->artists = NULL;
623
624   priv->hand_cursor = gdk_cursor_new (GDK_HAND2);
625   priv->regular_cursor = gdk_cursor_new (GDK_XTERM);
626   priv->hovering_over_link = FALSE;
627   priv->wrap_license = FALSE;
628
629   priv->license_type = GTK_LICENSE_UNKNOWN;
630
631   content_area = gtk_dialog_get_content_area (dialog);
632   action_area = gtk_dialog_get_action_area (dialog);
633
634   gtk_dialog_set_has_separator (dialog, FALSE);
635   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
636   gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
637   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
638
639   /* Widgets */
640   gtk_widget_push_composite_child ();
641
642   vbox = gtk_vbox_new (FALSE, 8);
643   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
644   gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
645
646   priv->logo_image = gtk_image_new ();
647   gtk_box_pack_start (GTK_BOX (vbox), priv->logo_image, FALSE, FALSE, 0);
648
649   priv->name_label = gtk_label_new (NULL);
650   gtk_label_set_selectable (GTK_LABEL (priv->name_label), TRUE);
651   gtk_label_set_justify (GTK_LABEL (priv->name_label), GTK_JUSTIFY_CENTER);
652   gtk_box_pack_start (GTK_BOX (vbox), priv->name_label, FALSE, FALSE, 0);
653
654   priv->comments_label = gtk_label_new (NULL);
655   gtk_label_set_selectable (GTK_LABEL (priv->comments_label), TRUE);
656   gtk_label_set_justify (GTK_LABEL (priv->comments_label), GTK_JUSTIFY_CENTER);
657   gtk_label_set_line_wrap (GTK_LABEL (priv->comments_label), TRUE);
658   gtk_box_pack_start (GTK_BOX (vbox), priv->comments_label, FALSE, FALSE, 0);
659
660   priv->copyright_label = gtk_label_new (NULL);
661   gtk_label_set_selectable (GTK_LABEL (priv->copyright_label), TRUE);
662   gtk_label_set_justify (GTK_LABEL (priv->copyright_label), GTK_JUSTIFY_CENTER);
663   gtk_box_pack_start (GTK_BOX (vbox), priv->copyright_label, FALSE, FALSE, 0);
664
665   hbox = gtk_hbox_new (TRUE, 0);
666   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
667
668   priv->website_label = button = gtk_label_new ("");
669   gtk_widget_set_no_show_all (button, TRUE);
670   gtk_label_set_selectable (GTK_LABEL (button), TRUE);
671   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
672   g_signal_connect (button, "activate-link",
673                     G_CALLBACK (website_clicked), about);
674
675   gtk_widget_show (vbox);
676   gtk_widget_show (priv->logo_image);
677   gtk_widget_show (priv->name_label);
678   gtk_widget_show (hbox);
679
680   /* Add the close button */
681   close_button = gtk_dialog_add_button (GTK_DIALOG (about), GTK_STOCK_CLOSE,
682                                         GTK_RESPONSE_CANCEL);
683   gtk_dialog_set_default_response (GTK_DIALOG (about), GTK_RESPONSE_CANCEL);
684
685   /* Add the credits button */
686   button = gtk_button_new_with_mnemonic (_("C_redits"));
687   gtk_widget_set_can_default (button, TRUE);
688   image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, GTK_ICON_SIZE_BUTTON);
689   gtk_button_set_image (GTK_BUTTON (button), image);
690   gtk_widget_set_no_show_all (button, TRUE);
691   gtk_box_pack_end (GTK_BOX (action_area), 
692                     button, FALSE, TRUE, 0); 
693   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area), button, TRUE);
694   g_signal_connect (button, "clicked",
695                     G_CALLBACK (display_credits_dialog), about);
696   priv->credits_button = button;
697   priv->credits_dialog = NULL;
698
699   /* Add the license button */
700   button = gtk_button_new_from_stock (_("_License"));
701   gtk_widget_set_can_default (button, TRUE);
702   gtk_widget_set_no_show_all (button, TRUE);
703   gtk_box_pack_end (GTK_BOX (action_area), 
704                     button, FALSE, TRUE, 0); 
705   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area), button, TRUE);
706   g_signal_connect (button, "clicked",
707                     G_CALLBACK (display_license_dialog), about);
708   priv->license_button = button;
709   priv->license_dialog = NULL;
710
711   gtk_window_set_resizable (GTK_WINDOW (about), FALSE);
712
713   gtk_widget_pop_composite_child ();
714
715   gtk_widget_grab_default (close_button);
716   gtk_widget_grab_focus (close_button);
717
718   /* force defaults */
719   gtk_about_dialog_set_program_name (about, NULL);
720   gtk_about_dialog_set_logo (about, NULL);
721 }
722
723 static void
724 gtk_about_dialog_finalize (GObject *object)
725 {
726   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
727   GtkAboutDialogPrivate *priv = about->priv;
728
729   g_free (priv->name);
730   g_free (priv->version);
731   g_free (priv->copyright);
732   g_free (priv->comments);
733   g_free (priv->license);
734   g_free (priv->website_url);
735   g_free (priv->website_text);
736   g_free (priv->translator_credits);
737
738   g_strfreev (priv->authors);
739   g_strfreev (priv->documenters);
740   g_strfreev (priv->artists);
741
742   g_slist_foreach (priv->visited_links, (GFunc)g_free, NULL);
743   g_slist_free (priv->visited_links);
744
745   gdk_cursor_unref (priv->hand_cursor);
746   gdk_cursor_unref (priv->regular_cursor);
747
748   G_OBJECT_CLASS (gtk_about_dialog_parent_class)->finalize (object);
749 }
750
751 static void
752 gtk_about_dialog_set_property (GObject      *object,
753                                guint         prop_id,
754                                const GValue *value,
755                                GParamSpec   *pspec)
756 {
757   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
758   GtkAboutDialogPrivate *priv = about->priv;
759
760   switch (prop_id)
761     {
762     case PROP_NAME:
763       gtk_about_dialog_set_program_name (about, g_value_get_string (value));
764       break;
765     case PROP_VERSION:
766       gtk_about_dialog_set_version (about, g_value_get_string (value));
767       break;
768     case PROP_COMMENTS:
769       gtk_about_dialog_set_comments (about, g_value_get_string (value));
770       break;
771     case PROP_WEBSITE:
772       gtk_about_dialog_set_website (about, g_value_get_string (value));
773       break;
774     case PROP_WEBSITE_LABEL:
775       gtk_about_dialog_set_website_label (about, g_value_get_string (value));
776       break;
777     case PROP_LICENSE:
778       gtk_about_dialog_set_license (about, g_value_get_string (value));
779       break;
780     case PROP_LICENSE_TYPE:
781       gtk_about_dialog_set_license_type (about, g_value_get_enum (value));
782       break;
783     case PROP_COPYRIGHT:
784       gtk_about_dialog_set_copyright (about, g_value_get_string (value));
785       break;
786     case PROP_LOGO:
787       gtk_about_dialog_set_logo (about, g_value_get_object (value));
788       break;
789     case PROP_AUTHORS:
790       gtk_about_dialog_set_authors (about, (const gchar**)g_value_get_boxed (value));
791       break;
792     case PROP_DOCUMENTERS:
793       gtk_about_dialog_set_documenters (about, (const gchar**)g_value_get_boxed (value));
794       break;
795     case PROP_ARTISTS:
796       gtk_about_dialog_set_artists (about, (const gchar**)g_value_get_boxed (value));
797       break;
798     case PROP_TRANSLATOR_CREDITS:
799       gtk_about_dialog_set_translator_credits (about, g_value_get_string (value));
800       break;
801     case PROP_LOGO_ICON_NAME:
802       gtk_about_dialog_set_logo_icon_name (about, g_value_get_string (value));
803       break;
804     case PROP_WRAP_LICENSE:
805       priv->wrap_license = g_value_get_boolean (value);
806       break;
807     default:
808       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
809       break;
810     }
811 }
812
813 static void
814 gtk_about_dialog_get_property (GObject    *object,
815                                guint       prop_id,
816                                GValue     *value,
817                                GParamSpec *pspec)
818 {
819   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
820   GtkAboutDialogPrivate *priv = about->priv;
821         
822   switch (prop_id) 
823     {
824     case PROP_NAME:
825       g_value_set_string (value, priv->name);
826       break;
827     case PROP_VERSION:
828       g_value_set_string (value, priv->version);
829       break;
830     case PROP_COPYRIGHT:
831       g_value_set_string (value, priv->copyright);
832       break;
833     case PROP_COMMENTS:
834       g_value_set_string (value, priv->comments);
835       break;
836     case PROP_WEBSITE:
837       g_value_set_string (value, priv->website_url);
838       break;
839     case PROP_WEBSITE_LABEL:
840       g_value_set_string (value, priv->website_text);
841       break;
842     case PROP_LICENSE:
843       g_value_set_string (value, priv->license);
844       break;
845     case PROP_LICENSE_TYPE:
846       g_value_set_enum (value, priv->license_type);
847       break;
848     case PROP_TRANSLATOR_CREDITS:
849       g_value_set_string (value, priv->translator_credits);
850       break;
851     case PROP_AUTHORS:
852       g_value_set_boxed (value, priv->authors);
853       break;
854     case PROP_DOCUMENTERS:
855       g_value_set_boxed (value, priv->documenters);
856       break;
857     case PROP_ARTISTS:
858       g_value_set_boxed (value, priv->artists);
859       break;
860     case PROP_LOGO:
861       if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_PIXBUF)
862         g_value_set_object (value, gtk_image_get_pixbuf (GTK_IMAGE (priv->logo_image)));
863       else
864         g_value_set_object (value, NULL);
865       break;
866     case PROP_LOGO_ICON_NAME:
867       if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_ICON_NAME)
868         {
869           const gchar *icon_name;
870
871           gtk_image_get_icon_name (GTK_IMAGE (priv->logo_image), &icon_name, NULL);
872           g_value_set_string (value, icon_name);
873         }
874       else
875         g_value_set_string (value, NULL);
876       break;
877     case PROP_WRAP_LICENSE:
878       g_value_set_boolean (value, priv->wrap_license);
879       break;
880     default:
881       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
882       break;
883     }
884 }
885
886 static void
887 update_website (GtkAboutDialog *about)
888 {
889   GtkAboutDialogPrivate *priv = about->priv;
890
891   gtk_widget_show (priv->website_label);
892
893   if (priv->website_url && (!activate_url_hook_set || activate_url_hook != NULL))
894     {
895       gchar *markup;
896
897       if (priv->website_text)
898         {
899           gchar *escaped;
900
901           escaped = g_markup_escape_text (priv->website_text, -1);
902           markup = g_strdup_printf ("<a href=\"%s\">%s</a>",
903                                     priv->website_url, escaped);
904           g_free (escaped);
905         }
906       else
907         {
908           markup = g_strdup_printf ("<a href=\"%s\">%s</a>",
909                                     priv->website_url, priv->website_url);
910         }
911
912       gtk_label_set_markup (GTK_LABEL (priv->website_label), markup);
913       g_free (markup);
914     }
915   else
916     {
917       if (priv->website_url)
918         gtk_label_set_text (GTK_LABEL (priv->website_label), priv->website_url);
919       else if (priv->website_text)
920         gtk_label_set_text (GTK_LABEL (priv->website_label), priv->website_text);
921       else
922         gtk_widget_hide (priv->website_label);
923     }
924 }
925
926 static void
927 gtk_about_dialog_show (GtkWidget *widget)
928 {
929   update_website (GTK_ABOUT_DIALOG (widget));
930
931   GTK_WIDGET_CLASS (gtk_about_dialog_parent_class)->show (widget);
932 }
933
934 /**
935  * gtk_about_dialog_get_program_name:
936  * @about: a #GtkAboutDialog
937  *
938  * Returns the program name displayed in the about dialog.
939  *
940  * Return value: The program name. The string is owned by the about
941  *  dialog and must not be modified.
942  *
943  * Since: 2.12
944  */
945 G_CONST_RETURN gchar *
946 gtk_about_dialog_get_program_name (GtkAboutDialog *about)
947 {
948   GtkAboutDialogPrivate *priv;
949
950   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
951
952   priv = about->priv;
953
954   return priv->name;
955 }
956
957 static void
958 update_name_version (GtkAboutDialog *about)
959 {
960   GtkAboutDialogPrivate *priv;
961   gchar *title_string, *name_string;
962
963   priv = about->priv;
964
965   title_string = g_strdup_printf (_("About %s"), priv->name);
966   gtk_window_set_title (GTK_WINDOW (about), title_string);
967   g_free (title_string);
968
969   if (priv->version != NULL)
970     name_string = g_markup_printf_escaped ("<span size=\"xx-large\" weight=\"bold\">%s %s</span>",
971                                              priv->name, priv->version);
972   else
973     name_string = g_markup_printf_escaped ("<span size=\"xx-large\" weight=\"bold\">%s</span>",
974                                            priv->name);
975
976   gtk_label_set_markup (GTK_LABEL (priv->name_label), name_string);
977
978   g_free (name_string);
979 }
980
981 /**
982  * gtk_about_dialog_set_program_name:
983  * @about: a #GtkAboutDialog
984  * @name: the program name
985  *
986  * Sets the name to display in the about dialog.
987  * If this is not set, it defaults to g_get_application_name().
988  *
989  * Since: 2.12
990  */
991 void
992 gtk_about_dialog_set_program_name (GtkAboutDialog *about,
993                                    const gchar    *name)
994 {
995   GtkAboutDialogPrivate *priv;
996   gchar *tmp;
997
998   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
999
1000   priv = about->priv;
1001
1002   tmp = priv->name;
1003   priv->name = g_strdup (name ? name : g_get_application_name ());
1004   g_free (tmp);
1005
1006   update_name_version (about);
1007
1008   g_object_notify (G_OBJECT (about), "program-name");
1009 }
1010
1011
1012 /**
1013  * gtk_about_dialog_get_version:
1014  * @about: a #GtkAboutDialog
1015  *
1016  * Returns the version string.
1017  *
1018  * Return value: The version string. The string is owned by the about
1019  *  dialog and must not be modified.
1020  *
1021  * Since: 2.6
1022  */
1023 G_CONST_RETURN gchar *
1024 gtk_about_dialog_get_version (GtkAboutDialog *about)
1025 {
1026   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1027
1028   return about->priv->version;
1029 }
1030
1031 /**
1032  * gtk_about_dialog_set_version:
1033  * @about: a #GtkAboutDialog
1034  * @version: (allow-none): the version string
1035  *
1036  * Sets the version string to display in the about dialog.
1037  *
1038  * Since: 2.6
1039  */
1040 void
1041 gtk_about_dialog_set_version (GtkAboutDialog *about,
1042                               const gchar    *version)
1043 {
1044   GtkAboutDialogPrivate *priv;
1045   gchar *tmp;
1046
1047   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1048
1049   priv = about->priv;
1050
1051   tmp = priv->version;
1052   priv->version = g_strdup (version);
1053   g_free (tmp);
1054
1055   update_name_version (about);
1056
1057   g_object_notify (G_OBJECT (about), "version");
1058 }
1059
1060 /**
1061  * gtk_about_dialog_get_copyright:
1062  * @about: a #GtkAboutDialog
1063  *
1064  * Returns the copyright string.
1065  *
1066  * Return value: The copyright string. The string is owned by the about
1067  *  dialog and must not be modified.
1068  *
1069  * Since: 2.6
1070  */
1071 G_CONST_RETURN gchar *
1072 gtk_about_dialog_get_copyright (GtkAboutDialog *about)
1073 {
1074   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1075
1076   return about->priv->copyright;
1077 }
1078
1079 /**
1080  * gtk_about_dialog_set_copyright:
1081  * @about: a #GtkAboutDialog
1082  * @copyright: (allow-none) the copyright string
1083  *
1084  * Sets the copyright string to display in the about dialog.
1085  * This should be a short string of one or two lines.
1086  *
1087  * Since: 2.6
1088  */
1089 void
1090 gtk_about_dialog_set_copyright (GtkAboutDialog *about,
1091                                 const gchar    *copyright)
1092 {
1093   GtkAboutDialogPrivate *priv;
1094   gchar *copyright_string, *tmp;
1095
1096   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1097
1098   priv = about->priv;
1099
1100   tmp = priv->copyright;
1101   priv->copyright = g_strdup (copyright);
1102   g_free (tmp);
1103
1104   if (priv->copyright != NULL)
1105     {
1106       copyright_string = g_markup_printf_escaped ("<span size=\"small\">%s</span>",
1107                                                   priv->copyright);
1108       gtk_label_set_markup (GTK_LABEL (priv->copyright_label), copyright_string);
1109       g_free (copyright_string);
1110
1111       gtk_widget_show (priv->copyright_label);
1112     }
1113   else
1114     gtk_widget_hide (priv->copyright_label);
1115
1116   g_object_notify (G_OBJECT (about), "copyright");
1117 }
1118
1119 /**
1120  * gtk_about_dialog_get_comments:
1121  * @about: a #GtkAboutDialog
1122  *
1123  * Returns the comments string.
1124  *
1125  * Return value: The comments. The string is owned by the about
1126  *  dialog and must not be modified.
1127  *
1128  * Since: 2.6
1129  */
1130 G_CONST_RETURN gchar *
1131 gtk_about_dialog_get_comments (GtkAboutDialog *about)
1132 {
1133   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1134
1135   return about->priv->comments;
1136 }
1137
1138 /**
1139  * gtk_about_dialog_set_comments:
1140  * @about: a #GtkAboutDialog
1141  * @comments: (allow-none): a comments string
1142  *
1143  * Sets the comments string to display in the about dialog.
1144  * This should be a short string of one or two lines.
1145  *
1146  * Since: 2.6
1147  */
1148 void
1149 gtk_about_dialog_set_comments (GtkAboutDialog *about,
1150                                const gchar    *comments)
1151 {
1152   GtkAboutDialogPrivate *priv;
1153   gchar *tmp;
1154
1155   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1156
1157   priv = about->priv;
1158
1159   tmp = priv->comments;
1160   if (comments)
1161     {
1162       priv->comments = g_strdup (comments);
1163       gtk_label_set_text (GTK_LABEL (priv->comments_label), priv->comments);
1164       gtk_widget_show (priv->comments_label);
1165     }
1166   else
1167     {
1168       priv->comments = NULL;
1169       gtk_widget_hide (priv->comments_label);
1170     }
1171   g_free (tmp);
1172
1173   g_object_notify (G_OBJECT (about), "comments");
1174 }
1175
1176 /**
1177  * gtk_about_dialog_get_license:
1178  * @about: a #GtkAboutDialog
1179  *
1180  * Returns the license information.
1181  *
1182  * Return value: The license information. The string is owned by the about
1183  *  dialog and must not be modified.
1184  *
1185  * Since: 2.6
1186  */
1187 G_CONST_RETURN gchar *
1188 gtk_about_dialog_get_license (GtkAboutDialog *about)
1189 {
1190   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1191
1192   return about->priv->license;
1193 }
1194
1195 /**
1196  * gtk_about_dialog_set_license:
1197  * @about: a #GtkAboutDialog
1198  * @license: (allow-none): the license information or %NULL
1199  *
1200  * Sets the license information to be displayed in the secondary
1201  * license dialog. If @license is %NULL, the license button is
1202  * hidden.
1203  *
1204  * Since: 2.6
1205  */
1206 void
1207 gtk_about_dialog_set_license (GtkAboutDialog *about,
1208                               const gchar    *license)
1209 {
1210   GtkAboutDialogPrivate *priv;
1211   gchar *tmp;
1212
1213   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1214
1215   priv = about->priv;
1216
1217   tmp = priv->license;
1218   if (license)
1219     {
1220       priv->license = g_strdup (license);
1221       priv->license_type = GTK_LICENSE_CUSTOM;
1222       gtk_widget_show (priv->license_button);
1223     }
1224   else
1225     {
1226       priv->license = NULL;
1227       priv->license_type = GTK_LICENSE_UNKNOWN;
1228       gtk_widget_hide (priv->license_button);
1229     }
1230   g_free (tmp);
1231
1232   g_object_notify (G_OBJECT (about), "license");
1233   g_object_notify (G_OBJECT (about), "license-type");
1234 }
1235
1236 /**
1237  * gtk_about_dialog_get_wrap_license:
1238  * @about: a #GtkAboutDialog
1239  *
1240  * Returns whether the license text in @about is
1241  * automatically wrapped.
1242  *
1243  * Returns: %TRUE if the license text is wrapped
1244  *
1245  * Since: 2.8
1246  */
1247 gboolean
1248 gtk_about_dialog_get_wrap_license (GtkAboutDialog *about)
1249 {
1250   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), FALSE);
1251
1252   return about->priv->wrap_license;
1253 }
1254
1255 /**
1256  * gtk_about_dialog_set_wrap_license:
1257  * @about: a #GtkAboutDialog
1258  * @wrap_license: whether to wrap the license
1259  *
1260  * Sets whether the license text in @about is
1261  * automatically wrapped.
1262  *
1263  * Since: 2.8
1264  */
1265 void
1266 gtk_about_dialog_set_wrap_license (GtkAboutDialog *about,
1267                                    gboolean        wrap_license)
1268 {
1269   GtkAboutDialogPrivate *priv;
1270
1271   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1272
1273   priv = about->priv;
1274
1275   wrap_license = wrap_license != FALSE;
1276
1277   if (priv->wrap_license != wrap_license)
1278     {
1279        priv->wrap_license = wrap_license;
1280
1281        g_object_notify (G_OBJECT (about), "wrap-license");
1282     }
1283 }
1284
1285 /**
1286  * gtk_about_dialog_get_website:
1287  * @about: a #GtkAboutDialog
1288  *
1289  * Returns the website URL.
1290  *
1291  * Return value: The website URL. The string is owned by the about
1292  *  dialog and must not be modified.
1293  *
1294  * Since: 2.6
1295  */
1296 G_CONST_RETURN gchar *
1297 gtk_about_dialog_get_website (GtkAboutDialog *about)
1298 {
1299   GtkAboutDialogPrivate *priv;
1300
1301   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1302
1303   priv = about->priv;
1304
1305   return priv->website_url;
1306 }
1307
1308 /**
1309  * gtk_about_dialog_set_website:
1310  * @about: a #GtkAboutDialog
1311  * @website: (allow-none): a URL string starting with "http://"
1312  *
1313  * Sets the URL to use for the website link.
1314  *
1315  * Note that that the hook functions need to be set up
1316  * before calling this function.
1317  *
1318  * Since: 2.6
1319  */
1320 void
1321 gtk_about_dialog_set_website (GtkAboutDialog *about,
1322                               const gchar    *website)
1323 {
1324   GtkAboutDialogPrivate *priv;
1325   gchar *tmp;
1326
1327   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1328
1329   priv = about->priv;
1330
1331   tmp = priv->website_url;
1332   priv->website_url = g_strdup (website);
1333   g_free (tmp);
1334
1335   update_website (about);
1336
1337   g_object_notify (G_OBJECT (about), "website");
1338 }
1339
1340 /**
1341  * gtk_about_dialog_get_website_label:
1342  * @about: a #GtkAboutDialog
1343  *
1344  * Returns the label used for the website link.
1345  *
1346  * Return value: The label used for the website link. The string is
1347  *     owned by the about dialog and must not be modified.
1348  *
1349  * Since: 2.6
1350  */
1351 G_CONST_RETURN gchar *
1352 gtk_about_dialog_get_website_label (GtkAboutDialog *about)
1353 {
1354   GtkAboutDialogPrivate *priv;
1355
1356   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1357
1358   priv = about->priv;
1359
1360   return priv->website_text;
1361 }
1362
1363 /**
1364  * gtk_about_dialog_set_website_label:
1365  * @about: a #GtkAboutDialog
1366  * @website_label: the label used for the website link
1367  *
1368  * Sets the label to be used for the website link.
1369  * It defaults to the website URL.
1370  *
1371  * Since: 2.6
1372  */
1373 void
1374 gtk_about_dialog_set_website_label (GtkAboutDialog *about,
1375                                     const gchar    *website_label)
1376 {
1377   GtkAboutDialogPrivate *priv;
1378   gchar *tmp;
1379
1380   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1381
1382   priv = about->priv;
1383
1384   tmp = priv->website_text;
1385   priv->website_text = g_strdup (website_label);
1386   g_free (tmp);
1387
1388   update_website (about);
1389
1390   g_object_notify (G_OBJECT (about), "website-label");
1391 }
1392
1393 /**
1394  * gtk_about_dialog_get_authors:
1395  * @about: a #GtkAboutDialog
1396  *
1397  * Returns the string which are displayed in the authors tab
1398  * of the secondary credits dialog.
1399  *
1400  * Return value: A %NULL-terminated string array containing
1401  *  the authors. The array is owned by the about dialog
1402  *  and must not be modified.
1403  *
1404  * Since: 2.6
1405  */
1406 G_CONST_RETURN gchar * G_CONST_RETURN *
1407 gtk_about_dialog_get_authors (GtkAboutDialog *about)
1408 {
1409   GtkAboutDialogPrivate *priv;
1410
1411   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1412
1413   priv = about->priv;
1414
1415   return (const gchar * const *) priv->authors;
1416 }
1417
1418 static void
1419 update_credits_button_visibility (GtkAboutDialog *about)
1420 {
1421   GtkAboutDialogPrivate *priv = about->priv;
1422   gboolean show;
1423
1424   show = priv->authors != NULL ||
1425          priv->documenters != NULL ||
1426          priv->artists != NULL ||
1427          (priv->translator_credits != NULL &&
1428           strcmp (priv->translator_credits, "translator_credits") &&
1429           strcmp (priv->translator_credits, "translator-credits"));
1430   if (show)
1431     gtk_widget_show (priv->credits_button);
1432   else
1433     gtk_widget_hide (priv->credits_button);
1434 }
1435
1436 /**
1437  * gtk_about_dialog_set_authors:
1438  * @about: a #GtkAboutDialog
1439  * @authors: a %NULL-terminated array of strings
1440  *
1441  * Sets the strings which are displayed in the authors tab
1442  * of the secondary credits dialog.
1443  *
1444  * Since: 2.6
1445  */
1446 void
1447 gtk_about_dialog_set_authors (GtkAboutDialog  *about,
1448                               const gchar    **authors)
1449 {
1450   GtkAboutDialogPrivate *priv;
1451   gchar **tmp;
1452
1453   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1454
1455   priv = about->priv;
1456
1457   tmp = priv->authors;
1458   priv->authors = g_strdupv ((gchar **)authors);
1459   g_strfreev (tmp);
1460
1461   update_credits_button_visibility (about);
1462
1463   g_object_notify (G_OBJECT (about), "authors");
1464 }
1465
1466 /**
1467  * gtk_about_dialog_get_documenters:
1468  * @about: a #GtkAboutDialog
1469  *
1470  * Returns the string which are displayed in the documenters
1471  * tab of the secondary credits dialog.
1472  *
1473  * Return value: A %NULL-terminated string array containing
1474  *  the documenters. The array is owned by the about dialog
1475  *  and must not be modified.
1476  *
1477  * Since: 2.6
1478  */
1479 G_CONST_RETURN gchar * G_CONST_RETURN *
1480 gtk_about_dialog_get_documenters (GtkAboutDialog *about)
1481 {
1482   GtkAboutDialogPrivate *priv;
1483
1484   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1485
1486   priv = about->priv;
1487
1488   return (const gchar * const *)priv->documenters;
1489 }
1490
1491 /**
1492  * gtk_about_dialog_set_documenters:
1493  * @about: a #GtkAboutDialog
1494  * @documenters: a %NULL-terminated array of strings
1495  *
1496  * Sets the strings which are displayed in the documenters tab
1497  * of the secondary credits dialog.
1498  *
1499  * Since: 2.6
1500  */
1501 void
1502 gtk_about_dialog_set_documenters (GtkAboutDialog *about,
1503                                   const gchar   **documenters)
1504 {
1505   GtkAboutDialogPrivate *priv;
1506   gchar **tmp;
1507
1508   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1509
1510   priv = about->priv;
1511
1512   tmp = priv->documenters;
1513   priv->documenters = g_strdupv ((gchar **)documenters);
1514   g_strfreev (tmp);
1515
1516   update_credits_button_visibility (about);
1517
1518   g_object_notify (G_OBJECT (about), "documenters");
1519 }
1520
1521 /**
1522  * gtk_about_dialog_get_artists:
1523  * @about: a #GtkAboutDialog
1524  *
1525  * Returns the string which are displayed in the artists tab
1526  * of the secondary credits dialog.
1527  *
1528  * Return value: A %NULL-terminated string array containing
1529  *  the artists. The array is owned by the about dialog
1530  *  and must not be modified.
1531  *
1532  * Since: 2.6
1533  */
1534 G_CONST_RETURN gchar * G_CONST_RETURN *
1535 gtk_about_dialog_get_artists (GtkAboutDialog *about)
1536 {
1537   GtkAboutDialogPrivate *priv;
1538
1539   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1540
1541   priv = about->priv;
1542
1543   return (const gchar * const *)priv->artists;
1544 }
1545
1546 /**
1547  * gtk_about_dialog_set_artists:
1548  * @about: a #GtkAboutDialog
1549  * @artists: a %NULL-terminated array of strings
1550  *
1551  * Sets the strings which are displayed in the artists tab
1552  * of the secondary credits dialog.
1553  *
1554  * Since: 2.6
1555  */
1556 void
1557 gtk_about_dialog_set_artists (GtkAboutDialog *about,
1558                               const gchar   **artists)
1559 {
1560   GtkAboutDialogPrivate *priv;
1561   gchar **tmp;
1562
1563   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1564
1565   priv = about->priv;
1566
1567   tmp = priv->artists;
1568   priv->artists = g_strdupv ((gchar **)artists);
1569   g_strfreev (tmp);
1570
1571   update_credits_button_visibility (about);
1572
1573   g_object_notify (G_OBJECT (about), "artists");
1574 }
1575
1576 /**
1577  * gtk_about_dialog_get_translator_credits:
1578  * @about: a #GtkAboutDialog
1579  *
1580  * Returns the translator credits string which is displayed
1581  * in the translators tab of the secondary credits dialog.
1582  *
1583  * Return value: The translator credits string. The string is
1584  *   owned by the about dialog and must not be modified.
1585  *
1586  * Since: 2.6
1587  */
1588 G_CONST_RETURN gchar *
1589 gtk_about_dialog_get_translator_credits (GtkAboutDialog *about)
1590 {
1591   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1592
1593   return about->priv->translator_credits;
1594 }
1595
1596 /**
1597  * gtk_about_dialog_set_translator_credits:
1598  * @about: a #GtkAboutDialog
1599  * @translator_credits: (allow-none): the translator credits
1600  *
1601  * Sets the translator credits string which is displayed in
1602  * the translators tab of the secondary credits dialog.
1603  *
1604  * The intended use for this string is to display the translator
1605  * of the language which is currently used in the user interface.
1606  * Using gettext(), a simple way to achieve that is to mark the
1607  * string for translation:
1608  * |[
1609  *  gtk_about_dialog_set_translator_credits (about, _("translator-credits"));
1610  * ]|
1611  * It is a good idea to use the customary msgid "translator-credits" for this
1612  * purpose, since translators will already know the purpose of that msgid, and
1613  * since #GtkAboutDialog will detect if "translator-credits" is untranslated
1614  * and hide the tab.
1615  *
1616  * Since: 2.6
1617  */
1618 void
1619 gtk_about_dialog_set_translator_credits (GtkAboutDialog *about,
1620                                          const gchar    *translator_credits)
1621 {
1622   GtkAboutDialogPrivate *priv;
1623   gchar *tmp;
1624
1625   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1626
1627   priv = about->priv;
1628
1629   tmp = priv->translator_credits;
1630   priv->translator_credits = g_strdup (translator_credits);
1631   g_free (tmp);
1632
1633   update_credits_button_visibility (about);
1634
1635   g_object_notify (G_OBJECT (about), "translator-credits");
1636 }
1637
1638 /**
1639  * gtk_about_dialog_get_logo:
1640  * @about: a #GtkAboutDialog
1641  *
1642  * Returns the pixbuf displayed as logo in the about dialog.
1643  *
1644  * Return value: the pixbuf displayed as logo. The pixbuf is
1645  *   owned by the about dialog. If you want to keep a reference
1646  *   to it, you have to call g_object_ref() on it.
1647  *
1648  * Since: 2.6
1649  */
1650 GdkPixbuf *
1651 gtk_about_dialog_get_logo (GtkAboutDialog *about)
1652 {
1653   GtkAboutDialogPrivate *priv;
1654
1655   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1656
1657   priv = about->priv;
1658
1659   if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_PIXBUF)
1660     return gtk_image_get_pixbuf (GTK_IMAGE (priv->logo_image));
1661   else
1662     return NULL;
1663 }
1664
1665 static GtkIconSet *
1666 icon_set_new_from_pixbufs (GList *pixbufs)
1667 {
1668   GtkIconSet *icon_set = gtk_icon_set_new ();
1669
1670   for (; pixbufs; pixbufs = pixbufs->next)
1671     {
1672       GdkPixbuf *pixbuf = GDK_PIXBUF (pixbufs->data);
1673
1674       GtkIconSource *icon_source = gtk_icon_source_new ();
1675       gtk_icon_source_set_pixbuf (icon_source, pixbuf);
1676       gtk_icon_set_add_source (icon_set, icon_source);
1677       gtk_icon_source_free (icon_source);
1678     }
1679
1680   return icon_set;
1681 }
1682
1683 /**
1684  * gtk_about_dialog_set_logo:
1685  * @about: a #GtkAboutDialog
1686  * @logo: (allow-none): a #GdkPixbuf, or %NULL
1687  *
1688  * Sets the pixbuf to be displayed as logo in the about dialog.
1689  * If it is %NULL, the default window icon set with
1690  * gtk_window_set_default_icon() will be used.
1691  *
1692  * Since: 2.6
1693  */
1694 void
1695 gtk_about_dialog_set_logo (GtkAboutDialog *about,
1696                            GdkPixbuf      *logo)
1697 {
1698   GtkAboutDialogPrivate *priv;
1699
1700   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1701
1702   priv = about->priv;
1703
1704   g_object_freeze_notify (G_OBJECT (about));
1705
1706   if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_ICON_NAME)
1707     g_object_notify (G_OBJECT (about), "logo-icon-name");
1708
1709   if (logo != NULL)
1710     gtk_image_set_from_pixbuf (GTK_IMAGE (priv->logo_image), logo);
1711   else
1712     {
1713       GList *pixbufs = gtk_window_get_default_icon_list ();
1714
1715       if (pixbufs != NULL)
1716         {
1717           GtkIconSet *icon_set = icon_set_new_from_pixbufs (pixbufs);
1718
1719           gtk_image_set_from_icon_set (GTK_IMAGE (priv->logo_image),
1720                                        icon_set, GTK_ICON_SIZE_DIALOG);
1721
1722           gtk_icon_set_unref (icon_set);
1723           g_list_free (pixbufs);
1724         }
1725     }
1726
1727   g_object_notify (G_OBJECT (about), "logo");
1728
1729   g_object_thaw_notify (G_OBJECT (about));
1730 }
1731
1732 /**
1733  * gtk_about_dialog_get_logo_icon_name:
1734  * @about: a #GtkAboutDialog
1735  *
1736  * Returns the icon name displayed as logo in the about dialog.
1737  *
1738  * Return value: the icon name displayed as logo. The string is
1739  *   owned by the dialog. If you want to keep a reference
1740  *   to it, you have to call g_strdup() on it.
1741  *
1742  * Since: 2.6
1743  */
1744 G_CONST_RETURN gchar *
1745 gtk_about_dialog_get_logo_icon_name (GtkAboutDialog *about)
1746 {
1747   GtkAboutDialogPrivate *priv;
1748   const gchar *icon_name = NULL;
1749
1750   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1751
1752   priv = about->priv;
1753
1754   if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_ICON_NAME)
1755     gtk_image_get_icon_name (GTK_IMAGE (priv->logo_image), &icon_name, NULL);
1756
1757   return icon_name;
1758 }
1759
1760 /**
1761  * gtk_about_dialog_set_logo_icon_name:
1762  * @about: a #GtkAboutDialog
1763  * @icon_name: (allow-none): an icon name, or %NULL
1764  *
1765  * Sets the pixbuf to be displayed as logo in the about dialog.
1766  * If it is %NULL, the default window icon set with
1767  * gtk_window_set_default_icon() will be used.
1768  *
1769  * Since: 2.6
1770  */
1771 void
1772 gtk_about_dialog_set_logo_icon_name (GtkAboutDialog *about,
1773                                      const gchar    *icon_name)
1774 {
1775   GtkAboutDialogPrivate *priv;
1776
1777   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1778
1779   priv = about->priv;
1780
1781   g_object_freeze_notify (G_OBJECT (about));
1782
1783   if (gtk_image_get_storage_type (GTK_IMAGE (priv->logo_image)) == GTK_IMAGE_PIXBUF)
1784     g_object_notify (G_OBJECT (about), "logo");
1785
1786   gtk_image_set_from_icon_name (GTK_IMAGE (priv->logo_image), icon_name,
1787                                 GTK_ICON_SIZE_DIALOG);
1788   g_object_notify (G_OBJECT (about), "logo-icon-name");
1789
1790   g_object_thaw_notify (G_OBJECT (about));
1791 }
1792
1793 static void
1794 follow_if_link (GtkAboutDialog *about,
1795                 GtkTextView    *text_view,
1796                 GtkTextIter    *iter)
1797 {
1798   GSList *tags = NULL, *tagp = NULL;
1799   GtkAboutDialogPrivate *priv = about->priv;
1800   gchar *url = NULL;
1801   GtkAboutDialogActivateLinkFunc email_hook, url_hook;
1802   gpointer email_hook_data, url_hook_data;
1803
1804   if (activate_email_hook_set)
1805     {
1806       email_hook = activate_email_hook;
1807       email_hook_data = activate_email_hook_data;
1808     }
1809   else
1810     {
1811       email_hook = default_email_hook;
1812       email_hook_data = NULL;
1813     }
1814
1815   if (activate_url_hook_set)
1816     {
1817       url_hook = activate_url_hook;
1818       url_hook_data = activate_url_hook_data;
1819     }
1820   else
1821     {
1822       url_hook = default_url_hook;
1823       url_hook_data = NULL;
1824     }
1825
1826   tags = gtk_text_iter_get_tags (iter);
1827   for (tagp = tags; tagp != NULL && !url; tagp = tagp->next)
1828     {
1829       GtkTextTag *tag = tagp->data;
1830
1831       if (email_hook != NULL)
1832         {
1833           url = g_object_get_data (G_OBJECT (tag), "email");
1834           if (url)
1835             email_hook (about, url, email_hook_data);
1836         }
1837
1838       if (!url && url_hook != NULL)
1839         {
1840           url = g_object_get_data (G_OBJECT (tag), "url");
1841           if (url)
1842             url_hook (about, url, url_hook_data);
1843         }
1844
1845       if (url && !g_slist_find_custom (priv->visited_links, url, (GCompareFunc)strcmp))
1846         {
1847           GdkColor *style_visited_link_color;
1848           GdkColor color;
1849
1850           gtk_widget_ensure_style (GTK_WIDGET (about));
1851           gtk_widget_style_get (GTK_WIDGET (about),
1852                                 "visited-link-color", &style_visited_link_color,
1853                                 NULL);
1854           if (style_visited_link_color)
1855             {
1856               color = *style_visited_link_color;
1857               gdk_color_free (style_visited_link_color);
1858             }
1859           else
1860             color = default_visited_link_color;
1861
1862           g_object_set (G_OBJECT (tag), "foreground-gdk", &color, NULL);
1863
1864           priv->visited_links = g_slist_prepend (priv->visited_links, g_strdup (url));
1865         }
1866     }
1867
1868   if (tags)
1869     g_slist_free (tags);
1870 }
1871
1872 static gboolean
1873 text_view_key_press_event (GtkWidget      *text_view,
1874                            GdkEventKey    *event,
1875                            GtkAboutDialog *about)
1876 {
1877   GtkTextIter iter;
1878   GtkTextBuffer *buffer;
1879
1880   switch (event->keyval)
1881     {
1882       case GDK_Return:
1883       case GDK_ISO_Enter:
1884       case GDK_KP_Enter:
1885         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
1886         gtk_text_buffer_get_iter_at_mark (buffer, &iter,
1887                                           gtk_text_buffer_get_insert (buffer));
1888         follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
1889         break;
1890
1891       default:
1892         break;
1893     }
1894
1895   return FALSE;
1896 }
1897
1898 static gboolean
1899 text_view_event_after (GtkWidget      *text_view,
1900                        GdkEvent       *event,
1901                        GtkAboutDialog *about)
1902 {
1903   GtkTextIter start, end, iter;
1904   GtkTextBuffer *buffer;
1905   GdkEventButton *button_event;
1906   gint x, y;
1907
1908   if (event->type != GDK_BUTTON_RELEASE)
1909     return FALSE;
1910
1911   button_event = (GdkEventButton *)event;
1912
1913   if (button_event->button != 1)
1914     return FALSE;
1915
1916   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
1917
1918   /* we shouldn't follow a link if the user has selected something */
1919   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
1920   if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
1921     return FALSE;
1922
1923   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
1924                                          GTK_TEXT_WINDOW_WIDGET,
1925                                          button_event->x, button_event->y, &x, &y);
1926
1927   gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
1928
1929   follow_if_link (about, GTK_TEXT_VIEW (text_view), &iter);
1930
1931   return FALSE;
1932 }
1933
1934 static void
1935 set_cursor_if_appropriate (GtkAboutDialog *about,
1936                            GtkTextView    *text_view,
1937                            GdkDevice      *device,
1938                            gint            x,
1939                            gint            y)
1940 {
1941   GtkAboutDialogPrivate *priv = about->priv;
1942   GSList *tags = NULL, *tagp = NULL;
1943   GtkTextIter iter;
1944   gboolean hovering_over_link = FALSE;
1945
1946   gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
1947
1948   tags = gtk_text_iter_get_tags (&iter);
1949   for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
1950     {
1951       GtkTextTag *tag = tagp->data;
1952       gchar *email = g_object_get_data (G_OBJECT (tag), "email");
1953       gchar *url = g_object_get_data (G_OBJECT (tag), "url");
1954
1955       if (email != NULL || url != NULL)
1956         {
1957           hovering_over_link = TRUE;
1958           break;
1959         }
1960     }
1961
1962   if (hovering_over_link != priv->hovering_over_link)
1963     {
1964       priv->hovering_over_link = hovering_over_link;
1965
1966       if (hovering_over_link)
1967         gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->hand_cursor);
1968       else
1969         gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->regular_cursor);
1970     }
1971
1972   if (tags)
1973     g_slist_free (tags);
1974 }
1975
1976 static gboolean
1977 text_view_motion_notify_event (GtkWidget *text_view,
1978                                GdkEventMotion *event,
1979                                GtkAboutDialog *about)
1980 {
1981   gint x, y;
1982
1983   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
1984                                          GTK_TEXT_WINDOW_WIDGET,
1985                                          event->x, event->y, &x, &y);
1986
1987   set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), event->device, x, y);
1988
1989   gdk_event_request_motions (event);
1990
1991   return FALSE;
1992 }
1993
1994
1995 static gboolean
1996 text_view_visibility_notify_event (GtkWidget          *text_view,
1997                                    GdkEventVisibility *event,
1998                                    GtkAboutDialog     *about)
1999 {
2000   GdkDeviceManager *device_manager;
2001   GdkDisplay *display;
2002   GList *devices, *d;
2003   gint wx, wy, bx, by;
2004
2005   display = gdk_drawable_get_display (event->window);
2006   device_manager = gdk_display_get_device_manager (display);
2007   devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2008
2009   for (d = devices; d; d = d->next)
2010     {
2011       GdkDevice *dev = d->data;
2012
2013       gdk_window_get_device_position (text_view->window, dev, &wx, &wy, NULL);
2014
2015       gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
2016                                              GTK_TEXT_WINDOW_WIDGET,
2017                                              wx, wy, &bx, &by);
2018
2019       set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), dev, bx, by);
2020     }
2021
2022   return FALSE;
2023 }
2024
2025 static GtkWidget *
2026 text_view_new (GtkAboutDialog  *about,
2027                GtkWidget       *dialog,
2028                gchar          **strings,
2029                GtkWrapMode      wrap_mode)
2030 {
2031   gchar **p;
2032   gchar *q0, *q1, *q2, *r1, *r2;
2033   GtkWidget *view;
2034   GtkTextView *text_view;
2035   GtkTextBuffer *buffer;
2036   gboolean linkify_email, linkify_urls;
2037   GdkColor *style_link_color;
2038   GdkColor *style_visited_link_color;
2039   GdkColor color;
2040   GdkColor link_color;
2041   GdkColor visited_link_color;
2042
2043   GtkAboutDialogPrivate *priv = about->priv;
2044
2045   linkify_email = (!activate_email_hook_set || activate_email_hook != NULL);
2046   linkify_urls = (!activate_url_hook_set || activate_url_hook != NULL);
2047
2048   gtk_widget_ensure_style (GTK_WIDGET (about));
2049   gtk_widget_style_get (GTK_WIDGET (about),
2050                         "link-color", &style_link_color,
2051                         "visited-link-color", &style_visited_link_color,
2052                         NULL);
2053   if (style_link_color)
2054     {
2055       link_color = *style_link_color;
2056       gdk_color_free (style_link_color);
2057     }
2058   else
2059     link_color = default_link_color;
2060
2061   if (style_visited_link_color)
2062     {
2063       visited_link_color = *style_visited_link_color;
2064       gdk_color_free (style_visited_link_color);
2065     }
2066   else
2067     visited_link_color = default_visited_link_color;
2068
2069   view = gtk_text_view_new ();
2070   text_view = GTK_TEXT_VIEW (view);
2071   buffer = gtk_text_view_get_buffer (text_view);
2072   gtk_text_view_set_cursor_visible (text_view, FALSE);
2073   gtk_text_view_set_editable (text_view, FALSE);
2074   gtk_text_view_set_wrap_mode (text_view, wrap_mode);
2075
2076   gtk_text_view_set_left_margin (text_view, 8);
2077   gtk_text_view_set_right_margin (text_view, 8);
2078
2079   g_signal_connect (view, "key-press-event",
2080                     G_CALLBACK (text_view_key_press_event), about);
2081   g_signal_connect (view, "event-after",
2082                     G_CALLBACK (text_view_event_after), about);
2083   g_signal_connect (view, "motion-notify-event",
2084                     G_CALLBACK (text_view_motion_notify_event), about);
2085   g_signal_connect (view, "visibility-notify-event",
2086                     G_CALLBACK (text_view_visibility_notify_event), about);
2087
2088   if (strings == NULL)
2089     {
2090       gtk_widget_hide (view);
2091       return view;
2092     }
2093
2094   for (p = strings; *p; p++)
2095     {
2096       q0  = *p;
2097       while (*q0)
2098         {
2099           q1 = linkify_email ? strchr (q0, '<') : NULL;
2100           q2 = q1 ? strchr (q1, '>') : NULL;
2101           r1 = linkify_urls ? strstr (q0, "http://") : NULL;
2102           if (r1)
2103             {
2104               r2 = strpbrk (r1, " \n\t");
2105               if (!r2)
2106                 r2 = strchr (r1, '\0');
2107             }
2108           else
2109             r2 = NULL;
2110
2111           if (r1 && r2 && (!q1 || !q2 || (r1 < q1)))
2112             {
2113               q1 = r1;
2114               q2 = r2;
2115             }
2116
2117           if (q1 && q2)
2118             {
2119               GtkTextIter end;
2120               gchar *link;
2121               const gchar *link_type;
2122               GtkTextTag *tag;
2123
2124               if (*q1 == '<')
2125                 {
2126                   gtk_text_buffer_insert_at_cursor (buffer, q0, (q1 - q0) + 1);
2127                   gtk_text_buffer_get_end_iter (buffer, &end);
2128                   q1++;
2129                   link_type = I_("email");
2130                 }
2131               else
2132                 {
2133                   gtk_text_buffer_insert_at_cursor (buffer, q0, q1 - q0);
2134                   gtk_text_buffer_get_end_iter (buffer, &end);
2135                   link_type = I_("url");
2136                 }
2137
2138               q0 = q2;
2139
2140               link = g_strndup (q1, q2 - q1);
2141
2142               if (g_slist_find_custom (priv->visited_links, link, (GCompareFunc)strcmp))
2143                 color = visited_link_color;
2144               else
2145                 color = link_color;
2146
2147               tag = gtk_text_buffer_create_tag (buffer, NULL,
2148                                                 "foreground-gdk", &color,
2149                                                 "underline", PANGO_UNDERLINE_SINGLE,
2150                                                 NULL);
2151               g_object_set_data_full (G_OBJECT (tag), link_type, g_strdup (link), g_free);
2152               gtk_text_buffer_insert_with_tags (buffer, &end, link, -1, tag, NULL);
2153
2154               g_free (link);
2155             }
2156           else
2157             {
2158               gtk_text_buffer_insert_at_cursor (buffer, q0, -1);
2159               break;
2160             }
2161         }
2162
2163       if (p[1])
2164         gtk_text_buffer_insert_at_cursor (buffer, "\n", 1);
2165     }
2166
2167   gtk_widget_show (view);
2168   return view;
2169 }
2170
2171 static void
2172 add_credits_page (GtkAboutDialog *about,
2173                   GtkWidget      *credits_dialog,
2174                   GtkWidget      *notebook,
2175                   gchar          *title,
2176                   gchar         **people)
2177 {
2178   GtkWidget *sw, *view;
2179
2180   view = text_view_new (about, credits_dialog, people, GTK_WRAP_NONE);
2181
2182   sw = gtk_scrolled_window_new (NULL, NULL);
2183   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
2184                                        GTK_SHADOW_IN);
2185   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2186                                   GTK_POLICY_AUTOMATIC,
2187                                   GTK_POLICY_AUTOMATIC);
2188   gtk_container_add (GTK_CONTAINER (sw), view);
2189
2190   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
2191                             sw, gtk_label_new (title));
2192 }
2193
2194 static void
2195 display_credits_dialog (GtkWidget *button,
2196                         gpointer   data)
2197 {
2198   GtkAboutDialog *about = (GtkAboutDialog *)data;
2199   GtkAboutDialogPrivate *priv = about->priv;
2200   GtkWidget *dialog, *notebook;
2201   GtkDialog *credits_dialog;
2202   GtkWidget *content_area;
2203   GtkWidget *action_area;
2204
2205   if (priv->credits_dialog != NULL)
2206     {
2207       gtk_window_present (GTK_WINDOW (priv->credits_dialog));
2208       return;
2209     }
2210
2211   dialog = gtk_dialog_new_with_buttons (_("Credits"),
2212                                         GTK_WINDOW (about),
2213                                         GTK_DIALOG_DESTROY_WITH_PARENT,
2214                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
2215                                         NULL);
2216   credits_dialog = GTK_DIALOG (dialog);
2217
2218   content_area = gtk_dialog_get_content_area (credits_dialog);
2219   action_area = gtk_dialog_get_action_area (credits_dialog);
2220
2221   gtk_dialog_set_has_separator (credits_dialog, FALSE);
2222   gtk_container_set_border_width (GTK_CONTAINER (credits_dialog), 5);
2223   gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
2224   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
2225
2226   priv->credits_dialog = dialog;
2227   gtk_window_set_default_size (GTK_WINDOW (dialog), 360, 260);
2228   gtk_dialog_set_default_response (credits_dialog, GTK_RESPONSE_CANCEL);
2229
2230   gtk_window_set_modal (GTK_WINDOW (dialog),
2231                         gtk_window_get_modal (GTK_WINDOW (about)));
2232
2233   g_signal_connect (dialog, "response",
2234                     G_CALLBACK (gtk_widget_destroy), dialog);
2235   g_signal_connect (dialog, "destroy",
2236                     G_CALLBACK (gtk_widget_destroyed),
2237                     &(priv->credits_dialog));
2238
2239   notebook = gtk_notebook_new ();
2240   gtk_container_set_border_width (GTK_CONTAINER (notebook), 5);
2241   gtk_box_pack_start (GTK_BOX (content_area), notebook, TRUE, TRUE, 0);
2242
2243   if (priv->authors != NULL)
2244     add_credits_page (about, dialog, notebook, _("Written by"), priv->authors);
2245
2246   if (priv->documenters != NULL)
2247     add_credits_page (about, dialog, notebook, _("Documented by"), priv->documenters);
2248
2249   /* Don't show an untranslated gettext msgid */
2250   if (priv->translator_credits != NULL &&
2251       strcmp (priv->translator_credits, "translator_credits") != 0 &&
2252       strcmp (priv->translator_credits, "translator-credits") != 0)
2253     {
2254       gchar *translators[2];
2255
2256       translators[0] = priv->translator_credits;
2257       translators[1] = NULL;
2258
2259       add_credits_page (about, dialog, notebook, _("Translated by"), translators);
2260     }
2261
2262   if (priv->artists != NULL)
2263     add_credits_page (about, dialog, notebook, _("Artwork by"), priv->artists);
2264
2265   gtk_widget_show_all (dialog);
2266 }
2267
2268 static void
2269 set_policy (GtkWidget *sw)
2270 {
2271   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2272                                   GTK_POLICY_AUTOMATIC,
2273                                   GTK_POLICY_AUTOMATIC);
2274 }
2275
2276 static void
2277 display_license_dialog (GtkWidget *button,
2278                         gpointer   data)
2279 {
2280   GtkAboutDialog *about = (GtkAboutDialog *)data;
2281   GtkAboutDialogPrivate *priv = about->priv;
2282   GtkWidget *dialog, *view, *sw;
2283   GtkDialog *license_dialog;
2284   GtkWidget *content_area;
2285   GtkWidget *action_area;
2286   gchar *strings[2];
2287
2288   if (priv->license_dialog != NULL)
2289     {
2290       gtk_window_present (GTK_WINDOW (priv->license_dialog));
2291       return;
2292     }
2293
2294   dialog = gtk_dialog_new_with_buttons (_("License"),
2295                                         GTK_WINDOW (about),
2296                                         GTK_DIALOG_DESTROY_WITH_PARENT,
2297                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
2298                                         NULL);
2299   license_dialog = GTK_DIALOG (dialog);
2300
2301   content_area = gtk_dialog_get_content_area (license_dialog);
2302   action_area = gtk_dialog_get_action_area (license_dialog);
2303
2304   gtk_dialog_set_has_separator (license_dialog, FALSE);
2305   gtk_container_set_border_width (GTK_CONTAINER (license_dialog), 5);
2306   gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
2307   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
2308
2309   priv->license_dialog = dialog;
2310   gtk_window_set_default_size (GTK_WINDOW (dialog), 420, 320);
2311   gtk_dialog_set_default_response (license_dialog, GTK_RESPONSE_CANCEL);
2312
2313   gtk_window_set_modal (GTK_WINDOW (dialog),
2314                         gtk_window_get_modal (GTK_WINDOW (about)));
2315
2316   g_signal_connect (dialog, "response",
2317                     G_CALLBACK (gtk_widget_destroy), dialog);
2318   g_signal_connect (dialog, "destroy",
2319                     G_CALLBACK (gtk_widget_destroyed),
2320                     &(priv->license_dialog));
2321
2322   sw = gtk_scrolled_window_new (NULL, NULL);
2323   gtk_container_set_border_width (GTK_CONTAINER (sw), 5);
2324   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
2325                                        GTK_SHADOW_IN);
2326   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2327                                   GTK_POLICY_NEVER,
2328                                   GTK_POLICY_AUTOMATIC);
2329   g_signal_connect (sw, "map", G_CALLBACK (set_policy), NULL);
2330   gtk_box_pack_start (GTK_BOX (content_area), sw, TRUE, TRUE, 0);
2331
2332   strings[0] = priv->license;
2333   strings[1] = NULL;
2334   view = text_view_new (about, dialog, strings,
2335                         priv->wrap_license ? GTK_WRAP_WORD : GTK_WRAP_NONE);
2336
2337   gtk_container_add (GTK_CONTAINER (sw), view);
2338
2339   gtk_widget_show_all (dialog);
2340 }
2341
2342 /**
2343  * gtk_about_dialog_new:
2344  *
2345  * Creates a new #GtkAboutDialog.
2346  *
2347  * Returns: a newly created #GtkAboutDialog
2348  *
2349  * Since: 2.6
2350  */
2351 GtkWidget *
2352 gtk_about_dialog_new (void)
2353 {
2354   GtkAboutDialog *dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG, NULL);
2355
2356   return GTK_WIDGET (dialog);
2357 }
2358
2359 /**
2360  * gtk_about_dialog_set_email_hook:
2361  * @func: a function to call when an email link is activated.
2362  * @data: data to pass to @func
2363  * @destroy: #GDestroyNotify for @data
2364  *
2365  * Installs a global function to be called whenever the user activates an
2366  * email link in an about dialog.
2367  *
2368  * Since 2.18 there exists a default function which uses gtk_show_uri(). To
2369  * deactivate it, you can pass %NULL for @func.
2370  *
2371  * Return value: the previous email hook.
2372  *
2373  * Since: 2.6
2374  */
2375 GtkAboutDialogActivateLinkFunc
2376 gtk_about_dialog_set_email_hook (GtkAboutDialogActivateLinkFunc func,
2377                                  gpointer                       data,
2378                                  GDestroyNotify                 destroy)
2379 {
2380   GtkAboutDialogActivateLinkFunc old;
2381
2382   if (activate_email_hook_destroy != NULL)
2383     (* activate_email_hook_destroy) (activate_email_hook_data);
2384
2385   old = activate_email_hook;
2386
2387   activate_email_hook_set = TRUE;
2388   activate_email_hook = func;
2389   activate_email_hook_data = data;
2390   activate_email_hook_destroy = destroy;
2391
2392   return old;
2393 }
2394
2395 /**
2396  * gtk_about_dialog_set_url_hook:
2397  * @func: a function to call when a URL link is activated.
2398  * @data: data to pass to @func
2399  * @destroy: #GDestroyNotify for @data
2400  *
2401  * Installs a global function to be called whenever the user activates a
2402  * URL link in an about dialog.
2403  *
2404  * Since 2.18 there exists a default function which uses gtk_show_uri(). To
2405  * deactivate it, you can pass %NULL for @func.
2406  *
2407  * Return value: the previous URL hook.
2408  *
2409  * Since: 2.6
2410  */
2411 GtkAboutDialogActivateLinkFunc
2412 gtk_about_dialog_set_url_hook (GtkAboutDialogActivateLinkFunc func,
2413                                gpointer                       data,
2414                                GDestroyNotify                 destroy)
2415 {
2416   GtkAboutDialogActivateLinkFunc old;
2417
2418   if (activate_url_hook_destroy != NULL)
2419     (* activate_url_hook_destroy) (activate_url_hook_data);
2420
2421   old = activate_url_hook;
2422
2423   activate_url_hook_set = TRUE;
2424   activate_url_hook = func;
2425   activate_url_hook_data = data;
2426   activate_url_hook_destroy = destroy;
2427
2428   return old;
2429 }
2430
2431 static void
2432 close_cb (GtkAboutDialog *about)
2433 {
2434   GtkAboutDialogPrivate *priv = about->priv;
2435
2436   if (priv->license_dialog != NULL)
2437     {
2438       gtk_widget_destroy (priv->license_dialog);
2439       priv->license_dialog = NULL;
2440     }
2441
2442   if (priv->credits_dialog != NULL)
2443     {
2444       gtk_widget_destroy (priv->credits_dialog);
2445       priv->credits_dialog = NULL;
2446     }
2447
2448   gtk_widget_hide (GTK_WIDGET (about));
2449
2450 }
2451
2452 /**
2453  * gtk_show_about_dialog:
2454  * @parent: (allow-none): transient parent, or %NULL for none
2455  * @first_property_name: the name of the first property
2456  * @Varargs: value of first property, followed by more properties, %NULL-terminated
2457  *
2458  * This is a convenience function for showing an application's about box.
2459  * The constructed dialog is associated with the parent window and
2460  * reused for future invocations of this function.
2461  *
2462  * Since: 2.6
2463  */
2464 void
2465 gtk_show_about_dialog (GtkWindow   *parent,
2466                        const gchar *first_property_name,
2467                        ...)
2468 {
2469   static GtkWidget *global_about_dialog = NULL;
2470   GtkWidget *dialog = NULL;
2471   va_list var_args;
2472
2473   if (parent)
2474     dialog = g_object_get_data (G_OBJECT (parent), "gtk-about-dialog");
2475   else
2476     dialog = global_about_dialog;
2477
2478   if (!dialog)
2479     {
2480       dialog = gtk_about_dialog_new ();
2481
2482       g_object_ref_sink (dialog);
2483
2484       g_signal_connect (dialog, "delete-event",
2485                         G_CALLBACK (gtk_widget_hide_on_delete), NULL);
2486
2487       /* Close dialog on user response */
2488       g_signal_connect (dialog, "response",
2489                         G_CALLBACK (close_cb), NULL);
2490
2491       va_start (var_args, first_property_name);
2492       g_object_set_valist (G_OBJECT (dialog), first_property_name, var_args);
2493       va_end (var_args);
2494
2495       if (parent)
2496         {
2497           gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
2498           gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
2499           g_object_set_data_full (G_OBJECT (parent),
2500                                   I_("gtk-about-dialog"),
2501                                   dialog, g_object_unref);
2502         }
2503       else
2504         global_about_dialog = dialog;
2505
2506     }
2507
2508   gtk_window_present (GTK_WINDOW (dialog));
2509 }
2510
2511 /**
2512  * gtk_about_dialog_set_license_type:
2513  * @about: a #GtkAboutDialog
2514  * @license_type: the type of license
2515  *
2516  * Sets the license of the application showing the @about dialog from a
2517  * list of known licenses.
2518  *
2519  * This function overrides the license set using
2520  * gtk_about_dialog_set_license().
2521  *
2522  * Since: 3.0
2523  */
2524 void
2525 gtk_about_dialog_set_license_type (GtkAboutDialog *about,
2526                                    GtkLicense      license_type)
2527 {
2528   GtkAboutDialogPrivate *priv;
2529
2530   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
2531   g_return_if_fail (license_type >= GTK_LICENSE_UNKNOWN &&
2532                     license_type <= GTK_LICENSE_ARTISTIC);
2533
2534   priv = about->priv;
2535
2536   if (priv->license_type != license_type)
2537     {
2538       g_object_freeze_notify (G_OBJECT (about));
2539
2540       priv->license_type = license_type;
2541
2542       /* custom licenses use the contents of the :license property */
2543       if (priv->license_type != GTK_LICENSE_CUSTOM)
2544         {
2545           const gchar *url;
2546           GString *str;
2547
2548           url = gtk_license_urls[priv->license_type];
2549           if (url == NULL)
2550             url = priv->website_url;
2551
2552           /* compose the new license string as:
2553            *
2554            *   <program-name>  <copyright>\n
2555            *   license preamble + URL
2556            *
2557            */
2558           str = g_string_sized_new (256);
2559           g_string_append (str, priv->name);
2560           g_string_append (str, "  ");
2561           g_string_append (str, priv->copyright);
2562           g_string_append (str, "\n");
2563           g_string_append_printf (str, gettext (gtk_license_preamble), url);
2564
2565           g_free (priv->license);
2566           priv->license = g_string_free (str, FALSE);
2567           priv->wrap_license = TRUE;
2568           gtk_widget_show (priv->license_button);
2569
2570           g_object_notify (G_OBJECT (about), "wrap-license");
2571           g_object_notify (G_OBJECT (about), "license");
2572         }
2573
2574       g_object_notify (G_OBJECT (about), "license-type");
2575
2576       g_object_thaw_notify (G_OBJECT (about));
2577     }
2578 }
2579
2580 /**
2581  * gtk_about_dialog_get_license_type:
2582  * @about: a #GtkAboutDialog
2583  *
2584  * Retrieves the license set using gtk_about_dialog_set_license_type()
2585  *
2586  * Return value: a #GtkLicense value
2587  *
2588  * Since: 3.0
2589  */
2590 GtkLicense
2591 gtk_about_dialog_get_license_type (GtkAboutDialog *about)
2592 {
2593   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), GTK_LICENSE_UNKNOWN);
2594
2595   return about->priv->license_type;
2596 }