/******************************************************************************
 *                                    CAUTION
 ******************************************************************************
 *
 * ̃t@ĆCWindows lCsjis ŕۑĂ܂D
 * ̂߁C̃t@CRpCĂ܂D
 *
 * shiki.c
 *
 * Copyright(C)2006 WAKATSUKI toshihiro
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * $Id: shiki.c,v 1.22 2006/11/13 05:37:07 aloha Exp $
 */

#include<gauche.h>
#include<gtk/gtk.h>
#include<gdk/gdkkeysyms.h>

static gint editor_indent_width = 2;

/* ^uƂ̏Ǘ\ */
typedef struct {
  GtkScrolledWindow *tabpage;          /* ^u */
  gchar             *tabpage_label;    /* x[X (t@C) ̂ */
  GtkTextView       *text_view;        /* ^u̕\ */
  GtkTextBuffer     *text_buffer;      /* ^ũeLXgobt@ */
  gchar             *filename;         /* t@C̃tpX */
  ScmObj            env;               /* ^uƂɓƗ Scheme  */
  guint             delete_handler_id; /* gbvxɓo^nh ID */
} ShikiTabInfo;

/* GfB^ŜɊւƁCݕ\Ă^uւ̃LbVǗ\ */
struct {
  GtkWidget     *editor_window;
  GtkWidget     *statusbar;
  GtkWidget     *modeline_label;
  GList         *tabInfoList;
  gint          current_tabpage_num;
  ShikiTabInfo  *current_tabpage_info;
} shiki_editor;

#define Shiki_EDITOR_WINDOW         shiki_editor.editor_window
#define Shiki_EDITOR_STATUSBAR      shiki_editor.statusbar
#define Shiki_EDITOR_MODELINE_LABEL shiki_editor.modeline_label
#define Shiki_EDITOR_TAB_INFO_LIST  shiki_editor.tabInfoList
#define Shiki_CURRENT_TAB_NUM       shiki_editor.current_tabpage_num
#define Shiki_CURRENT_TAB_INFO      shiki_editor.current_tabpage_info
#define Shiki_CURRENT_TAB           (shiki_editor.current_tabpage_info)->tabpage
#define Shiki_CURRENT_TAB_TITLE     (shiki_editor.current_tabpage_info)->tabpage_label
#define Shiki_CURRENT_TEXT_VIEW     (shiki_editor.current_tabpage_info)->text_view
#define Shiki_CURRENT_TEXT_BUFFER   (shiki_editor.current_tabpage_info)->text_buffer
#define Shiki_CURRENT_FILENAME      (shiki_editor.current_tabpage_info)->filename
#define Shiki_CURRENT_SCHEME_ENV    (shiki_editor.current_tabpage_info)->env

/* vg^Cv */
static void clear_current_buffer();
static void load_buffer_by_gauche();
static void load_scheme_file_by_gauche(GtkNotebook *notebook);
static gchar *load_cstring_by_gauche(gchar *s);
static void font_selection_ok(GtkWidget *button, GtkWidget *font_dialog);
static void select_font();
static void about_this_application();
static gint get_current_line_number(GtkTextBuffer *buffer); 
static void update_modeline_label();
static void text_buffer_cursor_moved_handler();
static gchar* get_all_buffer_contents(GtkTextBuffer *buffer);
static gboolean save_text_buffer(const gchar *filename, GtkTextBuffer *buffer);
static gchar *get_filename_from_dialog(const gchar *msg);
static void save_file_from_notebook(GtkNotebook *notebook);
static void save_file_handler(GtkWidget *widget, GtkWidget *notebook);
static void save_file_as_from_notebook(GtkNotebook *notebook);
static void save_file_as_handler(GtkWidget *widget, GtkWidget *notebook);
static void really_quit_dialog_yes(GtkWidget *widget, gboolean *flag);
static void really_quit_dialog_no(GtkWidget *widget, gint *flag);
static gboolean not_yet_save_changes_really_quit(GtkTextBuffer *buffer);
static gboolean delete_event_handler(GtkWidget *widget, GdkEvent *event, GtkWidget *buffer);
static void append_tabpage(GtkNotebook *notebook, gchar *filename);
static GtkWidget *new_scrolled_text_buffer(ShikiTabInfo **tabinfo);
static void open_file(GtkNotebook *notebook, gchar *filename);
static void open_file_from_notebook(GtkNotebook *notebook);
static void open_file_handler(GtkWidget *widget, GtkWidget *notebook);
static gchar *eval_cstring_by_gauche(gchar *s);
static void load_region_handler(GtkWidget *widget,  GtkWidget *notebook);
static gboolean is_kakko_or_kokka(gunichar ch, gpointer); 
static gboolean is_kokka(gunichar ch, gpointer);
static gboolean search_sexp_string(GtkTextIter *start);
static gint get_parent_nest_level_at_cursor(GtkTextBuffer *buffer);
static void switch_tabpage_handler(GtkNotebook *notebook, GtkNotebookPage *page, guint pagenum) ;
static void tabsborder_on_off(GtkButton *button, GtkNotebook *notebook);
static void remove_tabpage(GtkNotebook *notebook);
static void remove_tabpage_handler(GtkButton *button, GtkWidget *notebook);
static void append_tabpage_handler(GtkButton *button, GtkNotebook *notebook);
static void rotate_tab_position(GtkButton   *button, GtkNotebook *notebook);
static void forward_current_buffer();
static void backward_current_buffer();
static void line_forward_current_buffer();
static void line_backward_current_buffer();
static gboolean signal_key_press_handler(GtkWidget *notebook, GdkEventKey *event, gpointer contextid);
static gboolean signal_key_release_handler(GtkWidget *notebook, GdkEventKey *event, gpointer contextid);
static void open_online_help(GtkNotebook *notebook);
static void online_help_handler(GtkWidget *button, GtkNotebook *notebook);
static void shiki_editor_window_init(int argc, char **argv);

