mark PurpleImageClass as private
[pidgin-git.git] / finch / gntdebug.c
blob916d8c5ba63ba2ae6d84644cde249610b9be337e
1 /* finch
3 * Finch is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include <internal.h>
24 #include <gnt.h>
25 #include <gntbox.h>
26 #include <gntbutton.h>
27 #include <gntcheckbox.h>
28 #include <gntentry.h>
29 #include <gntfilesel.h>
30 #include <gntlabel.h>
31 #include <gntline.h>
32 #include <gnttextview.h>
34 #include "gntdebug.h"
35 #include "finch.h"
36 #include "notify.h"
37 #include "util.h"
39 #include <stdio.h>
40 #include <string.h>
42 #define PREF_ROOT "/finch/debug"
44 struct _FinchDebugUi
46 GObject parent;
48 /* Other members, including private data. */
51 static void finch_debug_ui_finalize(GObject *gobject);
52 static void finch_debug_ui_interface_init(PurpleDebugUiInterface *iface);
54 G_DEFINE_TYPE_WITH_CODE(FinchDebugUi, finch_debug_ui, G_TYPE_OBJECT,
55 G_IMPLEMENT_INTERFACE(PURPLE_TYPE_DEBUG_UI,
56 finch_debug_ui_interface_init));
58 static gboolean
59 handle_fprintf_stderr_cb(GIOChannel *source, GIOCondition cond, gpointer null)
61 gssize size;
62 char message[1024];
64 size = read(g_io_channel_unix_get_fd(source), message, sizeof(message) - 1);
65 if (size <= 0) {
66 /* Something bad probably happened elsewhere ... let's ignore */
67 } else {
68 message[size] = '\0';
69 g_log("stderr", G_LOG_LEVEL_WARNING, "%s", message);
72 return TRUE;
75 static void
76 handle_fprintf_stderr(gboolean stop)
78 GIOChannel *stderrch;
79 static int readhandle = -1;
80 int pipes[2];
82 if (stop) {
83 if (readhandle >= 0) {
84 g_source_remove(readhandle);
85 readhandle = -1;
87 return;
89 if (purple_input_pipe(pipes)) {
90 readhandle = -1;
91 return;
93 dup2(pipes[1], STDERR_FILENO);
95 stderrch = g_io_channel_unix_new(pipes[0]);
96 g_io_channel_set_close_on_unref(stderrch, TRUE);
97 readhandle = g_io_add_watch_full(stderrch, G_PRIORITY_HIGH,
98 G_IO_IN | G_IO_ERR | G_IO_PRI,
99 handle_fprintf_stderr_cb, NULL, NULL);
100 g_io_channel_unref(stderrch);
103 static struct
105 GntWidget *window;
106 GntWidget *tview;
107 GntWidget *search;
108 gboolean paused;
109 } debug;
111 static gboolean
112 match_string(const char *category, const char *args)
114 const char *str = gnt_entry_get_text(GNT_ENTRY(debug.search));
115 if (!str || !*str)
116 return TRUE;
117 if (g_strrstr(category, str) != NULL)
118 return TRUE;
119 if (g_strrstr(args, str) != NULL)
120 return TRUE;
121 return FALSE;
124 static void
125 finch_debug_print(PurpleDebugUi *self,
126 PurpleDebugLevel level, const char *category,
127 const char *args)
129 if (debug.window && !debug.paused && match_string(category, args))
131 int pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(debug.tview));
132 GntTextFormatFlags flag = GNT_TEXT_FLAG_NORMAL;
133 const char *mdate;
134 time_t mtime = time(NULL);
135 mdate = purple_utf8_strftime("%H:%M:%S ", localtime(&mtime));
136 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview),
137 mdate, flag);
139 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview),
140 category, GNT_TEXT_FLAG_BOLD);
141 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview),
142 ": ", GNT_TEXT_FLAG_BOLD);
144 switch (level)
146 case PURPLE_DEBUG_WARNING:
147 flag |= GNT_TEXT_FLAG_UNDERLINE;
148 /* fall through */
149 case PURPLE_DEBUG_ERROR:
150 case PURPLE_DEBUG_FATAL:
151 flag |= GNT_TEXT_FLAG_BOLD;
152 break;
153 default:
154 break;
157 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), args, flag);
158 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), "\n", GNT_TEXT_FLAG_NORMAL);
159 if (pos <= 1)
160 gnt_text_view_scroll(GNT_TEXT_VIEW(debug.tview), 0);
164 static gboolean
165 finch_debug_is_enabled(PurpleDebugUi *self, PurpleDebugLevel level, const char *category)
167 return debug.window && !debug.paused;
170 static void
171 finch_debug_ui_interface_init(PurpleDebugUiInterface *iface)
173 iface->print = finch_debug_print;
174 iface->is_enabled = finch_debug_is_enabled;
177 static void
178 reset_debug_win(GntWidget *w, gpointer null)
180 debug.window = debug.tview = debug.search = NULL;
183 static void
184 clear_debug_win(GntWidget *w, GntTextView *tv)
186 gnt_text_view_clear(tv);
189 static void
190 print_stderr(const char *string)
192 g_printerr("%s", string);
195 static void
196 toggle_pause(GntWidget *w, gpointer n)
198 debug.paused = !debug.paused;
201 /* Xerox */
202 static void
203 purple_glib_log_handler(const gchar *domain, GLogLevelFlags flags,
204 const gchar *msg, gpointer user_data)
206 PurpleDebugLevel level;
207 char *new_msg = NULL;
208 char *new_domain = NULL;
210 if ((flags & G_LOG_LEVEL_ERROR) == G_LOG_LEVEL_ERROR)
211 level = PURPLE_DEBUG_ERROR;
212 else if ((flags & G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL)
213 level = PURPLE_DEBUG_FATAL;
214 else if ((flags & G_LOG_LEVEL_WARNING) == G_LOG_LEVEL_WARNING)
215 level = PURPLE_DEBUG_WARNING;
216 else if ((flags & G_LOG_LEVEL_MESSAGE) == G_LOG_LEVEL_MESSAGE)
217 level = PURPLE_DEBUG_INFO;
218 else if ((flags & G_LOG_LEVEL_INFO) == G_LOG_LEVEL_INFO)
219 level = PURPLE_DEBUG_INFO;
220 else if ((flags & G_LOG_LEVEL_DEBUG) == G_LOG_LEVEL_DEBUG)
221 level = PURPLE_DEBUG_MISC;
222 else
224 purple_debug_warning("gntdebug",
225 "Unknown glib logging level in %d\n", flags);
227 level = PURPLE_DEBUG_MISC; /* This will never happen. */
230 if (msg != NULL)
231 new_msg = purple_utf8_try_convert(msg);
233 if (domain != NULL)
234 new_domain = purple_utf8_try_convert(domain);
236 if (new_msg != NULL)
238 purple_debug(level, (new_domain != NULL ? new_domain : "g_log"),
239 "%s\n", new_msg);
241 g_free(new_msg);
244 g_free(new_domain);
247 static void
248 size_changed_cb(GntWidget *widget, int oldw, int oldh)
250 int w, h;
251 gnt_widget_get_size(widget, &w, &h);
252 purple_prefs_set_int(PREF_ROOT "/size/width", w);
253 purple_prefs_set_int(PREF_ROOT "/size/height", h);
256 static gboolean
257 for_real(gpointer entry)
259 purple_prefs_set_string(PREF_ROOT "/filter", gnt_entry_get_text(entry));
260 return FALSE;
263 static void
264 update_filter_string(GntEntry *entry, gpointer null)
266 int id = g_timeout_add(1000, for_real, entry);
267 g_object_set_data_full(G_OBJECT(entry), "update-filter", GINT_TO_POINTER(id),
268 (GDestroyNotify)g_source_remove);
271 static void
272 file_save(GntFileSel *fs, const char *path, const char *file, GntTextView *tv)
274 FILE *fp;
276 if ((fp = g_fopen(path, "w+")) == NULL) {
277 purple_notify_error(NULL, NULL, _("Unable to open file."), NULL, NULL);
278 return;
281 fprintf(fp, "Finch Debug Log : %s\n", purple_date_format_full(NULL));
282 fprintf(fp, "%s", gnt_text_view_get_text(tv));
283 fclose(fp);
284 gnt_widget_destroy(GNT_WIDGET(fs));
287 static void
288 save_debug_win(GntWidget *w, GntTextView *tv)
290 GntWidget *window = gnt_file_sel_new();
291 GntFileSel *sel = GNT_FILE_SEL(window);
292 gnt_file_sel_set_current_location(sel, purple_home_dir());
293 gnt_file_sel_set_suggested_filename(sel, "debug.txt");
294 g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(file_save), tv);
295 g_signal_connect(G_OBJECT(sel), "cancelled", G_CALLBACK(gnt_widget_destroy), NULL);
296 gnt_widget_show(window);
299 void finch_debug_window_show()
301 GntWidget *wid, *box, *label;
303 debug.paused = FALSE;
304 if (debug.window) {
305 gnt_window_present(debug.window);
306 return;
309 debug.window = gnt_vbox_new(FALSE);
310 gnt_box_set_toplevel(GNT_BOX(debug.window), TRUE);
311 gnt_box_set_title(GNT_BOX(debug.window), _("Debug Window"));
312 gnt_box_set_pad(GNT_BOX(debug.window), 0);
313 gnt_box_set_alignment(GNT_BOX(debug.window), GNT_ALIGN_MID);
315 debug.tview = gnt_text_view_new();
316 gnt_box_add_widget(GNT_BOX(debug.window), debug.tview);
317 gnt_widget_set_size(debug.tview,
318 purple_prefs_get_int(PREF_ROOT "/size/width"),
319 purple_prefs_get_int(PREF_ROOT "/size/height"));
320 g_signal_connect(G_OBJECT(debug.tview), "size_changed", G_CALLBACK(size_changed_cb), NULL);
322 gnt_box_add_widget(GNT_BOX(debug.window), gnt_line_new(FALSE));
324 box = gnt_hbox_new(FALSE);
325 gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
326 gnt_box_set_fill(GNT_BOX(box), FALSE);
328 /* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
329 * it's necessary to make the width of the debug window resizable ... like I said,
330 * it doesn't make sense. The bug is likely in the packing in gntbox.c.
332 wid = gnt_button_new(_("Clear"));
333 g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(clear_debug_win), debug.tview);
334 gnt_widget_set_grow_y(wid, TRUE);
335 gnt_box_add_widget(GNT_BOX(box), wid);
337 wid = gnt_button_new(_("Save"));
338 g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(save_debug_win), debug.tview);
339 gnt_widget_set_grow_y(wid, TRUE);
340 gnt_box_add_widget(GNT_BOX(box), wid);
342 debug.search = gnt_entry_new(purple_prefs_get_string(PREF_ROOT "/filter"));
343 label = gnt_label_new(_("Filter:"));
344 gnt_widget_set_grow_x(label, FALSE);
345 gnt_box_add_widget(GNT_BOX(box), label);
346 gnt_box_add_widget(GNT_BOX(box), debug.search);
347 g_signal_connect(G_OBJECT(debug.search), "text_changed", G_CALLBACK(update_filter_string), NULL);
349 wid = gnt_check_box_new(_("Pause"));
350 g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_pause), NULL);
351 gnt_widget_set_grow_y(wid, TRUE);
352 gnt_box_add_widget(GNT_BOX(box), wid);
354 gnt_box_add_widget(GNT_BOX(debug.window), box);
355 gnt_widget_set_grow_y(box, TRUE);
357 gnt_widget_set_name(debug.window, "debug-window");
359 g_signal_connect(G_OBJECT(debug.window), "destroy", G_CALLBACK(reset_debug_win), NULL);
360 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
361 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(debug.tview), debug.window);
363 gnt_widget_show(debug.window);
366 static gboolean
367 start_with_debugwin(gpointer null)
369 finch_debug_window_show();
370 return FALSE;
373 static void
374 finch_debug_ui_class_init(FinchDebugUiClass *klass)
376 GObjectClass *object_class = G_OBJECT_CLASS(klass);
378 object_class->finalize = finch_debug_ui_finalize;
381 static void
382 finch_debug_ui_init(FinchDebugUi *self)
384 /* Xerox */
385 #define REGISTER_G_LOG_HANDLER(name) \
386 g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
387 | G_LOG_FLAG_RECURSION, \
388 purple_glib_log_handler, NULL)
390 /* Register the glib log handlers. */
391 REGISTER_G_LOG_HANDLER(NULL);
392 REGISTER_G_LOG_HANDLER("GLib");
393 REGISTER_G_LOG_HANDLER("GModule");
394 REGISTER_G_LOG_HANDLER("GLib-GObject");
395 REGISTER_G_LOG_HANDLER("GThread");
396 REGISTER_G_LOG_HANDLER("Gnt");
397 #ifdef USE_GSTREAMER
398 REGISTER_G_LOG_HANDLER("GStreamer");
399 #endif
400 REGISTER_G_LOG_HANDLER("stderr");
402 g_set_print_handler(print_stderr); /* Redirect the debug messages to stderr */
403 if (!purple_debug_is_enabled())
404 handle_fprintf_stderr(FALSE);
406 purple_prefs_add_none(PREF_ROOT);
407 purple_prefs_add_string(PREF_ROOT "/filter", "");
408 purple_prefs_add_none(PREF_ROOT "/size");
409 purple_prefs_add_int(PREF_ROOT "/size/width", 60);
410 purple_prefs_add_int(PREF_ROOT "/size/height", 15);
412 if (purple_debug_is_enabled())
413 g_timeout_add(0, start_with_debugwin, NULL);
416 static void
417 finch_debug_ui_finalize(GObject *gobject)
419 handle_fprintf_stderr(TRUE);
420 G_OBJECT_CLASS(finch_debug_ui_parent_class)->finalize(gobject);
423 FinchDebugUi *
424 finch_debug_ui_new(void)
426 return g_object_new(FINCH_TYPE_DEBUG_UI, NULL);