# include <iostream>

# include <gtkmm.h>
# include <gtkmm/widget.h>
# include <gtkmm/notebook.h>
# include <pangomm/fontdescription.h>
# include <pango/pango.h>

# ifndef DISABLE_VTE
# include <vte/vte.h>
# endif

# include <boost/filesystem.hpp>

# include "astroid.hh"
# include "poll.hh"
# include "main_window.hh"
# include "modes/mode.hh"
# include "modes/thread_index/thread_index.hh"
# include "modes/saved_searches.hh"
# include "modes/help_mode.hh"
# include "modes/edit_message.hh"
# include "modes/log_view.hh"
# include "command_bar.hh"
# include "actions/action.hh"
# include "actions/action_manager.hh"
# include "utils/resource.hh"

using namespace std;
namespace bfs = boost::filesystem;

# ifndef DISABLE_VTE
extern "C" {
  void mw_on_terminal_child_exit (VteTerminal * t, gint a, gpointer mw) {
    ((Astroid::MainWindow *) mw)->on_terminal_child_exit (t, a);
  }

  void mw_on_terminal_commit (VteTerminal * t, gchar ** tx, guint sz, gpointer mw) {
    ((Astroid::MainWindow *) mw)->on_terminal_commit (t, tx, sz);
  }

# if VTE_CHECK_VERSION(0,48,0)
  void mw_on_terminal_spawn_callback (VteTerminal * t, GPid pid, GError * err, gpointer mw)
  {
    ((Astroid::MainWindow *) mw)->on_terminal_spawn_callback (t, pid, err);
  }
# endif
}
# endif

namespace Astroid {
  atomic<uint> MainWindow::nextid (0);
  int          Notebook::icon_size = 42;

  Notebook::Notebook () {
    set_scrollable (true);

    set_action_widget (&icons, Gtk::PACK_END);
    icons.show_all ();

    astroid->poll->signal_poll_state ().connect (
        sigc::mem_fun (this, &Notebook::poll_state_changed));
    signal_size_allocate ().connect (
        sigc::mem_fun (this, &Notebook::on_my_size_allocate));

    poll_state_changed (astroid->poll->get_poll_state());
  }

  void Notebook::on_my_size_allocate (Gtk::Allocation &) {
    icon_size = icons.get_height ();
  }

  void Notebook::poll_state_changed (bool state) {
    if (state && !spinner_on) {
      /* set up spinner for poll */
      spinner_on = true;
      icons.pack_end (poll_spinner, true, true, 5);
      icons.show_all ();
      poll_spinner.start ();
    } else if (!state && spinner_on) {
      poll_spinner.stop ();
      icons.remove (poll_spinner);
      spinner_on = false;
    }
  }

  void Notebook::add_widget (Gtk::Widget * w) {
    icons.pack_start (*w, true, true, 5);
    w->show ();
    icons.show_all ();
  }

  void Notebook::remove_widget (Gtk::Widget * w) {
    icons.remove (*w);
  }

  MainWindow::MainWindow () {
    id = ++nextid;

    actions = astroid->actions;

    LOG (debug) << "mw: init, id: " << id;

    set_title ("");
    set_default_size (1200, 800);

    path icon = Resource (false, "ui/icons/icon_color.png").get_path ();
    try {
      refptr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file (icon.c_str (), 42, 42, true);
      set_icon (pixbuf);
    } catch (Gdk::PixbufError &e) {
      LOG (error) << "mw: could not set icon: " << e.what ();
    }

    vbox.set_orientation (Gtk::ORIENTATION_VERTICAL);

    command.set_main_window (this);

    command.property_search_mode_enabled().signal_changed().connect(
        sigc::mem_fun (*this, &MainWindow::on_command_mode_changed)
        );

    vbox.pack_start (command, false, true, 0);
    vbox.pack_start (notebook, Gtk::PACK_EXPAND_WIDGET, 0);

    /* set up yes-no asker */
    rev_yes_no = Gtk::manage (new Gtk::Revealer ());
    rev_yes_no->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP);