/* obt@̓e */
static void clear_current_buffer() {
  GtkTextIter start, end;
  gtk_text_buffer_get_start_iter(Shiki_CURRENT_TEXT_BUFFER, &start);
  gtk_text_buffer_get_end_iter(Shiki_CURRENT_TEXT_BUFFER, &end);
  gtk_text_buffer_delete(Shiki_CURRENT_TEXT_BUFFER, &start, &end);
}

/* obt@܂邲ƃ[h */
static void load_buffer_by_gauche() {
  GtkTextIter p;
  gtk_text_buffer_get_end_iter(Shiki_CURRENT_TEXT_BUFFER, &p);
  gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, "\n\n", -1);
  gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, load_cstring_by_gauche(get_all_buffer_contents(Shiki_CURRENT_TEXT_BUFFER)), -1); 
}

/* t@C[h */
static void load_scheme_file_by_gauche(GtkNotebook *notebook) {
  gchar *contents, *text;
  gsize br, bw, len;
  GError *err = NULL;
  gchar *filename = get_filename_from_dialog("File Selection");
  GtkTextIter p;

  if(!filename) return;
  
  gtk_text_buffer_get_end_iter(Shiki_CURRENT_TEXT_BUFFER, &p);
  gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, "\n\n", -1);

  if(g_file_get_contents(filename, &contents, &len, NULL)) {
    if(!(text = g_locale_to_utf8(contents, -1, &br, &bw, &err)))
      gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, load_cstring_by_gauche(text), -1); 
    else
      gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, load_cstring_by_gauche(contents), -1); 
  }
  g_free(text); g_free(contents); g_free(filename);
}

/* gauche Nĕ[h */
static gchar *load_cstring_by_gauche(gchar *s) {
  gchar *msg;

  ScmObj result, error;
  /* ͕|[gJ */
  ScmObj is = Scm_MakeInputStringPort(SCM_STRING(SCM_MAKE_STR(s)), TRUE);
  /* o͕|[gJ */
  ScmObj os = Scm_MakeOutputStringPort(TRUE);

  Scm_Define(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*input*")), is);
  Scm_Define(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*error*")), SCM_FALSE);
  /* Scheme ̃xŃG[nhOC|[g珇Ԃ S ǂݍ݁C]ĂDRpCG[Ȃǂ *error* ɕߑ */
  result = Scm_EvalCString("(guard (e (else (set! *error* e) #f)) (eval (load-from-port *input*) (current-module)))", SCM_OBJ(Scm_UserModule()));

  error = Scm_GlobalVariableRef(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*error*")), 0);

  /* ]ʂ|[gɏ */
  if (!SCM_FALSEP(error))
    Scm_Write(error, os, SCM_WRITE_DISPLAY);
  else
    Scm_Write(result, os, SCM_WRITE_DISPLAY);

  msg = Scm_GetString(SCM_STRING(Scm_GetOutputString(SCM_PORT(os))));
  /* |[g */
  Scm_ClosePort(SCM_PORT(is));
  Scm_ClosePort(SCM_PORT(os));

  return msg;
}

static void font_selection_ok(GtkWidget *button, GtkWidget *font_dialog) {
  gchar *font_name = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG (font_dialog));
  if(font_name) {
    GtkRcStyle *style = gtk_rc_style_new ();
    pango_font_description_free(style->font_desc);
    style->font_desc = pango_font_description_from_string(font_name);
    gtk_widget_modify_style (GTK_WIDGET(Shiki_CURRENT_TEXT_VIEW), style);
    g_free (font_name);
  }
}

/* tHgICxgnh */
static void select_font(){
  GtkWidget *font_dialog = gtk_font_selection_dialog_new("Font Selection Dialog");
  g_signal_connect (GTK_FONT_SELECTION_DIALOG (font_dialog)->ok_button, "clicked", G_CALLBACK(font_selection_ok), font_dialog);
  gtk_dialog_run(GTK_DIALOG(font_dialog));
  gtk_widget_destroy(font_dialog);
}

/* ̃AvP[Vɂ */
static void about_this_application() {
  GtkAboutDialog *about = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
  const gchar *authors[] = {
    "ΏrG () <alohakun@gmail.com>"
  };
  gtk_about_dialog_set_authors(about, authors);
  gtk_about_dialog_set_copyright(about, "Copyright(C)2006  WAKATSUKI Toshihiro");
  gtk_about_dialog_set_name(about, " (SHIKI)");
  gtk_about_dialog_set_website_label(about, "{30ŃGfB^oオ̂Ă݂Blog");
  gtk_about_dialog_set_website(about, "http://alohakun.blog7.fc2.com/blog-category-29.html");
  gtk_dialog_run(GTK_DIALOG(about));
  gtk_widget_destroy(GTK_WIDGET(about));
}

/* ݂̃obt@̃J[\ʒu̍sԍ擾 */
static gint get_current_line_number(GtkTextBuffer *b) {
  GtkTextIter p;
  gtk_text_buffer_get_iter_at_mark(b, &p, gtk_text_buffer_get_insert(b));
  return gtk_text_iter_get_line(&p) + 1;
}

/* obt@̏Ԃɍ킹ă[hCύX */
static void update_modeline_label() {
  gchar* basename = g_path_get_basename(Shiki_CURRENT_TAB_TITLE);
  gchar* l = g_strdup_printf("-E:%s  %-10s (Gauche Interaction)--L%d--------------------------------------",
      gtk_text_buffer_get_modified(Shiki_CURRENT_TEXT_BUFFER) ? "**" : "--",
      basename, get_current_line_number(Shiki_CURRENT_TEXT_BUFFER));
  gtk_label_set_text(GTK_LABEL(Shiki_EDITOR_MODELINE_LABEL), l);
  g_free(l); g_free(basename);  
}

static void text_buffer_cursor_moved_handler(){
  update_modeline_label();
}

/* eLXgobt@SĂ̕o */
static gchar* get_all_buffer_contents(GtkTextBuffer *buffer) {
  GtkTextIter start, end;
  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
}

/* buffer ̓et@C filename ɕۑ */
static gboolean save_text_buffer(const gchar *filename, GtkTextBuffer *buffer) {
  gchar *contents, *text;
  gsize br, bw;
  GError *err = NULL;

  if(!filename) return FALSE;
  contents = get_all_buffer_contents(buffer);
  text = g_locale_from_utf8(contents, -1, &br, &bw, &err);
  /* t@Cɕۑ */
  g_file_set_contents(filename, text, -1, NULL);
  gtk_text_buffer_set_modified(buffer, FALSE);
  update_modeline_label();
  g_free(contents); g_free(text);
  return TRUE;
}

/* _CAOJCt@C擾Dmsg ̓_CAÕbZ[W */
static gchar *get_filename_from_dialog(const gchar *msg) {

  GtkWidget *dialog = gtk_file_selection_new(msg);
  int resp = gtk_dialog_run(GTK_DIALOG(dialog));
  gchar *filename = NULL;

  /* gtk_file_selection_get_filename Ԃ萔͐ÓIɊmۂꂽobt@wĂ̂ŁCRs[Ȃ΂ȂȂ */
  if(resp == GTK_RESPONSE_OK)
    filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog)));

  gtk_widget_destroy(dialog);
  return filename;
}

