diff --git a/gnoemoe/widgets/gm-editor-view.c b/gnoemoe/widgets/gm-editor-view.c
index c10fd3f..627ad5b 100644
--- a/gnoemoe/widgets/gm-editor-view.c
+++ b/gnoemoe/widgets/gm-editor-view.c
@@ -9,9 +9,12 @@
#include "../gm-pixbuf.h"
#include "../gm-support.h"
#include "../gm-debug.h"
+#include "../gm-string.h"
#include "../gm-options.h"
#include "../gm-color-table.h"
#include "../gm-app.h"
+#include "../parser/gm-parser.h"
+
#include "gm-searchable.h"
#define GM_EDITOR_VIEW_GET_PRIVATE(object)( \
@@ -22,6 +25,18 @@ struct _GmEditorViewPrivate {
GmWorld *world;
GmEditor *editor;
+ GtkExpander *expander;
+ GtkWidget *message_area;
+ GtkWidget *error_label;
+ GtkWidget *warning_label;
+ GtkWidget *error_frame;
+ GtkWidget *warning_frame;
+
+ gboolean set_style;
+ guint timeout_handler;
+ guint hide_error_handler;
+ gboolean expanding;
+ gboolean was_expanded;
GtkSourceView *source_view;
};
@@ -47,12 +62,19 @@ void on_gm_editor_view_saveclose_clicked(GtkToolButton *button,
GmEditorView *view);
void on_gm_editor_view_close_clicked(GtkToolButton *button,
GmEditorView *view);
+void on_gm_editor_view_execute_clicked(GtkToolButton *button,
+ GmEditorView *view);
+
void on_gm_editor_view_editor_saved(GmEditor *editor, GmEditorView *view);
void on_gm_editor_view_font_changed(GmColorTable *color_table,
gchar const *desc, GmEditorView *view);
void on_gm_editor_view_modified_changed(GtkTextBuffer *buffer,
GmEditorView *view);
+void on_gm_editor_view_changed(GtkTextBuffer *buffer,
+ GmEditorView *view);
+void on_gm_editor_view_expander(GObject *object, GParamSpec *param_spec,
+ GmEditorView *view);
G_DEFINE_TYPE_EXTENDED(GmEditorView, gm_editor_view, GTK_TYPE_VBOX, 0, \
G_IMPLEMENT_INTERFACE(GM_TYPE_SEARCHABLE, \
@@ -74,7 +96,14 @@ gm_editor_view_searchable_get_text_view(GmSearchable *sea) {
static void
gm_editor_view_finalize(GObject *object) {
- //GmEditorView *obj = GM_EDITOR_VIEW(object);
+ GmEditorView *obj = GM_EDITOR_VIEW(object);
+
+ if (obj->priv->timeout_handler) {
+ g_source_remove(obj->priv->timeout_handler);
+ }
+ if (obj->priv->hide_error_handler) {
+ g_source_remove(obj->priv->hide_error_handler);
+ }
G_OBJECT_CLASS(gm_editor_view_parent_class)->finalize(object);
}
@@ -111,7 +140,7 @@ gm_editor_view_class_init(GmEditorViewClass *klass) {
}
GtkSourceView *
-gm_editor_create_source_view(GmEditorView *view) {
+gm_editor_view_create_source_view(GmEditorView *view) {
GtkTextBuffer *buffer;
GtkWidget *source_view;
GtkTextIter iter;
@@ -128,6 +157,7 @@ gm_editor_create_source_view(GmEditorView *view) {
gtk_source_buffer_set_language(GTK_SOURCE_BUFFER(buffer), language);
}
+ gtk_source_buffer_begin_not_undoable_action(GTK_SOURCE_BUFFER(buffer));
gtk_text_buffer_get_end_iter(buffer, &iter);
for (lines = gm_editor_lines(view->priv->editor); lines;
@@ -140,6 +170,7 @@ gm_editor_create_source_view(GmEditorView *view) {
}
}
+ gtk_source_buffer_end_not_undoable_action(GTK_SOURCE_BUFFER(buffer));
gtk_text_buffer_set_modified(buffer, FALSE);
st = gtk_source_tag_style_new();
@@ -169,19 +200,109 @@ gm_editor_create_source_view(GmEditorView *view) {
gtk_source_buffer_set_highlight(GTK_SOURCE_BUFFER(buffer), FALSE);
}
+ gtk_text_buffer_create_tag(buffer, "gm_error", "underline",
+ PANGO_UNDERLINE_ERROR, NULL);
+
g_signal_connect(buffer, "modified_changed",
G_CALLBACK(on_gm_editor_view_modified_changed), view);
+ g_signal_connect(buffer, "changed",
+ G_CALLBACK(on_gm_editor_view_changed), view);
+
gtk_widget_show(source_view);
return GTK_SOURCE_VIEW(source_view);
}
+static gboolean
+gm_editor_view_paint_message_area(GtkWidget *widget, GdkEventExpose *event,
+ GmEditorView *view) {
+ gtk_paint_flat_box(widget->style, widget->window, GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT, NULL, widget, "tooltip",
+ widget->allocation.x + 1, widget->allocation.y + 1,
+ widget->allocation.width - 2, widget->allocation.height - 2);
+
+ return FALSE;
+}
+
+static void
+gm_editor_view_message_area_style_set(GtkWidget *widget, GtkStyle *prev,
+ GmEditorView *view) {
+ GtkTooltips *tooltips;
+ GtkStyle *style;
+
+ if (view->priv->set_style) {
+ return;
+ }
+
+ tooltips = gtk_tooltips_new();
+ g_object_ref(G_OBJECT(tooltips));
+ gtk_object_sink(GTK_OBJECT(tooltips));
+
+ gtk_tooltips_force_window(tooltips);
+ gtk_widget_ensure_style(tooltips->tip_window);
+ style = gtk_widget_get_style(tooltips->tip_window);
+
+ view->priv->set_style = TRUE;
+ gtk_widget_set_style(widget, style);
+ view->priv->set_style = FALSE;
+ g_object_unref(tooltips);
+}
+
+static void
+gm_editor_view_create_message_area(GmEditorView *view) {
+ GtkWidget *message_area = gtk_vbox_new(FALSE, 6);
+ GtkWidget *align;
+ GtkWidget *lbl;
+ gtk_container_set_border_width(GTK_CONTAINER(message_area), 10);
+
+ g_signal_connect(message_area, "expose_event",
+ G_CALLBACK(gm_editor_view_paint_message_area), view);
+ g_signal_connect(message_area, "style-set",
+ G_CALLBACK(gm_editor_view_message_area_style_set), view);
+
+ view->priv->message_area = message_area;
+
+ lbl = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(lbl), _("Errors"));
+ view->priv->error_frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(view->priv->error_frame),
+ GTK_SHADOW_NONE);
+ gtk_frame_set_label_widget(GTK_FRAME(view->priv->error_frame), lbl);
+
+ view->priv->error_label = gtk_label_new(NULL);
+
+ gtk_misc_set_alignment(GTK_MISC(view->priv->error_label), 0.0, 0.0);
+ align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
+
+ gtk_container_add(GTK_CONTAINER(align), view->priv->error_label);
+ gtk_container_add(GTK_CONTAINER(view->priv->error_frame), align);
+ gtk_box_pack_start(GTK_BOX(message_area), view->priv->error_frame, TRUE,
+ TRUE, 0);
+
+ lbl = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(lbl), _("Warnings"));
+ view->priv->warning_frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(view->priv->warning_frame),
+ GTK_SHADOW_NONE);
+ gtk_frame_set_label_widget(GTK_FRAME(view->priv->warning_frame), lbl);
+ view->priv->warning_label = gtk_label_new(NULL);
+
+ gtk_misc_set_alignment(GTK_MISC(view->priv->warning_label), 0.0, 0.0);
+ align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
+
+ gtk_container_add(GTK_CONTAINER(align), view->priv->warning_label);
+ gtk_container_add(GTK_CONTAINER(view->priv->warning_frame), align);
+ gtk_box_pack_start(GTK_BOX(message_area), view->priv->warning_frame, TRUE, TRUE, 0);
+}
+
static void
gm_editor_view_init(GmEditorView *obj) {
- GtkWidget *toolbar, *item;
+ GtkWidget *toolbar, *item, *expander, *tmp;
obj->priv = GM_EDITOR_VIEW_GET_PRIVATE(obj);
- gtk_box_set_spacing(GTK_BOX(obj), 6);
+ gtk_box_set_spacing(GTK_BOX(obj), 0);
gtk_box_set_homogeneous(GTK_BOX(obj), FALSE);
toolbar = gtk_toolbar_new();
@@ -202,8 +323,32 @@ gm_editor_view_init(GmEditorView *obj) {
G_CALLBACK(on_gm_editor_view_close_clicked), obj);
gtk_container_add(GTK_CONTAINER(toolbar), item);
+ item = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_EXECUTE));
+ g_signal_connect(item, "clicked",
+ G_CALLBACK(on_gm_editor_view_execute_clicked), obj);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_separator_tool_item_new());
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
+ item = GTK_WIDGET(gtk_tool_item_new());
+ tmp = gtk_alignment_new(0.0, 1.0, 0.0, 0.0);
+ expander = gtk_expander_new(NULL);
+ g_signal_connect(expander, "notify::expanded",
+ G_CALLBACK(on_gm_editor_view_expander), obj);
+
+ gtk_container_add(GTK_CONTAINER(tmp), expander);
+ gtk_container_add(GTK_CONTAINER(item), tmp);
+ gtk_container_add(GTK_CONTAINER(toolbar), item);
+
gtk_box_pack_start(GTK_BOX(obj), toolbar, FALSE, FALSE, 0);
gtk_widget_show_all(toolbar);
+
+ gtk_widget_hide(expander);
+ obj->priv->expander = GTK_EXPANDER(expander);
+
+ gm_editor_view_create_message_area(obj);
+ gtk_box_pack_start(GTK_BOX(obj), obj->priv->message_area, FALSE, FALSE, 0);
}
GmEditorView *
@@ -213,7 +358,7 @@ gm_editor_view_new(GmWorld *world, GmEditor *editor) {
obj->priv->editor = editor;
obj->priv->world = world;
- obj->priv->source_view = gm_editor_create_source_view(obj);
+ obj->priv->source_view = gm_editor_view_create_source_view(obj);
srl = gtk_scrolled_window_new(NULL, NULL);
gtk_widget_show(srl);
@@ -221,7 +366,7 @@ gm_editor_view_new(GmWorld *world, GmEditor *editor) {
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(srl), GTK_WIDGET(obj->priv->source_view));
- gtk_box_pack_start(GTK_BOX(obj), srl, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(obj), srl, TRUE, TRUE, 0);
g_signal_connect(gm_app_color_table(gm_app_instance()), "font_changed",
G_CALLBACK(on_gm_editor_view_font_changed), obj);
@@ -255,6 +400,179 @@ gm_editor_view_save(GmEditorView *view) {
gm_editor_save(view->priv->editor);
}
+gboolean
+gm_editor_view_no_errors(GmEditorView *view) {
+ gtk_widget_hide(GTK_WIDGET(view->priv->expander));
+ return FALSE;
+}
+
+void
+gm_editor_view_parse_error(GmEditorView *view, GmParserError *error,
+ GString **str) {
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
+ view->priv->source_view));
+ GtkTextIter start, end;
+ gchar *msg;
+
+ if (error->ch == 0) {
+ gtk_text_buffer_get_iter_at_line(buffer, &start,
+ error->line - 1);
+ msg = g_markup_printf_escaped(_("Line %d: %s\n"), error->line,
+ error->message);
+ } else {
+ // Underline line
+ gtk_text_buffer_get_iter_at_line(buffer, &start, error->line);
+ msg = g_markup_printf_escaped(_("Line %d:%d: %s\n"),
+ error->line + 1, error->ch + 1, error->message);
+ }
+
+ if (!*str) {
+ *str = g_string_new(msg);
+ } else {
+ *str = g_string_append(*str, msg);
+ }
+
+ g_free(msg);
+ end = start;
+
+ if (error->ch != 0) {
+ gtk_text_iter_forward_chars(&start, error->ch - 1);
+ }
+
+ gtk_text_iter_forward_to_line_end(&end);
+ gtk_text_buffer_apply_tag_by_name(buffer, "gm_error", &start, &end);
+}
+
+enum {
+ SYNTAX_OK = 0,
+ SYNTAX_ERRORS = 1 << 0,
+ SYNTAX_WARNINGS = 1 << 1
+};
+
+int
+gm_editor_view_parse(GmEditorView *view) {
+ gchar *text;
+ GtkTextIter start, end;
+ GList *lines;
+ GmParserResult *result;
+ GList *errors;
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
+ view->priv->source_view));
+ gchar *msg;
+ GString *str = NULL, *lbl = NULL;
+ int ret = SYNTAX_OK;
+
+ if (view->priv->hide_error_handler) {
+ g_source_remove(view->priv->hide_error_handler);
+ }
+
+ gtk_text_buffer_get_bounds(buffer, &start, &end);
+ text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
+ lines = gm_string_split(text);
+
+ result = gm_parser_parse(lines);
+
+ gtk_text_buffer_remove_tag_by_name(buffer, "gm_error", &start, &end);
+
+ if (!result->isOk) {
+ if (result->errors) {
+ for (errors = result->errors; errors; errors = errors->next) {
+ gm_editor_view_parse_error(view, (GmParserError *)(errors->data),
+ &str);
+ }
+
+ str = g_string_truncate(str, str->len - 1);
+ gtk_label_set_markup(GTK_LABEL(view->priv->error_label), str->str);
+ gtk_widget_show_all(view->priv->error_frame);
+ g_string_free(str, TRUE);
+ str = NULL;
+
+ if (g_list_length(result->errors) == 1) {
+ msg = g_strdup(_("There is 1 error"));
+ } else {
+ msg = g_strdup_printf(_("There are %d errors"),
+ g_list_length(result->errors));
+ }
+
+ lbl = g_string_new(msg);
+ g_free(msg);
+
+ ret |= SYNTAX_ERRORS;
+ } else {
+ gtk_widget_hide(view->priv->error_frame);
+ }
+ if (result->warnings) {
+ for (errors = result->warnings; errors; errors = errors->next) {
+ gm_editor_view_parse_error(view, (GmParserError *)(errors->data),
+ &str);
+ }
+
+ str = g_string_truncate(str, str->len - 1);
+ gtk_label_set_markup(GTK_LABEL(view->priv->warning_label),
+ str->str);
+ gtk_widget_show_all(view->priv->warning_frame);
+ g_string_free(str, TRUE);
+
+ if (g_list_length(result->warnings) == 1) {
+ if (!lbl) {
+ msg = g_strdup(_("There is 1 warning"));
+ } else {
+ msg = g_strdup_printf(_(" and 1 warning"));
+ }
+ } else {
+ if (!lbl) {
+ msg = g_strdup_printf(_("There are %d warnings"),
+ g_list_length(result->warnings));
+ } else {
+ msg = g_strdup_printf(_(" and %d warnings"),
+ g_list_length(result->warnings));
+ }
+ }
+
+ if (!lbl) {
+ lbl = g_string_new(msg);
+ } else {
+ lbl = g_string_append(lbl, msg);
+ }
+
+ g_free(msg);
+
+ ret |= SYNTAX_WARNINGS;
+ } else {
+ gtk_widget_hide(view->priv->warning_frame);
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(view->priv->expander), TRUE);
+ gtk_expander_set_label(view->priv->expander, lbl->str);
+ g_string_free(lbl, TRUE);
+ gtk_widget_show(GTK_WIDGET(view->priv->expander));
+
+ if (view->priv->was_expanded &&
+ !gtk_expander_get_expanded(view->priv->expander)) {
+ view->priv->expanding = TRUE;
+ gtk_expander_set_expanded(view->priv->expander, TRUE);
+ view->priv->expanding = FALSE;
+ }
+ } else {
+ gtk_expander_set_label(view->priv->expander, _("There are no errors"));
+
+ view->priv->expanding = TRUE;
+ gtk_expander_set_expanded(view->priv->expander, FALSE);
+ view->priv->expanding = FALSE;
+
+ gtk_widget_set_sensitive(GTK_WIDGET(view->priv->expander), FALSE);
+ gtk_widget_hide(view->priv->message_area);
+ view->priv->hide_error_handler = g_timeout_add(3000,
+ (GSourceFunc)gm_editor_view_no_errors, view);
+ }
+
+ gm_parser_result_free(result);
+ gm_g_list_free_simple(lines);
+ g_free(text);
+
+ return ret;
+}
+
/* Callbacks */
void
@@ -296,9 +614,64 @@ on_gm_editor_view_close_clicked(GtkToolButton *button,
gm_editor_close(view->priv->editor);
}
+void
+on_gm_editor_view_execute_clicked(GtkToolButton *button,
+ GmEditorView *view) {
+ int ret = gm_editor_view_parse(view);
+
+ if (ret == SYNTAX_OK) {
+ gtk_expander_set_expanded(view->priv->expander, FALSE);
+ gtk_widget_hide(view->priv->message_area);
+ gm_info_dialog(_("No errors are found"), NULL);
+ } else {
+ gtk_expander_set_expanded(view->priv->expander, TRUE);
+ gtk_widget_show_all(view->priv->message_area);
+ }
+}
+
+
void
on_gm_editor_view_modified_changed(GtkTextBuffer *buffer,
GmEditorView *view) {
g_signal_emit(view, gm_editor_view_signals[MODIFIED_CHANGED], 0,
gtk_text_buffer_get_modified(buffer));
}
+
+gboolean
+gm_editor_view_timeout(GmEditorView *view) {
+ gm_editor_view_parse(view);
+ view->priv->timeout_handler = 0;
+ return FALSE;
+}
+
+void
+on_gm_editor_view_changed(GtkTextBuffer *buffer,
+ GmEditorView *view) {
+ if (view->priv->timeout_handler != 0) {
+ g_source_remove(view->priv->timeout_handler);
+ }
+
+ if (gm_options_get_int(gm_app_options(gm_app_instance()),
+ "auto_check_syntax")) {
+ view->priv->timeout_handler = g_timeout_add(500,
+ (GSourceFunc)gm_editor_view_timeout, view);
+ }
+}
+
+void
+on_gm_editor_view_expander(GObject *object, GParamSpec *param_spec,
+ GmEditorView *view) {
+ if (gtk_expander_get_expanded(view->priv->expander)) {
+ if (!view->priv->expanding) {
+ view->priv->was_expanded = TRUE;
+ }
+
+ gtk_widget_show(view->priv->message_area);
+ } else {
+ if (!view->priv->expanding) {
+ view->priv->was_expanded = FALSE;
+ }
+
+ gtk_widget_hide(view->priv->message_area);
+ }
+}