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