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