/* ݕ\Ăy[W̓et@Cɕۑ */
static void save_file_from_notebook(GtkNotebook *notebook) {

  /* wvւ̕ύX͕ۑȂ */
  if(g_ascii_strcasecmp("*help*", Shiki_CURRENT_TAB_TITLE) == 0) return;
    
  /* ύXΉȂ */
  if(!gtk_text_buffer_get_modified(Shiki_CURRENT_TEXT_BUFFER)) return;

  /* ܂t@Cݒ肳ĂȂC_CAOJē͂ */
  if(g_ascii_strcasecmp("*scratch*", Shiki_CURRENT_TAB_TITLE) == 0) {
    gchar *filename = get_filename_from_dialog("Save File As ...");
    if(!filename) return;
    if(!save_text_buffer(filename, Shiki_CURRENT_TEXT_BUFFER)) return;
    gtk_notebook_set_tab_label_text(notebook, GTK_WIDGET(Shiki_CURRENT_TAB), filename);
    gtk_window_set_title (GTK_WINDOW(Shiki_EDITOR_WINDOW), filename);
    g_free(filename);
  } else
    save_text_buffer(Shiki_CURRENT_TAB_TITLE, Shiki_CURRENT_TEXT_BUFFER);
}

/* t@CۑCxgnh */
static void save_file_handler(GtkWidget *widget, GtkWidget *notebook) {
  save_file_from_notebook(GTK_NOTEBOOK(notebook));
}

/* ݕ\Ăy[W̓et@Cɕʖۑ */
static void save_file_as_from_notebook(GtkNotebook *notebook) {
  gchar *filename = get_filename_from_dialog("Save File As ...");

  if(!filename) return;
  if(!save_text_buffer(filename, Shiki_CURRENT_TEXT_BUFFER)) return;

  gtk_notebook_set_tab_label_text(notebook, GTK_WIDGET(Shiki_CURRENT_TAB), filename);
  gtk_window_set_title (GTK_WINDOW (Shiki_EDITOR_WINDOW), filename);

  g_free(filename);
}

/* t@CʖۑCxgnh */
static void save_file_as_handler(GtkWidget *widget, GtkWidget *notebook) {
  save_file_as_from_notebook(GTK_NOTEBOOK(notebook));
}

/* YES {^CNO {^ꂼŌĂ΂ callback */
static void really_quit_dialog_yes(GtkWidget *widget, gboolean *flag){*flag = FALSE;}
static void really_quit_dialog_no(GtkWidget *widget, gint *flag){*flag = TRUE;}

