#include "../lf_mforms.h"

#include "base/string_utilities.h"
#include "../lf_code_editor.h"
#include "tinyxml.h"
#include <memory>
#include <ctype.h>
#include <stdlib.h>
#include "base/wb_memory.h"
#include "base/wb_iterators.h"

#include "Scintilla.h"
#include "SciLexer.h"
#define PLAT_GTK 2
#define GTK
#include "ScintillaWidget.h"

// Marker ID assignments. Markers with higher number overlay lower ones.
#define CE_STATEMENT_MARKER 0
#define CE_ERROR_MARKER 1
#define CE_BREAKPOINT_MARKER 2
#define CE_BREAKPOINT_HIT_MARKER 3
#define CE_CURRENT_LINE_MARKER 3

bool string_to_bool(const std::string& value)
{
  const std::string v = base::tolower(value);
  return v == "1" || v == "true" || v == "yes";
}

int string_to_int(const std::string& v, const int base = 10)
{
  char *end = 0;
  const char* start = v.c_str();
  int ret = strtoll(start, &end, base);
  if (!( !*end && end != start))
    ret = 0;

  return ret;
}

int str_color_to_int(const std::string& v)
{
  int ret = 0;
  size_t len = v.length();
  if (len >= 7)
  {
    size_t pos = v.find("#");
    if (pos != std::string::npos && (pos + 7) <= len)
    {
      ret = string_to_int(v.substr(pos + 1, 6), 16);
    }
  }
  return ret;
}

//==============================================================================


//==============================================================================
//
//==============================================================================
//------------------------------------------------------------------------------


static void notify_signal(GtkWidget *w, gint wParam, gpointer lParam, mforms::gtk::CodeEditorImpl *editor)
{
    SCNotification *event = reinterpret_cast<SCNotification *>(lParam);
    editor->notify(event);
}

mforms::gtk::CodeEditorImpl::CodeEditorImpl(CodeEditor* self)
       : ViewImpl(self)
       ,_sci_gtk_widget(0)
       ,_sci_gtkmm_widget(0)
       ,_sci(0)
{
  _sci_gtk_widget = scintilla_new();
  _sci_gtkmm_widget = Glib::wrap(_sci_gtk_widget);
  _sci = SCINTILLA(_sci_gtk_widget);
  _owner = self;
  gtk_signal_connect(GTK_OBJECT(_sci_gtk_widget), SCINTILLA_NOTIFY, GTK_SIGNAL_FUNC(notify_signal), this);
  _sci_gtkmm_widget->show();
}

//------------------------------------------------------------------------------
mforms::gtk::CodeEditorImpl::~CodeEditorImpl()
{}

//------------------------------------------------------------------------------
Gtk::Widget *mforms::gtk::CodeEditorImpl::get_outer() const
{
  return _sci_gtkmm_widget;
}

//--------------------------------------------------------------------------------------------------
/**
 * This function loads the style name map from an external file which allows us to map
 * style names to their associated ids.
 */
static void load_style_names(const std::string path, mforms::gtk::StyleNameMap *map)
{
  base::FILE_scope_ptr fp = fopen(path.c_str(), "r");

  if (fp)
  {
    int c = EOF;
    std::string name(256, ' ');
    std::string value(256, ' ');

    name = value = "";

    std::string *cur = &name;
    do
    {
      c = getc(fp);
      switch (c)
      {
        case '\n':
        case EOF:
          {
            if (name.length() > 0 && value.length() > 0)
            {
              char *end = 0;
              const char* start = value.c_str();
              const int style_id = strtoll(start, &end, 10);
              if ( !*end && end != start)
              {
                (*map)[name] = style_id;
              }
            }
            name.clear();
            value.clear();
            cur = &name;
            break;
          }
        case '=':
          {
            cur = &value;
            break;
          }
        default:
          {
            if (isalnum(c) || c == '_')
              cur->append(1, c);
          }
      }
    }
    while (c != EOF);
    }
}

//--------------------------------------------------------------------------------------------------
/**
 * Superordinated style map loader which controls the load_style_names function.
 */
