]> Pileus Git - ~andy/gtk/blob - gtk/gtkaboutdialog.c
b345c640cd6de2b601653874ea1c11c3194825e3
[~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 <gdk/gdkkeysyms.h>
34
35 #include "gtkalias.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 "gtkmarshalers.h"
45 #include "gtknotebook.h"
46 #include "gtkscrolledwindow.h"
47 #include "gtkstock.h"
48 #include "gtktextview.h"
49 #include "gtkvbox.h"
50 #include "gtkviewport.h"
51 #include "gtkiconfactory.h"
52 #include "gtkintl.h"
53
54 #include <string.h>
55
56 typedef struct _GtkAboutDialogPrivate GtkAboutDialogPrivate;
57 struct _GtkAboutDialogPrivate 
58 {
59   gchar *name;
60   gchar *version;
61   gchar *copyright;
62   gchar *comments;
63   gchar *website;
64   gchar *website_label;
65   gchar *translator_credits;
66   gchar *license;
67   
68   gchar **authors;
69   gchar **documenters;
70   gchar **artists;
71   
72   GtkWidget *logo_image;
73   GtkWidget *name_label;
74   GtkWidget *comments_label;
75   GtkWidget *copyright_label;
76   GtkWidget *website_button;
77
78   GtkWidget *credits_button;
79   GtkWidget *credits_dialog;
80   GtkWidget *license_button;
81   GtkWidget *license_dialog;
82   
83   GdkCursor *hand_cursor;
84   GdkCursor *regular_cursor;
85   gboolean hovering_over_link;
86 };
87
88 #define GTK_ABOUT_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ABOUT_DIALOG, GtkAboutDialogPrivate))
89
90
91 enum 
92 {
93   PROP_0,
94   PROP_NAME,
95   PROP_VERSION,
96   PROP_COPYRIGHT,
97   PROP_COMMENTS,
98   PROP_WEBSITE,
99   PROP_WEBSITE_LABEL,
100   PROP_LICENSE,
101   PROP_AUTHORS,
102   PROP_DOCUMENTERS,
103   PROP_TRANSLATOR_CREDITS,
104   PROP_ARTISTS,
105   PROP_LOGO
106 };
107
108 static void                 gtk_about_dialog_finalize       (GObject            *object);
109 static void                 gtk_about_dialog_get_property   (GObject            *object,
110                                                              guint               prop_id,
111                                                              GValue             *value,
112                                                              GParamSpec         *pspec);
113 static void                 gtk_about_dialog_set_property   (GObject            *object,
114                                                              guint               prop_id,
115                                                              const GValue       *value,
116                                                              GParamSpec         *pspec);
117 static void                 update_name_version             (GtkAboutDialog     *about);
118 static GtkIconSet *         icon_set_new_from_pixbufs       (GList              *pixbufs);
119 static void                 activate_url                    (GtkWidget          *widget,
120                                                              gpointer            data);
121 static void                 set_link_button_text            (GtkWidget          *about,
122                                                              GtkWidget          *button,
123                                                              gchar              *text);
124 static GtkWidget *          create_link_button              (GtkWidget          *about,
125                                                              gchar              *text,
126                                                              gchar              *url,
127                                                              GCallback           callback,
128                                                              gpointer            data);
129 static void                 follow_if_link                  (GtkAboutDialog     *about,
130                                                              GtkTextIter        *iter);
131 static void                 set_cursor_if_appropriate       (GtkAboutDialog     *about,
132                                                              GtkTextView        *text_view,
133                                                              gint                x,
134                                                              gint                y);
135 static void                 add_credits_page                (GtkAboutDialog     *about,
136                                                              GtkWidget          *notebook,
137                                                              gchar              *title,
138                                                              gchar             **people);
139 static gboolean             credits_key_press_event         (GtkWidget          *text_view,
140                                                              GdkEventKey        *event,
141                                                              GtkAboutDialog     *about);
142 static gboolean             credits_event_after             (GtkWidget          *text_view,
143                                                              GdkEvent           *event,
144                                                              GtkAboutDialog     *about);
145 static gboolean             credits_motion_notify_event     (GtkWidget          *text_view,
146                                                              GdkEventMotion     *event,
147                                                              GtkAboutDialog     *about);
148 static gboolean             credits_visibility_notify_event (GtkWidget          *text_view,
149                                                              GdkEventVisibility *event,
150                                                              GtkAboutDialog     *about);
151 static void                 display_credits_dialog          (GtkWidget          *button,
152                                                              gpointer            data);
153 static void                 display_license_dialog          (GtkWidget          *button,
154                                                              gpointer            data);
155                                  
156                                                                        
157 static GtkAboutDialogActivateLinkFunc activate_email_hook = NULL;
158 static gpointer activate_email_hook_data = NULL;
159 static GDestroyNotify activate_email_hook_destroy = NULL;
160
161 static GtkAboutDialogActivateLinkFunc activate_url_hook = NULL;
162 static gpointer activate_url_hook_data = NULL;
163 static GDestroyNotify activate_url_hook_destroy = NULL;
164
165 G_DEFINE_TYPE (GtkAboutDialog, gtk_about_dialog, GTK_TYPE_DIALOG);
166
167 static void
168 gtk_about_dialog_class_init (GtkAboutDialogClass *klass)
169 {
170   GObjectClass *object_class;
171   GtkWidgetClass *widget_class;
172   GtkDialogClass *dialog_class;
173         
174   object_class = (GObjectClass *)klass;
175   widget_class = (GtkWidgetClass *)klass;
176   dialog_class = (GtkDialogClass *)klass;
177         
178   object_class->set_property = gtk_about_dialog_set_property;
179   object_class->get_property = gtk_about_dialog_get_property;
180
181   object_class->finalize = gtk_about_dialog_finalize;
182
183   g_object_class_install_property (object_class,
184                                    PROP_NAME,
185                                    g_param_spec_string ("name",
186                                                         P_("Program name"),
187                                                         P_("The name of the program. If this is not set, it defaults to g_get_application_name()"),
188                                                         NULL,
189                                                         G_PARAM_READWRITE));
190
191   g_object_class_install_property (object_class,
192                                    PROP_VERSION,
193                                    g_param_spec_string ("version",
194                                                         P_("Program version"),
195                                                         P_("The version of the program"),
196                                                         NULL,
197                                                         G_PARAM_READWRITE));
198   g_object_class_install_property (object_class,
199                                    PROP_COPYRIGHT,
200                                    g_param_spec_string ("copyright",
201                                                         P_("Copyright string"),
202                                                         P_("Copyright information for the program"),
203                                                         NULL,
204                                                         G_PARAM_READWRITE));
205         
206   g_object_class_install_property (object_class,
207                                    PROP_COMMENTS,
208                                    g_param_spec_string ("comments",
209                                                         P_("Comments string"),
210                                                         P_("Comments about the program"),
211                                                         NULL,
212                                                         G_PARAM_READWRITE));
213   g_object_class_install_property (object_class,
214                                    PROP_LICENSE,
215                                    g_param_spec_string ("license",
216                                                         _("License"),
217                                                         _("The license of the program"),
218                                                         NULL,
219                                                         G_PARAM_READWRITE));
220
221   g_object_class_install_property (object_class,
222                                    PROP_WEBSITE,
223                                    g_param_spec_string ("website",
224                                                         P_("Website URL"),
225                                                         P_("The URL for the link to the website of the program"),
226                                                         NULL,
227                                                         G_PARAM_READWRITE));
228
229   g_object_class_install_property (object_class,
230                                    PROP_WEBSITE_LABEL,
231                                    g_param_spec_string ("website_label",
232                                                         P_("Website label"),
233                                                         P_("The label for the link to the website of the program. If this is not set, it defaults to the URL"),
234                                                         NULL,
235                                                         G_PARAM_READWRITE));
236
237   g_object_class_install_property (object_class,
238                                    PROP_AUTHORS,
239                                    g_param_spec_boxed ("authors",
240                                                        P_("Authors"),
241                                                        P_("List of authors of the programs"),
242                                                        G_TYPE_STRV,
243                                                        G_PARAM_READWRITE));
244   g_object_class_install_property (object_class,
245                                    PROP_DOCUMENTERS,
246                                    g_param_spec_boxed ("documenters",
247                                                        P_("Documenters"),
248                                                        P_("List of people documenting the program"),
249                                                        G_TYPE_STRV,
250                                                        G_PARAM_READWRITE));
251
252   g_object_class_install_property (object_class,
253                                    PROP_ARTISTS,
254                                    g_param_spec_boxed ("artists",
255                                                        P_("Artists"),
256                                                        P_("List of people who have contributed artwork to the program"),
257                                                        G_TYPE_STRV,
258                                                        G_PARAM_READWRITE));
259
260   g_object_class_install_property (object_class,
261                                    PROP_TRANSLATOR_CREDITS,
262                                    g_param_spec_string ("translator_credits",
263                                                         P_("Translator credits"),
264                                                         P_("Credits to the translators. This string should be marked as translatable"),
265                                                         NULL,
266                                                         G_PARAM_READWRITE));
267         
268   g_object_class_install_property (object_class,
269                                    PROP_LOGO,
270                                    g_param_spec_object ("logo",
271                                                         P_("Logo"),
272                                                         P_("A logo for the about box. If this is not set, it defaults to gtk_window_get_default_icon_list()"),
273                                                         GDK_TYPE_PIXBUF,
274                                                         G_PARAM_READWRITE));
275
276   /* Style properties */
277   gtk_widget_class_install_style_property (widget_class,
278                                            g_param_spec_boxed ("link_color",
279                                                                P_("Link Color"),
280                                                                P_("Color of hyperlinks"),
281                                                                GDK_TYPE_COLOR,
282                                                                G_PARAM_READABLE));
283
284   g_type_class_add_private (object_class, sizeof (GtkAboutDialogPrivate));
285 }
286
287 static void
288 gtk_about_dialog_init (GtkAboutDialog *about)
289 {
290   GtkAboutDialogPrivate *priv;
291   GtkWidget *vbox, *hbox, *button;
292
293   /* Data */
294   priv = GTK_ABOUT_DIALOG_GET_PRIVATE (about);
295   about->private_data = priv;
296
297   priv->name = NULL;
298   priv->version = NULL;
299   priv->copyright = NULL;
300   priv->comments = NULL;
301   priv->website = NULL;
302   priv->website_label = NULL;
303   priv->translator_credits = NULL;
304   priv->authors = NULL;
305   priv->documenters = NULL;
306   priv->artists = NULL;
307
308   priv->hand_cursor = gdk_cursor_new (GDK_HAND2);
309   priv->regular_cursor = gdk_cursor_new (GDK_XTERM);
310   priv->hovering_over_link = FALSE;
311
312   gtk_dialog_set_has_separator (GTK_DIALOG (about), FALSE);
313   
314   /* Widgets */
315   gtk_widget_push_composite_child ();
316   vbox = gtk_vbox_new (FALSE, 8);
317   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
318
319   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about)->vbox), vbox, TRUE, TRUE, 0);
320
321   priv->logo_image = gtk_image_new ();
322   gtk_box_pack_start (GTK_BOX (vbox), priv->logo_image, FALSE, FALSE, 0);
323
324   priv->name_label = gtk_label_new (NULL);
325   gtk_label_set_selectable (GTK_LABEL (priv->name_label), TRUE);
326   gtk_label_set_justify (GTK_LABEL (priv->name_label), GTK_JUSTIFY_CENTER);
327   gtk_box_pack_start (GTK_BOX (vbox), priv->name_label, FALSE, FALSE, 0);
328
329   priv->comments_label = gtk_label_new (NULL);
330   gtk_label_set_selectable (GTK_LABEL (priv->comments_label), TRUE);
331   gtk_label_set_justify (GTK_LABEL (priv->comments_label), GTK_JUSTIFY_CENTER);
332   gtk_label_set_line_wrap (GTK_LABEL (priv->comments_label), TRUE);
333   gtk_box_pack_start (GTK_BOX (vbox), priv->comments_label, FALSE, FALSE, 0);
334
335   priv->copyright_label = gtk_label_new (NULL);
336   gtk_label_set_selectable (GTK_LABEL (priv->copyright_label), TRUE);   
337   gtk_label_set_justify (GTK_LABEL (priv->copyright_label), GTK_JUSTIFY_CENTER);
338   gtk_box_pack_start (GTK_BOX (vbox), priv->copyright_label, FALSE, FALSE, 0);
339
340   button = create_link_button (GTK_WIDGET (about), "", "", 
341                                G_CALLBACK (activate_url), about);
342
343   hbox = gtk_hbox_new (TRUE, 0);
344   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
345   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0); 
346   priv->website_button = button;
347   
348   gtk_widget_show (vbox);
349   gtk_widget_show (priv->logo_image);
350   gtk_widget_show (priv->name_label);
351   gtk_widget_show (hbox);
352
353   /* Add the OK button */
354   gtk_dialog_add_button (GTK_DIALOG (about), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
355   gtk_dialog_set_default_response (GTK_DIALOG (about), GTK_RESPONSE_CLOSE);
356
357   /* Add the credits button */
358   button = gtk_button_new_from_stock (_("_Credits"));
359   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (about)->action_area), 
360                     button, FALSE, TRUE, 0); 
361   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (GTK_DIALOG (about)->action_area), button, TRUE);
362   g_signal_connect (button, "clicked", G_CALLBACK (display_credits_dialog), about);
363   priv->credits_button = button;
364   priv->credits_dialog = NULL;
365
366   /* Add the license button */
367   button = gtk_button_new_from_stock (_("_License"));
368   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (about)->action_area), 
369                     button, FALSE, TRUE, 0); 
370   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (GTK_DIALOG (about)->action_area), button, TRUE);
371   g_signal_connect (button, "clicked", G_CALLBACK (display_license_dialog), about);
372   priv->license_button = button;
373   priv->license_dialog = NULL;
374
375   gtk_window_set_resizable (GTK_WINDOW (about), FALSE);
376
377   gtk_widget_pop_composite_child ();
378 }
379
380 static void
381 gtk_about_dialog_finalize (GObject *object)
382 {
383   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
384   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
385
386   g_free (priv->name);
387   g_free (priv->version);
388   g_free (priv->copyright);
389   g_free (priv->comments);
390   g_free (priv->license);
391   g_free (priv->website);
392   g_free (priv->website_label);
393   g_free (priv->translator_credits);
394
395   g_strfreev (priv->authors);
396   g_strfreev (priv->documenters);
397   g_strfreev (priv->artists);
398
399   gdk_cursor_unref (priv->hand_cursor);
400   gdk_cursor_unref (priv->regular_cursor);
401
402   G_OBJECT_CLASS (gtk_about_dialog_parent_class)->finalize (object);
403 }
404
405 static void
406 gtk_about_dialog_set_property (GObject      *object, 
407                                guint         prop_id, 
408                                const GValue *value, 
409                                GParamSpec   *pspec)
410 {
411   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
412
413   switch (prop_id) 
414     {
415     case PROP_NAME:
416       gtk_about_dialog_set_name (about, g_value_get_string (value));
417       break;
418     case PROP_VERSION:
419       gtk_about_dialog_set_version (about, g_value_get_string (value));
420       break;
421     case PROP_COMMENTS:
422       gtk_about_dialog_set_comments (about, g_value_get_string (value));
423       break;
424     case PROP_WEBSITE:
425       gtk_about_dialog_set_website (about, g_value_get_string (value));
426       break;
427     case PROP_WEBSITE_LABEL:
428       gtk_about_dialog_set_website_label (about, g_value_get_string (value));
429       break;
430     case PROP_LICENSE:
431       gtk_about_dialog_set_license (about, g_value_get_string (value));
432       break;
433     case PROP_COPYRIGHT:
434       gtk_about_dialog_set_copyright (about, g_value_get_string (value));
435       break;
436     case PROP_LOGO:
437       gtk_about_dialog_set_logo (about, g_value_get_object (value));
438       break; 
439     case PROP_AUTHORS:
440       gtk_about_dialog_set_authors (about, (gchar**)g_value_get_boxed (value));
441       break;
442     case PROP_DOCUMENTERS:
443       gtk_about_dialog_set_documenters (about, (gchar**)g_value_get_boxed (value));
444       break;    
445     case PROP_ARTISTS:
446       gtk_about_dialog_set_artists (about, (gchar**)g_value_get_boxed (value));
447       break;    
448     case PROP_TRANSLATOR_CREDITS:
449       gtk_about_dialog_set_translator_credits (about, g_value_get_string (value));
450       break;
451     default:
452       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
453       break;
454     }
455 }
456
457 static void
458 gtk_about_dialog_get_property (GObject    *object, 
459                                guint       prop_id, 
460                                GValue     *value, 
461                                GParamSpec *pspec)
462 {
463   GtkAboutDialog *about = GTK_ABOUT_DIALOG (object);
464   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
465         
466   switch (prop_id) 
467     {
468     case PROP_NAME:
469       g_value_set_string (value, priv->name);
470       break;
471     case PROP_VERSION:
472       g_value_set_string (value, priv->version);
473       break;
474     case PROP_COPYRIGHT:
475       g_value_set_string (value, priv->copyright);
476       break;
477     case PROP_COMMENTS:
478       g_value_set_string (value, priv->comments);
479       break;
480     case PROP_WEBSITE:
481       g_value_set_string (value, priv->website);
482       break;
483     case PROP_WEBSITE_LABEL:
484       g_value_set_string (value, priv->website_label);
485       break;
486     case PROP_LICENSE:
487       g_value_set_string (value, priv->license);
488       break;
489     case PROP_TRANSLATOR_CREDITS:
490       g_value_set_string (value, priv->translator_credits);
491       break;
492     case PROP_AUTHORS:
493       g_value_set_boxed (value, priv->authors);
494       break;
495     case PROP_DOCUMENTERS:
496       g_value_set_boxed (value, priv->documenters);
497       break;
498     case PROP_ARTISTS:
499       g_value_set_boxed (value, priv->artists);
500       break;
501     case PROP_LOGO:
502       g_value_set_object (value, gtk_image_get_pixbuf (GTK_IMAGE (priv->logo_image)));
503       break;
504     default:
505       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
506       break;
507     }
508 }
509
510 /**
511  * gtk_about_dialog_get_name:
512  * @about: a #GtkAboutDialog
513  * 
514  * Returns the program name displayed in the about dialog.
515  * 
516  * Return value: The program name. The string is owned by the about
517  *  dialog and must not be modified.
518  *
519  * Since: 2.6
520  **/
521 G_CONST_RETURN gchar *
522 gtk_about_dialog_get_name (GtkAboutDialog *about)
523 {
524   GtkAboutDialogPrivate *priv;
525   
526   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
527
528   priv = (GtkAboutDialogPrivate *)about->private_data;
529
530   return priv->name;
531 }
532
533 static void
534 update_name_version (GtkAboutDialog *about)
535 {
536   GtkAboutDialogPrivate *priv;
537   gchar *title_string, *name_string;
538
539   priv = (GtkAboutDialogPrivate *)about->private_data;
540
541   title_string = g_strdup_printf (_("About %s"), priv->name);
542   gtk_window_set_title (GTK_WINDOW (about), title_string);
543   g_free (title_string);
544
545   if (priv->version != NULL) 
546     name_string = g_markup_printf_escaped ("<span size=\"xx-large\" weight=\"bold\">%s %s</span>", 
547                                              priv->name, priv->version);
548   else
549     name_string = g_markup_printf_escaped ("<span size=\"xx-large\" weight=\"bold\">%s</span>", 
550                                            priv->name);
551
552   gtk_label_set_markup (GTK_LABEL (priv->name_label), name_string);
553
554   g_free (name_string);
555 }
556
557 /**
558  * gtk_about_dialog_set_name:
559  * @about: a #GtkAboutDialog
560  * @name: the program name
561  *
562  * Sets the name to display in the about dialog. 
563  * If this is not set, it defaults to g_get_application_name().
564  * 
565  * Since: 2.6
566  **/
567 void
568 gtk_about_dialog_set_name (GtkAboutDialog *about, 
569                            const gchar    *name)
570 {
571   GtkAboutDialogPrivate *priv;
572   gchar *tmp;
573
574   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
575         
576   priv = (GtkAboutDialogPrivate *)about->private_data;
577   tmp = priv->name;
578   priv->name = g_strdup (name ? name : g_get_application_name ());
579   g_free (tmp);
580
581   update_name_version (about);
582
583   g_object_notify (G_OBJECT (about), "name");
584 }
585
586 /**
587  * gtk_about_dialog_get_version:
588  * @about: a #GtkAboutDialog
589  * 
590  * Returns the version string.
591  * 
592  * Return value: The version string. The string is owned by the about
593  *  dialog and must not be modified.
594  *
595  * Since: 2.6
596  **/
597 G_CONST_RETURN gchar *
598 gtk_about_dialog_get_version (GtkAboutDialog *about)
599 {
600   GtkAboutDialogPrivate *priv;
601   
602   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
603
604   priv = (GtkAboutDialogPrivate *)about->private_data;
605
606   return priv->version;
607 }
608
609 /**
610  * gtk_about_dialog_set_version:
611  * @about: a #GtkAboutDialog
612  * @version: the version string 
613  *
614  * Sets the version string to display in the about dialog.
615  * 
616  * Since: 2.6
617  **/
618 void
619 gtk_about_dialog_set_version (GtkAboutDialog *about, 
620                               const gchar    *version)
621 {
622   GtkAboutDialogPrivate *priv;
623   gchar *tmp;
624
625   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
626
627   priv = (GtkAboutDialogPrivate *)about->private_data;
628   
629   tmp = priv->version;
630   priv->version = version ? g_strdup (version) : NULL;
631   g_free (tmp);
632
633   update_name_version (about);
634
635   g_object_notify (G_OBJECT (about), "version");
636 }
637
638 /**
639  * gtk_about_dialog_get_copyright:
640  * @about: a #GtkAboutDialog
641  * 
642  * Returns the copyright string.
643  * 
644  * Return value: The copyright string. The string is owned by the about
645  *  dialog and must not be modified.
646  *
647  * Since: 2.6
648  **/
649 G_CONST_RETURN gchar *
650 gtk_about_dialog_get_copyright (GtkAboutDialog *about)
651 {
652   GtkAboutDialogPrivate *priv;
653   
654   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
655
656   priv = (GtkAboutDialogPrivate *)about->private_data;
657
658   return priv->copyright;
659 }
660
661 /**
662  * gtk_about_dialog_set_copyright:
663  * @about: a #GtkAboutDialog
664  * @copyright: the copyright string
665  * 
666  * Sets the copyright string to display in the about dialog.
667  * This should be a short string of one or two lines. 
668  *
669  * Since: 2.6
670  **/
671 void
672 gtk_about_dialog_set_copyright (GtkAboutDialog *about, 
673                                 const gchar    *copyright)
674 {
675   GtkAboutDialogPrivate *priv;
676   gchar *copyright_string, *tmp;
677
678   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
679
680   priv = (GtkAboutDialogPrivate *)about->private_data;
681         
682   tmp = priv->copyright;
683   priv->copyright = copyright ? g_strdup (copyright) : NULL;
684   g_free (tmp);
685   
686   if (priv->copyright != NULL) 
687     {
688       copyright_string = g_markup_printf_escaped ("<span size=\"small\">%s</span>", 
689                                                   priv->copyright);
690       gtk_label_set_markup (GTK_LABEL (priv->copyright_label), copyright_string);
691       g_free (copyright_string);
692   
693       gtk_widget_show (priv->copyright_label);
694     }
695   else 
696     gtk_widget_hide (priv->copyright_label);
697   
698   g_object_notify (G_OBJECT (about), "copyright");
699 }
700
701 /**
702  * gtk_about_dialog_get_comments:
703  * @about: a #GtkAboutDialog
704  * 
705  * Returns the comments string.
706  * 
707  * Return value: The comments. The string is owned by the about
708  *  dialog and must not be modified.
709  *
710  * Since: 2.6
711  **/
712 G_CONST_RETURN gchar *
713 gtk_about_dialog_get_comments (GtkAboutDialog *about)
714 {
715   GtkAboutDialogPrivate *priv;
716   
717   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
718
719   priv = (GtkAboutDialogPrivate *)about->private_data;
720
721   return priv->comments;
722 }
723
724 /**
725  * gtk_about_dialog_set_comments:
726  * @about: a #GtkAboutDialog
727  * @comments: a comments string
728  * 
729  * Sets the comments string to display in the about 
730  * dialog. This should be a short string of one or
731  * two lines.
732  *
733  * Since: 2.6
734  **/
735 void
736 gtk_about_dialog_set_comments (GtkAboutDialog *about, 
737                                const gchar    *comments)
738 {
739   GtkAboutDialogPrivate *priv;
740   gchar *tmp;
741
742   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
743
744   priv = (GtkAboutDialogPrivate *)about->private_data;
745   
746   tmp = priv->comments;
747   if (comments) 
748     {
749       priv->comments = g_strdup (comments);
750       gtk_label_set_text (GTK_LABEL (priv->comments_label), priv->comments);
751       gtk_widget_show (priv->comments_label);
752     }
753   else
754     {
755       priv->comments = NULL;
756       gtk_widget_hide (priv->comments_label);
757     }
758   g_free (tmp);
759
760   g_object_notify (G_OBJECT (about), "comments");
761 }
762
763 /**
764  * gtk_about_dialog_get_license:
765  * @about: a #GtkAboutDialog
766  * 
767  * Returns the license information.
768  * 
769  * Return value: The license information. The string is owned by the about
770  *  dialog and must not be modified.
771  *
772  * Since: 2.6
773  **/
774 G_CONST_RETURN gchar *
775 gtk_about_dialog_get_license (GtkAboutDialog *about)
776 {
777   GtkAboutDialogPrivate *priv;
778   
779   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
780
781   priv = (GtkAboutDialogPrivate *)about->private_data;
782
783   return priv->license;
784 }
785
786 /**
787  * gtk_about_dialog_set_license:
788  * @about: a #GtkAboutDialog
789  * @license: the license information or %NULL
790  *
791  * Sets the license information to be displayed in the secondary
792  * license dialog. If @license is %NULL, the license button is
793  * hidden.
794  * 
795  * Since: 2.6
796  **/
797 void
798 gtk_about_dialog_set_license (GtkAboutDialog *about, 
799                               const gchar    *license)
800 {
801   GtkAboutDialogPrivate *priv;
802   gchar *tmp;
803
804   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
805
806   priv = (GtkAboutDialogPrivate *)about->private_data;
807
808   tmp = priv->license;
809   if (license) 
810     {
811       priv->license = g_strdup (license);
812       gtk_widget_show (priv->license_button);
813     }
814   else
815     {
816       priv->license = NULL;
817       gtk_widget_hide (priv->license_button);
818     }
819   g_free (tmp);
820
821   g_object_notify (G_OBJECT (about), "license");
822 }
823
824 /**
825  * gtk_about_dialog_get_website:
826  * @about: a #GtkAboutDialog
827  * 
828  * Returns the website URL.
829  * 
830  * Return value: The website URL. The string is owned by the about
831  *  dialog and must not be modified.
832  *
833  * Since: 2.6
834  **/
835 G_CONST_RETURN gchar *
836 gtk_about_dialog_get_website (GtkAboutDialog *about)
837 {
838   GtkAboutDialogPrivate *priv;
839   
840   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
841
842   priv = (GtkAboutDialogPrivate *)about->private_data;
843
844   return priv->website;
845 }
846
847 /**
848  * gtk_about_dialog_set_website:
849  * @about: a #GtkAboutDialog
850  * @website: a URL string starting with "http://"
851  * 
852  * Sets the URL to use for the website link.
853  *
854  * Since: 2.6
855  **/
856 void
857 gtk_about_dialog_set_website (GtkAboutDialog *about, 
858                               const gchar    *website)
859 {
860   GtkAboutDialogPrivate *priv;
861   gchar *tmp;
862
863   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
864
865   priv = (GtkAboutDialogPrivate *)about->private_data;
866   
867   tmp = priv->website;
868   if (website != NULL)
869     {
870       priv->website = g_strdup (website);
871       if (activate_url_hook != NULL)
872         {
873           g_object_set_data_full (G_OBJECT (priv->website_button), 
874                                   "url", g_strdup (website), g_free);  
875           if (priv->website_label == NULL) 
876             gtk_about_dialog_set_website_label (about, website);
877         }
878       else 
879         {
880           GtkWidget *hbox = priv->website_button->parent;
881           gtk_widget_destroy (priv->website_button);
882           priv->website_button = gtk_label_new (website);
883           gtk_label_set_selectable (GTK_LABEL (priv->website_button), TRUE);
884           gtk_container_add (GTK_CONTAINER (hbox), priv->website_button);
885           gtk_widget_show (priv->website_button);
886         }
887     }
888   else 
889     {
890       priv->website = NULL;
891       g_object_set_data (G_OBJECT (priv->website_button), "url", NULL);
892     }
893   g_free (tmp);
894
895   g_object_notify (G_OBJECT (about), "website");
896 }
897
898 /**
899  * gtk_about_dialog_get_website_label:
900  * @about: a #GtkAboutDialog
901  * 
902  * Returns the label used for the website link. 
903  * 
904  * Return value: The label used for the website link. The string is owned by the about
905  *  dialog and must not be modified.
906  *
907  * Since: 2.6
908  **/
909 G_CONST_RETURN gchar *
910 gtk_about_dialog_get_website_label (GtkAboutDialog *about)
911 {
912   GtkAboutDialogPrivate *priv;
913   
914   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
915
916   priv = (GtkAboutDialogPrivate *)about->private_data;
917
918   return priv->website_label;
919 }
920
921 /**
922  * gtk_about_dialog_set_website_label:
923  * @about: a #GtkAboutDialog
924  * @website_label: the label used for the website link
925  * 
926  * Sets the label to be used for the website link.
927  * It defaults to the website URL.
928  *
929  * Since: 2.6
930  **/
931 void
932 gtk_about_dialog_set_website_label (GtkAboutDialog *about, 
933                                     const gchar    *website_label)
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
942   tmp = priv->website_label;
943   if (activate_url_hook != NULL)
944     {
945       if (website_label != NULL) 
946         {
947           priv->website_label = g_strdup (website_label);
948           set_link_button_text (GTK_WIDGET (about),
949                                 priv->website_button, 
950                                 priv->website_label);
951           gtk_widget_show (priv->website_button);
952         }
953       else 
954         {
955           priv->website_label = NULL;
956           gtk_widget_hide (priv->website_button);
957         }
958     }
959   g_free (tmp);
960
961   g_object_notify (G_OBJECT (about), "website_label");
962 }
963
964 /**
965  * gtk_about_dialog_get_authors:
966  * @about: a #GtkAboutDialog
967  * 
968  * Returns the string which are displayed in the authors tab
969  * of the secondary credits dialog.
970  * 
971  * Return value: A %NULL-terminated string array containing
972  *  the authors. The array is owned by the about dialog 
973  *  and must not be modified.
974  *
975  * Since: 2.6
976  **/
977 gchar **
978 gtk_about_dialog_get_authors (GtkAboutDialog *about)
979 {
980   GtkAboutDialogPrivate *priv;
981   
982   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
983
984   priv = (GtkAboutDialogPrivate *)about->private_data;
985
986   return priv->authors;
987 }
988
989 /**
990  * gtk_about_dialog_set_authors:
991  * @about: a #GtkAboutDialog
992  * @authors: a %NULL-terminated array of strings 
993  *
994  * Sets the strings which are displayed in the authors tab
995  * of the secondary credits dialog. 
996  * 
997  * Since: 2.6
998  **/
999 void
1000 gtk_about_dialog_set_authors (GtkAboutDialog  *about, 
1001                               gchar          **authors)
1002 {
1003   GtkAboutDialogPrivate *priv;
1004   gchar **tmp;
1005
1006   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1007
1008   priv = (GtkAboutDialogPrivate *)about->private_data;
1009
1010   tmp = priv->authors;
1011   priv->authors = g_strdupv (authors);
1012   g_strfreev (tmp);
1013
1014   if (priv->authors != NULL)
1015     gtk_widget_show (priv->credits_button);
1016
1017   g_object_notify (G_OBJECT (about), "authors");
1018 }
1019
1020 /**
1021  * gtk_about_dialog_get_documenters:
1022  * @about: a #GtkAboutDialog
1023  * 
1024  * Returns the string which are displayed in the documenters 
1025  * tab of the secondary credits dialog.
1026  * 
1027  * Return value: A %NULL-terminated string array containing
1028  *  the documenters. The array is owned by the about dialog 
1029  *  and must not be modified.
1030  *
1031  * Since: 2.6
1032  **/
1033 gchar **
1034 gtk_about_dialog_get_documenters (GtkAboutDialog *about)
1035 {
1036   GtkAboutDialogPrivate *priv;
1037   
1038   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1039
1040   priv = (GtkAboutDialogPrivate *)about->private_data;
1041
1042   return priv->documenters;
1043 }
1044
1045 /**
1046  * gtk_about_dialog_set_documenters:
1047  * @about: a #GtkAboutDialog
1048  * @documenters: a %NULL-terminated array of strings 
1049  *
1050  * Sets the strings which are displayed in the documenters tab
1051  * of the secondary credits dialog. 
1052  * 
1053  * Since: 2.6
1054  **/
1055 void
1056 gtk_about_dialog_set_documenters (GtkAboutDialog *about, 
1057                                   gchar         **documenters)
1058 {
1059   GtkAboutDialogPrivate *priv;
1060   gchar **tmp;
1061
1062   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1063
1064   priv = (GtkAboutDialogPrivate *)about->private_data;
1065   
1066   tmp = priv->documenters;
1067   priv->documenters = g_strdupv (documenters);
1068   g_strfreev (tmp);
1069
1070   if (priv->documenters != NULL)
1071     gtk_widget_show (priv->credits_button);
1072
1073   g_object_notify (G_OBJECT (about), "documenters");
1074 }
1075
1076 /**
1077  * gtk_about_dialog_get_artists:
1078  * @about: a #GtkAboutDialog
1079  * 
1080  * Returns the string which are displayed in the artists tab
1081  * of the secondary credits dialog.
1082  * 
1083  * Return value: A %NULL-terminated string array containing
1084  *  the artists. The array is owned by the about dialog 
1085  *  and must not be modified.
1086  *
1087  * Since: 2.6
1088  **/
1089 gchar **
1090 gtk_about_dialog_get_artists (GtkAboutDialog *about)
1091 {
1092   GtkAboutDialogPrivate *priv;
1093   
1094   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1095
1096   priv = (GtkAboutDialogPrivate *)about->private_data;
1097
1098   return priv->artists;
1099 }
1100
1101 /**
1102  * gtk_about_dialog_set_artists:
1103  * @about: a #GtkAboutDialog
1104  * @artists: a %NULL-terminated array of strings 
1105  *
1106  * Sets the strings which are displayed in the artists tab
1107  * of the secondary credits dialog. 
1108  * 
1109  * Since: 2.6
1110  **/
1111 void
1112 gtk_about_dialog_set_artists (GtkAboutDialog *about, 
1113                               gchar         **artists)
1114 {
1115   GtkAboutDialogPrivate *priv;
1116   gchar **tmp;
1117
1118   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1119
1120   priv = (GtkAboutDialogPrivate *)about->private_data;
1121   
1122   tmp = priv->artists;
1123   priv->artists = g_strdupv (artists);
1124   g_strfreev (tmp);
1125
1126   if (priv->artists != NULL)
1127     gtk_widget_show (priv->credits_button);
1128
1129   g_object_notify (G_OBJECT (about), "artists");
1130 }
1131
1132 /**
1133  * gtk_about_dialog_get_translator_credits:
1134  * @about: a #GtkAboutDialog
1135  * 
1136  * Returns the translator credits string which is displayed
1137  * in the translators tab of the secondary credits dialog.
1138  * 
1139  * Return value: The translator credits string. The string is
1140  *   owned by the about dialog and must not be modified.
1141  *
1142  * Since: 2.6
1143  **/
1144 G_CONST_RETURN gchar *
1145 gtk_about_dialog_get_translator_credits (GtkAboutDialog *about)
1146 {
1147   GtkAboutDialogPrivate *priv;
1148   
1149   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1150
1151   priv = (GtkAboutDialogPrivate *)about->private_data;
1152
1153   return priv->translator_credits;
1154 }
1155
1156 /**
1157  * gtk_about_dialog_set_translator_credits:
1158  * @about: a #GtkAboutDialog
1159  * @translator_credits: the translator credits
1160  * 
1161  * Sets the translator credits string which is displayed in
1162  * the translators tab of the secondary credits dialog.
1163  * 
1164  * The intended use for this string is to display the translator
1165  * of the language which is currently used in the user interface.
1166  * Using gettext(), a simple way to achieve that is to mark the
1167  * string for translation:
1168  * <informalexample><programlisting>
1169  *  gtk_about_dialog_set_translator_credits (about, _("translator-credits"));
1170  * </programlisting></informalexample>
1171  * It is a good idea to use the customary msgid "translator-credits" for this
1172  * purpose, since translators will already know the purpose of that msgid, and
1173  * since #GtkAboutDialog will detect if "translator-credits" is untranslated
1174  * and hide the tab.
1175  *
1176  * Since: 2.6
1177  **/
1178 void
1179 gtk_about_dialog_set_translator_credits (GtkAboutDialog *about, 
1180                                          const gchar    *translator_credits)
1181 {
1182   GtkAboutDialogPrivate *priv;
1183   gchar *tmp;
1184
1185   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1186
1187   priv = (GtkAboutDialogPrivate *)about->private_data;
1188
1189   tmp = priv->translator_credits;
1190   priv->translator_credits = g_strdup (translator_credits);
1191   g_free (tmp);
1192
1193   if (priv->translator_credits != NULL)
1194     gtk_widget_show (priv->credits_button);
1195
1196   g_object_notify (G_OBJECT (about), "translator-credits");
1197 }
1198
1199 /**
1200  * gtk_about_dialog_get_logo:
1201  * @about: a #GtkAboutDialog
1202  * 
1203  * Returns the pixbuf displayed as logo in the about dialog.
1204  * 
1205  * Return value: the pixbuf displayed as logo. The pixbuf is
1206  *   owned by the about dialog. If you want to keep a reference
1207  *   to it, you have to call g_object_ref() on it.
1208  *
1209  * Since: 2.6
1210  **/
1211 GdkPixbuf *
1212 gtk_about_dialog_get_logo (GtkAboutDialog *about)
1213 {
1214   GtkAboutDialogPrivate *priv;
1215   
1216   g_return_val_if_fail (GTK_IS_ABOUT_DIALOG (about), NULL);
1217
1218   priv = (GtkAboutDialogPrivate *)about->private_data;
1219
1220   return gtk_image_get_pixbuf (GTK_IMAGE (priv->logo_image));
1221 }
1222
1223 static GtkIconSet *
1224 icon_set_new_from_pixbufs (GList *pixbufs)
1225 {
1226   GtkIconSet *icon_set = gtk_icon_set_new ();
1227   
1228   for (; pixbufs; pixbufs = pixbufs->next)
1229     {
1230       GdkPixbuf *pixbuf = GDK_PIXBUF (pixbufs->data);
1231
1232       GtkIconSource *icon_source = gtk_icon_source_new ();
1233       gtk_icon_source_set_pixbuf (icon_source, pixbuf);
1234       gtk_icon_set_add_source (icon_set, icon_source);
1235       gtk_icon_source_free (icon_source);
1236     }
1237   
1238   return icon_set;
1239 }
1240
1241 /**
1242  * gtk_about_dialog_set_logo:
1243  * @about: a #GtkAboutDialog
1244  * @logo: a #GdkPixbuf, or %NULL
1245  * 
1246  * Sets the pixbuf to be displayed as logo in 
1247  * the about dialog. If it is %NULL, the default
1248  * window icon set with gtk_window_set_default_icon ()
1249  * will be used.
1250  *
1251  * Since: 2.6
1252  **/
1253 void
1254 gtk_about_dialog_set_logo (GtkAboutDialog *about,
1255                            GdkPixbuf      *logo)
1256 {
1257   GtkAboutDialogPrivate *priv;
1258
1259   g_return_if_fail (GTK_IS_ABOUT_DIALOG (about));
1260
1261   priv = (GtkAboutDialogPrivate *)about->private_data;
1262
1263   if (logo != NULL) 
1264     gtk_image_set_from_pixbuf (GTK_IMAGE (priv->logo_image), logo);
1265   else 
1266     {
1267       GList *pixbufs = gtk_window_get_default_icon_list ();
1268
1269       if (pixbufs != NULL)
1270         {
1271           GtkIconSet *icon_set = icon_set_new_from_pixbufs (pixbufs); 
1272           
1273           gtk_image_set_from_icon_set (GTK_IMAGE (priv->logo_image),
1274                                        icon_set, GTK_ICON_SIZE_DIALOG);
1275           
1276           gtk_icon_set_unref (icon_set);
1277           g_list_free (pixbufs);
1278         }
1279     }
1280
1281   g_object_notify (G_OBJECT (about), "logo");
1282 }
1283
1284 static void
1285 activate_url (GtkWidget *widget, 
1286               gpointer   data)
1287 {
1288   GtkAboutDialog *about = GTK_ABOUT_DIALOG (data);
1289   gchar *url = g_object_get_data (G_OBJECT (widget), "url");
1290   
1291   if (activate_url_hook != NULL)
1292     (* activate_url_hook) (about, url, activate_url_hook_data);
1293 }
1294
1295 static void
1296 set_link_button_text (GtkWidget *about,
1297                       GtkWidget *button, 
1298                       gchar     *text)
1299 {
1300   GtkWidget *label;
1301   gchar *link;
1302   GdkColor *style_link_color;
1303   GdkColor link_color = { 0, 0, 0, 0xffff };
1304
1305   gtk_widget_ensure_style (about);
1306   gtk_widget_style_get (about, "link_color", &style_link_color, NULL);
1307   if (style_link_color)
1308     {
1309       link_color = *style_link_color;
1310       gdk_color_free (style_link_color);
1311     }
1312
1313   link = g_markup_printf_escaped ("<span foreground=\"#%04x%04x%04x\" underline=\"single\">%s</span>", 
1314                                   link_color.red, link_color.green, link_color.blue, text);
1315
1316   label = gtk_bin_get_child (GTK_BIN (button));  
1317   gtk_label_set_markup (GTK_LABEL (label), link);
1318   g_free (link);
1319 }
1320
1321 static GtkWidget *
1322 create_link_button (GtkWidget *about,
1323                     gchar     *text,
1324                     gchar     *url, 
1325                     GCallback  callback, 
1326                     gpointer   data)
1327 {
1328   GtkWidget *button;
1329
1330   button = gtk_button_new_with_label ("");
1331   GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
1332   gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
1333
1334   g_object_set_data_full (G_OBJECT (button), "url", g_strdup (url), g_free);
1335   set_link_button_text (about, button, text);
1336   
1337   g_signal_connect (G_OBJECT (button), "clicked", callback, data);
1338
1339   return button;
1340 }
1341
1342 static void
1343 follow_if_link (GtkAboutDialog *about,
1344                 GtkTextIter    *iter)
1345 {
1346   GSList *tags = NULL, *tagp = NULL;
1347
1348   tags = gtk_text_iter_get_tags (iter);
1349   for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
1350     {
1351       GtkTextTag *tag = tagp->data;
1352       gchar *email = g_object_get_data (G_OBJECT (tag), "email");
1353       gchar *url = g_object_get_data (G_OBJECT (tag), "url");
1354
1355       if (email != NULL && activate_email_hook != NULL)
1356         {
1357           (* activate_email_hook) (about, email, activate_email_hook_data);
1358           break;
1359         }
1360
1361       if (url != NULL && activate_url_hook != NULL)
1362         {
1363           (* activate_url_hook) (about, url, activate_url_hook_data);
1364           break;
1365         }
1366     }
1367
1368   if (tags) 
1369     g_slist_free (tags);
1370 }
1371
1372 static gboolean
1373 credits_key_press_event (GtkWidget      *text_view,
1374                          GdkEventKey    *event,
1375                          GtkAboutDialog *about)
1376 {
1377   GtkTextIter iter;
1378   GtkTextBuffer *buffer;
1379
1380   switch (event->keyval)
1381     {
1382       case GDK_Return: 
1383       case GDK_KP_Enter:
1384         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
1385         gtk_text_buffer_get_iter_at_mark (buffer, &iter, 
1386                                           gtk_text_buffer_get_insert (buffer));
1387         follow_if_link (about, &iter);
1388         break;
1389
1390       default:
1391         break;
1392     }
1393
1394   return FALSE;
1395 }
1396
1397 static gboolean
1398 credits_event_after (GtkWidget      *text_view,
1399                      GdkEvent       *event,
1400                      GtkAboutDialog *about)
1401 {
1402   GtkTextIter start, end, iter;
1403   GtkTextBuffer *buffer;
1404   GdkEventButton *button_event;
1405   gint x, y;
1406
1407   if (event->type != GDK_BUTTON_RELEASE)
1408     return FALSE;
1409
1410   button_event = (GdkEventButton *)event;
1411
1412   if (button_event->button != 1)
1413     return FALSE;
1414
1415   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
1416
1417   /* we shouldn't follow a link if the user has selected something */
1418   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
1419   if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
1420     return FALSE;
1421
1422   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
1423                                          GTK_TEXT_WINDOW_WIDGET,
1424                                          button_event->x, button_event->y, &x, &y);
1425
1426   gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
1427
1428   follow_if_link (about, &iter);
1429
1430   return FALSE;
1431 }
1432
1433 static void
1434 set_cursor_if_appropriate (GtkAboutDialog *about,
1435                            GtkTextView    *text_view,
1436                            gint            x,
1437                            gint            y)
1438 {
1439   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
1440   GSList *tags = NULL, *tagp = NULL;
1441   GtkTextBuffer *buffer;
1442   GtkTextIter iter;
1443   gboolean hovering_over_link = FALSE;
1444
1445   buffer = gtk_text_view_get_buffer (text_view);
1446
1447   gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
1448   
1449   tags = gtk_text_iter_get_tags (&iter);
1450   for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
1451     {
1452       GtkTextTag *tag = tagp->data;
1453       gchar *email = g_object_get_data (G_OBJECT (tag), "email");
1454       gchar *url = g_object_get_data (G_OBJECT (tag), "url");
1455
1456       if (email != NULL || url != NULL) 
1457         {
1458           hovering_over_link = TRUE;
1459           break;
1460         }
1461     }
1462
1463   if (hovering_over_link != priv->hovering_over_link)
1464     {
1465       priv->hovering_over_link = hovering_over_link;
1466
1467       if (hovering_over_link)
1468         gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->hand_cursor);
1469       else
1470         gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->regular_cursor);
1471     }
1472
1473   if (tags) 
1474     g_slist_free (tags);
1475 }
1476
1477 static gboolean
1478 credits_motion_notify_event (GtkWidget *text_view,
1479                              GdkEventMotion *event,
1480                              GtkAboutDialog *about)
1481 {
1482   gint x, y;
1483
1484   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
1485                                          GTK_TEXT_WINDOW_WIDGET,
1486                                          event->x, event->y, &x, &y);
1487
1488   set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), x, y);
1489
1490   gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
1491
1492   return FALSE;
1493 }
1494
1495
1496 static gboolean
1497 credits_visibility_notify_event (GtkWidget          *text_view,
1498                                  GdkEventVisibility *event,
1499                                  GtkAboutDialog     *about)
1500 {
1501   gint wx, wy, bx, by;
1502
1503   gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
1504
1505   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
1506                                          GTK_TEXT_WINDOW_WIDGET,
1507                                          wx, wy, &bx, &by);
1508
1509   set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), bx, by);
1510
1511   return FALSE;
1512 }
1513
1514 static void
1515 text_view_style_set (GtkWidget *widget, GtkStyle *prev_style, GtkWidget *text_view)
1516 {
1517   gtk_widget_modify_base (text_view, GTK_STATE_NORMAL,
1518                           &widget->style->bg[GTK_STATE_NORMAL]);
1519 }
1520
1521 static void
1522 add_credits_page (GtkAboutDialog *about, 
1523                   GtkWidget      *notebook,
1524                   gchar          *title,
1525                   gchar         **people)
1526 {
1527   gchar **p;
1528   gchar *q0, *q1, *q2, *r1, *r2;
1529   GtkWidget *sw, *view;
1530   GtkTextBuffer *buffer;
1531   gboolean linkify_email, linkify_urls;
1532   GdkColor *style_link_color;
1533   GdkColor link_color = { 0, 0, 0, 0xffff };
1534
1535   linkify_email = (activate_email_hook != NULL);
1536   linkify_urls = (activate_url_hook != NULL);
1537
1538   gtk_widget_ensure_style (GTK_WIDGET (about));
1539   gtk_widget_style_get (GTK_WIDGET (about), "link_color", &style_link_color, NULL);
1540   if (style_link_color)
1541     {
1542       link_color = *style_link_color;
1543       gdk_color_free (style_link_color);
1544     }
1545
1546   view = gtk_text_view_new ();
1547   g_signal_connect (about, "style_set",
1548                     G_CALLBACK (text_view_style_set), view);
1549   
1550   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
1551   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
1552   gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
1553   gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 8);
1554   gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 8);
1555
1556   g_signal_connect (G_OBJECT (view), "key-press-event",
1557                     G_CALLBACK (credits_key_press_event), about);
1558   g_signal_connect (G_OBJECT (view), "event-after",
1559                     G_CALLBACK (credits_event_after), about);
1560   g_signal_connect (G_OBJECT (view), "motion-notify-event", 
1561                     G_CALLBACK (credits_motion_notify_event), about);
1562   g_signal_connect (G_OBJECT (view), "visibility-notify-event", 
1563                     G_CALLBACK (credits_visibility_notify_event), about);
1564
1565   sw = gtk_scrolled_window_new (NULL, NULL);
1566   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1567                                   GTK_POLICY_AUTOMATIC,
1568                                   GTK_POLICY_AUTOMATIC);
1569   gtk_container_add (GTK_CONTAINER (sw), view);
1570   
1571   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), 
1572                             sw, gtk_label_new (title));
1573
1574   if (people == NULL) 
1575     {
1576       gtk_widget_hide (view);
1577       return;
1578     }
1579   else 
1580     gtk_widget_show (view);
1581   
1582   for (p = people; *p; p++) 
1583     {
1584       q0  = *p;
1585       while (*q0) 
1586         {
1587           q1 = linkify_email ? strchr (q0, '<') : NULL;
1588           q2 = q1 ? strchr (q1, '>') : NULL;
1589           r1 = linkify_urls ? strstr (q0, "http://") : NULL;
1590           if (r1)
1591             {
1592               r2 = strpbrk (r1, " \n\t");
1593               if (!r2)
1594                 r2 = strchr (r1, '\0');
1595             }
1596           else  
1597             r2 = NULL;
1598
1599           if (r1 && r2 && (!q1 || !q2 || (r1 < q1))) 
1600             {
1601               q1 = r1;
1602               q2 = r2;
1603             }
1604
1605           if (q1 && q2) 
1606             {
1607               GtkTextIter end;
1608               gchar *link;
1609               gchar *link_type;
1610               GtkTextTag *tag;
1611               
1612               gtk_text_buffer_insert_at_cursor (buffer, q0, q1 - q0);
1613               gtk_text_buffer_get_end_iter (buffer, &end);
1614
1615               q0 = q2;
1616
1617               if (*q1 == '<') 
1618                 {
1619                   q1++;
1620                   q0++;
1621                   link_type = "email";
1622                 }
1623               else 
1624                 link_type = "url";
1625               
1626               link = g_strndup (q1, q2 - q1);
1627               tag = gtk_text_buffer_create_tag (buffer, NULL, 
1628                                                 "foreground_gdk", &link_color, 
1629                                                 "underline", PANGO_UNDERLINE_SINGLE, 
1630                                                 NULL);
1631               g_object_set_data_full (G_OBJECT (tag), link_type, g_strdup (link), g_free);
1632               gtk_text_buffer_insert_with_tags (buffer, &end, link, -1, tag, NULL);
1633
1634               g_free (link);
1635             }
1636           else
1637             {
1638               gtk_text_buffer_insert_at_cursor (buffer, q0, -1);
1639               break;
1640             }
1641         }
1642       
1643       if (p[1])
1644         gtk_text_buffer_insert_at_cursor (buffer, "\n", 1);
1645     }
1646 }
1647
1648 static void
1649 display_credits_dialog (GtkWidget *button, 
1650                         gpointer   data)
1651 {
1652   GtkAboutDialog *about = (GtkAboutDialog *)data;
1653   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
1654   GtkWidget *dialog, *notebook;
1655
1656   if (priv->credits_dialog != NULL)
1657     {
1658       gtk_window_present (GTK_WINDOW (priv->credits_dialog));
1659       return;
1660     }
1661         
1662   dialog = gtk_dialog_new_with_buttons (_("Credits"),
1663                                         GTK_WINDOW (about),
1664                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1665                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1666                                         NULL);
1667   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1668   
1669   priv->credits_dialog = dialog;
1670   gtk_window_set_default_size (GTK_WINDOW (dialog), 360, 260);
1671   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
1672
1673   gtk_window_set_modal (GTK_WINDOW (dialog), 
1674                         gtk_window_get_modal (GTK_WINDOW (about)));
1675
1676   g_signal_connect (dialog, "response",
1677                     G_CALLBACK (gtk_widget_destroy), dialog);
1678   g_signal_connect (dialog, "destroy",
1679                     G_CALLBACK (gtk_widget_destroyed),
1680                     &(priv->credits_dialog));
1681
1682   notebook = gtk_notebook_new ();
1683   gtk_container_set_border_width (GTK_CONTAINER (notebook), 8);
1684   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), notebook, TRUE, TRUE, 0);
1685
1686   if (priv->authors != NULL) 
1687     add_credits_page (about, notebook, _("Written by"), priv->authors);
1688   
1689   if (priv->documenters != NULL)
1690     add_credits_page (about, notebook, _("Documented by"), priv->documenters);
1691     
1692   /* Don't show an untranslated gettext msgid */
1693   if (priv->translator_credits != NULL &&
1694       strcmp (priv->translator_credits, "translator_credits") &&
1695       strcmp (priv->translator_credits, "translator-credits")) 
1696     {
1697       gchar *translators[2];
1698       
1699       translators[0] = priv->translator_credits;
1700       translators[1] = NULL;
1701
1702       add_credits_page (about, notebook, _("Translated by"), translators);
1703     }
1704
1705   if (priv->artists != NULL) 
1706     add_credits_page (about, notebook, _("Artwork by"), priv->artists);
1707   
1708   gtk_widget_show_all (dialog);
1709 }
1710
1711 static void
1712 display_license_dialog (GtkWidget *button, 
1713                         gpointer   data)
1714 {
1715   GtkAboutDialog *about = (GtkAboutDialog *)data;
1716   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
1717   GtkWidget *dialog, *view, *sw;
1718
1719   if (priv->license_dialog != NULL)
1720     {
1721       gtk_window_present (GTK_WINDOW (priv->license_dialog));
1722       return;
1723     }
1724         
1725   dialog = gtk_dialog_new_with_buttons (_("License"),
1726                                         GTK_WINDOW (about),
1727                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1728                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1729                                         NULL);
1730   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1731   priv->license_dialog = dialog;
1732   gtk_window_set_default_size (GTK_WINDOW (dialog), 420, 320);
1733   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
1734
1735   gtk_window_set_modal (GTK_WINDOW (dialog), 
1736                         gtk_window_get_modal (GTK_WINDOW (about)));
1737
1738   g_signal_connect (dialog, "response",
1739                     G_CALLBACK (gtk_widget_destroy), dialog);
1740   g_signal_connect (dialog, "destroy",
1741                     G_CALLBACK (gtk_widget_destroyed),
1742                     &(priv->license_dialog));
1743
1744   sw = gtk_scrolled_window_new (NULL, NULL);
1745   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
1746                                        GTK_SHADOW_IN);
1747   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1748                                   GTK_POLICY_AUTOMATIC,
1749                                   GTK_POLICY_AUTOMATIC);
1750   gtk_container_set_border_width (GTK_CONTAINER (sw), 8);
1751   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), sw, TRUE, TRUE, 0);
1752
1753   view = gtk_text_view_new ();
1754   gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)), 
1755                             priv->license, -1);
1756
1757   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
1758   gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
1759   gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 8);
1760   gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 8);
1761
1762   gtk_container_add (GTK_CONTAINER (sw), view);
1763
1764   gtk_widget_show_all (dialog);
1765 }
1766
1767 static void 
1768 close_cb (GtkAboutDialog *about)
1769 {
1770   GtkAboutDialogPrivate *priv = (GtkAboutDialogPrivate *)about->private_data;
1771
1772   if (priv->license_dialog != NULL)
1773     {
1774       gtk_widget_destroy (priv->license_dialog);
1775       priv->license_dialog = NULL;
1776     }
1777
1778   if (priv->credits_dialog != NULL)
1779     {
1780       gtk_widget_destroy (priv->credits_dialog);
1781       priv->credits_dialog = NULL;
1782     }
1783
1784   gtk_widget_hide (GTK_WIDGET (about));
1785   
1786 }
1787
1788 /**
1789  * gtk_about_dialog_new:
1790  *
1791  * Creates a new #GtkAboutDialog.
1792  *
1793  * Returns: a newly created #GtkAboutDialog
1794  *
1795  * Since: 2.6
1796  */
1797 GtkWidget *
1798 gtk_about_dialog_new (void)
1799 {
1800   GtkAboutDialog *dialog = GTK_ABOUT_DIALOG (g_object_new (GTK_TYPE_ABOUT_DIALOG, NULL));
1801
1802   /* force defaults */
1803   gtk_about_dialog_set_name (dialog, NULL);
1804   gtk_about_dialog_set_logo (dialog, NULL);
1805
1806   /* Close dialog on user response */
1807   g_signal_connect (G_OBJECT (dialog),
1808                     "response",
1809                     G_CALLBACK (close_cb),
1810                     NULL);
1811
1812   return GTK_WIDGET (dialog);
1813 }
1814
1815 /**
1816  * gtk_about_dialog_set_email_hook:
1817  * @func: a function to call when an email link is activated.
1818  * @data: data to pass to @func
1819  * @destroy: #GDestroyNotify for @data
1820  * 
1821  * Installs a global function to be called whenever the user activates an
1822  * email link in an about dialog. 
1823  * 
1824  * Return value: the previous email hook.
1825  *
1826  * Since: 2.6
1827  */
1828 GtkAboutDialogActivateLinkFunc      
1829 gtk_about_dialog_set_email_hook (GtkAboutDialogActivateLinkFunc func, 
1830                                  gpointer                       data, 
1831                                  GDestroyNotify                 destroy)
1832 {
1833   GtkAboutDialogActivateLinkFunc old;
1834
1835   if (activate_email_hook_destroy != NULL)
1836     (* activate_email_hook_destroy) (activate_url_hook_data);
1837
1838   old = activate_email_hook;
1839
1840   activate_email_hook = func;
1841   activate_email_hook_data = data;
1842   activate_email_hook_destroy = destroy;
1843
1844   return old;
1845 }
1846
1847 /**
1848  * gtk_about_dialog_set_url_hook:
1849  * @func: a function to call when a URL link is activated.
1850  * @data: data to pass to @func
1851  * @destroy: #GDestroyNotify for @data
1852  * 
1853  * Installs a global function to be called whenever the user activates a
1854  * URL link in an about dialog.
1855  * 
1856  * Return value: the previous URL hook.
1857  *
1858  * Since: 2.6
1859  */
1860 GtkAboutDialogActivateLinkFunc      
1861 gtk_about_dialog_set_url_hook (GtkAboutDialogActivateLinkFunc func, 
1862                                gpointer                       data, 
1863                                GDestroyNotify                 destroy)
1864 {
1865   GtkAboutDialogActivateLinkFunc old;
1866
1867   if (activate_url_hook_destroy != NULL)
1868     (* activate_url_hook_destroy) (activate_email_hook_data);
1869
1870   old = activate_url_hook;
1871
1872   activate_url_hook = func;
1873   activate_url_hook_data = data;
1874   activate_url_hook_destroy = destroy;
1875
1876   return old;
1877 }
1878
1879 /**
1880  * gtk_show_about_dialog:
1881  * @parent: transient parent, or %NULL for none
1882  * @first_property_name: the name of the first property 
1883  * @Varargs: value of first property, followed by more properties, %NULL-terminated
1884  *
1885  * This is a convenience function for showing an application's about box.
1886  *
1887  * Since: 2.6
1888  */
1889 void
1890 gtk_show_about_dialog (GtkWindow   *parent,
1891                        const gchar *first_property_name,
1892                        ...)
1893 {
1894   static GtkWidget *global_about_dialog = NULL;
1895   GtkWidget *dialog = NULL;
1896   va_list var_args;
1897
1898   if (parent)
1899     dialog = g_object_get_data (G_OBJECT (parent), "gtk-about-dialog");
1900   else 
1901     dialog = global_about_dialog;
1902
1903   if (!dialog) 
1904     {
1905       dialog = gtk_about_dialog_new ();
1906
1907       g_object_ref (G_OBJECT (dialog));
1908       gtk_object_sink (GTK_OBJECT (dialog));
1909
1910       g_signal_connect (dialog, "delete_event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
1911
1912       va_start (var_args, first_property_name);
1913       g_object_set_valist (G_OBJECT (dialog), first_property_name, var_args);
1914       va_end (var_args);
1915
1916       if (parent) 
1917         {
1918           gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
1919           g_object_set_data_full (G_OBJECT (parent), "gtk-about-dialog", 
1920                                   dialog, g_object_unref);
1921         }
1922       else 
1923         global_about_dialog = dialog;
1924       
1925     }
1926   
1927   gtk_window_present (GTK_WINDOW (dialog));
1928 }