#include "config.h"
#include <unistd.h>
#include <Elementary.h>

#include "Ecrire.h"
#include "file_utils.h"
#include "cfg.h"
#include "ui/ui.h"

static Eina_List *instances = NULL;

static Eina_Unicode plain_utf8 = EINA_TRUE;

/* specific log domain to help debug only ecrire */
int _ecrire_log_dom = -1;

static void
_editor_del(void *data)
{
   Eina_List *l, *l_next;
   Ecrire_Editor *editor, *inst = data;

   EINA_LIST_FOREACH_SAFE(instances, l, l_next, editor)
     {
        if (editor == inst)
          {
             instances = eina_list_remove_list(instances, l);
             break;
          }
     }
   Elm_Entry_Change_Info *inf;
   EINA_LIST_FREE(inst->undo_stack, inf)
     {
        if (!inf) continue;
        if (inf->insert)
          eina_stringshare_del(inf->change.insert.content);
        else
          eina_stringshare_del(inf->change.del.content);
        free(inf);
     }
   if (inst->search_win) evas_object_del(inst->search_win);
   evas_object_del(inst->win);
   eina_stringshare_del(inst->filename);
   free(inst);
}

static void
_editor_reset(Ecrire_Editor *inst)
{
   Elm_Entry_Change_Info *inf;

   elm_object_text_set(inst->entry, "");
   ecrire_editor_font_set(inst, inst->font.name, inst->font.size);

   EINA_LIST_FREE(inst->undo_stack, inf)
     {
        if (!inf) continue;
        if (inf->insert)
          eina_stringshare_del(inf->change.insert.content);
        else
          eina_stringshare_del(inf->change.del.content);
        free(inf);
     }
   inst->undo_stack = inst->undo_stack_ptr =
      eina_list_append(inst->undo_stack, NULL);
   inst->last_saved_stack_ptr = inst->undo_stack_ptr;
   inst->undo_stack_can_merge = EINA_FALSE;

   elm_object_item_disabled_set(inst->menu.undo, EINA_TRUE);
   elm_object_item_disabled_set(inst->menu.redo, EINA_TRUE);
}

static void
_alert_if_need_saving(void (*cb_done)(void *data), Ecrire_Editor *inst)
{
   if (!elm_object_item_disabled_get(inst->menu.save))
     ui_alert_need_saving(inst->entry, cb_done, inst);
   else
     cb_done(inst);
}

static void
_cb_sel_start(void *data, Evas_Object *obj EINA_UNUSED,
              void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_object_item_disabled_set(inst->menu.copy, EINA_FALSE);
   elm_object_item_disabled_set(inst->menu.cut, EINA_FALSE);
   elm_object_item_disabled_set(inst->toolbar.copy, EINA_FALSE);
   elm_object_item_disabled_set(inst->toolbar.cut, EINA_FALSE);
}

static void
_cb_sel_clear(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
              void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_object_item_disabled_set(inst->menu.copy, EINA_TRUE);
   elm_object_item_disabled_set(inst->menu.cut, EINA_TRUE);
   elm_object_item_disabled_set(inst->toolbar.copy, EINA_TRUE);
   elm_object_item_disabled_set(inst->toolbar.cut, EINA_TRUE);
}

static void
_cb_text_set_done(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
                  void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_progressbar_pulse(inst->busy, EINA_FALSE);
}

static void
_update_cur_file(const char *file, Ecrire_Editor *inst)
{
   char buf[1024];
   const char *saving = (!elm_object_item_disabled_get(inst->menu.save)) ? "*" : "";

   eina_stringshare_replace(&inst->filename, file);

   if (inst->filename)
     snprintf(buf, sizeof(buf), _("%s%s - %s"), saving, inst->filename,
              PACKAGE_NAME);
   else
     snprintf(buf, sizeof(buf), _("%sUntitled %d - %s"), saving,
              inst->unsaved, PACKAGE_NAME);

   elm_win_title_set(inst->win, buf);
}

static void
_cb_cur_changed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   char buf[64];
   int line;
   int col;
   const Evas_Object *tb = elm_entry_textblock_get(obj);
   const Evas_Textblock_Cursor *mcur = evas_object_textblock_cursor_get(tb);
   Evas_Textblock_Cursor *cur = evas_object_textblock_cursor_new(tb);
   line =
      evas_textblock_cursor_line_geometry_get(mcur, NULL, NULL, NULL, NULL) + 1;
   evas_textblock_cursor_copy(mcur, cur);
   evas_textblock_cursor_line_char_first(cur);
   col = evas_textblock_cursor_pos_get(mcur) -
      evas_textblock_cursor_pos_get(cur) + 1;
   evas_textblock_cursor_free(cur);

   snprintf(buf, sizeof(buf), _("Ln %d, Col %d"), line, col);
   elm_object_text_set(data, buf);
}

static void
_cb_cur_changed_manual(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;
   inst->undo_stack_can_merge = EINA_FALSE;
}

static void
_update_undo_redo_items(Ecrire_Editor *inst)
{
   Eina_Bool disabled;

   disabled = !eina_list_next(inst->undo_stack_ptr);

   elm_object_item_disabled_set(inst->menu.undo, disabled);
   elm_object_item_disabled_set(inst->toolbar.undo, disabled);

   disabled = !eina_list_prev(inst->undo_stack_ptr);

   elm_object_item_disabled_set(inst->menu.redo, disabled);
   elm_object_item_disabled_set(inst->toolbar.redo, disabled);

   if (inst->undo_stack_ptr == inst->last_saved_stack_ptr)
     {
        elm_object_item_disabled_set(inst->menu.save, EINA_TRUE);
        elm_object_item_disabled_set(inst->toolbar.save, EINA_TRUE);
        _update_cur_file(inst->filename, inst);
     }
   else if (elm_object_item_disabled_get(inst->menu.save))
     {
        elm_object_item_disabled_set(inst->menu.save, EINA_FALSE);
        elm_object_item_disabled_set(inst->toolbar.save, EINA_FALSE);
        _update_cur_file(inst->filename, inst);
     }
}

static void
_undo_stack_add(Ecrire_Editor *inst, Elm_Entry_Change_Info *_info)
{
   Elm_Entry_Change_Info *inf;

   inst->undo_stack = eina_list_split_list(inst->undo_stack, eina_list_prev(inst->undo_stack_ptr),
         &inst->undo_stack_ptr);

   EINA_LIST_FREE(inst->undo_stack, inf)
     {
        if (inf->insert)
          {
             eina_stringshare_del(inf->change.insert.content);
          }
        else
          {
             eina_stringshare_del(inf->change.del.content);
          }
        free(inf);
     }

   /* FIXME: Do a smarter merge, actually merge the structures, not just
    * mark them to be merged. */
#if 0
   inf = (Elm_Entry_Change_Info *) eina_list_data_get(undo_stack_ptr);
   /* If true, we should merge with the current top */
   if (undo_stack_can_merge && (_info->insert == inf->insert))
     {
     }
   else
#endif
     {
        Elm_Entry_Change_Info *head_inf = eina_list_data_get(inst->undo_stack_ptr);

        inf = calloc(1, sizeof(*inf));
        memcpy(inf, _info, sizeof(*inf));
        if (inf->insert)
          {
             eina_stringshare_ref(inf->change.insert.content);
          }
        else
          {
             eina_stringshare_ref(inf->change.del.content);
          }

        if (inst->undo_stack_can_merge && (inf->insert == head_inf->insert))
           inf->merge = EINA_TRUE;

        inst->undo_stack_ptr = eina_list_prepend(inst->undo_stack_ptr, inf);
     }

   inst->undo_stack = inst->undo_stack_ptr;

   inst->undo_stack_can_merge = EINA_TRUE;

   _update_undo_redo_items(inst);
}

