8a678d918a3f7d3342a261d4dda50ac5ecd1ce0b
[geeqie.git] / src / logwindow.c
1 /*
2  * Copyright (C) 2008 - 2016 The Geeqie Team
3  *
4  * Authors: Vladimir Nadvornik, Laurent Monin
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22 #include "logwindow.h"
23
24 #include "misc.h"
25 #include "window.h"
26
27 #include <gdk/gdkkeysyms.h>
28
29
30 typedef struct _LogWindow LogWindow;
31
32 struct _LogWindow
33 {
34         GtkWidget *window;
35         GtkWidget *scrolledwin;
36         GtkWidget *text;
37
38         GdkColor colors[LOG_COUNT];
39
40         guint lines;
41 };
42
43 typedef struct _LogDef LogDef;
44 struct _LogDef
45 {
46         LogType type;
47         const gchar *tag;
48         const gchar *color;
49 };
50
51 /* Keep LogType order !! */
52 static LogDef logdefs[LOG_COUNT] = {
53         { LOG_NORMAL,   "normal",       "black"  },
54         { LOG_MSG,      "message",      "blue"   },
55         { LOG_WARN,     "warning",      "orange" },
56         { LOG_ERROR,    "error",        "red"    },
57 };
58
59 static LogWindow *logwindow = NULL;
60
61 static void hide_cb(GtkWidget *widget, LogWindow *logwin)
62 {
63 }
64
65 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
66                             LogWindow *logwin)
67 {
68         if (event && event->keyval == GDK_KEY_Escape)
69                 gtk_widget_hide(logwin->window);
70         return FALSE;
71 }
72
73 static LogWindow *log_window_create(void)
74 {
75         LogWindow *logwin;
76         GtkWidget *window;
77         GtkWidget *scrolledwin;
78         GtkWidget *text;
79         GtkTextBuffer *buffer;
80         GtkTextIter iter;
81
82         logwin = g_new0(LogWindow, 1);
83
84         window = window_new(GTK_WINDOW_TOPLEVEL, "log", NULL, NULL, _("Log"));
85         gtk_widget_set_size_request(window, 520, 400);
86         g_signal_connect(G_OBJECT(window), "delete_event",
87                          G_CALLBACK(gtk_widget_hide_on_delete), NULL);
88         g_signal_connect(G_OBJECT(window), "key_press_event",
89                          G_CALLBACK(key_pressed), logwin);
90         g_signal_connect(G_OBJECT(window), "hide",
91                          G_CALLBACK(hide_cb), logwin);
92         gtk_widget_realize(window);
93
94         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
95         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
96                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
97         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
98                                             GTK_SHADOW_IN);
99         gtk_container_add(GTK_CONTAINER(window), scrolledwin);
100         gtk_widget_show(scrolledwin);
101
102         text = gtk_text_view_new();
103         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
104         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
105         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
106         gtk_text_buffer_get_start_iter(buffer, &iter);
107         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
108         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
109         gtk_widget_show(text);
110
111         logwin->window = window;
112         logwin->scrolledwin = scrolledwin;
113         logwin->text = text;
114         logwin->lines = 1;
115
116         return logwin;
117 }
118
119 static void log_window_init(LogWindow *logwin)
120 {
121         GtkTextBuffer *buffer;
122 #if !GTK_CHECK_VERSION(3,0,0)
123         GdkColormap *colormap;
124         gboolean success[LOG_COUNT];
125 #endif
126         gint i;
127
128         g_assert(logwin != NULL);
129         g_assert(logwin->colors != NULL);
130 #if !GTK_CHECK_VERSION(3,0,0)
131         for (i = LOG_NORMAL; i < LOG_COUNT; i++)
132                 {
133                 gboolean ok = gdk_color_parse(logdefs[i].color, &logwin->colors[i]);
134                 if (ok == TRUE) continue;
135
136                 if (i == LOG_NORMAL) return;
137                 memcpy(&logwin->colors[i], &logwin->colors[LOG_NORMAL], sizeof(GdkColor));
138                 }
139
140         colormap = gdk_drawable_get_colormap(gtk_widget_get_window(logwin->window));
141         gdk_colormap_alloc_colors(colormap, logwin->colors, LOG_COUNT, FALSE, TRUE, success);
142
143         for (i = LOG_NORMAL; i < LOG_COUNT; i++)
144                 {
145                 if (success[i] == FALSE)
146                         {
147                         GtkStyle *style;
148                         gint j;
149
150                         g_warning("LogWindow: color allocation failed\n");
151                         style = gtk_widget_get_style(logwin->window);
152                         for (j = LOG_NORMAL; j < LOG_COUNT; j++)
153                                 logwin->colors[j] = style->black;
154                         break;
155                         }
156                 }
157 #endif
158         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text));
159         for (i = LOG_NORMAL; i < LOG_COUNT; i++)
160                 gtk_text_buffer_create_tag(buffer, logdefs[i].tag,
161                                            "foreground-gdk", &logwin->colors[i],
162                                            "family", "MonoSpace",
163                                            NULL);
164 }
165
166 static void log_window_show(LogWindow *logwin)
167 {
168         GtkTextView *text = GTK_TEXT_VIEW(logwin->text);
169         GtkTextBuffer *buffer;
170         GtkTextMark *mark;
171
172         g_assert(logwin != NULL);
173
174         buffer = gtk_text_view_get_buffer(text);
175         mark = gtk_text_buffer_get_mark(buffer, "end");
176         gtk_text_view_scroll_mark_onscreen(text, mark);
177
178         gtk_window_present(GTK_WINDOW(logwin->window));
179
180         log_window_append("", LOG_NORMAL); // to flush memorized lines
181 }
182
183 void log_window_new(void)
184 {
185         if (logwindow == NULL)
186                 {
187                 LogWindow *logwin;
188
189                 logwin = log_window_create();
190                 log_window_init(logwin);
191                 logwindow = logwin;
192                 }
193
194         log_window_show(logwindow);
195 }
196
197 typedef struct _LogMsg LogMsg;
198
199 struct _LogMsg {
200         gchar *text;
201         LogType type;
202 };
203
204
205 static void log_window_insert_text(GtkTextBuffer *buffer, GtkTextIter *iter,
206                                    const gchar *text, const gchar *tag)
207 {
208         gchar *str_utf8;
209
210         if (!text || !*text) return;
211
212         str_utf8 = utf8_validate_or_convert(text);
213         gtk_text_buffer_insert_with_tags_by_name(buffer, iter, str_utf8, -1, tag, NULL);
214         g_free(str_utf8);
215 }
216
217
218 void log_window_append(const gchar *str, LogType type)
219 {
220         GtkTextView *text;
221         GtkTextBuffer *buffer;
222         GtkTextIter iter;
223         static GList *memory = NULL;
224
225         if (logwindow == NULL)
226                 {
227                 if (*str) {
228                         LogMsg *msg = g_new(LogMsg, 1);
229
230                         msg->text = g_strdup(str);
231                         msg->type = type;
232
233                         memory = g_list_prepend(memory, msg);
234
235                         while (g_list_length(memory) >= options->log_window_lines)
236                                 {
237                                 GList *work = g_list_last(memory);
238                                 LogMsg *oldest_msg = work->data;
239
240                                 g_free(oldest_msg->text);
241                                 memory = g_list_delete_link(memory, work);
242                                 }
243                         }
244                 return;
245                 }
246
247         text = GTK_TEXT_VIEW(logwindow->text);
248         buffer = gtk_text_view_get_buffer(text);
249
250         if (options->log_window_lines > 0 && logwindow->lines >= options->log_window_lines)
251                 {
252                 GtkTextIter start, end;
253
254                 gtk_text_buffer_get_start_iter(buffer, &start);
255                 end = start;
256                 gtk_text_iter_forward_lines(&end, logwindow->lines - options->log_window_lines);
257                 gtk_text_buffer_delete(buffer, &start, &end);
258                 }
259
260         gtk_text_buffer_get_end_iter(buffer, &iter);
261
262         {
263         GList *work = g_list_last(memory);
264
265         while (work)
266                 {
267                 GList *prev;
268                 LogMsg *oldest_msg = work->data;
269
270                 log_window_insert_text(buffer, &iter, oldest_msg->text, logdefs[oldest_msg->type].tag);
271
272                 prev = work->prev;
273                 memory = g_list_delete_link(memory, work);
274                 work = prev;
275                 }
276         }
277
278         log_window_insert_text(buffer, &iter, str, logdefs[type].tag);
279
280         if (gtk_widget_get_visible(GTK_WIDGET(text)))
281                 {
282                 GtkTextMark *mark;
283
284                 mark = gtk_text_buffer_get_mark(buffer, "end");
285                 gtk_text_view_scroll_mark_onscreen(text, mark);
286                 }
287
288         logwindow->lines = gtk_text_buffer_get_line_count(buffer);
289 }
290 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */