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