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