    Gtk::Box * rh = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));

    label_yes_no = Gtk::manage (new Gtk::Label ());
    rh->pack_start (*label_yes_no, true, true, 5);
    label_yes_no->set_halign (Gtk::ALIGN_START);

    /* buttons */
    Gtk::Button * yes = Gtk::manage (new Gtk::Button("_Yes"));
    Gtk::Button * no  = Gtk::manage (new Gtk::Button("_No"));

    yes->set_use_underline (true);
    no->set_use_underline (true);

    rh->pack_start (*yes, false, true, 5);
    rh->pack_start (*no, false, true, 5);

    rev_yes_no->set_margin_top (0);
    rh->set_margin_bottom (3);
    rh->set_margin_top (3);

    rev_yes_no->add (*rh);
    rev_yes_no->set_reveal_child (false);
    vbox.pack_end (*rev_yes_no, false, true, 0);

    yes->signal_clicked().connect (sigc::mem_fun (this, &MainWindow::on_yes));
    no->signal_clicked().connect (sigc::mem_fun (this, &MainWindow::on_no));

    /* multi key handler */
    rev_multi = Gtk::manage (new Gtk::Revealer ());
    rev_multi->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP);

    Gtk::Box * rh_ = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));

    label_multi = Gtk::manage (new Gtk::Label ());
    rh_->pack_start (*label_multi, true, true, 5);
    label_multi->set_halign (Gtk::ALIGN_START);
    label_multi->set_line_wrap();

    rev_multi->set_margin_top (0);
    rh_->set_margin_bottom (5);
    rh_->set_margin_top (5);

    rev_multi->add (*rh_);
    rev_multi->set_reveal_child (false);
    vbox.pack_end (*rev_multi, false, true, 0);

    /* terminal */
# ifndef DISABLE_VTE
    rev_terminal = Gtk::manage (new Gtk::Revealer ());
    rev_terminal->set_transition_type (Gtk::REVEALER_TRANSITION_TYPE_SLIDE_UP);
    rev_terminal->set_reveal_child (false);
    vbox.pack_end (*rev_terminal, false, true, 0);

    terminal_cwd = bfs::current_path ();