static mforms::gtk::StyleNameMap get_style_map(const std::string& language)
{
  mforms::gtk::StyleNameMap ret;

  // These are global styles and always available.
  ret["BRACEBAD"]    = STYLE_BRACEBAD;
  ret["BRACELIGHT"]  = STYLE_BRACELIGHT;
  ret["CALLTIP"]     = STYLE_CALLTIP;
  ret["CONTROLCHAR"] = STYLE_CONTROLCHAR;
  ret["DEFAULT"]     = STYLE_DEFAULT;
  ret["LINENUMBER"]  = STYLE_LINENUMBER;

  load_style_names(mforms::App::get()->get_resource_path("default.txt"), &ret);
  if (language != "Null")
  {
    load_style_names(mforms::App::get()->get_resource_path(language + ".txt"), &ret);
  }

  return ret;
}

//--------------------------------------------------------------------------------------------------
/**
 * Loads the configuration file for the given language and fills the given style map with the loaded
 * style data. Every other setting (tab usage, keywords etc.) will be applied
 * to the editor on the way. Styles need a bit more processing so the caller will take for that.
 */
void mforms::gtk::CodeEditorImpl::load_language_settings(const std::string& language, mforms::gtk::CodeEditorImpl::StyleMap &sm)
{
  const std::string config_file_path = mforms::App::get()->get_resource_path(language + ".xml");
  TiXmlDocument doc(config_file_path);
  if (!doc.LoadFile())
  {
    // TODO: Print error and exit load_language_settings
    return;
  }
  else
  {
    TiXmlHandle hdoc(&doc);
    TiXmlHandle root(0);

    // Check that we loaded correct xml file
    TiXmlElement *pel = hdoc.FirstChildElement().Element();
    if (pel->ValueStr() != "ScintillaNET")
    {
      fprintf(stderr, "Error, root xml node is not correct\n");
    }
    root = TiXmlHandle(pel);

    // Locate child node Language with Name attribute equal to language
    pel = root.FirstChild("Language").Element();
    TiXmlElement *lang_el(0);
    for (; pel; pel=pel->NextSiblingElement())
    {
      if (pel->Attribute("Name") == language)
      {
        lang_el = pel;
        break;
      }
    }

    if (lang_el)
    {
      //parse Indentation
      TiXmlElement *child = lang_el->FirstChildElement("Indentation");
      if (child)
      {
        const char* attr = child->Attribute("TabWidth");
        if (attr)
          send_editor(SCI_SETTABWIDTH, atoi(attr));
        attr = child->Attribute("UseSpaces");
        if (attr)
          send_editor(SCI_SETUSETABS, bool(atoi(attr)));
      }

      // Parse lexer
      child = lang_el->FirstChildElement("Lexer");
      if (child)
      {
        TiXmlElement *lex_child = child->FirstChildElement("Keywords");
        int list_id = 0;
        const char *kw_list = 0;
        for (; lex_child; lex_child = lex_child->NextSiblingElement("Keywords"))
        {
          if (TIXML_SUCCESS != lex_child->QueryIntAttribute("List", &list_id))
            list_id = 0;
          kw_list = lex_child->GetText();
          if (kw_list)
          {
            send_editor(SCI_SETKEYWORDS, list_id, (sptr_t)kw_list);
          }
        }

        lex_child = child->FirstChildElement("Properties");
        for (; lex_child; lex_child = lex_child->NextSiblingElement("Properties"))
        {
          TiXmlElement *prop = lex_child->FirstChildElement("Property");
          const char* name  = 0;
          const char* value = 0;
          for(; prop; prop = prop->NextSiblingElement("Property"))
          {
            name  = prop->Attribute("Name");
            value = prop->Attribute("Value");
            if (name && value && strlen(name) > 0 && strlen(value) > 0)
              send_editor(SCI_SETPROPERTY, (uptr_t)name, (sptr_t)value);
          }
        }
      }

      // Parse styles
      TiXmlElement* style       = 0;
      const char*   name        = 0;
      const char*   value       = 0;
      StyleNameMap  style_names = get_style_map(language);

      child = lang_el->FirstChildElement("Styles");
      for(; child; child = child->NextSiblingElement())
      {
        style = child->FirstChildElement("Style");
        for (; style; style = style->NextSiblingElement("Style"))
        {
          name = style->Attribute("Name");
          int style_id = -1;
          if (name)
          {
            StyleNameMap::iterator name_it = style_names.find(name);
            if (name_it != style_names.end())
              style_id = name_it->second;
          }

          if (style_id >= 0)
          {
            std::string style_entry;
            StyleMap::mapped_type &dict_for_style = sm[style_id];

            // Number and correspondence of items must be maintained!
            static const char *tags[] = {"ForeColor", "BackColor", "Bold", "FontName", "Italic", "Size", "Underline", 0};
            static const int tag_id[] = {SCI_STYLESETFORE, SCI_STYLESETBACK, SCI_STYLESETBOLD, SCI_STYLESETFONT, SCI_STYLESETITALIC, SCI_STYLESETSIZE, SCI_STYLESETUNDERLINE, 0};

            const int* ptag_id = tag_id;
            for (const char** t = tags; *t; ++t, ++ptag_id)
            {
              if (TIXML_SUCCESS == style->QueryStringAttribute(*t, &style_entry))
                dict_for_style[*tag_id] = style_entry;
            }
          }
        } // for (; style; style = style->NextSiblingElement("Style"))
      } // for(; child; child = child->NextSiblingElement())
    } // if (lang_el)
  }
}

