2007-07-19 [paul] 2.10.0cvs46
[claws.git] / src / gtk / about.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkwidget.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtksignal.h>
32 #include <gtk/gtkvbox.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkhseparator.h>
36 #include <gtk/gtkscrolledwindow.h>
37 #include <gtk/gtktext.h>
38 #include <gtk/gtkbutton.h>
39 #if HAVE_SYS_UTSNAME_H
40 #  include <sys/utsname.h>
41 #endif
42 #include <errno.h>
43
44 #include "about.h"
45 #include "gtkutils.h"
46 #include "stock_pixmap.h"
47 #include "prefs_common.h"
48 #include "utils.h"
49 #include "version.h"
50 #include "authors.h"
51 #include "codeconv.h"
52 #include "menu.h"
53 #include "textview.h"
54
55 static GtkWidget *window;
56 static gchar* uri_hover = NULL;
57 static GtkTextIter uri_hover_start_iter;
58 static GtkTextIter uri_hover_end_iter;
59 static GdkCursor *hand_cursor = NULL;
60 static GdkCursor *text_cursor = NULL;
61
62 static void about_create(void);
63 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event);
64 static gboolean about_textview_uri_clicked(GtkTextTag *tag, GObject *obj,
65                                         GdkEvent *event, GtkTextIter *iter,
66                                         GtkWidget *textview);
67 static void about_open_link_cb(GtkWidget *widget, guint action, void *data);
68 static void about_copy_link_cb(GtkWidget *widget, guint action, void *data);
69 static gboolean about_textview_motion_notify(GtkWidget *widget,
70                                         GdkEventMotion *event,
71                                         GtkWidget *textview);
72 static gboolean about_textview_leave_notify(GtkWidget *widget,
73                                         GdkEventCrossing *event,
74                                         GtkWidget *textview);
75 static void about_textview_uri_update(GtkWidget *textview, gint x, gint y);
76
77 static GtkItemFactoryEntry textview_link_popup_entries[] = 
78 {
79         {N_("/_Open with Web browser"), NULL, about_open_link_cb, 0, NULL},
80         {N_("/Copy this _link"),        NULL, about_copy_link_cb, 0, NULL},
81 };
82
83 static GtkWidget *link_popupmenu;
84
85
86 void about_show(void)
87 {
88         if (!window)
89                 about_create();
90         else
91                 gtk_window_present(GTK_WINDOW(window));
92 }
93
94 static GtkWidget *about_create_child_page_info(void)
95 {
96         GtkWidget *scrolledwin;
97         GtkWidget *text;
98         GtkTextBuffer *buffer;
99         GtkTextIter iter;
100         GdkColor uri_color;
101         gchar buf[1024];
102         GtkTextTag *tag;
103 #if HAVE_SYS_UTSNAME_H
104         struct utsname utsbuf;
105 #endif
106
107         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
108         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
109                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
110         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
111                                             GTK_SHADOW_IN);
112
113         text = gtk_text_view_new();
114         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
115         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
116         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
117         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
118         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
119         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
120         gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK);
121
122         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
123         gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
124
125         /* textview link style (based upon main prefs) */
126         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
127                                 (GdkColor*)&uri_color);
128         tag = gtk_text_buffer_create_tag(buffer, "link",
129                                 "foreground-gdk", &uri_color,
130                                 "wrap-mode", GTK_WRAP_NONE,
131                                 NULL);
132         gtk_text_buffer_create_tag(buffer, "link-hover",
133                                 "foreground-gdk", &uri_color,
134                                 "underline", PANGO_UNDERLINE_SINGLE,
135                                 NULL);
136
137         gtk_text_buffer_insert(buffer, &iter, _(
138                                 "Claws Mail is a lightweight, fast and "
139                                 "highly-configurable email client.\n\n"
140                                 "For further information visit the Claws Mail "
141                                 "website:\n"), -1);
142         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, HOMEPAGE_URI, -1,
143                                 "link", NULL);
144         gtk_text_buffer_insert(buffer, &iter, _("\n\n"
145                                 "Claws Mail is free software released "
146                                 "under the GPL license. If you wish to donate "
147                                 "to the Claws Mail project you can do "
148                                 "so at:\n"), -1);
149         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, DONATE_URI, -1,
150                                 "link", NULL);
151
152         gtk_text_buffer_create_tag(buffer, "indented-list-item",
153                                 "indent", 8,
154                                 NULL);
155         gtk_text_buffer_create_tag(buffer, "underlined-list-title",
156                                 "underline", PANGO_UNDERLINE_SINGLE,
157                                 NULL);
158 #ifdef MAEMO
159         gtk_text_buffer_insert(buffer, &iter, _(
160                                 "\n\nCopyright (C) 1999-2007\nHiroyuki Yamamoto <hiro-y@kcn.ne.jp>\n"
161                                 "and the Claws Mail team"), -1);
162 #endif
163         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("\n\nSystem Information\n")), -1,
164                         "underlined-list-title", NULL);
165
166 #if HAVE_SYS_UTSNAME_H
167         uname(&utsbuf);
168         g_snprintf(buf, sizeof(buf),
169                    _("GTK+ %d.%d.%d / GLib %d.%d.%d\n"
170                      "Locale: %s (charset: %s)\n"
171                      "Operating System: %s %s (%s)"),
172                    gtk_major_version, gtk_minor_version, gtk_micro_version,
173                    glib_major_version, glib_minor_version, glib_micro_version,
174                    conv_get_current_locale(), conv_get_locale_charset_str(),
175                    utsbuf.sysname, utsbuf.release, utsbuf.machine);
176 #elif defined(G_OS_WIN32)
177         g_snprintf(buf, sizeof(buf),
178                    _("GTK+ %d.%d.%d / GLib %d.%d.%d\n"
179                      "Locale: %s (charset: %s)\n"
180                      "Operating System: %s"),
181                    gtk_major_version, gtk_minor_version, gtk_micro_version,
182                    glib_major_version, glib_minor_version, glib_micro_version,
183                    conv_get_current_locale(), conv_get_locale_charset_str(),
184                    "Win32");
185 #else
186         g_snprintf(buf, sizeof(buf),
187                    _("GTK+ %d.%d.%d / GLib %d.%d.%d\n"
188                      "Locale: %s (charset: %s)\n"
189                      "Operating System: unknown"),
190                    gtk_major_version, gtk_minor_version, gtk_micro_version,
191                    glib_major_version, glib_minor_version, glib_micro_version,
192                    conv_get_current_locale(), conv_get_locale_charset_str());
193 #endif
194
195         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1,
196                                                  "indented-list-item", NULL);
197
198         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
199
200         g_signal_connect(G_OBJECT(tag), "event",
201                                 G_CALLBACK(about_textview_uri_clicked), text);
202         g_signal_connect(G_OBJECT(text), "motion-notify-event",
203                                 G_CALLBACK(about_textview_motion_notify), text);
204         g_signal_connect(G_OBJECT(text), "leave-notify-event",
205                                 G_CALLBACK(about_textview_leave_notify), text);
206
207         return scrolledwin;
208 }
209
210 static GtkWidget *about_create_child_page_authors(void)
211 {
212         GtkWidget *scrolledwin;
213         GtkWidget *text;
214         GtkTextBuffer *buffer;
215         GtkTextIter iter;
216         gint i;
217
218         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
219         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
220                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
221         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
222                                             GTK_SHADOW_IN);
223
224         text = gtk_text_view_new();
225         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
226         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
227         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
228         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
229         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
230         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
231         gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK);
232
233         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
234         gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
235
236         /* init formatting tag: indentation  for list items */
237         gtk_text_buffer_create_tag(buffer, "indented-list-item",
238                                 "indent", 8,
239                                 NULL);
240         gtk_text_buffer_create_tag(buffer, "underlined-list-title",
241                                 "underline", PANGO_UNDERLINE_SINGLE,
242                                 NULL);
243
244         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("The Claws Mail Team")), -1,
245                         "underlined-list-title", NULL);
246         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
247
248         for (i = 0; TEAM_LIST[i] != NULL; i++) {
249                 if (g_utf8_validate(TEAM_LIST[i], -1, NULL))
250                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, TEAM_LIST[i], -1,
251                                         "indented-list-item", NULL);
252                 else {
253                         gchar *conv = conv_codeset_strdup(TEAM_LIST[i], CS_ISO_8859_1, CS_UTF_8);
254                         if (conv)
255                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
256                                                 "indented-list-item", NULL);
257                         g_free(conv);
258                 }
259                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
260         }
261
262         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
263         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("Previous team members")), -1,
264                         "underlined-list-title", NULL);
265         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
266
267         for (i = 0; EX_TEAM_LIST[i] != NULL; i++) {
268                 if (g_utf8_validate(EX_TEAM_LIST[i], -1, NULL))
269                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, EX_TEAM_LIST[i], -1,
270                                         "indented-list-item", NULL);
271                 else {
272                         gchar *conv = conv_codeset_strdup(EX_TEAM_LIST[i], CS_ISO_8859_1, CS_UTF_8);
273                         if (conv)
274                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
275                                                 "indented-list-item", NULL);
276                         g_free(conv);
277                 }
278                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
279         }
280
281         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
282         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("The translation team")), -1,
283                         "underlined-list-title", NULL);
284         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
285
286         for (i = 0; TRANS_TEAM_LIST[i] != NULL; i++) {
287                 if (g_utf8_validate(TRANS_TEAM_LIST[i], -1, NULL))
288                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, TRANS_TEAM_LIST[i], -1,
289                                         "indented-list-item", NULL);
290                 else {
291                         gchar *conv = conv_codeset_strdup(TRANS_TEAM_LIST[i], CS_ISO_8859_1, CS_UTF_8);
292                         if (conv)
293                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
294                                                 "indented-list-item", NULL);
295                         g_free(conv);
296                 }
297                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
298         }
299
300         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
301         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("Documentation team")), -1,
302                         "underlined-list-title", NULL);
303         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
304
305         for (i = 0; DOC_TEAM_LIST[i] != NULL; i++) {
306                 if (g_utf8_validate(DOC_TEAM_LIST[i], -1, NULL))
307                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, DOC_TEAM_LIST[i], -1,
308                                         "indented-list-item", NULL);
309                 else {
310                         gchar *conv = conv_codeset_strdup(DOC_TEAM_LIST[i], CS_ISO_8859_1, CS_UTF_8);
311                         if (conv)
312                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
313                                                 "indented-list-item", NULL);
314                         g_free(conv);
315                 }
316                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
317         }
318
319         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
320         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("Logo")), -1,
321                         "underlined-list-title", NULL);
322         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
323
324         for (i = 0; LOGO_LIST[i] != NULL; i++) {
325                 if (g_utf8_validate(LOGO_LIST[i], -1, NULL))
326                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, LOGO_LIST[i], -1,
327                                         "indented-list-item", NULL);
328                 else {
329                         gchar *conv = conv_codeset_strdup(LOGO_LIST[i], CS_ISO_8859_1, CS_UTF_8);
330                         if (conv)
331                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
332                                                 "indented-list-item", NULL);
333                         g_free(conv);
334                 }
335                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
336         }
337
338         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
339         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("Icons")), -1,
340                         "underlined-list-title", NULL);
341         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
342
343         for (i = 0; ICONS_LIST[i] != NULL; i++) {
344                 if (g_utf8_validate(ICONS_LIST[i], -1, NULL))
345                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, ICONS_LIST[i], -1,
346                                         "indented-list-item", NULL);
347                 else {
348                         gchar *conv = conv_codeset_strdup(ICONS_LIST[i], CS_ISO_8859_1, CS_UTF_8);
349                         if (conv)
350                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
351                                                 "indented-list-item", NULL);
352                         g_free(conv);
353                 }
354                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
355         }
356
357         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
358         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (_("Contributors")), -1,
359                         "underlined-list-title", NULL);
360         gtk_text_buffer_insert(buffer, &iter, "\n", 1);
361
362         for (i = 0; CONTRIBS_LIST[i] != NULL; i++) {
363                 if (g_utf8_validate(CONTRIBS_LIST[i], -1, NULL))
364                         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, CONTRIBS_LIST[i], -1,
365                                         "indented-list-item", NULL);
366                 else {
367                         gchar *conv = conv_codeset_strdup(CONTRIBS_LIST[i], CS_ISO_8859_1, CS_UTF_8);
368                         if (conv)
369                                 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, conv, -1,
370                                                 "indented-list-item", NULL);
371                         g_free(conv);
372                 }
373                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
374         }
375
376         return scrolledwin;
377 }
378
379 static GtkWidget *about_create_child_page_features(void)
380 {
381         GtkWidget *scrolledwin;
382         GtkWidget *text;
383         GtkTextBuffer *buffer;
384         GtkTextIter iter;
385         GdkPixbuf *active_pixbuf;
386         GdkPixbuf *inactive_pixbuf;
387
388         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
389         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
390                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
391         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
392                                             GTK_SHADOW_IN);
393
394         text = gtk_text_view_new();
395         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
396         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
397         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
398         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
399         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
400         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
401         gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK);
402
403         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
404         gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
405
406         gtk_text_buffer_insert(buffer, &iter, _("Compiled-in Features\n"), -1);
407
408         gtk_text_buffer_create_tag(buffer, "bold", "weight", PANGO_WEIGHT_BOLD,
409                                    NULL);
410
411         stock_pixbuf_gdk(window, STOCK_PIXMAP_CHECKBOX_ON, &active_pixbuf);
412         stock_pixbuf_gdk(window, STOCK_PIXMAP_CHECKBOX_OFF, &inactive_pixbuf);
413
414 #if INET6
415         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
416 #else
417         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
418 #endif
419         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" IPv6 "), -1,
420                                                  "bold", NULL);
421         gtk_text_buffer_insert(buffer, &iter, 
422                 (gchar *)Q_("IPv6|adds support for IPv6 addresses, the new internet "
423                             "addressing protocol\n"), -1);
424
425 #if HAVE_ICONV
426         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
427 #else
428         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
429 #endif
430         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" iconv "), -1,
431                                                  "bold", NULL);
432         gtk_text_buffer_insert(buffer, &iter, 
433                 (gchar *)Q_("iconv|allows converting to and from different character sets\n"), -1);
434
435 #if HAVE_LIBCOMPFACE
436         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
437 #else
438         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
439 #endif
440         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" compface "), -1,
441                                                  "bold", NULL);
442         gtk_text_buffer_insert(buffer, &iter, 
443                 (gchar *)Q_("compface|adds support for the X-Face header\n"), -1);
444
445 #if USE_OPENSSL
446         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
447 #else
448         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
449 #endif
450         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" OpenSSL "), -1,
451                                                  "bold", NULL);
452         gtk_text_buffer_insert(buffer, &iter, 
453                 (gchar *)Q_("OpenSSL|adds support for encrypted connections to servers\n"), -1);
454
455 #if USE_LDAP
456         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
457 #else
458         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
459 #endif
460         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" LDAP "), -1,
461                                                  "bold", NULL);
462         gtk_text_buffer_insert(buffer, &iter, 
463                 (gchar *)Q_("LDAP|adds support for LDAP shared addressbooks\n"), -1);
464
465 #if USE_JPILOT
466         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
467 #else
468         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
469 #endif
470         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" JPilot "), -1,
471                                                  "bold", NULL);
472         gtk_text_buffer_insert(buffer, &iter, 
473                 (gchar *)Q_("JPilot|adds support for PalmOS addressbooks\n"), -1);
474
475 #if USE_ASPELL
476         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
477 #else
478         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
479 #endif
480         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" GNU/aspell "), -1,
481                                                  "bold", NULL);
482         gtk_text_buffer_insert(buffer, &iter, 
483                 (gchar *)Q_("GNU/aspell|adds support for spell checking\n"), -1);
484
485 #if HAVE_LIBETPAN
486         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
487 #else
488         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
489 #endif
490         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" libetpan "), -1,
491                                                  "bold", NULL);
492         gtk_text_buffer_insert(buffer, &iter, 
493                 (gchar *)Q_("libetpan|adds support for IMAP servers\n"), -1);
494
495 #if USE_GNOMEPRINT
496         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
497 #else
498         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
499 #endif
500         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" libgnomeprint "), -1,
501                                                  "bold", NULL);
502         gtk_text_buffer_insert(buffer, &iter, 
503                 (gchar *)Q_("libgnomeprint|adds support for a complete print dialog\n"), -1);
504
505 #if HAVE_LIBSM
506         gtk_text_buffer_insert_pixbuf(buffer, &iter, active_pixbuf);
507 #else
508         gtk_text_buffer_insert_pixbuf(buffer, &iter, inactive_pixbuf);
509 #endif
510         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, (" libSM "), -1,
511                                                  "bold", NULL);
512         gtk_text_buffer_insert(buffer, &iter, 
513                 (gchar *)Q_("libSM|adds support for session handling\n"), -1);
514
515         return scrolledwin;
516 }
517
518 static GtkWidget *about_create_child_page_license(void)
519 {
520         GtkWidget *scrolledwin;
521         GtkWidget *text;
522         GtkTextBuffer *buffer;
523         GtkTextIter iter;
524         GdkColor uri_color;
525         GtkTextTag *tag;
526
527         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
528         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
529                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
530         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
531                                             GTK_SHADOW_IN);
532
533         text = gtk_text_view_new();
534         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
535         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
536         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
537         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
538         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
539         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
540
541         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
542         gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
543
544         gtk_text_buffer_insert(buffer, &iter,
545                 _("This program is free software; you can redistribute it and/or modify "
546                   "it under the terms of the GNU General Public License as published by "
547                   "the Free Software Foundation; either version 3, or (at your option) "
548                   "any later version.\n\n"), -1);
549
550         gtk_text_buffer_insert(buffer, &iter,
551                 _("This program is distributed in the hope that it will be useful, "
552                   "but WITHOUT ANY WARRANTY; without even the implied warranty of "
553                   "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "
554                   "See the GNU General Public License for more details.\n\n"), -1);
555
556         /* textview link style (based upon main prefs) */
557         gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
558                         (GdkColor*)&uri_color);
559
560         tag = gtk_text_buffer_create_tag(buffer, "link",
561                 "foreground-gdk", &uri_color,
562                 NULL);
563         gtk_text_buffer_create_tag(buffer, "link-hover",
564                 "foreground-gdk", &uri_color,
565                 "underline", PANGO_UNDERLINE_SINGLE,
566                 NULL);
567
568         gtk_text_buffer_insert(buffer, &iter,
569                 _("You should have received a copy of the GNU General Public License "
570                   "along with this program. If not, see <"), -1);
571         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, 
572                 "http://www.gnu.org/licenses/", -1,
573                 "link", NULL);
574         gtk_text_buffer_insert(buffer, &iter, _(">. \n\n"), -1);
575 #ifdef USE_OPENSSL
576         gtk_text_buffer_insert(buffer, &iter,
577                 _("This product includes software developed by the OpenSSL Project "
578                   "for use in the OpenSSL Toolkit ("), -1);
579         gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, OPENSSL_URI, -1,
580                 "link", NULL);
581         gtk_text_buffer_insert(buffer, &iter, _(").\n"), -1);
582
583         g_signal_connect(G_OBJECT(tag), "event",
584                                 G_CALLBACK(about_textview_uri_clicked), text);
585         g_signal_connect(G_OBJECT(text), "motion-notify-event",
586                          G_CALLBACK(about_textview_motion_notify), text);
587         g_signal_connect(G_OBJECT(text), "leave-notify-event",
588                                 G_CALLBACK(about_textview_leave_notify), text);
589 #endif
590
591         return scrolledwin;
592 }
593
594 static gboolean release_notes_available(void)
595 {
596         gboolean ret = FALSE;
597         gchar *path = NULL;
598
599         path = g_strconcat(DOCDIR, G_DIR_SEPARATOR_S, RELEASE_NOTES_FILE, NULL);
600         ret = (is_file_exist(path));
601         g_free(path);
602
603         return ret;
604 }
605
606 static GtkWidget *about_create_child_page_release_notes(void)
607 {
608         GtkWidget *scrolledwin;
609         GtkWidget *text;
610         GtkTextBuffer *buffer;
611         GtkTextIter iter;
612         gchar *path, buf[1024];
613         FILE *fp;
614
615         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
616         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
617                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
618         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
619                         GTK_SHADOW_IN);
620         text = gtk_text_view_new();
621         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
622         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
623         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
624         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
625         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
626         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
627
628         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
629         gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
630
631         path = g_strconcat(DOCDIR, G_DIR_SEPARATOR_S, RELEASE_NOTES_FILE, NULL);
632         if ((fp = g_fopen(path, "rb")) == NULL) {
633                 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
634                 g_free(path);
635                 return scrolledwin;
636         }
637         g_free(path);
638
639         while (fgets(buf, sizeof(buf), fp) != NULL) {
640                 const gchar *src_codeset = conv_get_locale_charset_str();
641                 const gchar *dest_codeset = CS_UTF_8;
642                 gchar *tmp;
643
644                 tmp = conv_codeset_strdup(buf, src_codeset, dest_codeset);
645                 if (!tmp) {
646                         g_warning("Failed to convert character set of action configuration\n");
647                         tmp = g_strdup(buf);
648                 }
649
650                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
651                 g_free(tmp);
652         }
653         fclose(fp);
654
655         return scrolledwin;
656 }
657
658 static void about_create(void)
659 {
660         GtkWidget *vbox1;
661         GtkWidget *image;       
662         GtkWidget *vbox2;
663         GtkWidget *label;
664         GtkWidget *button;
665         GtkWidget *scrolledwin;
666         GtkWidget *notebook;
667         GtkWidget *table;
668         char *markup;
669         GtkWidget *confirm_area;
670         GtkWidget *close_button;
671         static GdkGeometry geometry;
672
673         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "about");
674         gtk_window_set_title(GTK_WINDOW(window), _("About Claws Mail"));
675         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
676         gtk_widget_set_size_request(window, -1, -1);
677         g_signal_connect(G_OBJECT(window), "delete_event",
678                          G_CALLBACK(gtk_widget_hide_on_delete), NULL);
679         g_signal_connect(G_OBJECT(window), "key_press_event",
680                          G_CALLBACK(key_pressed), NULL);
681         gtk_widget_realize(window);
682
683         if (!geometry.min_width) {
684                 geometry.min_width = 450;
685                 geometry.min_height = 500;
686         }
687
688         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
689                                       GDK_HINT_MIN_SIZE);
690
691         vbox1 = gtk_vbox_new(FALSE, 8);
692         gtk_container_add(GTK_CONTAINER(window), vbox1);
693
694         table = gtk_table_new (2, 1, FALSE);
695         gtk_box_pack_start(GTK_BOX(vbox1), table,
696                         FALSE, FALSE, 0);
697         gtk_container_set_border_width (GTK_CONTAINER (table), 8);
698         gtk_table_set_row_spacings (GTK_TABLE (table), 8);
699         gtk_table_set_col_spacings (GTK_TABLE (table), 8);
700
701         image = stock_pixmap_widget(window, STOCK_PIXMAP_CLAWS_MAIL_LOGO);
702         gtk_table_attach (GTK_TABLE (table), image, 0, 1, 0, 1,
703                         (GtkAttachOptions) (GTK_EXPAND),
704                         (GtkAttachOptions) (0), 0, 0);
705
706         vbox2 = gtk_vbox_new (FALSE, 4);
707         gtk_table_attach (GTK_TABLE (table), vbox2, 1, 2, 0, 1,
708                         (GtkAttachOptions) (GTK_EXPAND),
709                         (GtkAttachOptions) (0), 0, 0);
710
711         label = gtk_label_new("");
712         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
713         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
714         gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0);
715         markup = g_markup_printf_escaped
716                 ("<span weight=\"bold\" size=\"xx-large\">Claws Mail</span>\nversion %s",
717                  VERSION);
718         gtk_label_set_markup(GTK_LABEL(label), markup);
719         g_free(markup);
720
721         button = gtkut_get_link_btn(window, HOMEPAGE_URI, " "HOMEPAGE_URI" ");
722         gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
723 #ifndef MAEMO
724         label = gtk_label_new
725                 (_("Copyright (C) 1999-2007\nHiroyuki Yamamoto <hiro-y@kcn.ne.jp>\n"
726                  "and the Claws Mail team"));
727         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
728         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
729         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
730         gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0);
731 #endif
732         notebook = gtk_notebook_new();
733         gtk_widget_set_size_request(notebook, -1, 220);
734         gtk_widget_show(notebook);
735
736         if ((scrolledwin = about_create_child_page_info()) != NULL) {
737                 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
738                                 scrolledwin,
739                                 gtk_label_new_with_mnemonic(_("_Info")));
740         }
741
742         if ((scrolledwin = about_create_child_page_authors()) != NULL) {
743                 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
744                                 scrolledwin,
745                                 gtk_label_new_with_mnemonic(_("_Authors")));
746         }
747
748         if ((scrolledwin = about_create_child_page_features()) != NULL) {
749                 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
750                                 scrolledwin,
751                                 gtk_label_new_with_mnemonic(_("_Features")));
752         }
753
754         if ((scrolledwin = about_create_child_page_license()) != NULL) {
755                 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
756                                 scrolledwin,
757                                 gtk_label_new_with_mnemonic(_("_License")));
758         }
759
760         if (release_notes_available() &&
761                         (scrolledwin = about_create_child_page_release_notes()) != NULL) {
762
763                 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
764                                 scrolledwin,
765                                 gtk_label_new_with_mnemonic(_("_Release Notes")));
766         }
767
768         gtk_box_pack_start(GTK_BOX(vbox1), notebook, TRUE, TRUE, 0);
769
770         gtkut_stock_button_set_create(&confirm_area, &close_button, GTK_STOCK_CLOSE,
771                                       NULL, NULL, NULL, NULL);
772         gtk_box_pack_end(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 4);
773         gtk_widget_grab_default(close_button);
774         gtk_widget_grab_focus(close_button);
775         g_signal_connect_closure
776                 (G_OBJECT(close_button), "clicked",
777                  g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide_on_delete),
778                                      window, NULL), FALSE);
779
780         gtk_widget_show_all(window);
781 }
782
783 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event)
784 {
785         if (event && event->keyval == GDK_Escape)
786                 gtk_widget_hide(window);
787         return FALSE;
788 }
789
790 static gboolean about_textview_uri_clicked(GtkTextTag *tag, GObject *obj,
791                                         GdkEvent *event, GtkTextIter *iter,
792                                         GtkWidget *textview)
793 {
794         GtkTextIter start_iter, end_iter;
795         GdkEventButton *bevent;
796         gchar *link = NULL;
797
798         if (!event || !tag) {
799                 return FALSE;
800         }
801
802         if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS
803                 && event->type != GDK_BUTTON_RELEASE) {
804                 return FALSE;
805         }
806
807         /* get link text from tag */
808         if (get_tag_range(iter, tag, &start_iter,
809                                    &end_iter) == FALSE) {
810                 return FALSE;
811         }
812         link = gtk_text_iter_get_text(&start_iter, &end_iter);
813         if (link == NULL) {
814                 return FALSE;
815         }
816
817         bevent = (GdkEventButton *) event;
818         if (bevent->button == 1 && event->type == GDK_BUTTON_RELEASE) {
819                 GtkTextBuffer *buffer;
820
821                 /* we shouldn't follow a link if the user has selected something */
822                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
823                 gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter);
824                 if (gtk_text_iter_get_offset(&start_iter) != gtk_text_iter_get_offset(&end_iter)) {
825                         return FALSE;
826                 }
827                 /* open link and do *not* return TRUE so that
828                    further gtk processing of the signal is done */
829                 open_uri(link, prefs_common.uri_cmd);
830
831         } else {
832                 if (bevent->button == 3 && event->type == GDK_BUTTON_PRESS) {
833                         GtkItemFactory *link_popupfactory;
834                         gint n_entries;
835
836                         n_entries = sizeof(textview_link_popup_entries) /
837                                         sizeof(textview_link_popup_entries[0]);
838                         link_popupmenu = menu_create_items(
839                                                         textview_link_popup_entries, n_entries,
840                                                 "<UriPopupMenu>", &link_popupfactory,
841                                                 textview);
842
843                         g_object_set_data(
844                                         G_OBJECT(link_popupmenu),
845                                         "menu_button", link);
846                         gtk_menu_popup(GTK_MENU(link_popupmenu), 
847                                         NULL, NULL, NULL, NULL, 
848                                         bevent->button, bevent->time);
849
850                         return TRUE;
851                 }
852         }
853         return FALSE;
854 }
855
856 static void about_open_link_cb(GtkWidget *widget, guint action, void *data)
857 {
858         gchar *link = g_object_get_data(G_OBJECT(link_popupmenu),
859                                            "menu_button");
860
861         if (link == NULL) {
862                 return;
863         }
864
865         open_uri(link, prefs_common.uri_cmd);
866         g_object_set_data(G_OBJECT(link_popupmenu), "menu_button",
867                           NULL);
868 }
869
870 static void about_copy_link_cb(GtkWidget *widget, guint action, void *data)
871 {
872         gchar *link = g_object_get_data(G_OBJECT(link_popupmenu),
873                                            "menu_button");
874
875         if (link == NULL) {
876                 return;
877         }
878
879         gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), link, -1);
880         gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), link, -1);
881         g_object_set_data(G_OBJECT(link_popupmenu), "menu_button", NULL);
882 }
883
884 static gboolean about_textview_motion_notify(GtkWidget *widget,
885                                         GdkEventMotion *event,
886                                         GtkWidget *textview)
887 {
888         about_textview_uri_update(textview, event->x, event->y);
889         gdk_window_get_pointer(widget->window, NULL, NULL, NULL);
890
891         return FALSE;
892 }
893
894 static gboolean about_textview_leave_notify(GtkWidget *widget,
895                                         GdkEventCrossing *event,
896                                         GtkWidget *textview)
897 {
898         about_textview_uri_update(textview, -1, -1);
899
900         return FALSE;
901 }
902
903 static void about_textview_uri_update(GtkWidget *textview, gint x, gint y)
904 {
905         GtkTextBuffer *buffer;
906         GtkTextIter start_iter, end_iter;
907         gchar *uri = NULL;
908         gboolean same;
909         
910         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
911
912         if (x != -1 && y != -1) {
913                 gint bx, by;
914                 GtkTextIter iter;
915                 GSList *tags;
916                 GSList *cur;
917             
918                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(textview), 
919                                 GTK_TEXT_WINDOW_WIDGET,
920                                 x, y, &bx, &by);
921                 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(textview),
922                                 &iter, bx, by);
923
924                 tags = gtk_text_iter_get_tags(&iter);
925                 for (cur = tags; cur != NULL; cur = cur->next) {
926                         GtkTextTag *tag = cur->data;
927                         char *name;
928
929                         g_object_get(G_OBJECT(tag), "name", &name, NULL);
930                         if (strcmp(name, "link") == 0
931                             && get_tag_range(&iter, tag, &start_iter, &end_iter)) {
932                                 uri = gtk_text_iter_get_text(&start_iter, &end_iter);
933                         }
934                         g_free(name);
935
936                         if (uri) {
937                                 break;
938                         }
939                 }
940                 g_slist_free(tags);
941         }
942
943         /* compare previous hovered link and this one
944            (here links must be unique in text buffer otherwise ClickableText structures should be
945            used as in textview.c) */
946         same = (uri != NULL && uri_hover != NULL
947                 && strcmp((char*)uri, (char*)uri_hover) == 0);
948
949         if (same == FALSE) {
950                 GdkWindow *window;
951
952                 if (uri_hover) {
953                         gtk_text_buffer_remove_tag_by_name(buffer,
954                                         "link-hover",
955                                         &uri_hover_start_iter,
956                                         &uri_hover_end_iter);
957                 }
958                     
959                 uri_hover = uri;
960                 if (uri) {
961                         uri_hover_start_iter = start_iter;
962                         uri_hover_end_iter = end_iter;
963
964                         gtk_text_buffer_apply_tag_by_name(buffer,
965                                         "link-hover",
966                                         &start_iter,
967                                         &end_iter);
968                 }
969                 
970                 window = gtk_text_view_get_window(GTK_TEXT_VIEW(textview),
971                                                 GTK_TEXT_WINDOW_TEXT);
972                 if (!hand_cursor)
973                         hand_cursor = gdk_cursor_new(GDK_HAND2);
974                 if (!text_cursor)
975                         text_cursor = gdk_cursor_new(GDK_XTERM);
976                 gdk_window_set_cursor(window, uri ? hand_cursor : text_cursor);
977         }
978 }