# endif

    add (vbox);

    show_all_children ();

    /* connect keys */
    add_events (Gdk::KEY_PRESS_MASK);
    signal_key_press_event ().connect (
        sigc::mem_fun(*this, &MainWindow::on_key_press));

    /* got focus, grab keys */
    signal_focus_in_event ().connect (
        sigc::mem_fun (this, &MainWindow::on_my_focus_in_event));
    signal_focus_out_event ().connect (
        sigc::mem_fun (this, &MainWindow::on_my_focus_out_event));

    /* change page */
    notebook.signal_switch_page ().connect (
        sigc::mem_fun (this, &MainWindow::on_my_switch_page));

    /* catch update title events */
    update_title_dispatcher.connect (
        sigc::mem_fun (this, &MainWindow::on_update_title));

    /* catch window close events */
    signal_delete_event ().connect (
        sigc::mem_fun (this, &MainWindow::on_delete_event));

    /* register keys {{{ */
    keys.title = "MainWindow";

    keys.register_key ("q",
        "main_window.quit_ask",
        "Quit astroid",
        [&] (Key) {
          if (astroid->get_windows().size () > 1) {
            /* other windows, just close this one */
            quit ();
          } else {
            LOG (debug) << "really quit?: " << id;
            ask_yes_no ("Really quit?", [&](bool yes){ if (yes) quit (); });
          }
          return true;
        });

    keys.register_key ("Q",
        "main_window.quit",
        "Quit astroid (without asking)",
        [&] (Key) {
          quit ();
          return true;
        });

    keys.register_key ("l", { "b" },
        "main_window.next_page",
        "Next page",
        [&] (Key)
        {
          if (notebook.get_current_page () == (notebook.get_n_pages () - 1))
            set_active (0);
          else
            set_active (notebook.get_current_page() + 1);

          return true;
        });

    keys.register_key ("h", { "B" },
        "main_window.previous_page",
        "Previous page",
        [&] (Key) {
          if (notebook.get_current_page() == 0)
            set_active (notebook.get_n_pages()-1);
          else
            set_active (notebook.get_current_page() - 1);

          return true;
        });

    keys.register_key ("M-1",
        "main_window.jump_to_page_1",
        "Jump to page 1",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 1));

    keys.register_key ("M-2",
        "main_window.jump_to_page_2",
        "Jump to page 2",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 2));

    keys.register_key ("M-3",
        "main_window.jump_to_page_3",
        "Jump to page 3",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 3));

    keys.register_key ("M-4",
        "main_window.jump_to_page_4",
        "Jump to page 4",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 4));

    keys.register_key ("M-5",
        "main_window.jump_to_page_5",
        "Jump to page 5",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 5));

    keys.register_key ("M-6",
        "main_window.jump_to_page_6",
        "Jump to page 6",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 6));

    keys.register_key ("M-7",
        "main_window.jump_to_page_7",
        "Jump to page 7",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 7));

    keys.register_key ("M-8",
        "main_window.jump_to_page_8",
        "Jump to page 8",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 8));

    keys.register_key ("M-9",
        "main_window.jump_to_page_9",
        "Jump to page 9",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 9));

    keys.register_key ("M-0",
        "main_window.jump_to_page_0",
        "Jump to page 0",
        bind (&MainWindow::jump_to_page, this, std::placeholders::_1, 0));

    keys.register_key ("C-w", "main_window.close_page",
        "Close mode (or window if other windows are open)",
        [&] (Key) {
          close_page ();
          return true;
        });

    keys.register_key ("C-W", "main_window.close_page_force",
        "Force close mode (or window if other windows are open)",
        [&] (Key) {
          close_page (true);
          return true;
        });

    keys.register_key ("o",
        "main_window.search",
        "Search",
        [&] (Key) {
          enable_command (CommandBar::CommandMode::Search, "", NULL);
          return true;
        });

    keys.register_key ("M-s", "main_window.show_saved_searches",
        "Show saved searches",
        [&] (Key) {
          add_mode (new SavedSearches (this));
          return true;
        });

    keys.register_key ("L", "main_window.search_tag",
        "Search for tag:",
        [&] (Key) {
          enable_command (CommandBar::CommandMode::Search, "tag:", NULL);
          return true;
        });


    keys.register_key (Key (GDK_KEY_question), "main_window.show_help",
        "Show help",
        [&] (Key) {
          HelpMode * h = new HelpMode (this);
          h->show_help ((Mode*) notebook.get_nth_page (notebook.get_current_page()));
          add_mode (h);
          return true;
        });

    keys.register_key ("z", "main_window.show_log",
        "Show log window",
        [&] (Key) {
          add_mode (new LogView (this));
          return true;
        });

    keys.register_key ("u", "main_window.undo",
        "Undo last action",
        [&] (Key) {
          actions->undo ();
          return true;
        });

    keys.register_key ("m", "main_window.new_mail",
        "Compose new mail",
        [&] (Key) {
          add_mode (new EditMessage (this));
          return true;
        });

    keys.register_key ("P", "main_window.poll",
        "Start manual poll",
        [&] (Key) {
          astroid->poll->poll ();
          return true;
        });

    keys.register_key ("M-p", "main_window.toggle_auto_poll",
        "Toggle auto poll",
        [&] (Key) {
          astroid->poll->toggle_auto_poll ();
          return true;
        });

    keys.register_key ("C-c", "main_window.cancel_poll",
        "Cancel ongoing poll",
        [&] (Key) {
          astroid->poll->cancel_poll ();
          return true;
        });

    keys.register_key ("C-o", "main_window.open_new_window",
        "Open new main window",
        [&] (Key) {
          astroid->open_new_window ();
          return true;
        });

    keys.register_key ("\"", "main_window.clipboard",
        "Set target clipboard",
        [&] (Key k) {
          multi_key (clipboard, k);
          return true;
        });

    clipboard.register_key ("+", "main_window.clipboard.clipboard",
        "Set target clipboard to CLIPBOARD (default)",
        [&] (Key) {
          astroid->clipboard_target = GDK_SELECTION_CLIPBOARD;
          return true;
        });

    clipboard.register_key ("*", "main_window.clipboard.primary",
        "Set target clipboard to PRIMARY",
        [&] (Key) {
          astroid->clipboard_target = GDK_SELECTION_PRIMARY;
          return true;
        });

