summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);
+}
+