2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2019 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"
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
29 #include "logwindow.h"
34 #include "prefs_common.h"
36 static void hide_cb (GtkWidget
*widget
,
38 static gboolean
key_pressed (GtkWidget
*widget
,
41 static void size_allocate_cb (GtkWidget
*widget
,
42 GtkAllocation
*allocation
,
44 static gboolean
log_window_append (gpointer source
,
46 static void log_window_clip (LogWindow
*logwin
,
48 static void log_window_clear (GtkWidget
*widget
,
50 static void log_window_popup_menu_extend (GtkTextView
*textview
,
55 *\brief Save Gtk object size to prefs dataset
57 static void size_allocate_cb(GtkWidget
*widget
,
58 GtkAllocation
*allocation
,
61 gint
*prefs_logwin_width
= NULL
;
62 gint
*prefs_logwin_height
= NULL
;
63 LogInstance instance
= GPOINTER_TO_INT(data
);
65 cm_return_if_fail(allocation
!= NULL
);
67 get_log_prefs(instance
, &prefs_logwin_width
, &prefs_logwin_height
);
68 cm_return_if_fail(prefs_logwin_width
!= NULL
);
69 cm_return_if_fail(prefs_logwin_height
!= NULL
);
71 gtk_window_get_size(GTK_WINDOW(widget
),
72 prefs_logwin_width
, prefs_logwin_height
);
75 LogWindow
*log_window_create(LogInstance instance
)
79 GtkWidget
*scrolledwin
;
81 GtkTextBuffer
*buffer
;
83 static GdkGeometry geometry
;
84 gint
*prefs_logwin_width
= NULL
;
85 gint
*prefs_logwin_height
= NULL
;
87 debug_print("Creating log window...\n");
89 get_log_prefs(instance
, &prefs_logwin_width
, &prefs_logwin_height
);
90 cm_return_val_if_fail(prefs_logwin_width
!= NULL
, NULL
);
91 cm_return_val_if_fail(prefs_logwin_height
!= NULL
, NULL
);
93 logwin
= g_new0(LogWindow
, 1);
95 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "logwindow");
96 gtk_window_set_title(GTK_WINDOW(window
), get_log_title(instance
));
97 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
98 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
99 g_signal_connect(G_OBJECT(window
), "delete_event",
100 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
101 g_signal_connect(G_OBJECT(window
), "key_press_event",
102 G_CALLBACK(key_pressed
), logwin
);
103 g_signal_connect_after(G_OBJECT(window
), "hide",
104 G_CALLBACK(hide_cb
), logwin
);
105 gtk_widget_realize(window
);
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
),
112 gtk_container_add(GTK_CONTAINER(window
), scrolledwin
);
113 gtk_widget_show(scrolledwin
);
115 text
= gtk_text_view_new();
116 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
117 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
118 logwin
->buffer
= buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
120 g_object_ref(G_OBJECT(logwin
->buffer
));
121 gtk_text_view_set_buffer(GTK_TEXT_VIEW(text
), NULL
);
122 logwin
->hidden
= TRUE
;
123 logwin
->never_shown
= TRUE
;
125 gtk_text_buffer_get_start_iter(buffer
, &iter
);
126 logwin
->end_mark
= gtk_text_buffer_create_mark(buffer
, "end", &iter
, FALSE
);
128 g_signal_connect(G_OBJECT(text
), "populate-popup",
129 G_CALLBACK(log_window_popup_menu_extend
), logwin
);
130 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
131 gtk_widget_show(text
);
133 g_signal_connect(G_OBJECT(window
), "size_allocate",
134 G_CALLBACK(size_allocate_cb
), GINT_TO_POINTER(instance
));
136 if (!geometry
.min_height
) {
137 geometry
.min_width
= 520;
138 geometry
.min_height
= 400;
141 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
, &geometry
,
143 gtk_window_set_default_size(GTK_WINDOW(window
), *prefs_logwin_width
,
144 *prefs_logwin_height
);
146 logwin
->window
= window
;
147 logwin
->scrolledwin
= scrolledwin
;
149 logwin
->hook_id
= hooks_register_hook(get_log_hook(instance
), log_window_append
, logwin
);
150 logwin
->has_error_capability
= get_log_error_capability(instance
);
157 void log_window_init(LogWindow
*logwin
)
159 GtkTextBuffer
*buffer
;
161 logwin
->msg_color
= &prefs_common
.color
[COL_LOG_MSG
];
162 logwin
->warn_color
= &prefs_common
.color
[COL_LOG_WARN
];
163 logwin
->error_color
= &prefs_common
.color
[COL_LOG_ERROR
];
164 logwin
->in_color
= &prefs_common
.color
[COL_LOG_IN
];
165 logwin
->out_color
= &prefs_common
.color
[COL_LOG_OUT
];
166 logwin
->status_ok_color
= &prefs_common
.color
[COL_LOG_STATUS_OK
];
167 logwin
->status_nok_color
= &prefs_common
.color
[COL_LOG_STATUS_NOK
];
168 logwin
->status_skip_color
= &prefs_common
.color
[COL_LOG_STATUS_SKIP
];
170 buffer
= logwin
->buffer
;
171 gtk_text_buffer_create_tag(buffer
, "message",
172 "foreground-rgba", logwin
->msg_color
,
174 gtk_text_buffer_create_tag(buffer
, "warn",
175 "foreground-rgba", logwin
->warn_color
,
177 logwin
->error_tag
= gtk_text_buffer_create_tag(buffer
, "error",
178 "foreground-rgba", logwin
->error_color
,
180 gtk_text_buffer_create_tag(buffer
, "input",
181 "foreground-rgba", logwin
->in_color
,
183 gtk_text_buffer_create_tag(buffer
, "output",
184 "foreground-rgba", logwin
->out_color
,
186 gtk_text_buffer_create_tag(buffer
, "status_ok",
187 "foreground-rgba", logwin
->status_ok_color
,
189 gtk_text_buffer_create_tag(buffer
, "status_nok",
190 "foreground-rgba", logwin
->status_nok_color
,
192 gtk_text_buffer_create_tag(buffer
, "status_skip",
193 "foreground-rgba", logwin
->status_skip_color
,
199 void log_window_show(LogWindow
*logwin
)
201 GtkTextView
*text
= GTK_TEXT_VIEW(logwin
->text
);
202 GtkTextBuffer
*buffer
= logwin
->buffer
;
205 logwin
->hidden
= FALSE
;
207 if (logwin
->never_shown
)
208 gtk_text_view_set_buffer(GTK_TEXT_VIEW(logwin
->text
), logwin
->buffer
);
210 logwin
->never_shown
= FALSE
;
212 mark
= gtk_text_buffer_get_mark(buffer
, "end");
213 gtk_text_view_scroll_mark_onscreen(text
, mark
);
215 gtk_window_deiconify(GTK_WINDOW(logwin
->window
));
216 gtk_widget_show(logwin
->window
);
217 gtk_window_present(GTK_WINDOW(logwin
->window
));
220 static void log_window_jump_to_error(LogWindow
*logwin
)
223 gtk_text_buffer_get_end_iter(logwin
->buffer
, &iter
);
224 if (!gtk_text_iter_backward_to_tag_toggle(&iter
, logwin
->error_tag
))
227 gtk_text_iter_backward_line(&iter
);
228 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(logwin
->text
), &iter
, 0, TRUE
, 0, 0);
231 void log_window_show_error(LogWindow
*logwin
)
233 log_window_show(logwin
);
234 log_window_jump_to_error(logwin
);
237 void log_window_set_clipping(LogWindow
*logwin
, gboolean clip
, guint clip_length
)
239 cm_return_if_fail(logwin
!= NULL
);
242 logwin
->clip_length
= clip_length
;
245 static gboolean
log_window_append(gpointer source
, gpointer data
)
247 LogText
*logtext
= (LogText
*) source
;
248 LogWindow
*logwindow
= (LogWindow
*) data
;
250 GtkTextBuffer
*buffer
;
255 cm_return_val_if_fail(logtext
!= NULL
, TRUE
);
256 cm_return_val_if_fail(logtext
->text
!= NULL
, TRUE
);
257 cm_return_val_if_fail(logwindow
!= NULL
, FALSE
);
259 if (logwindow
->clip
&& !logwindow
->clip_length
)
262 text
= GTK_TEXT_VIEW(logwindow
->text
);
263 buffer
= logwindow
->buffer
;
264 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, -1);
266 switch (logtext
->type
) {
278 logwindow
->has_error
= TRUE
;
288 case LOG_STATUS_SKIP
:
290 head
= "> skipped: ";
297 if (logtext
->instance
== LOG_PROTOCOL
) {
299 if (strstr(logtext
->text
, "] POP>")
300 || strstr(logtext
->text
, "] IMAP>")
301 || strstr(logtext
->text
, "] SMTP>")
302 || strstr(logtext
->text
, "] ESMTP>")
303 || strstr(logtext
->text
, "] NNTP>"))
305 if (strstr(logtext
->text
, "] POP<")
306 || strstr(logtext
->text
, "] IMAP<")
307 || strstr(logtext
->text
, "] SMTP<")
308 || strstr(logtext
->text
, "] ESMTP<")
309 || strstr(logtext
->text
, "] NNTP<"))
315 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, head
, -1,
318 if (!g_utf8_validate(logtext
->text
, -1, NULL
)) {
319 gchar
* mybuf
= g_malloc(strlen(logtext
->text
)*2 +1);
320 conv_localetodisp(mybuf
, strlen(logtext
->text
)*2 +1, logtext
->text
);
321 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, mybuf
, -1,
325 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, logtext
->text
, -1,
328 gtk_text_buffer_get_start_iter(buffer
, &iter
);
331 log_window_clip (logwindow
, logwindow
->clip_length
);
333 if (!logwindow
->hidden
) {
334 GtkAdjustment
*vadj
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(text
));
335 gfloat upper
= gtk_adjustment_get_upper(vadj
) -
336 gtk_adjustment_get_page_size(vadj
);
337 gfloat value
= gtk_adjustment_get_value(vadj
);
338 if (value
== upper
||
339 (upper
- value
< 16 && value
< 8))
340 gtk_text_view_scroll_mark_onscreen(text
, logwindow
->end_mark
);
346 static void hide_cb(GtkWidget
*widget
, LogWindow
*logwin
)
348 logwin
->hidden
= TRUE
;
351 static gboolean
key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
354 if (event
&& event
->keyval
== GDK_KEY_Escape
)
355 gtk_widget_hide(logwin
->window
);
356 else if (event
&& event
->keyval
== GDK_KEY_Delete
)
357 log_window_clear(NULL
, logwin
);
362 static void log_window_clip(LogWindow
*logwin
, guint clip_length
)
366 GtkTextBuffer
*textbuf
= logwin
->buffer
;
367 GtkTextIter start_iter
, end_iter
;
369 length
= gtk_text_buffer_get_line_count(textbuf
);
370 /* debug_print("Log window length: %u\n", length); */
372 if (length
> clip_length
) {
373 /* find the end of the first line after the cut off
375 point
= length
- clip_length
;
376 gtk_text_buffer_get_iter_at_line(textbuf
, &end_iter
, point
);
377 if (!gtk_text_iter_forward_to_line_end(&end_iter
))
379 gtk_text_buffer_get_start_iter(textbuf
, &start_iter
);
380 gtk_text_buffer_delete(textbuf
, &start_iter
, &end_iter
);
381 if (logwin
->has_error
) {
382 gtk_text_buffer_get_start_iter(textbuf
, &start_iter
);
383 if (mainwindow_get_mainwindow() && !gtk_text_iter_forward_to_tag_toggle(&start_iter
, logwin
->error_tag
)) {
384 mainwindow_clear_error(mainwindow_get_mainwindow());
385 logwin
->has_error
= FALSE
;
391 static void log_window_clear(GtkWidget
*widget
, LogWindow
*logwin
)
393 GtkTextBuffer
*textbuf
= logwin
->buffer
;
394 GtkTextIter start_iter
, end_iter
;
396 gtk_text_buffer_get_start_iter(textbuf
, &start_iter
);
397 gtk_text_buffer_get_end_iter(textbuf
, &end_iter
);
398 gtk_text_buffer_delete(textbuf
, &start_iter
, &end_iter
);
401 static void log_window_go_to_last_error(GtkWidget
*widget
, LogWindow
*logwin
)
403 log_window_jump_to_error(logwin
);
406 static void log_window_popup_menu_extend(GtkTextView
*textview
,
407 GtkMenu
*menu
, LogWindow
*logwin
)
411 cm_return_if_fail(menu
!= NULL
);
412 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
414 menuitem
= gtk_separator_menu_item_new();
415 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
416 gtk_widget_show(menuitem
);
418 if (logwin
->has_error_capability
) {
419 menuitem
= gtk_menu_item_new_with_mnemonic(_("_Go to last error"));
420 g_signal_connect(G_OBJECT(menuitem
), "activate",
421 G_CALLBACK(log_window_go_to_last_error
), logwin
);
422 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
423 gtk_widget_show(menuitem
);
426 menuitem
= gtk_menu_item_new_with_mnemonic(_("Clear _Log"));
427 g_signal_connect(G_OBJECT(menuitem
), "activate",
428 G_CALLBACK(log_window_clear
), logwin
);
429 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
430 gtk_widget_show(menuitem
);