/* {ɏIĂ낵ł ? */
static gboolean not_yet_save_changes_really_quit(GtkTextBuffer *buffer) {
  GtkWidget *yes_button, *no_button;
  static GtkWidget *dialog_window = NULL;

  /* ύX΂̂܂܏I */
  if(!gtk_text_buffer_get_modified(buffer)) return FALSE;

  if(dialog_window == NULL) {
    gboolean flag = TRUE;
    dialog_window = gtk_dialog_new ();

    /* {ɏIĂ낵ł ? _CAȌ */
    g_signal_connect(G_OBJECT(dialog_window), "delete_event", G_CALLBACK(gtk_false), NULL);
    g_signal_connect(G_OBJECT(dialog_window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog_window)->vbox), 
        gtk_label_new("obt@ύXĂ܂D\nZ[uȂŏI܂ ?"), TRUE, TRUE, 0);
    gtk_window_set_title(GTK_WINDOW (dialog_window), "Really Quit ?");
    /* YES ̃{^ */
    yes_button = gtk_button_new_with_mnemonic("͂ (_Y)");
    g_signal_connect(GTK_OBJECT(yes_button), "clicked", G_CALLBACK(really_quit_dialog_yes), &flag);
    g_signal_connect_swapped(GTK_OBJECT(yes_button), "clicked", G_CALLBACK(gtk_widget_destroy), G_OBJECT(dialog_window));
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_window)->action_area),  yes_button, TRUE, TRUE, 0);

    /* NO ̃{^ */
    no_button = gtk_button_new_with_mnemonic(" (_N)");
    g_signal_connect(GTK_OBJECT(no_button), "clicked", G_CALLBACK(really_quit_dialog_no), &flag);
    g_signal_connect_swapped(GTK_OBJECT(no_button), "clicked", G_CALLBACK(gtk_widget_destroy), G_OBJECT(dialog_window));
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_window)->action_area),  no_button, TRUE, TRUE, 0);

    gtk_window_set_modal(GTK_WINDOW(dialog_window), TRUE);
    gtk_window_set_transient_for(GTK_WINDOW(dialog_window), GTK_WINDOW (Shiki_EDITOR_WINDOW));

    gtk_widget_show_all(dialog_window);
    gtk_main ();
    dialog_window = NULL;

    /* "delete_event" ̕Ԃl FALSE Ȃ"destory" sCwindow ͔j󂳂 */
    return flag;
  }
  return TRUE;
}

/* obt@܂ۑĂȂ̂ɖ{ɏÎq˂ */
static gboolean delete_event_handler(GtkWidget *widget, GdkEvent *event, GtkWidget *buffer){
  return not_yet_save_changes_really_quit(GTK_TEXT_BUFFER(buffer));
}

/* V^u */
static GtkWidget *new_scrolled_text_buffer(ShikiTabInfo **tabinfo) {

  /* ^uɑΉ^u */
  *tabinfo = g_malloc(sizeof(ShikiTabInfo));

  /* XN[EBh */
  (*tabinfo)->tabpage = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
  gtk_scrolled_window_set_policy ((*tabinfo)->tabpage, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  /* eLXgr[ƃobt@ */
  (*tabinfo)->text_view = GTK_TEXT_VIEW(gtk_text_view_new());
  (*tabinfo)->text_buffer = gtk_text_view_get_buffer((*tabinfo)->text_view);
  gtk_container_add(GTK_CONTAINER((*tabinfo)->tabpage), GTK_WIDGET((*tabinfo)->text_view));
  gtk_widget_set_size_request(GTK_WIDGET((*tabinfo)->text_view), 500, 600);
  g_signal_connect((*tabinfo)->text_buffer, "mark_set", G_CALLBACK(text_buffer_cursor_moved_handler), (*tabinfo)->text_view);
  /* ^u폜ہCf[gnh폜ĂȂƌxo邩 */
  (*tabinfo)->delete_handler_id = g_signal_connect(Shiki_EDITOR_WINDOW, "delete_event", G_CALLBACK(delete_event_handler), (*tabinfo)->text_buffer);
  /* lXȏ */
  /* ʂ̋\̂߂̃^O */
  gtk_text_buffer_create_tag ((*tabinfo)->text_buffer, "parent_emphasis_background", "background", "green", NULL);

  return GTK_WIDGET((*tabinfo)->tabpage);
}

/* t@CJ */
static void open_file(GtkNotebook *notebook, gchar *filename) {
  gchar *contents, *text;
  gsize br, bw, len;
  GError *err = NULL;
  
  if(g_file_get_contents(filename, &contents, &len, NULL)) {
    GtkTextIter p;

    /* Vobt@J */
    append_tabpage(notebook, g_strdup(filename));

    if(!(text = g_locale_to_utf8(contents, -1, &br, &bw, &err)))
      gtk_text_buffer_set_text(Shiki_CURRENT_TEXT_BUFFER, contents, len);
    else
      gtk_text_buffer_set_text(Shiki_CURRENT_TEXT_BUFFER, text, len);

    /* obt@ύX */
    gtk_text_buffer_set_modified(Shiki_CURRENT_TEXT_BUFFER, FALSE);
    /* J[\ʒu擪 */
    gtk_text_buffer_get_start_iter(Shiki_CURRENT_TEXT_BUFFER, &p);
    gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);
    update_modeline_label();
    gtk_window_set_title (GTK_WINDOW (Shiki_EDITOR_WINDOW), filename);
    gtk_widget_show_all(GTK_WIDGET(notebook));
    g_free(contents); g_free(text); g_free(filename);
  } else
    g_printerr("Get file contents error !\n");
}

/* t@C_CAOŎw肵ĊJ */
static void open_file_from_notebook(GtkNotebook *notebook) {
  gchar *filename = get_filename_from_dialog("File Selection");

  if(!filename) return;
  open_file(notebook, filename);
}

/* t@CJCxgnh */
static void open_file_handler(GtkWidget *widget,  GtkWidget *notebook) {
  open_file_from_notebook(GTK_NOTEBOOK(notebook));
}

