diff --git a/gnoemoe/widgets/gm-log-view.c b/gnoemoe/widgets/gm-log-view.c new file mode 100644 index 0000000..4e50d4b --- /dev/null +++ b/gnoemoe/widgets/gm-log-view.c @@ -0,0 +1,306 @@ +#include "gm-log-view.h" +#include "gm-searchable.h" +#include "../gm-app.h" +#include "../gm-color-table.h" +#include "../gm-support.h" +#include "../gm-options.h" +#include "../gm-debug.h" + +#define GM_LOG_VIEW_GET_PRIVATE(object)( \ + G_TYPE_INSTANCE_GET_PRIVATE((object), \ + GM_TYPE_LOG_VIEW, GmLogViewPrivate)) + +struct _GmLogViewPrivate { + GtkTextView *text_view; +}; + +/* Signals + +enum { + PROTO + NUM_SIGNALS +}; + +static guint gm_log_view_signals[NUM_SIGNALS] = {0};*/ +static void gm_log_view_searchable_iface_init( + GmSearchableInterface *iface); + +static GtkTextView *gm_log_view_searchable_get_text_view(GmSearchable *sea); +void on_gm_log_view_font_changed(GmColorTable *color_table, + gchar *font_description, GmLogView *view); +void on_gm_log_view_filter_toggled(GtkToggleButton *button, GmLogView *view); + +G_DEFINE_TYPE_EXTENDED(GmLogView, gm_log_view, GTK_TYPE_VBOX, 0, \ + G_IMPLEMENT_INTERFACE(GM_TYPE_SEARCHABLE, \ + gm_log_view_searchable_iface_init)) + +static void +gm_log_view_searchable_iface_init(GmSearchableInterface *iface) { + iface->get_text_view = gm_log_view_searchable_get_text_view; +} + +static GtkTextView * +gm_log_view_searchable_get_text_view(GmSearchable *sea) { + GmLogView *view = (GmLogView *)(sea); + + g_return_val_if_fail(GM_IS_LOG_VIEW(sea), NULL); + + return view->priv->text_view; +} + +static void +gm_log_view_finalize(GObject *object) { + //GmLogView *obj = GM_LOG_VIEW(object); + + g_signal_handlers_disconnect_by_func(gm_app_color_table(gm_app_instance()), + on_gm_log_view_font_changed, object); + G_OBJECT_CLASS(gm_log_view_parent_class)->finalize(object); +} + +static void +gm_log_view_class_init(GmLogViewClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GmOptions *options; + + object_class->finalize = gm_log_view_finalize; + + /*gm_log_view_signals[PROTO] = + g_signal_new("proto", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GmLogViewClass, proto), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0);*/ + + options = gm_app_options(gm_app_instance()); + + g_type_class_add_private(object_class, sizeof(GmLogViewPrivate)); +} + +static void +gm_log_view_create_tags(GmLogView *obj) { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(obj->priv->text_view); + gtk_text_buffer_create_tag(buffer, "in", NULL); + gtk_text_buffer_create_tag(buffer, "out", NULL); + gtk_text_buffer_create_tag(buffer, "status", NULL); + gtk_text_buffer_create_tag(buffer, "mcp_in", NULL); + gtk_text_buffer_create_tag(buffer, "mcp_out", NULL); + gtk_text_buffer_create_tag(buffer, "mcp_status", NULL); +} + +void +gm_log_view_update_font(GmLogView *view) { + PangoFontDescription *f = pango_font_description_from_string( + gm_color_table_font_description(gm_app_color_table( + gm_app_instance()))); + + if (f != NULL) { + gtk_widget_modify_font(GTK_WIDGET(view->priv->text_view), f); + pango_font_description_free(f); + } +} + +static void +gm_log_view_create_text_view(GmLogView *obj) { + GtkTextView *text_view = GTK_TEXT_VIEW(gtk_text_view_new()); + + gtk_text_view_set_editable(text_view, FALSE); + + // Margins + gtk_text_view_set_left_margin(text_view, 3); + gtk_text_view_set_right_margin(text_view, 3); + + // Set default wrapping mode + gtk_text_view_set_wrap_mode(text_view, GTK_WRAP_WORD_CHAR); + gtk_widget_show(GTK_WIDGET(text_view)); + obj->priv->text_view = text_view; + + gm_log_view_create_tags(obj); + gm_log_view_update_font(obj); +} + +GtkWidget * +gm_log_view_create_filter(GmLogView *obj) { + GtkWidget *box = gtk_hbox_new(FALSE, 6); + GtkWidget *label = gtk_label_new(_("Filter: ")); + GtkWidget *check; + GmOptions *options = gm_app_options(gm_app_instance()); + + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Incoming")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("in"), g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_in")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Outgoing")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("out"), g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_out")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Status")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("status"), + g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_status")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Mcp incoming")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("mcp_in"), + g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_mcp_in")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Mcp outgoing")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("mcp_out"), + g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_mcp_out")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + check = gtk_check_button_new_with_label(_("Mcp status")); + g_object_set_data_full(G_OBJECT(check), "option", g_strdup("mcp_status"), + g_free); + g_signal_connect(check, "toggled", + G_CALLBACK(on_gm_log_view_filter_toggled), obj); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), + gm_options_get_int(options, "logging_filter_mcp_status")); + gtk_box_pack_start(GTK_BOX(box), check, FALSE, FALSE, 0); + + gtk_container_set_border_width(GTK_CONTAINER(box), 3); + + gtk_widget_show_all(box); + return box; +} + +static void +gm_log_view_init(GmLogView *obj) { + GtkWidget *srl; + obj->priv = GM_LOG_VIEW_GET_PRIVATE(obj); + + gtk_box_set_spacing(GTK_BOX(obj), 6); + gtk_box_set_homogeneous(GTK_BOX(obj), FALSE); + + gm_log_view_create_text_view(obj); + //gtk_box_pack_start(GTK_BOX(obj), gm_log_view_create_filter(obj), FALSE, + // FALSE, 0); + + srl = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(srl), + GTK_SHADOW_IN); + + gtk_widget_show(srl); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(srl), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_container_add(GTK_CONTAINER(srl), GTK_WIDGET(obj->priv->text_view)); + gtk_box_pack_start(GTK_BOX(obj), srl, TRUE, TRUE, 0); + + g_signal_connect(gm_app_color_table(gm_app_instance()), "font_changed", + G_CALLBACK(on_gm_log_view_font_changed), obj); +} + +GmLogView * +gm_log_view_new() { + GmLogView *obj = GM_LOG_VIEW(g_object_new(GM_TYPE_LOG_VIEW, NULL)); + + return obj; +} + +gchar const * +gm_log_view_get_tag(gchar const *last) { + switch (last[11]) { + case '>': + return "out"; + break; + case '<': + return "in"; + break; + case '#': + return "status"; + break; + case '[': + switch (last[17]) { + case '>': + return "mcp_out"; + break; + case '<': + return "mcp_in"; + break; + case '#': + return "mcp_status"; + break; + } + break; + } + + return NULL; +} + +void +gm_log_view_set_text(GmLogView *view, gchar const *text) { + GtkTextBuffer *buffer = gtk_text_view_get_buffer(view->priv->text_view); + gchar *ptr; + gchar const *last = text, *tag; + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(buffer, &iter); + tag = gm_log_view_get_tag(text); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, text, -1, tag, NULL); + return; + + // log format: [hh:mm:ss] >|<|#|[MCP] <|[MCP] > text + while ((ptr = g_utf8_strchr(last, -1, '\n'))) { + gtk_text_buffer_get_end_iter(buffer, &iter); + tag = gm_log_view_get_tag(last); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, last, + ptr - last + 1, tag, NULL); + last = ptr + 1; + } + + if (*last != '\0') { + gtk_text_buffer_get_end_iter(buffer, &iter); + tag = gm_log_view_get_tag(last); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, last, + ptr - last + 1, tag, NULL); + } +} + +/* Callbacks */ + +void +on_gm_log_view_font_changed(GmColorTable *color_table, + gchar *font_description, GmLogView *view) { + gm_log_view_update_font(view); +} + +void +on_gm_log_view_filter_toggled(GtkToggleButton *button, GmLogView *view) { + gchar *option = (gchar *)g_object_get_data(G_OBJECT(button), "option"); + gchar *opt = g_strconcat("logging_filter_", option, NULL); + gboolean active = gtk_toggle_button_get_active(button); + GmOptions *options = gm_app_options(gm_app_instance()); + + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table( + gtk_text_view_get_buffer(view->priv->text_view)), option); + + g_object_set(G_OBJECT(tag), "invisible", !active, NULL); + + gm_options_set_int(options, opt, active); + g_free(opt); +} diff --git a/gnoemoe/widgets/gm-log-view.h b/gnoemoe/widgets/gm-log-view.h new file mode 100644 index 0000000..2e0afd8 --- /dev/null +++ b/gnoemoe/widgets/gm-log-view.h @@ -0,0 +1,58 @@ +#ifndef __GM_LOG_VIEW_H__ +#define __GM_LOG_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GM_TYPE_LOG_VIEW (gm_log_view_get_type()) +#define GM_LOG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GM_TYPE_LOG_VIEW, GmLogView)) +#define GM_LOG_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + GM_TYPE_LOG_VIEW, GmLogView const)) +#define GM_LOG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + GM_TYPE_LOG_VIEW, GmLogViewClass)) +#define GM_IS_LOG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + GM_TYPE_LOG_VIEW)) +#define GM_IS_LOG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GM_TYPE_LOG_VIEW)) +#define GM_LOG_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GM_TYPE_LOG_VIEW, GmLogViewClass)) + +/* Private structure type */ +typedef struct _GmLogViewPrivate GmLogViewPrivate; + +/* + * Main object structure + */ +typedef struct _GmLogView GmLogView; + +struct _GmLogView { + GtkVBox parent; + + /*< private > */ + GmLogViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GmLogViewClass GmLogViewClass; + +struct _GmLogViewClass { + GtkVBoxClass parent_class; + + /* Signals + void (* proto) (GmLogView *obj); */ +}; + +GType gm_log_view_get_type(void) G_GNUC_CONST; +GmLogView *gm_log_view_new(void); +void gm_log_view_set_text(GmLogView *view, gchar const *text); + +G_END_DECLS +#endif /* __GM_LOG_VIEW_H__ */