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