271 lines
9.3 KiB
Vala
271 lines
9.3 KiB
Vala
|
/*
|
||
|
Copyright (C) 2013 André Stösel <andre@stoesel.de>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Lesser General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
|
See the file COPYING for the full license text.
|
||
|
*/
|
||
|
|
||
|
Gtk.IconTheme theme;
|
||
|
|
||
|
namespace DevPet {
|
||
|
enum TreeCells {
|
||
|
MESSAGE,
|
||
|
BACKTRACE,
|
||
|
STOCK,
|
||
|
COUNT
|
||
|
}
|
||
|
|
||
|
private class DataWindow : Gtk.Window {
|
||
|
public string message {get; construct; }
|
||
|
public string backtrace {get; construct; }
|
||
|
|
||
|
private void create_content () {
|
||
|
this.title = this.message;
|
||
|
this.set_default_size (500, 500);
|
||
|
|
||
|
Gtk.VBox vbox = new Gtk.VBox (false, 1);
|
||
|
this.add (vbox);
|
||
|
|
||
|
Gtk.TextBuffer message_buffer = new Gtk.TextBuffer (null);
|
||
|
message_buffer.set_text (this.message);
|
||
|
|
||
|
Gtk.TextView message_text_view = new Gtk.TextView.with_buffer (message_buffer);
|
||
|
message_text_view.editable = false;
|
||
|
|
||
|
Gtk.TextBuffer backtrace_buffer = new Gtk.TextBuffer (null);
|
||
|
backtrace_buffer.set_text (this.backtrace);
|
||
|
|
||
|
Gtk.TextView backtrace_text_view = new Gtk.TextView.with_buffer (backtrace_buffer);
|
||
|
backtrace_text_view.editable = false;
|
||
|
|
||
|
Gtk.ScrolledWindow message_scroll = new Gtk.ScrolledWindow (null, null);
|
||
|
message_scroll.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||
|
message_scroll.add (message_text_view);
|
||
|
|
||
|
Gtk.ScrolledWindow backtrace_scroll = new Gtk.ScrolledWindow (null, null);
|
||
|
backtrace_scroll.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||
|
backtrace_scroll.add (backtrace_text_view);
|
||
|
|
||
|
vbox.pack_start (message_scroll, false, false, 0);
|
||
|
vbox.pack_end (backtrace_scroll, true, true, 0);
|
||
|
|
||
|
this.show_all ();
|
||
|
}
|
||
|
|
||
|
internal DataWindow (string message, string backtrace) {
|
||
|
GLib.Object (type: Gtk.WindowType.TOPLEVEL,
|
||
|
window_position: Gtk.WindowPosition.CENTER,
|
||
|
message: message,
|
||
|
backtrace: backtrace);
|
||
|
|
||
|
this.create_content ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class LogWindow : Gtk.Window {
|
||
|
private Manager manager;
|
||
|
|
||
|
private void clear_list () {
|
||
|
this.manager.clear_list ();
|
||
|
this.destroy ();
|
||
|
}
|
||
|
|
||
|
#if HAVE_EXECINFO_H
|
||
|
private void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
|
||
|
Gtk.TreeIter iter;
|
||
|
if (this.manager.list_store.get_iter (out iter, path)) {
|
||
|
string message;
|
||
|
string backtrace;
|
||
|
this.manager.list_store.get(iter,
|
||
|
TreeCells.MESSAGE, out message,
|
||
|
TreeCells.BACKTRACE, out backtrace, -1);
|
||
|
|
||
|
DataWindow data_window = new DataWindow (message, backtrace);
|
||
|
data_window.show ();
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
private void create_content () {
|
||
|
this.title = "Midori - DevPet";
|
||
|
this.set_default_size (500, 250);
|
||
|
this.destroy.connect (this.manager.log_window_closed);
|
||
|
|
||
|
Gtk.VBox vbox = new Gtk.VBox (false, 1);
|
||
|
this.add (vbox);
|
||
|
|
||
|
#if HAVE_EXECINFO_H
|
||
|
Gtk.Label label = new Gtk.Label (_("Double click for more information"));
|
||
|
vbox.pack_start (label, false, false, 0);
|
||
|
#endif
|
||
|
|
||
|
Gtk.ScrolledWindow scroll_windows = new Gtk.ScrolledWindow (null, null);
|
||
|
scroll_windows.set_policy (Gtk.PolicyType.NEVER , Gtk.PolicyType.AUTOMATIC);
|
||
|
scroll_windows.set_shadow_type (Gtk.ShadowType.ETCHED_IN);
|
||
|
|
||
|
|
||
|
Gtk.Button clear = new Gtk.Button.from_stock ("gtk-clear");
|
||
|
clear.clicked.connect (this.clear_list);
|
||
|
|
||
|
vbox.pack_start (scroll_windows, true, true, 0);
|
||
|
vbox.pack_start (clear, false, false, 0);
|
||
|
|
||
|
|
||
|
Gtk.TreeView treeview = new Gtk.TreeView.with_model (this.manager.list_store);
|
||
|
scroll_windows.add (treeview);
|
||
|
|
||
|
treeview.insert_column_with_attributes (
|
||
|
-1, "Type",
|
||
|
new Gtk.CellRendererPixbuf (), "stock-id", TreeCells.STOCK);
|
||
|
treeview.insert_column_with_attributes (
|
||
|
-1, "Message",
|
||
|
new Gtk.CellRendererText (), "text", TreeCells.MESSAGE);
|
||
|
|
||
|
#if HAVE_EXECINFO_H
|
||
|
treeview.row_activated.connect (this.row_activated);
|
||
|
#endif
|
||
|
|
||
|
this.show_all ();
|
||
|
}
|
||
|
|
||
|
internal LogWindow (Manager manager) {
|
||
|
GLib.Object (type: Gtk.WindowType.TOPLEVEL,
|
||
|
window_position: Gtk.WindowPosition.CENTER);
|
||
|
|
||
|
this.manager = manager;
|
||
|
this.create_content ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private class Manager : Midori.Extension {
|
||
|
public Gtk.ListStore list_store;
|
||
|
private Gtk.StatusIcon? trayicon = null;
|
||
|
private LogWindow? log_window;
|
||
|
private GLib.LogFunc default_log_func;
|
||
|
private GLib.LogLevelFlags icon_flag = GLib.LogLevelFlags.LEVEL_DEBUG;
|
||
|
|
||
|
public void clear_list() {
|
||
|
this.icon_flag = GLib.LogLevelFlags.LEVEL_DEBUG;
|
||
|
if(this.trayicon != null)
|
||
|
this.trayicon.set_visible (false);
|
||
|
this.list_store.clear ();
|
||
|
}
|
||
|
|
||
|
public void log_window_closed () {
|
||
|
this.log_window = null;
|
||
|
}
|
||
|
|
||
|
private unowned string get_stock_from_log_level (GLib.LogLevelFlags flags) {
|
||
|
if ((flags & LogLevelFlags.LEVEL_CRITICAL) == flags || (flags & LogLevelFlags.LEVEL_ERROR) == flags) {
|
||
|
return Gtk.Stock.DIALOG_ERROR;
|
||
|
} else if ((flags & LogLevelFlags.LEVEL_WARNING) == flags) {
|
||
|
return Gtk.Stock.DIALOG_WARNING;
|
||
|
}
|
||
|
return Gtk.Stock.DIALOG_INFO;
|
||
|
}
|
||
|
|
||
|
private void ensure_trayicon() {
|
||
|
if(this.trayicon != null)
|
||
|
return;
|
||
|
|
||
|
this.trayicon = new Gtk.StatusIcon ();
|
||
|
this.trayicon.set_tooltip_text ("Midori - DevPet");
|
||
|
this.trayicon.activate.connect(this.show_error_log);
|
||
|
}
|
||
|
|
||
|
private void log_handler(string? domain, GLib.LogLevelFlags flags, string message) {
|
||
|
Gtk.TreeIter iter;
|
||
|
unowned string stock = this.get_stock_from_log_level (flags);
|
||
|
|
||
|
this.ensure_trayicon();
|
||
|
|
||
|
if (flags < this.icon_flag) {
|
||
|
this.icon_flag = flags;
|
||
|
this.trayicon.set_from_stock (stock);
|
||
|
}
|
||
|
|
||
|
#if HAVE_EXECINFO_H
|
||
|
string bt = "";
|
||
|
void* buffer[100];
|
||
|
int num = Linux.backtrace (buffer, 100);
|
||
|
/* Upstream bug: https://git.gnome.org/browse/vala/commit/?id=f402af94e8471c8314ee7a312260a776e4d6fbe2 */
|
||
|
unowned string[] symbols = Midori.Linux.backtrace_symbols (buffer, num);
|
||
|
if (symbols != null) {
|
||
|
/* we don't need the first three lines */
|
||
|
for (int i = 3; i < num; i++) {
|
||
|
bt += "\r\n%s".printf(symbols[i]);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
this.list_store.append (out iter);
|
||
|
this.list_store.set (iter,
|
||
|
TreeCells.MESSAGE, message,
|
||
|
#if HAVE_EXECINFO_H
|
||
|
TreeCells.BACKTRACE, bt,
|
||
|
#endif
|
||
|
TreeCells.STOCK, stock);
|
||
|
|
||
|
this.trayicon.set_visible (true);
|
||
|
}
|
||
|
|
||
|
private void show_error_log () {
|
||
|
if (this.log_window == null) {
|
||
|
this.log_window = new LogWindow (this);
|
||
|
this.log_window.show ();
|
||
|
} else {
|
||
|
if (this.log_window.is_active) {
|
||
|
this.log_window.hide ();
|
||
|
} else {
|
||
|
this.log_window.present ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void activated (Midori.App app) {
|
||
|
this.default_log_func = GLib.Log.default_handler;
|
||
|
GLib.Log.set_default_handler (this.log_handler);
|
||
|
if (this.trayicon != null) {
|
||
|
int length = 0;
|
||
|
this.list_store.foreach((model, path, iter) => {
|
||
|
length++;
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
if (length > 0) {
|
||
|
this.trayicon.set_visible (true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void deactivated () {
|
||
|
if (this.trayicon != null)
|
||
|
this.trayicon.set_visible (false);
|
||
|
|
||
|
GLib.Log.set_default_handler (this.default_log_func);
|
||
|
}
|
||
|
|
||
|
internal Manager () {
|
||
|
GLib.Object (name: _("DevPet"),
|
||
|
description: _("This extension shows glib error messages in systray."),
|
||
|
version: "0.1",
|
||
|
authors: "André Stösel <andre@stoesel.de>");
|
||
|
|
||
|
this.list_store = new Gtk.ListStore (TreeCells.COUNT, typeof(string), typeof(string), typeof (string));
|
||
|
|
||
|
this.activate.connect (this.activated);
|
||
|
this.deactivate.connect (this.deactivated);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Midori.Extension extension_init () {
|
||
|
theme = Gtk.IconTheme.get_default ();
|
||
|
return new DevPet.Manager ();
|
||
|
}
|
||
|
|