/* gauche Nĕ] */
static gchar *eval_cstring_by_gauche(gchar *s) {
  gchar *msg;

  ScmObj result, error; 
  /* o͕|[gJ */
  ScmObj os = Scm_MakeOutputStringPort(TRUE);

  /* Scheme xŃG[nhO */
  /* http://alohakun.blog7.fc2.com/blog-entry-517.html */
  Scm_Define(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*input*")), SCM_MAKE_STR(s));
  Scm_Define(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*error*")), SCM_FALSE);

  result = Scm_EvalCString("(guard (e (else (set! *error* e) #f)) (eval (read-from-string *input*) (current-module)))", SCM_OBJ(Scm_UserModule()));

  error = Scm_GlobalVariableRef(Scm_UserModule(), SCM_SYMBOL(SCM_INTERN("*error*")), 0);

  /* ]ʂ|[gɏ */
  if (!SCM_FALSEP(error))
    Scm_Write(error, os, SCM_WRITE_DISPLAY);
  else
    Scm_Write(result, os, SCM_WRITE_DISPLAY);

  msg = Scm_GetString(SCM_STRING(Scm_GetOutputString(SCM_PORT(os))));
  /* |[g */
  Scm_ClosePort(SCM_PORT(os));

  return msg;
}

/* ΃{^̃nhODobt@̑I͈͂ S [h (]) */
static void load_region_handler(GtkWidget *widget,  GtkWidget *notebook) {

  GtkTextIter start, end, p;
  gchar *code;
  gtk_text_buffer_get_end_iter(Shiki_CURRENT_TEXT_BUFFER, &p);
  gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, "\n\n", -1);

  /* }EXőIĂ͈͂̕擾 */
  if(gtk_text_buffer_get_selection_bounds(Shiki_CURRENT_TEXT_BUFFER, &start, &end)) {
    code = gtk_text_buffer_get_text(Shiki_CURRENT_TEXT_BUFFER, &start, &end, FALSE);
    gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &p, load_cstring_by_gauche(code), -1);
    g_free(code);
  }
}

// GtkTextCharPredicate
static gboolean is_kakko_or_kokka(gunichar ch, gpointer p) {
  return ch == '(' || ch == ')';
}
static gboolean is_kokka(gunichar ch, gpointer p) {return ch == ')';}


/* ')' ɑΉ '(' ܂ł̕ (S ) ؂o */
static gboolean search_sexp_string(GtkTextIter *start) {
  gint nest_level = 0;
  /* J[\ʒȗOɂ S ؂o */
  while(1) {
    if(!gtk_text_iter_backward_find_char(start, is_kakko_or_kokka, NULL, NULL))
      return FALSE;

    if(gtk_text_iter_get_char(start) == ')')
      nest_level++;
    else {
      if(!nest_level)
        break;
      else
        nest_level--;
    }
  }
  return TRUE;
}

/* J[\ʒũlXgxԂ */
static gint get_parent_nest_level_at_cursor(GtkTextBuffer *buffer) {
  gint nest_level = 0;
  GtkTextIter start, end;
  gtk_text_buffer_get_start_iter(buffer, &start);
  if(gtk_text_iter_get_char(&start) == '(') nest_level++;

  /* J[\̈ʒu (= end) 擾 */
  gtk_text_buffer_get_iter_at_mark(buffer,&end, gtk_text_buffer_get_insert(buffer));

  while(1) {
    /* end ܂ '('  ')' TČȂI */
    if(!gtk_text_iter_forward_find_char(&start, is_kakko_or_kokka, NULL, &end))
      return nest_level;

    if(gtk_text_iter_get_char(&start) == '(')
      nest_level++;
    else 
      nest_level--;
  }
}

/* ^u؂ւ鎞̃CxgnhO */
static void switch_tabpage_handler(GtkNotebook *notebook, GtkNotebookPage *page, guint pagenum) {
  /* ^uɑΉ񂪊i[ꂽ\̂ؑւ */
  Shiki_CURRENT_TAB_INFO = (ShikiTabInfo *)g_list_nth_data(Shiki_EDITOR_TAB_INFO_LIST, pagenum);
  /* ݂̃^uԍؑւ */
  Shiki_CURRENT_TAB_NUM = pagenum;

  /* ^ũxEBhẼ^Cg */
  if(!Shiki_CURRENT_TAB_INFO) return;
  gtk_window_set_title (GTK_WINDOW(Shiki_EDITOR_WINDOW), Shiki_CURRENT_FILENAME);

  update_modeline_label();  
}

/* y[W̃^uƋE on/off */
static void tabsborder_on_off(GtkButton *button, GtkNotebook *notebook) {
  gint tval = FALSE;
  gint bval = FALSE;
  if(notebook->show_tabs == FALSE)
    tval = TRUE; 
  if(notebook->show_border == FALSE)
    bval = TRUE;

  gtk_notebook_set_show_tabs(notebook, tval);
  gtk_notebook_set_show_border(notebook, bval);
}

/* m[gubNy[W폜 */
static void remove_tabpage(GtkNotebook *notebook) {
  /* ^u 1 cĂȂȂ */
  if(g_list_length(Shiki_EDITOR_TAB_INFO_LIST) == 1)
    return;
  if(!not_yet_save_changes_really_quit(Shiki_CURRENT_TEXT_BUFFER)) {
    /* f[gnhGfB^gbvx̃EBWbg菜 */
    g_signal_handler_disconnect(Shiki_EDITOR_WINDOW, (Shiki_CURRENT_TAB_INFO)->delete_handler_id);
    /* ^u̒g폜 */
    gtk_widget_destroy(GTK_WIDGET(Shiki_CURRENT_TEXT_VIEW));
    /* ^uɑΉ^u폜 */
    g_free(Shiki_CURRENT_TAB_TITLE);
    g_free(Shiki_CURRENT_FILENAME);
    Shiki_EDITOR_TAB_INFO_LIST = g_list_delete_link(Shiki_EDITOR_TAB_INFO_LIST, g_list_nth(Shiki_EDITOR_TAB_INFO_LIST, Shiki_CURRENT_TAB_NUM));

    /* Jg̃^uXV */
    Shiki_CURRENT_TAB_INFO = g_list_nth_data(Shiki_EDITOR_TAB_INFO_LIST, Shiki_CURRENT_TAB_NUM);
    gtk_notebook_remove_page(notebook, Shiki_CURRENT_TAB_NUM);
    /* EBWbgIɍĕ` */
    gtk_widget_queue_draw(GTK_WIDGET(notebook));
  }
}

