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