# ifndef DISABLE_VTE
    keys.register_key ("|", "main_window.open_terminal",
        "Open terminal",
        [&] (Key) {
          enable_terminal ();
          return true;
        });
# endif

    // }}}
  }

  bool MainWindow::jump_to_page (Key, int pg) {
    if (pg == 0) {
      pg = notebook.get_n_pages () - 1;
    } else {
      pg--;
    }

    LOG (debug) << "mw: swapping to page: " << pg;

    if (notebook.get_current_page () != pg) {
      set_active (pg);
    }

    return true;
  }

  bool MainWindow::is_current (Mode * m) {
    int i = notebook.get_current_page ();
    return (m == ((Mode *) notebook.get_nth_page (i)));
  }

  void MainWindow::set_title (ustring t) {

    ustring tt = ustring::compose( "Astroid (%1)", Astroid::version);

    if (t.size() > 0) {
      tt = t + " - " + tt;
    }

    Gtk::Window::set_title (tt);
  }

  void MainWindow::on_update_title () {
    int n = notebook.get_current_page();

    if (n >= 0 && n <= notebook.get_n_pages()-1) {
      set_title (((Mode*) notebook.get_nth_page(n))->get_label());
    } else {
      set_title ("");
    }
  }

  void MainWindow::enable_command (CommandBar::CommandMode m, ustring title, ustring cmd, function<void(ustring)> f) {
    ungrab_active ();
    command.enable_command (m, title, cmd, f);
    active_mode = Command;
    command.add_modal_grab ();
  }

  void MainWindow::enable_command (CommandBar::CommandMode m, ustring cmd, function<void(ustring)> f) {
    ungrab_active ();
    command.enable_command (m, cmd, f);
    active_mode = Command;
    command.add_modal_grab ();
  }

  void MainWindow::disable_command () {
    // hides itself
    command.remove_modal_grab();
    set_active (current);
    active_mode = Window;
  }

  void MainWindow::on_command_mode_changed () {
    if (!command.get_search_mode()) {
      disable_command ();
    }
  }

  /* Terminal {{{ */
# ifndef DISABLE_VTE
  void MainWindow::enable_terminal () {
    rev_terminal->set_reveal_child (true);
    ungrab_active ();
    active_mode = Terminal;

    vte_term = vte_terminal_new ();
    gtk_container_add (GTK_CONTAINER(rev_terminal->gobj ()), vte_term);
    rev_terminal->show_all ();

    /* load font settings */
    ustring font_desc_string = astroid->config("terminal").get<string> ("font_description");

    if (font_desc_string == "" || font_desc_string == "default") {
      auto settings = Gio::Settings::create ("org.gnome.desktop.interface");
      font_desc_string = settings->get_string ("monospace-font-name");
    }

    auto font_description = Pango::FontDescription (font_desc_string);

    /* https://developer.gnome.org/pangomm/stable/classPango_1_1FontDescription.html#details */
    if (font_description.get_size () == 0) {
      LOG (warn) << "terminal.font_description: no size specified, expect weird behaviour.";
    }

    vte_terminal_set_font (VTE_TERMINAL(vte_term), font_description.gobj ());
    vte_terminal_set_size (VTE_TERMINAL (vte_term), 1, astroid->config("terminal").get<int> ("height"));

    /* start shell */
    char * shell = vte_get_user_shell ();

    char * args[2] = { shell, NULL };
    char * envs[1] = { NULL };

    LOG (info) << "mw: starting terminal..: " << shell;

    if (!bfs::exists (terminal_cwd)) {
      terminal_cwd = bfs::current_path ();
    }

# if VTE_CHECK_VERSION(0,48,0)
    vte_terminal_spawn_async (VTE_TERMINAL(vte_term),
        VTE_PTY_DEFAULT,
        terminal_cwd.c_str(),
        args,
        envs,
        G_SPAWN_DEFAULT,
        NULL,
        NULL,
        NULL,
        -1,
        NULL,
        mw_on_terminal_spawn_callback,
        this);
# else
    GError * err = NULL;
    vte_terminal_spawn_sync (VTE_TERMINAL(vte_term),
        VTE_PTY_DEFAULT,
        terminal_cwd.c_str(),
        args,
        envs,
        G_SPAWN_DEFAULT,
        NULL,
        NULL,
        &terminal_pid,
        NULL,
        (err = NULL, &err));

    on_terminal_spawn_callback (VTE_TERMINAL(vte_term), terminal_pid, err);

# endif

    gtk_widget_grab_focus (vte_term);
    gtk_grab_add (vte_term);
  }

  void MainWindow::on_terminal_spawn_callback (VteTerminal * vte_term, GPid pid, GError * err)
  {
    if (err) {
      LOG (error) << "mw: terminal: " << err->message;
      disable_terminal ();
    } else {
      terminal_pid = pid;

      LOG (debug) << "mw: terminal started: " << terminal_pid;
      g_signal_connect (vte_term, "child-exited",
          G_CALLBACK (mw_on_terminal_child_exit),
          (gpointer) this);

      g_signal_connect (vte_term, "commit",
          G_CALLBACK (mw_on_terminal_commit),
          (gpointer) this);

    }
  }

  void MainWindow::disable_terminal () {
    LOG (info) << "mw: disabling terminal..";
    rev_terminal->set_reveal_child (false);
    set_active (current);
    active_mode = Window;
    gtk_grab_remove (vte_term);

    gtk_widget_destroy (vte_term);
  }

  void MainWindow::on_terminal_child_exit (VteTerminal *, gint) {
    LOG (info) << "mw: terminal exited (cwd: " << terminal_cwd.c_str () << ")";
    disable_terminal ();
  }

  void MainWindow::on_terminal_commit (VteTerminal *, gchar **, guint) {
    bfs::path pth = bfs::path(ustring::compose ("/proc/%1/cwd", terminal_pid).c_str ());

    if (bfs::exists (pth)) {
      terminal_cwd = bfs::canonical (pth);
    }
  }