static void remove_tabpage_handler(GtkButton *button, GtkWidget *notebook) {
  remove_tabpage(GTK_NOTEBOOK(notebook));
}

/* m[gubNɃy[Wǉ */
static void append_tabpage(GtkNotebook *notebook, gchar *filename) {
  ShikiTabInfo *tabinfo;
  GtkWidget *tabpage = new_scrolled_text_buffer(&tabinfo);
  tabinfo->filename = filename;
  tabinfo->tabpage_label = g_path_get_basename(filename);
  gtk_notebook_append_page(notebook, tabpage, gtk_label_new(tabinfo->tabpage_label));
  Shiki_EDITOR_TAB_INFO_LIST = g_list_append(Shiki_EDITOR_TAB_INFO_LIST, tabinfo);
  gtk_notebook_set_current_page(notebook, g_list_length(Shiki_EDITOR_TAB_INFO_LIST) - 1);
  gtk_widget_show_all(GTK_WIDGET(notebook));
  gtk_notebook_set_current_page(notebook, Shiki_CURRENT_TAB_NUM);
}

static void append_tabpage_handler(GtkButton *button, GtkNotebook *notebook) {
  append_tabpage(notebook, g_strdup("*scratch*"));
}

/* ^üʒu𒲐 */
static void rotate_tab_position(GtkButton *button, GtkNotebook *notebook ) {
  gtk_notebook_set_tab_pos(notebook, (notebook->tab_pos + 1) % 4);
}

/* ̃L[̃nhO */

/* J[\̈ړ ^npfb */
static void forward_current_buffer() {
  GtkTextIter p;
  gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER,&p, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));
  gtk_text_iter_forward_char(&p);
  gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);
}
static void backward_current_buffer() {
  GtkTextIter p;
  gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER,&p, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));
  gtk_text_iter_backward_char(&p);
  gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);
}
static void line_forward_current_buffer() {
  GtkTextIter p;
  gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER, &p, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));
  gtk_text_view_forward_display_line(Shiki_CURRENT_TEXT_VIEW, &p);
  gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);
}
static void line_backward_current_buffer() {
  GtkTextIter p;
  gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER,&p, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));
  gtk_text_view_backward_display_line(Shiki_CURRENT_TEXT_VIEW, &p);
  gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);
}

/* L[ꂽ */
static gboolean signal_key_press_handler (GtkWidget *notebook, GdkEventKey *event, gpointer contextid) {
  GtkTextIter start, end;

  /* ʂ̑Ή̋𖳌 */
  gtk_text_buffer_get_start_iter(Shiki_CURRENT_TEXT_BUFFER, &start);
  gtk_text_buffer_get_end_iter(Shiki_CURRENT_TEXT_BUFFER, &end);
  gtk_text_buffer_remove_tag_by_name(Shiki_CURRENT_TEXT_BUFFER, "parent_emphasis_background", &start, &end);

  if(event->state & GDK_CONTROL_MASK) {
    switch(event->keyval) {
      case GDK_f :  /* Ctrl + f : forward */
        forward_current_buffer();
        break;
      case GDK_b : /* Ctrl + b : backward */
        backward_current_buffer();
        break;
      case GDK_n : /* Ctrl + n : next line */
        line_forward_current_buffer();
        break;
      case GDK_p :  /* Ctrl + p : previous line */
        line_backward_current_buffer();
        break;

      case GDK_j :  /* Ctrl + j : J[\O S ̕] */
        {
          gchar *code;
          GtkTextIter start, end;

          /* J[\̈ʒu擾 */
          gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER, &end, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));

          gtk_text_iter_backward_find_char(&end, is_kokka, NULL, NULL);
          start = end;
          gtk_text_iter_forward_char(&end);

          /* J[\ʒȗOɂ S ؂o */
          if(!search_sexp_string(&start)) return FALSE;

          code = gtk_text_buffer_get_text(Shiki_CURRENT_TEXT_BUFFER, &start, &end, FALSE);
          gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &end, "\n\n", -1);
          gtk_text_buffer_insert(Shiki_CURRENT_TEXT_BUFFER, &end, eval_cstring_by_gauche(code), -1);
          g_free(code);
        }
        break;

      case GDK_t : /* Ctrl + t : ^uJ */
        append_tabpage(GTK_NOTEBOOK(notebook), g_strdup("*scratch*"));
        break;

      case GDK_k : /* Ctrl + k : ^u */
        remove_tabpage(GTK_NOTEBOOK(notebook));
        break;
    }
  }
  return FALSE;
}

