/*
 * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */
#include "gtk/lf_mforms.h"
#include "sqlide/grid_view.h"
#include <gtkmm/statusbar.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/fixed.h>
#include <gtkmm/main.h>

#include "main_form.h"
#include "active_label.h"
// the rest, backend, etc ...
#include "workbench/wb_context_ui.h"
#include "model/wb_model_diagram_form.h"
#include "workbench/wb_overview.h"
#include "model/wb_context_model.h"
#include "model/wb_overview_physical.h"
#include "linux_utilities/plugin_editor_base.h"
#include "linux_utilities/form_view_base.h"

#include "diagram_size_form.h"
#include "info_box.h"

#include "mdc.h"

#include "gtk_helpers.h"

#include "model_panel.h"
#include "model_diagram_panel.h"
#include "overview_panel.h"
#include "find_panel.h"

#include "linux_utilities/gtk_helpers.h"
#include "linux_utilities/image_cache.h"


#include "base/string_utilities.h"
#include "base/geometry.h"
#include "mforms/../gtk/lf_menubar.h"
#include "mforms/../gtk/lf_toolbar.h"
#include "mforms/../gtk/lf_form.h"

#include <base/log.h>
DEFAULT_LOG_DOMAIN("linux.ui")

using base::strfmt;



static void set_window_icons(Gtk::Window *window)
{
  std::vector<Glib::RefPtr<Gdk::Pixbuf> > icons;

  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-16.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-32.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-48.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-128.png", false));

  window->set_default_icon_list(icons);
}

static Gtk::Window *_dummy_window = 0;
static Gdk::Color _sys_selection_color;

//------------------------------------------------------------------------------
// @ctx is passed from Program class, see Program::Program
MainForm::MainForm(wb::WBContextUI* ctx)
  : _wbui_context(ctx),
    _exiting(false)
{
  _dummy_window = new Gtk::Window(Gtk::WINDOW_TOPLEVEL);
  gtk_widget_realize(GTK_WIDGET(_dummy_window->gobj()));

  setup_mforms_app();
  
  bec::GRTManager *grtm= _wbui_context->get_wb()->get_grt_manager();
  _ui= Gtk::Builder::create_from_file(grtm->get_data_file_path("wb.glade"));

 {
    Gtk::TreeView dummy_treeview;
    _dummy_window->add(dummy_treeview);
    dummy_treeview.show();
// There is some bug in Fedora 14 where this code causes other stuff later to crash
// The same code works just fine in Ubuntu, where the exact same version of gtk is shipped
//    _sys_selection_color= Gtk::RC::get_style(dummy_treeview)->get_bg(Gtk::STATE_SELECTED);
    GtkStyle *style = gtk_rc_get_style(GTK_WIDGET(dummy_treeview.gobj()));
    if (style)
      _sys_selection_color = Gdk::Color(&style->bg[GTK_STATE_SELECTED]);
  }

  get_mainwindow()->signal_delete_event().connect(sigc::mem_fun(this, &MainForm::close_window));
  get_mainwindow()->signal_set_focus().connect(sigc::mem_fun(this, &MainForm::on_focus_widget));
  get_mainwindow()->signal_configure_event().connect_notify(sigc::mem_fun(this, &MainForm::on_configure_window));
  get_mainwindow()->signal_window_state_event().connect_notify(sigc::mem_fun(this, &MainForm::on_window_state));
  get_mainwindow()->property_is_active().signal_changed().connect(sigc::mem_fun(this, &MainForm::is_active_changed));
  get_mainwindow()->set_events(Gdk::FOCUS_CHANGE_MASK);
  get_mainwindow()->signal_focus_in_event().connect(sigc::bind_return(sigc::hide(sigc::mem_fun(mforms::Form::main_form(), &mforms::Form::activated)), false));
  get_mainwindow()->signal_focus_out_event().connect(sigc::bind_return(sigc::hide(sigc::mem_fun(mforms::Form::main_form(), &mforms::Form::deactivated)), false));
  get_mainwindow()->set_title("MySQL Workbench");

  _model_panel= 0;

  get_upper_note()->signal_switch_page().connect(sigc::mem_fun(*this, &MainForm::switch_page));

  Gtk::Statusbar *status = 0;
  _ui->get_widget("statusbar1", status);
  if (status)
  {
    status->pack_end(_progress_bar, false, false);
    _progress_bar.show();
  }

  _gui_locked= false;

  //  switch_page(0, 0);
  mforms::gtk::FormImpl::init_main_form(this->get_mainwindow());

  base::NotificationCenter::get()->add_observer(this, "GNFormTitleDidChange");
  base::NotificationCenter::get()->add_observer(this, "GNFocusChanged");
}
//------------------------------------------------------------------------------
MainForm::~MainForm()
{

  _sig_flush_idle.disconnect();
  _sig_set_current_page.disconnect();
  _sig_close_tab.disconnect();
  _sig_change_status.disconnect();

  base::NotificationCenter::get()->remove_observer(this);

  const size_t n_slots = _slots.size();
  for (size_t i = 0; i < n_slots; ++i)
    _slots[i].disconnect();
  _slots.clear();


  std::map<mdc::CanvasView*,ModelDiagramPanel*>::iterator it;
  for (it = _diagram_panel_list.begin(); it != _diagram_panel_list.end(); it++)
    delete it->second;
  _diagram_panel_list.clear();

  notify_callbacks(); // disconnect all signals

  delete _model_panel;
}

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

void MainForm::register_form_view_factory(const std::string &name, FormViewFactory factory)
{
  _form_view_factories[name]= factory;
}

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

static void close_plugin(PluginEditorBase *editor, wb::WBContext *wb)
{
  wb->close_gui_plugin(dynamic_cast<GUIPluginBase*>(editor));
}

//------------------------------------------------------------------------------
bool MainForm::close_window(GdkEventAny *ev)
{/*
  if (_wbui_context->request_quit())
  {
    get_mainwindow()->hide();
    _wbui_context->perform_quit();
  }*/
  _wbui_context->get_wb()->quit_application();
  return true; // true means stop processing the event
}
//------------------------------------------------------------------------------
void MainForm::is_active_changed()
{
  static bool reentrancy_preventer = false;
  if (!reentrancy_preventer && get_mainwindow()->property_is_active())
  {
    //We sent notification on each focus_in of the main window,
    //because we have no reliable way to detect when the app itself got focus,
    //as opposed to focus switch between windows of the same app
    reentrancy_preventer = true;
    base::NotificationInfo info;
    base::NotificationCenter::get()->send("GNApplicationActivated", NULL, info);
    reentrancy_preventer = false;
  }
}
//------------------------------------------------------------------------------
void MainForm::on_focus_widget(Gtk::Widget *w)
{
  if (_ui)
  {
    void *data = 0;
    bec::UIForm* form = 0;
    
    while (!data && w)
    {
      data = w->get_data("uiform");
      if (data)
      {
        form = reinterpret_cast<bec::UIForm*>(data);
        break;
      }
      w = w->get_parent();
    }
    
    if (form)
      _wbui_context->set_active_form(form);
  }

  _wbui_context->get_command_ui()->revalidate_edit_menu_items();

  // for updating the Edit menu
 //!!! Gtk::Alignment* menu_holder;
 //!!! _menu_manager->rebuild(_ui->get("main_menu_box", &menu_holder));
}

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

