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