/* L[ꂽ */
static gboolean signal_key_release_handler (GtkWidget *notebook, GdkEventKey *event, gpointer contextid) {
  static gint metakey_pressed = 0;
  static gint controlx_pressed = 0;

  if(event->keyval == GDK_parenright && event->state & GDK_SHIFT_MASK) {
    GtkTextIter start, end;

    /* J[\̈ʒu擾 */
    gtk_text_buffer_get_iter_at_mark(Shiki_CURRENT_TEXT_BUFFER, &end, gtk_text_buffer_get_insert(Shiki_CURRENT_TEXT_BUFFER));

    start = end;
    gtk_text_iter_backward_char(&start);

    /* J[\ʒȗOɂ S ؂o */
    if(!search_sexp_string(&start)) return FALSE;

    gtk_text_buffer_apply_tag_by_name(Shiki_CURRENT_TEXT_BUFFER, "parent_emphasis_background", &start, &end);
  }

  /* s邽тɁCIɊʂ̃lXg̐[ɉ̃Xy[X (Cfg) sɓ */
  if(event->keyval == GDK_Return) {
    gint indentWidth = get_parent_nest_level_at_cursor(Shiki_CURRENT_TEXT_BUFFER) * editor_indent_width;
    gchar *indent = g_strnfill(indentWidth, ' ');
    gtk_text_buffer_insert_at_cursor(Shiki_CURRENT_TEXT_BUFFER, indent, -1);
    g_free(indent);
  }

  /* C-x */
  if(event->keyval == GDK_x && event->state & GDK_CONTROL_MASK) {
    controlx_pressed++;
    gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "C-x -");
  } else if(event->state & GDK_CONTROL_MASK) {

    if(controlx_pressed > 0) {
      switch(event->keyval) {
        case GDK_c :/* C-x C-c : I */
          gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "C-c");
          {/* "delete-event" 𔭐DEBhE ~ {^ꂽ̂Ɠ */
            GdkEvent ev;

            ev.any.type = GDK_DELETE;
            ev.any.window = Shiki_EDITOR_WINDOW->window;
            ev.any.send_event = FALSE;
            gdk_event_put (&ev);
          }
          break;

        case GDK_f : /* C-x C-f : t@CJ */
          gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "C-f");
          open_file_from_notebook(GTK_NOTEBOOK(notebook));
          break;

        case GDK_s : /* C-x C-s : t@Cۑ */
          gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "C-s");
          save_file_from_notebook(GTK_NOTEBOOK(notebook));    
          break;

        case GDK_w : /* C-x C-w : t@Cʖۑ */
          gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "C-w");
          save_file_as_from_notebook(GTK_NOTEBOOK(notebook));    
          break;
      }
      controlx_pressed = 0;
    }

    switch(event->keyval) {
      case GDK_g :/* C-g : LZ */
        metakey_pressed = 0;
        controlx_pressed = 0;

        gtk_statusbar_push(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), GPOINTER_TO_INT(contextid), "Quit");
        break;
    }

  }
  return FALSE;
}
static void open_online_help(GtkNotebook *notebook) {
  GtkTextIter p;
  append_tabpage(notebook, g_strdup("*help*"));
  gtk_text_buffer_set_text(Shiki_CURRENT_TEXT_BUFFER,
"[tH_̃ACR]             t@CJ (C-x C-f)\n"
"[tbs[fBXÑACR]   obt@ۑ (C-x C-s)\n"
"[tbs[Ƃ҂̃ACR] obt@ʖۑ (C-x C-w)\n"
"[MÃACR]                 Ï gauche Ƀ[h (region-compile)\n"
"[}[LO () ̃ACR] ^u on/off\n"
"[H (Xpi) ̃ACR]       ^üʒu̒\n"
"[܂ + ACR]                VKobt@J (Ctrl + t) (tab)\n"
"[S~ACR]                 obt@NA\n"
"[~ ACR]                     obt@ (Ctrl + k) (kill buffer)\n"
"[A ACR]                     tHg̑I\n"
"[݂ȃACR]             Scheme t@C̃[h\n"
"[΂ۂCϊACR]         obt@̓eSă[h\n"
"[wv (?) ACR]            ̃wvʂ\܂\n"
"[info ACR]                  ̃AvP[VɂĂ̏\n\n"
"C-f :  Ɉړ (forward)\n"
"C-b :  Ɉړ (backward)\n"
"C-n :  Ɉړ (next line)\n"
"C-p :  Ɉړ (previous line)\n\n"
"C-j : J[\O S ] (eval-last-sexp)\n"
"(emacs/xyzzy  *scratch* obt@Ɠ)\n\n"

"C-x C-c : IDEBhE ~ {^̂Ɠ\n\n"

"܂Ce鋰ꂪƂɂ͑SāuReqlly Quit ?v̂悤Ɍx܂D\n", -1);
    gtk_text_buffer_set_modified(Shiki_CURRENT_TEXT_BUFFER, FALSE);
    /* J[\ʒu擪 */
    gtk_text_buffer_get_start_iter(Shiki_CURRENT_TEXT_BUFFER, &p);
    gtk_text_buffer_place_cursor(Shiki_CURRENT_TEXT_BUFFER, &p);    
}

static void online_help_handler(GtkWidget *button, GtkNotebook *notebook) {
  open_online_help(notebook);
}