void MainForm::on_configure_window(GdkEventConfigure *conf)
{
  if (get_mainwindow()->is_visible())
  {
    int x, y;
    get_mainwindow()->get_position(x, y);
    std::string geom = base::strfmt("%i %i %i %i", x, y, conf->width, conf->height);
    _wbui_context->get_wb()->save_state("MainWindow", "geometry", geom);
  }
}

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

void MainForm::on_window_state(GdkEventWindowState *conf)
{
  if (get_mainwindow()->is_visible() && (conf->changed_mask & GDK_WINDOW_STATE_MAXIMIZED))
  {
    if (conf->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
      _wbui_context->get_wb()->save_state("MainWindow", "geometry", std::string("maximized"));
    else
    {
      int x, y, width, height;
      get_mainwindow()->get_position(x, y);
      get_mainwindow()->get_size(width, height);
      std::string geom = base::strfmt("%i %i %i %i", x, y, width, height);
      _wbui_context->get_wb()->save_state("MainWindow", "geometry", geom);
    }
  }
}

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

void MainForm::setup_ui()
{  
  set_window_icons(get_mainwindow());

  register_commands();
  
  reset_layout();

  switch_page(0, 0);
}


//------------------------------------------------------------------------------
void MainForm::show()
{
  Gtk::Window *window = get_mainwindow();
  // restore saved size/pos
  std::string geom = _wbui_context->get_wb()->read_state("MainWindow", "geometry", std::string());
  if (!geom.empty())
  {
    int x, y, width, height;
    if (geom == "maximized")
      window->maximize();
    else if (sscanf(geom.c_str(), "%i %i %i %i", &x, &y, &width, &height) == 4)
    {
      window->move(x, y);
      window->resize(width, height);
    }
  }
  window->show();
}


//------------------------------------------------------------------------------
Gtk::Window* MainForm::get_mainwindow() const
{
  Gtk::Window *win = 0;
  _ui->get_widget("wb_main_window", win);

  return win;
}


Gtk::Notebook *MainForm::get_upper_note() const
{
  Gtk::Notebook *note= 0;
  _ui->get_widget("model_tabs", note);
  return note;
}


//------------------------------------------------------------------------------
static bool change_status(Gtk::Statusbar *status, const std::string& text)
{
  status->pop();
  status->push(text);
  return false;
}


void MainForm::show_status_text_becb(const std::string& text)
{
  Gtk::Statusbar *status = 0;

  _ui->get_widget("statusbar1", status);

  if (_wbui_context->get_wb()->get_grt_manager()->in_main_thread())
    change_status(status, text);
  else
    // execute when idle in case we're being called from the worker thread
    _sig_change_status = Glib::signal_idle().connect(sigc::bind<Gtk::Statusbar*,std::string>(sigc::ptr_fun(change_status), status, text));
}


//------------------------------------------------------------------------------
bool MainForm::quit_app_becb()
{
  // close the model 1st
  if (_wbui_context->get_wb()->can_close_document())
    _wbui_context->get_wb()->close_document();
  else
    return false;

  Gtk::Notebook *note = get_upper_note();
  for (int i= note->get_n_pages()-1; i > 0; --i)
  {
    // skip diagram tabs from check
    if (i < note->get_n_pages())
    {
      bool is_diagram = dynamic_cast<ModelDiagramPanel*>(reinterpret_cast<FormViewBase*>(note->get_nth_page(i)->get_data("FormViewBase")));
      if (!close_tab(note, note->get_nth_page(i)) && !is_diagram)
        return false;
    }
  }
  Gtk::Main::quit();

  return true;
}

//------------------------------------------------------------------------------
bool MainForm::show_progress_becb(const std::string& title, const std::string& status, float pct)
{
  if (pct < 0.0)
    pct = 0.0;

  _progress_bar.set_fraction(pct);
  _progress_bar.set_text(status);

  return true;
}


//------------------------------------------------------------------------------
NativeHandle MainForm::open_plugin_becb(bec::GRTManager* mgr, grt::Module *grtmodule,
                                        const std::string& shlib, const std::string& editor_class,
                                        grt::BaseListRef args, bec::GUIPluginFlags flags)
{
  GUIPluginCreateFunction create_function= 0;
  std::string path= grtmodule->path();
  std::string full_path= path.substr(0, path.rfind('/')+1)+shlib;

  // if not forcing a new window creation, check if there's an editor already open for it
  if (!(flags & bec::ForceNewWindowFlag) && !(flags & bec::StandaloneWindowFlag))
  {
    std::vector<NativeHandle> handles =
      mgr->get_plugin_manager()->get_similar_open_plugins(grtmodule, editor_class, args);

    if (!handles.empty())
    {
      GUIPluginBase* guiplugin = reinterpret_cast<GUIPluginBase*>(handles[0]);
      // try to reuse the plugin
      PluginEditorBase *editor= dynamic_cast<PluginEditorBase*>(guiplugin);
 
      if (flags & bec::StandaloneWindowFlag)
      {
        add_plugin_form(editor);
      }
      else
      {
        // add it to the bottom panel of the current tab
        bring_plugin_pane(editor);
      }

      if (editor && !editor->is_editing_live_object() && editor->can_close() && editor->switch_edited_object(mgr, args))
      {
        mgr->get_plugin_manager()->forget_gui_plugin_handle(handles[0]);
        return handles[0];
      }
    }
  }

  // lookup for the editor_class symbol (create<class>) in the shlib
  GModule *module= g_module_open(full_path.c_str(), (GModuleFlags)G_MODULE_BIND_LOCAL);
  if (!module)
  {
    g_warning("Could not open editor shared object '%s'", full_path.c_str());
    return 0;
  }

  if (!g_module_symbol(module, ("create"+editor_class).c_str(), (void**)&create_function)
      || !create_function)
  {
    g_warning("UI creation function '%s' not found in %s",
              ("create"+editor_class).c_str(), full_path.c_str());
    g_module_close(module);
    return 0;
  }

  // call the initializer with arguments
  GUIPluginBase *object= (*create_function)(grtmodule, mgr, args);
  if (!object)
  {
    g_warning("UI creation function from %s returned 0", full_path.c_str());
    g_module_close(module);
    return 0;
  }

  g_module_close(module);
  
  PluginEditorBase *editor= dynamic_cast<PluginEditorBase*>(object);
  if (editor)
  {
    if (flags & bec::StandaloneWindowFlag)
    {
      add_plugin_form(editor);
    }
    else
    {
      // add it to the bottom panel
      add_plugin_pane(editor);
    }
  }
  else
  {
    //object->execute();
  }

  // return the pointer
  return reinterpret_cast<NativeHandle>(object);
}


//------------------------------------------------------------------------------
void MainForm::show_plugin_becb(NativeHandle handle)
{
  GUIPluginBase *plugin= reinterpret_cast<GUIPluginBase*>(handle);

  PluginEditorBase *editor= dynamic_cast<PluginEditorBase*>(plugin);
 
  if (editor)
    bring_plugin_pane(editor);
  else
    g_message("Can't show plugin");
}


//------------------------------------------------------------------------------
void MainForm::hide_plugin_becb(NativeHandle handle)
{
//  reinterpret_cast<GUIPluginBase*>(handle)->hide_plugin();
}


static mforms::CodeEditor *get_focused_code_editor(Gtk::Window *w)
{
  Gtk::Widget *focused = w->get_focus();
  if (focused)
    return dynamic_cast<mforms::CodeEditor*>(reinterpret_cast<mforms::View*>(focused->get_data("mforms")));
  return NULL;
}


void MainForm::call_find()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
  {
    // generic handling for Scintilla editors
    editor->show_find_panel(false);
    return;
  }
  if (_wbui_context->get_active_context(true) != WB_CONTEXT_QUERY)
  {
    if (validate_find_replace())
      call_find_replace();
  }

  FormViewBase *form = reinterpret_cast<FormViewBase*>(get_upper_note()->get_nth_page(get_upper_note()->get_current_page())->get_data("FormViewBase"));
  if (form)
  {
    mforms::ToolBar *toolbar = form->get_form()->get_toolbar();
    if (toolbar)
    {
      Gtk::Entry *entry = dynamic_cast<Gtk::Entry*>(mforms::widget_for_toolbar_item_named(toolbar, "find"));
      if (entry)
      {
        entry->grab_focus();
        form->find_text(entry->get_text());
      }
    }
  }
}

