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