static void
_undo_redo_do(Ecrire_Editor *inst, Elm_Entry_Change_Info *inf, Eina_Bool undo)
{
   DBG("%s: %s", (undo) ? "Undo" : "Redo", inf->change.insert.content);

   if ((inf->insert && undo) || (!inf->insert && !undo))
     {
        Evas_Textblock_Cursor *mcur, *end;
        const Evas_Object *tb = elm_entry_textblock_get(inst->entry);

        mcur = (Evas_Textblock_Cursor *) evas_object_textblock_cursor_get(tb);
        end = evas_object_textblock_cursor_new(tb);

        if (inf->insert)
          {
             elm_entry_cursor_pos_set(inst->entry, inf->change.insert.pos);
             evas_textblock_cursor_pos_set(end, inf->change.insert.pos +
                                           inf->change.insert.plain_length);
          }
        else
          {
             elm_entry_cursor_pos_set(inst->entry, inf->change.del.start);
             evas_textblock_cursor_pos_set(end, inf->change.del.end);
          }

        evas_textblock_cursor_range_delete(mcur, end);
        evas_textblock_cursor_free(end);
        elm_entry_calc_force(inst->entry);
     }
   else
     {
        if (inf->insert)
          {
             elm_entry_cursor_pos_set(inst->entry, inf->change.insert.pos);
             elm_entry_entry_insert(inst->entry, inf->change.insert.content);
          }
        else
          {
             size_t start;
             start = (inf->change.del.start < inf->change.del.end) ?
                inf->change.del.start : inf->change.del.end;

             elm_entry_cursor_pos_set(inst->entry, start);
             elm_entry_entry_insert(inst->entry, inf->change.insert.content);
             elm_entry_cursor_pos_set(inst->entry, inf->change.del.end);
          }
     }

   /* No matter what, once we did an undo/redo we don't want to merge,
    * even if we got backt to the top of the stack. */
   inst->undo_stack_can_merge = EINA_FALSE;
}

static void
_cb_undo(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   /* In undo we care about the current item */
   Ecrire_Editor *inst = data;
   Elm_Entry_Change_Info *inf = NULL;

   if (!eina_list_next(inst->undo_stack_ptr))
      return;

   do
     {
        inf = eina_list_data_get(inst->undo_stack_ptr);

        _undo_redo_do(inst, inf, EINA_TRUE);

        if (eina_list_next(inst->undo_stack_ptr))
          {
             inst->undo_stack_ptr = eina_list_next(inst->undo_stack_ptr);
          }
        else
          {
             break;
          }
     }
   while (inf && inf->merge);

   _update_undo_redo_items(inst);
}

static void
_cb_redo(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;
   Elm_Entry_Change_Info *inf = NULL;

   if (!eina_list_prev(inst->undo_stack_ptr))
      return;

   do
     {
        if (eina_list_prev(inst->undo_stack_ptr))
          {
             inst->undo_stack_ptr = eina_list_prev(inst->undo_stack_ptr);
             /* In redo we care about the NEW item */
             inf = eina_list_data_get(inst->undo_stack_ptr);
             _undo_redo_do(inst, inf, EINA_FALSE);
          }
        else
          {
             break;
          }

        /* Update inf to next for the condition. */
        if (eina_list_prev(inst->undo_stack_ptr))
          {
             inf = eina_list_data_get(eina_list_prev(inst->undo_stack_ptr));
          }
     }
   while (inf && inf->merge);

   _update_undo_redo_items(inst);
}

static void
_cb_ent_changed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Ecrire_Editor *inst = data;

   elm_object_item_disabled_set(inst->menu.save, EINA_FALSE);
   elm_object_item_disabled_set(inst->toolbar.save, EINA_FALSE);

   _update_cur_file(inst->filename, inst);

   /* Undo/redo */
   _undo_stack_add(inst, event_info);
}

static void
_editor_file_open(Ecrire_Editor *inst, const char *file)
{
   Eina_Bool ok = 1;

   if (!file)
     _editor_reset(inst);
   else
     {
        char *buf;

        if (plain_utf8)
          buf = file_plain_load(file);
        else
          buf = file_load(file);

        if (!buf)
          ok = 0;
        else
          {
             _editor_reset(inst);
             elm_progressbar_pulse(inst->busy, EINA_TRUE);
             elm_entry_entry_append(inst->entry, buf);
             elm_object_item_disabled_set(inst->menu.save, EINA_TRUE);
             elm_object_item_disabled_set(inst->toolbar.save, EINA_TRUE);
             free(buf);
          }
     }

   if (ok)
     _update_cur_file(file, inst);
}