void MainForm::call_find_replace()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
  {
    // generic handling for Scintilla editors
    editor->show_find_panel(true);
    return;
  }

//  FormViewBase *fview = get_active_pane();
//  if (fview && !fview->show_find(true))
//     return;
}


void MainForm::call_undo()
{
    mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
    if (editor)
    {
        editor->undo();
        return;
    }

    if (_wbui_context->get_active_main_form() && _wbui_context->get_active_main_form()->can_undo())
        _wbui_context->get_active_main_form()->undo();
}


void MainForm::call_redo()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
  {
    editor->redo();
    return;
  }

  if (_wbui_context->get_active_main_form() && _wbui_context->get_active_main_form()->can_redo())
    _wbui_context->get_active_main_form()->redo();
}


void MainForm::call_copy()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  Gtk::Widget *focused= get_mainwindow()->get_focus();

  if (editor)
  {
    editor->copy();
    return;
  }



  GridView *gv = NULL;
  if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->copy_clipboard();
  else if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    _wbui_context->get_active_form()->copy();
  else if ((gv = dynamic_cast<GridView*>(focused)))
    gv->copy();


}


void MainForm::call_cut()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  Gtk::Widget *focused= get_mainwindow()->get_focus();

  if (editor)
  {
    editor->cut();
    return;
  }

  if (dynamic_cast<Gtk::Editable*>(focused))
    dynamic_cast<Gtk::Editable*>(focused)->cut_clipboard();
  else if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_cut())
    _wbui_context->get_active_form()->cut();
}


void MainForm::call_paste()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  Gtk::Widget *focused= get_mainwindow()->get_focus();

  if (editor)
  {
    editor->paste();
    return;
  }

  if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->paste_clipboard();
  else if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_paste())
    _wbui_context->get_active_form()->paste();
}


void MainForm::call_delete()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  Gtk::Widget *focused= get_mainwindow()->get_focus();

  if (editor)
  {
    editor->do_delete();
    return;
  }

  if (dynamic_cast<Gtk::Editable*>(focused))
    dynamic_cast<Gtk::Editable*>(focused)->delete_selection();
  else if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_delete())
    _wbui_context->get_active_form()->delete_selection();
}

void MainForm::call_select_all()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  Gtk::Widget *focused= get_mainwindow()->get_focus();
  if (editor)
  {
    editor->select_all();
  }
  else if (dynamic_cast<Gtk::Editable*>(focused))
    dynamic_cast<Gtk::Editable*>(focused)->select_region(0, -1);
  else if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_select_all())
    _wbui_context->get_active_form()->select_all();
}

bool MainForm::validate_find()
{
  std::string context = _wbui_context->get_active_context();
  
  if (context == WB_CONTEXT_MODEL || context == WB_CONTEXT_QUERY || context == WB_CONTEXT_PHYSICAL_OVERVIEW)
    return true;
  return validate_find_replace();
}

bool MainForm::validate_undo()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_undo();

  bec::UIForm *form = _wbui_context->get_active_main_form();
  if (form)
    return form->can_undo();
  
  return false;
}

bool MainForm::validate_redo()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_redo();

  bec::UIForm *form = _wbui_context->get_active_main_form();
  if (form)
    return form->can_redo();
  
  return false;
}

bool MainForm::validate_copy()
{
  bool ret = false;
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_copy();

  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e))
      ret = true;
    else
    {
      GridView *gv = dynamic_cast<GridView*>(get_mainwindow()->get_focus());
      if (gv)
        ret = true;
    }
  }

  return ret;
}

bool MainForm::validate_cut()
{
  bool ret = false;
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_cut();

  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e) && edit->get_editable())
      ret = true;
  }
  return ret;
}

bool MainForm::validate_paste()
{
  bool ret = false;
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_paste();

  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_paste())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    if (edit && edit->get_editable())
      ret = true;
  }

  return ret;
}

bool MainForm::validate_delete()
{
  bool ret = false;
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
  if (editor)
    return editor->can_delete();

  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_delete())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e) && edit->get_editable())
      ret = true;
  }
  return ret;
}


bool MainForm::validate_select_all()
{
  bool ret = false;
  Gtk::Widget *focused= get_mainwindow()->get_focus();

  if (focused)
  {
    mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
    if (editor)
      return true;
  }

  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_select_all())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(focused);
    if (edit)
      ret = true;
  }

  return ret;
}

bool MainForm::validate_find_replace()
{
  mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow());
 
  if (editor)
    return true;

  return false;
}


void MainForm::register_commands()
{
  std::list<std::string> commands;

  commands.push_back("diagram_size");
  //  commands.push_back("view_model_navigator");
  //  commands.push_back("view_catalog");
  //  commands.push_back("view_layers");
  //  commands.push_back("view_user_datatypes");
  //  commands.push_back("view_object_properties");
  //  commands.push_back("view_object_description");
  //  commands.push_back("view_undo_history");
  commands.push_back("reset_layout");
  commands.push_back("wb.page_setup");
  commands.push_back("closetab");
  commands.push_back("close_tab");
  commands.push_back("close_editor");
  commands.push_back("wb.next_tab");
  commands.push_back("wb.back_tab");
  commands.push_back("wb.next_query_tab");
  commands.push_back("wb.back_query_tab");
  //  commands.push_back("help_index");
  //  commands.push_back("help_version_check");
  commands.push_back("wb.sidebarHide");

  // SQLIDE
  commands.push_back("wb.toggleSidebar");
  commands.push_back("wb.toggleSecondarySidebar");
  commands.push_back("wb.toggleOutputArea");


  _wbui_context->get_command_ui()->add_frontend_commands(commands);

  _wbui_context->get_command_ui()->add_builtin_command("find_replace",
                                               make_slot(&MainForm::call_find_replace),
                                               make_slot(&MainForm::validate_find_replace));
  _wbui_context->get_command_ui()->add_builtin_command("find",
                                               make_slot(&MainForm::call_find),
                                               make_slot(&MainForm::validate_find));
  _wbui_context->get_command_ui()->add_builtin_command("undo",
                                               make_slot(&MainForm::call_undo),
                                               make_slot(&MainForm::validate_undo));
  _wbui_context->get_command_ui()->add_builtin_command("redo",
                                               make_slot(&MainForm::call_redo),
                                               make_slot(&MainForm::validate_redo));
  _wbui_context->get_command_ui()->add_builtin_command("copy",
                                               make_slot(&MainForm::call_copy),
                                               make_slot(&MainForm::validate_copy));
  _wbui_context->get_command_ui()->add_builtin_command("cut",
                                               make_slot(&MainForm::call_cut),
                                               make_slot(&MainForm::validate_cut));
  _wbui_context->get_command_ui()->add_builtin_command("paste",
                                               make_slot(&MainForm::call_paste),
                                               make_slot(&MainForm::validate_paste));
  _wbui_context->get_command_ui()->add_builtin_command("delete",
                                               make_slot(&MainForm::call_delete),
                                               make_slot(&MainForm::validate_delete));
  _wbui_context->get_command_ui()->add_builtin_command("selectAll",
                                               make_slot(&MainForm::call_select_all),
                                               make_slot(&MainForm::validate_select_all));

}

