summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Hacohen <tom@stosb.com>2016-05-27 16:48:27 +0100
committerTom Hacohen <tom@stosb.com>2016-05-27 16:49:57 +0100
commit9f129a8711f9bb71ee7cf6c13f7965926255c975 (patch)
tree1496c258c4ae581e389e1062b5f70fe8f5b33bcc
parent072674dbe2118a26d8616b56b4169a6e543805ef (diff)
parent1aa32dc591f757c6cbd305dcb75f81d063df83c6 (diff)
downloadefl-9f129a8711f9bb71ee7cf6c13f7965926255c975.tar.gz
Merge in elm code widget
This commit only merges the code itself and doesn't integrate it into the build system. This is the first step towards the merge. Elm code is a widget for viewing and editing code. Currently used in EDI, but in the future will also be used in enventor and other tools. @feature
-rw-r--r--legacy/elm_code/Makefile.am4
-rw-r--r--legacy/elm_code/data/Makefile.am4
-rw-r--r--legacy/elm_code/data/themes/Makefile.am4
-rw-r--r--legacy/elm_code/data/themes/default/Makefile.am20
-rw-r--r--legacy/elm_code/data/themes/default/default.edc19
-rw-r--r--legacy/elm_code/src/Makefile.am4
-rw-r--r--legacy/elm_code/src/bin/Makefile.am20
-rw-r--r--legacy/elm_code/src/bin/elm_code_test_main.c441
-rw-r--r--legacy/elm_code/src/bin/elm_code_test_private.h6
-rw-r--r--legacy/elm_code/src/lib/Elm_Code.h143
-rw-r--r--legacy/elm_code/src/lib/Makefile.am73
-rw-r--r--legacy/elm_code/src/lib/elm_code.c115
-rw-r--r--legacy/elm_code/src/lib/elm_code_common.h87
-rw-r--r--legacy/elm_code/src/lib/elm_code_diff_widget.c131
-rw-r--r--legacy/elm_code/src/lib/elm_code_diff_widget.h37
-rw-r--r--legacy/elm_code/src/lib/elm_code_file.c322
-rw-r--r--legacy/elm_code/src/lib/elm_code_file.h89
-rw-r--r--legacy/elm_code/src/lib/elm_code_line.c234
-rw-r--r--legacy/elm_code/src/lib/elm_code_line.h117
-rw-r--r--legacy/elm_code/src/lib/elm_code_parse.c206
-rw-r--r--legacy/elm_code/src/lib/elm_code_parse.h41
-rw-r--r--legacy/elm_code/src/lib/elm_code_private.h39
-rw-r--r--legacy/elm_code/src/lib/elm_code_text.c327
-rw-r--r--legacy/elm_code/src/lib/elm_code_text.h73
-rw-r--r--legacy/elm_code/src/lib/widget/Makefile.am0
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget.c1847
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget.eo265
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_legacy.h13
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_private.h66
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_selection.c454
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_selection.h44
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_text.c197
-rw-r--r--legacy/elm_code/src/lib/widget/elm_code_widget_undo.c69
-rw-r--r--legacy/elm_code/src/tests/Makefile.am49
-rw-r--r--legacy/elm_code/src/tests/elm_code_file_test_load.c146
-rw-r--r--legacy/elm_code/src/tests/elm_code_file_test_memory.c43
-rw-r--r--legacy/elm_code/src/tests/elm_code_suite.c130
-rw-r--r--legacy/elm_code/src/tests/elm_code_suite.h31
-rw-r--r--legacy/elm_code/src/tests/elm_code_test_basic.c24
-rw-r--r--legacy/elm_code/src/tests/elm_code_test_line.c75
-rw-r--r--legacy/elm_code/src/tests/elm_code_test_parse.c94
-rw-r--r--legacy/elm_code/src/tests/elm_code_test_text.c158
-rw-r--r--legacy/elm_code/src/tests/testdiff.diff10
-rw-r--r--legacy/elm_code/src/tests/testfile-windows.txt4
-rw-r--r--legacy/elm_code/src/tests/testfile-withblanks.txt8
-rw-r--r--legacy/elm_code/src/tests/testfile.txt4
-rw-r--r--legacy/elm_code/src/tests/widget/elm_code_test_widget.c92
-rw-r--r--legacy/elm_code/src/tests/widget/elm_code_test_widget_selection.c639
-rw-r--r--legacy/elm_code/src/tests/widget/elm_code_test_widget_text.c59
-rw-r--r--legacy/elm_code/src/tests/widget/elm_code_test_widget_undo.c164
50 files changed, 7241 insertions, 0 deletions
diff --git a/legacy/elm_code/Makefile.am b/legacy/elm_code/Makefile.am
new file mode 100644
index 0000000000..b10ba50bda
--- /dev/null
+++ b/legacy/elm_code/Makefile.am
@@ -0,0 +1,4 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = src data
+
diff --git a/legacy/elm_code/data/Makefile.am b/legacy/elm_code/data/Makefile.am
new file mode 100644
index 0000000000..f67d4f3e7b
--- /dev/null
+++ b/legacy/elm_code/data/Makefile.am
@@ -0,0 +1,4 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = themes
+
diff --git a/legacy/elm_code/data/themes/Makefile.am b/legacy/elm_code/data/themes/Makefile.am
new file mode 100644
index 0000000000..31a2b40dd3
--- /dev/null
+++ b/legacy/elm_code/data/themes/Makefile.am
@@ -0,0 +1,4 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = default
+
diff --git a/legacy/elm_code/data/themes/default/Makefile.am b/legacy/elm_code/data/themes/default/Makefile.am
new file mode 100644
index 0000000000..46593bf159
--- /dev/null
+++ b/legacy/elm_code/data/themes/default/Makefile.am
@@ -0,0 +1,20 @@
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = \
+default.edc
+
+include ../../../../Makefile_Edje_Helper.am
+
+filesdir = $(datadir)/$(PACKAGE)/themes
+files_DATA = elm_code.edj
+
+elm_code.edj: Makefile $(EXTRA_DIST)
+ $(AM_V_EDJ)$(EDJE_CC) $(EDJE_CC_FLAGS) \
+ -id ${top_srcdir}/elm_code/data/themes/default/images \
+ -sd ${top_srcdir}/elm_code/data/themes/default/sounds \
+ $(top_srcdir)/elm_code/data/themes/default/default.edc \
+ $(top_builddir)/elm_code/data/themes/default/elm_code.edj
+
+clean-local:
+ rm -f *.edj
diff --git a/legacy/elm_code/data/themes/default/default.edc b/legacy/elm_code/data/themes/default/default.edc
new file mode 100644
index 0000000000..dea7a81adb
--- /dev/null
+++ b/legacy/elm_code/data/themes/default/default.edc
@@ -0,0 +1,19 @@
+collections {
+ /* simple layout to pack our scrolling content into an elm_layout */
+ group { name: "elm_code/layout/default";
+ parts {
+ part { name: "elm.swallow.content"; type: SWALLOW;
+ description { state: "default" 0.0;
+ align: 0.5 0.0;
+ fixed: 0 1;
+
+ rel2 {
+ relative: 1.0 1.0;
+ offset: 0 0;
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/legacy/elm_code/src/Makefile.am b/legacy/elm_code/src/Makefile.am
new file mode 100644
index 0000000000..15871c99eb
--- /dev/null
+++ b/legacy/elm_code/src/Makefile.am
@@ -0,0 +1,4 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = lib bin tests
+
diff --git a/legacy/elm_code/src/bin/Makefile.am b/legacy/elm_code/src/bin/Makefile.am
new file mode 100644
index 0000000000..58105bcfc7
--- /dev/null
+++ b/legacy/elm_code/src/bin/Makefile.am
@@ -0,0 +1,20 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+bin_PROGRAMS = elm_code_test
+
+AM_CPPFLAGS = \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+-I$(top_srcdir)/elm_code/src/lib/ \
+-I$(top_builddir)/elm_code/src/lib/ \
+-DLOCALEDIR=\"$(datadir)/locale\" \
+-DDATA_DIR=\"$(abspath $(srcdir))/../tests/\" \
+-DEFL_BETA_API_SUPPORT \
+@EFL_CFLAGS@
+
+elm_code_test_SOURCES = \
+elm_code_test_main.c \
+elm_code_test_private.h
+
+elm_code_test_LDADD = @EFL_LIBS@ $(top_builddir)/elm_code/src/lib/libelm_code.la
diff --git a/legacy/elm_code/src/bin/elm_code_test_main.c b/legacy/elm_code/src/bin/elm_code_test_main.c
new file mode 100644
index 0000000000..b61484fdd9
--- /dev/null
+++ b/legacy/elm_code/src/bin/elm_code_test_main.c
@@ -0,0 +1,441 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* NOTE: Respecting header order is important for portability.
+ * Always put system first, then EFL, then your public header,
+ * and finally your private one. */
+
+#if ENABLE_NLS
+# include <libintl.h>
+#endif
+
+#include <Ecore_Getopt.h>
+#include <Elementary.h>
+
+#include <Elm_Code.h>
+
+#include "elm_code_test_private.h"
+
+#define COPYRIGHT "Copyright © 2014 andy <andy@andywilliams.me> and various contributors (see AUTHORS)."
+
+static void
+_elm_code_test_win_del(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ elm_exit();
+}
+
+static void _append_line(Elm_Code_File *file, const char *line)
+{
+ int length;
+
+ length = strlen(line);
+ elm_code_file_line_append(file, line, length, NULL);
+}
+
+static Eina_Bool
+_elm_code_test_line_clicked_cb(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Elm_Code_Line *line;
+
+ line = (Elm_Code_Line *)event->info;
+
+ printf("CLICKED line %d\n", line->number);
+ return EO_CALLBACK_CONTINUE;
+}
+
+static Eina_Bool
+_elm_code_test_line_done_cb(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Elm_Code_Line *line;
+
+ line = (Elm_Code_Line *)event->info;
+
+ if (line->number == 1)
+ elm_code_line_token_add(line, 17, 24, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+ else if (line->number == 4)
+ line->status = ELM_CODE_STATUS_TYPE_ERROR;
+
+ return EO_CALLBACK_STOP;
+}
+
+static Evas_Object *
+_elm_code_test_welcome_setup(Evas_Object *parent)
+{
+ Elm_Code *code;
+ Elm_Code_Widget *widget;
+
+ code = elm_code_create();
+ widget = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, code));
+ elm_obj_code_widget_font_set(widget, NULL, 12);
+ eo_event_callback_add(widget, &ELM_CODE_EVENT_LINE_LOAD_DONE, _elm_code_test_line_done_cb, NULL);
+ eo_event_callback_add(widget, ELM_OBJ_CODE_WIDGET_EVENT_LINE_CLICKED, _elm_code_test_line_clicked_cb, code);
+
+ _append_line(code->file, "❤ Hello World, Elm Code! ❤");
+ _append_line(code->file, "");
+ _append_line(code->file, "This is a demo of elm_code's capabilities.");
+ _append_line(code->file, "⚑ *** Currently experimental ***");
+
+ evas_object_size_hint_weight_set(widget, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(widget, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(widget);
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 1, 13);
+
+ return widget;
+}
+
+static Evas_Object *
+_elm_code_test_editor_setup(Evas_Object *parent, Eina_Bool log)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+
+ code = elm_code_create();
+ widget = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, code));
+ elm_obj_code_widget_font_set(widget, NULL, 14);
+ elm_obj_code_widget_editable_set(widget, EINA_TRUE);
+ elm_obj_code_widget_show_whitespace_set(widget, EINA_TRUE);
+ elm_obj_code_widget_line_numbers_set(widget, EINA_TRUE);
+
+ if (!log)
+ {
+ _append_line(code->file, "Edit me :)");
+ _append_line(code->file, "");
+ _append_line(code->file, "");
+ _append_line(code->file, "...Please?");
+
+ line = elm_code_file_line_get(code->file, 1);
+ elm_code_line_token_add(line, 5, 6, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+ elm_code_callback_fire(code, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
+ }
+
+ evas_object_size_hint_weight_set(widget, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(widget, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(widget);
+
+ return widget;
+}
+
+static Evas_Object *
+_elm_code_test_mirror_setup(Elm_Code *code, char *font_name, Evas_Object *parent)
+{
+ Elm_Code_Widget *widget;
+
+ widget = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, code));
+ elm_obj_code_widget_font_set(widget, font_name, 11);
+ elm_obj_code_widget_line_numbers_set(widget, EINA_TRUE);
+
+ evas_object_size_hint_weight_set(widget, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(widget, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(widget);
+
+ return widget;
+}
+
+static Evas_Object *
+_elm_code_test_diff_inline_setup(Evas_Object *parent)
+{
+ Evas_Object *diff;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ diff = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, code));
+
+ evas_object_size_hint_weight_set(diff, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(diff, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(diff);
+
+ elm_code_parser_standard_add(code, ELM_CODE_PARSER_STANDARD_DIFF);
+ elm_code_file_open(code, DATA_DIR "testdiff.diff");
+
+ return diff;
+}
+
+static Evas_Object *
+_elm_code_test_diff_setup(Evas_Object *parent)
+{
+ Evas_Object *diff;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ elm_code_file_open(code, DATA_DIR "testdiff.diff");
+
+ diff = elm_code_diff_widget_add(parent, code);
+ return diff;
+}
+
+static Eina_Bool
+_elm_code_test_log_timer(void *data)
+{
+ Elm_Code *code = data;
+ static int line = 0;
+ char buf[250];
+
+ sprintf(buf, "line %d", ++line);
+ _append_line(code->file, buf);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_elm_code_test_log_clicked(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ static Ecore_Timer *t = NULL;
+
+ if (t)
+ {
+ elm_object_text_set(obj, "Start");
+ ecore_timer_del(t);
+ t = NULL;
+ return;
+ }
+
+ t = ecore_timer_add(0.05, _elm_code_test_log_timer, data);
+ elm_object_text_set(obj, "Stop");
+}
+
+static void
+_elm_code_test_welcome_editor_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Evas_Object *naviframe, *screen;
+
+ naviframe = (Evas_Object *)data;
+ screen = elm_box_add(naviframe);
+ evas_object_size_hint_weight_set(screen, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_box_pack_end(screen, _elm_code_test_editor_setup(screen, EINA_FALSE));
+ evas_object_show(screen);
+
+ elm_naviframe_item_push(naviframe, "Editor",
+ NULL, NULL, screen, NULL);
+}
+
+static void
+_elm_code_test_welcome_log_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Evas_Object *naviframe, *screen, *o, *code;
+
+ naviframe = (Evas_Object *)data;
+ screen = elm_box_add(naviframe);
+ evas_object_size_hint_weight_set(screen, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+
+ code = _elm_code_test_editor_setup(screen, EINA_TRUE);
+ elm_box_pack_end(screen, code);
+
+ o = elm_button_add(screen);
+ elm_object_text_set(o, "log");
+ evas_object_smart_callback_add(o, "clicked", _elm_code_test_log_clicked, elm_obj_code_widget_code_get(code));
+ elm_box_pack_end(screen, o);
+ evas_object_show(o);
+
+ evas_object_show(screen);
+
+ elm_naviframe_item_push(naviframe, "Editor",
+ NULL, NULL, screen, NULL);
+}
+
+static void
+_elm_code_test_welcome_mirror_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Elm_Code *code;
+ Evas_Object *naviframe, *screen, *widget;
+
+ naviframe = (Evas_Object *)data;
+ screen = elm_box_add(naviframe);
+ elm_box_homogeneous_set(screen, EINA_TRUE);
+ evas_object_size_hint_weight_set(screen, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+
+ widget = _elm_code_test_editor_setup(screen, EINA_FALSE);
+ code = elm_obj_code_widget_code_get(widget);
+ elm_box_pack_end(screen, widget);
+
+ elm_box_pack_end(screen, _elm_code_test_mirror_setup(code, "Mono:style=Oblique", screen));
+ elm_box_pack_end(screen, _elm_code_test_mirror_setup(code, "Nimbus Mono", screen));
+
+ evas_object_show(screen);
+ elm_naviframe_item_push(naviframe, "Mirrored editor",
+ NULL, NULL, screen, NULL);
+}
+
+static void
+_elm_code_test_welcome_diff_inline_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Evas_Object *naviframe, *screen;
+
+ naviframe = (Evas_Object *)data;
+ screen = elm_box_add(naviframe);
+ evas_object_size_hint_weight_set(screen, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_box_pack_end(screen, _elm_code_test_diff_inline_setup(screen));
+ evas_object_show(screen);
+
+ elm_naviframe_item_push(naviframe, "Diff widget (inline)",
+ NULL, NULL, screen, NULL);
+}
+
+static void
+_elm_code_test_welcome_diff_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Evas_Object *naviframe, *screen;
+
+ naviframe = (Evas_Object *)data;
+ screen = elm_box_add(naviframe);
+ evas_object_size_hint_weight_set(screen, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_box_pack_end(screen, _elm_code_test_diff_setup(screen));
+ evas_object_show(screen);
+
+ elm_naviframe_item_push(naviframe, "Diff widget (comparison)",
+ NULL, NULL, screen, NULL);
+}
+
+static Evas_Object *
+elm_code_test_win_setup(void)
+{
+ Evas_Object *win, *vbox, *text, *button, *naviframe;
+ Elm_Object_Item *item;
+
+ win = elm_win_util_standard_add("main", "Elm_Code Demo");
+ if (!win) return NULL;
+
+ naviframe = elm_naviframe_add(win);
+ evas_object_size_hint_weight_set(naviframe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, naviframe);
+ evas_object_show(naviframe);
+
+ vbox = elm_box_add(win);
+ evas_object_size_hint_weight_set(vbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(vbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(vbox);
+
+ text = _elm_code_test_welcome_setup(vbox);
+ evas_object_size_hint_weight_set(text, EVAS_HINT_EXPAND, 0.5);
+ elm_box_pack_end(vbox, text);
+
+ button = elm_button_add(vbox);
+ elm_object_text_set(button, "Editor");
+ evas_object_size_hint_weight_set(button, 0.5, 0.25);
+ evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 1.0);
+ evas_object_smart_callback_add(button, "clicked",
+ _elm_code_test_welcome_editor_cb, naviframe);
+ elm_box_pack_end(vbox, button);
+ evas_object_show(button);
+
+ button = elm_button_add(vbox);
+ elm_object_text_set(button, "Log viewer");
+ evas_object_size_hint_weight_set(button, 0.5, 0.0);
+ evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 1.0);
+ evas_object_smart_callback_add(button, "clicked",
+ _elm_code_test_welcome_log_cb, naviframe);
+ elm_box_pack_end(vbox, button);
+ evas_object_show(button);
+
+ button = elm_button_add(vbox);
+ elm_object_text_set(button, "Mirrored editor");
+ evas_object_size_hint_weight_set(button, 0.5, 0.0);
+ evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5);
+ evas_object_smart_callback_add(button, "clicked",
+ _elm_code_test_welcome_mirror_cb, naviframe);
+ elm_box_pack_end(vbox, button);
+ evas_object_show(button);
+
+ button = elm_button_add(vbox);
+ elm_object_text_set(button, "Diff (inline)");
+ evas_object_size_hint_weight_set(button, 0.5, 0.0);
+ evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5);
+ evas_object_smart_callback_add(button, "clicked",
+ _elm_code_test_welcome_diff_inline_cb, naviframe);
+ elm_box_pack_end(vbox, button);
+ evas_object_show(button);
+
+ button = elm_button_add(vbox);
+ elm_object_text_set(button, "Diff (comparison)");
+ evas_object_size_hint_weight_set(button, 0.5, 0.25);
+ evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.0);
+ evas_object_smart_callback_add(button, "clicked",
+ _elm_code_test_welcome_diff_cb, naviframe);
+ elm_box_pack_end(vbox, button);
+ evas_object_show(button);
+
+ item = elm_naviframe_item_push(naviframe, "Choose Demo",
+ NULL, NULL,vbox, NULL);
+ elm_naviframe_item_title_enabled_set(item, EINA_FALSE, EINA_FALSE);
+ elm_win_resize_object_add(win, naviframe);
+
+ evas_object_smart_callback_add(win, "delete,request", _elm_code_test_win_del, NULL);
+ evas_object_resize(win, 400 * elm_config_scale_get(), 320 * elm_config_scale_get());
+ evas_object_show(win);
+
+ return win;
+}
+
+static const Ecore_Getopt optdesc = {
+ "elm_code_test",
+ "%prog [options]",
+ PACKAGE_VERSION,
+ COPYRIGHT,
+ "BSD with advertisement clause",
+ "An EFL elm_code_test program",
+ 0,
+ {
+ ECORE_GETOPT_LICENSE('L', "license"),
+ ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+ ECORE_GETOPT_VERSION('V', "version"),
+ ECORE_GETOPT_HELP('h', "help"),
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+EAPI_MAIN int
+elm_main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
+{
+ Evas_Object *win;
+ int args;
+ Eina_Bool quit_option = EINA_FALSE;
+
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_NONE
+ };
+
+#if ENABLE_NLS
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset(PACKAGE, "UTF-8");
+ textdomain(PACKAGE);
+#endif
+
+ elm_code_init();
+
+ args = ecore_getopt_parse(&optdesc, values, argc, argv);
+ if (args < 0)
+ {
+ EINA_LOG_CRIT("Could not parse arguments.");
+ goto end;
+ }
+ else if (quit_option)
+ {
+ goto end;
+ }
+
+ /* tell elm about our app so it can figure out where to get files */
+ elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
+ elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR);
+ elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
+ elm_app_info_set(elm_main, "elm_code_test", "images/elm_code.png");
+
+ if (!(win = elm_code_test_win_setup()))
+ goto end;
+
+ elm_run();
+
+ end:
+ elm_code_shutdown();
+ elm_shutdown();
+
+ return 0;
+}
+ELM_MAIN()
diff --git a/legacy/elm_code/src/bin/elm_code_test_private.h b/legacy/elm_code/src/bin/elm_code_test_private.h
new file mode 100644
index 0000000000..04fb817a02
--- /dev/null
+++ b/legacy/elm_code/src/bin/elm_code_test_private.h
@@ -0,0 +1,6 @@
+#ifndef ELM_CODE_TEST_PRIVATE_H_
+# define ELM_CODE_TEST_PRIVATE_H_
+
+// FIXME: put some private stuff related to your binary
+
+#endif
diff --git a/legacy/elm_code/src/lib/Elm_Code.h b/legacy/elm_code/src/lib/Elm_Code.h
new file mode 100644
index 0000000000..9d14b400ec
--- /dev/null
+++ b/legacy/elm_code/src/lib/Elm_Code.h
@@ -0,0 +1,143 @@
+#ifndef ELM_CODE_H_
+# define ELM_CODE_H_
+
+#include <Eina.h>
+#include <Eo.h>
+#include <Elementary.h>
+#define ELM_INTERNAL_API_ARGESFSDFEFC
+#include <elm_widget.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ELM_CODE_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_ELM_CODE_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# else
+# define EAPI
+# endif
+# else
+# define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#include "elm_code_common.h"
+#include "elm_code_line.h"
+#include "elm_code_text.h"
+#include "elm_code_file.h"
+#include "elm_code_parse.h"
+#include "widget/elm_code_widget.eo.h"
+#include "widget/elm_code_widget_legacy.h"
+#include "widget/elm_code_widget_selection.h"
+#include "elm_code_diff_widget.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for loading Elm Code widgets.
+ */
+
+/**
+ * @brief Init / shutdown functions.
+ * @defgroup Init Init / Shutdown
+ *
+ * @{
+ *
+ * Functions of obligatory usage, handling proper initialization
+ * and shutdown routines.
+ *
+ * Before the usage of any other function, Elm Code should be properly
+ * initialized with @ref elm_code_init() and the last call to Elm Code's
+ * functions should be @ref elm_code_shutdown(), so everything will
+ * be correctly freed.
+ *
+ * Elm Code logs everything with Eina Log, using the "elm_code" log domain.
+ *
+ */
+
+/**
+ * Initialize Elm Code.
+ *
+ * Initializes Elm Code, its dependencies and modules. Should be the first
+ * function of Elm Code to be called.
+ *
+ * @return The init counter value.
+ *
+ * @see elm_code_shutdown().
+ *
+ * @ingroup Init
+ */
+EAPI int elm_code_init(void);
+
+/**
+ * Shutdown Elm Code
+ *
+ * Shutdown Elm Code. If init count reaches 0, all the internal structures will
+ * be freed. Any Elm Code library call after this point will leads to an error.
+ *
+ * @return Elm Code's init counter value.
+ *
+ * @see elm_code_init()
+ *
+ * @ingroup Init
+ */
+EAPI int elm_code_shutdown(void);
+
+/**
+ * Create a new Elm Code instance
+ *
+ * This method creates a new Elm Code instance using an in-memory file for backing changes.
+ * A regular file can be set after creation if required.
+ * Once an Elm Code has been created you can create widgets that render the content.
+ *
+ * @return an allocated Elm_Code that references the given file
+ * @see elm_code_file_open()
+ */
+EAPI Elm_Code *elm_code_create();
+
+/**
+ * Free an Elm Code instance
+ *
+ * Releases the resources retained by the code instance and any files it references.
+ */
+EAPI void elm_code_free(Elm_Code *code);
+
+/**
+ * @}
+ *
+ * @brief Callbacks and message passing.
+ * @defgroup Callbacks Managing the information flow between Elm_Code objects and Evas_Object widgets
+ *
+ * @{
+ *
+ * Managing the callbacks and other behaviours that cross the backend - frontend divide.
+ */
+
+
+EAPI void elm_code_callback_fire(Elm_Code *code, const Eo_Event_Description *signal, void *data);
+
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_H_ */
diff --git a/legacy/elm_code/src/lib/Makefile.am b/legacy/elm_code/src/lib/Makefile.am
new file mode 100644
index 0000000000..91bf637d34
--- /dev/null
+++ b/legacy/elm_code/src/lib/Makefile.am
@@ -0,0 +1,73 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = widget
+
+CLEANFILES=
+
+EOLIAN_FLAGS = @DEPS_EOLIAN_FLAGS@ \
+ -I$(top_srcdir)/elm_code/src/lib
+
+include $(top_srcdir)/Makefile_Eolian_Helper.am
+
+AM_CPPFLAGS = \
+-I$(top_srcdir)/elm_code/src/lib \
+-I$(top_builddir)/elm_code/src/lib \
+-I$(top_srcdir)/elm_code/src/lib/widget \
+-I$(top_builddir)/elm_code/src/lib/widget \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+-DEFL_BETA_API_SUPPORT \
+-DEFL_EO_API_SUPPORT \
+@EFL_CFLAGS@ \
+-DEFL_ELM_CODE_BUILD
+
+lib_LTLIBRARIES = libelm_code.la
+
+includes_HEADERS = \
+elm_code_common.h \
+elm_code_line.h \
+elm_code_text.h \
+elm_code_file.h \
+elm_code_parse.h \
+widget/elm_code_widget.eo.h \
+widget/elm_code_widget.eo.legacy.h \
+widget/elm_code_widget_legacy.h \
+widget/elm_code_widget_selection.h \
+elm_code_diff_widget.h \
+Elm_Code.h
+includesdir = $(includedir)/edi-@VMAJ@
+
+libelm_code_la_SOURCES = \
+elm_code_line.c \
+elm_code_text.c \
+elm_code_file.c \
+elm_code_parse.c \
+widget/elm_code_widget_selection.c \
+widget/elm_code_widget.c \
+elm_code_diff_widget.c \
+elm_code.c \
+elm_code_private.h \
+widget/elm_code_widget_private.h
+
+libelm_code_la_LIBADD = @EFL_LIBS@ -lm
+libelm_code_la_LDFLAGS = -no-undefined @EFL_LTLIBRARY_FLAGS@
+
+elm_code_eolian_files = \
+widget/elm_code_widget.eo
+
+elm_code_eolian_c = $(elm_code_eolian_files:%.eo=%.eo.c)
+elm_code_eolian_h = $(elm_code_eolian_files:%.eo=%.eo.h)
+elm_code_eolian_legacy_h = $(elm_code_eolian_files:%.eo=%.eo.legacy.h)
+
+BUILT_SOURCES = \
+ $(elm_code_eolian_c) \
+ $(elm_code_eolian_h) \
+ $(elm_code_eolian_legacy_h)
+
+elmcodeeolianfilesdir = $(datadir)/eolian/include/elm_code-@VMAJ@
+elmcodeeolianfiles_DATA = $(elm_code_eolian_files)
+EXTRA_DIST = ${elmcodeeolianfiles_DATA} \
+ widget/elm_code_widget_text.c \
+ widget/elm_code_widget_undo.c
+
+CLEANFILES += $(elm_code_eolian_h) $(elm_code_eolian_legacy_h)
+
diff --git a/legacy/elm_code/src/lib/elm_code.c b/legacy/elm_code/src/lib/elm_code.c
new file mode 100644
index 0000000000..598f928622
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code.c
@@ -0,0 +1,115 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include <Eo.h>
+#include <Evas.h>
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+static int _elm_code_init = 0;
+int _elm_code_lib_log_dom = -1;
+
+EAPI const Eo_Event_Description ELM_CODE_EVENT_LINE_LOAD_DONE =
+ EO_EVENT_DESCRIPTION("line,load,done");
+EAPI const Eo_Event_Description ELM_CODE_EVENT_FILE_LOAD_DONE =
+ EO_EVENT_DESCRIPTION("file,load,done");
+
+
+EAPI int
+elm_code_init(void)
+{
+ _elm_code_init++;
+ if (_elm_code_init > 1) return _elm_code_init;
+
+ eina_init();
+
+ _elm_code_lib_log_dom = eina_log_domain_register("elm_code", EINA_COLOR_CYAN);
+ if (_elm_code_lib_log_dom < 0)
+ {
+ EINA_LOG_ERR("Elm Code can not create its log domain.");
+ goto shutdown_eina;
+ }
+
+ _elm_code_parse_setup();
+
+ eina_log_timing(_elm_code_lib_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT);
+
+ return _elm_code_init;
+
+ shutdown_eina:
+ eina_shutdown();
+ _elm_code_init--;
+
+ return _elm_code_init;
+}
+
+EAPI int
+elm_code_shutdown(void)
+{
+ _elm_code_init--;
+ if (_elm_code_init != 0) return _elm_code_init;
+
+ eina_log_timing(_elm_code_lib_log_dom,
+ EINA_LOG_STATE_START,
+ EINA_LOG_STATE_SHUTDOWN);
+
+ // Put here your shutdown logic
+
+ eina_log_domain_unregister(_elm_code_lib_log_dom);
+ _elm_code_lib_log_dom = -1;
+
+ eina_shutdown();
+
+ return _elm_code_init;
+}
+
+EAPI Elm_Code *
+elm_code_create()
+{
+ Elm_Code *ret;
+
+ ret = calloc(1, sizeof(Elm_Code));
+
+ // create an in-memory backing for this elm_code by default
+ elm_code_file_new(ret);
+ return ret;
+}
+
+EAPI void
+elm_code_free(Elm_Code *code)
+{
+ Evas_Object *widget;
+ Elm_Code_Parser *parser;
+
+ if (code->file)
+ elm_code_file_free(code->file);
+
+ EINA_LIST_FREE(code->widgets, widget)
+ {
+ evas_object_hide(widget);
+ evas_object_del(widget);
+ }
+
+ EINA_LIST_FREE(code->parsers, parser)
+ {
+ _elm_code_parser_free(parser);
+ }
+
+ free(code);
+}
+
+EAPI void
+elm_code_callback_fire(Elm_Code *code, const Eo_Event_Description *signal, void *data)
+{
+ Eina_List *item;
+ Eo *widget;
+
+ EINA_LIST_FOREACH(code->widgets, item, widget)
+ {
+ eo_event_callback_call(widget, signal, data);
+ }
+}
+
diff --git a/legacy/elm_code/src/lib/elm_code_common.h b/legacy/elm_code/src/lib/elm_code_common.h
new file mode 100644
index 0000000000..163bbae072
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_common.h
@@ -0,0 +1,87 @@
+#ifndef ELM_CODE_COMMON_H_
+# define ELM_CODE_COMMON_H_
+
+typedef struct _Elm_Code Elm_Code;
+typedef struct _Elm_Code_File Elm_Code_File;
+
+/** Event marking that a single line has loaded or changed */
+EAPI extern const Eo_Event_Description ELM_CODE_EVENT_LINE_LOAD_DONE;
+/** Event that marks a file load has been completed */
+EAPI extern const Eo_Event_Description ELM_CODE_EVENT_FILE_LOAD_DONE;
+
+typedef enum {
+ ELM_CODE_STATUS_TYPE_DEFAULT = 0,
+ ELM_CODE_STATUS_TYPE_CURRENT,
+ ELM_CODE_STATUS_TYPE_IGNORED,
+ ELM_CODE_STATUS_TYPE_NOTE,
+ ELM_CODE_STATUS_TYPE_WARNING,
+ ELM_CODE_STATUS_TYPE_ERROR,
+ ELM_CODE_STATUS_TYPE_FATAL,
+
+ ELM_CODE_STATUS_TYPE_ADDED,
+ ELM_CODE_STATUS_TYPE_REMOVED,
+ ELM_CODE_STATUS_TYPE_CHANGED,
+
+ ELM_CODE_STATUS_TYPE_PASSED,
+ ELM_CODE_STATUS_TYPE_FAILED,
+
+ ELM_CODE_STATUS_TYPE_TODO,
+
+ ELM_CODE_STATUS_TYPE_COUNT
+} Elm_Code_Status_Type;
+
+
+typedef enum {
+ ELM_CODE_TOKEN_TYPE_DEFAULT = ELM_CODE_STATUS_TYPE_COUNT,
+ ELM_CODE_TOKEN_TYPE_COMMENT,
+ ELM_CODE_TOKEN_TYPE_STRING,
+ ELM_CODE_TOKEN_TYPE_NUMBER,
+ ELM_CODE_TOKEN_TYPE_BRACE,
+ ELM_CODE_TOKEN_TYPE_TYPE,
+ ELM_CODE_TOKEN_TYPE_CLASS,
+ ELM_CODE_TOKEN_TYPE_FUNCTION,
+ ELM_CODE_TOKEN_TYPE_PARAM,
+ ELM_CODE_TOKEN_TYPE_KEYWORD,
+ ELM_CODE_TOKEN_TYPE_PREPROCESSOR,
+
+ ELM_CODE_TOKEN_TYPE_ADDED,
+ ELM_CODE_TOKEN_TYPE_REMOVED,
+ ELM_CODE_TOKEN_TYPE_CHANGED,
+
+ ELM_CODE_TOKEN_TYPE_COUNT
+} Elm_Code_Token_Type;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+/**
+ * @file
+ * @brief Common data structures and constants.
+ */
+
+struct _Elm_Code_Config
+{
+ Eina_Bool trim_whitespace;
+};
+
+struct _Elm_Code
+{
+ Elm_Code_File *file;
+ Eina_List *widgets;
+ Eina_List *parsers;
+
+ struct _Elm_Code_Config config;
+};
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_COMMON_H_ */
diff --git a/legacy/elm_code/src/lib/elm_code_diff_widget.c b/legacy/elm_code/src/lib/elm_code_diff_widget.c
new file mode 100644
index 0000000000..7efb84894c
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_diff_widget.c
@@ -0,0 +1,131 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+#define _ELM_CODE_DIFF_WIDGET_LEFT "diffwidgetleft"
+#define _ELM_CODE_DIFF_WIDGET_RIGHT "diffwidgetright"
+
+#define _ELM_CODE_DIFF_WIDGET_TYPE_ADDED "added"
+#define _ELM_CODE_DIFF_WIDGET_TYPE_REMOVED "removed"
+#define _ELM_CODE_DIFF_WIDGET_TYPE_CHANGED "changed"
+
+static void
+_elm_code_diff_widget_parse_diff_line(Elm_Code_Line *line, Elm_Code_File *left, Elm_Code_File *right)
+{
+ const char *content;
+ unsigned int length;
+
+ if (line->length < 1)
+ {
+ elm_code_file_line_append(left, "", 0, NULL);
+ elm_code_file_line_append(right, "", 0, NULL);
+ }
+
+ content = elm_code_line_text_get(line, &length);
+ if (content[0] == '+')
+ {
+ elm_code_file_line_append(left, "", 0, NULL);
+ elm_code_file_line_append(right, content, length, NULL);
+ }
+ else if (content[0] == '-')
+ {
+ elm_code_file_line_append(left, content, length, NULL);
+ elm_code_file_line_append(right, "", 0, NULL);
+ }
+ else
+ {
+ elm_code_file_line_append(left, content, length, NULL);
+ elm_code_file_line_append(right, content, length, NULL);
+ }
+}
+
+static void
+_elm_code_diff_widget_parse_diff(Elm_Code_File *diff, Elm_Code_File *left, Elm_Code_File *right)
+{
+ Eina_List *item;
+ Elm_Code_Line *line;
+ const char *content;
+ unsigned int offset, length;
+
+ offset = 0;
+ EINA_LIST_FOREACH(diff->lines, item, line)
+ {
+ content = elm_code_line_text_get(line, &length);
+
+ if (length > 0 && (content[0] == 'd' || content[0] == 'i' || content[0] == 'n'))
+ {
+ offset = 0;
+ elm_code_file_line_append(left, content, length, NULL);
+ elm_code_file_line_append(right, content, length, NULL);
+
+ continue;
+ }
+
+ if (offset == 0)
+ elm_code_file_line_append(left, content, length, NULL);
+ else if (offset == 1)
+ elm_code_file_line_append(right, content, length, NULL);
+ else
+ _elm_code_diff_widget_parse_diff_line(line, left, right);
+
+ offset++;
+ }
+
+ _elm_code_parse_file(left->parent, left);
+ _elm_code_parse_file(right->parent, right);
+}
+
+EAPI Evas_Object *
+elm_code_diff_widget_add(Evas_Object *parent, Elm_Code *code)
+{
+ Elm_Code *wcode1, *wcode2;
+ Elm_Code_Widget *widget_left, *widget_right;
+ Evas_Object *hbox;
+
+ hbox = elm_panes_add(parent);
+ evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_panes_horizontal_set(hbox, EINA_FALSE);
+ evas_object_show(hbox);
+
+ // left side of diff
+ wcode1 = elm_code_create();
+ elm_code_parser_standard_add(wcode1, ELM_CODE_PARSER_STANDARD_DIFF);
+ widget_left = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, wcode1));
+
+ evas_object_size_hint_weight_set(widget_left, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(widget_left, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(widget_left);
+ evas_object_data_set(hbox, _ELM_CODE_DIFF_WIDGET_LEFT, widget_left);
+ elm_object_part_content_set(hbox, "left", widget_left);
+
+ // right side of diff
+ wcode2 = elm_code_create();
+ elm_code_parser_standard_add(wcode2, ELM_CODE_PARSER_STANDARD_DIFF);
+ widget_right = eo_add(ELM_CODE_WIDGET_CLASS, parent, elm_obj_code_widget_code_set(eo_self, wcode2));
+
+ evas_object_size_hint_weight_set(widget_right, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(widget_right, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(widget_right);
+ evas_object_data_set(hbox, _ELM_CODE_DIFF_WIDGET_RIGHT, widget_right);
+ elm_object_part_content_set(hbox, "right", widget_right);
+
+ _elm_code_diff_widget_parse_diff(code->file, wcode1->file, wcode2->file);
+ return hbox;
+}
+
+EAPI void
+elm_code_diff_widget_font_set(Evas_Object *widget, const char *name, int size)
+{
+ Elm_Code_Widget *child;
+
+ child = (Elm_Code_Widget *) evas_object_data_get(widget, _ELM_CODE_DIFF_WIDGET_LEFT);
+ elm_obj_code_widget_font_set(child, name, size);
+ child = (Elm_Code_Widget *) evas_object_data_get(widget, _ELM_CODE_DIFF_WIDGET_RIGHT);
+ elm_obj_code_widget_font_set(child, name, size);
+}
+
diff --git a/legacy/elm_code/src/lib/elm_code_diff_widget.h b/legacy/elm_code/src/lib/elm_code_diff_widget.h
new file mode 100644
index 0000000000..7a6f2d6412
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_diff_widget.h
@@ -0,0 +1,37 @@
+#ifndef ELM_CODE_DIFF_WIDGET_H_
+# define ELM_CODE_DIFF_WIDGET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for rendering diff instances of Elm Code.
+ */
+
+/**
+ * @brief UI Loading functions.
+ * @defgroup Init Creating a diff widget to render an Elm Code backend
+ * when it's referencing a diff file
+ *
+ * @{
+ *
+ * Functions for Diff UI loading.
+ *
+ */
+
+EAPI Evas_Object *elm_code_diff_widget_add(Evas_Object *parent, Elm_Code *code);
+
+EAPI void elm_code_diff_widget_font_set(Evas_Object *widget, const char *name,
+ int size);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_DIFF_WIDGET_H_ */
diff --git a/legacy/elm_code/src/lib/elm_code_file.c b/legacy/elm_code/src/lib/elm_code_file.c
new file mode 100644
index 0000000000..5db1bfa9f2
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_file.c
@@ -0,0 +1,322 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+static Elm_Code_Line *_elm_code_file_line_blank_create(Elm_Code_File *file, int line, void *data)
+{
+ Elm_Code_Line *ecl;
+
+ ecl = calloc(1, sizeof(Elm_Code_Line));
+ if (!ecl) return NULL;
+
+ ecl->file = file;
+ ecl->number = line;
+ ecl->status = ELM_CODE_STATUS_TYPE_DEFAULT;
+ ecl->data = data;
+
+ return ecl;
+}
+
+static Elm_Code_File_Line_Ending _elm_code_line_ending_get(const char *ending)
+{
+ switch (*ending)
+ {
+ case '\r':
+ return ELM_CODE_FILE_LINE_ENDING_WINDOWS;
+ default:
+ return ELM_CODE_FILE_LINE_ENDING_UNIX;
+ }
+}
+
+static void _elm_code_file_line_insert_data(Elm_Code_File *file, const char *content, unsigned int length,
+ unsigned int row, Eina_Bool mapped, void *data)
+{
+ Elm_Code_Line *line, *after;
+
+ line = _elm_code_file_line_blank_create(file, row, data);
+ if (!line) return;
+
+ if (mapped)
+ {
+ line->content = content;
+ line->length = length;
+ }
+ else
+ {
+ line->modified = malloc(sizeof(char)*(length+1));
+ strncpy(line->modified, content, length);
+ line->modified[length] = 0;
+ line->length = length;
+ }
+
+ if (row == 1)
+ file->lines = eina_list_prepend(file->lines, line);
+ else if (row == eina_list_count(file->lines) + 1)
+ file->lines = eina_list_append(file->lines, line);
+ else
+ {
+ after = eina_list_nth(file->lines, row - 2);
+ file->lines = eina_list_append_relative(file->lines, line, after);
+ }
+
+ if (file->parent)
+ {
+ _elm_code_parse_line(file->parent, line);
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
+ }
+}
+
+EAPI const char *elm_code_file_filename_get(Elm_Code_File *file)
+{
+ return basename((char *)eina_file_filename_get(file->file));
+}
+
+EAPI const char *elm_code_file_path_get(Elm_Code_File *file)
+{
+ return eina_file_filename_get(file->file);
+}
+
+EAPI char *_elm_code_file_tmp_path_get(Elm_Code_File *file)
+{
+ const char *name, *path;
+ char *tmp;
+ size_t dirlen;
+
+ path = elm_code_file_path_get(file);
+ name = elm_code_file_filename_get(file);
+ dirlen = strlen(path) - strlen(name);
+
+ tmp = malloc(sizeof(char) * (strlen(path) + 6));
+ snprintf(tmp, dirlen + 1, "%s", path);
+ snprintf(tmp + dirlen, strlen(name) + 6, ".%s.tmp", name);
+
+ return tmp;
+}
+
+EAPI Elm_Code_File *elm_code_file_new(Elm_Code *code)
+{
+ Elm_Code_File *ret;
+
+ if (code->file)
+ elm_code_file_free(code->file);
+
+ ret = calloc(1, sizeof(Elm_Code_File));
+ code->file = ret;
+ ret->parent = code;
+
+ return ret;
+}
+
+EAPI Elm_Code_File *elm_code_file_open(Elm_Code *code, const char *path)
+{
+ Elm_Code_File *ret;
+ Eina_File *file;
+ Eina_File_Line *line;
+ Eina_Iterator *it;
+ unsigned int lastindex;
+
+ ret = elm_code_file_new(code);
+ file = eina_file_open(path, EINA_FALSE);
+ ret->file = file;
+ lastindex = 1;
+
+ ret->map = eina_file_map_all(file, EINA_FILE_POPULATE);
+ it = eina_file_map_lines(file);
+ EINA_ITERATOR_FOREACH(it, line)
+ {
+ Elm_Code_Line *ecl;
+
+ if (lastindex == 1)
+ ret->line_ending = _elm_code_line_ending_get(line->start + line->length);
+
+ /* Working around the issue that eina_file_map_lines does not trigger an item for empty lines */
+ /* This was fixed in 1.13.99 so once we depend on 1.14 minimum this can go */
+ while (lastindex < line->index - 1)
+ {
+ ecl = _elm_code_file_line_blank_create(ret, ++lastindex, NULL);
+ if (!ecl) continue;
+
+ ret->lines = eina_list_append(ret->lines, ecl);
+ }
+
+ _elm_code_file_line_insert_data(ret, line->start, line->length, lastindex = line->index, EINA_TRUE, NULL);
+ }
+ eina_iterator_free(it);
+
+ if (ret->parent)
+ {
+ _elm_code_parse_file(ret->parent, ret);
+ elm_code_callback_fire(ret->parent, &ELM_CODE_EVENT_FILE_LOAD_DONE, ret);
+ }
+ return ret;
+}
+
+EAPI void elm_code_file_save(Elm_Code_File *file)
+{
+ Eina_List *item;
+ Elm_Code *code;
+ Elm_Code_Line *line_item;
+ const char *path, *content, *crchars;
+ char *tmp;
+ unsigned int length;
+ short crlength;
+ FILE *out;
+
+ code = file->parent;
+ path = elm_code_file_path_get(file);
+ tmp = _elm_code_file_tmp_path_get(file);
+ crchars = elm_code_file_line_ending_chars_get(file, &crlength);
+
+ out = fopen(tmp, "w");
+ if (out == NULL)
+ {
+ free(tmp);
+ return;
+ }
+
+ EINA_LIST_FOREACH(file->lines, item, line_item)
+ {
+ if (code && code->config.trim_whitespace &&
+ !elm_code_line_contains_widget_cursor(line_item))
+ elm_code_line_text_trailing_whitespace_strip(line_item);
+ content = elm_code_line_text_get(line_item, &length);
+
+ fwrite(content, sizeof(char), length, out);
+ fwrite(crchars, sizeof(char), crlength, out);
+ }
+ fclose(out);
+
+ ecore_file_mv(tmp, path);
+ free(tmp);
+
+ if (file->parent)
+ {
+ _elm_code_parse_reset_file(file->parent, file);
+ _elm_code_parse_file(file->parent, file);
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_FILE_LOAD_DONE, file);
+ }
+}
+
+EAPI void elm_code_file_free(Elm_Code_File *file)
+{
+ Elm_Code_Line *l;
+
+ EINA_LIST_FREE(file->lines, l)
+ {
+ elm_code_line_free(l);
+ }
+
+ if (file->file)
+ {
+ if (file->map)
+ eina_file_map_free(file->file, file->map);
+
+ eina_file_close(file->file);
+ }
+ free(file);
+}
+
+EAPI void elm_code_file_close(Elm_Code_File *file)
+{
+ eina_file_close(file->file);
+}
+
+EAPI Elm_Code_File_Line_Ending elm_code_file_line_ending_get(Elm_Code_File *file)
+{
+ return file->line_ending;
+}
+
+EAPI const char *elm_code_file_line_ending_chars_get(Elm_Code_File *file, short *length)
+{
+ if (length)
+ {
+ if (elm_code_file_line_ending_get(file) == ELM_CODE_FILE_LINE_ENDING_WINDOWS)
+ *length = 2;
+ else
+ *length = 1;
+ }
+
+ if (elm_code_file_line_ending_get(file) == ELM_CODE_FILE_LINE_ENDING_WINDOWS)
+ return "\r\n";
+ return "\n";
+}
+
+EAPI void elm_code_file_clear(Elm_Code_File *file)
+{
+ Elm_Code_Line *l;
+
+ EINA_LIST_FREE(file->lines, l)
+ {
+ elm_code_line_free(l);
+ }
+
+ if (file->parent)
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_FILE_LOAD_DONE, file);
+}
+
+EAPI unsigned int elm_code_file_lines_get(Elm_Code_File *file)
+{
+ return eina_list_count(file->lines);
+}
+
+
+EAPI void elm_code_file_line_append(Elm_Code_File *file, const char *line, int length, void *data)
+{
+ int row;
+
+ row = elm_code_file_lines_get(file) + 1;
+ _elm_code_file_line_insert_data(file, line, length, row, EINA_FALSE, data);
+}
+
+EAPI void elm_code_file_line_insert(Elm_Code_File *file, unsigned int row, const char *line, int length, void *data)
+{
+ Eina_List *item;
+ Elm_Code_Line *line_item;
+ unsigned int r;
+
+ _elm_code_file_line_insert_data(file, line, length, row, EINA_FALSE, data);
+
+ r = row;
+ EINA_LIST_FOREACH(file->lines, item, line_item)
+ {
+ if (line_item->number < row)
+ continue;
+
+ line_item->number = r++;
+ }
+}
+
+EAPI void elm_code_file_line_remove(Elm_Code_File *file, unsigned int row)
+{
+ Eina_List *item, *next;
+ Elm_Code_Line *line_item, *tofree = NULL;
+ unsigned int r;
+
+ r = row;
+ EINA_LIST_FOREACH_SAFE(file->lines, item, next, line_item)
+ {
+ if (line_item->number < row)
+ continue;
+ else if (line_item->number == row)
+ {
+ tofree = line_item;
+ file->lines = eina_list_remove_list(file->lines, item);
+ continue;
+ }
+
+ line_item->number = r++;
+ }
+
+ if (tofree)
+ elm_code_line_free(tofree);
+}
+
+EAPI Elm_Code_Line *elm_code_file_line_get(Elm_Code_File *file, unsigned int number)
+{
+ return eina_list_nth(file->lines, number - 1);
+}
+
diff --git a/legacy/elm_code/src/lib/elm_code_file.h b/legacy/elm_code/src/lib/elm_code_file.h
new file mode 100644
index 0000000000..3c3d4bd339
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_file.h
@@ -0,0 +1,89 @@
+#ifndef ELM_CODE_FILE_H_
+# define ELM_CODE_FILE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for interacting with files using Elm Code.
+ */
+
+typedef enum {
+ ELM_CODE_FILE_LINE_ENDING_UNIX = 0,
+ ELM_CODE_FILE_LINE_ENDING_WINDOWS
+} Elm_Code_File_Line_Ending;
+
+struct _Elm_Code_File
+{
+ void *parent;
+
+ Eina_List *lines;
+ Eina_File *file;
+ void *map;
+
+ Elm_Code_File_Line_Ending line_ending;
+};
+
+/**
+ * @brief File handling functions.
+ * @defgroup File I/O at a file level
+ *
+ * @{
+ *
+ * Functions for file handling within elm code.
+ *
+ */
+
+EAPI Elm_Code_File *elm_code_file_new(Elm_Code *code);
+
+EAPI Elm_Code_File *elm_code_file_open(Elm_Code *code, const char *path);
+
+EAPI void elm_code_file_save(Elm_Code_File *file);
+
+EAPI void elm_code_file_free(Elm_Code_File *file);
+
+EAPI void elm_code_file_close(Elm_Code_File *file);
+
+EAPI const char *elm_code_file_filename_get(Elm_Code_File *file);
+
+EAPI const char *elm_code_file_path_get(Elm_Code_File *file);
+
+EAPI Elm_Code_File_Line_Ending elm_code_file_line_ending_get(Elm_Code_File *file);
+
+EAPI const char *elm_code_file_line_ending_chars_get(Elm_Code_File *file, short *length);
+
+/**
+ * @}
+ *
+ * @brief Content functions.
+ * @defgroup Content Functions for accessing file content
+ *
+ * @{
+ *
+ * File content handling functions.
+ *
+ */
+
+EAPI void elm_code_file_clear(Elm_Code_File *file);
+
+EAPI unsigned int elm_code_file_lines_get(Elm_Code_File *file);
+
+EAPI void elm_code_file_line_append(Elm_Code_File *file, const char *line, int length, void *data);
+
+EAPI void elm_code_file_line_insert(Elm_Code_File *file, unsigned int row, const char *line, int length, void *data);
+
+EAPI void elm_code_file_line_remove(Elm_Code_File *file, unsigned int row);
+
+EAPI Elm_Code_Line *elm_code_file_line_get(Elm_Code_File *file, unsigned int line);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_FILE_H_ */
diff --git a/legacy/elm_code/src/lib/elm_code_line.c b/legacy/elm_code/src/lib/elm_code_line.c
new file mode 100644
index 0000000000..b2ee71419a
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_line.c
@@ -0,0 +1,234 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+EAPI void
+elm_code_line_free(Elm_Code_Line *line)
+{
+ if (!line)
+ return;
+
+ if (line->status_text)
+ free((char *)line->status_text);
+ if (line->modified)
+ free(line->modified);
+
+ free(line);
+}
+
+static void
+_elm_code_line_tokens_split_at(Elm_Code_Line *oldline, Elm_Code_Line *newline,
+ Eina_List *tokens, int position)
+{
+ Eina_List *item, *next;
+ Elm_Code_Token *token, *newtoken;
+
+ EINA_LIST_FOREACH_SAFE(tokens, item, next, token)
+ {
+ if (!token->continues && token->end < position)
+ {
+ oldline->tokens = eina_list_append(oldline->tokens, token);
+ continue;
+ }
+ if (token->start >= position)
+ {
+ token->start -= position;
+ token->end -= position;
+ newline->tokens = eina_list_append(newline->tokens, token);
+ continue;
+ }
+
+ if (token->continues)
+ elm_code_line_token_add(newline, 0, token->end, 1, token->type);
+ else
+ {
+ elm_code_line_token_add(newline, 0, token->end - position, 1, token->type);
+ token->end = position - 1;
+ }
+
+ newtoken = eina_list_data_get(newline->tokens);
+ newtoken->continues = token->continues;
+ token->continues = EINA_TRUE;
+ oldline->tokens = eina_list_append(oldline->tokens, token);
+ }
+
+ elm_code_callback_fire(oldline->file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, oldline);
+ elm_code_callback_fire(newline->file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, newline);
+}
+
+EAPI void elm_code_line_split_at(Elm_Code_Line *line, unsigned int position)
+{
+ Elm_Code_Line *newline;
+ Elm_Code_Token *token EINA_UNUSED;
+ Eina_List *tokens;
+ char *content;
+ unsigned int length;
+
+ content = (char *) elm_code_line_text_get(line, &length);
+ content = strndup(content, length);
+ elm_code_file_line_insert(line->file, line->number + 1, "", 0, NULL);
+ newline = elm_code_file_line_get(line->file, line->number + 1);
+
+ tokens = line->tokens;
+ line->tokens = NULL;
+ elm_code_line_text_set(newline, content + position, length - position);
+ elm_code_line_text_set(line, content, position);
+ _elm_code_line_tokens_split_at(line, newline, tokens, position);
+
+ EINA_LIST_FREE(tokens, token) {} // don't free tokens, we re-used them
+ free(content);
+}
+
+static void
+_elm_code_line_merge_into(Elm_Code_Line *line1, Elm_Code_Line *line2)
+{
+ Eina_List *tokens1, *tokens2;
+ Elm_Code_Token *token;
+ const char *text1, *text2;
+ char *newtext;
+ unsigned int length1, length2;
+
+ text1 = elm_code_line_text_get(line1, &length1);
+ text2 = elm_code_line_text_get(line2, &length2);
+
+ newtext = malloc(sizeof(char) * (length1 + length2 + 1));
+ snprintf(newtext, length1 + 1, "%s", text1);
+ snprintf(newtext + length1, length2 + 1, "%s", text2);
+
+ tokens1 = line1->tokens;
+ line1->tokens = NULL;
+ tokens2 = line2->tokens;
+ line2->tokens = NULL;
+ elm_code_file_line_remove(line2->file, line2->number);
+ elm_code_line_text_set(line1, newtext, length1 + length2);
+
+ EINA_LIST_FREE(tokens1, token)
+ {
+ token->continues = EINA_FALSE;
+ line1->tokens = eina_list_append(line1->tokens, token);
+ }
+ EINA_LIST_FREE(tokens2, token)
+ {
+ token->start += length1;
+ token->end += length1;
+
+ line1->tokens = eina_list_append(line1->tokens, token);
+ }
+
+ elm_code_callback_fire(line1->file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line1);
+ free(newtext);
+}
+
+EAPI void
+elm_code_line_merge_up(Elm_Code_Line *line)
+{
+ Elm_Code_Line *other;
+
+ other = elm_code_file_line_get(line->file, line->number - 1);
+
+ if (other)
+ _elm_code_line_merge_into(other, line);
+}
+
+EAPI void
+elm_code_line_merge_down(Elm_Code_Line *line)
+{
+ Elm_Code_Line *other;
+
+ other = elm_code_file_line_get(line->file, line->number + 1);
+
+ if (other)
+ _elm_code_line_merge_into(line, other);
+}
+
+EAPI void elm_code_line_status_set(Elm_Code_Line *line, Elm_Code_Status_Type status)
+{
+ if (!line)
+ return;
+
+ line->status = status;
+}
+
+EAPI void elm_code_line_status_text_set(Elm_Code_Line *line, const char *text)
+{
+ if (line->status_text)
+ free(line->status_text);
+
+ if (text)
+ line->status_text = strdup(text);
+ else
+ line->status_text = NULL;
+}
+
+EAPI void elm_code_line_token_add(Elm_Code_Line *line, int start, int end, int lines,
+ Elm_Code_Token_Type type)
+{
+ Elm_Code_Token *tok;
+ Elm_Code_Line *next_line;
+
+ if (!line)
+ return;
+
+ tok = calloc(1, sizeof(Elm_Code_Token));
+
+ tok->start = start;
+ tok->end = end;
+ tok->continues = lines > 1;
+ tok->type = type;
+
+ line->tokens = eina_list_append(line->tokens, tok);
+
+ if (lines > 1)
+ {
+ next_line = elm_code_file_line_get(line->file, line->number + 1);
+ elm_code_line_token_add(next_line, 0, end, lines - 1, type);
+ }
+}
+
+EAPI void elm_code_line_tokens_clear(Elm_Code_Line *line)
+{
+ Elm_Code_Token *token;
+
+ if (!line->tokens)
+ return;
+
+ EINA_LIST_FREE(line->tokens, token)
+ free(token);
+ line->tokens = NULL;
+}
+
+EAPI void elm_code_line_status_clear(Elm_Code_Line *line)
+{
+ line->status = ELM_CODE_STATUS_TYPE_DEFAULT;
+ if (line->status_text)
+ {
+ free((char *)line->status_text);
+ line->status_text = NULL;
+ }
+}
+
+EAPI Eina_Bool
+elm_code_line_contains_widget_cursor(Elm_Code_Line *line)
+{
+ Elm_Code *code = line->file->parent;
+ Eina_List *item;
+ Eo *widget;
+ unsigned int col, number;
+
+ if (!code)
+ return EINA_FALSE;
+
+ EINA_LIST_FOREACH(code->widgets, item, widget)
+ {
+ elm_code_widget_cursor_position_get(widget, &col, &number);
+
+ if (number == line->number)
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
diff --git a/legacy/elm_code/src/lib/elm_code_line.h b/legacy/elm_code/src/lib/elm_code_line.h
new file mode 100644
index 0000000000..88196893de
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_line.h
@@ -0,0 +1,117 @@
+#ifndef ELM_CODE_LINE_H_
+# define ELM_CODE_LINE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for interacting with lines of content using Elm Code.
+ */
+
+typedef struct _Elm_Code_Token
+{
+ int start, end;
+ Eina_Bool continues;
+
+ Elm_Code_Token_Type type;
+
+} Elm_Code_Token;
+
+typedef struct _Elm_Code_Line
+{
+ Elm_Code_File *file;
+
+ const char *content;
+ unsigned int length;
+ unsigned int number;
+ char *modified;
+
+ Elm_Code_Status_Type status;
+ Eina_List *tokens;
+
+ void *data;
+ char *status_text;
+} Elm_Code_Line;
+
+EAPI void elm_code_line_free(Elm_Code_Line *line);
+
+/**
+ * @brief Line manipulation functions.
+ * @defgroup Content
+ * @{
+ *
+ * Functions for changing the content of lines in an Elm_Code_File
+ */
+
+/**
+ * Split the given line into two at the specified character position.
+ * The additional line will be inserted into the file immediately below the specified line.
+ *
+ * @param line The line to split
+ * @param position The character position to split at
+ *
+ * @ingroup Content
+ */
+EAPI void elm_code_line_split_at(Elm_Code_Line *line, unsigned int position);
+
+/**
+ * Merge the specified line with the line above.
+ * The content of the specified line will be added to the end of the previous line.
+ * The specified line will then be removed from the file.
+ *
+ * If there is no previous line this method does nothing.
+ *
+ * @param line The line to merge with the previous line.
+ *
+ * @ingroup Content
+ */
+EAPI void elm_code_line_merge_up(Elm_Code_Line *line);
+
+/**
+ * Merge the specified line with the line below.
+ * The content of the specified line will have the contents of the next line added to the end.
+ * The next line will then be removed from the file.
+ *
+ * If there is no next line this method does nothing.
+ *
+ * @param line The line to merge with the next line.
+ *
+ * @ingroup Content
+ */
+EAPI void elm_code_line_merge_down(Elm_Code_Line *line);
+
+/**
+ * @}
+ *
+ * @brief Line markup functions.
+ * @defgroup Highlighting
+ *
+ * @{
+ *
+ * Functions for handling styling and marking up lines within elm code.
+ *
+ */
+
+EAPI void elm_code_line_status_set(Elm_Code_Line *line, Elm_Code_Status_Type status);
+
+EAPI void elm_code_line_status_text_set(Elm_Code_Line *line, const char *text);
+
+EAPI void elm_code_line_status_clear(Elm_Code_Line *line);
+
+EAPI void elm_code_line_token_add(Elm_Code_Line *line, int start, int end, int lines, Elm_Code_Token_Type type);
+
+EAPI void elm_code_line_tokens_clear(Elm_Code_Line *line);
+
+EAPI Eina_Bool elm_code_line_contains_widget_cursor(Elm_Code_Line *line);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_LINE_H_ */
diff --git a/legacy/elm_code/src/lib/elm_code_parse.c b/legacy/elm_code/src/lib/elm_code_parse.c
new file mode 100644
index 0000000000..f7fd97ee77
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_parse.c
@@ -0,0 +1,206 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+struct _Elm_Code_Parser
+{
+ void (*parse_line)(Elm_Code_Line *, void *);
+
+ void (*parse_file)(Elm_Code_File *, void *);
+
+ void *data;
+ Eina_Bool standard;
+};
+
+
+void
+_elm_code_parse_line(Elm_Code *code, Elm_Code_Line *line)
+{
+ Elm_Code_Parser *parser;
+ Eina_List *item;
+
+ elm_code_line_tokens_clear(line);
+ elm_code_line_status_clear(line);
+
+ EINA_LIST_FOREACH(code->parsers, item, parser)
+ {
+ if (parser->parse_line)
+ parser->parse_line(line, parser->data);
+ }
+}
+
+void
+_elm_code_parse_file(Elm_Code *code, Elm_Code_File *file)
+{
+ Elm_Code_Parser *parser;
+ Eina_List *item;
+
+ EINA_LIST_FOREACH(code->parsers, item, parser)
+ {
+ if (parser->parse_file)
+ parser->parse_file(file, parser->data);
+ }
+}
+
+void
+_elm_code_parse_reset_file(Elm_Code *code, Elm_Code_File *file)
+{
+ Elm_Code_Line *line;
+ Eina_List *item;
+
+ EINA_LIST_FOREACH(file->lines, item, line)
+ {
+ _elm_code_parse_line(code, line);
+ }
+}
+
+static Elm_Code_Parser *
+_elm_code_parser_new(void (*parse_line)(Elm_Code_Line *, void *),
+ void (*parse_file)(Elm_Code_File *, void *))
+{
+ Elm_Code_Parser *parser;
+
+ parser = calloc(1, sizeof(Elm_Code_Parser));
+ if (!parser)
+ return NULL;
+
+ parser->parse_line = parse_line;
+ parser->parse_file = parse_file;
+ parser->standard = EINA_FALSE;
+
+ return parser;
+}
+
+EAPI void
+elm_code_parser_add(Elm_Code *code,
+ void (*parse_line)(Elm_Code_Line *, void *),
+ void (*parse_file)(Elm_Code_File *, void *), void *data)
+{
+ Elm_Code_Parser *parser;
+
+ parser = _elm_code_parser_new(parse_line, parse_file);
+ if (!parser)
+ return;
+
+ parser->data = data;
+
+ code->parsers = eina_list_append(code->parsers, parser);
+}
+
+EAPI void
+elm_code_parser_standard_add(Elm_Code *code, Elm_Code_Parser *parser)
+{
+ if (!parser || !code)
+ return;
+
+ parser->standard = EINA_TRUE;
+ code->parsers = eina_list_append(code->parsers, parser);
+}
+
+EAPI Elm_Code_Parser *
+ELM_CODE_PARSER_STANDARD_DIFF;
+
+static void
+_elm_code_parser_diff_trim_leading(Elm_Code_Line *line, unsigned int count)
+{
+ char *replace, *old = NULL;
+
+ if (line->modified)
+ {
+ old = line->modified;
+ replace = malloc(sizeof(char) * (line->length - count));
+
+ strncpy(replace, old + count, line->length - count);
+ line->modified = replace;
+ free(old);
+ }
+ else
+ {
+ line->content += count;
+ }
+
+ line->length -= count;
+}
+
+static void
+_elm_code_parser_diff_parse_line(Elm_Code_Line *line, void *data EINA_UNUSED)
+{
+ const char *content;
+ unsigned int length;
+
+ content = elm_code_line_text_get(line, &length);
+ if (length < 1)
+ return;
+
+ if (content[0] == 'd' || content[0] == 'i' || content[0] == 'n')
+ {
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_CHANGED);
+ return;
+ }
+
+ if (content[0] == '+')
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_ADDED);
+ else if (content[0] == '-')
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_REMOVED);
+
+ _elm_code_parser_diff_trim_leading(line, 1);
+}
+
+static void
+_elm_code_parser_diff_parse_file(Elm_Code_File *file, void *data EINA_UNUSED)
+{
+ Eina_List *item;
+ Elm_Code_Line *line;
+ const char *content;
+ unsigned int length, offset;
+
+ offset = 0;
+ EINA_LIST_FOREACH(file->lines, item, line)
+ {
+ content = elm_code_line_text_get(line, &length);
+
+ if (length > 0 && (content[0] == 'd' || content[0] == 'i' || content[0] == 'n'))
+ {
+ offset = 0;
+ continue;
+ }
+
+ if (offset <= 1 && (content[0] == '+' || content[0] == '-'))
+ {
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_CHANGED);
+ _elm_code_parser_diff_trim_leading(line, 3);
+ }
+
+ offset++;
+ }
+}
+
+static void
+_elm_code_parser_todo_parse_line(Elm_Code_Line *line, void *data EINA_UNUSED)
+{
+ if (elm_code_line_text_strpos(line, "TODO", 0) != ELM_CODE_TEXT_NOT_FOUND)
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_TODO);
+ else if (elm_code_line_text_strpos(line, "FIXME", 0) != ELM_CODE_TEXT_NOT_FOUND)
+ elm_code_line_status_set(line, ELM_CODE_STATUS_TYPE_TODO);
+}
+
+void
+_elm_code_parser_free(Elm_Code_Parser *parser)
+{
+ if (parser->standard)
+ return;
+
+ free(parser);
+}
+
+void
+_elm_code_parse_setup()
+{
+ ELM_CODE_PARSER_STANDARD_DIFF = _elm_code_parser_new(_elm_code_parser_diff_parse_line,
+ _elm_code_parser_diff_parse_file);
+ ELM_CODE_PARSER_STANDARD_TODO = _elm_code_parser_new(_elm_code_parser_todo_parse_line, NULL);
+}
diff --git a/legacy/elm_code/src/lib/elm_code_parse.h b/legacy/elm_code/src/lib/elm_code_parse.h
new file mode 100644
index 0000000000..bc674f8b41
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_parse.h
@@ -0,0 +1,41 @@
+#ifndef ELM_CODE_PARSE_H_
+# define ELM_CODE_PARSE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for handling the parsing of Elm Code content.
+ */
+
+typedef struct _Elm_Code_Parser Elm_Code_Parser;
+
+EAPI Elm_Code_Parser *ELM_CODE_PARSER_STANDARD_DIFF; /**< A provided parser that will mark up diff text */
+EAPI Elm_Code_Parser *ELM_CODE_PARSER_STANDARD_TODO; /**< A provided parser that will highlight TODO and FIXME lines */
+
+/**
+ * @brief Parser helper functions.
+ * @defgroup Parser Hooking in and launching parsers
+ *
+ * @{
+ *
+ * Parser functions for marking up elm code.
+ *
+ */
+
+EAPI void elm_code_parser_add(Elm_Code *code, void (*parse_line)(Elm_Code_Line *, void *),
+ void (*parse_file)(Elm_Code_File *, void *), void *data);
+
+EAPI void elm_code_parser_standard_add(Elm_Code *code, Elm_Code_Parser *parser);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_PARSE_H_ */
diff --git a/legacy/elm_code/src/lib/elm_code_private.h b/legacy/elm_code/src/lib/elm_code_private.h
new file mode 100644
index 0000000000..39f89bdd5e
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_private.h
@@ -0,0 +1,39 @@
+#ifndef ELM_CODE_PRIVATE_H
+# define ELM_CODE_PRIVATE_H
+
+int _elm_code_lib_log_dom;
+
+#ifdef ERR
+# undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_elm_code_lib_log_dom, __VA_ARGS__)
+#ifdef INF
+# undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_elm_code_lib_log_dom, __VA_ARGS__)
+#ifdef WRN
+# undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_elm_code_lib_log_dom, __VA_ARGS__)
+#ifdef CRIT
+# undef CRIT
+#endif
+#define CRIT(...) EINA_LOG_DOM_CRIT(_elm_code_lib_log_dom, __VA_ARGS__)
+#ifdef DBG
+# undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_elm_code_lib_log_dom, __VA_ARGS__)
+
+/* Private parser callbacks */
+
+void _elm_code_parse_setup();
+
+void _elm_code_parse_line(Elm_Code *code, Elm_Code_Line *line);
+
+void _elm_code_parse_file(Elm_Code *code, Elm_Code_File *file);
+
+void _elm_code_parse_reset_file(Elm_Code *code, Elm_Code_File *file);
+
+void _elm_code_parser_free(Elm_Code_Parser *parser);
+
+#endif
diff --git a/legacy/elm_code/src/lib/elm_code_text.c b/legacy/elm_code/src/lib/elm_code_text.c
new file mode 100644
index 0000000000..901b6aefb9
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_text.c
@@ -0,0 +1,327 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_private.h"
+
+EAPI const char *
+elm_code_line_text_get(Elm_Code_Line *line, unsigned int *length)
+{
+ if (!line)
+ return NULL;
+
+ if (length)
+ *length = line->length;
+
+ if (line->modified)
+ return line->modified;
+ return line->content;
+}
+
+EAPI void
+elm_code_line_text_set(Elm_Code_Line *line, const char *chars, unsigned int length)
+{
+ Elm_Code_File *file;
+ char *newtext, *oldtext = NULL;
+
+ if (!line)
+ return;
+
+ if (line->modified)
+ oldtext = line->modified;
+
+ newtext = malloc(sizeof(char) * length);
+ strncpy(newtext, chars, length);
+ line->modified = newtext;
+ line->length = length;
+
+ if (oldtext)
+ free(oldtext);
+
+ file = line->file;
+ if (file->parent)
+ {
+ _elm_code_parse_line(file->parent, line);
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
+ }
+}
+
+EAPI int
+elm_code_text_strnpos(const char *content, unsigned int length, const char *search, int offset)
+{
+ unsigned int searchlen, c;
+ char *ptr;
+
+ searchlen = strlen(search);
+ ptr = (char *) content;
+
+ if (searchlen > length)
+ return ELM_CODE_TEXT_NOT_FOUND;
+
+ ptr += offset;
+ for (c = offset; c <= length - searchlen; c++)
+ {
+ if (!strncmp(ptr, search, searchlen))
+ return c;
+
+ ptr++;
+ }
+
+ return ELM_CODE_TEXT_NOT_FOUND;
+}
+
+EAPI int
+elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset)
+{
+ unsigned int length = 0;
+ const char *content;
+
+ content = elm_code_line_text_get(line, &length);
+ return elm_code_text_strnpos(content, length, search, offset);
+}
+
+EAPI Eina_Bool
+elm_code_line_text_contains(Elm_Code_Line *line, const char *search)
+{
+ return elm_code_line_text_strpos(line, search, 0) != ELM_CODE_TEXT_NOT_FOUND;
+}
+
+EAPI char *
+elm_code_line_text_substr(Elm_Code_Line *line, unsigned int position, int length)
+{
+ const char *content;
+
+ if (!line || length < 1)
+ return strdup("");
+
+ if (position + length > line->length)
+ length = line->length - position;
+
+ content = elm_code_line_text_get(line, NULL);
+ return strndup(content + position, length);
+}
+
+static void
+_elm_code_line_tokens_move_right(Elm_Code_Line *line, int position, int move)
+{
+ Eina_List *item;
+ Elm_Code_Token *token;
+
+ EINA_LIST_FOREACH(line->tokens, item, token)
+ {
+ if (token->end >= position)
+ token->end += move;
+
+ if (token->start > position)
+ token->start += move;
+ }
+}
+
+static void
+_elm_code_line_tokens_move_left(Elm_Code_Line *line, int position, int move)
+{
+ Eina_List *item, *next;
+ Elm_Code_Token *token;
+
+ EINA_LIST_FOREACH_SAFE(line->tokens, item, next, token)
+ {
+ if (token->end >= position)
+ token->end -= move;
+
+ if (token->start > position)
+ token->start -= move;
+
+ if (token->end < token->start)
+ line->tokens = eina_list_remove_list(line->tokens, item);
+ }
+}
+
+EAPI void
+elm_code_line_text_insert(Elm_Code_Line *line, unsigned int position, const char *string, int length)
+{
+ Elm_Code_File *file;
+ char *inserted;
+
+ if (!line)
+ return;
+
+ inserted = malloc(sizeof(char) * line->length + length);
+ if (position > line->length)
+ position = line->length;
+
+ _elm_code_line_tokens_move_right(line, position, length);
+
+ if (line->modified)
+ {
+ strncpy(inserted, line->modified, position);
+ strncpy(inserted + position, string, length);
+ strncpy(inserted + position + length, line->modified + position, line->length - position);
+
+ free(line->modified);
+ }
+ else
+ {
+ strncpy(inserted, line->content, position);
+ strncpy(inserted + position, string, length);
+ strncpy(inserted + position + length, line->content + position, line->length - position);
+ }
+
+ line->modified = inserted;
+ line->length += length;
+
+ file = line->file;
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
+}
+
+EAPI void
+elm_code_line_text_remove(Elm_Code_Line *line, unsigned int position, int length)
+{
+ Elm_Code_File *file;
+ char *removed;
+
+ if (!line)
+ return;
+
+ removed = malloc(sizeof(char) * line->length - length);
+ if (position > line->length)
+ position = line->length;
+
+ _elm_code_line_tokens_move_left(line, position, length);
+
+ if (line->modified)
+ {
+ strncpy(removed, line->modified, position);
+ strncpy(removed + position, line->modified + position + length, line->length - position - length);
+
+ free(line->modified);
+ }
+ else
+ {
+ strncpy(removed, line->content, position);
+ strncpy(removed + position, line->content + position + length, line->length - position - length);
+ }
+
+ line->modified = removed;
+ line->length -= length;
+
+ file = line->file;
+ elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
+}
+
+EAPI void elm_code_line_text_leading_whitespace_strip(Elm_Code_Line *line)
+{
+ unsigned int length = 0;
+ unsigned int leading;
+ const char *content;
+
+ content = elm_code_line_text_get(line, &length);
+ leading = elm_code_text_leading_whitespace_length(content, length);
+ if (leading == 0)
+ return;
+
+ elm_code_line_text_remove(line, 0, leading);
+}
+
+EAPI void elm_code_line_text_trailing_whitespace_strip(Elm_Code_Line *line)
+{
+ unsigned int length = 0;
+ unsigned int trailing;
+ const char *content;
+
+ content = elm_code_line_text_get(line, &length);
+ trailing = elm_code_text_trailing_whitespace_length(content, length);
+ if (trailing == 0)
+ return;
+
+ elm_code_line_text_remove(line, length - trailing, trailing);
+}
+
+/* generic text functions */
+
+EAPI int
+elm_code_text_newlinenpos(const char *text, unsigned int length, short *nllen)
+{
+ int lfpos, crpos;
+ int check;
+
+ if (nllen)
+ *nllen = 1;
+ lfpos = elm_code_text_strnpos(text, length, "\n", 0);
+ check = length;
+ if (lfpos != ELM_CODE_TEXT_NOT_FOUND)
+ check = lfpos + 1;
+ crpos = elm_code_text_strnpos(text, check, "\r", 0);
+
+ if (lfpos == ELM_CODE_TEXT_NOT_FOUND && crpos == ELM_CODE_TEXT_NOT_FOUND)
+ return ELM_CODE_TEXT_NOT_FOUND;
+
+ if (crpos == ELM_CODE_TEXT_NOT_FOUND)
+ return lfpos;
+ if (lfpos == ELM_CODE_TEXT_NOT_FOUND)
+ return crpos;
+
+ if (nllen)
+ *nllen = 2;
+ if (lfpos < crpos)
+ return lfpos;
+ return crpos;
+}
+
+static Eina_Bool
+_elm_code_text_char_is_whitespace(char c)
+{
+ return c == ' ' || c == '\t';
+}
+
+EAPI unsigned int
+elm_code_text_leading_whitespace_length(const char *text, unsigned int length)
+{
+ unsigned int count = 0;
+ char *ptr = (char *)text;
+
+ while (count < length)
+ {
+ if (!_elm_code_text_char_is_whitespace(*ptr))
+ break;
+
+ count++;
+ ptr++;
+ }
+
+ return count;
+}
+
+EAPI unsigned int
+elm_code_text_trailing_whitespace_length(const char *text, unsigned int length)
+{
+ unsigned int count = 0;
+ char *ptr;
+
+ if (length == 0)
+ return 0;
+
+ ptr = (char *)text + length - 1;
+ while (count < length)
+ {
+ if (!_elm_code_text_char_is_whitespace(*ptr))
+ break;
+
+ count++;
+ ptr--;
+ }
+
+ return count;
+}
+
+EAPI unsigned int
+elm_code_text_is_whitespace(const char *text, unsigned int length)
+{
+ unsigned int leading;
+
+ leading = elm_code_text_trailing_whitespace_length(text, length);
+
+ return leading == length;
+}
+
diff --git a/legacy/elm_code/src/lib/elm_code_text.h b/legacy/elm_code/src/lib/elm_code_text.h
new file mode 100644
index 0000000000..54b64d165a
--- /dev/null
+++ b/legacy/elm_code/src/lib/elm_code_text.h
@@ -0,0 +1,73 @@
+#ifndef ELM_CODE_TEXT_H_
+# define ELM_CODE_TEXT_H_
+
+#define ELM_CODE_TEXT_NOT_FOUND -1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * @brief These routines are used for interacting with the textual content of files/lines in Elm Code.
+ */
+
+/**
+ * @brief Line text handling functions.
+ * @defgroup Text access and manipulation within lines
+ *
+ * @{
+ *
+ * Functions for handling content of lines within elm code.
+ *
+ */
+
+EAPI const char *elm_code_line_text_get(Elm_Code_Line *line, unsigned int *length);
+
+EAPI void elm_code_line_text_set(Elm_Code_Line *line, const char *chars, unsigned int length);
+
+EAPI int elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset);
+
+EAPI Eina_Bool elm_code_line_text_contains(Elm_Code_Line *line, const char *search);
+
+EAPI char *elm_code_line_text_substr(Elm_Code_Line *line, unsigned int position, int length);
+
+EAPI void elm_code_line_text_insert(Elm_Code_Line *line, unsigned int position, const char *string, int length);
+
+EAPI void elm_code_line_text_remove(Elm_Code_Line *line, unsigned int position, int length);
+
+EAPI void elm_code_line_text_leading_whitespace_strip(Elm_Code_Line *line);
+
+EAPI void elm_code_line_text_trailing_whitespace_strip(Elm_Code_Line *line);
+
+/**
+ * @}
+ *
+ * @brief Generic text handling functions.
+ * @defgroup Text helper functions
+ *
+ * @{
+ *
+ * Functions for managing unicode text.
+ *
+ */
+
+EAPI int elm_code_text_strnpos(const char *text, unsigned int length, const char *search, int offset);
+
+EAPI int elm_code_text_newlinenpos(const char *text, unsigned int length, short *nllen);
+
+EAPI unsigned int elm_code_text_leading_whitespace_length(const char *text, unsigned int length);
+
+EAPI unsigned int elm_code_text_trailing_whitespace_length(const char *text, unsigned int length);
+
+EAPI unsigned int elm_code_text_is_whitespace(const char *text, unsigned int length);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_TEXT_H_ */
diff --git a/legacy/elm_code/src/lib/widget/Makefile.am b/legacy/elm_code/src/lib/widget/Makefile.am
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/Makefile.am
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget.c b/legacy/elm_code/src/lib/widget/elm_code_widget.c
new file mode 100644
index 0000000000..9af37bd6d8
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget.c
@@ -0,0 +1,1847 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+#include "elm_code_private.h"
+#include "elm_code_widget_private.h"
+
+#define MY_CLASS ELM_CODE_WIDGET_CLASS
+
+typedef enum {
+ ELM_CODE_WIDGET_COLOR_GUTTER_BG = ELM_CODE_TOKEN_TYPE_COUNT,
+ ELM_CODE_WIDGET_COLOR_GUTTER_FG,
+ ELM_CODE_WIDGET_COLOR_WHITESPACE,
+ ELM_CODE_WIDGET_COLOR_CURSOR,
+ ELM_CODE_WIDGET_COLOR_SELECTION,
+
+ ELM_CODE_WIDGET_COLOR_COUNT
+} Elm_Code_Widget_Colors;
+
+Eina_Unicode status_icons[] = {
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ '!',
+ '!',
+ '!',
+
+ '+',
+ '-',
+ ' ',
+
+ 0x2713,
+ 0x2717,
+
+ 0x2691,
+
+ 0
+};
+
+#define EO_CONSTRUCTOR_CHECK_RETURN(obj) do { \
+ Eina_Bool finalized; \
+ \
+ finalized = eo_finalized_get(obj); \
+ if (finalized) \
+ { \
+ ERR("This function is only allowed during construction."); \
+ return; \
+ } \
+} while (0)
+
+static void _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline);
+
+EAPI Evas_Object *
+elm_code_widget_add(Evas_Object *parent, Elm_Code *code)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
+ Evas_Object *obj = NULL;
+ obj = eo_add(MY_CLASS, parent, elm_obj_code_widget_code_set(eo_self, code));
+ return obj;
+}
+
+EOLIAN static Eo *
+_elm_code_widget_eo_base_constructor(Eo *obj, Elm_Code_Widget_Data *pd)
+{
+ obj = eo_constructor(eo_super(obj, ELM_CODE_WIDGET_CLASS));
+
+ pd->cursor_line = 1;
+ pd->cursor_col = 1;
+
+ pd->tabstop = 8;
+
+ return obj;
+}
+
+EOLIAN static Eo *
+_elm_code_widget_eo_base_finalize(Eo *obj, Elm_Code_Widget_Data *pd)
+{
+ obj = eo_finalize(eo_super(obj, ELM_CODE_WIDGET_CLASS));
+
+ if (pd->code)
+ return obj;
+
+ ERR("Elm_Code_Widget cannot finalize without calling elm_code_widget_code_set.");
+ return NULL;
+}
+
+EOLIAN static void
+_elm_code_widget_class_constructor(Eo_Class *klass EINA_UNUSED)
+{
+
+}
+
+void
+_elm_code_widget_cell_size_get(Elm_Code_Widget *widget, Evas_Coord *width, Evas_Coord *height)
+{
+ Elm_Code_Widget_Data *pd;
+ Evas_Object *grid;
+ Evas_Coord w = 0, h = 0;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ grid = eina_list_nth(pd->grids, 0);
+ evas_object_textgrid_cell_size_get(grid, &w, &h);
+ if (w == 0) w = 5;
+ if (h == 0) h = 10;
+
+ if (width) *width = w;
+ if (height) *height = h;
+}
+
+static void
+_elm_code_widget_scroll_by(Elm_Code_Widget *widget, int by_x, int by_y)
+{
+ Elm_Code_Widget_Data *pd;
+ Evas_Coord x, y, w, h;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ elm_scroller_region_get(pd->scroller, &x, &y, &w, &h);
+ x += by_x;
+ y += by_y;
+ elm_scroller_region_show(pd->scroller, x, y, w, h);
+}
+
+static void
+_elm_code_widget_fill_line_token(Evas_Textgrid_Cell *cells, int count, int start, int end, Elm_Code_Token_Type type)
+{
+ int x;
+
+ for (x = start; x <= end && x < count; x++)
+ {
+ cells[x - 1].fg = type;
+ }
+}
+
+static unsigned int
+_elm_code_widget_status_type_get(Elm_Code_Widget *widget, Elm_Code_Line *line, unsigned int col)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (line->status != ELM_CODE_STATUS_TYPE_DEFAULT)
+ return line->status;
+
+ if (pd->editable && pd->focussed && pd->cursor_line == line->number)
+ return ELM_CODE_STATUS_TYPE_CURRENT;
+
+ if (pd->line_width_marker == col)
+ return ELM_CODE_WIDGET_COLOR_GUTTER_BG;
+
+ return ELM_CODE_STATUS_TYPE_DEFAULT;
+}
+
+static void
+_elm_code_widget_fill_line_tokens(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells,
+ unsigned int count, Elm_Code_Line *line)
+{
+ Eina_List *item;
+ Elm_Code_Token *token;
+ unsigned int start, end, length, offset;
+ unsigned int token_start_col, token_end_col;
+
+ offset = elm_obj_code_widget_text_left_gutter_width_get(widget);
+ start = offset;
+ length = elm_code_widget_line_text_column_width_get(widget, line) + offset;
+
+ EINA_LIST_FOREACH(line->tokens, item, token)
+ {
+ token_start_col = elm_code_widget_line_text_column_width_to_position(widget, line, token->start) + offset;
+ token_end_col = elm_code_widget_line_text_column_width_to_position(widget, line, token->end) + offset;
+
+ if (token_start_col > start)
+ _elm_code_widget_fill_line_token(cells, count, start, token_start_col, ELM_CODE_TOKEN_TYPE_DEFAULT);
+
+ // TODO handle a token starting before the previous finishes
+ end = token_end_col;
+ if (token->continues)
+ end = length + offset;
+ _elm_code_widget_fill_line_token(cells, count, token_start_col, end, token->type);
+
+ start = end + 1;
+ }
+
+ _elm_code_widget_fill_line_token(cells, count, start, length, ELM_CODE_TOKEN_TYPE_DEFAULT);
+}
+
+static void
+_elm_code_widget_fill_gutter(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells,
+ int width, Elm_Code_Status_Type status, int line)
+{
+ char *number = NULL;
+ int gutter, g;
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ gutter = elm_code_widget_text_left_gutter_width_get(widget);
+
+ if (width < gutter)
+ return;
+
+ cells[gutter-1].codepoint = status_icons[status];
+ cells[gutter-1].bold = 1;
+ cells[gutter-1].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
+ cells[gutter-1].bg = (status == ELM_CODE_STATUS_TYPE_DEFAULT) ? ELM_CODE_WIDGET_COLOR_GUTTER_BG : status;
+
+ if (pd->show_line_numbers)
+ {
+ if (line > 0)
+ {
+ number = malloc(sizeof(char) * gutter);
+ snprintf(number, gutter, "%*d", gutter - 1, line);
+ }
+ for (g = 0; g < gutter - 1; g++)
+ {
+ if (number)
+ cells[g].codepoint = *(number + g);
+ else
+ cells[g].codepoint = 0;
+
+ cells[g].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
+ cells[g].bg = ELM_CODE_WIDGET_COLOR_GUTTER_BG;
+ }
+
+ if (number)
+ free(number);
+ }
+}
+
+static void
+_elm_code_widget_fill_whitespace(Elm_Code_Widget *widget, Eina_Unicode character, Evas_Textgrid_Cell *cell)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ if (!pd->show_whitespace)
+ {
+ if (character== '\t')
+ cell->codepoint = 0;
+ return;
+ }
+
+ switch (character)
+ {
+ case ' ':
+ cell->codepoint = 0x00b7;
+ break;
+ case '\t':
+ cell->codepoint = 0x2192;
+ break;
+ case '\n':
+ cell->codepoint = 0x23ce;
+ break;
+ default:
+ return;
+ }
+
+ cell->fg = ELM_CODE_WIDGET_COLOR_WHITESPACE;
+}
+
+static void
+_elm_code_widget_fill_cursor(Elm_Code_Widget *widget, unsigned int number,
+ Evas_Textgrid_Cell *cells, int gutter, int w)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (pd->editable && pd->focussed && pd->cursor_line == number)
+ {
+ if (pd->cursor_col + gutter - 1 < (unsigned int) w)
+ cells[pd->cursor_col + gutter - 1].bg = ELM_CODE_WIDGET_COLOR_CURSOR;
+ }
+}
+
+static void
+_elm_code_widget_fill_selection(Elm_Code_Widget *widget, Elm_Code_Line *line, Evas_Textgrid_Cell *cells,
+ int gutter, int w)
+{
+ Elm_Code_Widget_Data *pd;
+ unsigned int x, start, end;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ if (!pd->selection)
+ return;
+
+ selection = elm_code_widget_selection_normalized_get(widget);
+ if (selection->start_line > line->number || selection->end_line < line->number)
+ {
+ free(selection);
+ return;
+ }
+
+ start = selection->start_col;
+ if (selection->start_line < line->number)
+ start = 1;
+ end = selection->end_col;
+ if (selection->end_line > line->number)
+ end = w;
+ free(selection);
+
+ for (x = gutter + start - 1; x < gutter + end && x < (unsigned int) w; x++)
+ cells[x].bg = ELM_CODE_WIDGET_COLOR_SELECTION;
+}
+
+static void
+_elm_code_widget_fill_line(Elm_Code_Widget *widget, Elm_Code_Line *line)
+{
+ char *chr;
+ Eina_Unicode unichr;
+ unsigned int length, x, charwidth, i, w;
+ int chrpos, gutter;
+ Evas_Object *grid, *status;
+ Evas_Textgrid_Cell *cells;
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ gutter = elm_obj_code_widget_text_left_gutter_width_get(widget);
+ if (eina_list_count(pd->grids) < line->number)
+ return;
+
+ w = elm_code_widget_columns_get(widget);
+ grid = eina_list_nth(pd->grids, line->number - 1);
+ cells = evas_object_textgrid_cellrow_get(grid, 0);
+
+ _elm_code_widget_fill_gutter(widget, cells, w, line->status, line->number);
+ _elm_code_widget_fill_line_tokens(widget, cells, w, line);
+
+ length = elm_code_widget_line_text_column_width_get(widget, line);
+ chrpos = 0;
+ chr = (char *)elm_code_line_text_get(line, NULL);
+
+ for (x = gutter; x < (unsigned int) w && x < length + gutter; x+=charwidth)
+ {
+ unichr = eina_unicode_utf8_next_get(chr, &chrpos);
+
+ cells[x].codepoint = unichr;
+ cells[x].bg = _elm_code_widget_status_type_get(widget, line, x - gutter + 1);
+
+ charwidth = 1;
+ if (unichr == '\t')
+ charwidth = elm_code_widget_text_tabwidth_at_column_get(widget, x - gutter + 1);
+ for (i = x + 1; i < x + charwidth; i++)
+ {
+ cells[i].codepoint = 0;
+ cells[i].bg = _elm_code_widget_status_type_get(widget, line, i - gutter + 1);
+ }
+
+ _elm_code_widget_fill_whitespace(widget, unichr, &cells[x]);
+ }
+ for (; x < (unsigned int) w; x++)
+ {
+ cells[x].codepoint = 0;
+ cells[x].bg = _elm_code_widget_status_type_get(widget, line, x - gutter + 1);
+ }
+
+ _elm_code_widget_fill_selection(widget, line, cells, gutter, w);
+ _elm_code_widget_fill_cursor(widget, line->number, cells, gutter, w);
+ if (line->number < elm_code_file_lines_get(line->file))
+ _elm_code_widget_fill_whitespace(widget, '\n', &cells[length + gutter]);
+
+ evas_object_textgrid_update_add(grid, 0, 0, w, 1);
+
+ // add a status below the line if needed (and remove those no longer needed)
+ status = evas_object_data_get(grid, "status");
+ if (line->status_text)
+ {
+ if (!status)
+ {
+ status = elm_label_add(pd->gridbox);
+ evas_object_size_hint_weight_set(status, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(status, 0.05, EVAS_HINT_FILL);
+ evas_object_show(status);
+
+ elm_box_pack_after(pd->gridbox, status, grid);
+ evas_object_data_set(grid, "status", status);
+ }
+ elm_object_text_set(status, line->status_text);
+ }
+ else if (status)
+ {
+ elm_box_unpack(pd->gridbox, status);
+ evas_object_hide(status);
+ evas_object_data_set(grid, "status", NULL);
+ }
+}
+
+static void
+_elm_code_widget_fill_range(Elm_Code_Widget *widget, unsigned int first_row, unsigned int last_row,
+ Elm_Code_Line *newline)
+{
+ Elm_Code_Line *line;
+ unsigned int y;
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ _elm_code_widget_resize(widget, newline);
+
+ // if called from new line cb, no need to update whole range unless visible
+ if (newline && !elm_obj_code_widget_line_visible_get(widget, newline))
+ return;
+
+ for (y = first_row; y <= last_row; y++)
+ {
+ line = elm_code_file_line_get(pd->code->file, y);
+
+ _elm_code_widget_fill_line(widget, line);
+ }
+}
+
+static void
+_elm_code_widget_refresh(Elm_Code_Widget *widget, Elm_Code_Line *line)
+{
+ Evas_Coord scroll_y, scroll_h, oy;
+ unsigned int first_row, last_row;
+
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ evas_object_geometry_get(widget, NULL, &oy, NULL, NULL);
+ elm_scroller_region_get(pd->scroller, NULL, &scroll_y, NULL, &scroll_h);
+ if (scroll_h == 0)
+ return;
+
+ elm_code_widget_position_at_coordinates_get(widget, 0, oy, &first_row, NULL);
+ elm_code_widget_position_at_coordinates_get(widget, 0, oy + scroll_h, &last_row, NULL);
+
+ if (last_row > elm_code_file_lines_get(pd->code->file))
+ last_row = elm_code_file_lines_get(pd->code->file);
+
+ _elm_code_widget_fill_range(widget, first_row, last_row, line);
+}
+
+static void
+_elm_code_widget_fill(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ _elm_code_widget_fill_range(widget, 1, elm_code_file_lines_get(pd->code->file), NULL);
+}
+
+static Eina_Bool
+_elm_code_widget_line_cb(void *data, const Eo_Event *event)
+{
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+
+ line = (Elm_Code_Line *)event->info;
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_refresh(widget, line);
+
+ return EO_CALLBACK_CONTINUE;
+}
+
+static Eina_Bool
+_elm_code_widget_file_cb(void *data, const Eo_Event *event EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_fill(widget);
+ return EO_CALLBACK_CONTINUE;
+}
+
+static Eina_Bool
+_elm_code_widget_selection_cb(void *data, const Eo_Event *event EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_refresh(widget, NULL);
+ return EO_CALLBACK_CONTINUE;
+}
+
+static Eina_Bool
+_elm_code_widget_selection_clear_cb(void *data, const Eo_Event *event EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_fill(widget);
+ return EO_CALLBACK_CONTINUE;
+}
+
+static void
+_elm_code_widget_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_refresh(widget, NULL);
+}
+
+static Eina_Bool
+_elm_code_widget_cursor_key_will_move(Elm_Code_Widget *widget, const char *key)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ line = elm_code_file_line_get(pd->code->file, pd->cursor_line);
+
+ if (!line)
+ return EINA_FALSE;
+
+ if (!strcmp(key, "Up"))
+ return pd->cursor_line > 1;
+ else if (!strcmp(key, "Down"))
+ return pd->cursor_line < elm_code_file_lines_get(pd->code->file);
+ else if (!strcmp(key, "Left"))
+ return pd->cursor_col > 1 || pd->cursor_line > 1;
+ else if (!strcmp(key, "Right"))
+ return pd->cursor_col < elm_code_widget_line_text_column_width_get(widget, line) + 1 ||
+ pd->cursor_line < elm_code_file_lines_get(pd->code->file);
+ else
+ return EINA_FALSE;
+}
+
+static void
+_elm_code_widget_update_focus_directions(Elm_Code_Widget *obj)
+{
+ if (_elm_code_widget_cursor_key_will_move(obj, "Up"))
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_UP);
+ else
+ elm_widget_focus_next_object_set(obj, NULL, ELM_FOCUS_UP);
+
+ if (_elm_code_widget_cursor_key_will_move(obj, "Down"))
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_DOWN);
+ else
+ elm_widget_focus_next_object_set(obj, NULL, ELM_FOCUS_DOWN);
+
+ if (_elm_code_widget_cursor_key_will_move(obj, "Left"))
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_LEFT);
+ else
+ elm_widget_focus_next_object_set(obj, NULL, ELM_FOCUS_LEFT);
+
+ if (_elm_code_widget_cursor_key_will_move(obj, "Right"))
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_RIGHT);
+ else
+ elm_widget_focus_next_object_set(obj, NULL, ELM_FOCUS_RIGHT);
+
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_PREVIOUS);
+ elm_widget_focus_next_object_set(obj, obj, ELM_FOCUS_NEXT);
+}
+
+static void
+_elm_code_widget_cursor_ensure_visible(Elm_Code_Widget *widget)
+{
+ Evas_Coord viewx, viewy, vieww, viewh, cellw, cellh;
+ Evas_Coord curx, cury, oy, rowy;
+ Evas_Object *grid;
+ Elm_Code_Widget_Data *pd;
+ int gutter;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ evas_object_geometry_get(widget, NULL, &oy, NULL, NULL);
+ elm_scroller_region_get(pd->scroller, &viewx, &viewy, &vieww, &viewh);
+ _elm_code_widget_cell_size_get(widget, &cellw, &cellh);
+
+ grid = eina_list_data_get(eina_list_nth_list(pd->grids, pd->cursor_line - 1));
+ evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
+
+ gutter = elm_obj_code_widget_text_left_gutter_width_get(widget);
+ curx = (pd->cursor_col + gutter - 1) * cellw;
+ cury = rowy + viewy - oy;
+
+ if (curx >= viewx && cury >= viewy && curx + cellw <= viewx + vieww && cury + cellh <= viewy + viewh)
+ return;
+
+ elm_scroller_region_show(pd->scroller, curx, cury, cellw, cellh);
+}
+
+static void
+_elm_code_widget_cursor_move(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, unsigned int col, unsigned int line,
+ Eina_Bool was_key)
+{
+ Elm_Code *code;
+ unsigned int oldrow;
+
+ oldrow = pd->cursor_line;
+ pd->cursor_col = col;
+ pd->cursor_line = line;
+
+ if (!was_key)
+ _elm_code_widget_update_focus_directions(widget);
+
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CURSOR_CHANGED, widget);
+ _elm_code_widget_cursor_ensure_visible(widget);
+
+ if (oldrow != pd->cursor_line)
+ {
+ code = pd->code;
+ if (oldrow <= elm_code_file_lines_get(code->file))
+ _elm_code_widget_fill_line(widget, elm_code_file_line_get(pd->code->file, oldrow));
+ }
+ _elm_code_widget_fill_line(widget, elm_code_file_line_get(pd->code->file, pd->cursor_line));
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_position_at_coordinates_get(Eo *obj, Elm_Code_Widget_Data *pd,
+ Evas_Coord x, Evas_Coord y,
+ unsigned int *row, int *col)
+{
+ Elm_Code_Widget *widget;
+ Eina_List *item;
+ Elm_Code_Line *line;
+ Evas_Coord ox, oy, sx, sy, rowy;
+ Evas_Object *grid;
+ int cw, ch, gutter;
+ unsigned int guess, number;
+
+ widget = (Elm_Code_Widget *)obj;
+ evas_object_geometry_get(widget, &ox, &oy, NULL, NULL);
+ elm_scroller_region_get(pd->scroller, &sx, &sy, NULL, NULL);
+ x = x + sx - ox;
+ y = y + sy - oy;
+
+ _elm_code_widget_cell_size_get(widget, &cw, &ch);
+ gutter = elm_obj_code_widget_text_left_gutter_width_get(widget);
+
+ guess = ((double) y / ch) + 1;
+ number = guess;
+
+ // unfortunately EINA_LIST_REVERSE_FOREACH skips to the end of the list...
+ for (item = eina_list_nth_list(pd->grids, guess - 1), grid = eina_list_data_get(item);
+ item;
+ item = eina_list_prev(item), grid = eina_list_data_get(item))
+ {
+ evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
+
+ if (rowy + sy - oy - 1<= y)
+ break;
+
+ number--;
+ }
+
+ if (col)
+ *col = ((double) x / cw) - gutter + 1;
+ if (row)
+ *row = number;
+
+ line = elm_code_file_line_get(pd->code->file, number);
+ return !!line;
+}
+
+static void
+_elm_code_widget_clicked_gutter_cb(Elm_Code_Widget *widget, unsigned int row)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ line = elm_code_file_line_get(pd->code->file, row);
+ if (!line)
+ return;
+
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_LINE_GUTTER_CLICKED, line);
+}
+
+static void
+_elm_code_widget_clicked_editable_cb(Elm_Code_Widget *widget, unsigned int row, unsigned int col)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int column_width;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ line = elm_code_file_line_get(pd->code->file, row);
+ if (!line)
+ return;
+ column_width = elm_code_widget_line_text_column_width_get(widget, line);
+
+ if (col > column_width + 1)
+ col = column_width + 1;
+ else if (col <= 0)
+ col = 1;
+
+ _elm_code_widget_cursor_move(widget, pd, col, row, EINA_FALSE);
+}
+
+static void
+_elm_code_widget_clicked_readonly_cb(Elm_Code_Widget *widget, unsigned int row)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ line = elm_code_file_line_get(pd->code->file, row);
+ if (!line)
+ return;
+
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_LINE_CLICKED, line);
+}
+
+static void
+_elm_code_widget_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
+ void *event_info)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+ Evas_Event_Mouse_Down *event;
+ unsigned int row;
+ int col;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ event = (Evas_Event_Mouse_Down *)event_info;
+ _elm_code_widget_position_at_coordinates_get(widget, pd, event->canvas.x, event->canvas.y, &row, &col);
+
+ elm_code_widget_selection_clear(widget);
+ if (event->flags & EVAS_BUTTON_TRIPLE_CLICK)
+ {
+ elm_code_widget_selection_select_line(widget, row);
+ return;
+ }
+ else if (event->flags & EVAS_BUTTON_DOUBLE_CLICK)
+ {
+ elm_code_widget_selection_select_word(widget, row, col);
+ return;
+ }
+
+ if (pd->editable)
+ _elm_code_widget_clicked_editable_cb(widget, row, (unsigned int) col);
+}
+
+static void
+_elm_code_widget_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
+ void *event_info)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+ Evas_Event_Mouse_Move *event;
+ unsigned int row;
+ int col;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ event = (Evas_Event_Mouse_Move *)event_info;
+
+ _elm_code_widget_position_at_coordinates_get(widget, pd, event->cur.canvas.x, event->cur.canvas.y, &row, &col);
+
+ if (!pd->editable || !event->buttons)
+ return;
+
+ if (!pd->selection)
+ if (col > 0 && row <= elm_code_file_lines_get(pd->code->file))
+ elm_code_widget_selection_start(widget, row, col);
+ elm_code_widget_selection_end(widget, row, col);
+}
+
+static void
+_elm_code_widget_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
+ void *event_info)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+ Evas_Event_Mouse_Up *event;
+ Evas_Coord x, y;
+ unsigned int row;
+ int col;
+ Eina_Bool hasline;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ event = (Evas_Event_Mouse_Up *)event_info;
+
+ if (pd->selection)
+ {
+ if (pd->selection->start_line == pd->selection->end_line &&
+ pd->selection->start_col == pd->selection->end_col)
+ elm_code_widget_selection_clear(widget);
+ else
+ return;
+ }
+
+ x = event->canvas.x;
+ y = event->canvas.y;
+ hasline = _elm_code_widget_position_at_coordinates_get(widget, pd, x, y, &row, &col);
+ if (!hasline)
+ return;
+
+ if (col <= 0)
+ _elm_code_widget_clicked_gutter_cb(widget, row);
+ else if (pd->editable)
+ _elm_code_widget_clicked_editable_cb(widget, row, (unsigned int) col);
+ else
+ _elm_code_widget_clicked_readonly_cb(widget, row);
+}
+
+static void
+_elm_code_widget_cursor_move_home(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ elm_code_widget_selection_clear(widget);
+
+ if (pd->cursor_col <= 1)
+ return;
+
+ _elm_code_widget_cursor_move(widget, pd, 1, pd->cursor_line, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_end(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int lastcol;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ elm_code_widget_selection_clear(widget);
+
+ line = elm_code_file_line_get(pd->code->file, pd->cursor_line);
+ lastcol = elm_code_widget_line_text_column_width_get(widget, line);
+ if (pd->cursor_col > lastcol + 1)
+ return;
+
+ _elm_code_widget_cursor_move(widget, pd, lastcol + 1, pd->cursor_line, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_up(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int row, col, column_width;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ row = pd->cursor_line;
+ col = pd->cursor_col;
+ elm_code_widget_selection_clear(widget);
+
+ if (pd->cursor_line <= 1)
+ return;
+
+ row--;
+ line = elm_code_file_line_get(pd->code->file, row);
+ column_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if (col > column_width + 1)
+ col = column_width + 1;
+
+ _elm_code_widget_cursor_move(widget, pd, col, row, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_down(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int row, col, column_width;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ row = pd->cursor_line;
+ col = pd->cursor_col;
+ elm_code_widget_selection_clear(widget);
+
+ if (pd->cursor_line >= elm_code_file_lines_get(pd->code->file))
+ return;
+
+ row++;
+ line = elm_code_file_line_get(pd->code->file, row);
+ column_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if (col > column_width + 1)
+ col = column_width + 1;
+
+ _elm_code_widget_cursor_move(widget, pd, col, row, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_left(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ elm_code_widget_selection_clear(widget);
+
+ if (pd->cursor_col <= 1)
+ {
+ if (pd->cursor_line > 1)
+ {
+ _elm_code_widget_cursor_move_up(widget);
+ _elm_code_widget_cursor_move_end(widget);
+ }
+ return;
+ }
+
+ _elm_code_widget_cursor_move(widget, pd, pd->cursor_col-1, pd->cursor_line, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_right(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ elm_code_widget_selection_clear(widget);
+
+ line = elm_code_file_line_get(pd->code->file, pd->cursor_line);
+ if (pd->cursor_col > elm_code_widget_line_text_column_width_get(widget, line))
+ {
+ if (pd->cursor_line < elm_code_file_lines_get(pd->code->file))
+ {
+ _elm_code_widget_cursor_move_down(widget);
+ _elm_code_widget_cursor_move_home(widget);
+ }
+ return;
+ }
+
+ _elm_code_widget_cursor_move(widget, pd, pd->cursor_col+1, pd->cursor_line, EINA_TRUE);
+}
+
+static unsigned int
+_elm_code_widget_cursor_move_page_height_get(Elm_Code_Widget *widget)
+{
+ unsigned int lines;
+
+ lines = elm_obj_code_widget_lines_visible_get(widget);
+ return lines * 0.85;
+}
+
+static void
+_elm_code_widget_cursor_move_pageup(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int row, col, column_width;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ row = pd->cursor_line;
+ col = pd->cursor_col;
+
+ elm_code_widget_selection_clear(widget);
+ if (pd->cursor_line <= 1)
+ return;
+
+ if (row > _elm_code_widget_cursor_move_page_height_get(widget))
+ row -= _elm_code_widget_cursor_move_page_height_get(widget);
+ else
+ row = 1;
+
+ line = elm_code_file_line_get(pd->code->file, row);
+ column_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if (col > column_width + 1)
+ col = column_width + 1;
+
+ _elm_code_widget_cursor_move(widget, pd, col, row, EINA_TRUE);
+}
+
+static void
+_elm_code_widget_cursor_move_pagedown(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *line;
+ unsigned int row, col, column_width;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ row = pd->cursor_line;
+ col = pd->cursor_col;
+
+ elm_code_widget_selection_clear(widget);
+ if (pd->cursor_line >= elm_code_file_lines_get(pd->code->file))
+ return;
+
+ row += _elm_code_widget_cursor_move_page_height_get(widget);
+ if (row > elm_code_file_lines_get(pd->code->file))
+ row = elm_code_file_lines_get(pd->code->file);
+
+ line = elm_code_file_line_get(pd->code->file, row);
+ column_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if (col > column_width + 1)
+ col = column_width + 1;
+
+ _elm_code_widget_cursor_move(widget, pd, col, row, EINA_TRUE);
+}
+
+static Eina_Bool
+_elm_code_widget_delete_selection(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (!pd->selection)
+ return EINA_FALSE;
+
+ selection = elm_code_widget_selection_normalized_get(widget);
+ elm_code_widget_selection_delete(widget);
+ elm_code_widget_cursor_position_set(widget, selection->start_col, selection->start_line);
+ free(selection);
+
+ return EINA_TRUE;
+}
+
+static Elm_Code_Widget_Change_Info *
+_elm_code_widget_change_create(unsigned int start_col, unsigned int start_line,
+ unsigned int end_col, unsigned int end_line,
+ const char *text, unsigned int length, Eina_Bool insert)
+{
+ Elm_Code_Widget_Change_Info *info;
+
+ info = calloc(1, sizeof(*info));
+ info->insert = insert;
+
+ info->start_col = start_col;
+ info->start_line = start_line;
+ info->end_col = end_col;
+ info->end_line = end_line;
+
+ info->content = malloc((length + 1) * sizeof(char));
+ strncpy(info->content, text, length);
+ info->content[length] = '\0';
+ info->length = length;
+
+ return info;
+}
+
+static void
+_elm_code_widget_change_free(Elm_Code_Widget_Change_Info *info)
+{
+ free((char *)info->content);
+ free(info);
+}
+
+void
+_elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text, int length)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line;
+ Elm_Code_Widget_Change_Info *change;
+ unsigned int row, col, position, col_width;
+
+ _elm_code_widget_delete_selection(widget);
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+ line = elm_code_file_line_get(code->file, row);
+ if (line == NULL)
+ {
+ elm_code_file_line_append(code->file, "", 0, NULL);
+ row = elm_code_file_lines_get(code->file);
+ line = elm_code_file_line_get(code->file, row);
+ }
+
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ elm_code_line_text_insert(line, position, text, length);
+ col_width = elm_code_widget_line_text_column_width_to_position(widget, line, position + length) -
+ elm_code_widget_line_text_column_width_to_position(widget, line, position);
+
+ // a workaround for when the cursor position would be off the line width
+ _elm_code_widget_resize(widget, line);
+ elm_obj_code_widget_cursor_position_set(widget, col + col_width, row);
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+
+ change = _elm_code_widget_change_create(col, row, col + col_width - 1, row, text, length, EINA_TRUE);
+ _elm_code_widget_undo_change_add(widget, change);
+ _elm_code_widget_change_free(change);
+}
+
+static void
+_elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ unsigned int col, row, rem;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ if (!pd->tab_inserts_spaces)
+ {
+ _elm_code_widget_text_at_cursor_insert(widget, "\t", 1);
+ return;
+ }
+
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+ rem = (col - 1) % pd->tabstop;
+
+ while (rem < pd->tabstop)
+ {
+ _elm_code_widget_text_at_cursor_insert(widget, " ", 1);
+ rem++;
+ }
+}
+
+void
+_elm_code_widget_newline(Elm_Code_Widget *widget)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line;
+ Elm_Code_Widget_Change_Info *change;
+ unsigned int row, col, position, oldlen, leading, width, indent;
+ char *oldtext;
+
+ _elm_code_widget_delete_selection(widget);
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+ line = elm_code_file_line_get(code->file, row);
+ if (line == NULL)
+ {
+ elm_code_file_line_append(code->file, "", 0, NULL);
+ row = elm_code_file_lines_get(code->file);
+ line = elm_code_file_line_get(code->file, row);
+ }
+ oldtext = (char *) elm_code_line_text_get(line, &oldlen);
+ oldtext = strndup(oldtext, oldlen);
+
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ elm_code_line_split_at(line, position);
+ width = elm_code_widget_line_text_column_width_get(widget, line);
+
+ line = elm_code_file_line_get(code->file, row + 1);
+ leading = elm_code_text_leading_whitespace_length(oldtext, oldlen);
+ elm_code_line_text_leading_whitespace_strip(line);
+ elm_code_line_text_insert(line, 0, oldtext, leading);
+ free(oldtext);
+
+ indent = elm_obj_code_widget_line_text_column_width_to_position(widget, line, leading);
+ elm_obj_code_widget_cursor_position_set(widget, indent, row + 1);
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+
+ change = _elm_code_widget_change_create(width + 1, row, indent - 1, row + 1, "\n", 1, EINA_TRUE);
+ _elm_code_widget_undo_change_add(widget, change);
+ _elm_code_widget_change_free(change);
+}
+
+static void
+_elm_code_widget_backspaceline(Elm_Code_Widget *widget, Eina_Bool nextline)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line, *oldline;
+ unsigned int row, col, oldlength, position;
+
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+ line = elm_code_file_line_get(code->file, row);
+
+ if (nextline)
+ {
+ elm_code_line_merge_down(line);
+ }
+ else
+ {
+ oldline = elm_code_file_line_get(code->file, row - 1);
+ elm_code_line_text_get(oldline, &oldlength);
+ elm_code_line_merge_up(line);
+
+ position = elm_code_widget_line_text_column_width_to_position(widget, oldline, oldlength);
+
+ elm_obj_code_widget_cursor_position_set(widget, position, row - 1);
+ }
+// TODO construct and pass a change object
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+}
+
+void
+_elm_code_widget_backspace(Elm_Code_Widget *widget)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line;
+ Elm_Code_Widget_Change_Info *change;
+ unsigned int row, col, position, start_col, end_col, char_width;
+ const char *text;
+
+ if (_elm_code_widget_delete_selection(widget))
+ return; // TODO fire the change and log it
+
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+
+ if (col <= 1)
+ {
+ if (row == 1)
+ return;
+
+ _elm_code_widget_backspaceline(widget, EINA_FALSE);
+ return;
+ }
+
+ line = elm_code_file_line_get(code->file, row);
+
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ end_col = elm_code_widget_line_text_column_width_to_position(widget, line, position);
+ start_col = elm_code_widget_line_text_column_width_to_position(widget, line,
+ elm_code_widget_line_text_position_for_column_get(widget, line, col - 1));
+ char_width = position - elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
+
+ text = elm_code_widget_text_between_positions_get(widget, start_col, row, end_col, row);
+ elm_code_line_text_remove(line, position - char_width, char_width);
+ elm_obj_code_widget_cursor_position_set(widget, start_col, row);
+
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+
+ change = _elm_code_widget_change_create(start_col, row, end_col, row, text, char_width, EINA_FALSE);
+ _elm_code_widget_undo_change_add(widget, change);
+ _elm_code_widget_change_free(change);
+}
+
+void
+_elm_code_widget_delete(Elm_Code_Widget *widget)
+{
+ Elm_Code *code;
+ Elm_Code_Line *line;
+ Elm_Code_Widget_Change_Info *change;
+ unsigned int row, col, position, char_width, start_col, end_col;
+ const char *text;
+
+ if (_elm_code_widget_delete_selection(widget))
+ return; // TODO fire the change and log it
+
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+ line = elm_code_file_line_get(code->file, row);
+ if (col > elm_code_widget_line_text_column_width_get(widget, line))
+ {
+ if (row == elm_code_file_lines_get(code->file))
+ return;
+
+ _elm_code_widget_backspaceline(widget, EINA_TRUE);
+ return;
+ }
+
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ char_width = elm_code_widget_line_text_position_for_column_get(widget, line, col + 1) - position;
+ if (char_width == 0) // a partial tab
+ char_width = 1;
+ start_col = elm_code_widget_line_text_column_width_to_position(widget, line, position);
+ end_col = elm_code_widget_line_text_column_width_to_position(widget, line, position + char_width);
+
+ text = elm_code_widget_text_between_positions_get(widget, start_col, row, end_col, row);
+ elm_code_line_text_remove(line, position, char_width);
+ elm_obj_code_widget_cursor_position_set(widget, start_col, row);
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+
+ change = _elm_code_widget_change_create(start_col, row, col, row, text, char_width, EINA_FALSE);
+ _elm_code_widget_undo_change_add(widget, change);
+ _elm_code_widget_change_free(change);
+}
+
+static void
+_elm_code_widget_control_key_down_cb(Elm_Code_Widget *widget, const char *key)
+{
+ if (!key)
+ return;
+
+ if (!strcmp("c", key))
+ {
+ elm_code_widget_selection_copy(widget);
+ return;
+ }
+
+ if (!strcmp("v", key))
+ elm_code_widget_selection_paste(widget);
+ else if (!strcmp("x", key))
+ elm_code_widget_selection_cut(widget);
+ else if (!strcmp("z", key))
+ elm_code_widget_undo(widget);
+
+ // TODO construct and pass a change object for cut and paste
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
+}
+
+static void
+_elm_code_widget_key_down_cb(void *data, Evas *evas EINA_UNUSED,
+ Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ Evas_Event_Key_Down *ev = event_info;
+
+ if (!pd->editable)
+ return;
+
+ _elm_code_widget_update_focus_directions((Elm_Code_Widget *)obj);
+
+ if (evas_key_modifier_is_set(ev->modifiers, "Control"))
+ {
+ _elm_code_widget_control_key_down_cb(widget, ev->key);
+ return;
+ }
+
+ if (!strcmp(ev->key, "Up"))
+ _elm_code_widget_cursor_move_up(widget);
+ else if (!strcmp(ev->key, "Down"))
+ _elm_code_widget_cursor_move_down(widget);
+ else if (!strcmp(ev->key, "Left"))
+ _elm_code_widget_cursor_move_left(widget);
+ else if (!strcmp(ev->key, "Right"))
+ _elm_code_widget_cursor_move_right(widget);
+ else if (!strcmp(ev->key, "Home"))
+ _elm_code_widget_cursor_move_home(widget);
+ else if (!strcmp(ev->key, "End"))
+ _elm_code_widget_cursor_move_end(widget);
+ else if (!strcmp(ev->key, "Prior"))
+ _elm_code_widget_cursor_move_pageup(widget);
+ else if (!strcmp(ev->key, "Next"))
+ _elm_code_widget_cursor_move_pagedown(widget);
+
+ else if (!strcmp(ev->key, "KP_Enter") || !strcmp(ev->key, "Return"))
+ _elm_code_widget_newline(widget);
+ else if (!strcmp(ev->key, "BackSpace"))
+ _elm_code_widget_backspace(widget);
+ else if (!strcmp(ev->key, "Delete"))
+ _elm_code_widget_delete(widget);
+ else if (!strcmp(ev->key, "Tab"))
+ _elm_code_widget_tab_at_cursor_insert(widget);
+
+ else if (!strcmp(ev->key, "Escape"))
+ DBG("TODO - Escape not yet captured");
+
+ else if (ev->string && strlen(ev->string) == 1)
+ _elm_code_widget_text_at_cursor_insert(widget, ev->string, 1);
+ else
+ INF("Unhandled key %s (%s) (%s)", ev->key, ev->keyname, ev->string);
+}
+
+static void
+_elm_code_widget_focused_event_cb(void *data, Evas_Object *obj,
+ void *event_info EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ pd->focussed = EINA_TRUE;
+
+ _elm_code_widget_update_focus_directions(widget);
+ _elm_code_widget_refresh(obj, NULL);
+}
+
+static void
+_elm_code_widget_unfocused_event_cb(void *data, Evas_Object *obj,
+ void *event_info EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Data *pd;
+
+ widget = (Elm_Code_Widget *)data;
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ pd->focussed = EINA_FALSE;
+ _elm_code_widget_refresh(obj, NULL);
+}
+
+static void
+_elm_code_widget_scroll_event_cb(void *data, Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+
+ _elm_code_widget_refresh(widget, NULL);
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_elm_widget_event(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd EINA_UNUSED,
+ Evas_Object *src EINA_UNUSED, Evas_Callback_Type type, void *event_info)
+{
+ Evas_Event_Key_Down *ev = event_info;
+
+ if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
+
+ if (!strcmp(ev->key, "BackSpace"))
+ {
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_elm_widget_focus_next_manager_is(Eo *obj EINA_UNUSED,
+ Elm_Code_Widget_Data *pd EINA_UNUSED)
+{
+ return EINA_FALSE;
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_elm_widget_focus_direction_manager_is(Eo *obj EINA_UNUSED,
+ Elm_Code_Widget_Data *pd EINA_UNUSED)
+{
+ return EINA_TRUE;
+}
+
+static void
+_elm_code_widget_setup_palette(Evas_Object *o)
+{
+ double feint = 0.5;
+
+ // setup status colors
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_DEFAULT,
+ 36, 36, 36, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_CURRENT,
+ 12, 12, 12, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_IGNORED,
+ 36, 36, 36, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_NOTE,
+ 255, 153, 0, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_WARNING,
+ 255, 153, 0, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_ERROR,
+ 205, 54, 54, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_FATAL,
+ 205, 54, 54, 255);
+
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_ADDED,
+ 36, 96, 36, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_REMOVED,
+ 96, 36, 36, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_CHANGED,
+ 36, 36, 96, 255);
+
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_PASSED,
+ 54, 96, 54, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_FAILED,
+ 96, 54, 54, 255);
+
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_STATUS_TYPE_TODO,
+ 54, 54, 96, 255);
+
+ // setup token colors
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_DEFAULT,
+ 205, 205, 205, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_COMMENT,
+ 51, 153, 255, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_STRING,
+ 255, 90, 53, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_NUMBER,
+ 212, 212, 42, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_BRACE,
+ 101, 101, 101, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_TYPE,
+ 51, 153, 255, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_CLASS,
+ 114, 170, 212, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_FUNCTION,
+ 114, 170, 212, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_PARAM,
+ 255, 255, 255, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_KEYWORD,
+ 255, 153, 0, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_PREPROCESSOR,
+ 0, 176, 0, 255);
+
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_ADDED,
+ 54, 255, 54, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_REMOVED,
+ 255, 54, 54, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_CHANGED,
+ 54, 54, 255, 255);
+
+ // other styles that the widget uses
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_CURSOR,
+ 205, 205, 54, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_SELECTION,
+ 51, 153, 255, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_GUTTER_BG,
+ 75, 75, 75, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_GUTTER_FG,
+ 139, 139, 139, 255);
+ evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_WHITESPACE,
+ 101 * feint, 101 * feint, 101 * feint, 255 * feint);
+}
+
+static void
+_elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
+{
+ Evas_Object *grid;
+ int existing, i;
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ existing = eina_list_count(pd->grids);
+
+ // trim unneeded rows in our rendering
+ if (rows < existing)
+ {
+ for (i = existing - rows; i > 0; i--)
+ {
+ grid = eina_list_data_get(eina_list_last(pd->grids));
+ evas_object_hide(grid);
+ elm_box_unpack(pd->gridbox, grid);
+ pd->grids = eina_list_remove_list(pd->grids, eina_list_last(pd->grids));
+ }
+ rows = existing;
+ }
+
+ if (rows == existing)
+ return;
+
+ for (i = existing; i < rows; i++)
+ {
+ grid = evas_object_textgrid_add(pd->gridbox);
+ evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, 0.0);
+ evas_object_show(grid);
+ _elm_code_widget_setup_palette(grid);
+
+ elm_box_pack_end(pd->gridbox, grid);
+ pd->grids = eina_list_append(pd->grids, grid);
+
+ evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_DOWN, _elm_code_widget_mouse_down_cb, widget);
+ evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_MOVE, _elm_code_widget_mouse_move_cb, widget);
+ evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_UP, _elm_code_widget_mouse_up_cb, widget);
+
+ evas_object_textgrid_font_set(grid, pd->font_name, pd->font_size * elm_config_scale_get());
+ }
+}
+
+static void
+_elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
+{
+ Elm_Code_Line *line;
+ Eina_List *item;
+ Evas_Object *grid;
+ Evas_Coord ww, wh, old_width, old_height;
+ int w, h, cw, ch, gutter;
+ unsigned int line_width;
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ gutter = elm_obj_code_widget_text_left_gutter_width_get(widget);
+
+ if (!pd->code)
+ return;
+
+ evas_object_geometry_get(widget, NULL, NULL, &ww, &wh);
+
+ old_width = ww;
+ old_height = wh;
+ w = 0;
+ h = elm_code_file_lines_get(pd->code->file);
+
+ if (newline)
+ {
+ line = eina_list_data_get(pd->code->file->lines);
+ if (line)
+ {
+ line_width = elm_code_widget_line_text_column_width_get(widget, newline);
+ w = (int) line_width + gutter + 1;
+ }
+ line_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if ((int) line_width + gutter + 1 > w)
+ {
+ w = (int) line_width + gutter + 1;
+ }
+ }
+ else
+ {
+ EINA_LIST_FOREACH(pd->code->file->lines, item, line)
+ {
+ line_width = elm_code_widget_line_text_column_width_get(widget, line);
+ if ((int) line_width + gutter + 1 > w)
+ w = (int) line_width + gutter + 1;
+ }
+ }
+
+ _elm_code_widget_ensure_n_grid_rows(widget, h);
+ _elm_code_widget_cell_size_get(widget, &cw, &ch);
+ if (w*cw > ww)
+ ww = w*cw;
+ if (h*ch > wh)
+ wh = h*ch;
+ pd->col_count = ww/cw + 1;
+
+ EINA_LIST_FOREACH(pd->grids, item, grid)
+ {
+ evas_object_textgrid_size_set(grid, pd->col_count, 1);
+ evas_object_size_hint_min_set(grid, w*cw, ch);
+ }
+
+ if (!newline) return;
+
+ if (pd->gravity_x == 1.0 || pd->gravity_y == 1.0)
+ _elm_code_widget_scroll_by(widget,
+ (pd->gravity_x == 1.0 && ww > old_width) ? ww - old_width : 0,
+ (pd->gravity_y == 1.0 && wh > old_height) ? wh - old_height : 0);
+}
+
+EOAPI void
+_elm_code_widget_line_refresh(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line)
+{
+ _elm_code_widget_fill_line(obj, line);
+}
+
+EOAPI Eina_Bool
+_elm_code_widget_line_visible_get(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
+{
+ Evas_Coord cellh, viewy, viewh;
+
+ elm_scroller_region_get(pd->scroller, NULL, &viewy, NULL, &viewh);
+ _elm_code_widget_cell_size_get(obj, NULL, &cellh);
+
+ if (((int)line->number - 1) * cellh > viewy + viewh || (int)line->number * cellh < viewy)
+ return EINA_FALSE;
+
+ return EINA_TRUE;;
+}
+
+EOAPI unsigned int
+_elm_code_widget_lines_visible_get(Eo *obj, Elm_Code_Widget_Data *pd)
+{
+ Evas_Coord cellh, viewh;
+
+ elm_scroller_region_get(pd->scroller, NULL, NULL, NULL, &viewh);
+ _elm_code_widget_cell_size_get(obj, NULL, &cellh);
+
+ return viewh / cellh + 1;
+}
+
+EOLIAN static void
+_elm_code_widget_font_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd,
+ const char *name, Evas_Font_Size size)
+{
+ Eina_List *item;
+ Evas_Object *grid;
+
+ const char *face = name;
+ if (!face)
+ face = "Mono";
+
+ EINA_LIST_FOREACH(pd->grids, item, grid)
+ {
+ evas_object_textgrid_font_set(grid, face, size * elm_config_scale_get());
+ }
+ if (pd->font_name)
+ eina_stringshare_del((char *)pd->font_name);
+ pd->font_name = eina_stringshare_add(face);
+ pd->font_size = size;
+}
+
+EOLIAN static void
+_elm_code_widget_font_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd,
+ const char **name, Evas_Font_Size *size)
+{
+ if (name)
+ *name = strdup((const char *)pd->font_name);
+ if (size)
+ *size = pd->font_size;
+}
+
+EOLIAN static unsigned int
+_elm_code_widget_columns_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->col_count;
+}
+
+EOLIAN static void
+_elm_code_widget_code_set(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code *code)
+{
+ EO_CONSTRUCTOR_CHECK_RETURN(obj);
+
+ pd->code = code;
+
+ code->widgets = eina_list_append(code->widgets, obj);
+}
+
+EOLIAN static Elm_Code *
+_elm_code_widget_code_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->code;
+}
+
+EOLIAN static void
+_elm_code_widget_gravity_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, double x, double y)
+{
+ pd->gravity_x = x;
+ pd->gravity_y = y;
+}
+
+EOLIAN static void
+_elm_code_widget_gravity_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, double *x, double *y)
+{
+ *x = pd->gravity_x;
+ *y = pd->gravity_y;
+}
+
+EOLIAN static void
+_elm_code_widget_policy_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Elm_Scroller_Policy policy_h, Elm_Scroller_Policy policy_v)
+{
+ elm_scroller_policy_set(pd->scroller, policy_h, policy_v);
+}
+
+EOLIAN static void
+_elm_code_widget_policy_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Elm_Scroller_Policy *policy_h, Elm_Scroller_Policy *policy_v)
+{
+ elm_scroller_policy_get(pd->scroller, policy_h, policy_v);
+}
+
+EOLIAN static void
+_elm_code_widget_tabstop_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int tabstop)
+{
+ pd->tabstop = tabstop;
+ _elm_code_widget_fill(obj);
+}
+
+EOLIAN static unsigned int
+_elm_code_widget_tabstop_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->tabstop;
+}
+
+EOLIAN static void
+_elm_code_widget_editable_set(Eo *obj, Elm_Code_Widget_Data *pd, Eina_Bool editable)
+{
+ pd->editable = editable;
+ elm_object_focus_allow_set(obj, editable);
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_editable_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->editable;
+}
+
+EOLIAN static void
+_elm_code_widget_line_numbers_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Eina_Bool line_numbers)
+{
+ pd->show_line_numbers = line_numbers;
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_line_numbers_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->show_line_numbers;
+}
+
+EOLIAN static void
+_elm_code_widget_line_width_marker_set(Eo *obj, Elm_Code_Widget_Data *pd, unsigned int col)
+{
+ pd->line_width_marker = col;
+ _elm_code_widget_fill(obj);
+}
+
+EOLIAN static unsigned int
+_elm_code_widget_line_width_marker_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->line_width_marker;
+}
+
+EOLIAN static void
+_elm_code_widget_show_whitespace_set(Eo *obj, Elm_Code_Widget_Data *pd, Eina_Bool show)
+{
+ pd->show_whitespace = show;
+ _elm_code_widget_fill(obj);
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_show_whitespace_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->show_whitespace;
+}
+
+EOLIAN static void
+_elm_code_widget_tab_inserts_spaces_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd,
+ Eina_Bool spaces)
+{
+ pd->tab_inserts_spaces = spaces;
+}
+
+EOLIAN static Eina_Bool
+_elm_code_widget_tab_inserts_spaces_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ return pd->tab_inserts_spaces;
+}
+
+EOLIAN static void
+_elm_code_widget_cursor_position_set(Eo *obj, Elm_Code_Widget_Data *pd, unsigned int col, unsigned int line)
+{
+ _elm_code_widget_cursor_move(obj, pd, col, line, EINA_FALSE);
+}
+
+EOLIAN static void
+_elm_code_widget_cursor_position_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int *col, unsigned int *line)
+{
+ *col = pd->cursor_col;
+ *line = pd->cursor_line;
+}
+
+EOLIAN static void
+_elm_code_widget_evas_object_smart_add(Eo *obj, Elm_Code_Widget_Data *pd)
+{
+ Evas_Object *background, *gridrows, *scroller;
+
+ evas_obj_smart_add(eo_super(obj, ELM_CODE_WIDGET_CLASS));
+ elm_object_focus_allow_set(obj, EINA_TRUE);
+
+ elm_layout_file_set(obj, PACKAGE_DATA_DIR "/themes/elm_code.edj", "elm_code/layout/default");
+
+ scroller = elm_scroller_add(obj);
+ evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(scroller);
+ elm_layout_content_set(obj, "elm.swallow.content", scroller);
+ elm_object_focus_allow_set(scroller, EINA_FALSE);
+ pd->scroller = scroller;
+
+ background = elm_bg_add(scroller);
+ evas_object_color_set(background, 145, 145, 145, 255);
+ evas_object_size_hint_weight_set(background, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(background, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(background);
+ elm_object_part_content_set(scroller, "elm.swallow.background", background);
+
+ gridrows = elm_box_add(scroller);
+ evas_object_size_hint_weight_set(gridrows, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(gridrows, EVAS_HINT_FILL, 0.0);
+ elm_object_content_set(scroller, gridrows);
+ pd->gridbox = gridrows;
+
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _elm_code_widget_resize_cb, obj);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, _elm_code_widget_key_down_cb, obj);
+
+ evas_object_smart_callback_add(obj, "focused", _elm_code_widget_focused_event_cb, obj);
+ evas_object_smart_callback_add(obj, "unfocused", _elm_code_widget_unfocused_event_cb, obj);
+ evas_object_smart_callback_add(scroller, "scroll", _elm_code_widget_scroll_event_cb, obj);
+
+ eo_event_callback_add(obj, &ELM_CODE_EVENT_LINE_LOAD_DONE, _elm_code_widget_line_cb, obj);
+ eo_event_callback_add(obj, &ELM_CODE_EVENT_FILE_LOAD_DONE, _elm_code_widget_file_cb, obj);
+ eo_event_callback_add(obj, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CHANGED, _elm_code_widget_selection_cb, obj);
+ eo_event_callback_add(obj, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CLEARED, _elm_code_widget_selection_clear_cb, obj);
+}
+
+#include "elm_code_widget_text.c"
+#include "elm_code_widget_undo.c"
+#include "elm_code_widget.eo.c"
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget.eo b/legacy/elm_code/src/lib/widget/elm_code_widget.eo
new file mode 100644
index 0000000000..65dd56a780
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget.eo
@@ -0,0 +1,265 @@
+import evas_types;
+import edje_types;
+import elm_interface_scrollable;
+
+struct @extern Elm_Code; /* The main interface currently defined in code */
+struct @extern Elm_Code_Line; /* Parts of the interface currently defined in code */
+
+class Elm.Code_Widget (Elm.Layout, Elm.Interface.Atspi.Text)
+{
+ eo_prefix: elm_obj_code_widget;
+ legacy_prefix: elm_code_widget;
+ methods {
+ @property code {
+ set {
+ [[Set the underlying code object that this widget renders.
+ This can only be set during construction, once the widget is created the
+ backing code object cannot be changed.]]
+ }
+ get {
+ [[Get the underlying code object we are rendering]]
+ }
+ values {
+ code: Elm_Code *; [[Our underlying Elm_Code object]]
+ }
+ }
+ @property font {
+ set {
+ [[Set the font that this widget uses, the font should be a monospaced scalable font.
+ Passing NULL will load the default system monospaced font.]]
+ }
+ get {
+ [[Get the font currently in use.
+ The font name is a copy ad should be freed once it is no longer needed]]
+ }
+ values {
+ name: const(char) *; [[The name of the font to load]]
+ size: Evas.Font.Size; [[The font size for the widget]]
+ }
+ }
+ @property columns {
+ get {
+ [[Get the number of columns in the widget currently.
+ This will be the max of the number of columns to represent the longest line and
+ the minimum required to fill the visible widget width.]]
+ }
+ values {
+ columns: uint; [[The number of columns required to render the widget]]
+ }
+ }
+ @property gravity {
+ set {
+ [[Set how this widget's scroller should respond to new lines being added.
+
+ An x value of 0.0 will maintain the distance from the left edge, 1.0 will ensure the rightmost edge (of the longest line) is respected
+ With 0.0 for y the view will keep it's position relative to the top whereas 1.0 will scroll downward as lines are added.]]
+ }
+ get {
+ [[Get the current x and y gravity of the widget's scroller]]
+ }
+ values {
+ x: double; [[The horizontal value of the scroller gravity - valid values are 0.0 and 1.0]]
+ y: double; [[The vertical gravity of the widget's scroller - valid values are 0.0 and 1.0]]
+ }
+ }
+ @property policy {
+ set {
+ [[Set the policy for scrollbar visibility.]]
+ }
+ get {
+ [[Get the widget's policy for scrollbar visibility.]]
+ }
+ values {
+ policy_h: Elm.Scroller.Policy; [[The horizontal scrollbar visibility policy]]
+ policy_v: Elm.Scroller.Policy; [[The vertical scrollbar visibility policy]]
+ }
+ }
+ @property tabstop {
+ set {
+ [[Set the width of a tab stop, used purely for visual layout of tab characters.
+
+ Recommended value is between 2 and 8.]]
+ }
+ get {
+ [[Get the current width of a tab stop.
+ This is used to determine where characters after a tab should appear in the line.]]
+ }
+ values {
+ tabstop: uint; [[Maximum width of a tab character]]
+ }
+ }
+ @property editable {
+ set {
+ [[Set whether this widget allows editing
+
+ If editable then the widget will allow user input to manipulate
+ the underlying Elm_Code_File of this Elm_Code instance.
+ Any other Elm_Code_Widget's connected to this Elm_Code will
+ update to reflect the changes.]]
+ }
+ get {
+ [[Get the current editable state of this widget
+
+ returns EINA_TRUE if the widget is editable, EINA_FALSE otherwise.
+ If this widget is not editable the underlying Elm_Code_File could
+ still be manipulated by a different widget or the filesystem.]]
+ }
+ values {
+ editable: bool; [[The editable state of the widget]]
+ }
+ }
+ @property line_numbers {
+ set {
+ [[Set whether line numbers should be displayed in the left gutter.
+
+ Passing EINA_TRUE will reserve a space for showing line numbers,
+ EINA_FALSE will turn this off.]]
+ }
+ get {
+ [[Get the status of line number display for this widget.]]
+ }
+ values {
+ line_numbers: bool; [[Whether or not line numbers (or their placeholder) should be shown]]
+ }
+ }
+ @property line_width_marker {
+ set {
+ [[Set where the line width market should be shown.
+
+ Passing a non-zero value will set which line width to mark with a vertical line.
+ Passing 0 will hide this marker.]]
+ }
+ get {
+ [[Get the position of the line width marker, any positive return indicates where the marker appears.]]
+ }
+ values {
+ line_width_marker: uint; [[Where to display a line width marker, if at all]]
+ }
+ }
+ @property show_whitespace {
+ set {
+ [[Set where white space should be shown.]]
+ }
+ get {
+ [[Get whether or not white space will be visible.]]
+ }
+ values {
+ show_whitespace: bool; [[Whether or not we show whitespace characters]]
+ }
+ }
+ @property tab_inserts_spaces {
+ set {
+ [[Set whether space characters should be inserted instead of tabs.]]
+ }
+ get {
+ [[Get whether or not space characters will be inserted instead of tabs.]]
+ }
+ values {
+ tab_inserts_spaces: bool; [[EINA_TRUE if we should insert space characters instead of a tab when the Tab key is pressed]]
+ }
+ }
+ @property cursor_position {
+ set {
+ [[Set the current location of the text cursor.]]
+ }
+ get {
+ [[Get the current x and y position of the widget's cursor.]]
+ }
+ values {
+ col: uint; [[The horizontal position of the cursor, starting from column 1]]
+ line: uint; [[The vertical position of the cursor - the top row is 1]]
+ }
+ }
+ line_refresh {
+ params {
+ line: Elm_Code_Line *; [[The line to refresh.]]
+ }
+ }
+ line_visible_get {
+ params {
+ line: Elm_Code_Line *; [[The line to test for visibility.]]
+ }
+ return: bool; [[true if the line specified is currently visible within the scroll region.]]
+ }
+ lines_visible_get {
+ return: uint; [[the number of lines currently visible in the widget.]]
+ }
+ position_at_coordinates_get {
+ [[get the row, col position for a given coordinate on the widget.]]
+ params {
+ x: Evas.Coord; [[the x coordinate in the widget]]
+ y: Evas.Coord; [[the y coordinate in the widget]]
+ row: uint *; [[the row for the coordinates]]
+ col: int *; [[the column for the coordinates]]
+ }
+ return: bool; [[true if a line exists at these coordinates]]
+ }
+
+ //text functions
+ text_left_gutter_width_get {
+ return: int; [[the current column width of the gutter for the widget.]]
+ }
+ text_line_number_width_get {
+ return: int; [[the column width required to represent the number of lines in the widget.]]
+ }
+ text_between_positions_get {
+ params {
+ start_col: uint; [[the widget column of the first character to get]]
+ start_line: uint; [[the line of the first character to get]]
+ end_col: uint; [[the widget column of the last character to get]]
+ end_line: uint; [[the line of the last character to get]]
+ }
+ return: char *; [[the text content between start and end positions]]
+ }
+
+ line_text_column_width_to_position {
+ params {
+ line: Elm_Code_Line *;
+ position: uint;
+ }
+ return: uint;
+ }
+ line_text_column_width_get {
+ params {
+ line: Elm_Code_Line *;
+ }
+ return: uint;
+ }
+ line_text_position_for_column_get {
+ params {
+ line: Elm_Code_Line *;
+ column: uint;
+ }
+ return: uint;
+ }
+ text_tabwidth_at_column_get {
+ params {
+ column: uint;
+ }
+ return: uint;
+ }
+ undo {
+ }
+ }
+ implements {
+ class.constructor;
+ Eo.Base.constructor;
+ Eo.Base.finalize;
+ Evas.Object.Smart.add;
+ Elm.Widget.event;
+ Elm.Widget.focus_next_manager_is;
+ Elm.Widget.focus_direction_manager_is;
+ }
+ constructors {
+ .code;
+ }
+ events {
+ line,clicked;
+ line,gutter,clicked;
+ cursor,changed;
+ changed,user;
+ selection,changed;
+ selection,cleared;
+ }
+
+}
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_legacy.h b/legacy/elm_code/src/lib/widget/elm_code_widget_legacy.h
new file mode 100644
index 0000000000..450f4adeae
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_legacy.h
@@ -0,0 +1,13 @@
+/**
+ * @brief Add a new elm_code widget to the parent
+ *
+ * @param parent The parent object
+ * @return The new object or NULL if it cannot be created
+ *
+ * @see elm_code_widget_code_set
+ *
+ * @ingroup Data
+ */
+EAPI Evas_Object *elm_code_widget_add(Evas_Object *parent, Elm_Code *code);
+
+#include "widget/elm_code_widget.eo.legacy.h"
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_private.h b/legacy/elm_code/src/lib/widget/elm_code_widget_private.h
new file mode 100644
index 0000000000..09c5003994
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_private.h
@@ -0,0 +1,66 @@
+#ifndef ELM_CODE_WIDGET_PRIVATE_H
+# define ELM_CODE_WIDGET_PRIVATE_H
+
+/**
+ * Structure holding the info about a selected region.
+ */
+typedef struct
+{
+ unsigned int start_line, end_line;
+ unsigned int start_col, end_col;
+} Elm_Code_Widget_Selection_Data;
+
+typedef struct
+{
+ Elm_Code *code;
+ Eina_List *grids;
+ unsigned int col_count;
+ Evas_Object *scroller, *gridbox;
+
+ const char *font_name;
+ Evas_Font_Size font_size;
+ double gravity_x, gravity_y;
+
+ unsigned int cursor_line, cursor_col;
+ Eina_Bool editable, focussed;
+ Eina_Bool show_line_numbers;
+ unsigned int line_width_marker, tabstop;
+ Eina_Bool show_whitespace, tab_inserts_spaces;
+
+ Elm_Code_Widget_Selection_Data *selection;
+
+ /* Undo stack */
+ Eina_List *undo_stack;
+ Eina_List *undo_stack_ptr;
+} Elm_Code_Widget_Data;
+
+typedef struct
+{
+ char *content;
+ unsigned int length;
+ unsigned int start_line, start_col, end_line, end_col;
+
+ Eina_Bool insert : 1; /**< True if the change is an insertion */
+} Elm_Code_Widget_Change_Info;
+
+/* Private widget methods */
+
+void _elm_code_widget_cell_size_get(Elm_Code_Widget *widget, Evas_Coord *width, Evas_Coord *height);
+
+void _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text, int length);
+
+void _elm_code_widget_newline(Elm_Code_Widget *widget);
+
+void _elm_code_widget_backspace(Elm_Code_Widget *widget);
+
+void _elm_code_widget_delete(Elm_Code_Widget *widget);
+
+void _elm_code_widget_tooltip_text_set(Evas_Object *widget, const char *text);
+
+void _elm_code_widget_tooltip_add(Evas_Object *widget);
+
+EAPI Elm_Code_Widget_Selection_Data *elm_code_widget_selection_normalized_get(Evas_Object *widget);
+
+void _elm_code_widget_undo_change_add(Evas_Object *widget, Elm_Code_Widget_Change_Info *info);
+
+#endif
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_selection.c b/legacy/elm_code/src/lib/widget/elm_code_widget_selection.c
new file mode 100644
index 0000000000..9e82ac5456
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_selection.c
@@ -0,0 +1,454 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_widget_private.h"
+
+static char _breaking_chars[] = " \t,.?!;:*&()[]{}";
+
+static Elm_Code_Widget_Selection_Data *
+_elm_code_widget_selection_new()
+{
+ Elm_Code_Widget_Selection_Data *data;
+
+ data = calloc(1, sizeof(Elm_Code_Widget_Selection_Data));
+
+ return data;
+}
+
+static void
+_elm_code_widget_selection_limit(Evas_Object *widget EINA_UNUSED, Elm_Code_Widget_Data *pd,
+ unsigned int *row, unsigned int *col)
+{
+ Elm_Code_Line *line;
+ Elm_Code_File *file;
+ unsigned int width;
+
+ file = pd->code->file;
+
+ if (*row > elm_code_file_lines_get(file))
+ *row = elm_code_file_lines_get(file);
+
+ line = elm_code_file_line_get(file, *row);
+ width = elm_code_widget_line_text_column_width_get(widget, line);
+
+ if (*col > width + 1)
+ *col = width + 1;
+ if (*col < 1)
+ *col = 1;
+}
+
+EAPI void
+elm_code_widget_selection_start(Evas_Object *widget,
+ unsigned int line, unsigned int col)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ _elm_code_widget_selection_limit(widget, pd, &line, &col);
+ if (!pd->selection)
+ {
+ selection = _elm_code_widget_selection_new();
+
+ selection->end_line = line;
+ selection->end_col = col;
+
+ pd->selection = selection;
+ }
+
+ pd->selection->start_line = line;
+ pd->selection->start_col = col;
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CHANGED, widget);
+ elm_obj_code_widget_cursor_position_set(widget, col, line);
+}
+
+EAPI void
+elm_code_widget_selection_end(Evas_Object *widget,
+ unsigned int line, unsigned int col)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ _elm_code_widget_selection_limit(widget, pd, &line, &col);
+ if (!pd->selection)
+ {
+ selection = _elm_code_widget_selection_new();
+
+ selection->start_line = line;
+ selection->start_col = col;
+
+ pd->selection = selection;
+ }
+
+ pd->selection->end_line = line;
+ pd->selection->end_col = col;
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CHANGED, widget);
+}
+
+EAPI Elm_Code_Widget_Selection_Data *
+elm_code_widget_selection_normalized_get(Evas_Object *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Selection_Data *selection;
+ Eina_Bool reverse;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ selection = _elm_code_widget_selection_new();
+
+ if (!pd->selection)
+ {
+ selection->start_line = selection->end_line = 1;
+ selection->start_col = selection->end_col = 1;
+
+ return selection;
+ }
+
+ if (pd->selection->start_line == pd->selection->end_line)
+ reverse = pd->selection->start_col > pd->selection->end_col;
+ else
+ reverse = pd->selection->start_line > pd->selection->end_line;
+
+ if (reverse)
+ {
+ selection->start_line = pd->selection->end_line;
+ selection->start_col = pd->selection->end_col;
+ selection->end_line = pd->selection->start_line;
+ selection->end_col = pd->selection->start_col;
+ }
+ else
+ {
+ selection->start_line = pd->selection->start_line;
+ selection->start_col = pd->selection->start_col;
+ selection->end_line = pd->selection->end_line;
+ selection->end_col = pd->selection->end_col;
+ }
+
+ return selection;
+}
+
+EAPI void
+elm_code_widget_selection_clear(Evas_Object *widget)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (!pd->selection)
+ return;
+
+ free(pd->selection);
+ pd->selection = NULL;
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CLEARED, widget);
+}
+
+static void
+_elm_code_widget_selection_delete_single(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
+{
+ Elm_Code_Line *line;
+ const char *old;
+ unsigned int old_length, start, end, length;
+ char *content;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ selection = elm_code_widget_selection_normalized_get(widget);
+ line = elm_code_file_line_get(pd->code->file, selection->start_line);
+ old = elm_code_line_text_get(line, &old_length);
+ start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col);
+ end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col);
+ length = line->length - (end - start + 1);
+
+ if (end == line->length)
+ {
+ length = line->length - (end - start);
+
+ content = malloc(sizeof(char) * length);
+ strncpy(content, old, start);
+ }
+ else
+ {
+ length = line->length - (end - start + 1);
+
+ content = malloc(sizeof(char) * length);
+ strncpy(content, old, start);
+ strncpy(content + start, old + end + 1,
+ old_length - (end + 1));
+ }
+ elm_code_line_text_set(line, content, length);
+ free(content);
+ free(selection);
+}
+
+static void
+_elm_code_widget_selection_delete_multi(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
+{
+ Elm_Code_Line *line;
+ const char *first, *last;
+ unsigned int last_length, start, end, length, i;
+ char *content;
+ Elm_Code_Widget_Selection_Data *selection;
+
+ if (pd->selection->end_line == pd->selection->start_line)
+ return;
+
+ selection = elm_code_widget_selection_normalized_get(widget);
+ line = elm_code_file_line_get(pd->code->file, selection->start_line);
+ first = elm_code_line_text_get(line, NULL);
+ start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col);
+
+ line = elm_code_file_line_get(pd->code->file, selection->end_line);
+ last = elm_code_line_text_get(line, &last_length);
+ end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col);
+
+ if (last_length == end)
+ {
+ length = start + last_length - end;
+ content = malloc(sizeof(char) * length);
+ strncpy(content, first, start);
+ }
+ else
+ {
+ length = start + last_length - (end + 1);
+ content = malloc(sizeof(char) * length);
+ strncpy(content, first, start);
+
+ strncpy(content + start, last + end + 1, last_length - (end + 1));
+ }
+
+ for (i = line->number; i > selection->start_line; i--)
+ elm_code_file_line_remove(pd->code->file, i);
+
+ line = elm_code_file_line_get(pd->code->file, selection->start_line);
+ elm_code_line_text_set(line, content, length);
+ free(content);
+ free(selection);
+}
+
+EAPI void
+elm_code_widget_selection_delete(Evas_Object *widget)
+{
+ Elm_Code_Widget_Data *pd;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (!pd->selection)
+ return;
+
+ if (pd->selection->start_line == pd->selection->end_line)
+ _elm_code_widget_selection_delete_single(widget, pd);
+ else
+ _elm_code_widget_selection_delete_multi(widget, pd);
+
+ free(pd->selection);
+ pd->selection = NULL;
+ eo_event_callback_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CLEARED, widget);
+}
+
+EAPI void
+elm_code_widget_selection_select_line(Evas_Object *widget, unsigned int line)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *lineobj;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ lineobj = elm_code_file_line_get(pd->code->file, line);
+
+ if (!lineobj)
+ return;
+
+ elm_code_widget_selection_start(widget, line, 1);
+ elm_code_widget_selection_end(widget, line, lineobj->length);
+}
+
+static Eina_Bool
+_elm_code_widget_selection_char_breaks(char chr)
+{
+ unsigned int i;
+
+ if (chr == 0)
+ return EINA_TRUE;
+
+ for (i = 0; i < sizeof(_breaking_chars); i++)
+ if (chr == _breaking_chars[i])
+ return EINA_TRUE;
+
+
+ return EINA_FALSE;
+}
+
+EAPI void
+elm_code_widget_selection_select_word(Evas_Object *widget, unsigned int line, unsigned int col)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Line *lineobj;
+ unsigned int colpos, length, pos;
+ const char *content;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+ lineobj = elm_code_file_line_get(pd->code->file, line);
+ content = elm_code_line_text_get(lineobj, &length);
+
+ _elm_code_widget_selection_limit(widget, pd, &line, &col);
+ colpos = elm_code_widget_line_text_position_for_column_get(widget, lineobj, col);
+
+ pos = colpos;
+ while (pos > 0)
+ {
+ if (_elm_code_widget_selection_char_breaks(content[pos - 1]))
+ break;
+ pos--;
+ }
+ elm_code_widget_selection_start(widget, line,
+ elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));
+
+ pos = colpos;
+ while (pos < length - 1)
+ {
+ if (_elm_code_widget_selection_char_breaks(content[pos + 1]))
+ break;
+ pos++;
+ }
+ elm_code_widget_selection_end(widget, line,
+ elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));
+}
+
+EAPI char *
+elm_code_widget_selection_text_get(Evas_Object *widget)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Selection_Data *selection;
+ char *text;
+
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ if (!pd->selection)
+ return strdup("");
+
+ selection = elm_code_widget_selection_normalized_get(widget);
+
+ text = elm_code_widget_text_between_positions_get(widget,
+ selection->start_col, selection->start_line,
+ selection->end_col, selection->end_line);
+
+ free(selection);
+ return text;
+}
+
+static void
+_selection_loss_cb(void *data, Elm_Sel_Type selection EINA_UNUSED)
+{
+ Elm_Code_Widget *widget;
+
+ widget = (Elm_Code_Widget *)data;
+// TODO we need to know whih selection we are clearing!
+// elm_code_widget_selection_clear(widget);
+}
+
+EAPI void
+elm_code_widget_selection_cut(Evas_Object *widget)
+{
+ char *text;
+
+ text = elm_code_widget_selection_text_get(widget);
+ elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
+ elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
+ free(text);
+
+ elm_code_widget_selection_delete(widget);
+}
+
+EAPI void
+elm_code_widget_selection_copy(Evas_Object *widget)
+{
+ char *text;
+
+ text = elm_code_widget_selection_text_get(widget);
+ elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
+ elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
+ free(text);
+}
+
+static void
+_selection_paste_single(Elm_Code_Widget *widget, Elm_Code *code,
+ unsigned int col, unsigned int row, const char *text, unsigned int len)
+{
+ Elm_Code_Line *line;
+ unsigned int position, newcol;
+
+ line = elm_code_file_line_get(code->file, row);
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ elm_code_line_text_insert(line, position, text, len);
+
+ newcol = elm_code_widget_line_text_column_width_to_position(widget, line, position + len);
+ elm_obj_code_widget_cursor_position_set(widget, newcol, row);
+}
+
+static void
+_selection_paste_multi(Elm_Code_Widget *widget, Elm_Code *code,
+ unsigned int col, unsigned int row, const char *text, unsigned int len)
+{
+ Elm_Code_Line *line;
+ unsigned int position, newrow, remain;
+ int nlpos;
+ short nllen;
+ char *ptr;
+
+ line = elm_code_file_line_get(code->file, row);
+ position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
+ elm_code_line_split_at(line, position);
+
+ newrow = row;
+ ptr = (char *)text;
+ remain = len;
+ while ((nlpos = elm_code_text_newlinenpos(ptr, remain, &nllen)) != ELM_CODE_TEXT_NOT_FOUND)
+ {
+ if (newrow == row)
+ _selection_paste_single(widget, code, col, row, text, nlpos);
+ else
+ elm_code_file_line_insert(code->file, newrow, ptr, nlpos, NULL);
+
+ remain -= nlpos + nllen;
+ ptr += nlpos + nllen;
+ newrow++;
+ }
+
+ _selection_paste_single(widget, code, 1, newrow, ptr, len - (ptr - text));
+}
+
+static Eina_Bool
+_selection_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, Elm_Selection_Data *ev)
+{
+ Elm_Code *code;
+ Elm_Code_Widget *widget;
+ unsigned int row, col;
+
+ widget = (Elm_Code_Widget *)data;
+
+ if (ev->format != ELM_SEL_FORMAT_TEXT)
+ return EINA_TRUE;
+ if (ev->len <= 0)
+ return EINA_TRUE;
+
+ code = elm_obj_code_widget_code_get(widget);
+ elm_obj_code_widget_cursor_position_get(widget, &col, &row);
+
+ if (elm_code_text_newlinenpos(ev->data, ev->len, NULL) == ELM_CODE_TEXT_NOT_FOUND)
+ _selection_paste_single(widget, code, col, row, ev->data, ev->len - 1);
+ else
+ _selection_paste_multi(widget, code, col, row, ev->data, ev->len - 1);
+
+ return EINA_TRUE;
+}
+
+EAPI void
+elm_code_widget_selection_paste(Evas_Object *widget)
+{
+ elm_code_widget_selection_delete(widget);
+
+ elm_cnp_selection_get(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, _selection_paste_cb, widget);
+}
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_selection.h b/legacy/elm_code/src/lib/widget/elm_code_widget_selection.h
new file mode 100644
index 0000000000..b79abc763f
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_selection.h
@@ -0,0 +1,44 @@
+#ifndef ELM_CODE_WIDGET_SELECTION_H_
+# define ELM_CODE_WIDGET_SELECTION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Selection handling functions.
+ * @defgroup Managing the complexities of selecting text across seperate lines.
+ *
+ * @{
+ *
+ * Functions for selection handling
+ *
+ */
+
+EAPI void elm_code_widget_selection_start(Evas_Object *widget, unsigned int line, unsigned int col);
+
+EAPI void elm_code_widget_selection_end(Evas_Object *widget, unsigned int line, unsigned int col);
+
+EAPI void elm_code_widget_selection_clear(Evas_Object *widget);
+
+EAPI void elm_code_widget_selection_delete(Evas_Object *widget);
+
+EAPI void elm_code_widget_selection_select_line(Evas_Object *widget, unsigned int line);
+
+EAPI void elm_code_widget_selection_select_word(Evas_Object *widget, unsigned int line, unsigned int col);
+
+EAPI char *elm_code_widget_selection_text_get(Evas_Object *widget);
+
+EAPI void elm_code_widget_selection_cut(Evas_Object *widget);
+EAPI void elm_code_widget_selection_copy(Evas_Object *widget);
+EAPI void elm_code_widget_selection_paste(Evas_Object *widget);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ELM_CODE_WIDGET_SELECTION_H_ */
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_text.c b/legacy/elm_code/src/lib/widget/elm_code_widget_text.c
new file mode 100644
index 0000000000..3689e8284a
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_text.c
@@ -0,0 +1,197 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_widget_private.h"
+
+static int
+_elm_code_widget_text_line_number_width_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ int max;
+
+ max = elm_code_file_lines_get(pd->code->file);
+ if (max < 1)
+ max = 1;
+
+ return floor(log10(max)) + 1;
+}
+
+static int
+_elm_code_widget_text_left_gutter_width_get(Eo *obj, Elm_Code_Widget_Data *pd)
+{
+ Elm_Code_Widget *widget;
+ int width = 1; // the status icon, for now
+
+ widget = obj;
+ if (!widget)
+ return width;
+
+ if (pd->show_line_numbers)
+ width += _elm_code_widget_text_line_number_width_get(widget, pd);
+
+ return width;
+}
+
+static char *
+_elm_code_widget_text_multi_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
+ unsigned int start_col, unsigned int start_line,
+ unsigned int end_col, unsigned int end_line)
+{
+ Elm_Code_Line *line;
+ char *first, *last, *ret, *ptr;
+ const char *newline;
+ short newline_len;
+ int ret_len;
+ unsigned int row, start, end;
+
+ newline = elm_code_file_line_ending_chars_get(pd->code->file, &newline_len);
+
+ line = elm_code_file_line_get(pd->code->file, start_line);
+ start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
+ first = elm_code_line_text_substr(line, start, line->length - start + 1);
+
+ line = elm_code_file_line_get(pd->code->file, end_line);
+ end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
+ last = elm_code_line_text_substr(line, 0, end);
+
+ ret_len = strlen(first) + strlen(last) + newline_len;
+
+ for (row = pd->selection->start_line + 1; row < end_line; row++)
+ {
+ line = elm_code_file_line_get(pd->code->file, row);
+ ret_len += line->length + newline_len;
+ }
+
+ ret = malloc(sizeof(char) * (ret_len + 1));
+
+ snprintf(ret, strlen(first) + newline_len + 1, "%s%s", first, newline);
+
+ ptr = ret;
+ ptr += strlen(first) + newline_len;
+
+ for (row = start_line + 1; row < end_line; row++)
+ {
+ line = elm_code_file_line_get(pd->code->file, row);
+ if (line->modified)
+ snprintf(ptr, line->length + 1, "%s", line->modified);
+ else
+ snprintf(ptr, line->length + 1, "%s", line->content);
+
+ snprintf(ptr + line->length, newline_len + 1, "%s", newline);
+ ptr += line->length + newline_len;
+ }
+ snprintf(ptr, strlen(last) + 1, "%s", last);
+
+ free(first);
+ free(last);
+ return ret;
+}
+
+static char *
+_elm_code_widget_text_single_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
+ unsigned int start_col, unsigned int start_line,
+ unsigned int end_col)
+{
+ Elm_Code_Line *line;
+ unsigned int start, end;
+
+ line = elm_code_file_line_get(pd->code->file, start_line);
+ start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
+ end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
+
+ return elm_code_line_text_substr(line, start, end - start);
+}
+
+static char *
+_elm_code_widget_text_between_positions_get(Eo *widget, Elm_Code_Widget_Data *pd,
+ unsigned int start_col, unsigned int start_line,
+ unsigned int end_col, unsigned int end_line)
+{
+ if (start_line == end_line)
+ return _elm_code_widget_text_single_get(widget, pd, start_col, start_line, end_col);
+ else
+ return _elm_code_widget_text_multi_get(widget, pd, start_col, start_line, end_col, end_line);
+}
+
+static unsigned int
+_elm_code_widget_line_text_column_width_to_position(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int position)
+{
+ Eina_Unicode unicode;
+ unsigned int count = 1;
+ int index = 0;
+ const char *chars;
+
+ if (line->length == 0)
+ return 1;
+
+ if (line->modified)
+ chars = line->modified;
+ else
+ chars = line->content;
+ if (position > line->length)
+ position = line->length;
+
+ while ((unsigned int) index < position)
+ {
+ unicode = eina_unicode_utf8_next_get(chars, &index);
+ if (unicode == 0)
+ break;
+
+ if (unicode == '\t')
+ count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
+ else
+ count++;
+ }
+
+ return count;
+}
+
+static unsigned int
+_elm_code_widget_line_text_column_width_get(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
+{
+ if (!line)
+ return 0;
+
+ return _elm_code_widget_line_text_column_width_to_position(obj, pd, line, line->length) - 1;
+}
+
+static unsigned int
+_elm_code_widget_line_text_position_for_column_get(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int column)
+{
+ Eina_Unicode unicode;
+ unsigned int count = 1, position = 0;
+ int index = 0;
+ const char *chars;
+
+ if (line->length == 0 || column == 1)
+ return 0;
+
+ if (line->modified)
+ chars = line->modified;
+ else
+ chars = line->content;
+
+ while ((unsigned int) count <= column && index <= (int) line->length)
+ {
+ position = (unsigned int) index;
+ unicode = eina_unicode_utf8_next_get(chars, &index);
+
+ if (unicode == 0)
+ return line->length;
+ else if (unicode == '\t')
+ count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
+ else
+ count++;
+ }
+
+ return position;
+}
+
+static unsigned int
+_elm_code_widget_text_tabwidth_at_column_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int column)
+{
+ return pd->tabstop - ((column - 1) % pd->tabstop);
+}
+
diff --git a/legacy/elm_code/src/lib/widget/elm_code_widget_undo.c b/legacy/elm_code/src/lib/widget/elm_code_widget_undo.c
new file mode 100644
index 0000000000..1eaceaaedc
--- /dev/null
+++ b/legacy/elm_code/src/lib/widget/elm_code_widget_undo.c
@@ -0,0 +1,69 @@
+#ifdef HAVE_CONFIG
+# include "config.h"
+#endif
+
+#include "Elm_Code.h"
+
+#include "elm_code_widget_private.h"
+
+Elm_Code_Widget_Change_Info *
+_elm_code_widget_undo_info_copy(Elm_Code_Widget_Change_Info *info)
+{
+ Elm_Code_Widget_Change_Info *copy;
+
+ copy = calloc(1, sizeof(*info));
+ memcpy(copy, info, sizeof(*info));
+ copy->content = malloc(sizeof(char) * (info->length + 1));
+ strncpy(copy->content, info->content, info->length);
+
+ return copy;
+}
+
+void
+_elm_code_widget_undo_change_add(Evas_Object *widget,
+ Elm_Code_Widget_Change_Info *info)
+{
+ Elm_Code_Widget_Data *pd;
+ Elm_Code_Widget_Change_Info *info_copy;
+
+ info_copy = _elm_code_widget_undo_info_copy(info);
+ pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
+
+ pd->undo_stack_ptr = eina_list_prepend(pd->undo_stack_ptr, info_copy);
+ pd->undo_stack = pd->undo_stack_ptr;
+}
+
+static void
+_elm_code_widget_undo_change(Evas_Object *widget,
+ Elm_Code_Widget_Change_Info *info)
+{
+ if (info->insert)
+ {
+ elm_code_widget_selection_start(widget, info->start_line, info->start_col);
+ elm_code_widget_selection_end(widget, info->end_line, info->end_col);
+ elm_code_widget_selection_delete(widget);
+ }
+ else
+ {
+ elm_code_widget_cursor_position_set(widget, info->start_col, info->start_line);
+ _elm_code_widget_text_at_cursor_insert(widget, info->content, info->length);
+ }
+}
+
+static void
+_elm_code_widget_undo(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
+{
+ Elm_Code_Widget_Change_Info *info;
+
+ if (!pd->undo_stack_ptr)
+ return;
+
+ info = eina_list_data_get(pd->undo_stack_ptr);
+ _elm_code_widget_undo_change(obj, info);
+
+ if (eina_list_next(pd->undo_stack_ptr))
+ pd->undo_stack_ptr = eina_list_next(pd->undo_stack_ptr);
+ else
+ pd->undo_stack_ptr = NULL;
+}
+
diff --git a/legacy/elm_code/src/tests/Makefile.am b/legacy/elm_code/src/tests/Makefile.am
new file mode 100644
index 0000000000..6150c6f6e0
--- /dev/null
+++ b/legacy/elm_code/src/tests/Makefile.am
@@ -0,0 +1,49 @@
+MAINTAINERCLEANFILES = Makefile.in
+CLEANFILES = check-results.xml
+
+if EFL_HAVE_TESTS
+
+check_PROGRAMS = elm_code_suite
+
+elm_code_suite_SOURCES = \
+elm_code_file_test_load.c \
+elm_code_file_test_memory.c \
+elm_code_test_basic.c \
+elm_code_test_line.c \
+elm_code_test_parse.c \
+elm_code_test_text.c \
+widget/elm_code_test_widget.c \
+widget/elm_code_test_widget_text.c \
+widget/elm_code_test_widget_selection.c \
+widget/elm_code_test_widget_undo.c \
+elm_code_suite.c
+
+elm_code_suite_CPPFLAGS = \
+-DEFL_BETA_API_SUPPORT \
+-DEFL_EO_API_SUPPORT \
+-I$(top_srcdir)/elm_code/src/lib \
+-I$(top_builddir)/elm_code/src/lib \
+-I$(top_srcdir)/elm_code/src/lib/widget \
+-I$(top_builddir)/elm_code/src/lib/widget \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+-DPACKAGE_TESTS_DIR=\"$(top_srcdir)/elm_code/src/tests/\" \
+-DPACKAGE_BUILD_DIR=\"`pwd`/$(top_builddir)/elm_code/src/tests/\" \
+-DTESTS_DIR=\"$(abspath $(srcdir))/\" \
+-DEFL_BETA_API_SUPPORT \
+@EFL_CFLAGS@ \
+-DEFL_ELM_CODE_BUILD \
+@CHECK_CFLAGS@
+
+elm_code_suite_LDADD = @EFL_LIBS@ @CHECK_LIBS@ $(top_builddir)/elm_code/src/lib/libelm_code.la
+elm_code_suite_DEPENDENCIES = $(top_builddir)/elm_code/src/lib/libelm_code.la
+
+testdir = $(PACKAGE_TESTS_DIR)
+test_DATA = \
+testfile.txt \
+testfile-windows.txt \
+testfile-withblanks.txt \
+testdiff.diff
+
+EXTRA_DIST = elm_code_suite.h $(test_DATA)
+
+endif
diff --git a/legacy/elm_code/src/tests/elm_code_file_test_load.c b/legacy/elm_code/src/tests/elm_code_file_test_load.c
new file mode 100644
index 0000000000..02a6f7ee74
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_file_test_load.c
@@ -0,0 +1,146 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+START_TEST (elm_code_file_load)
+{
+ char *path = TESTS_DIR "testfile.txt";
+ char real[EINA_PATH_MAX];
+ Elm_Code_File *file;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+ realpath(path, real);
+
+ ck_assert_str_eq(basename(path), elm_code_file_filename_get(file));
+ ck_assert_str_eq(real, elm_code_file_path_get(file));
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_load_lines)
+{
+ char *path = TESTS_DIR "testfile.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ ck_assert_uint_eq(4, elm_code_file_lines_get(file));
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_load_blank_lines)
+{
+ char *path = TESTS_DIR "testfile-withblanks.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ ck_assert_uint_eq(8, elm_code_file_lines_get(file));
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_load_windows)
+{
+ char *path = TESTS_DIR "testfile-windows.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ ck_assert_uint_eq(4, elm_code_file_lines_get(file));
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+static void _assert_line_content_eq(const char *content, Elm_Code_Line *line)
+{
+ int length;
+ int c;
+
+ length = strlen(content);
+ ck_assert_int_eq(length, line->length);
+
+ for (c = 0; c < length; c++)
+ ck_assert_uint_eq(content[c], line->content[c]);
+}
+
+START_TEST (elm_code_file_load_content)
+{
+ char *path = TESTS_DIR "testfile.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ _assert_line_content_eq("line2", elm_code_file_line_get(file, 2));
+ _assert_line_content_eq("another line", elm_code_file_line_get(file, 4));
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_line_ending_unix)
+{
+ char *path = TESTS_DIR "testfile.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+ short len;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ ck_assert_int_eq(ELM_CODE_FILE_LINE_ENDING_UNIX, elm_code_file_line_ending_get(file));
+ ck_assert_str_eq("\n", elm_code_file_line_ending_chars_get(file, &len));
+ ck_assert_int_eq(1, len);
+
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_line_ending_windows)
+{
+ char *path = TESTS_DIR "testfile-windows.txt";
+ Elm_Code_File *file;
+ Elm_Code *code;
+ short len;
+
+ code = elm_code_create();
+ file = elm_code_file_open(code, path);
+
+ ck_assert_int_eq(ELM_CODE_FILE_LINE_ENDING_WINDOWS, elm_code_file_line_ending_get(file));
+ ck_assert_str_eq("\r\n", elm_code_file_line_ending_chars_get(file, &len));
+ ck_assert_int_eq(2, len);
+
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+void elm_code_file_test_load(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_file_load);
+ tcase_add_test(tc, elm_code_file_load_lines);
+ tcase_add_test(tc, elm_code_file_load_blank_lines);
+ tcase_add_test(tc, elm_code_file_load_windows);
+ tcase_add_test(tc, elm_code_file_load_content);
+ tcase_add_test(tc, elm_code_file_line_ending_unix);
+ tcase_add_test(tc, elm_code_file_line_ending_windows);
+}
+
diff --git a/legacy/elm_code/src/tests/elm_code_file_test_memory.c b/legacy/elm_code/src/tests/elm_code_file_test_memory.c
new file mode 100644
index 0000000000..b82ce2eda2
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_file_test_memory.c
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+START_TEST (elm_code_file_memory_lines)
+{
+ Elm_Code *code;
+
+ code = elm_code_create();
+ ck_assert_uint_eq(0, elm_code_file_lines_get(code->file));
+
+ elm_code_file_line_append(code->file, "a line", 6, NULL);
+
+ ck_assert_uint_eq(1, elm_code_file_lines_get(code->file));
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_file_memory_tokens)
+{
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code *code;
+
+ code = elm_code_create();
+ file = code->file;
+ elm_code_file_line_append(file, "a line", 6, NULL);
+
+ line = elm_code_file_line_get(file, 1);
+ elm_code_line_token_add(line, 2, 5, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+ ck_assert_uint_eq(1, eina_list_count(line->tokens));
+ elm_code_free(code);
+}
+END_TEST
+
+void elm_code_file_test_memory(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_file_memory_lines);
+ tcase_add_test(tc, elm_code_file_memory_tokens);
+}
+
diff --git a/legacy/elm_code/src/tests/elm_code_suite.c b/legacy/elm_code/src/tests/elm_code_suite.c
new file mode 100644
index 0000000000..31ea7d9f0e
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_suite.c
@@ -0,0 +1,130 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <Ecore_Getopt.h>
+
+#include "Elm_Code.h"
+#include "elm_code_suite.h"
+
+#define COPYRIGHT "Copyright © 2014 Andy Williams <andy@andyilliams.me> and various contributors (see AUTHORS)."
+
+static const struct {
+ const char *name;
+ void (*build)(TCase *tc);
+} tests[] = {
+ { "file_load", elm_code_file_test_load },
+ { "file_memory", elm_code_file_test_memory },
+ { "parse", elm_code_test_parse },
+ { "text", elm_code_test_text },
+ { "basic", elm_code_test_basic },
+ { "widget", elm_code_test_widget },
+ { "widget_text", elm_code_test_widget_text },
+ { "widget_selection", elm_code_test_widget_selection },
+ { "widget_undo", elm_code_test_widget_undo },
+};
+
+START_TEST(elm_code_initialization)
+{
+ fail_if(elm_code_init() != 1);
+
+// TODO add other init checks here
+
+ fail_if(elm_code_shutdown() != 0);
+}
+END_TEST
+
+void
+edi_test_basic(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_initialization);
+}
+
+static const Ecore_Getopt optdesc = {
+ "elm_code",
+ "%prog [options]",
+ PACKAGE_VERSION,
+ COPYRIGHT,
+ "BSD with advertisement clause",
+ "Elm Code",
+ 0,
+ {
+ ECORE_GETOPT_STORE_TRUE('l', "list", "list available tests"),
+ ECORE_GETOPT_STORE_STR('t', "test", "test to run"),
+ ECORE_GETOPT_LICENSE('L', "license"),
+ ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+ ECORE_GETOPT_VERSION('V', "version"),
+ ECORE_GETOPT_HELP('h', "help"),
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+int
+main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
+{
+ Suite *s;
+ SRunner *sr;
+ TCase *tc = NULL;
+ char *test = NULL;
+ unsigned int i;
+ int failed_count = -1;
+ int args;
+ Eina_Bool quit_option = EINA_FALSE;
+ Eina_Bool list_option = EINA_FALSE;
+
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_BOOL(list_option),
+ ECORE_GETOPT_VALUE_STR(test),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_NONE
+ };
+
+ eina_init();
+
+ args = ecore_getopt_parse(&optdesc, values, argc, argv);
+ if (args < 0)
+ {
+ EINA_LOG_CRIT("Could not parse arguments.");
+ goto end;
+ }
+ else if (quit_option)
+ {
+ goto end;
+ }
+ else if (list_option)
+ {
+ fprintf(stdout, "Available tests :\n");
+ for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
+ fprintf(stdout, "\t%s\n", tests[i].name);
+ goto end;
+ }
+
+ s = suite_create("Elm_Code");
+
+ for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
+ {
+ if (test && strcmp(tests[i].name, test))
+ continue ;
+
+ tc = tcase_create(tests[i].name);
+ tcase_set_timeout(tc, 0);
+
+ tests[i].build(tc);
+ suite_add_tcase(s, tc);
+ }
+
+ sr = srunner_create(s);
+ srunner_set_xml(sr, PACKAGE_BUILD_DIR "/check-results.xml");
+
+ srunner_run_all(sr, CK_ENV);
+ failed_count = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ end:
+ eina_shutdown();
+
+ return (failed_count == 0) ? 0 : 255;
+}
diff --git a/legacy/elm_code/src/tests/elm_code_suite.h b/legacy/elm_code/src/tests/elm_code_suite.h
new file mode 100644
index 0000000000..750bfd7f18
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_suite.h
@@ -0,0 +1,31 @@
+#ifndef _ELM_CODE_SUITE_H
+#define _ELM_CODE_SUITE_H
+
+#include <check.h>
+
+#define ck_assert_strn_eq(s1, s2, len) \
+ { \
+ char expected[len+1], actual[len+1]; \
+\
+ strncpy(expected, s1, len); \
+ expected[len] = '\0'; \
+ strncpy(actual, s2, len); \
+ actual[len] = '\0'; \
+\
+ ck_assert_str_eq(expected, actual); \
+ }
+
+#include <Elm_Code.h>
+
+void elm_code_file_test_load(TCase *tc);
+void elm_code_file_test_memory(TCase *tc);
+void elm_code_test_basic(TCase *tc);
+void elm_code_test_line(TCase *tc);
+void elm_code_test_parse(TCase *tc);
+void elm_code_test_text(TCase *tc);
+void elm_code_test_widget(TCase *tc);
+void elm_code_test_widget_text(TCase *tc);
+void elm_code_test_widget_selection(TCase *tc);
+void elm_code_test_widget_undo(TCase *tc);
+
+#endif /* _EDLM_CODE_SUITE_H */
diff --git a/legacy/elm_code/src/tests/elm_code_test_basic.c b/legacy/elm_code/src/tests/elm_code_test_basic.c
new file mode 100644
index 0000000000..b968e90523
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_test_basic.c
@@ -0,0 +1,24 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+START_TEST (elm_code_create_test)
+{
+ char *path = "elm_code/src/tests/testfile.txt";
+ Elm_Code *code;
+
+ code = elm_code_create();
+ elm_code_file_open(code, path);
+
+ ck_assert(!!code);
+ elm_code_free(code);
+}
+END_TEST
+
+void elm_code_test_basic(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_create_test);
+}
+
diff --git a/legacy/elm_code/src/tests/elm_code_test_line.c b/legacy/elm_code/src/tests/elm_code_test_line.c
new file mode 100644
index 0000000000..b19bf615c3
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_test_line.c
@@ -0,0 +1,75 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+START_TEST (elm_code_line_create_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "a test string...", 16, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ ck_assert(!!line);
+
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_line_token_count_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "a test string...", 16, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ ck_assert_int_eq(0, eina_list_count(line->tokens));
+ elm_code_line_token_add(line, 2, 5, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+ ck_assert_int_eq(1, eina_list_count(line->tokens));
+ elm_code_line_tokens_clear(line);
+ ck_assert_int_eq(0, eina_list_count(line->tokens));
+
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_line_split_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line, *newline;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "line1line2", 10, NULL);
+ line = elm_code_file_line_get(file, 1);
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+ ck_assert_int_eq(10, line->length);
+
+ elm_code_line_split_at(line, 5);
+ ck_assert_int_eq(2, elm_code_file_lines_get(file));
+ newline = elm_code_file_line_get(file, 2);
+ ck_assert_int_eq(5, line->length);
+ ck_assert_int_eq(5, newline->length);
+}
+END_TEST
+
+void elm_code_test_line(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_line_create_test);
+ tcase_add_test(tc, elm_code_line_token_count_test);
+ tcase_add_test(tc, elm_code_line_split_test);
+}
+
diff --git a/legacy/elm_code/src/tests/elm_code_test_parse.c b/legacy/elm_code/src/tests/elm_code_test_parse.c
new file mode 100644
index 0000000000..e91845c3b5
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_test_parse.c
@@ -0,0 +1,94 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+#include "elm_code_parse.h"
+
+static int line_calls, file_calls;
+
+static void _parser_line_callback(Elm_Code_Line *line EINA_UNUSED, void *data EINA_UNUSED)
+{
+ line_calls++;
+}
+
+static void _parser_file_callback(Elm_Code_File *file EINA_UNUSED, void *data EINA_UNUSED)
+{
+ file_calls++;
+}
+
+START_TEST (elm_code_parse_hook_memory_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+
+ line_calls = 0;
+ file_calls = 0;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_parser_add(code, _parser_line_callback, _parser_file_callback, NULL);
+ elm_code_file_line_append(file, "some \"test content\" for parsing", 31, NULL);
+
+ ck_assert_int_eq(1, line_calls);
+ ck_assert_int_eq(0, file_calls);
+
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_parse_hook_file_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ char *path = TESTS_DIR "testfile.txt";
+
+ line_calls = 0;
+ file_calls = 0;
+
+ code = elm_code_create();
+
+ elm_code_parser_add(code, _parser_line_callback, _parser_file_callback, NULL);
+ file = elm_code_file_open(code, path);
+
+ ck_assert_int_eq(4, line_calls);
+ ck_assert_int_eq(1, file_calls);
+
+ elm_code_file_close(file);
+ elm_code_free(code);
+}
+END_TEST
+
+START_TEST (elm_code_parse_todo_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ elm_code_init();
+
+ code = elm_code_create();
+ elm_code_parser_standard_add(code, ELM_CODE_PARSER_STANDARD_TODO);
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "xxx TODO line", 13, NULL);
+ line = elm_code_file_line_get(file, 1);
+ ck_assert_int_eq(ELM_CODE_STATUS_TYPE_TODO, line->status);
+
+ elm_code_line_text_set(line, "FIXME too", 9);
+ ck_assert_int_eq(ELM_CODE_STATUS_TYPE_TODO, line->status);
+
+ elm_code_line_text_set(line, "TOFIX", 5);
+ ck_assert_int_eq(ELM_CODE_STATUS_TYPE_DEFAULT, line->status);
+ elm_code_shutdown();
+}
+END_TEST
+
+void elm_code_test_parse(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_parse_hook_memory_test);
+ tcase_add_test(tc, elm_code_parse_hook_file_test);
+ tcase_add_test(tc, elm_code_parse_todo_test);
+}
+
diff --git a/legacy/elm_code/src/tests/elm_code_test_text.c b/legacy/elm_code/src/tests/elm_code_test_text.c
new file mode 100644
index 0000000000..26e1aa18de
--- /dev/null
+++ b/legacy/elm_code/src/tests/elm_code_test_text.c
@@ -0,0 +1,158 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+#include "elm_code_text.h"
+
+START_TEST (elm_code_text_get_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "test", 4, NULL);
+ line = elm_code_file_line_get(file, 1);
+ ck_assert_str_eq("test", elm_code_line_text_get(line, NULL));
+}
+END_TEST
+
+START_TEST (elm_code_text_insert_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "test", 4, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ elm_code_line_text_insert(line, 4, "ing", 3);
+ ck_assert_str_eq("testing", elm_code_line_text_get(line, NULL));
+}
+END_TEST
+
+START_TEST (elm_code_text_contains_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "a test string...", 16, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ ck_assert_int_eq(EINA_TRUE, elm_code_line_text_contains(line, "test"));
+ ck_assert_int_eq(EINA_FALSE, elm_code_line_text_contains(line, "text"));
+
+ ck_assert_int_eq(EINA_TRUE, elm_code_line_text_contains(line, "a t"));
+ ck_assert_int_eq(EINA_TRUE, elm_code_line_text_contains(line, "..."));
+}
+END_TEST
+
+START_TEST (elm_code_text_strpos_test)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+
+ elm_code_file_line_append(file, "a test string...", 16, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ ck_assert_int_eq(2, elm_code_line_text_strpos(line, "test", 0));
+ ck_assert_int_eq(2, elm_code_line_text_strpos(line, "test", 1));
+ ck_assert_int_eq(2, elm_code_line_text_strpos(line, "test", 2));
+ ck_assert_int_eq(ELM_CODE_TEXT_NOT_FOUND, elm_code_line_text_strpos(line, "test", 5));
+ ck_assert_int_eq(ELM_CODE_TEXT_NOT_FOUND, elm_code_line_text_strpos(line, "text", 0));
+
+ ck_assert_int_eq(0, elm_code_line_text_strpos(line, "a t", 0));
+ ck_assert_int_eq(ELM_CODE_TEXT_NOT_FOUND, elm_code_line_text_strpos(line, "a t", 2));
+ ck_assert_int_eq(13, elm_code_line_text_strpos(line, "...", 0));
+}
+END_TEST
+
+START_TEST (elm_code_text_newline_position_test)
+{
+ short nllen;
+ const char *unixtext = "a test\nwith newline";
+ const char *wintext = "a windows\r\nnewline";
+
+ ck_assert_int_eq(6, elm_code_text_newlinenpos(unixtext, strlen(unixtext), &nllen));
+ ck_assert_int_eq(1, nllen);
+ ck_assert_int_eq(9, elm_code_text_newlinenpos(wintext, strlen(wintext), &nllen));
+ ck_assert_int_eq(2, nllen);
+}
+END_TEST
+
+START_TEST (elm_code_text_leading_whitespace_test)
+{
+ const char *text;
+
+ text = "testing";
+ ck_assert_int_eq(0, elm_code_text_leading_whitespace_length(text, strlen(text)));
+
+ text = " spaces";
+ ck_assert_int_eq(2, elm_code_text_leading_whitespace_length(text, strlen(text)));
+
+ text = "\t\ttabs";
+ ck_assert_int_eq(2, elm_code_text_leading_whitespace_length(text, strlen(text)));
+
+ text = " \t mix";
+ ck_assert_int_eq(3, elm_code_text_leading_whitespace_length(text, strlen(text)));
+}
+END_TEST
+
+START_TEST (elm_code_text_trailing_whitespace_test)
+{
+ const char *text;
+
+ text = "testing";
+ ck_assert_int_eq(0, elm_code_text_trailing_whitespace_length(text, strlen(text)));
+
+ text = "spaces ";
+ ck_assert_int_eq(2, elm_code_text_trailing_whitespace_length(text, strlen(text)));
+
+ text = "tabs\t\t";
+ ck_assert_int_eq(2, elm_code_text_trailing_whitespace_length(text, strlen(text)));
+
+ text = "mix \t ";
+ ck_assert_int_eq(3, elm_code_text_trailing_whitespace_length(text, strlen(text)));
+}
+END_TEST
+
+START_TEST (elm_code_text_is_whitespace_test)
+{
+ const char *text;
+
+ text = " ";
+ ck_assert_int_eq(1, elm_code_text_is_whitespace(text, strlen(text)));
+
+ text = " \t\t ";
+ ck_assert_int_eq(1, elm_code_text_is_whitespace(text, strlen(text)));
+
+ text = " . ";
+ ck_assert_int_eq(0, elm_code_text_is_whitespace(text, strlen(text)));
+}
+END_TEST
+
+void elm_code_test_text(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_text_get_test);
+ tcase_add_test(tc, elm_code_text_insert_test);
+ tcase_add_test(tc, elm_code_text_contains_test);
+ tcase_add_test(tc, elm_code_text_strpos_test);
+ tcase_add_test(tc, elm_code_text_newline_position_test);
+ tcase_add_test(tc, elm_code_text_leading_whitespace_test);
+ tcase_add_test(tc, elm_code_text_trailing_whitespace_test);
+ tcase_add_test(tc, elm_code_text_is_whitespace_test);
+}
diff --git a/legacy/elm_code/src/tests/testdiff.diff b/legacy/elm_code/src/tests/testdiff.diff
new file mode 100644
index 0000000000..157cbb7b98
--- /dev/null
+++ b/legacy/elm_code/src/tests/testdiff.diff
@@ -0,0 +1,10 @@
+--- testdiff1.txt 2014-11-22 21:16:16.279872989 +0000
++++ testdiff2.txt 2014-11-22 21:16:34.406052375 +0000
+@@ -1,5 +1,5 @@
+ Some content to diff
++added
+ more
+-removed
+-will change
++changed
+ unchanged
diff --git a/legacy/elm_code/src/tests/testfile-windows.txt b/legacy/elm_code/src/tests/testfile-windows.txt
new file mode 100644
index 0000000000..c397f82dcb
--- /dev/null
+++ b/legacy/elm_code/src/tests/testfile-windows.txt
@@ -0,0 +1,4 @@
+line 1
+line2
+a third
+another line
diff --git a/legacy/elm_code/src/tests/testfile-withblanks.txt b/legacy/elm_code/src/tests/testfile-withblanks.txt
new file mode 100644
index 0000000000..0f2ead3796
--- /dev/null
+++ b/legacy/elm_code/src/tests/testfile-withblanks.txt
@@ -0,0 +1,8 @@
+line 1
+line2
+
+another link
+
+
+double blank
+8
diff --git a/legacy/elm_code/src/tests/testfile.txt b/legacy/elm_code/src/tests/testfile.txt
new file mode 100644
index 0000000000..8fd6a8b140
--- /dev/null
+++ b/legacy/elm_code/src/tests/testfile.txt
@@ -0,0 +1,4 @@
+line 1
+line2
+a third
+another line
diff --git a/legacy/elm_code/src/tests/widget/elm_code_test_widget.c b/legacy/elm_code/src/tests/widget/elm_code_test_widget.c
new file mode 100644
index 0000000000..7f2303ff4d
--- /dev/null
+++ b/legacy/elm_code/src/tests/widget/elm_code_test_widget.c
@@ -0,0 +1,92 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+#include "widget/elm_code_widget.c"
+
+static void _assert_cell_type(Evas_Textgrid_Cell cell, Elm_Code_Token_Type type, int id)
+{
+ ck_assert_msg(cell.fg == type, "Wrong type for cell %d", id);
+}
+
+START_TEST (elm_code_widget_token_render_simple_test)
+{
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code *code;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ int length;
+
+ Evas_Textgrid_Cell cells[25];
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ file = code->file;
+ elm_code_file_line_append(file, "some \"test content\", 45", 23, NULL);
+ line = elm_code_file_line_get(file, 1);
+ length = line->length;
+
+ elm_code_line_token_add(line, 6, 17, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+ elm_code_line_token_add(line, 21, 22, 1, ELM_CODE_TOKEN_TYPE_COMMENT);
+
+ _elm_code_widget_fill_line_tokens(widget, cells, length+1, line);
+ _assert_cell_type(cells[1], ELM_CODE_TOKEN_TYPE_DEFAULT, 1);
+ _assert_cell_type(cells[4], ELM_CODE_TOKEN_TYPE_DEFAULT, 4);
+ _assert_cell_type(cells[5], ELM_CODE_TOKEN_TYPE_DEFAULT, 5);
+ _assert_cell_type(cells[16], ELM_CODE_TOKEN_TYPE_COMMENT, 16);
+ _assert_cell_type(cells[20], ELM_CODE_TOKEN_TYPE_DEFAULT, 20);
+ _assert_cell_type(cells[22], ELM_CODE_TOKEN_TYPE_COMMENT, 22);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_widget_construct)
+{
+ Elm_Code *code;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ ck_assert(!!widget);
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_widget_construct_nocode)
+{
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = eo_add(ELM_CODE_WIDGET_CLASS, win);
+ ck_assert(!widget);
+
+ elm_shutdown();
+}
+END_TEST
+
+void elm_code_test_widget(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_widget_token_render_simple_test);
+ tcase_add_test(tc, elm_code_widget_construct);
+ tcase_add_test(tc, elm_code_widget_construct_nocode);
+}
+
diff --git a/legacy/elm_code/src/tests/widget/elm_code_test_widget_selection.c b/legacy/elm_code/src/tests/widget/elm_code_test_widget_selection.c
new file mode 100644
index 0000000000..9769f6bcd0
--- /dev/null
+++ b/legacy/elm_code/src/tests/widget/elm_code_test_widget_selection.c
@@ -0,0 +1,639 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+#include "elm_code_widget_private.h"
+
+#include "widget/elm_code_widget_selection.h"
+
+START_TEST (elm_code_test_widget_selection_set)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 1, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+ elm_code_widget_selection_clear(widget);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_normalized_get)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Elm_Code_Widget_Selection_Data *selection;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 1, 2);
+ selection = elm_code_widget_selection_normalized_get(widget);
+
+ ck_assert_int_eq(selection->start_col, 2);
+ ck_assert_int_eq(selection->end_col, 3);
+ elm_code_widget_selection_clear(widget);
+ free(selection);
+
+ elm_code_file_line_append(file, "another", 7, NULL);
+ elm_code_widget_selection_start(widget, 2, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+ selection = elm_code_widget_selection_normalized_get(widget);
+
+ ck_assert_int_eq(selection->start_line, 1);
+ ck_assert_int_eq(selection->start_col, 3);
+ ck_assert_int_eq(selection->end_line, 2);
+ ck_assert_int_eq(selection->end_col, 2);
+ elm_code_widget_selection_clear(widget);
+ free(selection);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_text_get)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ ck_assert_str_eq("", elm_code_widget_selection_text_get(widget));
+
+ elm_code_widget_selection_start(widget, 1, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("es", selection);
+ free(selection);
+
+ elm_code_widget_selection_clear(widget);
+ ck_assert_str_eq("", elm_code_widget_selection_text_get(widget));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_text_get)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ ck_assert_str_eq("", elm_code_widget_selection_text_get(widget));
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 1, 2);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("es", selection);
+ free(selection);
+
+ elm_code_widget_selection_clear(widget);
+ ck_assert_str_eq("", elm_code_widget_selection_text_get(widget));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_text_get_twoline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 2, 2);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("st\nte", selection);
+ free(selection);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_text_get_twoline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 2, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("st\nte", selection);
+ free(selection);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_text_get_multiline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 3, 2);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("st\ntest\nte", selection);
+ free(selection);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_text_get_multiline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_start(widget, 3, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("st\ntest\nte", selection);
+ free(selection);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_delete)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+ unsigned int length;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, &length);
+ ck_assert_int_eq(4, length);
+ ck_assert_strn_eq("text", text, length);
+
+ elm_code_widget_selection_start(widget, 1, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, &length);
+ ck_assert_int_eq(2, length);
+ ck_assert_strn_eq("tt", text, length);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_delete)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+ unsigned int length;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, &length);
+ ck_assert_int_eq(4, length);
+ ck_assert_strn_eq("text", text, length);
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 1, 2);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, &length);
+ ck_assert_int_eq(2, length);
+ ck_assert_strn_eq("tt", text, length);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_delete_twoline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+ elm_code_file_line_append(file, "TEXT", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("text", text);
+ ck_assert_int_eq(2, elm_code_file_lines_get(file));
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 2, 2);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("teXT", text);
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_delete_twoline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+ elm_code_file_line_append(file, "TEXT", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("text", text);
+ ck_assert_int_eq(2, elm_code_file_lines_get(file));
+
+ elm_code_widget_selection_start(widget, 2, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("teXT", text);
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_delete_multiline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+ elm_code_file_line_append(file, "remove", 6, NULL);
+ elm_code_file_line_append(file, "TEXT", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("text", text);
+ ck_assert_int_eq(3, elm_code_file_lines_get(file));
+
+ elm_code_widget_selection_start(widget, 1, 3);
+ elm_code_widget_selection_end(widget, 3, 2);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("teXT", text);
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_reverse_delete_multiline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ const char *text;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "text", 4, NULL);
+ elm_code_file_line_append(file, "remove", 6, NULL);
+ elm_code_file_line_append(file, "TEXT", 4, NULL);
+
+ win = elm_win_add(NULL, "code", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("text", text);
+ ck_assert_int_eq(3, elm_code_file_lines_get(file));
+
+ elm_code_widget_selection_start(widget, 3, 2);
+ elm_code_widget_selection_end(widget, 1, 3);
+ elm_code_widget_selection_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ text = elm_code_line_text_get(line, NULL);
+ ck_assert_str_eq("teXT", text);
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_select_line)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "line selection", 14, NULL);
+ elm_code_file_line_append(file, "line2", 5, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_select_line(widget, 1);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("line selection", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_line(widget, 2);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("line2", selection);
+ free(selection);
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_select_word)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "word selection test", 19, NULL);
+ elm_code_file_line_append(file, "more stuff\tto test", 18, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_select_word(widget, 1, 3);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("word", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 16);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("test", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 2, 9);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("stuff", selection);
+ free(selection);
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_select_word_punctuation)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "comma, stop. question? mark!", 38, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_select_word(widget, 1, 3);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("comma", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 10);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("stop", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 20);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("question", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 25);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("mark", selection);
+ free(selection);
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_selection_select_word_symbols)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ char *selection;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "colon: [array] (brackets) {braces}", 38, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_selection_select_word(widget, 1, 3);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("colon", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 10);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("array", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 20);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("brackets", selection);
+ free(selection);
+
+ elm_code_widget_selection_select_word(widget, 1, 30);
+ selection = elm_code_widget_selection_text_get(widget);
+ ck_assert_str_eq("braces", selection);
+ free(selection);
+}
+END_TEST
+
+void elm_code_test_widget_selection(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_test_widget_selection_set);
+ tcase_add_test(tc, elm_code_test_widget_selection_normalized_get);
+ tcase_add_test(tc, elm_code_test_widget_selection_text_get);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_text_get);
+ tcase_add_test(tc, elm_code_test_widget_selection_text_get_twoline);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_text_get_twoline);
+ tcase_add_test(tc, elm_code_test_widget_selection_text_get_multiline);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_text_get_multiline);
+ tcase_add_test(tc, elm_code_test_widget_selection_delete);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_delete);
+ tcase_add_test(tc, elm_code_test_widget_selection_delete_twoline);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_delete_twoline);
+ tcase_add_test(tc, elm_code_test_widget_selection_delete_multiline);
+ tcase_add_test(tc, elm_code_test_widget_selection_reverse_delete_multiline);
+ tcase_add_test(tc, elm_code_test_widget_selection_select_line);
+ tcase_add_test(tc, elm_code_test_widget_selection_select_word);
+ tcase_add_test(tc, elm_code_test_widget_selection_select_word_punctuation);
+ tcase_add_test(tc, elm_code_test_widget_selection_select_word_symbols);
+}
+
diff --git a/legacy/elm_code/src/tests/widget/elm_code_test_widget_text.c b/legacy/elm_code/src/tests/widget/elm_code_test_widget_text.c
new file mode 100644
index 0000000000..86d4a33e51
--- /dev/null
+++ b/legacy/elm_code/src/tests/widget/elm_code_test_widget_text.c
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+START_TEST (elm_code_test_widget_text_tab_width)
+{
+ Elm_Code *code;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ elm_code_widget_tabstop_set(widget, 8);
+
+ ck_assert_int_eq(8, elm_code_widget_text_tabwidth_at_column_get(widget, 1));
+ ck_assert_int_eq(8, elm_code_widget_text_tabwidth_at_column_get(widget, 9));
+ ck_assert_int_eq(6, elm_code_widget_text_tabwidth_at_column_get(widget, 3));
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_text_position)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "a\tb", 4, NULL);
+ line = elm_code_file_line_get(file, 1);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+ elm_code_widget_tabstop_set(widget, 8);
+
+ ck_assert_int_eq(0, elm_code_widget_line_text_position_for_column_get(widget, line, 1));
+ ck_assert_int_eq(1, elm_code_widget_line_text_position_for_column_get(widget, line, 2));
+
+ ck_assert_int_eq(2, elm_code_widget_line_text_position_for_column_get(widget, line, 9));
+ ck_assert_int_eq(1, elm_code_widget_line_text_position_for_column_get(widget, line, 7));
+}
+END_TEST
+
+void elm_code_test_widget_text(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_test_widget_text_tab_width);
+ tcase_add_test(tc, elm_code_test_widget_text_position);
+}
+
diff --git a/legacy/elm_code/src/tests/widget/elm_code_test_widget_undo.c b/legacy/elm_code/src/tests/widget/elm_code_test_widget_undo.c
new file mode 100644
index 0000000000..0de2cb91a5
--- /dev/null
+++ b/legacy/elm_code/src/tests/widget/elm_code_test_widget_undo.c
@@ -0,0 +1,164 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "elm_code_suite.h"
+
+#include "elm_code_widget_private.h"
+
+START_TEST (elm_code_test_widget_undo_text_insert)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ unsigned int length;
+ const char *content;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ _elm_code_widget_text_at_cursor_insert(widget, "a", 1);
+ line = elm_code_file_line_get(file, 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("atest", content, length);
+
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+
+ elm_code_widget_cursor_position_set(widget, 3, 1);
+ _elm_code_widget_text_at_cursor_insert(widget, "r", 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("terst", content, length);
+
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+
+ elm_code_widget_cursor_position_set(widget, 4, 1);
+ _elm_code_widget_text_at_cursor_insert(widget, "\t", 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("tes\tt", content, length);
+
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_undo_newline)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ unsigned int length;
+ const char *content;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_cursor_position_set(widget, 5, 1);
+ _elm_code_widget_newline(widget);
+ ck_assert_int_eq(2, elm_code_file_lines_get(file));
+ line = elm_code_file_line_get(file, 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, 1);
+
+ elm_code_widget_undo(widget);
+
+ ck_assert_int_eq(1, elm_code_file_lines_get(file));
+ line = elm_code_file_line_get(file, 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, 4);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+START_TEST (elm_code_test_widget_undo_delete)
+{
+ Elm_Code *code;
+ Elm_Code_File *file;
+ Elm_Code_Line *line;
+ Elm_Code_Widget *widget;
+ Evas_Object *win;
+ unsigned int length;
+ const char *content;
+
+ elm_init(1, NULL);
+ code = elm_code_create();
+ file = elm_code_file_new(code);
+ elm_code_file_line_append(file, "test", 4, NULL);
+
+ win = elm_win_add(NULL, "entry", ELM_WIN_BASIC);
+ widget = elm_code_widget_add(win, code);
+
+ elm_code_widget_cursor_position_set(widget, 4, 1);
+ _elm_code_widget_backspace(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("tet", content, length);
+
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+
+ elm_code_widget_cursor_position_set(widget, 2, 1);
+ _elm_code_widget_delete(widget);
+
+ line = elm_code_file_line_get(file, 1);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("tst", content, length);
+
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+
+ elm_code_widget_cursor_position_set(widget, 4, 1);
+ _elm_code_widget_text_at_cursor_insert(widget, "\t", 1);
+ _elm_code_widget_backspace(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("tes\tt", content, length);
+
+ elm_code_widget_cursor_position_set(widget, 4, 1);
+ _elm_code_widget_delete(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("test", content, length);
+ elm_code_widget_undo(widget);
+ content = elm_code_line_text_get(line, &length);
+ ck_assert_strn_eq("tes\tt", content, length);
+
+ elm_code_free(code);
+ elm_shutdown();
+}
+END_TEST
+
+void elm_code_test_widget_undo(TCase *tc)
+{
+ tcase_add_test(tc, elm_code_test_widget_undo_text_insert);
+ tcase_add_test(tc, elm_code_test_widget_undo_newline);
+ tcase_add_test(tc, elm_code_test_widget_undo_delete);
+}
+