static void
_cb_fs_ecrire_editor_save(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
                           void *event_info)
{
   Ecrire_Editor *inst = data;
   const char *filename = event_info;

   if (filename)
     ecrire_editor_save(inst, filename);
}

void
ecrire_editor_save(Ecrire_Editor *inst, const char *file)
{
   Eina_Bool ok;

   if (plain_utf8)
     ok = file_plain_save(file, elm_object_text_get(inst->entry));
   else
     ok = file_save(file, elm_object_text_get(inst->entry));

   if (!ok)
     {
        ui_alert_warning_popup(inst->win, eina_slstr_printf("Unable to save to %s <br>(%s)", file, strerror(errno)));
        ui_file_open_save_dialog_open(inst->win, EINA_TRUE, _cb_fs_ecrire_editor_save, inst);
     }
   else
     {
        elm_object_item_disabled_set(inst->menu.save, EINA_TRUE);
        elm_object_item_disabled_set(inst->toolbar.save, EINA_TRUE);
        inst->last_saved_stack_ptr = inst->undo_stack_ptr;
        _update_cur_file(file, inst);
     }
}

static void
_cb_goto_line(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ui_goto_dialog_open(inst->win, inst);
}

static void
_cb_fs_open_done(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
                 void *event_info)
{
   const char *selected = event_info;

   if (!selected) return;

   ecrire_editor_add(selected, _ent_cfg->font.name, _ent_cfg->font.size);
}

static void
_cb_open(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ui_file_open_save_dialog_open(inst->win, EINA_FALSE, _cb_fs_open_done, inst);
}

void
ecrire_editor_try_save(Ecrire_Editor *inst, void *callback_func)
{
   if (inst->filename)
     ecrire_editor_save(inst, inst->filename);
   else
     ui_file_open_save_dialog_open(inst->win, EINA_TRUE, callback_func, inst);
}

static void
_cb_save(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ecrire_editor_try_save(inst, _cb_fs_ecrire_editor_save);
}

static void
_cb_save_as(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ui_file_open_save_dialog_open(inst->win, EINA_TRUE, _cb_fs_ecrire_editor_save, inst);
}

static void
_cb_new(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   ecrire_editor_add(NULL, _ent_cfg->font.name, _ent_cfg->font.size);
}

static void
_cb_close(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   _alert_if_need_saving(_editor_del, inst);
}

static void
_cb_cut(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_entry_selection_cut(inst->entry);
}

static void
_cb_copy(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_entry_selection_copy(inst->entry);
}

static void
_cb_paste(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_entry_selection_paste(inst->entry);
}

static void
_cb_find(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ui_find_dialog_open(inst->win, inst);
}

static void
_cb_font_settings(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   ui_settings_open(inst->win, inst);
}

static void
_cb_win_del(void *data, Evas_Object *obj, void *event_info)
{
   Ecrire_Editor *inst = data;

   _alert_if_need_saving(_editor_del, inst);
}

static void
_cb_win_focused(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Ecrire_Editor *inst = data;

   elm_object_focus_set(inst->entry, 1);
}

static void
_cb_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event)
{
   Ecrire_Editor *inst = data;
   Eina_Bool ctrl, alt, shift;
   Evas_Event_Key_Down *ev = event;

   ctrl = evas_key_modifier_is_set(ev->modifiers, "Control");
   alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
   shift = evas_key_modifier_is_set(ev->modifiers, "Shift");

   if ((ctrl) && (!alt) && (!shift))
     {
        if      (!strcmp(ev->keyname, "a")) elm_entry_select_all(inst->entry);
        else if (!strcmp(ev->keyname, "f")) _cb_find(data, obj, event);
        else if (!strcmp(ev->keyname, "g")) _cb_goto_line(data, obj, event);
        else if (!strcmp(ev->keyname, "n")) _cb_new(data, obj, event);
        else if (!strcmp(ev->keyname, "s")) _cb_save(data, obj, event);
     }
}