//------------------------------------------------------------------------------
void MainForm::perform_command_becb(const std::string& command)
{
  if (command == "reset_layout")
    reset_layout();
  else if (command == "diagram_size")
    show_diagram_options();
  else if (command == "wb.page_setup")
    show_page_setup();
  else if (command == "closetab")
    close_active_tab();
  else if (command == "close_tab")
    close_main_tab();
  else if (command == "close_editor")
    close_inner_tab();
  else if (command == "wb.next_tab")
  {
    int i = get_upper_note()->get_current_page();
    get_upper_note()->next_page();
    if (i == get_upper_note()->get_current_page())
      get_upper_note()->set_current_page(0);
  }
  else if (command == "wb.back_tab")
  {
    int i = get_upper_note()->get_current_page();
    get_upper_note()->prev_page();
    if (i == get_upper_note()->get_current_page())
      get_upper_note()->set_current_page(get_upper_note()->get_n_pages()-1);
  }
  /*
   // Help
   else if (command == "help_index")
   Help.ShowHelp(null, System.IO.Path.Combine(Application.StartupPath, "MySQLWorkbench.chm"));
   else if (command == "help_version_check")
   Program.CheckForNewVersion();
   */
  else if (command == "wb.toggleSidebar" || command == "wb.toggleSecondarySidebar")
  {
    FormViewBase *form = get_active_pane();
    if (form)
      form->perform_command(command);
  }
  else
  {
    FormViewBase *form = get_active_pane();
    if (!form || !form->perform_command(command))
      g_message("Command '%s' not handled!\n", command.c_str());
  }
}

//------------------------------------------------------------------------------
mdc::CanvasView* MainForm::create_view_becb(const model_DiagramRef &diagram)
{
  ModelDiagramPanel *model_panel= Gtk::manage(ModelDiagramPanel::create(_wbui_context));

  model_panel->set_close_editor_callback(sigc::bind(sigc::ptr_fun(close_plugin), _wbui_context->get_wb()));

  _signal_close_editor.connect(sigc::hide_return(sigc::mem_fun(model_panel, &ModelDiagramPanel::close_editors_for_object)));

  model_panel->init(diagram.id());

  TabStateInfo tabState;
  if (!model_panel->get_diagram_form()->is_closed())
  {
    if (model_ModelRef::cast_from(diagram->owner())->currentDiagram() == diagram)
      tabState = TabOpenActive;
    else
      tabState = TabOpen;
  }
  else
  {
    tabState = TabClosed;
  }

  add_form_pane(model_panel, tabState);
 // model_panel->set_data("model_panel", model_panel);
  mdc::CanvasView* view = model_panel->get_canvas();
  _diagram_panel_list.insert(std::pair<mdc::CanvasView*, ModelDiagramPanel*>(view, model_panel));

  return view;
}


//------------------------------------------------------------------------------
void MainForm::destroy_view_becb(mdc::CanvasView* view)
{
  Gtk::Notebook *note= get_upper_note();

  if (!_wbui_context->get_wb()->get_grt_manager()->in_main_thread())
    G_BREAKPOINT();
  
  for (int c= note->get_n_pages(), i= 1; i < c; i++)
  {
    FormViewBase *form = reinterpret_cast<FormViewBase*>(note->get_nth_page(i)->get_data("FormViewBase"));
    ModelDiagramPanel *model_panel = dynamic_cast<ModelDiagramPanel*>(form);
    //ModelDiagramPanel *model_panel = reinterpret_cast<ModelDiagramPanel*>(note->get_nth_page(i)->get_data("model_panel"));

    if (model_panel && model_panel->get_canvas() == view)
    {
      note->remove_page(i);
      break;
    }
  }

  std::map<mdc::CanvasView*,ModelDiagramPanel*>::iterator it;
  it = _diagram_panel_list.find(view);
  if (it != _diagram_panel_list.end())
  {
    delete it->second;
    _diagram_panel_list.erase(it);
  }

}

//------------------------------------------------------------------------------
void MainForm::switched_view_becb(mdc::CanvasView* view)
{
  Gtk::Notebook *note= get_upper_note();

  bec::UIForm *view_form= _wbui_context->get_wb()->get_model_context()->get_diagram_form(view);

  for (int c= note->get_n_pages(), i= 1; i < c; i++)
  {
    if (note->get_nth_page(i)->get_data("uiform") == view_form)
    {
      note->get_nth_page(i)->show();
      note->set_current_page(i);
      break;
    }
  }
}

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

void MainForm::create_main_form_view_becb(const std::string &name, boost::shared_ptr<bec::UIForm> form)
{
  FormViewBase *view;
  
  if (_form_view_factories.find(name) != _form_view_factories.end())
  {
    view= _form_view_factories[name](form);

    add_form_pane(view, TabOpenActive);
  }
  else
  {
    throw std::runtime_error("Form type not supported.");
  }
}

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

void MainForm::destroy_main_form_view_becb(bec::UIForm* form)
{
}


//------------------------------------------------------------------------------
void MainForm::tool_changed_becb(mdc::CanvasView* view)
{
  ModelDiagramPanel *panel= get_panel_for_view(view);
  if (panel)
  {
    // update cursor in canvas
    panel->update_tool_cursor();
  }
}


//------------------------------------------------------------------------------
void MainForm::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info)
{

  if (name == "GNFormTitleDidChange")
  {
    std::string form_id = info["form"];
    Gtk::Notebook *note= get_upper_note();
    for (int c= note->get_n_pages(), i= 1; i < c; i++)
    {
      FormViewBase *form = reinterpret_cast<FormViewBase*>(note->get_nth_page(i)->get_data("FormViewBase"));
      bec::UIForm *fui = form ? form->get_form() : 0;
      if (fui && fui->form_id() == form_id)
      {
        form->signal_title_changed().emit(info["title"]);
        break;
      }
    }
  }
  else if (name == "GNFocusChanged")
  {
    _wbui_context->get_command_ui()->revalidate_edit_menu_items();
  }
}


const char* RefreshTypeStr[] = {
  "RefreshNeeded",
    "RefreshNothing",
    "RefreshSchemaNoReload",
    "RefreshNewDiagram",
    "RefreshSelection",
    "RefreshMessages",
    "RefreshCloseEditor",
    "RefreshNewModel",
    "RefreshOverviewNodeInfo",
    "RefreshOverviewNodeChildren",
    "RefreshDocument",
    "RefreshCloseDocument",
    "RefreshZoom",
    "RefreshTimer",
    "RefreshFinishEdit"
};

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


