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