/* GfB^̕ҏWʂ̏ */
static void shiki_editor_window_init(int argc, char **argv) {
  GtkWidget *vbox, *toolbar, *notebook;
  GtkToolItem *icon;
  GtkIconSize iconsize;
  GtkTooltips *toolbar_tips = gtk_tooltips_new();
  /* uJvuۑvuʖۑvuÏsvACR */
  GtkToolItem *oicon, *sicon, *saicon, *eicon;

  gint contextid;

  /*  */
  Shiki_EDITOR_WINDOW = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(G_OBJECT(Shiki_EDITOR_WINDOW), "destroy", G_CALLBACK(gtk_main_quit), NULL);

  /* pbLO{bNX */
  vbox = gtk_vbox_new(FALSE, 0);
  /* c[o[ */
  toolbar = gtk_toolbar_new();
  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);

  notebook = gtk_notebook_new();
  g_signal_connect(G_OBJECT(notebook), "switch-page", GTK_SIGNAL_FUNC(switch_tabpage_handler), NULL);

  /* c[o[ɕtACR̐ݒ */
  gtk_toolbar_set_style(GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
  iconsize = gtk_toolbar_get_icon_size (GTK_TOOLBAR (toolbar));

  /* ACR */

  /* t@CJ */
  oicon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-open", iconsize), "");
  /* uJv{^Ƀt@CǂݍރANV֘At */
  g_signal_connect(G_OBJECT(oicon), "clicked", G_CALLBACK(open_file_handler), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(oicon));
  gtk_tool_item_set_tooltip(oicon, toolbar_tips, "t@CJ܂",
      "Vobt@JāCɃt@C̓eWJ܂D");

  /* obt@ۑ */
  sicon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-save", iconsize), "");
  /* uۑv{^Ƀt@CoANV֘At */
  g_signal_connect(G_OBJECT(sicon), "clicked", G_CALLBACK(save_file_handler), G_OBJECT(notebook));
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET(sicon));      
  gtk_tool_item_set_tooltip(sicon, toolbar_tips, "obt@ۑ܂",
      "obt@Ƀt@Cݒ肳ĂȂꍇɂ́C_CAOJăt@C擾܂");

  /* obt@ʖۑ */
  saicon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-save-as", iconsize), "");
  /* uʖۑv{^ɕʖ̃t@CJēeoANV֘At */
  g_signal_connect(G_OBJECT(saicon), "clicked", G_CALLBACK(save_file_as_handler), G_OBJECT(notebook));
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET(saicon));
  gtk_tool_item_set_tooltip(saicon, toolbar_tips, "obt@ʖۑ܂",
      "");

  /* obt@s */
  eicon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-execute", iconsize), "");
  /* uI͈͂[hv{^ libgauche ֘At */
  g_signal_connect(G_OBJECT(eicon), "clicked", G_CALLBACK(load_region_handler), G_OBJECT(notebook));
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET(eicon));
  gtk_tool_item_set_tooltip(eicon, toolbar_tips, "I͈͂ S [h܂ (load-region-lisp)",
      "Scheme (gauche) ŕ]ł S ]܂D");

  gtk_container_add(GTK_CONTAINER(Shiki_EDITOR_WINDOW), vbox);
  gtk_container_add(GTK_CONTAINER(vbox), notebook);

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-apply", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(tabsborder_on_off), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "^u on/off", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-preferences", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(rotate_tab_position), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "^uʒu̐ݒ", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-add", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(append_tabpage_handler), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "VKobt@J܂", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-delete", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(clear_current_buffer), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "obt@̓eS",
      "܂eۑĂȂꍇłx܂");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-close", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(remove_tabpage_handler), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "obt@܂",
      "܂eۑĂȂꍇ͌x܂");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-bold", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(select_font), NULL);
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "tHg̕ύX", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-connect", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(load_scheme_file_by_gauche), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "Scheme t@C[h", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-convert", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(load_buffer_by_gauche), G_OBJECT(notebook));
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "obt@[h", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-dialog-question", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(online_help_handler), notebook);
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "wv", "");

  icon = gtk_tool_button_new(gtk_image_new_from_stock ("gtk-dialog-info", iconsize), "");
  g_signal_connect(G_OBJECT(icon), "clicked", G_CALLBACK(about_this_application), NULL);
  gtk_container_add(GTK_CONTAINER (toolbar), GTK_WIDGET(icon));
  gtk_tool_item_set_tooltip(icon, toolbar_tips, "̃AvP[Vɂ", "");

  gtk_box_pack_start(GTK_BOX(vbox), Shiki_EDITOR_MODELINE_LABEL = gtk_label_new("-E:**  *scratch*     (Gauche Interaction)--L1--All---------------------------------"), TRUE, TRUE, 0);

  /* C-x C-s Ȃǂ̏Ԃ\Xe[^Xo[EBhE{gɒǉ */
  Shiki_EDITOR_STATUSBAR = gtk_statusbar_new();
  gtk_box_pack_start(GTK_BOX(vbox), Shiki_EDITOR_STATUSBAR, TRUE, TRUE, 0);
  contextid = gtk_statusbar_get_context_id(GTK_STATUSBAR(Shiki_EDITOR_STATUSBAR), "");

  /* L[oCh̃nhOo^ */
  g_signal_connect(G_OBJECT(notebook), "key-press-event", G_CALLBACK (signal_key_press_handler), GINT_TO_POINTER(contextid));
  g_signal_connect(G_OBJECT(notebook), "key-release-event", G_CALLBACK (signal_key_release_handler), GINT_TO_POINTER(contextid));

  /* Ɏw肳ꂽt@CJ */
  if(argc >= 2) {
    int i;
    for(i = 1; i < argc; i++)
      open_file(GTK_NOTEBOOK(notebook), g_strdup(argv[i]));
  } else /* w肳ĂȂ΁CftHg̃y[Wǉ */
    open_online_help(GTK_NOTEBOOK(notebook));

  gtk_widget_grab_focus(notebook);
  gtk_widget_show_all(Shiki_EDITOR_WINDOW);
}

int main(int argc, char *argv[]) {
  /* ăC[v */
  gtk_set_locale(); 
  gtk_init(&argc, &argv);
  GC_INIT(); Scm_Init(GAUCHE_SIGNATURE);
  shiki_editor_window_init(argc, argv);
  gtk_main();
  Scm_Exit(0);
  return 0;
}