# endif

  // }}}

  void MainWindow::quit () {
    LOG (info) << "mw: quit: " << id;
    in_quit = true;

    /* focus out */
    ungrab_active ();

    for (int c = notebook.get_n_pages () -1; c >= 0; c--) {
      /* ((Mode*) notebook.get_nth_page (c))->pre_close (); */
      del_mode (c);
    }

    close (); // Gtk::Window::close ()
  }

  bool MainWindow::on_delete_event (GdkEventAny *) {
    if (!in_quit)
      quit ();
    return false;
  }

  void MainWindow::on_yes () {
    answer_yes_no (true);
  }

  void MainWindow::on_no () {
    answer_yes_no (false);
  }


  bool MainWindow::mode_key_handler (GdkEventKey * event) {
    if (yes_no_waiting) {
      switch (event->keyval) {
        case GDK_KEY_Y:
        case GDK_KEY_y:
          answer_yes_no (true);
          return true;

        case GDK_KEY_Escape:
        case GDK_KEY_N:
        case GDK_KEY_n:
          answer_yes_no (false);
          return true;
      }

      /* swallow all other keys */
      return true;

    } else if (multi_waiting) {
      bool res = false;

      switch (event->keyval) {
        case GDK_KEY_Escape:
          {
            res = true;
          }
          break;

        default:
          {
            res = multi_keybindings.handle (event);
          }
          break;
      }

      /* close rev */
      multi_waiting = !res;

      if (res) {
        rev_multi->set_reveal_child (false);
        multi_keybindings.clear ();
      }

      return true; // swallow all keys

# ifndef DISABLE_VTE
    } else if (active_mode == Terminal) {
      return true;
    }
# else
    }
