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