2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2025 the Claws Mail team and Hiroyuki Yamamoto
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.
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.
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/>.
21 #include "claws-features.h"
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
30 #if HAVE_SYS_UTSNAME_H
31 # include <sys/utsname.h>
37 #include "stock_pixmap.h"
38 #include "prefs_common.h"
46 #include "file-utils.h"
48 extern SessionStats session_stats
;
49 static GtkTextBuffer
*stats_text_buffer
;
51 static GtkWidget
*window
;
52 static gchar
* uri_hover
= NULL
;
53 static GtkTextIter uri_hover_start_iter
;
54 static GtkTextIter uri_hover_end_iter
;
55 static GdkCursor
*hand_cursor
= NULL
;
56 static GdkCursor
*text_cursor
= NULL
;
58 static void about_create(void);
59 static gboolean
key_pressed(GtkWidget
*widget
, GdkEventKey
*event
);
60 static gboolean
about_textview_uri_clicked(GtkTextTag
*tag
, GObject
*obj
,
61 GdkEvent
*event
, GtkTextIter
*iter
,
63 static gboolean
about_textview_motion_notify(GtkWidget
*widget
,
64 GdkEventMotion
*event
,
66 static gboolean
about_textview_leave_notify(GtkWidget
*widget
,
67 GdkEventCrossing
*event
,
69 static void about_size_allocate_cb(GtkWidget
*widget
,
70 GtkAllocation
*allocation
);
71 static void about_textview_uri_update(GtkWidget
*textview
, gint x
, gint y
);
72 static void about_update_stats(void);
74 static GtkWidget
*link_popupmenu
;
83 gtk_window_present(GTK_WINDOW(window
));
88 #define ADD_TEXT(text) gtk_text_buffer_insert(buffer, &iter, text, -1)
90 static GtkWidget
*about_create_child_page_info(void)
92 GtkWidget
*scrolledwin
;
94 GtkTextBuffer
*buffer
;
99 #if HAVE_SYS_UTSNAME_H
100 struct utsname utsbuf
;
104 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
105 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
106 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
107 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
110 text
= gtk_text_view_new();
111 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
112 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
113 /* disable margins until GTK bug 793407 is fixed
114 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
115 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
116 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
117 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
118 gtk_widget_add_events(text
, GDK_LEAVE_NOTIFY_MASK
);
120 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
121 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, 0);
123 /* textview link style (based upon main prefs) */
124 uri_color
= prefs_common
.color
[COL_URI
];
125 tag
= gtk_text_buffer_create_tag(buffer
, "link",
126 "foreground-rgba", &uri_color
,
127 "wrap-mode", GTK_WRAP_NONE
,
129 gtk_text_buffer_create_tag(buffer
, "link-hover",
130 "foreground-rgba", &uri_color
,
131 "underline", PANGO_UNDERLINE_SINGLE
,
134 ADD_TEXT(_("Claws Mail is a lightweight, fast and highly-configurable "
137 ADD_TEXT(_("For further information visit the Claws Mail website:"));
139 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, HOMEPAGE_URI
, -1,
142 ADD_TEXT(_("For support and discussion subscribe to the Claws Mail "
143 "users' mailing list:"));
145 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, USERS_ML_URI
, -1,
148 ADD_TEXT(_("Claws Mail is free software released "
149 "under the GPL. If you wish to donate "
150 "to the Claws Mail project you can do "
153 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, DONATE_URI
, -1,
156 gtk_text_buffer_create_tag(buffer
, "indented-list-item",
159 gtk_text_buffer_create_tag(buffer
, "underlined-list-title",
160 "underline", PANGO_UNDERLINE_SINGLE
,
164 ADD_TEXT(_("Copyright (C) 1999-2025\nThe Claws Mail Team\n"
165 "and Hiroyuki Yamamoto"));
168 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
169 (_("System Information\n")), -1, "underlined-list-title", NULL
);
171 #if HAVE_SYS_UTSNAME_H
173 format
= g_strconcat(
174 "GTK %d.%d.%d / GLib %d.%d.%d\n",
175 _("Locale: %s (charset: %s)\n"
176 "Operating System: %s %s (%s)"), NULL
);
177 g_snprintf(buf
, sizeof(buf
), format
,
178 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
179 glib_major_version
, glib_minor_version
, glib_micro_version
,
180 conv_get_current_locale(), conv_get_locale_charset_str(),
181 utsbuf
.sysname
, utsbuf
.release
, utsbuf
.machine
);
182 #elif defined(G_OS_WIN32)
183 format
= g_strconcat(
184 "GTK %d.%d.%d / GLib %d.%d.%d\n",
185 _("Locale: %s (charset: %s)\n"
186 "Operating System: %s"), NULL
);
187 g_snprintf(buf
, sizeof(buf
), format
,
188 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
189 glib_major_version
, glib_minor_version
, glib_micro_version
,
190 conv_get_current_locale(), conv_get_locale_charset_str(),
193 format
= g_strconcat(
194 "GTK %d.%d.%d / GLib %d.%d.%d\n",
195 _("Locale: %s (charset: %s)\n"
196 "Operating System: unknown"), NULL
);
197 g_snprintf(buf
, sizeof(buf
), format
,
198 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
199 glib_major_version
, glib_minor_version
, glib_micro_version
,
200 conv_get_current_locale(), conv_get_locale_charset_str());
204 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, buf
, -1,
205 "indented-list-item", NULL
);
208 g_signal_connect(G_OBJECT(tag
), "event",
209 G_CALLBACK(about_textview_uri_clicked
), text
);
210 g_signal_connect(G_OBJECT(text
), "motion-notify-event",
211 G_CALLBACK(about_textview_motion_notify
), text
);
212 g_signal_connect(G_OBJECT(text
), "leave-notify-event",
213 G_CALLBACK(about_textview_leave_notify
), text
);
218 static GtkWidget
*about_create_child_page_authors(void)
220 GtkWidget
*scrolledwin
;
222 GtkTextBuffer
*buffer
;
226 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
227 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
228 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
229 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
232 text
= gtk_text_view_new();
233 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
234 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
235 /* disable margins until GTK bug 793407 is fixed
236 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
237 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
238 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
239 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
240 gtk_widget_add_events(text
, GDK_LEAVE_NOTIFY_MASK
);
242 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
243 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, 0);
245 /* init formatting tag: indentation for list items */
246 gtk_text_buffer_create_tag(buffer
, "indented-list-item",
249 gtk_text_buffer_create_tag(buffer
, "underlined-list-title",
250 "underline", PANGO_UNDERLINE_SINGLE
,
253 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
254 (_("The Claws Mail Team")), -1, "underlined-list-title", NULL
);
255 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
257 for (i
= 0; TEAM_LIST
[i
] != NULL
; i
++) {
258 if (g_utf8_validate(TEAM_LIST
[i
], -1, NULL
))
259 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, TEAM_LIST
[i
], -1,
260 "indented-list-item", NULL
);
262 gchar
*conv
= conv_codeset_strdup(TEAM_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
264 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
265 "indented-list-item", NULL
);
268 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
272 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
273 (_("Previous team members")), -1, "underlined-list-title", NULL
);
276 for (i
= 0; EX_TEAM_LIST
[i
] != NULL
; i
++) {
277 if (g_utf8_validate(EX_TEAM_LIST
[i
], -1, NULL
))
278 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, EX_TEAM_LIST
[i
], -1,
279 "indented-list-item", NULL
);
281 gchar
*conv
= conv_codeset_strdup(EX_TEAM_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
283 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
284 "indented-list-item", NULL
);
291 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
292 (_("The translation team")), -1, "underlined-list-title", NULL
);
295 for (i
= 0; TRANS_TEAM_LIST
[i
] != NULL
; i
++) {
296 if (g_utf8_validate(TRANS_TEAM_LIST
[i
], -1, NULL
))
297 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, TRANS_TEAM_LIST
[i
], -1,
298 "indented-list-item", NULL
);
300 gchar
*conv
= conv_codeset_strdup(TRANS_TEAM_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
302 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
303 "indented-list-item", NULL
);
310 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
311 (_("Documentation team")), -1, "underlined-list-title", NULL
);
314 for (i
= 0; DOC_TEAM_LIST
[i
] != NULL
; i
++) {
315 if (g_utf8_validate(DOC_TEAM_LIST
[i
], -1, NULL
))
316 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, DOC_TEAM_LIST
[i
], -1,
317 "indented-list-item", NULL
);
319 gchar
*conv
= conv_codeset_strdup(DOC_TEAM_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
321 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
322 "indented-list-item", NULL
);
329 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (_("Logo")), -1,
330 "underlined-list-title", NULL
);
333 for (i
= 0; LOGO_LIST
[i
] != NULL
; i
++) {
334 if (g_utf8_validate(LOGO_LIST
[i
], -1, NULL
))
335 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, LOGO_LIST
[i
], -1,
336 "indented-list-item", NULL
);
338 gchar
*conv
= conv_codeset_strdup(LOGO_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
340 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
341 "indented-list-item", NULL
);
348 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (_("Icons")), -1,
349 "underlined-list-title", NULL
);
352 for (i
= 0; ICONS_LIST
[i
] != NULL
; i
++) {
353 if (g_utf8_validate(ICONS_LIST
[i
], -1, NULL
))
354 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, ICONS_LIST
[i
], -1,
355 "indented-list-item", NULL
);
357 gchar
*conv
= conv_codeset_strdup(ICONS_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
359 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
360 "indented-list-item", NULL
);
367 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (_("Contributors")), -1,
368 "underlined-list-title", NULL
);
371 for (i
= 0; CONTRIBS_LIST
[i
] != NULL
; i
++) {
372 if (g_utf8_validate(CONTRIBS_LIST
[i
], -1, NULL
))
373 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, CONTRIBS_LIST
[i
], -1,
374 "indented-list-item", NULL
);
376 gchar
*conv
= conv_codeset_strdup(CONTRIBS_LIST
[i
], CS_ISO_8859_1
, CS_UTF_8
);
378 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, conv
, -1,
379 "indented-list-item", NULL
);
388 static GtkWidget
*about_create_child_page_features(void)
390 GtkWidget
*scrolledwin
;
392 GtkTextBuffer
*buffer
;
394 GdkPixbuf
*active_pixbuf
;
395 GdkPixbuf
*inactive_pixbuf
;
397 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
398 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
399 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
400 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
403 text
= gtk_text_view_new();
404 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
405 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
406 /* disable margins until GTK bug 793407 is fixed
407 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
408 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
409 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
410 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
411 gtk_widget_add_events(text
, GDK_LEAVE_NOTIFY_MASK
);
413 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
414 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, 0);
416 ADD_TEXT(_("Compiled-in Features"));
419 gtk_text_buffer_create_tag(buffer
, "bold", "weight", PANGO_WEIGHT_BOLD
,
422 stock_pixbuf_gdk(STOCK_PIXMAP_CHECKBOX_ON
, &active_pixbuf
);
423 stock_pixbuf_gdk(STOCK_PIXMAP_CHECKBOX_OFF
, &inactive_pixbuf
);
426 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
428 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
430 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" compface "), -1,
432 gtk_text_buffer_insert(buffer
, &iter
,
433 (gchar
*)C_("compface", "adds support for the X-Face header\n"), -1);
436 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
438 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
440 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" Enchant "), -1,
442 gtk_text_buffer_insert(buffer
, &iter
,
443 (gchar
*)C_("Enchant", "adds support for spell checking\n"), -1);
446 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
448 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
450 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" GnuTLS "), -1,
452 gtk_text_buffer_insert(buffer
, &iter
,
453 (gchar
*)C_("GnuTLS", "adds support for encrypted connections to servers\n"), -1);
456 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
458 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
460 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" iconv "), -1,
462 gtk_text_buffer_insert(buffer
, &iter
,
463 (gchar
*)C_("iconv", "allows converting to and from different character sets\n"), -1);
466 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
468 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
470 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" IPv6 "), -1,
472 gtk_text_buffer_insert(buffer
, &iter
,
473 (gchar
*)C_("IPv6", "adds support for IPv6 addresses\n"), -1);
476 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
478 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
480 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" JPilot "), -1,
482 gtk_text_buffer_insert(buffer
, &iter
,
483 (gchar
*)C_("JPilot", "adds support for PalmOS addressbooks\n"), -1);
486 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
488 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
490 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" LDAP "), -1,
492 gtk_text_buffer_insert(buffer
, &iter
,
493 (gchar
*)C_("LDAP", "adds support for LDAP shared addressbooks\n"), -1);
496 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
498 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
500 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" libetpan "), -1,
502 gtk_text_buffer_insert(buffer
, &iter
,
503 (gchar
*)C_("libetpan", "adds support for IMAP and NNTP servers\n"), -1);
506 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
508 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
510 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" librSVG "), -1,
512 gtk_text_buffer_insert(buffer
, &iter
,
513 (gchar
*)C_("librSVG", "adds support for SVG themes\n"), -1);
516 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
518 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
520 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" libSM "), -1,
522 gtk_text_buffer_insert(buffer
, &iter
,
523 (gchar
*)C_("libSM", "adds support for session handling\n"), -1);
525 #if HAVE_NETWORKMANAGER_SUPPORT
526 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
528 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
530 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" NetworkManager "), -1,
532 gtk_text_buffer_insert(buffer
, &iter
,
533 (gchar
*)C_("NetworkManager", "adds support for detection of network connection changes\n"), -1);
536 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, active_pixbuf
);
538 gtk_text_buffer_insert_pixbuf(buffer
, &iter
, inactive_pixbuf
);
540 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, (" OAuth2 "), -1,
542 gtk_text_buffer_insert(buffer
, &iter
,
543 (gchar
*)C_("OAuth2", "adds support for OAuth2 authentication\n"), -1);
548 static GtkWidget
*about_create_child_page_license(void)
550 GtkWidget
*scrolledwin
;
552 GtkTextBuffer
*buffer
;
557 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
558 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
559 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
560 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
563 text
= gtk_text_view_new();
564 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
565 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
566 /* disable margins until GTK bug 793407 is fixed
567 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
568 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
569 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
570 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
572 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
573 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, 0);
576 _("This program is free software; you can redistribute it and/or modify "
577 "it under the terms of the GNU General Public License as published by "
578 "the Free Software Foundation; either version 3, or (at your option) "
579 "any later version."));
583 _("This program is distributed in the hope that it will be useful, "
584 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
585 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "
586 "See the GNU General Public License for more details."));
589 /* textview link style (based upon main prefs) */
590 uri_color
= prefs_common
.color
[COL_URI
];
592 tag
= gtk_text_buffer_create_tag(buffer
, "link",
593 "foreground-rgba", &uri_color
,
595 gtk_text_buffer_create_tag(buffer
, "link-hover",
596 "foreground-rgba", &uri_color
,
597 "underline", PANGO_UNDERLINE_SINGLE
,
600 gtk_text_buffer_insert(buffer
, &iter
, g_strconcat(
601 _("You should have received a copy of the GNU General Public License "
602 "along with this program. If not, see "), "<", NULL
), -1);
603 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
,
604 "http://www.gnu.org/licenses/", -1,
606 gtk_text_buffer_insert(buffer
, &iter
, ">. \n\n", -1);
608 g_signal_connect(G_OBJECT(tag
), "event",
609 G_CALLBACK(about_textview_uri_clicked
), text
);
610 g_signal_connect(G_OBJECT(text
), "motion-notify-event",
611 G_CALLBACK(about_textview_motion_notify
), text
);
612 g_signal_connect(G_OBJECT(text
), "leave-notify-event",
613 G_CALLBACK(about_textview_leave_notify
), text
);
620 static gboolean
release_notes_available(void)
622 gboolean ret
= FALSE
;
625 path
= g_strconcat(DOCDIR
, G_DIR_SEPARATOR_S
, RELEASE_NOTES_FILE
, NULL
);
626 ret
= (is_file_exist(path
));
632 static GtkWidget
*about_create_child_page_release_notes(void)
634 GtkWidget
*scrolledwin
;
636 GtkTextBuffer
*buffer
;
638 gchar
*path
, buf
[1024];
641 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
642 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
643 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
644 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
646 text
= gtk_text_view_new();
647 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
648 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
649 /* disable margins until GTK bug 793407 is fixed
650 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
651 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
652 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
653 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
655 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
656 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, 0);
658 path
= g_strconcat(DOCDIR
, G_DIR_SEPARATOR_S
, RELEASE_NOTES_FILE
, NULL
);
659 if ((fp
= claws_fopen(path
, "rb")) == NULL
) {
660 if (ENOENT
!= errno
) FILE_OP_ERROR(path
, "claws_fopen");
666 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
667 const gchar
*src_codeset
= conv_get_locale_charset_str();
668 const gchar
*dest_codeset
= CS_UTF_8
;
671 tmp
= conv_codeset_strdup(buf
, src_codeset
, dest_codeset
);
673 g_warning("failed to convert character set of action configuration");
677 gtk_text_buffer_insert(buffer
, &iter
, tmp
, -1);
685 static GtkWidget
*about_create_child_page_session_stats(void)
687 GtkWidget
*scrolledwin
;
691 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
692 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
693 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
694 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
696 text
= gtk_text_view_new();
697 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
698 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
699 /* disable margins until GTK bug 793407 is fixed
700 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
701 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); */
702 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
703 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
705 stats_text_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
707 gtk_text_buffer_get_iter_at_offset(stats_text_buffer
, &iter
, 0);
708 gtk_text_buffer_create_tag(stats_text_buffer
, "indented-list-item",
711 gtk_text_buffer_create_tag(stats_text_buffer
, "underlined-list-title",
712 "underline", PANGO_UNDERLINE_SINGLE
,
714 gtk_text_buffer_create_tag(stats_text_buffer
, "bold", "weight", PANGO_WEIGHT_BOLD
,
717 about_update_stats();
722 static void about_update_stats(void)
724 if (stats_text_buffer
!= NULL
)
726 GtkTextIter start
, end
, iter
;
729 gtk_text_buffer_get_start_iter(stats_text_buffer
, &start
);
730 gtk_text_buffer_get_end_iter(stats_text_buffer
, &end
);
731 gtk_text_buffer_delete(stats_text_buffer
, &start
, &end
);
733 gtk_text_buffer_get_iter_at_offset(stats_text_buffer
, &iter
, 0);
735 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
,
736 (_("Session statistics\n")), -1,
737 "underlined-list-title", NULL
);
739 if (prefs_common
.date_format
) {
744 lt
= localtime(&session_stats
.time_started
);
745 fast_strftime(date
, len
, prefs_common
.date_format
, lt
);
746 g_snprintf(buf
, sizeof(buf
), _("Started: %s\n"),
747 lt
? date
: ctime(&session_stats
.time_started
));
749 g_snprintf(buf
, sizeof(buf
), _("Started: %s\n"),
750 ctime(&session_stats
.time_started
));
751 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
752 "indented-list-item", NULL
);
754 gtk_text_buffer_insert(stats_text_buffer
, &iter
, "\n", 1);
755 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
,
756 (_("Incoming traffic\n")), -1,
757 "underlined-list-title", NULL
);
759 g_snprintf(buf
, sizeof(buf
), _("Received messages: %d\n"),
760 session_stats
.received
);
761 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
762 "indented-list-item", "bold", NULL
);
763 if (session_stats
.spam
> 0) {
764 g_snprintf(buf
, sizeof(buf
), _("Spam messages: %d\n"), session_stats
.spam
);
765 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
766 "indented-list-item", NULL
);
769 gtk_text_buffer_insert(stats_text_buffer
, &iter
, "\n", 1);
770 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
,
771 (_("Outgoing traffic\n")), -1,
772 "underlined-list-title", NULL
);
774 g_snprintf(buf
, sizeof(buf
), _("New/redirected messages: %d\n"),
776 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
777 "indented-list-item", NULL
);
779 g_snprintf(buf
, sizeof(buf
), _("Replied messages: %d\n"),
780 session_stats
.replied
);
781 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
782 "indented-list-item", NULL
);
784 g_snprintf(buf
, sizeof(buf
), _("Forwarded messages: %d\n"),
785 session_stats
.forwarded
);
786 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
787 "indented-list-item", NULL
);
789 g_snprintf(buf
, sizeof(buf
), _("Total outgoing messages: %d\n"),
790 (session_stats
.sent
+ session_stats
.replied
+
791 session_stats
.forwarded
));
792 gtk_text_buffer_insert_with_tags_by_name(stats_text_buffer
, &iter
, buf
, -1,
793 "indented-list-item", "bold", NULL
);
797 static void about_create(void)
804 GtkWidget
*scrolledwin
;
807 GtkWidget
*confirm_area
;
808 GtkWidget
*close_button
;
809 static GdkGeometry geometry
;
812 stats_text_buffer
= NULL
;
814 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "about");
815 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER_ALWAYS
);
816 gtk_window_set_title(GTK_WINDOW(window
), _("About Claws Mail"));
817 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
818 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
819 gtk_widget_set_size_request(window
, -1, -1);
820 g_signal_connect(G_OBJECT(window
), "size_allocate",
821 G_CALLBACK(about_size_allocate_cb
), NULL
);
822 g_signal_connect(G_OBJECT(window
), "delete_event",
823 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
824 g_signal_connect(G_OBJECT(window
), "key_press_event",
825 G_CALLBACK(key_pressed
), NULL
);
827 if (!geometry
.min_width
) {
828 geometry
.min_width
= 450;
829 geometry
.min_height
= 500;
832 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
, &geometry
,
834 gtk_window_set_default_size(GTK_WINDOW(window
), prefs_common
.aboutwin_width
,
835 prefs_common
.aboutwin_height
);
837 gtk_widget_realize(window
);
839 grid1
= gtk_grid_new();
840 gtk_widget_set_halign(grid1
, GTK_ALIGN_FILL
);
841 gtk_orientable_set_orientation(GTK_ORIENTABLE(grid1
),
842 GTK_ORIENTATION_VERTICAL
);
843 gtk_grid_set_row_spacing(GTK_GRID(grid1
), 8);
844 gtk_grid_set_column_spacing(GTK_GRID(grid1
), 8);
845 gtk_container_add(GTK_CONTAINER(window
), grid1
);
847 image
= priv_pixmap_widget(PRIV_PIXMAP_CLAWS_MAIL_LOGO
);
848 gtk_widget_set_halign(image
, GTK_ALIGN_CENTER
);
849 gtk_widget_set_valign(image
, GTK_ALIGN_CENTER
);
850 gtk_grid_attach(GTK_GRID(grid1
), image
, 0, row
, 1, 1);
852 grid2
= gtk_grid_new();
853 gtk_orientable_set_orientation(GTK_ORIENTABLE(grid2
),
854 GTK_ORIENTATION_VERTICAL
);
855 gtk_widget_set_halign(grid2
, GTK_ALIGN_CENTER
);
856 gtk_widget_set_valign(grid2
, GTK_ALIGN_CENTER
);
857 gtk_grid_set_row_spacing(GTK_GRID(grid2
), 4);
859 gtk_grid_attach(GTK_GRID(grid1
), grid2
, 1, row
, 1, 1);
862 label
= gtk_label_new("");
863 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
864 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_CENTER
);
865 gtk_container_add(GTK_CONTAINER(grid2
), label
);
866 markup
= g_markup_printf_escaped
867 ("<span weight=\"bold\" size=\"xx-large\">Claws Mail</span>\nversion %s",
869 gtk_label_set_markup(GTK_LABEL(label
), markup
);
872 button
= gtk_link_button_new(HOMEPAGE_URI
);
873 gtk_container_add(GTK_CONTAINER(grid2
), button
);
875 label
= gtk_label_new
876 (_("Copyright (C) 1999-2025\nThe Claws Mail Team\n"
877 "and Hiroyuki Yamamoto"));
878 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
879 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_CENTER
);
880 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
881 gtk_container_add(GTK_CONTAINER(grid2
), label
);
884 notebook
= gtk_notebook_new();
885 gtk_widget_set_size_request(notebook
, -1, 220);
886 gtk_widget_show(notebook
);
888 if ((scrolledwin
= about_create_child_page_info()) != NULL
) {
889 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
891 gtk_label_new_with_mnemonic(_("_Info")));
894 if ((scrolledwin
= about_create_child_page_authors()) != NULL
) {
895 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
897 gtk_label_new_with_mnemonic(_("_Authors")));
900 if ((scrolledwin
= about_create_child_page_features()) != NULL
) {
901 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
903 gtk_label_new_with_mnemonic(_("_Features")));
906 if ((scrolledwin
= about_create_child_page_license()) != NULL
) {
907 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
909 gtk_label_new_with_mnemonic(_("_License")));
912 if (release_notes_available() &&
913 (scrolledwin
= about_create_child_page_release_notes()) != NULL
) {
915 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
917 gtk_label_new_with_mnemonic(_("_Release Notes")));
920 if ((scrolledwin
= about_create_child_page_session_stats()) != NULL
) {
921 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
923 gtk_label_new_with_mnemonic(_("_Statistics")));
926 gtk_widget_set_hexpand(notebook
, TRUE
);
927 gtk_widget_set_vexpand(notebook
, TRUE
);
928 gtk_grid_attach(GTK_GRID(grid1
), notebook
, 0, row
, 2, 1);
931 gtkut_stock_button_set_create(&confirm_area
, &close_button
, "window-close", _("_Close"),
932 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
933 gtk_grid_attach(GTK_GRID(grid1
), confirm_area
, 0, row
, 2, 1);
936 gtk_widget_grab_default(close_button
);
937 gtk_widget_grab_focus(close_button
);
938 g_signal_connect_closure
939 (G_OBJECT(close_button
), "clicked",
940 g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide_on_delete
),
941 window
, NULL
), FALSE
);
943 gtk_widget_show_all(window
);
946 static gboolean
key_pressed(GtkWidget
*widget
, GdkEventKey
*event
)
948 if (event
&& event
->keyval
== GDK_KEY_Escape
)
949 gtk_widget_hide(window
);
953 static void about_size_allocate_cb(GtkWidget
*widget
,
954 GtkAllocation
*allocation
)
956 cm_return_if_fail(allocation
!= NULL
);
958 gtk_window_get_size(GTK_WINDOW(widget
),
959 &prefs_common
.aboutwin_width
, &prefs_common
.aboutwin_height
);
963 static gboolean
about_textview_uri_clicked(GtkTextTag
*tag
, GObject
*obj
,
964 GdkEvent
*event
, GtkTextIter
*iter
,
967 GtkTextIter start_iter
, end_iter
;
968 GdkEventButton
*bevent
;
971 if (!event
|| !tag
) {
975 if (event
->type
!= GDK_BUTTON_PRESS
&& event
->type
!= GDK_2BUTTON_PRESS
976 && event
->type
!= GDK_BUTTON_RELEASE
) {
980 /* get link text from tag */
981 if (get_tag_range(iter
, tag
, &start_iter
,
982 &end_iter
) == FALSE
) {
985 link
= gtk_text_iter_get_text(&start_iter
, &end_iter
);
990 bevent
= (GdkEventButton
*) event
;
991 if (bevent
->button
== 1 && event
->type
== GDK_BUTTON_RELEASE
) {
992 GtkTextBuffer
*buffer
;
994 /* we shouldn't follow a link if the user has selected something */
995 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview
));
996 gtk_text_buffer_get_selection_bounds(buffer
, &start_iter
, &end_iter
);
997 if (gtk_text_iter_get_offset(&start_iter
) != gtk_text_iter_get_offset(&end_iter
)) {
1000 /* open link and do *not* return TRUE so that
1001 further gtk processing of the signal is done */
1002 open_uri(link
, prefs_common_get_uri_cmd());
1005 if (bevent
->button
== 3 && event
->type
== GDK_BUTTON_PRESS
) {
1006 link_popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1007 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/TextviewPopupLink")));
1010 G_OBJECT(link_popupmenu
),
1012 gtk_menu_popup_at_pointer(GTK_MENU(link_popupmenu
), NULL
);
1020 static gboolean
about_textview_motion_notify(GtkWidget
*widget
,
1021 GdkEventMotion
*event
,
1022 GtkWidget
*textview
)
1024 about_textview_uri_update(textview
, event
->x
, event
->y
);
1028 static gboolean
about_textview_leave_notify(GtkWidget
*widget
,
1029 GdkEventCrossing
*event
,
1030 GtkWidget
*textview
)
1032 about_textview_uri_update(textview
, -1, -1);
1037 static void about_textview_uri_update(GtkWidget
*textview
, gint x
, gint y
)
1039 GtkTextBuffer
*buffer
;
1040 GtkTextIter start_iter
, end_iter
;
1044 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview
));
1046 if (x
!= -1 && y
!= -1) {
1052 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(textview
),
1053 GTK_TEXT_WINDOW_WIDGET
,
1055 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(textview
),
1058 tags
= gtk_text_iter_get_tags(&iter
);
1059 for (cur
= tags
; cur
!= NULL
; cur
= cur
->next
) {
1060 GtkTextTag
*tag
= cur
->data
;
1063 g_object_get(G_OBJECT(tag
), "name", &name
, NULL
);
1064 if (strcmp(name
, "link") == 0
1065 && get_tag_range(&iter
, tag
, &start_iter
, &end_iter
)) {
1066 uri
= gtk_text_iter_get_text(&start_iter
, &end_iter
);
1077 /* compare previous hovered link and this one
1078 (here links must be unique in text buffer otherwise ClickableText structures should be
1079 used as in textview.c) */
1080 same
= (uri
!= NULL
&& uri_hover
!= NULL
1081 && strcmp((char*)uri
, (char*)uri_hover
) == 0);
1083 if (same
== FALSE
) {
1087 gtk_text_buffer_remove_tag_by_name(buffer
,
1089 &uri_hover_start_iter
,
1090 &uri_hover_end_iter
);
1095 uri_hover_start_iter
= start_iter
;
1096 uri_hover_end_iter
= end_iter
;
1098 gtk_text_buffer_apply_tag_by_name(buffer
,
1104 window
= gtk_text_view_get_window(GTK_TEXT_VIEW(textview
),
1105 GTK_TEXT_WINDOW_TEXT
);
1107 hand_cursor
= gdk_cursor_new_for_display(
1108 gdk_window_get_display(window
), GDK_HAND2
);
1110 text_cursor
= gdk_cursor_new_for_display(
1111 gdk_window_get_display(window
), GDK_XTERM
);
1112 gdk_window_set_cursor(window
, uri
? hand_cursor
: text_cursor
);