void MainForm::refresh_gui_becb(wb::RefreshType type, const std::string& arg_id, NativeHandle arg_ptr)
{
  if (_exiting)
    return;

  // _overview is OverviewPanel
  switch (type)
  {
  case wb::RefreshNeeded:
    {
      _sig_flush_idle = Glib::signal_idle().connect(sigc::bind_return(sigc::mem_fun(_wbui_context->get_wb(), &wb::WBContext::flush_idle_tasks), false));
      break;
    }

  case wb::RefreshCloseDocument:
    {
      prepare_close_document();

      _wbui_context->get_wb()->flush_idle_tasks();

      _wbui_context->get_wb()->close_document_finish();

      handle_model_closed();
    }
  case wb::RefreshNothing:
    break;

  case wb::RefreshSchemaNoReload:
    {
      ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel*>(get_active_pane());
      if (panel)
        panel->refresh_catalog(false);
      break;
    }

  case wb::RefreshNewDiagram:
    {
      ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel*>(reinterpret_cast<FormViewBase*>(arg_ptr));
      if (panel)
      panel->setup_navigator();
//         switch_to_diagram(panel);
      break;
    }

  case wb::RefreshOverviewNodeInfo:
  {
    if (_model_panel && (arg_ptr == dynamic_cast<bec::UIForm*>(_model_panel->get_overview()->get_be()) || arg_ptr == NULL))
    {
      _model_panel->get_overview()->refresh_node(bec::NodeId(arg_id));
    }
    break;
  }
  
  case wb::RefreshOverviewNodeChildren:
  {
    if (_model_panel && (arg_ptr == dynamic_cast<bec::UIForm*>(_wbui_context->get_physical_overview()) || arg_ptr == NULL))
    {
      if (arg_id.empty())
      {
        _model_panel->get_overview()->show();
        _model_panel->get_overview()->rebuild_all();
      }
      _model_panel->get_overview()->refresh_children(bec::NodeId(arg_id));
    }
    break;
  }
  
  case wb::RefreshDocument:
    {
      get_mainwindow()->set_title(_wbui_context->get_title());

      ///_model_sidebar.refresh_usertypes();
      break;
    }
  case wb::RefreshSelection:
    {
      FormViewBase *form = get_active_pane();
      
      if (form == _model_panel)
      {
        if (_model_panel)
          _model_panel->selection_changed();
      }
      else if (dynamic_cast<ModelDiagramPanel*>(form))
      {
        ModelDiagramPanel *model = dynamic_cast<ModelDiagramPanel*>(form);
        model->selection_changed();
      }
      break;
    }

  case wb::RefreshNewModel:
    handle_model_created();
    _wbui_context->get_wb()->new_model_finish();
    break;
    
  case wb::RefreshZoom:
    {
      ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel*>(get_active_pane());
      if (panel)
        panel->refresh_zoom();
      break;
    }

  case wb::RefreshCloseEditor:
    _signal_close_editor.emit(arg_id);
    break;

  case wb::RefreshTimer:
    update_timer();
    break;

  case wb::RefreshFinishEdits:
    {
      Gtk::Widget *focused= get_mainwindow()->get_focus();
      if (focused)
      {
        if (dynamic_cast<Gtk::Entry*>(focused))
        {
          Gtk::TreeView *tree = dynamic_cast<Gtk::TreeView*>(focused->get_parent());
          if (!tree && focused->get_parent())
            tree = dynamic_cast<Gtk::TreeView*>(focused->get_parent()->get_parent());
          
          if (tree)
          {
            Gtk::TreePath path;
            Gtk::TreeViewColumn *column = 0;
            tree->get_cursor(path, column);
            if (column)
            {
              // this is not working...
              std::vector<Gtk::CellRenderer*> rends(column->get_cell_renderers());
              for (std::vector<Gtk::CellRenderer*>::iterator iter = rends.begin(); iter != rends.end(); ++iter)
              {
                (*iter)->stop_editing(true);
              }
              // fallback
              tree->set_cursor(path, *column, false);
            }
          }
        }
      }
    }
    break;
  }
}


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

void MainForm::handle_model_created()
{
  wb::OverviewBE* overview_be = _wbui_context->get_physical_overview();
  // Create the model overview panel
  _model_panel= ModelPanel::create(_wbui_context, overview_be);
  _model_panel->set_close_editor_callback(sigc::bind(sigc::ptr_fun(close_plugin), _wbui_context->get_wb()));
  
  _model_panel->get_overview()->get_be()->set_frontend_data(dynamic_cast<FormViewBase*>(_model_panel));

  _signal_close_editor.connect(sigc::hide_return(sigc::mem_fun(_model_panel, &ModelPanel::close_editors_for_object)));

  add_form_pane(_model_panel, TabOpenActive);

  _model_panel->get_overview()->rebuild_all();
}
//------------------------------------------------------------------------------
void MainForm::handle_model_closed()
{
  if (_model_panel)
    get_upper_note()->remove_page(*_model_panel);
  delete _model_panel;
  _model_panel= 0;
}

//------------------------------------------------------------------------------
void MainForm::lock_gui_becb(bool lock)
{
  _gui_locked= lock;
}

//------------------------------------------------------------------------------
void MainForm::switch_page(GtkNotebookPage*, guint pagenum)
{
  if (_gui_locked)
    return;
  Gtk::Notebook *note= get_upper_note();
  Gtk::Widget *page = note->get_nth_page(pagenum);
  if (page)
  {
    page->show();

    bec::UIForm* uiform = static_cast<bec::UIForm*>(page->get_data("uiform"));
    _wbui_context->set_active_form(uiform);

    FormViewBase *form = static_cast<FormViewBase*>(page->get_data("FormViewBase"));
    if (form)
      form->on_activate();
  }
  _wbui_context->get_command_ui()->revalidate_edit_menu_items();
}


//==============================================================================
ModelDiagramPanel *MainForm::get_panel_for_view(mdc::CanvasView *view)
{
  bec::UIForm *form;
  if ((form= _wbui_context->get_wb()->get_model_context()->get_diagram_form(view)))
    return dynamic_cast<ModelDiagramPanel*>(static_cast<FormViewBase*>(form->get_frontend_data()));

  return 0;
}

void MainForm::add_plugin_pane(PluginEditorBase *pane)
{
  // locate the active main tab
  Gtk::Notebook *note = get_upper_note(); 
  int pagenum= note->get_current_page();

  if (pagenum < 0)
  {
    g_warning("Cannot add plugin with no active tab");
    return;
  }
  
  Gtk::Widget *page = note->get_nth_page(pagenum);
  if (page)
  {
    FormViewBase *form = reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));
    
    if (form)
    {
      // add the editor to the active tab
      form->add_plugin_tab(pane);

      pane->set_data("ContainerForm", form);
    }
  }
}


void MainForm::bring_plugin_pane(PluginEditorBase *pane)
{
  // locate the active main tab
  Gtk::Notebook *note = get_upper_note(); 
  int pagenum= note->get_current_page();

  if (pagenum < 0)
  {
    g_warning("Cannot add plugin with no active tab");
    return;
  }

  FormViewBase *old_form = reinterpret_cast<FormViewBase*>(pane->get_data("ContainerForm"));

  Gtk::Widget *page = note->get_nth_page(pagenum);
  if (page)
  {
    FormViewBase *form = reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));

    if (form && old_form != form && old_form->get_form()->get_form_context_name() != WB_CONTEXT_QUERY)
    {
      pane->reference();
      if (old_form)
        old_form->remove_plugin_tab(pane);
      // remove from where it is now
      // add the editor to the active tab
      form->add_plugin_tab(pane);
      pane->unreference();
      pane->set_data("ContainerForm", form);
    }
  }
}



static bool close_plugin_form(GdkEventAny *ev, PluginEditorBase *frame)
{
  //wnd window is deleted inside closE_live_object_editor
  frame->close_live_object_editor();
  return true;
}