void
ecrire_editor_font_set(Ecrire_Editor *inst, const char *font, int font_size)
{
   Eina_Strbuf *sbuf;
   const Evas_Object *tb;

   tb = elm_entry_textblock_get(inst->entry);
   sbuf = eina_strbuf_new();

   if (font)
     eina_strbuf_append_printf(sbuf, "font=\\'%s\\'", font);

   if (font_size > 0)
     eina_strbuf_append_printf(sbuf, " font_size=\\'%d\\'", font_size);

   if (!eina_strbuf_length_get(sbuf))
     evas_object_textblock_style_user_pop((Evas_Object *) tb);
   else
     {
        Evas_Textblock_Style *ts = evas_textblock_style_new();

        eina_strbuf_prepend(sbuf, "DEFAULT='");
        eina_strbuf_append(sbuf, "'");
        evas_textblock_style_set(ts, eina_strbuf_string_get(sbuf));

        evas_object_textblock_style_user_push((Evas_Object *) tb, ts);

        evas_textblock_style_free(ts);
     }

   elm_entry_calc_force(inst->entry);
   eina_strbuf_free(sbuf);
}

void
ecrire_editor_font_save(Ecrire_Editor *inst, const char *font, int size)
{
   ecrire_editor_font_set(inst, font, size);

   eina_stringshare_replace(&_ent_cfg->font.name, font);
   _ent_cfg->font.size = size;
   /* Save the font for future runs */
   ecrire_cfg_save();
}


static Evas_Object *
_ecrire_toolbar_add(Ecrire_Editor *inst)
{
   Evas_Object *tb;
   Elm_Object_Item *it;

   tb = elm_toolbar_add(inst->win);
   elm_toolbar_align_set(tb, 0);
   elm_toolbar_horizontal_set(tb, 1);
   elm_toolbar_icon_size_set(tb, 24);
   elm_toolbar_select_mode_set(tb, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
   evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, 0);
   evas_object_show(tb);

   elm_toolbar_item_append(tb, "document-new", _("New"), _cb_new, inst);
   elm_toolbar_item_append(tb, "document-open", _("Open"), _cb_open, inst);
   inst->toolbar.save = elm_toolbar_item_append(tb, "document-save", _("Save"), _cb_save, inst);
   elm_toolbar_item_append(tb, "document-save-as", _("Save As"), _cb_save_as, inst);
   it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
   elm_toolbar_item_separator_set(it, 1);
   inst->toolbar.cut = elm_toolbar_item_append(tb, "edit-cut", _("Cut"), _cb_cut, inst);
   inst->toolbar.copy = elm_toolbar_item_append(tb, "edit-copy", _("Copy"), _cb_copy, inst);
   inst->toolbar.paste = elm_toolbar_item_append(tb, "edit-paste", _("Paste"), _cb_paste, inst);
   it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
   elm_toolbar_item_separator_set(it, 1);
   inst->toolbar.undo = elm_toolbar_item_append(tb, "edit-undo", _("Undo"), _cb_undo, inst);
   inst->toolbar.redo = elm_toolbar_item_append(tb, "edit-redo", _("Redo"), _cb_redo, inst);
   it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
   elm_toolbar_item_separator_set(it, 1);
   elm_toolbar_item_append(tb, "edit-find-replace", _("Find"), _cb_find, inst);
   elm_toolbar_item_append(tb, "go-jump", _("Go to..."), _cb_goto_line, inst);
   it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
   elm_toolbar_item_separator_set(it, 1);
   elm_toolbar_item_append(tb, "preferences-system", _("Settings"), _cb_font_settings, inst);

   elm_object_item_disabled_set(inst->toolbar.copy, EINA_TRUE);
   elm_object_item_disabled_set(inst->toolbar.cut, EINA_TRUE);
   elm_object_item_disabled_set(inst->toolbar.save, EINA_TRUE);

   return tb;
}