# endif

    return false;
  }

  bool MainWindow::on_key_press (GdkEventKey * event) {
    if (mode_key_handler (event)) return true;

    if (active_mode == Command) {
      command.command_handle_event (event);
      return true;
    }

    return keys.handle (event);
  }

  void MainWindow::add_mode (Mode * m) {
    m = Gtk::manage (m);

    Gtk::Widget * w = m;

    int n = notebook.insert_page ((*w), m->tab_label, current+1);

    notebook.show_all ();

    set_active (n);
  }

  void MainWindow::ask_yes_no (
      ustring question,
      std::function <void (bool)> closure)
  {
    using std::endl;
    LOG (info) << "mw: " << question;

    if (yes_no_waiting || multi_waiting) {
      LOG (warn) << "mw: already waiting for answer to previous question, discarding this one.";
      return;
    }

    yes_no_waiting = true;
    yes_no_closure = closure;

    rev_yes_no->set_reveal_child (true);
    label_yes_no->set_text (question + " [y/n]");
  }

  void MainWindow::answer_yes_no (bool yes) {
    using std::endl;
    rev_yes_no->set_reveal_child (false);

    if (yes) {
      LOG (info) << "mw: yes-no: got yes!";
    } else {
      LOG (info) << "mw: yes-no: got no :/";
    }

    if (yes_no_waiting) {
      if (yes_no_closure != NULL) {
        yes_no_closure (yes);
      }
    }

    yes_no_closure = NULL;
    yes_no_waiting = false;
  }

  bool MainWindow::multi_key (Keybindings & kb, Key /* k */)
  {
    using std::endl;
    LOG (info) << "mw: starting multi key.";

    if (yes_no_waiting || multi_waiting) {
      LOG (warn) << "mw: already waiting for answer to previous question, discarding this one.";
      return true;
    }

    multi_waiting = true;
    multi_keybindings = kb;

    rev_multi->set_reveal_child (true);
    label_multi->set_markup (kb.short_help ());

    return true;
  }

  void MainWindow::del_mode (int c) {
    // LOG (debug) << "mw: del mode: " << c;
    if (notebook.get_n_pages() > 1) {
      if (c >= 0) {
        if (c == 0) {
          set_active (c + 1);
        } else {
          set_active (c - 1);
        }

        ((Mode*) notebook.get_nth_page (c))->pre_close ();
        notebook.remove_page (c); // this should free the widget (?)
      } else {
        LOG (warn) << "mw: attempt to remove negative page";
      }
    } else {
      if (!in_quit && astroid->get_windows().size () > 1) {
        LOG (debug) << "mw: other windows available, closing this one.";
        quit ();
      }
    }
  }

  void MainWindow::close_page (Mode * m, bool force) {
    m->close (force);
  }

  void MainWindow::close_page (bool force) {
    int c = notebook.get_current_page ();

    ((Mode*) notebook.get_nth_page (c))->close (force);
  }

  void MainWindow::ungrab_active () {
    if (current >= 0) {
      if (notebook.get_n_pages() > current) {
        //LOG (debug) << "mw: release modal, from: " << current;
        ((Mode*) notebook.get_nth_page (current))->release_modal();
        active = false;
      }
    }
  }

  void MainWindow::on_my_switch_page (Gtk::Widget * /* w */, guint no) {
    grab_active (no);
  }

  void MainWindow::grab_active (int n) {
    //LOG (debug) << "mw: set active: " << n << ", current: " << current;

    ungrab_active ();

    //LOG (debug) << "mw: grab modal to: " << n;

    if (_has_focus) {
      /* we have focus */
      ((Mode*) notebook.get_nth_page (n))->grab_modal();
    } else {
      LOG (debug) << "mw: does not have focus, will not grab modal.";
    }

    current = n;
    active = true;

    on_update_title ();
  }

  void MainWindow::set_active (int n) {
    LOG (debug) << "mw: set active: " << n << ", current: " << current;

    if (n >= 0 && n <= notebook.get_n_pages()-1) {

      if (notebook.get_current_page() != n) {
        notebook.set_current_page (n);
      }

      grab_active (n);

    } else {
      // LOG (debug) << "mw: set active: page is out of range: " << n;
      on_update_title ();
    }
  }

  bool MainWindow::on_my_focus_in_event (GdkEventFocus * /* event */) {
    _has_focus = true;
    if (!in_quit && active) set_active (current);
    LOG (debug) << "mw: focus-in: " << id << " active: " << active << ", in_quit: " << in_quit;
    return false;
  }

  bool MainWindow::on_my_focus_out_event (GdkEventFocus * /* event */) {
    //LOG (debug) << "mw: focus-out: " << id;
    _has_focus = false;
    if ((current < notebook.get_n_pages ()) && (current >= 0))
      ((Mode*) notebook.get_nth_page (current))->release_modal();
    return false;
  }
}