void MainForm::add_plugin_form(PluginEditorBase *frame)
{
  Gtk::Window *window= new Gtk::Window();
  window->add(*frame);
  window->set_title(frame->get_title());
  window->signal_delete_event().connect(sigc::bind(sigc::ptr_fun(close_plugin_form), frame));
  frame->signal_title_changed().connect(sigc::mem_fun(window, &Gtk::Window::set_title));
  int width= 800;
  int height= 600;
  window->resize(width, height);
  window->show_all();
}


void MainForm::close_main_tab()
{
  Gtk::Widget *focused= get_mainwindow()->get_focus();
  Gtk::Notebook *upper= get_upper_note();

  // go up the hierarchy to see if a child of the upper notebooks is focused
  while (focused && focused != upper)
    focused= focused->get_parent();

  if (focused)
  {
    int curpagenum = upper->get_current_page();
    if (curpagenum >= 0)
    {
      Gtk::Widget *page = upper->get_nth_page(curpagenum);
      bec::UIForm *form = reinterpret_cast<bec::UIForm*>(page->get_data("uiform"));
      if (form && form->get_form_context_name() == "home")
        return;
      
      close_tab(upper, page);
    }
  }
}


void MainForm::close_inner_tab()
{
  Gtk::Widget *focused= get_mainwindow()->get_focus();
  Gtk::Notebook *upper= get_upper_note();

  // go up the hierarchy to see if a child of the upper notebooks is focused
  while (focused && focused != upper)
    focused= focused->get_parent();

  if (focused)
  {
    int curpagenum = upper->get_current_page();
    if (curpagenum >= 0)
    {
      Gtk::Widget *page = upper->get_nth_page(curpagenum);
      FormViewBase *panel = reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));
      bec::UIForm *form = reinterpret_cast<bec::UIForm*>(page->get_data("uiform"));
      if (form && form->get_form_context_name() == "home")
        return;
      
      if (panel)
        panel->close_focused_tab();
    }
  }
}


void MainForm::close_active_tab()
{  
  Gtk::Widget *focused= get_mainwindow()->get_focus();
  Gtk::Notebook *upper= get_upper_note();

  // go up the hierarchy to see if a child of the upper notebooks is focused
  while (focused && focused != upper)
    focused= focused->get_parent();

  if (focused)
  {
    int curpagenum = upper->get_current_page();
    if (curpagenum >= 0)
    {
      Gtk::Widget *page = upper->get_nth_page(curpagenum);
      FormViewBase *panel = reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));
      bec::UIForm *form = reinterpret_cast<bec::UIForm*>(page->get_data("uiform"));
      if (form && form->get_form_context_name() == "home")
        return;
      
      if (panel)
      {
        if (!panel->close_focused_tab())
          close_tab(upper, page);
      }
      else
        close_tab(upper, page);
    }
  }
}


static bool note_contains_page(Gtk::Notebook *note, Gtk::Widget *page)
{
  for (int i = note->get_n_pages()-1; i >= 0; --i)
    if (note->get_nth_page(i) == page)
      return true;
  return false;
}


bool MainForm::close_tab(Gtk::Notebook *note, Gtk::Widget *page)
{
  // on_close should return true if the form should be closed/removed
  mforms::AppView *app_view = reinterpret_cast<mforms::AppView*>(page->get_data("AppView"));
  FormViewBase *form = reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));

  if (app_view || form)
  {
    if ((app_view && app_view->on_close()) || (form && form->on_close()))
    {
       // can't use page_num, because it will dereference the pointer and it could be deleted (shouldn't be, but it is happening)
      //if (note->page_num(*page) >= 0)
      if (note_contains_page(note, page))
      {
        if (form)
          form->dispose();

        note->remove_page(*page);
        page->unreference();

        if (form == _model_panel)
          _model_panel = 0;
      }
    }
    else
    {
      return false;
    }
  }
  else
    page->hide();

  bool visible= false;
  for (int c= note->get_n_pages(), i= 0; i < c; i++)
  {
    if (note->get_nth_page(i)->is_visible())
    {
      visible= true;
      break;
    }
  }

  if (!visible)
    note->hide();

  return true;
}


void MainForm::append_tab_page(Gtk::Notebook *note, Gtk::Widget *widget, const std::string &title,
                                TabStateInfo tabState,
                               ActiveLabel **title_label_ret)
{
  // Make a lookup of existing page with the same content
  bool already_have_page = false;
  for (int i = note->get_n_pages() - 1; i >= 0; --i)
  {
    if (note->get_nth_page(i) == widget)
    {
      already_have_page = true;
      break;
    }
  }

  if (!already_have_page)
  {
    ActiveLabel* label = Gtk::manage(new ActiveLabel(title, sigc::hide_return(sigc::bind(sigc::mem_fun(this, &MainForm::close_tab), note, widget))));

    if (title_label_ret)
      *title_label_ret = label;

    mforms::Menu* menu = init_tab_menu(widget);
    label->set_menu(menu, true);
    menu->set_handler(sigc::bind(sigc::mem_fun(this, &MainForm::tab_menu_handler), label, widget, note));

    if (g_object_is_floating(G_OBJECT(widget->gobj())))
      g_object_ref_sink(G_OBJECT(widget->gobj()));
    note->append_page(*widget, *label);

    if (tabState == TabOpenActive)
    {
      // switch the current page when we're in idle
      _sig_set_current_page = Glib::signal_idle().connect(sigc::bind_return(sigc::bind(sigc::mem_fun(note, &Gtk::Notebook::set_current_page),
                                                               note->get_n_pages()-1), false));
    }
    if (tabState != TabClosed)
      note->show();
  }
}

//------------------------------------------------------------------------------
mforms::Menu* MainForm::init_tab_menu(Gtk::Widget *widget)
{
  {
    mforms::Menu *m = new mforms::Menu();
    m->add_item("Close Tab", "close tab");
    m->add_item("Close Other Tabs", "close other tabs");
    m->add_item("Close Other Tabs of This Type", "close similar");
    return m;
  }
}

//------------------------------------------------------------------------------
void MainForm::tab_menu_handler(const std::string& action, ActiveLabel* label, Gtk::Widget* widget, Gtk::Notebook* note)
{
  bec::UIForm *uiform = (bec::UIForm*)widget->get_data("uiform");
  if (uiform)
  {
    if (action == "close tab")
    {
      _sig_close_tab = Glib::signal_idle().connect(sigc::bind_return(sigc::bind(sigc::mem_fun(this, &MainForm::close_tab), note, widget), false));
    }
    else if (action == "close other tabs")
    {
      const int note_size = note->get_n_pages();
      for (int i = note_size - 1; i >= 0; --i)
      {
        Gtk::Widget *cont = note->get_nth_page(i);
        bec::UIForm* cont_uiform = (bec::UIForm*)cont->get_data("uiform");
        const std::string uiform_name = cont_uiform->get_form_context_name();
        if (uiform_name != "home" && cont_uiform != uiform)
        {
          close_tab(note, cont);
        }
      }
    }
    else if (action == "close similar")
    {
      const int note_size = note->get_n_pages();
      const std::string type = uiform->get_form_context_name();
      for (int i = note_size - 1; i >= 0; --i)
      {
        Gtk::Widget *cont = note->get_nth_page(i);
        bec::UIForm* cont_uiform = (bec::UIForm*)cont->get_data("uiform");
        const std::string uiform_type = cont_uiform->get_form_context_name();
        if (uiform_type != "home" && cont != widget && type == uiform_type)
        {
          close_tab(note, cont);
        }
      }
    }
  }
}