static void
_ecrire_menu_add(Ecrire_Editor *inst)
{
   Evas_Object *menu;
   Elm_Object_Item  *it;

   menu = elm_win_main_menu_get(inst->win);
   it = elm_menu_item_add(menu, NULL, NULL, _("File"), NULL, NULL);
   elm_menu_item_add(menu, it, "document-new", _("New"), _cb_new, inst);
   elm_menu_item_add(menu, it, "document-open", _("Open"), _cb_open, inst);
   inst->menu.save =
   elm_menu_item_add(menu, it, "document-save", _("Save"), _cb_save, inst);
   elm_menu_item_add(menu, it, "document-save-as", _("Save As"), _cb_save_as, inst);
   elm_menu_item_separator_add(menu, it);
   elm_menu_item_add(menu, it, "application-exit", _("Close"), _cb_close, inst);

   it = elm_menu_item_add(menu, NULL, NULL, _("Edit"), NULL, NULL);
   inst->menu.cut =
   elm_menu_item_add(menu, it, "edit-cut", _("Cut"), _cb_cut, inst);
   inst->menu.copy =
   elm_menu_item_add(menu, it, "edit-copy", _("Copy"), _cb_copy, inst);
   inst->menu.paste =
   elm_menu_item_add(menu, it, "edit-paste", _("Paste"), _cb_paste, inst);
   elm_menu_item_separator_add(menu, it);
   inst->menu.undo =
   elm_menu_item_add(menu, it, "edit-undo", _("Undo"), _cb_undo, inst);
   inst->menu.redo =
   elm_menu_item_add(menu, it, "edit-redo", _("Redo"), _cb_redo, inst);
   elm_menu_item_separator_add(menu, it);
   elm_menu_item_add(menu, it, "edit-find-replace", _("Find"), _cb_find, inst);
   elm_menu_item_add(menu, it, "go-jump", _("Go to line..."), _cb_goto_line, inst);
   elm_menu_item_separator_add(menu, it);
   elm_menu_item_add(menu, it, "preferences-system", _("Settings"), _cb_font_settings, inst);

   elm_object_item_disabled_set(inst->menu.copy, EINA_TRUE);
   elm_object_item_disabled_set(inst->menu.cut, EINA_TRUE);
   elm_object_item_disabled_set(inst->menu.save, EINA_TRUE);
}

