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