FormViewBase *MainForm::get_active_pane()
{
  int p = get_upper_note()->get_current_page();
  if (p >= 0)
  {
    Gtk::Widget *page = get_upper_note()->get_nth_page(p);
    if (page)
      return reinterpret_cast<FormViewBase*>(page->get_data("FormViewBase"));
  }
  return 0;
}


void MainForm::add_form_pane(FormViewBase *panel, TabStateInfo tabState)
{
  panel->get_panel()->set_data("FormViewBase", panel);
  panel->get_panel()->set_data("uiform", dynamic_cast<bec::UIForm*>(panel->get_form()));

  ActiveLabel *label = 0;
  append_tab_page(get_upper_note(), panel->get_panel(), panel->get_title(), tabState, &label);
  if (tabState == TabClosed)
    panel->get_panel()->hide();

  if (label)
    panel->signal_title_changed().connect(sigc::mem_fun(label, &ActiveLabel::set_text));

  mforms::on_add_menubar_to_window(panel->get_form()->get_menubar(), get_mainwindow());

  panel->reset_layout();
}


void MainForm::prepare_close_document()
{
  // close all diagram tabs so they stop receiving events (which can lead to a crash)
  Gtk::Notebook *note= get_upper_note();

  for (int i= note->get_n_pages()-1; i >= 0; --i)
  {
    Gtk::Widget *panel= note->get_nth_page(i);
    if (dynamic_cast<ModelDiagramPanel*>(panel))
      panel->hide();
  }

  // reset overview
  if (_model_panel)
    _model_panel->get_overview()->reset();
}


bool MainForm::fire_timer()
{
  _wbui_context->get_wb()->get_grt_manager()->flush_timers();

  update_timer();
  return false;
}


void MainForm::update_timer()
{
  int delay_ms= (int)(1000 * _wbui_context->get_wb()->get_grt_manager()->delay_for_next_timeout());

  if (delay_ms >= 0)
    Glib::signal_timeout().connect(sigc::mem_fun(this, &MainForm::fire_timer), delay_ms);
}



//==============================================================================
// Here go command handlers
//
//------------------------------------------------------------------------------
void MainForm::reset_layout()
{
///
}

void MainForm::show_diagram_options()
{
  DiagramSizeForm *form= DiagramSizeForm::create(_wbui_context);

  form->run();

  form->hide();
  delete form;
}


void MainForm::show_page_setup()
{
  _wbui_context->get_wb()->execute_plugin("wb.print.setup");
}




#include <mforms.h>
#include "gtk/lf_view.h"

static std::string get_resource_path(mforms::App* app, const std::string& file)
{
  MainForm *self= reinterpret_cast<MainForm*>(app->get_data_ptr());
  if (file.empty()) return self->get_wbui_context()->get_wb()->get_grt_manager()->get_data_file_path("");
  if (file[0] == '/') return file;

  if (g_str_has_suffix(file.c_str(), ".png") || g_str_has_suffix(file.c_str(), ".xpm"))
    return bec::IconManager::get_instance()->get_icon_path(file);
  else if (g_str_has_suffix(file.c_str(), ".vbs"))
  {
    return self->get_wbui_context()->get_wb()->get_grt_manager()->get_data_file_path(file);
  }

  return self->get_wbui_context()->get_wb()->get_grt_manager()->get_data_file_path(file);
}


static std::string get_executable_path(mforms::App* app, const std::string& file)
{
  MainForm *self= reinterpret_cast<MainForm*>(app->get_data_ptr());

  std::string path = self->get_wbui_context()->get_wb()->get_grt_manager()->get_data_file_path(file);
  if (!path.empty() && base::file_exists(path))
    return path;

  path = base::dirname(std::string(getenv("MWB_MODULE_DIR"))) + "/" + file;
  if (base::file_exists(path))
    return path;

  const char *basedir = getenv("MWB_BASE_DIR");
  if (basedir)
  {
    char *p = g_strdup_printf("%s/libexec/mysql-workbench/%s", basedir, file.c_str());
    path = p;
    g_free(p);
    if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS))
      return path;    
    
    p = g_strdup_printf("%s/bin/%s", basedir, file.c_str());
    path = p;
    g_free(p);
    if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS))
      return path;

    p = g_strdup_printf("%s/libexec/%s", basedir, file.c_str());
    path = p;
    g_free(p);
    if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS))
      return path;
  }

  return "";
}


static base::Rect get_main_window_bounds(mforms::App* app)
{
  Gtk::Window *w = get_mainwindow();
  int x, y;
  
  w->get_window()->get_position(x, y);
  
  return base::Rect(x, y, w->get_width(), w->get_height());
}


void MainForm::set_status_text(mforms::App* app, const std::string &text)
{
  MainForm *self= reinterpret_cast<MainForm*>(app->get_data_ptr());
  
  self->show_status_text_becb(text);
}

//------------------------------------------------------------------------------
static base::Color gdk_color_to_mforms(const Gdk::Color& c)
{
  return base::Color(c.get_red_p(), c.get_green_p(), c.get_blue_p(), 1);
}


//------------------------------------------------------------------------------
static base::Color get_system_color(mforms::SystemColor type)
{
  typedef std::map<mforms::SystemColor, base::Color> Colors;
  static Colors     colors;

  base::Color ret;

  switch (type)
  {
    case mforms::SystemColorHighlight:
    {
      Colors::const_iterator it = colors.find(type);
      if (it != colors.end())
        ret = it->second;
      else
      {
        base::Color new_color(gdk_color_to_mforms(_sys_selection_color));
        colors[type] = new_color;
        ret = new_color;
      }
      break;
    }
    case mforms::SystemColorEditor:
    {
      Colors::const_iterator it = colors.find(type);
      if (it != colors.end())
        ret = it->second;
      else
      {
        Glib::RefPtr<Gtk::Style> style = get_mainwindow()->get_style();
        base::Color new_color(gdk_color_to_mforms(style->get_base(Gtk::STATE_NORMAL)));
        colors[type] = new_color;
        ret = new_color;
      }
      break;

    }
    case mforms::SystemColorDisabled:
    {
      Colors::const_iterator it = colors.find(type);
      if (it != colors.end())
        ret = it->second;
      else
      {
        Glib::RefPtr<Gtk::Style> style = get_mainwindow()->get_style();
        base::Color new_color(gdk_color_to_mforms(style->get_base(Gtk::STATE_INSENSITIVE)));
        colors[type] = new_color;
        ret = new_color;
      }
      break;
    }
    case mforms::SystemColorContainer:
      break;
  }

  return ret;
}


static void begin_thread_loop()
{
}

static void end_thread_loop()
{
}


struct EventLoopFrame
{
  int exit_code;
  bool timedout;
  bool ended;
};

static std::list<EventLoopFrame*> event_loop_exit_codes;

static bool event_loop_timeout()
{
  if (!event_loop_exit_codes.empty())
  {
    EventLoopFrame *frame = event_loop_exit_codes.back();
    if (!frame->timedout && !frame->ended)
    {
      frame->timedout = true;
      Gtk::Main::quit();
    }
  }
  return false;
}