void
ecrire_editor_add(const char *filename, const char *font_name, int font_size)
{
   Evas_Object *win, *pad, *bx, *entry, *cur_info, *tab, *busy;
   Evas_Object *toolbar;
   Evas_Coord w = 600, h = 600;

   Ecrire_Editor *inst = calloc(1, sizeof(Ecrire_Editor));
   EINA_SAFETY_ON_NULL_RETURN(inst);

   instances = eina_list_append(instances, inst);

   inst->unsaved = 1;
   inst->filename = NULL;
   inst->last_saved_stack_ptr = NULL;
   inst->undo_stack_can_merge = EINA_FALSE;
   inst->font.name = _ent_cfg->font.name;
   inst->font.size = _ent_cfg->font.size;
   inst->filename = filename;

   DBG("Opening filename: '%s'", inst->filename);

   inst->win = win = elm_win_util_standard_add("ecrire", "Ecrire");
   elm_win_autodel_set(inst->win, EINA_FALSE);

   bx = elm_box_add(win);
   elm_win_resize_object_add(inst->win, bx);
   evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_show(bx);

   toolbar = _ecrire_toolbar_add(inst);
   elm_box_pack_end(bx, toolbar);

   tab = elm_table_add(win);
   evas_object_size_hint_align_set(tab, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(tab, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_box_pack_end(bx, tab);
   evas_object_show(tab);

   inst->entry = entry = elm_entry_add(win);
   elm_entry_scrollable_set(entry, EINA_TRUE);
   elm_entry_line_wrap_set(entry, _ent_cfg->wrap_type);
   elm_entry_cnp_mode_set(entry, ELM_CNP_MODE_PLAINTEXT);
   evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_table_pack(tab, entry, 0, 0, 1, 1);
   evas_object_show(entry);

   pad = elm_frame_add(win);
   elm_object_style_set(pad, "pad_large");
   evas_object_size_hint_align_set(pad, 1.0, 1.0);
   evas_object_size_hint_weight_set(pad, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_table_pack(tab, pad, 0, 0, 1, 1);
   evas_object_pass_events_set(pad, EINA_TRUE);
   evas_object_show(pad);

   inst->busy = busy = elm_progressbar_add(win);
   elm_object_style_set(busy, "hidden_wheel");
   elm_progressbar_pulse_set(busy, EINA_TRUE);
   elm_object_content_set(pad, busy);
   evas_object_pass_events_set(busy, EINA_TRUE);
   evas_object_show(busy);

   pad = elm_frame_add(win);
   elm_object_style_set(pad, "pad_medium");
   evas_object_size_hint_align_set(pad, EVAS_HINT_FILL, 1.0);
   evas_object_size_hint_weight_set(pad, EVAS_HINT_EXPAND, 0.0);
   elm_box_pack_end(bx, pad);
   evas_object_show(pad);

   bx = elm_box_add(win);
   elm_box_horizontal_set(bx, 1);
   evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5);
   evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_object_content_set(pad, bx);
   evas_object_show(bx);

   cur_info = elm_label_add(win);
   _cb_cur_changed(cur_info, entry, NULL);
   evas_object_size_hint_align_set(cur_info, 1.0, 0.5);
   evas_object_size_hint_weight_set(cur_info, EVAS_HINT_EXPAND, 0.0);
   elm_box_pack_end(bx, cur_info);
   evas_object_show(cur_info);

   evas_object_smart_callback_add(entry, "cursor,changed", _cb_cur_changed, cur_info);
   evas_object_smart_callback_add(entry, "cursor,changed,manual", _cb_cur_changed_manual, inst);
   evas_object_smart_callback_add(entry, "changed,user", _cb_ent_changed, inst);
   evas_object_smart_callback_add(entry, "undo,request", _cb_undo, inst);
   evas_object_smart_callback_add(entry, "redo,request", _cb_redo, inst);
   evas_object_smart_callback_add(entry, "selection,start", _cb_sel_start, inst);
   evas_object_smart_callback_add(entry, "selection,cleared", _cb_sel_clear, inst);
   evas_object_event_callback_add(entry, EVAS_CALLBACK_KEY_DOWN, _cb_key_down, inst);
   evas_object_smart_callback_add(entry, "text,set,done", _cb_text_set_done, inst);

   _ecrire_menu_add(inst);

   evas_object_smart_callback_add(win, "delete,request", _cb_win_del, inst);
   evas_object_smart_callback_add(win, "focus,in", _cb_win_focused, inst);

   evas_object_resize(win, w, h);
   elm_win_center(win, 1, 1);
   evas_object_show(win);

   _editor_file_open(inst, inst->filename);
}

EAPI_MAIN int
elm_main(int argc, char **argv)
{
   const char *filename = NULL;
   int c;

   opterr = 0;

   _ecrire_log_dom = eina_log_domain_register("ecrire", ECRIRE_DEFAULT_LOG_COLOR);
   if (_ecrire_log_dom < 0)
     {
        EINA_LOG_ERR("Unable to create a log domain.");
        exit(-1);
     }

   while ((c = getopt (argc, argv, "")) != -1)
     {
        switch (c)
          {
           case '?':
              fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
              if (isprint (optopt))
                {
                   ERR("Unknown option or requires an argument `-%c'.",
                         optopt);
                }
              else
                {
                   ERR("Unknown option character `\\x%x'.", optopt);
                }
              return 1;
              break;
           default:
              abort();
          }
     }

   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
   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);
#ifdef ENABLE_NLS
   elm_app_compile_locale_set(LOCALEDIR);
#endif
   elm_app_info_set(elm_main, "ecrire", "COPYING");

   setlocale(LC_ALL, "");
   bindtextdomain(PACKAGE, LOCALE_DIR);
   textdomain(PACKAGE);

   ecrire_cfg_init(PACKAGE_NAME);
   ecrire_cfg_load();

   if (optind < argc)
     {
        if (!ecore_file_exists(argv[optind]))
          return 1;
        char *path = ecore_file_realpath(argv[optind]);
        if (path)
          {
             if (ecore_file_is_dir(argv[optind])) return 1;

             filename = eina_stringshare_add(path);
          }
     }

   ecrire_editor_add(filename, _ent_cfg->font.name, _ent_cfg->font.size);

   elm_run();

   eina_list_free(instances);

   ecrire_cfg_shutdown();
   eina_log_domain_unregister(_ecrire_log_dom);
   _ecrire_log_dom = -1;

   return 0;
}

ELM_MAIN()