//--------------------------------------------------------------------------------------------------

void mforms::gtk::CodeEditorImpl::language_setup(const std::string& language)
{
  // In opposition to the Windows version of this implementation we do not have automatic language
  // setup, so we have to take care for keywords and colors manually here.
  send_editor(SCI_SETLEXERLANGUAGE, 0, (sptr_t)language.c_str());

  StyleMap map;
  load_language_settings("default", map);
  if (language != "Null")
  {
    load_language_settings(language, map);
  }

  // Here we have styles loaded
  int style_id = 0;
  for (base::const_range<StyleMap> style(map); style; ++style)
  {
    style_id = style->first;
    int property = 0;
    std::string value;

    const StyleMap::mapped_type &style_traits = style->second;
    for (base::const_range<StyleMap::mapped_type> trait(style_traits); trait; ++trait)
    {
      property = trait->first;
      value    = trait->second;

      switch (property)
      {
        case SCI_STYLESETFORE:
        case SCI_STYLESETBACK:
          send_editor(property, style_id, str_color_to_int(value));
          break;

        case SCI_STYLESETFONT:
          send_editor(property, style_id, (sptr_t)value.c_str());
          break;

        case SCI_STYLESETBOLD:
        case SCI_STYLESETITALIC:
        case SCI_STYLESETUNDERLINE:
          send_editor(property, style_id, string_to_bool(value));
          break;

        case SCI_STYLESETSIZE:
          send_editor(property, style_id, string_to_int(value));
          break;
      }
    }
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::setup_marker(const int marker_id, const char* icon_path, int background)
{
  const std::string     full_path = mforms::App::get()->get_resource_path(icon_path);
  base::FILE_scope_ptr  fp        = fopen(full_path.c_str(), "r");
  base::AutoCharArray   xpm(0);

  if (fp)
  {
    fseek(fp,0,SEEK_END);
    const long size = ftell(fp);
    fseek(fp,0,SEEK_SET);
    if (size > 0)
    {
      xpm = base::AutoCharArray(new char[size]);
      long items_read = fread(xpm.get(), 1, size, fp);
      // TODO: Check for EINTR and ENOMEM
      if (items_read != size)
        xpm = base::AutoCharArray();
    }
  }

  if (!xpm.get())
  {
    send_editor(SCI_MARKERDEFINE, marker_id, SC_MARK_BACKGROUND);
    send_editor(SCI_MARKERSETBACK, marker_id, background);
    send_editor(SCI_MARKERSETFORE, marker_id, 0xffffff);
  }
  else
    send_editor(SCI_MARKERDEFINEPIXMAP, marker_id, (sptr_t)xpm.get());
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::setup_editor(const bool use_tabs, const int indentation, const std::string& lang)
{
  // Some values as default before reading the config file.
  send_editor(SCI_SETUSETABS, use_tabs ? 1 : 0);
  send_editor(SCI_SETTABWIDTH, indentation);
  send_editor(SCI_SETTABINDENTS, 1);
  send_editor(SCI_SETINDENT, indentation);

  language_setup(lang);

  send_editor(SCI_SETCARETLINEVISIBLE, 1);
  send_editor(SCI_SETCARETLINEBACK, 0xF8C800);
  send_editor(SCI_SETCARETLINEBACKALPHA, 20);

  send_editor(SCI_SETBACKSPACEUNINDENTS, 1);
  const int guide_type = (lang == "python") ? SC_IV_LOOKFORWARD : SC_IV_LOOKBOTH;
  send_editor(SCI_SETINDENTATIONGUIDES, guide_type);

  // Margin: Line number style.
  send_editor(SCI_STYLESETFORE, STYLE_LINENUMBER, 0x404040);
  send_editor(SCI_STYLESETBACK, STYLE_LINENUMBER, 0xE0E0E0);

  send_editor(SCI_SETMARGINTYPEN, 0, SC_MARGIN_NUMBER);
  const int lineNumberStyleWidth = send_editor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"_99999");
  send_editor(SCI_SETMARGINWIDTHN, 0, lineNumberStyleWidth);
  send_editor(SCI_SETMARGINSENSITIVEN, 0, 1); // Make Scintilla send a notification for clicks in this margin.

  // Margin: Markers.
  send_editor(SCI_SETMARGINWIDTHN, 1, 16);
  send_editor(SCI_SETMARGINSENSITIVEN, 1, 1);
  
  // Margin: Indicators (folders).
  send_editor(SCI_SETFOLDMARGINCOLOUR, 1, 0xE6E6E6);
  send_editor(SCI_SETMARGINWIDTHN, 2, 16);
  send_editor(SCI_SETMARGINSENSITIVEN, 2, 1);
  
  send_editor(SCI_SETEOLMODE, SC_EOL_LF);

  // Marker definitions.
  setup_marker(CE_STATEMENT_MARKER, "editor_statement.xpm", 0x0000ff);
  setup_marker(CE_ERROR_MARKER, "editor_error.xpm", 0x32de2e);
  setup_marker(CE_BREAKPOINT_MARKER, "editor_breakpoint.xpm", 0xa9443e);
  setup_marker(CE_BREAKPOINT_HIT_MARKER, "editor_breakpoint_hit.xpm", 0xa9443e);
  setup_marker(CE_CURRENT_LINE_MARKER, "editor_current_pos.xpm", 0xa9443e);

  // Folder setup.
  send_editor(SCI_SETMARGINWIDTHN, 2, 16);
  send_editor(SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
  send_editor(SCI_SETMARGINSENSITIVEN, 2, 1);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
  send_editor(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
  for (int n= 25; n < 32; ++n) // Markers 25..31 are reserved for folding.
  {
    send_editor(SCI_MARKERSETFORE, n, 0xffffff);
    send_editor(SCI_MARKERSETBACK, n, 0x404040);
  }
  
  // Init markers & indicators for highlighting of syntax errors.
  send_editor(SCI_INDICSETFORE, 0, 0xD01921);
  send_editor(SCI_INDICSETUNDER, 0, 1);
  send_editor(SCI_INDICSETSTYLE, 0, INDIC_SQUIGGLE);
}

//------------------------------------------------------------------------------
bool mforms::gtk::CodeEditorImpl::create(CodeEditor* self)
{
  return new mforms::gtk::CodeEditorImpl(self);
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::set_text(CodeEditor* self, const std::string& text)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  const int pos = ce->send_editor(SCI_GETCURRENTPOS);
  ce->send_editor(SCI_SETTEXT, 0, (sptr_t)text.c_str());
  ce->send_editor(SCI_GOTOPOS, pos);
  ce->send_editor(SCI_SCROLLCARET);
}

//------------------------------------------------------------------------------
const std::string mforms::gtk::CodeEditorImpl::get_text(CodeEditor* self, bool selection_only)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  char *buffer(0);
  const int length = ce->send_editor(SCI_GETLENGTH);
  if ( length > 0 )
  {
    buffer = new char[length+1];
    try
    {
      ce->send_editor(SCI_GETTEXT, length+1, (sptr_t)buffer);
      //send_editor(SCI_SETSAVEPOINT); // TODO: Check if we should tell sci that document is unmodified
    }
    catch (...)
    {
      delete[] buffer;
      buffer = 0;
    }
  }

  const std::string ret = buffer ? buffer : "";
  delete[] buffer;
  return ret;
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::get_selection(CodeEditor* self, int &start, int &length)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  start  = ce->send_editor(SCI_GETSELECTIONSTART);
  length = ce->send_editor(SCI_GETSELECTIONEND);
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::set_selection(CodeEditor* self, int start, int length)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  ce->send_editor(SCI_SETSELECTIONSTART, start);
  ce->send_editor(SCI_SETSELECTIONEND, start + length);
}

//------------------------------------------------------------------------------
bool mforms::gtk::CodeEditorImpl::get_range_of_line(CodeEditor* self, int line, int &start, int &length)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  const int lstart  = ce->send_editor(SCI_POSITIONFROMLINE, line);
  if (lstart >= 0)
    start = lstart;
  length = ce->send_editor(SCI_LINELENGTH, line);

  return lstart >= 0;
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::set_language(CodeEditor* self, SyntaxHighlighterLanguage language)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();

  if (ce)
  {
    switch (language)
    {
      case mforms::LanguageCpp:
        ce->setup_editor(false, 2, "cpp"); // Currently string based. Will later use enum.
        break;

      case mforms::LanguageLua:
        ce->setup_editor(true, 4, "lua");
        break;

      case mforms::LanguagePython:
        ce->setup_editor(true, 4, "python");
        break;

      case mforms::LanguageMySQL:
        ce->setup_editor(false, 2, "mysql");
        break;

      default:
        ce->setup_editor(false, 2, "Null");
    }
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::set_read_only(CodeEditor* self, bool flag)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
  {
    ce->send_editor(SCI_SETREADONLY, flag);
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::show_markup(CodeEditor* self, LineMarkup markup, int line)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
  {
    // The marker mask contains one bit for each set marker (0..31).
    unsigned int marker_mask = ce->send_editor(SCI_MARKERGET, line);
    unsigned int new_marker_mask = 0;
    if ((markup & mforms::LineMarkupStatement) != 0)
    {
      unsigned int mask = 1 << CE_STATEMENT_MARKER;
      if ((marker_mask & mask) != mask)
        new_marker_mask |= mask;
    }
    if ((markup & mforms::LineMarkupError) != 0)
    {
      unsigned int mask = 1 << CE_ERROR_MARKER;
      if ((marker_mask & mask) != mask)
        new_marker_mask |= mask;
    }
    if ((markup & mforms::LineMarkupBreakpoint) != 0)
    {
      unsigned int mask = 1 << CE_BREAKPOINT_MARKER;
      if ((marker_mask & mask) != mask)
        new_marker_mask |= mask;
    }
    if ((markup & mforms::LineMarkupBreakpointHit) != 0)
    {
      unsigned int mask = 1 << CE_BREAKPOINT_HIT_MARKER;
      if ((marker_mask & mask) != mask)
        new_marker_mask |= mask;
    }
    if ((markup & mforms::LineMarkupCurrent) != 0)
    {
      unsigned int mask = 1 << CE_CURRENT_LINE_MARKER;
      if ((marker_mask & mask) != mask)
        new_marker_mask |= mask;
    }

    ce->send_editor(SCI_MARKERADDSET, line, new_marker_mask);
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::remove_markup(CodeEditor* self, LineMarkup markup, int line)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
  {
    if (markup == mforms::LineMarkupAll)
      ce->send_editor(SCI_MARKERDELETEALL, line);
    else
    {
      if ((markup & mforms::LineMarkupStatement) != 0)
        ce->send_editor(SCI_MARKERDELETE, line, CE_STATEMENT_MARKER);
      if ((markup & mforms::LineMarkupError) != 0)
        ce->send_editor(SCI_MARKERDELETE, line, CE_ERROR_MARKER);
      if ((markup & mforms::LineMarkupBreakpoint) != 0)
        ce->send_editor(SCI_MARKERDELETE, line, CE_BREAKPOINT_MARKER);
      if ((markup & mforms::LineMarkupBreakpointHit) != 0)
        ce->send_editor(SCI_MARKERDELETE, line, CE_BREAKPOINT_HIT_MARKER);
      if ((markup & mforms::LineMarkupCurrent) != 0)
        ce->send_editor(SCI_MARKERDELETE, line, CE_CURRENT_LINE_MARKER);
    }
  }
}

//------------------------------------------------------------------------------
int mforms::gtk::CodeEditorImpl::line_count(CodeEditor* self)
{
  int ret = 0;
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
    ret = ce->send_editor(SCI_GETLINECOUNT);
  return ret;
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::set_font(CodeEditor* self, const std::string &fontDescription)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
  {
    std::string name;
    int size = 10;
    bool bold = 0;
    bool italic = 0;
    if (base::parse_font_description(fontDescription, name, size, bold, italic))
    {
      if (!name.empty() && name[0] != '!')
        name = "!"+name;
      ce->send_editor(SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)name.c_str());
      ce->send_editor(SCI_STYLESETSIZE, STYLE_DEFAULT, size);
      ce->send_editor(SCI_STYLESETBOLD, STYLE_DEFAULT, bold);
      ce->send_editor(SCI_STYLESETITALIC, STYLE_DEFAULT, italic);
    }
  }
}

//------------------------------------------------------------------------------


void mforms::gtk::CodeEditorImpl::notify(SCNotification *event)
{
  switch (event->nmhdr.code)
  {
    case SCN_MODIFIED:
      {
      const int mod_type = event->modificationType;
      if (mod_type & SC_MOD_INSERTTEXT || mod_type & SC_MOD_DELETETEXT)
      {
        //set_dirty(true);
        
        //_background_action_timer_conn.disconnect();
        //_background_action_timer_conn= Glib::signal_timeout().connect(
        //  sigc::mem_fun(this, &SqlEditorFE::on_background_action_timer),
        //  2000); //! get value from wb options
        _owner->text_changed(event->line, event->linesAdded);
      }
     // if (mod_type & SC_MOD_CHANGEFOLD)
     //   fold_changed(event->line, event->foldLevelNow, event->foldLevelPrev);
      }
      break;

    case SCN_MARGINCLICK:
      {
         int line = send_editor(SCI_LINEFROMPOSITION, event->position);
         if (event->margin == 2)
         {
           // Click on the folder margin. Toggle the current line if possible.
           send_editor(SCI_TOGGLEFOLD, line);
         }
        _owner->gutter_clicked(event->margin, line, (mforms::ModifierKey)event->modifiers);
      }
      break;

    case SCN_UPDATEUI:
      break;
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::show_gutter(CodeEditor* self, bool on)
{
  CodeEditorImpl* ce = self->get_data<CodeEditorImpl>();
  if (ce)
  {
    if (on)
    {
      const int width = ce->send_editor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"_99999");
      ce->send_editor(SCI_SETMARGINWIDTHN, 0, width);
      ce->send_editor(SCI_SETMARGINSENSITIVEN, 0, true);
      ce->send_editor(SCI_SETMARGINWIDTHN, 1, 16);
      ce->send_editor(SCI_SETMARGINSENSITIVEN, 1, true);
      ce->send_editor(SCI_SETMARGINWIDTHN, 2, 16);
      ce->send_editor(SCI_SETMARGINSENSITIVEN, 2, true);

      //scintilla->Styles[Constants::STYLE_LINENUMBER]->ForeColor = Color::FromArgb(64, 64, 64);
      //scintilla->Styles[Constants::STYLE_LINENUMBER]->BackColor = Color::FromArgb(220, 220, 220);
      //scintilla->Margins->FoldMarginColor = Color::FromArgb(230, 230, 230);

    }
    else
    {
      ce->send_editor(SCI_SETMARGINWIDTHN, 0, 0);
      ce->send_editor(SCI_SETMARGINWIDTHN, 1, 0);
      ce->send_editor(SCI_SETMARGINWIDTHN, 2, 0);
    }
  }
}


//------------------------------------------------------------------------------
void mforms::gtk::CodeEditorImpl::init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();

  f->_code_editor_impl.create = mforms::gtk::CodeEditorImpl::create;
  f->_code_editor_impl.set_text = mforms::gtk::CodeEditorImpl::set_text;
  f->_code_editor_impl.get_text = mforms::gtk::CodeEditorImpl::get_text;
  f->_code_editor_impl.get_selection = mforms::gtk::CodeEditorImpl::get_selection;
  f->_code_editor_impl.set_selection = mforms::gtk::CodeEditorImpl::set_selection;
  f->_code_editor_impl.get_range_of_line = mforms::gtk::CodeEditorImpl::get_range_of_line;
  f->_code_editor_impl.set_language = mforms::gtk::CodeEditorImpl::set_language;
  f->_code_editor_impl.set_read_only = mforms::gtk::CodeEditorImpl::set_read_only;
  f->_code_editor_impl.show_markup = mforms::gtk::CodeEditorImpl::show_markup;
  f->_code_editor_impl.remove_markup = mforms::gtk::CodeEditorImpl::remove_markup;
  f->_code_editor_impl.line_count = mforms::gtk::CodeEditorImpl::line_count;
  f->_code_editor_impl.set_font = mforms::gtk::CodeEditorImpl::set_font;
  f->_code_editor_impl.show_gutter = mforms::gtk::CodeEditorImpl::show_gutter;
}