static int begin_event_loop(mforms::App*,float timeout)
{
  EventLoopFrame frame;

  frame.exit_code = -1;
  frame.timedout = false;
  frame.ended = false;

  sigc::connection timeout_conn;
  if (timeout > 0.0)
    timeout_conn = Glib::signal_timeout().connect(sigc::ptr_fun(event_loop_timeout), (unsigned int)(timeout * 1000)); 

  event_loop_exit_codes.push_back(&frame);

  Gtk::Main::run();

  timeout_conn.disconnect();
  if (event_loop_exit_codes.empty() || event_loop_exit_codes.back() != &frame)
  {
    g_warning("Internal inconsistency in begin_event_loop");
    return -1;
  }

  if (!frame.ended && !frame.timedout)
  {
    // means something other than end_event_loop() called quit(), could mean the app is being quit.. 
    Gtk::Main::quit();
    return -1;
  }
  else
  {
    int rc = event_loop_exit_codes.back()->exit_code;
    event_loop_exit_codes.pop_back();
    return rc;
  }
}

static void end_event_loop(mforms::App *,int rc)
{
  if (event_loop_exit_codes.empty())
  {
    g_warning("Attempt to exit unexisting event loop");
    return;
  }
    
  if (!event_loop_exit_codes.back()->timedout)
  {
    event_loop_exit_codes.back()->exit_code = rc;
    event_loop_exit_codes.back()->ended = true;
    Gtk::Main::quit();
  }
}

void MainForm::setup_mforms_app()
{
  mforms::ControlFactory *cf = mforms::ControlFactory::get_instance();
  g_assert(cf);
  
  mforms::App::instantiate(this, false);
  mforms::App::get()->set_data(this);
  
  cf->_app_impl.get_resource_path = &get_resource_path;
  cf->_app_impl.get_executable_path = &get_executable_path;
  cf->_app_impl.set_status_text= &set_status_text;
  cf->_app_impl.get_application_bounds= &get_main_window_bounds;
  cf->_app_impl.begin_thread_loop= &begin_thread_loop;
  cf->_app_impl.end_thread_loop= &end_thread_loop;
  cf->_app_impl.enter_event_loop= &begin_event_loop;
  cf->_app_impl.exit_event_loop= &end_event_loop;
  cf->_app_impl.get_system_color= &get_system_color;
}



Gtk::Widget *MainForm::decorate_widget(Gtk::Widget *panel, bec::UIForm *form)
{
  mforms::MenuBar *menu = form->get_menubar();
  mforms::ToolBar *toolbar = form->get_toolbar();

  Gtk::VBox *top_box = Gtk::manage(new Gtk::VBox(false, 0));
  if (menu)
  {
    Gtk::Widget *w = mforms::widget_for_menubar(menu);
    w->set_name(form->get_form_context_name());
    top_box->pack_start(*w, false, true);
    w->show();
    on_add_menubar_to_window(menu, get_mainwindow());
  }

  if (toolbar)
  {
    Gtk::Widget *w = mforms::widget_for_toolbar(toolbar);
    top_box->pack_start(*w, false, true);
    w->show();
  }
  top_box->pack_start(*panel, true, true);
  panel->show();

  top_box->show();


  return top_box;
}


static gpointer delete_appview(mforms::AppView *appview)
{
  delete appview;
  return 0;
}

std::pair<int, int> MainForm::get_size()
{
  Gtk::Widget *note = get_upper_note();
  int w = note->get_width();
  int h = note->get_height();
  return std::pair<int, int>(w, h);
}

void MainForm::dock_view(mforms::AppView *view, const std::string &position, int)
{
  g_return_if_fail(view != NULL);
  Gtk::Widget *w= mforms::widget_for_view(view);
  g_return_if_fail(w != NULL);

  if (position == "maintab" || position.empty())
  {
    Gtk::Notebook *note = get_upper_note();
    // Make a lookup of existing page with the same content
    bool already_have_page = false;
    for (int i = note->get_n_pages() - 1; i >= 0; --i)
    {
      Gtk::Widget *page;
      if ((page = note->get_nth_page(i)) && page->get_data("uiform")  == dynamic_cast<bec::UIForm*>(view))
      {
        already_have_page = true;
        break;
      }
    }
    if (already_have_page)
      return;

    if (view->release_on_add())
      view->set_release_on_add(false);
    else
      view->retain();

    if (!view->get_menubar())
      view->set_menubar(mforms::manage(_wbui_context->get_command_ui()->create_menubar_for_context(view->is_main_form() ? view->get_form_context_name() : "")));

    Gtk::Widget *decorated = reinterpret_cast<Gtk::Widget*>(w->get_data("DockDecoration"));
    if (!decorated)
    {
      decorated = decorate_widget(w, view);
      w->set_data("DockDecoration", decorated);

 //     if (g_object_is_floating(decorated->gobj()))
 //       g_object_ref_sink(decorated->gobj()); // turn the floating ref to a normal one
    }

    decorated->set_data("AppView", view);
    decorated->set_data("uiform", dynamic_cast<bec::UIForm*>(view));
    decorated->add_destroy_notify_callback(view, (gpointer(*)(gpointer))delete_appview);

    if (dynamic_cast<bec::UIForm*>(view)->get_form_context_name() == "home")
    {
      Gtk::Widget *tab = Gtk::manage(new Gtk::Image(bec::IconManager::get_instance()->get_icon_path("WB_Home.png")));
      Gtk::Alignment *align = Gtk::manage(new Gtk::Alignment());
      align->add(*tab);
      align->show_all();
      align->set_padding(0, 0, 10, 10);
      note->append_page(*decorated, *align);
    }
    else
    {
      ActiveLabel *label = 0;
      append_tab_page(get_upper_note(), decorated, "", TabOpenActive, &label);
      w->set_data("tablabel", label);
    }
    decorated->set_data("uiform", dynamic_cast<bec::UIForm*>(view));
    w->set_data("containerNote", note);
  }
}


bool MainForm::select_view(mforms::AppView *view)
{
  Gtk::Notebook *upper_note = get_upper_note();
  for (int i= 0; i < upper_note->get_n_pages(); i++)
  {
    Gtk::Widget *page = upper_note->get_nth_page(i);
    if (page && reinterpret_cast<mforms::AppView*>(page->get_data("AppView")) == view)
    {
      upper_note->set_current_page(i);
      return true;
    }
  }
  return false;
}


void MainForm::undock_view(mforms::AppView *view)
{
  g_return_if_fail(view != NULL);
  Gtk::Widget *w= mforms::widget_for_view(view);
  g_return_if_fail(w != NULL);
  
  Gtk::Notebook *note= reinterpret_cast<Gtk::Notebook*>(w->get_data("containerNote"));
  Gtk::Widget *decorated = reinterpret_cast<Gtk::Widget*>(w->get_data("DockDecoration"));
  if (note && decorated)
  {
    note->remove_page(*decorated);
  }
  view->release();
}


void MainForm::set_view_title(mforms::AppView *view, const std::string &title)
{
  g_return_if_fail(view != NULL);
  Gtk::Widget *w= mforms::widget_for_view(view);
  g_return_if_fail(w != NULL);

  ActiveLabel *label= reinterpret_cast<ActiveLabel*>(w->get_data("tablabel"));
  if (label)
    label->set_text(title);
}


mforms::AppView *MainForm::selected_view()
{
  int i = get_upper_note()->get_current_page();
  if (i >= 0)
    return view_at_index(i);
  return NULL;
}


int MainForm::view_count()
{
  return get_upper_note()->get_n_pages();
}


mforms::AppView *MainForm::view_at_index(int index)
{
  Gtk::Widget *page = get_upper_note()->get_nth_page(index);
  if (page)
    return reinterpret_cast<mforms::AppView*>(page->get_data("AppView"));
  return NULL;
}


