#include "stdafx.h"

#include <errno.h>
#include <glib/gstdio.h>
#include <errno.h>
#include <iostream>

#include "wb_context.h"
#include "wb_context_ui.h"
#include "wb_model_diagram_form.h"
#include "wb_model_file.h"

#include <grtpp_module_cpp.h>
#include <grtpp_module_python.h>
#include <grtpp_util.h>

#include "grt/clipboard.h"
#include "grt/plugin_manager.h"
#include "grt/common.h"

#include "cppdbc.h"

#include "wbcanvas/model_diagram_impl.h"
/*
#include "wbcanvas/model_layer.h"
#include "wbcanvas/model_figure.h"
*/
#include "wb_module.h"

#include "mdc_canvas_view_opengl.h"

#include "grts/structs.workbench.h"
#include "grts/structs.workbench.model.h"

#include "wb_overview.h"

#include "wb_component_basic.h"
#include "wb_component_logical.h"
#include "wb_component_physical.h"
#include "upgrade_helper.h"

#include "interfaces/interfaces.h"

#ifdef _WIN32
#define DEFAULT_FONT_FAMILY "Tahoma"
#define DEFAULT_MONOSPACE_FONT_FAMILY "Bitstream Vera Sans Mono"
#else
#define DEFAULT_FONT_FAMILY "Helvetica"
#define DEFAULT_MONOSPACE_FONT_FAMILY "Andale Mono"
#endif

#define PLUGIN_GROUP_PATH "/wb/registry/pluginGroups"
#define PLUGIN_LIST_PATH "/wb/registry/plugins"

#define TYPE_GROUP_FILE "data/db_datatype_groups.xml"

#define SYS_INIT_FILE "wbinit.lua"

#ifdef _WIN32
# define USER_INIT_FILE "wbinit.lua"
#elif defined(__APPLE__)
# define USER_INIT_FILE "Library/Application Support/MySQL/Workbench/wbinit.lua"
#else
# define USER_INIT_FILE ".mysqlgui/workbench/wbinit.lua"
#endif

#define PAPER_LANDSCAPE "landscape"
#define PAPER_PORTRAIT "portrait"

// Options file.
#define OPTIONS_FILE_NAME "wb_options.xml"
#define OPTIONS_DOCUMENT_FORMAT "MySQL Workbench Options"
#define OPTIONS_DOCUMENT_VERSION "1.0.1"

// State file.
#define STATE_FILE_NAME "wb_state.xml"
#define STATE_DOCUMENT_FORMAT "MySQL Workbench Application State"
#define STATE_DOCUMENT_VERSION "1.0.0"

// Don't send a given refresh_request unless no new ones arrive in this time.
#define UI_REQUEST_THROTTLE 0.3

#define DEFAULT_UNDO_STACK_SIZE 50


/**
 * @file  wb_context.cpp
 * @brief 
 */

using namespace grt;
using namespace bec;
using namespace wb;


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

#ifndef Constructor____

#ifdef _WIN32
static bool set_log_stuff= false;

static void log_func(const gchar   *log_domain,
                      GLogLevelFlags log_level,
                      const gchar   *message,
                      gpointer       user_data)
{
  OutputDebugStringA(message);
  OutputDebugStringA("\n");
}
#endif


static grt::ValueRef get_app_option(const std::string &option, WBContext *wb)
{
  grt::DictRef model_options(wb->get_document()->physicalModels().get(0)->options());

  if (model_options.get_int("useglobal", 0))
    return wb->get_wb_options().get(option);

  if (model_options.has_key(option))
    return model_options.get(option);
  return wb->get_wb_options().get(option);
}


WBContext::WBContext(WBContextUI *ui, bool verbose)
  : _uicontext(ui), _model_context(ui), _file(0), _save_point(0)
{ 
  extern void register_all_metaclasses();
  static bool registered_metaclasses= false;

#ifdef _WIN32
  if (!set_log_stuff)
  {
    g_log_set_handler(NULL, (GLogLevelFlags)0xfffff, log_func, NULL);
    set_log_stuff= true;
  }
#endif

  // register GRT object class implementations
  if (!registered_metaclasses)
  {
    registered_metaclasses= true;
    register_all_metaclasses();
  }

  _user_interaction_blocked= 0;
  block_user_interaction(true);
  _send_messages_to_shell= true;

  _use_opengl= false;
  _opengl_available= -1;
  _using_opengl= -1;

  _manager= new GRTManager(true, verbose);
  _manager->set_app_option_slot(sigc::bind<WBContext*>(sigc::ptr_fun(get_app_option), this));

  _manager->set_timeout_request_slot(sigc::bind<RefreshType,std::string,NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh),
                                                            RefreshTimer,"", 0));

  // register interface classes
  register_interfaces(_manager->get_grt());
  
  _clipboard= new bec::Clipboard();
  _manager->set_clipboard(_clipboard);
  _clipboard->signal_changed().connect(
    sigc::bind<RefreshType,std::string,NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh), 
      RefreshMenubar, "", 0));

  if (getenv("DEBUG_UNDO"))
    _manager->get_grt()->get_undo_manager()->enable_logging_to(&std::cout);
  

  _pending_refresh_mutex= g_mutex_new();

  _plugin_manager= _manager->get_plugin_manager();
  _plugin_manager->set_registry_paths(PLUGIN_LIST_PATH, PLUGIN_GROUP_PATH);

  // make changes in the messages list trigger a refresh of the msglist in the frontend
  _manager->get_messages_list()->signal_changed().connect(
    sigc::bind<RefreshType,std::string,NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh), RefreshMessages, "", (NativeHandle)0));

  _manager->get_messages_list()->signal_show().connect(
    sigc::bind<RefreshType,std::string,NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh), RefreshMessages, "show", (NativeHandle)0));


  _wb_module= _manager->get_grt()->get_native_module<WorkbenchImpl>();
  _wb_module->set_context(this);

  _components.push_back(new WBComponentBasic(this));
  _components.push_back(new WBComponentPhysical(this));
  _components.push_back(new WBComponentLogical(this));
}


WBContext::~WBContext()
{
  //{
  //  workbench_WorkbenchRef app(get_root());

  //  app.options().unref_tree();
  //  app.registry().unref_tree();
  //  app.info().unref_tree();
  //  app.unref_tree();
  //}

  delete _clipboard;
//  delete _plugin_manager; this is deleted by the GRT, since its a module
  delete _manager;

  std::vector<WBComponent*>::iterator       it   = _components.begin();
  std::vector<WBComponent*>::const_iterator last = _components.end();
  for ( ; last != it; ++it )
  {
    delete *it;
    *it = 0;
  }
}


#endif // Constructor____

#ifndef Components____

WBComponent *WBContext::get_component_named(const std::string &name)
{
  FOREACH_COMPONENT(_components, iter)
    if ((*iter)->get_name() == name)
      return (*iter);
  return 0;
}


void WBContext::foreach_component(const sigc::slot<void, WBComponent*> &slot)
{
  FOREACH_COMPONENT(_components, iter)
    slot(*iter);
}


WBComponent *WBContext::get_component_handling(const model_ObjectRef &object)
{
  FOREACH_COMPONENT(_components, iter)
    if ((*iter)->handles_figure(object))
      return *iter;
  return 0;
}

#endif // Components____


bec::UIForm *WBContext::get_active_form()
{
  return _uicontext->get_active_form();
}


bec::UIForm *WBContext::get_active_main_form()
{
  return _uicontext->get_active_main_form();
}



void WBContext::finalize()
{
  close_document(true);

  save_app_options();
  save_app_state();

  _manager->get_dispatcher()->shutdown();
}


void WBContext::block_user_interaction(bool flag)
{
  if (flag)
    _user_interaction_blocked++;
  else
    _user_interaction_blocked--;

  if (_user_interaction_blocked == 1 && flag)
    lock_gui(true);
  else if (_user_interaction_blocked == 0 && !flag)
    lock_gui(false);
}

#ifndef Setup____

//--------------------------------------------------------------------------------
// Various Setup Stuff

bool WBContext::is_opengl_available()
{
  if (_opengl_available < 0)
  {
    // For now we still disable OpenGL by default.
    // TODO: _opengl_version= mdc::detect_opengl_version();

    _opengl_available= 0;
    
    if (!_opengl_version.empty())
    {
      int major= 0, minor= 0, rel= 0;
      sscanf(_opengl_version.c_str(), "%i.%i.%i", &major, &minor, &rel);
      if (major >= 2 || (major == 1 && minor >= 5))
        _opengl_available= 1;
    }
  }
  return _opengl_available > 0;
}


bool WBContext::do_request_input(const std::string &title, int flags, char **text)
{
  bool flag;
  std::string result;

  flag= request_input(title, flags, result);

  *text= g_strdup(result.c_str());

  return flag;
}


bool WBContext::init_(WBFrontendCallbacks *callbacks, WBOptions *options)
{
  grt::ValueRef res;

   // Copy callbacks
  this->show_internal_error= callbacks->show_internal_error;
  this->show_error= callbacks->show_error;
  this->show_warning= callbacks->show_warning;
  this->show_message= callbacks->show_message;
  this->print_output= callbacks->print_output;
  this->shell_output= callbacks->shell_output;

  this->confirm_action= callbacks->confirm_action;
  this->show_file_dialog= callbacks->show_file_dialog;

  this->request_input= callbacks->request_input;

  this->create_view= callbacks->create_view;
  this->destroy_view= callbacks->destroy_view;
  this->switched_view= callbacks->switched_view;

  this->show_status_text= callbacks->show_status_text;
  
  _manager->set_status_slot(this->show_status_text);

  this->tool_changed= callbacks->tool_changed;

  this->refresh_gui= callbacks->refresh_gui;

  this->popup_menu= callbacks->popup_menu;

  this->perform_command= callbacks->perform_command;

  this->lock_gui= callbacks->lock_gui;
  
  this->quit_application= callbacks->quit_application;

  // Already blocked (when constructed), but call it again so that lock_gui() gets called now.
  // Unlock not before a new document is created (see new_document()).
  block_user_interaction(true);
  block_user_interaction(false); // decrement the block counter to be back at 1
  
  // set the path for the options dictionary that modules should use to store their stuff
  _manager->get_grt()->set_global_module_data_path("/wb/customData");
  _manager->get_grt()->set_document_module_data_path("/wb/doc/customData");
  
  _manager->set_datadir(options->basedir);
  _manager->set_basedir(options->basedir);
  _manager->set_user_datadir(options->user_data_dir);
  _manager->cleanup_tmp_dir();

  bec::IconManager::get_instance()->set_basedir(options->basedir);

  // Setup image search paths
  std::string path;
  static const char* dirs[]= {
    "images", 
    "images/icons",
    "images/grt",
    "images/grt/structs",
    "images/png",
    "images/ui",
    "",
    NULL
  };

  for (unsigned int i= 0; dirs[i]!=NULL; i++)
  {
    mdc::ImageManager::get_instance()->add_search_path(make_path(options->basedir, dirs[i]));
    bec::IconManager::get_instance()->add_search_path(dirs[i]);
  }

  std::string loader_module_path= options->plugin_search_path;
  {
    // Set location of cdbc drivers and module loader (python etc) DLLs
    sql::DriverManager *dbc_driver_man= sql::DriverManager::getDriverManager();
    
#ifdef _WIN32
    dbc_driver_man->set_driver_dir(options->basedir);
#elif defined(__APPLE__)
    dbc_driver_man->set_driver_dir(options->cdbc_driver_search_path);
#else
    if (getenv("DBC_DRIVER_PATH"))
      dbc_driver_man->set_driver_dir(getenv("DBC_DRIVER_PATH"));
    else
      dbc_driver_man->set_driver_dir(options->cdbc_driver_search_path);
#endif
  }

  // Set callbacks
  
  _plugin_manager->set_gui_plugin_callbacks(callbacks->open_editor,
                                            callbacks->show_editor,
                                            callbacks->hide_editor);
  
  _manager->get_shell()->set_output_handler(callbacks->shell_output);

  _manager->set_message_callback(sigc::mem_fun(this, &WBContext::handle_message));

  _manager->set_progress_callback(callbacks->show_progress);

  _manager->set_error_callback(callbacks->show_error);

  get_grt()->send_output("Starting Workbench...");

  // Set options

  _datadir= options->basedir;
  _user_datadir= options->user_data_dir;

  std::string modules_path;
  std::string libraries_path;
  std::string user_modules_path;
  std::string user_scripts_path;
  std::string user_libraries_path;

  user_modules_path= make_path(options->user_data_dir, "modules");
  user_scripts_path= make_path(options->user_data_dir, "scripts");
  user_libraries_path= make_path(options->user_data_dir, "libraries");

  modules_path= options->module_search_path;
#ifdef _WIN32
  modules_path= pathlist_prepend(modules_path, ".");
#endif

  libraries_path= options->library_search_path;
  
  // create user_data_dir/modules dir if it does not exist yet
  if (!g_file_test(user_modules_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
    g_mkdir_with_parents(user_modules_path.c_str(), 0700);

  if (!g_file_test(user_scripts_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
    g_mkdir_with_parents(user_scripts_path.c_str(), 0700);

  if (!g_file_test(user_libraries_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
    g_mkdir_with_parents(user_libraries_path.c_str(), 0700);

  _manager->set_search_paths(
    modules_path,
    pathlist_prepend(options->struct_search_path, make_path(options->basedir, "structs")),
    libraries_path);

  _manager->set_user_extension_paths(user_modules_path, user_scripts_path, user_libraries_path);

  std::list<std::string> exts;
  exts.push_back(".grt");

  _manager->set_module_extensions(exts);


  if (options->force_sw_rendering)
    _use_opengl= false;
  else if (options->force_opengl_rendering)
    _use_opengl= true;
  else
    _use_opengl= is_opengl_available();

  show_status_text(_("Initializing GRT..."));

  // Initialize GRT Manager and start the shell
  _manager->initialize(loader_module_path);
  
  _manager->get_shell()->set_ready_handler(callbacks->next_shell_command);

  _manager->get_shell()->set_save_directory(options->user_data_dir);
  _manager->get_shell()->set_saves_history(200); // limit history to 200 commands

  // add some handy shortcuts to shell tree bookmarks list
  _manager->get_shell()->add_grt_tree_bookmark("/wb/doc/physicalModels/0/catalog");
  _manager->get_shell()->add_grt_tree_bookmark("/wb/doc/physicalModels/0/catalog/schemata/tables");
  _manager->get_shell()->add_grt_tree_bookmark("/wb/doc/physicalModels/0/diagrams/0");
  _manager->get_shell()->add_grt_tree_bookmark("/wb/doc/physicalModels/0/diagrams/0/figures");
  _manager->get_shell()->add_grt_tree_bookmark("/wb/registry/plugins");

  //_manager->get_shell()->start();
  _manager->get_grt()->send_output(strfmt("Looking for user plugins in %s\n", user_modules_path.c_str()));

  show_status_text(_("Initializing Workbench components...")); 
  res= execute_in_grt_thread("Initialize WB Context",
                        sigc::bind<WBOptions*>(sigc::mem_fun(this, &WBContext::setup_context_grt),
                                               options));

  if (res.is_valid() && *grt::IntegerRef::cast_from(res) != 1)
    show_error(_("Initialization Error"), _("There was an error during initialization of Workbench, some functionality may not work."));

  try
  {
    _manager->initialize_shell(get_root()->options()->options().get_string("grtshell:ShellLanguage", "lua"));
  }
  catch (std::exception)
  {
    _manager->initialize_shell("lua");
  }
  
  // Load last application state. This will only load it into the grt tree.
  // Components that have stored their settings will later read those values and reapply them.
  load_app_state();

  get_root()->options()->signal_dict_changed().
    connect(sigc::mem_fun(this, &WBContext::option_dict_changed));

  _send_messages_to_shell= false;

  _manager->set_use_log_file(options->use_log_file);

  return true;
}


void WBContext::init_finish_(WBOptions *options)
{
  // open initial document when GUI init finishes
  std::string initial_file;
  if (!options->open_at_startup.empty())
    initial_file= options->open_at_startup;
  else if (get_wb_options().get_int("workbench.AutoReopenLastModel", 0))
  {
    if (get_root()->options()->recentFiles().count() > 0)
      initial_file= get_root()->options()->recentFiles().get(0);
  }

  if (!initial_file.empty())
    open_document(initial_file);
    //_manager->run_when_idle(sigc::bind_return(sigc::bind(sigc::mem_fun(this, &WBContext::open_document), initial_file), false));
  else
    new_document();

  block_user_interaction(false);
  
  show_status_text(_("Ready."));
}



void WBContext::handle_message(const grt::Message &msg)
{
  if (_send_messages_to_shell)
    _manager->get_shell()->handle_msg(msg);
  else
  {
    if (msg.type == grt::OutputMsg)
      print_output(msg.format().c_str());
    else
      _manager->get_messages_list()->handle_message(msg);
  }
}


#if 0
#include "myx_grt_private.h"

static void EXPORT_MODULES_HACK(MYX_GRT *grt)
{
  for (int i= 0; i < grt->modules_num; i++)
  {
    std::string path= strfmt("..\\..\\generated\\grtm\\%s.h", grt->modules[i]->name);

    myx_grt_modules_export_wrapper(grt->modules+i, 1, path.c_str());
  }
}
#else
#define EXPORT_MODULES_HACK(xx) do {} while(0)
#endif

grt::ValueRef WBContext::setup_context_grt(grt::GRT *grt, WBOptions *options)
{
  // init the GRT tree nodes, set default options
  init_grt_tree(grt, options);

  init_plugin_groups_grt(grt, options);

  run_init_scripts_grt(grt, options);

  init_plugins_grt(grt, options);

  FOREACH_COMPONENT(_components, iter)
    (*iter)->setup_context_grt(grt, options);

  // app options must be loaded after everything else is initialized
  load_app_options(false);

  init_canvas_grt(grt, options);  

  EXPORT_MODULES_HACK(grt->grt());

  return grt::IntegerRef(1);
}


void WBContext::init_grt_tree(grt::GRT *grt, WBOptions *options)
{
  grt::DictRef root(grt);
  workbench_WorkbenchRef app(grt);

  _wb_root= app;

  root.set("wb", app);

  // setup application subtree
  {
    app_InfoRef info(grt);
    GrtVersionRef version(grt);
    info->owner(app);

    version->majorNumber(APP_MAJOR_NUMBER);
    version->minorNumber(APP_MINOR_NUMBER);
    version->releaseNumber(APP_RELEASE_NUMBER);
    version->buildNumber(APP_REVISION_NUMBER);
    version->status(1);

    info->name("MySQL Workbench");
    info->version(version);
    info->copyright("MySQL AB");
    info->license("GPL");
    app->info(info);
  }

  {
    app_OptionsRef options(grt);
    options->owner(app);

    append_contents(options->paperTypes(), get_paper_types(grt));

    set_default_options(options->options());

    app->options(options);
  }

  {
    app_RegistryRef registry(grt);
    registry->owner(app);

    app->registry(registry);
  }

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

  db_mgmt_ManagementRef mgmt_info(grt);

  // load datatype groups from XML
  
  ListRef<db_DatatypeGroup> grouplist;

  grouplist= ListRef<db_DatatypeGroup>::cast_from(grt->unserialize(make_path(options->basedir, TYPE_GROUP_FILE)));
  for (size_t c= grouplist.count(), i= 0; i < c; i++)
  {
    grouplist[i]->owner(mgmt_info);
    mgmt_info->datatypeGroups().insert(grouplist[i]);
  }
  app->rdbmsMgmt(mgmt_info);

  grt->set_root(root);
}



void WBContext::run_init_scripts_grt(grt::GRT *grt, WBOptions *options)
{
  std::string sysinitpath= make_path(options->basedir, SYS_INIT_FILE);
  std::string userinitpath= make_path(g_get_home_dir(), USER_INIT_FILE);

  // first try the user's custom init script
  if (g_file_test(userinitpath.c_str(), G_FILE_TEST_EXISTS))
    _manager->get_shell()->run_script(userinitpath);

  // if it doesn't exist, use the system one
  else if (g_file_test(sysinitpath.c_str(), G_FILE_TEST_EXISTS))
    _manager->get_shell()->run_script(sysinitpath);
}


void WBContext::init_plugin_groups_grt(grt::GRT *grt, WBOptions *options)
{
  struct group_def {
    const char *category;
    const char *name;
  } std_groups[]= {
    {"Database", "Database"},
    {"Catalog",   "Editors"},
    {"Application", "Workbench"},
    {"Model", "Validation"},
    {"Model", "Export"},

    {"Model",     "Menu/Model"},
    {"Catalog",   "Menu/Catalog"},
    {"Catalog",   "Menu/Objects"},
    {"Database",  "Menu/Database"},
    {"Utilities", "Menu/Utilities"},
#if 0
    {"File",     "MainMenu"},
    {"Import",   "File"},
    {"Export",   "File"},
    {"Edit",     "MainMenu"},
    {"View",     "MainMenu"},
    {"Model",    "MainMenu"},
    {"Validation","Workbench"},
    {"Catalog",   "Workbench"},

    {"Others", NULL},
#endif
  };

  std::map<std::string, app_PluginGroupRef> groups;

  grt::ListRef<app_PluginGroup> group_list= grt::ListRef<app_PluginGroup>::cast_from(_manager->get_grt()->get(PLUGIN_GROUP_PATH));

  for (unsigned int i= 0; i < sizeof(std_groups)/sizeof(group_def); i++)
  {
    app_PluginGroupRef group;

    group= app_PluginGroupRef(grt);
    group->category(std_groups[i].category);
    group->name(std_groups[i].name);

    group_list.insert(group);

    groups[std_groups[i].name]= group;
  }
}


void WBContext::init_plugins_grt(grt::GRT *grt, WBOptions *options)
{
  std::map<std::string, bool> scanned_dir_list;
  std::list<std::string> exts;

# if defined(_WIN32)
  exts.push_back(".wbp.be");
# endif
  exts.push_back(".wbp");


  // scan user plugins  
  std::string plugin_path= normalize_path(make_path(options->user_data_dir, "plugins"));
  _manager->get_grt()->send_output(strfmt("Looking for user plugins in %s\n", plugin_path.c_str()));

  _manager->do_scan_modules(plugin_path, exts, false);
  scanned_dir_list[plugin_path]= true;

  std::vector<std::string> paths= split_string(options->plugin_search_path, G_SEARCHPATH_SEPARATOR_S);

  for (size_t c= paths.size(), i= 0; i < c; i++)
  {
    if (scanned_dir_list.find(paths[i]) == scanned_dir_list.end()
      && g_file_test(paths[i].c_str(), G_FILE_TEST_IS_DIR))
    {
      std::string full_path= normalize_path(make_path(options->user_data_dir, paths[i]));

      if (scanned_dir_list.find(full_path) == scanned_dir_list.end())
      {
        _manager->get_grt()->send_output(strfmt("Looking for plugins in %s\n", full_path.c_str()));
        _manager->do_scan_modules(paths[i], exts, false);
      }
      scanned_dir_list[paths[i]]= true;
    }
  }
  _plugin_manager->rescan_plugins();
}


void WBContext::init_properties_grt(workbench_DocumentRef &doc)
{
  grt::GRT *grt= _manager->get_grt();

  app_DocumentInfoRef info(grt);
  info->name("Properties");
  info->owner(doc);

  info->caption("New Model");
  info->version("1.0");
  info->project("Name of the project");
  info->dateCreated(fmttime(0, DATETIME_FMT));
  info->dateChanged(fmttime(0, DATETIME_FMT));
  info->author(g_get_real_name());

  doc->info(info);
}


static void set_default(grt::DictRef dict, const char *option, int value)
{
  if (!dict.has_key(option))
    dict.gset(option, value);
}


static void set_default(grt::DictRef dict, const char *option, const std::string &value)
{
  if (!dict.has_key(option) || option[0] == '@')
    dict.gset(option, value);
}


/** 
 ****************************************************************************
 * @brief Sets Workbench specific options
 *
 * To get a configuration option, use GRTManager::get_app_option()
 * 
 ****************************************************************************
 */
void WBContext::set_default_options(grt::DictRef options)
{
  set_default(options, "workbench:OSSHideMissing", 0);
  set_default(options, "workbench:UndoEntries", DEFAULT_UNDO_STACK_SIZE);
  set_default(options, "workbench.AutoReopenLastModel", 0);

  set_default(options, "workbench.physical:DeleteObjectConfirmation", "ask");

  set_default(options, "grtshell:ShellLanguage", "lua");
  set_default(options, "@grtshell:ShellLanguage/Items", "lua,python");
  
  // URL of latest versions file (used by version updater)
  set_default(options, "VersionsFileURL", "http://dev.mysql.com/workbench/versions.php");

  // URL of addons manager
  set_default(options, "AddonDirectoryPageURL", "http://dev.mysql.com/workbench/addons.html");
  set_default(options, "AddonBaseURL", "http://dev.mysql.com/workbench/addon.php");

  // Network settings (proxy)
  set_default(options, "ProxyType", "HTTP");
  set_default(options, "ProxyServer", ""); // syntax: [servername]:[port]
  set_default(options, "ProxyUserPwd", ""); // syntax: [user]:[password]

  // SQL parsing options
  set_default(options, "SqlIdentifiersCS", 1);
  set_default(options, "SqlMode", "");

  // Name templates
  set_default(options, "PkColumnNameTemplate", "id%table%");
  set_default(options, "DefaultPkColumnType", "INT");
  
  set_default(options, "ColumnNameTemplate", "%table%col");
  set_default(options, "DefaultColumnType", "VARCHAR(45)");
  
  //set_default(options, "FKNameTemplate", "fk%table%");
  set_default(options, "FKNameTemplate", "fk_%stable%_%dtable%");
  set_default(options, "FKColumnNameTemplate", "%table%_%column%");
  set_default(options, "AuxTableTemplate", "%stable%_has_%dtable%");

  // Model Defaults
  set_default(options, "DefaultFigureNotation", "workbench/default");
  set_default(options, "DefaultConnectionNotation", "crowsfoot");

  set_default(options, "SynchronizeObjectColors", 1);

  // MySQL Defaults
  set_default(options, "db.mysql.Table:tableEngine", "InnoDB");

  // FK defaults
  set_default(options, "db.ForeignKey:deleteRule", "NO ACTION");
  set_default(options, "db.ForeignKey:updateRule", "NO ACTION");

  // Default Colors
  set_default(options, "workbench.model.Layer:Color", "#F0F1FE");
  set_default(options, "workbench.model.NoteFigure:Color", "#FEFDED");

  set_default(options, "workbench.physical.Diagram:DrawLineCrossings", 0);
  set_default(options, "workbench.physical.ObjectFigure:Expanded", 1);
  set_default(options, "workbench.physical.TableFigure:ShowColumnTypes", 1);
  set_default(options, "workbench.physical.TableFigure:ShowColumnFlags", 0);
  set_default(options, "workbench.physical.TableFigure:MaxColumnTypeLength", 20);
  set_default(options, "workbench.physical.TableFigure:MaxColumnsDisplayed", 30);
  set_default(options, "workbench.physical.RoutineGroupFigure:MaxRoutineNameLength", 20);
  
  set_default(options, "workbench.physical.TableFigure:Color", "#98BFDA");
  set_default(options, "workbench.physical.ViewFigure:Color", "#FEDE58");
  set_default(options, "workbench.physical.RoutineGroupFigure:Color", "#98D8A5");

  // Default Fonts

  set_default(options, "workbench.physical.TableFigure:TitleFont", DEFAULT_FONT_FAMILY" Bold 12");
  set_default(options, "workbench.physical.TableFigure:SectionFont", DEFAULT_FONT_FAMILY" Bold 11");
  set_default(options, "workbench.physical.TableFigure:ItemsFont", DEFAULT_FONT_FAMILY" 11");
  set_default(options, "workbench.physical.ViewFigure:TitleFont", DEFAULT_FONT_FAMILY" Bold 12");
  set_default(options, "workbench.physical.RoutineGroupFigure:TitleFont", DEFAULT_FONT_FAMILY" Bold 12");
  set_default(options, "workbench.physical.RoutineGroupFigure:ItemsFont", DEFAULT_FONT_FAMILY" 12");
  set_default(options, "workbench.physical.Connection:CaptionFont", DEFAULT_FONT_FAMILY" 11");

  set_default(options, "workbench.general.Editor:Font", DEFAULT_MONOSPACE_FONT_FAMILY" 10");

  set_default(options, "workbench.model.Layer:TitleFont", DEFAULT_FONT_FAMILY" 11");
  set_default(options, "workbench.model.NoteFigure:TitleFont", DEFAULT_FONT_FAMILY" Bold 11");
  set_default(options, "workbench.model.NoteFigure:TextFont", DEFAULT_FONT_FAMILY" 11");

  set_default(options, "workbench.model.NoteFigure:TextFont", DEFAULT_FONT_FAMILY" 11");

  std::string colors;
  colors= "#FFEEEC\n";
  colors+= "#FEFDED\n";
  colors+= "#EAFFE5\n";
  colors+= "#ECFDFF\n";
  colors+= "#F0F1FE\n";
  colors+= "#FFEBFA\n";
  set_default(options, "workbench.model.Figure:ColorList", colors);

  colors= "#98BFDA\n";
  colors+= "#FEDE58\n";
  colors+= "#98D8A5\n";
  colors+= "#FE9898\n";
  colors+= "#FE98FE\n";
  colors+= "#FFFFFF\n";
  set_default(options, "workbench.model.ObjectFigure:ColorList", colors);

  // Other options
  set_default(options, "workbench.physical.Connection:HideCaptions", 1);
  set_default(options, "workbench.physical.Connection:CenterCaptions", 0);  
}



void WBContext::init_canvas_grt(grt::GRT *grt, WBOptions *options)
{
  // register bridges
  FOREACH_COMPONENT(_components, iter)
  {
    (*iter)->init_canvas_grt(grt, options);
  }
}


grt::ListRef<app_PaperType> WBContext::get_paper_types(grt::GRT *grt)
{
  return grt::ListRef<app_PaperType>::cast_from(grt->unserialize(make_path(get_datadir(),
    "data/paper_types.xml")));
}


static void strip_options_dict(grt::DictRef dict)
{
  std::vector<std::string> keys;
  {
    grt::DictRef::const_iterator iter= dict.begin();
    grt::DictRef::const_iterator end= dict.end();
  
    while (iter != end)
    {
      if (iter->first[0] == '@')
        keys.push_back(iter->first);
      ++iter;
    }
  }
  
  for (std::vector<std::string>::const_iterator end= keys.end(), iter= keys.begin();
       iter != end; ++iter)
  {
    dict.remove(*iter);
  }
}


void WBContext::load_app_options(bool update)
{
  grt::GRT *grt= get_grt();
  
  // load ui related stuff (menus, toolbars etc)
  _uicontext->load_app_options(update);

  // load saved options
  std::string options_xml= make_path(_user_datadir, OPTIONS_FILE_NAME);
  if (g_file_test(options_xml.c_str(), G_FILE_TEST_EXISTS))
  {
    try 
    {
      app_OptionsRef curOptions(get_root()->options());

      xmlDocPtr xmlDocument= grt->load_xml(options_xml);

      std::string doctype, version;
      grt->get_xml_metainfo(xmlDocument, doctype, version);

      // Older option files without a version number are considered as 1.0.0 and
      // upgraded from there to latest version.
      if (version.empty())
        version= "1.0.0";
      else
        // Document format has been introduced in 1.0.1.
        if (doctype != OPTIONS_DOCUMENT_FORMAT)
          throw std::runtime_error(strfmt(_("The file \"%s\" is not a valid MySQL Workbench options file."), 
          options_xml.c_str()));

      // Try to upgrade document at XML level.
      if (version != OPTIONS_DOCUMENT_VERSION)
        attempt_options_upgrade(xmlDocument, version);

      grt::ValueRef options_value= grt->unserialize_xml(xmlDocument, options_xml);
      app_OptionsRef options(app_OptionsRef::cast_from(options_value));

      if (options.is_valid())
      {
        // strip stuff that are not options
        strip_options_dict(options->options());
        strip_options_dict(options->commonOptions());
        
        // set loaded options dict
        grt::merge_contents(curOptions->options(), options->options(), true);

        grt::merge_contents(curOptions->commonOptions(), options->commonOptions(), true);

        // set loaded recent files list
        while (curOptions->recentFiles().count() > 0)
          curOptions->recentFiles().remove(0);
        grt::append_contents(curOptions->recentFiles(), options->recentFiles());

        // merge paper types (so we get custom types)
        grt::merge_contents_by_id(grt::ObjectListRef::cast_from(curOptions->paperTypes()),
        grt::ObjectListRef::cast_from(options->paperTypes()), false);
        grt::ListRef<app_PaperType> paperTypes = curOptions->paperTypes();
        
        for (size_t c= paperTypes.count(), i= 0; i < c; i++)
        {
          app_PaperTypeRef value(paperTypes[i]);
          
          if (value.is_valid())
            value->owner(curOptions);
        }
      }
    }
    catch (std::exception &exc)
    {
      grt->send_warning(strfmt("Error while loading '%s': %s",
        options_xml.c_str(), exc.what()));
    }
  }

  // commit options
  option_dict_changed();

  // remove junk from options dict (this can be deleted some day)
  get_root()->options()->options().remove("workbench.physical.ConnectionFigure:CaptionFont");


  // load options specific for other parts of wb
  FOREACH_COMPONENT(_components, iter)
    (*iter)->load_app_options(update);
}


void WBContext::attempt_options_upgrade(xmlDocPtr xmldoc, const std::string &version)
{
  std::vector<std::string> ver= bec::split_string(version, ".");

  int major= atoi(ver[0].c_str());
  int minor= atoi(ver[1].c_str());
  int revision= atoi(ver[2].c_str());

  // Version * -> 1.0.1
  // Performed changes:
  //   * Removed formPositions tag from options tag.
  if (major == 1 && minor == 0 && revision == 0) 
  {
    XMLTraverser xml(xmldoc);
    std::vector<xmlNodePtr> options_list(xml.scan_objects_of_type("app.Options"));

    for (size_t c= options_list.size(), i= 0; i < c; i++)
      xml.delete_object_item(options_list[i], "formPositions");

    revision= 1;
  }
}

void WBContext::save_app_options()
{
  std::string options_file= make_path(_user_datadir, OPTIONS_FILE_NAME);
  _manager->get_grt()->serialize(get_root()->options(), options_file + ".tmp", OPTIONS_DOCUMENT_FORMAT, 
    OPTIONS_DOCUMENT_VERSION);
  g_remove(options_file.c_str());
  g_rename(std::string(options_file + ".tmp").c_str(), options_file.c_str());

  FOREACH_COMPONENT(_components, iter)
    (*iter)->save_app_options();
}

void WBContext::load_app_state()
{
  // Load saved state.
  std::string state_xml= make_path(_user_datadir, STATE_FILE_NAME);
  if (g_file_test(state_xml.c_str(), G_FILE_TEST_EXISTS))
  {
    xmlDocPtr xmlDocument= NULL;
    try 
    {
      xmlDocument= _manager->get_grt()->load_xml(state_xml);

      std::string doctype, version;
      _manager->get_grt()->get_xml_metainfo(xmlDocument, doctype, version);

      if (doctype != STATE_DOCUMENT_FORMAT)
      {
        throw std::runtime_error(strfmt(_("The file \"%s\" is not a valid MySQL Workbench state file."), 
                                     state_xml.c_str()));
      }

      grt::DictRef current_state(get_root()->state());
      grt::DictRef new_state= grt::DictRef::cast_from(_manager->get_grt()->unserialize_xml(xmlDocument, state_xml));

      // Store new state in grt tree.
      grt::merge_contents(current_state, new_state, true);
      
      xmlFreeDoc(xmlDocument);
    }
    catch (std::exception &exc)
    {
      xmlFreeDoc(xmlDocument);

      _manager->get_grt()->send_warning(strfmt("Error while loading '%s': %s",
        state_xml.c_str(), exc.what()));
    }
  }
  
  // restore stuff from grt shell
  _manager->get_shell()->restore_state();
}


void WBContext::save_app_state()
{
  std::string state_file= make_path(_user_datadir, STATE_FILE_NAME);
  _manager->get_grt()->serialize(get_root()->state(), state_file + ".tmp", STATE_DOCUMENT_FORMAT, 
    STATE_DOCUMENT_VERSION);
  g_remove(state_file.c_str());
  g_rename(std::string(state_file + ".tmp").c_str(), state_file.c_str());
  
  try
  {
    _manager->get_shell()->store_state();
  } 
  catch (std::exception &exc)
  {
    _manager->get_grt()->send_error("Error saving GRT shell state: %s", exc.what());
    g_warning("Error saving GRT shell state: %s", exc.what());
  }
}

void WBContext::add_recent_file(const std::string &file)
{
  grt::StringListRef recentFiles(get_root()->options()->recentFiles());
  recentFiles.remove_value(file);
  recentFiles.insert(file, 0);
#if APP_MAJOR_NUMBER == 5 && APP_MINOR_NUMBER < 2 //XXX delete this snippet once 5.2 goes GA
  while (recentFiles.count() > 10)
    recentFiles.remove(10);
#endif
  save_app_options();
}


void WBContext::option_dict_changed(grt::internal::OwnedDict *options, bool, const std::string&)
{
  if (get_wb_options() == grt::DictRef(options))
  {
    int undo_size= get_wb_options().get_int("workbench:UndoEntries", DEFAULT_UNDO_STACK_SIZE);

    if (undo_size == 0)
      undo_size= 1;

    get_grt()->get_undo_manager()->set_undo_limit(undo_size);
  }
}

#endif // Setup____



void WBContext::flush_idle_tasks()
{
  _manager->perform_idle_tasks();
 
  if (_user_interaction_blocked)
  {
    return;
  }
  
  mdc::Timestamp now= mdc::get_time();

  g_mutex_lock(_pending_refresh_mutex);
  std::list<RefreshRequest> refreshes;

  // separate the requests that can be executed now
  std::list<RefreshRequest>::iterator iter= _pending_refreshes.begin();
  while (iter != _pending_refreshes.end())
  {
    std::list<RefreshRequest>::iterator next= iter;
    ++next;

    if (now - iter->timestamp >= UI_REQUEST_THROTTLE)
    {
      refreshes.push_back(*iter);
      _pending_refreshes.erase(iter);
    }

    iter= next;
  }
  g_mutex_unlock(_pending_refresh_mutex);

  // send the refresh requests
  for (std::list<RefreshRequest>::iterator iter= refreshes.begin();
    iter != refreshes.end(); ++iter)
    refresh_gui(iter->type, iter->str, iter->ptr);  
}


void WBContext::request_refresh(RefreshType type, const std::string &str, NativeHandle ptr)
{  
  g_mutex_lock(_pending_refresh_mutex);

  mdc::Timestamp now= mdc::get_time();

  // check if dupe 
  for (std::list<RefreshRequest>::iterator iter= _pending_refreshes.begin();
    iter != _pending_refreshes.end(); ++iter)
  {
    if (iter->type == type && iter->str == str && iter->ptr == ptr)
    {
      // if its a dupe, update the timestamp so that notifications are only sent when
      // there's no more fresh requests arriving
      iter->timestamp= now;

      g_mutex_unlock(_pending_refresh_mutex);
      return;
    }
  }

  RefreshRequest refresh;
  refresh.type= type;
  refresh.str= str;
  refresh.ptr= ptr;
  refresh.timestamp= now;
  // Do not remove the following refresh! W/o it linux version hangs at times.
  if (_pending_refreshes.empty())
    refresh_gui(RefreshNeeded, "", (NativeHandle)0);

  _pending_refreshes.push_back(refresh);

  g_mutex_unlock(_pending_refresh_mutex);
}


#ifndef Document____

//--------------------------------------------------------------------------------
// Creating/Loading documents

void WBContext::new_document()
{
  try {
    show_status_text(_("Creating new document..."));
    execute_in_grt_thread("New Document", sigc::mem_fun(this, &WBContext::new_document_grt));
    show_status_text(_("New document."));
  } catch (grt::grt_runtime_error &error) {
    _manager->show_error(error.what(), error.detail);
    show_status_text(_("Error creating new document."));
  }
}


grt::ValueRef WBContext::new_document_grt(grt::GRT *grt)
{
  // ask whether unsaved changes should be saved
  if (has_unsaved_changes())
  {
    int answer= 
      execute_in_main_thread<int>("check save changes",
                             sigc::bind(confirm_action, _("New Document"),
                                        _("Do you want to save pending changes to the document?\n\n"
                                          "If you don't save your changes, they will be lost."),
                                        _("Save"), _("Don't Save"), _("Cancel")));
    if (answer == 1)
    {
      if (!save_as(_filename))
        return grt::IntegerRef(0);
    }
    else if (answer == -1)
      return grt::IntegerRef(0);
  }

  block_user_interaction(true);
  
  // close the current document
  execute_in_main_thread("close document", sigc::bind(sigc::mem_fun(this, &WBContext::close_document), false), true);

  // create an empty document and add a physical model to it
  workbench_DocumentRef doc(grt);
  workbench_WorkbenchRef wb(get_root());
  wb->doc(doc);
  doc->owner(wb);

  {
    // setup default page settings
    app_PageSettingsRef page(grt);

    page->owner(doc);
    page->paperType(grt::find_named_object_in_list(wb->options()->paperTypes(), "iso-a4"));
    if (!page->paperType().is_valid())
    {
      // if there is no paperType available, create A4
      app_PaperTypeRef paperType(grt);
      
      paperType->owner(page);
      paperType->name("iso-a4");
      paperType->caption("A4 (210 mm x 297 mm)");
      paperType->width(210.0);
      paperType->height(297.0);
      paperType->marginsSet(0);
      
      page->paperType(paperType);
    }
      
      
    page->marginTop(6.35);
    page->marginBottom(14.46);
    page->marginLeft(6.35);
    page->marginRight(6.35);
    page->orientation(PAPER_PORTRAIT);

    doc->pageSettings(page);
  }

  get_component<WBComponentLogical>()->setup_logical_model(grt, doc);

  get_component<WBComponentPhysical>()->setup_physical_model(grt, doc, "Mysql", "5.1.22");

  init_properties_grt(doc);

  reset_document();

  _file= new ModelFile();

  _file->signal_changed().connect(
    sigc::bind<RefreshType, std::string, NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh), 
      RefreshDocument, "", 0));

  _file->create();

  _filename= "";

  realize();

  _save_point= grt->get_undo_manager()->get_latest_undo_action();
  request_refresh(RefreshDocument, "");

  _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind<std::string>(perform_command, "reset_layout"), false));
  _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind(sigc::mem_fun(this, &WBContext::block_user_interaction), false), false));

  return grt::IntegerRef(1);
}

/**
 * Convenience method to trigger figure realization.
 */
void WBContext::realize()
{
  get_document()->physicalModels()[0]->get_data()->realize();
}

void WBContext::open_recent_document(int index)
{
  if (index-1 < (int)get_root()->options()->recentFiles().count())
    open_document(get_root()->options()->recentFiles().get(index-1));
}


void WBContext::clear_refreshes()
{
  g_mutex_lock(_pending_refresh_mutex);

  const gchar* refresh_types[] = 
  {"RefreshNeeded",    "RefreshNothing",    "RefreshLayers",     "RefreshSchemaList",
   "RefreshSchema",    "RefreshNewView",    "RefreshSelection",    "RefreshToolbar",
   "RefreshMessages",    "RefreshCloseEditor",     "RefreshFindOutput",    "RefreshHistory",
   "RefreshUserTypes",    "RefreshOverviewNodeInfo",    "RefreshOverviewNodeChildren",
   "RefreshCatalogTreeNode",    "RefreshViewName",    "RefreshDocument",
   "RefreshCloseDocument",    "RefreshZoom",    "RefreshMenubar",    "RefreshTimer"};

  for (std::list<RefreshRequest>::iterator iter= _pending_refreshes.begin();
    iter != _pending_refreshes.end(); ++iter)
      g_message("dropping on reset %s", refresh_types[iter->type]);
  _pending_refreshes.clear();
  g_mutex_unlock(_pending_refresh_mutex);
}


void WBContext::reset_document()
{
  workbench_DocumentRef doc(get_document());

  FOREACH_COMPONENT(_components, iter)
    (*iter)->reset_document();

  get_grt()->get_undo_manager()->reset();

  get_ui()->reset();
  
  _clipboard->clear();
  _clipboard->set_content_description("");
 
  // refresh internal environment of loaders
  get_grt()->refresh_loaders();
}



bool WBContext::open_document(const std::string &file)
{  
  if (has_unsaved_changes())
  {
    int answer= 
      execute_in_main_thread<int>("check save changes",
                                  sigc::bind(confirm_action, _("Open Document"),
                                             _("Do you want to save pending changes to the document?\n\n"
                                               "If you don't save your changes, they will be lost."),
                                             _("Save"), _("Don't Save"), _("Cancel")));

    if (answer == 1)
    {
      if (!save_as(_filename))
        return false;
    }
    else if (answer == -1)
      return false;
  }

  show_status_text(strfmt(_("Loading %s..."), file.c_str()));
  
  GUILock lock(this);

  execute_in_main_thread("close document", sigc::bind(sigc::mem_fun(this, &WBContext::close_document), false), true);
  //close_document();


  grt::GRT *grt(_manager->get_grt());

  show_status_text(strfmt(_("Loading %s..."), file.c_str()));

  _manager->block_idle_tasks();

  workbench_DocumentRef doc;

  FOREACH_COMPONENT(_components, iter)
    (*iter)->block_model_notifications();

  _file= new ModelFile();
  _file->signal_changed().connect(
    sigc::bind<RefreshType, std::string, NativeHandle>(sigc::mem_fun(this, &WBContext::request_refresh), 
      RefreshDocument, "", 0));

  try
  {
    _file->open(file);

    doc= _file->retrieve_document(grt);
  }
/*  catch (grt::grt_runtime_exception &exc)
  {
    show_exception(strfmt(_("Cannot open document '%s'."), file.c_str()), exc);
    new_document();
    _manager->unblock_idle_tasks();

    FOREACH_COMPONENT(_components, iter)
      (*iter)->unblock_model_notifications();

    lock_gui(false);

    return false;
  }*/
  catch (std::exception &exc)
  {
    show_exception(strfmt(_("Cannot open document '%s'."), file.c_str()), exc);
    new_document();
    _manager->unblock_idle_tasks();

    FOREACH_COMPONENT(_components, iter)
      (*iter)->unblock_model_notifications();

    return false;
  }
  
  std::list<std::string> warnings(_file->get_load_warnings());
  if (!warnings.empty())
  {
    if (warnings.size() == 1)
    {
      show_warning(_("Corrected Model File"), 
                   strfmt(_("The model in file '%s' contained a problem which was successfully recovered:\n %s"),
                          file.c_str(), warnings.front().c_str()));
    }
    else
    {
      std::string msg= strfmt(_("The model in file '%s' contained problems which were successfully recovered:\n"),
                              file.c_str());
      int i= 0;
      
      for (std::list<std::string>::const_iterator iter= warnings.begin(); iter != warnings.end(); ++iter)
      {
        if (i++ >= 2)
        {
          msg.append("(see log for more details)");
          break;
        }
        msg.append(" -").append(*iter).append("\n");
      }
      
      show_warning(_("Corrected Model File"), msg);
    }
    
    g_warning("%i problems found and corrected during load of file '%s':", (int)warnings.size(), file.c_str());
    for (std::list<std::string>::const_iterator iter= warnings.begin(); iter != warnings.end(); ++iter)
      g_warning(" - %s", iter->c_str());
    
    _file->copy_file(file, file+".beforefix");
    g_warning("Original file backed up to %s", (file+".beforefix").c_str());
  }
  
  // 5.0 -> 5.1 was done at 1.2.0, if document is from 5.0, make a backup
  std::vector<std::string> version_parts= split_string(_file->in_disk_document_version(), ".");
  if (version_parts.size() >= 2 && version_parts[0] == "1" && atoi(version_parts[1].c_str()) <= 2)
  {
    std::string::size_type dot= file.rfind('.');
    std::string bakpath;
    if (file.substr(dot) == ".mwb")
      bakpath= file.substr(0, dot).append(".wb50.mwb");
    else
      bakpath= file+".wb50.mwb";
    
    _file->copy_file(file, bakpath);
    
    // make backup
    grt->send_info(strfmt("Model file is from 5.0, making backup to %s", bakpath.c_str()));
  }
  

  // add the file to the recent files list and sync the options file
  add_recent_file(file);

  workbench_WorkbenchRef wb(get_root());

  wb->doc(doc);
  doc->owner(wb);

  // check if paperType is properly set (if its null it could be a custom type
  // not available locally)
  if (!doc->pageSettings()->paperType().is_valid())
  {
    doc->pageSettings()->paperType(grt::find_named_object_in_list(get_root()->options()->paperTypes(), "A4"));
  }

  FOREACH_COMPONENT(_components, iter)
    (*iter)->unblock_model_notifications();

  reset_document();

  FOREACH_COMPONENT(_components, iter)
    (*iter)->document_loaded();
  
  realize();

  _filename= file;
  _save_point= get_grt()->get_undo_manager()->get_latest_undo_action();

  request_refresh(RefreshDocument, "");

  show_status_text(_("Document loaded."));

  _manager->unblock_idle_tasks();

  _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind<std::string>(perform_command, "reset_layout"), false));
  
  return true;
}


void WBContext::close_document(bool destroying)
{
  delete _file;
  _file= 0;

  // reset undo manager before destroying views to make sure that old refs to
  // figures will be released and bridges will be deleted 1st
  get_grt()->get_undo_manager()->reset();

  // close all open editors synchronously
  if (!destroying)
  {
    execute_in_main_thread("close editors", 
      sigc::bind<RefreshType,std::string,NativeHandle>(refresh_gui, RefreshCloseEditor, "", (NativeHandle)0), true);
  }

  workbench_DocumentRef doc(get_document());

  // unrealize all models
  if (doc.is_valid() && doc->physicalModels().is_valid())
  {
    for (size_t c= doc->physicalModels().count(), i= 0; i < c; i++)
    {
      doc->physicalModels().get(i)->get_data()->unrealize();
    }
  }

  if (!destroying)
  {
    execute_in_main_thread("Close document", 
      sigc::bind<RefreshType,std::string,NativeHandle>(refresh_gui, RefreshCloseDocument, "", (NativeHandle)0), true);
  }

  clear_refreshes();
}


/** Tells backend that the frontend has finished doing cleanup for document close.
 */
void WBContext::close_document_finish()
{
  workbench_DocumentRef doc(get_document());
  
  get_root()->doc(workbench_DocumentRef());
  
  // reset circular references in the document once the app goes idle
  if (doc.is_valid())
    doc->reset_references();
}


//--------------------------------------------------------------------------------
// Saving

grt::ValueRef WBContext::save_grt(grt::GRT *grt)
{
  // update last change timestamp
  app_DocumentInfoRef info(get_document()->info());

  info->dateChanged(fmttime(0, DATETIME_FMT));

  try
  {
    _file->store_document(grt, get_document());
  } 
  catch (std::exception &exc)
  {
    show_exception(_("Could not store document data."), exc);
    return grt::ValueRef(0);
  }

  try
  {
    _file->save_to(_filename);
  }
  catch (std::exception &exc)
  {
    show_exception(strfmt(_("Could not save document to %s"), _filename.c_str()), exc);
    return grt::ValueRef(0);
  }

  // add the file to the recent files list and sync the options file
  add_recent_file(_filename);

  _save_point= grt->get_undo_manager()->get_latest_undo_action();
  request_refresh(RefreshDocument, "");

  return grt::IntegerRef(1);
}


std::string WBContext::get_filename() const
{
  return _filename;
}


bool WBContext::save_as(const std::string &path)
{
  if (path.empty())
  {
    std::string s= 
      execute_in_main_thread<std::string>("save", 
        sigc::bind<std::string,std::string,std::string>(show_file_dialog,
              "save", _("Save Model"), "mwb"));
    if (s.empty())
      return false;

    if (!bec::has_suffix(s, ".mwb"))
      s.append(".mwb");
    
    _filename= s;
  }
  else
    _filename= path;

  try {
    show_status_text(strfmt(_("Saving %s..."), _filename.c_str()));
    grt::ValueRef ret= 
      execute_in_grt_thread("Save", sigc::mem_fun(this, &WBContext::save_grt));

    if (grt::IntegerRef::cast_from(ret) == 1)
    {
      show_status_text(strfmt(_("%s saved."), _filename.c_str()));
      return true;
    }
    else
      show_status_text(_("Error saving document."));
  }
  catch (grt::grt_runtime_error &error) 
  {
    show_exception(_("Error saving document"), error);
    show_status_text(_("Error saving document."));
  }
  return false;
}


bool WBContext::has_unsaved_changes()
{
  if (get_grt()->get_undo_manager()->get_latest_undo_action() != _save_point)
    return true;

  if (_file && _file->has_unsaved_changes())
    return true;

  return false;
}


bool WBContext::save_changes()
{
  save_as(_filename);

  return !has_unsaved_changes();
}


#endif // Document____


#ifndef BasicExport____

void WBContext::export_png(const std::string &path)
{
  ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(get_active_main_form());
  if (form)
  {
    show_status_text(strfmt(_("Exporting to %s..."), path.c_str()));
    try
    {
      form->get_view()->export_png(path, true);
      show_status_text(strfmt(_("Exported diagram image to %s"), path.c_str()));
    }
    catch (std::exception &exc)
    {
      show_status_text(_("Could not export to PNG file."));
      show_exception(_("Export to PNG"), exc);
    }
  }
  else
    show_error(_("Cannot Export Diagram"), _("Current diagram cannot be exported as image, please select a diagram first."));
}


void WBContext::export_pdf(const std::string &path)
{
  ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(get_active_main_form());
  if (form)
  {
    mdc::Size size= form->get_view()->get_total_view_size();
    double scale= get_document()->pageSettings()->scale();

    size.width= size.width / scale * 2.834;
    size.height= size.height / scale * 2.834;

    show_status_text(strfmt(_("Exporting full model diagram to %s..."), path.c_str()));

    try
    {
      form->get_view()->export_pdf(path, size);
      show_status_text(strfmt(_("Exported PDF to %s"), path.c_str()));
    }
    catch (std::exception &exc)
    {
      show_status_text(_("Could not export to PDF"));
      show_exception(_("Export to PDF"), exc);
    }
  }
  else
    show_error(_("Cannot Export Diagram"), _("Current diagram cannot be exported as image, please select a diagram first."));
}


void WBContext::export_svg(const std::string &path)
{
  ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(get_active_main_form());
  if (form)
  {
    mdc::Size size= form->get_view()->get_total_view_size();
    double scale= get_document()->pageSettings()->scale();

    size.width= MM_TO_PT(size.width / scale);
    size.height= MM_TO_PT(size.height / scale);
    
    show_status_text(strfmt(_("Exporting full model diagram to %s..."), path.c_str()));

    try
    {
      form->get_view()->export_svg(path, size);
      show_status_text(strfmt(_("Exported SVG to %s"), path.c_str()));
    }
    catch (std::exception &exc)
    {
      show_status_text(_("Could not export to SVG"));
      show_exception(_("Export to SVG"), exc);
    }
  }
  else
    show_error(_("Cannot Export Diagram"), _("Current diagram cannot be exported as image, please select a diagram first."));
}


void WBContext::export_ps(const std::string &path)
{
  ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(get_active_main_form());
  if (form)
  {
    mdc::Size size= form->get_view()->get_total_view_size();
    double scale= get_document()->pageSettings()->scale();

    size.width= MM_TO_PT(size.width / scale);
    size.height= MM_TO_PT(size.height / scale);

    show_status_text(strfmt(_("Exporting full model diagram to %s..."), path.c_str()));

    try
    {
      form->get_view()->export_ps(path, size);
      show_status_text(strfmt(_("Exported PS to %s"), path.c_str()));
    }
    catch (std::exception &exc)
    {
      show_status_text(_("Could not export to PS file"));
      show_exception(_("Export to PS File"), exc);
    }
  }
  else
    show_error(_("Cannot Export Diagram"), _("Current diagram cannot be exported as image, please select a diagram first."));
}

#endif // BasicExport____


#ifndef Plugins____

//--------------------------------------------------------------------------------
// Plugin Handling

bool WBContext::check_plugin_runnable(const app_PluginRef &plugin, const std::string &extra_arg)
{
  for (size_t c= plugin->inputValues().count(), i= 0; i < c; i++)
  {
    app_PluginInputDefinitionRef pdef(plugin->inputValues()[i]);
    
    if (pdef.class_name() == app_PluginInputDefinition::static_class_name())
    {
      if (pdef->name() == "string")
      {
        if (i != 0)
          g_warning("plugin %s expects 'string' argument in invalid order (should be 0)", plugin->name().c_str());
      }
      else
        g_warning("plugin input '%s' for %s is unknown", pdef->name().c_str(), plugin->name().c_str());
    }
    else if (!check_plugin_input_available(pdef))
    {
      return false;
    }
  }
  return true;
}


bool WBContext::check_plugin_input_available(const app_PluginInputDefinitionRef &pdef)
{
  if (pdef.is_instance(app_PluginObjectInput::static_class_name()))
  {
    app_PluginObjectInputRef oinput(app_PluginObjectInputRef::cast_from(pdef));
    grt::MetaClass *wanted_struct= _manager->get_grt()->get_metaclass(oinput->objectStructName().c_str());
    if (!wanted_struct)
      return false;

    if (wanted_struct->is_a(db_Catalog::static_class_name()))
    {
      model_ModelRef model(get_ui()->get_active_model(true));

      if (model.is_instance(workbench_physical_Model::static_class_name()))
      {
        db_CatalogRef catalog(workbench_physical_ModelRef::cast_from(model)->catalog());

        if (catalog.is_instance(wanted_struct->name()))
          return true;
      }
      return false;
    }
    else if (wanted_struct->is_a(model_Model::static_class_name()))
    {
      model_ModelRef model(get_ui()->get_active_model(true));
      if (model.is_valid() && model.is_instance(wanted_struct->name()))
        return true;
      return false;
    }
    else if (wanted_struct->is_a(model_Diagram::static_class_name()))
    {
      // for views the plugins require it to be the active view (not just main)
      model_DiagramRef view(get_ui()->get_active_model_diagram(false));

      if (view.is_valid() && view.is_instance(wanted_struct->name()))
        return true;
    }
    else
    {
      ModelDiagramForm *view= dynamic_cast<ModelDiagramForm*>(get_active_form());
      if (view)
      {
        grt::ListRef<model_Object> selection(view->get_selection());

        if (selection.count() == 1)
        {
          model_ObjectRef object(selection[0]);

          // check if object itself is wanted
          if (object.is_instance(wanted_struct->name()))
            return true;
          else
          {
            // check if object represented by this is wanted
            FOREACH_COMPONENT(_components, iter)
            {
              GrtObjectRef obj = (*iter)->get_object_for_figure(object);
              
              if (obj.is_valid() && obj.is_instance(wanted_struct->name()))
                return true;
            }
          }
        }
      }
      
      OverviewBE *overview= dynamic_cast<OverviewBE*>(get_active_form());
      if (overview)
      {
        grt::ListRef<GrtObject> selection(overview->get_selection());

        if (selection.count() == 1)
        {
          GrtObjectRef object(selection[0]);

          if (object.is_instance(oinput->objectStructName()))
            return true;
        }
      }
    }
    return false;
  }
  else if (pdef.is_instance(app_PluginSelectionInput::static_class_name()))
  {
    model_DiagramRef view(get_ui()->get_active_model_diagram(false));

    if (view.is_valid())
    {
      if (!_plugin_manager->check_plugin_input(pdef, view->selection()))
      {
        grt::ListRef<GrtObject> list(get_grt());

        for (size_t c= view->selection().count(), i= 0; i < c; i++)
        {
          model_ObjectRef fig(view->selection()[i]);

          FOREACH_COMPONENT(_components, iter)
          {
            if ((*iter)->handles_figure(fig))
            {
              GrtObjectRef obj((*iter)->get_object_for_figure(fig));
              list.insert(obj);
              break;
            }
          }
        }

        return _plugin_manager->check_plugin_input(pdef, list);
      }
      return true;
    }
    return false;
  }
  else if (pdef.is_instance(app_PluginFileInput::static_class_name()))
    return true;
 
  return false;
}


grt::ValueRef WBContext::get_plugin_input_value(const app_PluginInputDefinitionRef &pdef)
{
  if (pdef.is_instance(app_PluginObjectInput::static_class_name()))
  {
    app_PluginObjectInputRef oinput(app_PluginObjectInputRef::cast_from(pdef));

    grt::MetaClass *wanted_struct(_manager->get_grt()->get_metaclass(oinput->objectStructName()));

    if (oinput->objectStructName() == db_Catalog::static_class_name())
    {
      model_ModelRef model(get_ui()->get_active_model(true));

      if (model.is_valid() && model.is_instance(workbench_physical_Model::static_class_name()))
        return workbench_physical_ModelRef::cast_from(model)->catalog();

      throw grt::grt_runtime_error("Cannot execute command.", "The active model is not of the expected type.");
    }
    else if (wanted_struct->is_a(model_Model::static_class_name()))
    {
      model_ModelRef model(get_ui()->get_active_model(true));

      if (model.is_valid())
        return model;

      throw grt::grt_runtime_error("Cannot execute plugin", "The active diagram is not of the expected type.");
    }
    else if (wanted_struct->is_a(model_Diagram::static_class_name()))
    {
      model_DiagramRef view(get_ui()->get_active_model_diagram(false));

      if (view.is_valid() && view.is_instance(wanted_struct->name()))
        return view;

      throw grt::grt_runtime_error("Cannot execute plugin", "A model diagram must be selected.");
    }
    else // check selection
    { 
      ModelDiagramForm *view= dynamic_cast<ModelDiagramForm*>(get_active_form());
      if (view)
      {
        grt::ListRef<model_Object> selection(view->get_selection());

        if (selection.count() == 1)
        {
          model_ObjectRef object(selection[0]);

          // check if object itself is wanted
          if (object.is_instance(oinput->objectStructName()))
            return object;
          else
          {
            // check if object represented by this is wanted
            FOREACH_COMPONENT(_components, iter)
            {
              if ((*iter)->handles_figure(object))
                return (*iter)->get_object_for_figure(object);
            }
          }
        }
      }
      
      OverviewBE *overview= dynamic_cast<OverviewBE*>(get_active_form());
      if (overview)
      {
        grt::ListRef<GrtObject> selection(overview->get_selection());

        if (selection.count() == 1)
        {
          GrtObjectRef object(selection[0]);

          if (object.is_instance(oinput->objectStructName()))
            return object;
        }
      }
    }
  }
  else if (pdef.is_instance(app_PluginSelectionInput::static_class_name()))
  {
    app_PluginSelectionInputRef sinput(app_PluginSelectionInputRef::cast_from(pdef));
    model_DiagramRef view(get_ui()->get_active_model_diagram(false));

    //XXX make it conform to requested type
    if (view.is_valid())
      return view->selection();
  }
  return grt::ValueRef();
}



void WBContext::execute_plugin(const std::string &plugin_name, const grt::BaseListRef &args)
{  
  app_PluginRef plugin(_plugin_manager->get_plugin(plugin_name));

  if (!plugin.is_valid())
    throw grt::grt_runtime_error("Invalid plugin", "Invalid plugin "+plugin_name);

  // build the argument list
  grt::BaseListRef fargs(_manager->get_grt());
  
  const size_t c= plugin->inputValues().count();
  const size_t argc = args.is_valid() ? args.count() : 0;
  for (size_t i= 0; i < c; i++)
  {
    if (i < argc && args[i].is_valid())
      fargs.ginsert(args[i]);
    else
    {
      app_PluginInputDefinitionRef pdef(plugin->inputValues().get(i));

      if (pdef.is_instance(app_PluginFileInput::static_class_name()))
      {
        app_PluginFileInputRef finput(app_PluginFileInputRef::cast_from(pdef));
        std::string fname;

        fname= show_file_dialog(finput->dialogType(), finput->dialogTitle(), finput->fileExtensions());
        if (fname.empty())
        {
          show_status_text(_("Cancelled."));
          return;
        }
        fargs.ginsert(grt::StringRef(fname));
      }
      else
      {
        grt::ValueRef value(get_plugin_input_value(pdef));

        if (value.is_valid())
          fargs.ginsert(value);
        else
        {
          g_message("Cannot satisfy plugin input for %s: %s", plugin_name.c_str(), pdef.class_name().c_str());
          g_message("%s", pdef.repr().c_str());

          throw grt::grt_runtime_error("Cannot execute "+plugin_name,
            "Plugin requires unhandled argument type.");
        }
      }
    }
  }

  _manager->execute_grt_task(strfmt(_("Performing %s..."), plugin->caption().c_str()),
    sigc::bind<app_PluginRef, grt::BaseListRef>(sigc::mem_fun(this, &WBContext::execute_plugin_grt), plugin, fargs),
    sigc::bind<app_PluginRef>(sigc::mem_fun(this, &WBContext::plugin_finished), plugin),
    *plugin->showProgress() != 0);
}


grt::ValueRef WBContext::execute_plugin_grt(grt::GRT *grt, const app_PluginRef &plugin, const grt::BaseListRef &args)
{
  if (plugin.is_instance(app_DocumentPlugin::static_class_name()))
  {
    throw std::logic_error("not implemented");
    /* FIXME
    app_DocumentPluginRef doc_plugin(app_DocumentPluginRef::cast_from(plugin));
    workbench_DocumentRef document(get_document());
    bool flag= false;

    for (size_t c= doc_plugin.documentStructNames().count(), i= 0; i < c; i++)
    {
      if (document.is_instance(*doc_plugin.documentStructNames().get(i)))
      {
        flag= true;
        break;
      }
    }

    if (flag)
    {
      _plugin_manager->open_plugin(plugin, document);
    }
    else
      throw grt::grt_runtime_error(_("Invalid document type for plugin."),
            _("The plugin cannot be executed because the current document type is not known to it."));
          */
  }
  else
  {
    GTimer *timer= g_timer_new();
    g_timer_start(timer);

    // lock the canvas so that it doesnt keep refreshing all the time.
    // XXX we have to find some way to allow plugins control refresh freezing
    ModelDiagramForm *view= dynamic_cast<ModelDiagramForm*>(_uicontext->get_active_main_form());
    mdc::CanvasView *cview= 0;

    if (view) 
    {
      cview= view->get_view();
      cview->lock_redraw();
    }
    
    try
    {
      bool skip_undo= false;

      if (*plugin->pluginType() != "normal")
        skip_undo= true;

      grt::AutoUndo undo(get_grt(), skip_undo);
      
      _plugin_manager->open_plugin(plugin, args);

      undo.end_or_cancel_if_empty(plugin->caption());
      
      request_refresh(RefreshSchema, "");
    }
    catch (std::exception &exc)
    {
      grt->send_error(strfmt("Error executing plugin %s: %s", plugin->name().c_str(), exc.what()));
    }

    // form can get destroyed after a plugin is executed
    if (cview && _uicontext->get_diagram_form(cview))
      cview->unlock_redraw();

    g_timer_stop(timer);
    double elapsed= g_timer_elapsed(timer, NULL);
    g_timer_destroy(timer);

    grt->send_verbose(strfmt("%s finished in %.2fs\n", plugin->name().c_str(), elapsed));
  }

  return grt::ValueRef();
}


void WBContext::plugin_finished(const grt::ValueRef &result, const app_PluginRef &plugin)
{
  if (*plugin->showProgress())
    show_status_text(strfmt(_("%s done."), plugin->caption().c_str()));

  // request a refresh on the toolbars and menus in case some button state has changed
  request_refresh(RefreshToolbar, "");

  request_refresh(RefreshMenubar, "");
}


//--------------------------------------------------------------------------------
// Object Editors

void WBContext::close_gui_plugin(NativeHandle handle)
{
  _plugin_manager->forget_gui_plugin_handle(handle);
}


void WBContext::register_builtin_plugins(grt::ListRef<app_Plugin> plugins)
{
  _plugin_manager->register_plugins(plugins);
}


void WBContext::open_object_editor(const GrtObjectRef &object, bool newwindow)
{
  try 
  {
    grt::BaseListRef args(_manager->get_grt(), AnyType);
    args.ginsert(object);

    bec::GUIPluginFlags flags= bec::NoFlags;
    if (newwindow)
      flags= (bec::GUIPluginFlags)(flags | bec::ForceNewWindowFlag);

    app_PluginRef plugin(_plugin_manager->select_plugin_for_input("catalog/Editors", args));
    if (!plugin.is_valid())
      plugin= _plugin_manager->select_plugin_for_input("model/Editors", args);

    if (plugin.is_valid())
      _plugin_manager->open_gui_plugin(plugin, args, flags);
    else
      show_error(_("Edit Object"), strfmt(_("No suitable editor found for object of type '%s'."), object.get_metaclass()->get_attribute("caption").c_str()));
  } catch (grt::grt_runtime_error &exc) {
    show_exception(_("Edit Object"), exc);
  }
}


#endif // Plugins____


#ifndef Diagrams_and_Canvas____

//--------------------------------------------------------------------------------
// Canvas Management


void WBContext::add_new_diagram(const model_ModelRef &model)
{
  show_status_text(_("Creating Diagram..."));
  
  model_DiagramRef view(model_DiagramRef::cast_from(execute_in_grt_thread("Create new diagram",
        sigc::hide(sigc::bind(sigc::mem_fun(model.content(), &model_Model::addNewDiagram), true)))));

  if (view.is_valid())
  {
    view->get_data()->realize();
  }

  show_status_text(_("Diagram added."));
}


void WBContext::switch_diagram(const model_DiagramRef &view)
{
  switched_view(view->get_data()->get_canvas_view());
}


mdc::CanvasView *WBContext::create_canvas_view_main(const model_DiagramRef &mview)
{
  ModelDiagramForm *view_form= 0;

  FOREACH_COMPONENT(_components, iter)
  {
    if (mview.is_instance((*iter)->get_diagram_class_name())
      && (*iter)->get_diagram_class_name() != model_Diagram::static_class_name())
    {
      view_form= new ModelDiagramForm(*iter, mview);
      break;
    }
  }

  // fallback
  if (!view_form)
    view_form= new ModelDiagramForm(get_component_named("basic"), mview);

  if (!view_form)
  {
    show_internal_error("Error adding a new diagram.", "Unknown diagram type.");
    return 0;
  }

  mview->signal_objectActivated().connect(sigc::mem_fun(this, &WBContext::activate_canvas_object));

  mview->signal_refreshDisplay().connect(sigc::hide(sigc::bind(sigc::mem_fun(this,&WBContext::request_refresh), RefreshLayers, "", (NativeHandle)0)));
  
  mview->signal_list_changed().connect(sigc::bind(sigc::mem_fun(this, &WBContext::diagram_object_list_changed), view_form));

  _uicontext->register_diagram_form(view_form);

  // forward creation to frontend
  mdc::CanvasView *view= create_view(mview.id(), mview->name());
  if (view)
  {
    _using_opengl = view->has_gl();
      
    view_form->attach_canvas_view(view);

    _uicontext->notify_diagram_created(view_form);

    request_refresh(RefreshNewView, mview.id(), (NativeHandle)view->get_user_data());
  }
  else
  {
    delete view_form;
    show_internal_error("Error adding a new diagram.", "Frontend did not return a diagram.");
  }
  
  if (getenv("DEBUG_CANVAS"))
    view->enable_debug(true);
  
  return view;
}


bool WBContext::delete_diagram(const model_DiagramRef &view)
{
  grt::AutoUndo undo(get_grt());
  view->owner()->diagrams().remove_value(view);
  undo.end(strfmt(_("Delete Diagram '%s'"), view->name().c_str()));
  return true;
}


/** 
 ****************************************************************************
 * @brief Update the canvas view according to app.PageSettings 
 *
 * This will update the page size and total view size to reflect changes
 * to the page/print settings.
 ****************************************************************************
 */
void WBContext::update_view_page_settings()
{
  grt::ListRef<model_Diagram> views(grt::ListRef<model_Diagram>::cast_from(get_document()->logicalModel()->diagrams()));
  for (size_t vc= views.count(), v= 0; v < vc; v++)
  {
    views[v]->get_data()->update_size();
  }

  grt::ListRef<workbench_physical_Model> models(get_document()->physicalModels());
  for (size_t c= models.count(), i= 0; i < c; i++)
  {
    views= grt::ListRef<model_Diagram>::cast_from(models[i]->diagrams());
    for (size_t vc= views.count(), v= 0; v < vc; v++)
    {
      views[v]->get_data()->update_from_page_size();
    }
  }
}
#endif // Diagrams_and_Canvas____


#ifndef Canvas_Objects____
/** 
 ****************************************************************************
 * @brief Delete a canvas object from the canvas/model
 * 
 * Will remove the figure from the model and delete all known
 * references to it. There may be some references left, which will cause
 * the GRT object and its bridge to stay alive for some more time, so the
 * bridge is marked as invalid to prevent the canvas item from appearing
 * on screen.
 *
 * @param figure to be removed
 ****************************************************************************
 */
bool WBContext::delete_object(model_ObjectRef object, std::map<std::string, bool>& options)
{
  model_DiagramRef view(model_DiagramRef::cast_from(object->owner()));

  FOREACH_COMPONENT(_components, iter)
  {
    if ((*iter)->handles_figure(object))
      return (*iter)->delete_model_object(object, options);
  }
  return false;
}


void WBContext::delete_canvas_item(mdc::CanvasItem *item)
{
  model_DiagramRef view(get_view_with_id(item->get_layer()->get_view()->get_tag()));

  model_FigureRef figure;
  figure= grt::find_object_in_list(view->figures(), item->get_tag());
  if (figure.is_valid())
  {
    std::map<std::string, bool> map;
    delete_object(figure, map);
    return;
  }

  model_ConnectionRef conn;
  conn= grt::find_object_in_list(view->connections(), item->get_tag());
  if (conn.is_valid())
  {
    std::map<std::string, bool> map;
    delete_object(conn, map);
    return;
  }

  model_LayerRef layer;
  layer= grt::find_object_in_list(view->layers(), item->get_tag());
  if (layer.is_valid())
  {
    std::map<std::string, bool> map;
    delete_object(layer, map);
    return;
  }

  throw std::logic_error("Could not find GRT object for canvas item");
}

struct sortplugin
{
  bool operator ()(const app_PluginRef &a, const app_PluginRef &b) const
  {
    return a->rating() < b->rating();
  }
};


int WBContext::add_object_plugins_to_popup_menu(const grt::ListRef<GrtObject> &objects,
                                                const std::list<std::string> &groups,
                                                bec::MenuItemList &items)
{
  int count= 0;
  // look for plugins that take this object type as input
  std::vector<app_PluginRef> plugins(get_plugin_manager()->get_plugins_for_objects(grt::ObjectListRef::cast_from(objects)));



  // sort by rating

  std::sort(plugins.begin(), plugins.end(), sortplugin());

  for (std::vector<app_PluginRef>::const_iterator iter= plugins.begin(); iter != plugins.end(); ++iter)
  {
    bool match= false;
//    bool matchPrefix= false;

    for (size_t c= (*iter)->groups().count(), i= 0; i < c; i++)
    {
      std::string str= (*iter)->groups().get(i);
      for (std::list<std::string>::const_iterator ctx= groups.begin();
        ctx != groups.end(); ++ctx)
      {
        if ((*ctx)[ctx->size()-1] == '*' && g_strncasecmp(ctx->c_str(), str.c_str(), (guint) ctx->size() - 1) == 0)
        {
          match= true;
          break;
        }
        else if ((*ctx)[ctx->size() - 1] != '*' && g_strcasecmp(ctx->c_str(), str.c_str()) == 0)
        {
          match= true;
          break;
        }
      }
    }
    if (!match)
      continue;

    bec::MenuItem item;

    item.type= MenuAction;
    item.caption= *(*iter)->caption() + ((*iter)->pluginType()=="gui"?"...":"");
    item.checked= false;
    item.enabled= check_plugin_runnable(*iter, "");
    item.shortcut= "";
    item.name= "plugin:"+*(*iter)->name();
    if (item.caption.empty())
      item.caption= item.name;
    items.push_back(item);
    count++;

    if ((*iter)->groups().get_index("catalog/Editors") != BaseListRef::npos || (*iter)->groups().get_index("model/Editors") != BaseListRef::npos)
    {
      app_PluginRef plugin(_plugin_manager->get_plugin("wb.edit.editSelectedFigureInNewWindow"));
      
      item.caption= _("Edit in New Window...");
      item.name= "plugin:"+*plugin->name();
      item.type= MenuAction;
      item.enabled= check_plugin_runnable(plugin, "");
      items.push_back(item);
      count++;
    }
  }

  return count;
}


int WBContext::get_object_list_popup_items(bec::UIForm *form, 
				      const grt::ListRef<GrtObject> &objects,
                                      const std::string &label,
                                      const std::list<std::string> &groups, 
                                      bec::MenuItemList &items)
{
  size_t ocount= items.size();

  // add generic object commands
  bec::MenuItem item;

  // add separator if needed
  if (items.size() > ocount)
  {
    bec::MenuItem sep;
    sep.type= MenuSeparator;
    items.push_back(sep);
  }

  // add some builtin items
  if (objects.count() == 0)
  {
    bec::MenuItem item;

    item.enabled= form->can_paste();
    item.caption= label.empty() ? _("Paste") : strfmt(_("Paste %s"), label.c_str());
    item.name= "builtin:paste";
    item.type= MenuAction;
    items.push_back(item);
    
    item.type= MenuSeparator;
    items.push_back(item);
  }
  else
  {
    bec::MenuItem item;

    item.checked= false;
    item.enabled= form->can_cut();
    // edit menu stuff
    item.caption= label.empty() ? _("Cut") : strfmt(_("Cut %s"), label.c_str());
    item.name= "builtin:cut";
    item.type= MenuAction;
    items.push_back(item);

    item.enabled= form->can_copy();
    item.caption= label.empty() ? _("Copy") : strfmt(_("Copy %s"), label.c_str());
    item.name= "builtin:copy";
    item.type= MenuAction;
    items.push_back(item);

    item.type= MenuSeparator;
    items.push_back(item);
  }

  size_t old_count= items.size();

  if (objects.count() > 0)
  {
    add_object_plugins_to_popup_menu(objects, groups, items);

    grt::ListRef<GrtObject> contents(get_grt());
    for (size_t c= objects.count(), i= 0; i < c; i++)
    {
      if (objects[i].is_instance(model_Object::static_class_name()))
      {
        FOREACH_COMPONENT(_components, compo)
        {
          GrtObjectRef content((*compo)->get_object_for_figure(model_ObjectRef::cast_from(objects[i])));

          if (content.is_valid())
          {
            contents.insert(content);
            break;
          }
        }
      }
    }
  
    if (contents.count() > 0)
      add_object_plugins_to_popup_menu(contents, groups, items);
  }

  // add some more builtin items
  {
    bec::MenuItem item;

    item.checked= false;
    item.enabled= form->can_delete();

    if (old_count != items.size())
    {
      item.type= MenuSeparator;
      items.push_back(item);
    }
    item.caption= label.empty() ? _("Delete") : strfmt(_("Delete %s"), label.c_str());
    item.name= "builtin:delete";
    item.type= MenuAction;
    items.push_back(item);
  }
  
  return int(items.size() - ocount);
}


std::string WBContext::get_object_tooltip(const model_ObjectRef &object, mdc::CanvasItem *item)
{
  FOREACH_COMPONENT(_components, iter)
  {
    if ((*iter)->handles_figure(object))
      return (*iter)->get_object_tooltip(object, item);
  }
  return "";
}

#endif // Canvas_Objects____


#ifndef Bridge_Delegate____

#include "wb_model_file.h"

cairo_surface_t *WBContext::fetch_image(const std::string &file)
{
  return _file->get_image(file);
}


std::string WBContext::attach_image(const std::string &file)
{
  return _file->add_image_file(file);
}


void WBContext::release_image(const std::string &file)
{
  // QQQ
  // _file->release_image(file);
}


/** 
 ****************************************************************************
 * @brief Delegate method for creating canvas views
 *
 * This is called by the GRT bridge when a new view object is created.
 * It will in turn, call a frontend supplied callback which should create
 * the canvas view (and its viewer) and then return the canvas view.
 *
 * The free_canvas_view() method will be called once the view can be freed.
 * 
 * @param name the name of the canvas. Will be the object-id of the view.
 *****************************************************************************/
mdc::CanvasView *WBContext::create_canvas_view(const model_DiagramRef &view)
{
  return execute_in_main_thread<mdc::CanvasView*>("create_diagram", sigc::bind<model_DiagramRef>
    (sigc::mem_fun(this, &WBContext::create_canvas_view_main), view));
}


void WBContext::free_canvas_view(mdc::CanvasView *view)
{
  // this function is expected to be called from the main thread

  // forward to frontend
  destroy_view(view);
  
  _uicontext->notify_diagram_destroyed(_uicontext->get_diagram_form(view));

//  execute_in_main_thread<void>("destroy view", 
//    sigc::bind<mdc::CanvasView*>(destroy_view, view));  
}



void WBContext::diagram_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, ModelDiagramForm *vform)
{
  if (vform == get_active_main_form())
  {
    if (vform->get_model_diagram()->selection().valueptr() == list)
    {
      request_refresh(RefreshSelection, "", reinterpret_cast<NativeHandle>(vform->get_frontend_data()));
      request_refresh(RefreshLayers, grt::ObjectRef::cast_from(value).id(), 0);
    }
    else if (value.is_valid())
    {
      GrtObjectRef object(GrtObjectRef::cast_from(value));

      request_refresh(RefreshLayers, "", 0);
      
      if (!added)
        request_refresh(RefreshCloseEditor, object.id(), 0);
    }
  }
}


void WBContext::activate_canvas_object(const model_ObjectRef &object, int flags)
{
  bool newwindow= flags & 1;

  FOREACH_COMPONENT(_components, iter)
  {
    if ((*iter)->handles_figure(object))
      (*iter)->activate_canvas_object(object, newwindow);
  }
}


#endif // Bridge_Delegate____


#ifndef Utilities____
workbench_WorkbenchRef WBContext::get_root()
{
  return workbench_WorkbenchRef::cast_from(_manager->get_grt()->get("/wb"));
}


workbench_DocumentRef WBContext::get_document()
{
  return workbench_DocumentRef::cast_from(_manager->get_grt()->get("/wb/doc"));
}


grt::DictRef WBContext::get_wb_options()
{
  return get_root()->options()->options();
}


void WBContext::execute_in_main_thread(const std::string &name, 
                              const sigc::slot<void> &function, bool wait) THROW (grt::grt_runtime_error)
{
  _manager->get_dispatcher()->call_from_main_thread<void>(function, wait);
}


grt::ValueRef WBContext::execute_in_grt_thread(const std::string &name, 
                                                   const sigc::slot1<grt::ValueRef, grt::GRT*> &function) THROW (grt::grt_runtime_error)
{
  return _manager->get_dispatcher()->execute_simple_function(name, function);
}


void WBContext::execute_async_in_grt_thread(const std::string &name, 
                                            const sigc::slot1<grt::ValueRef, grt::GRT*> &function) THROW (grt::grt_runtime_error)
{
  _manager->get_dispatcher()->execute_async_function(name, function);
}



void WBContext::show_exception(const std::string &operation, const std::exception &exc)
{
  const grt::grt_runtime_error *rt= dynamic_cast<const grt::grt_runtime_error*>(&exc);

  if (rt)
  {
    if (_manager->in_main_thread())
      show_error(operation, std::string(rt->what())+"\n"+rt->detail);
    else
      _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind<std::string,std::string>(show_error, operation, std::string(rt->what())+"\n"+rt->detail), false));
  }
  else
  {
    if (_manager->in_main_thread())
      show_error(operation, exc.what());
    else
      _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind<std::string,std::string>(show_error, operation, exc.what()), false));
  }
}


void WBContext::show_exception(const std::string &operation, const grt::grt_runtime_error &exc)
{
  if (_manager->in_main_thread())
    show_error(operation, std::string(exc.what())+"\n"+exc.detail);
  else
    _manager->run_when_idle(sigc::bind_return<bool>(sigc::bind<std::string,std::string>(show_error, operation, std::string(exc.what())+"\n"+exc.detail), false));
}


model_DiagramRef WBContext::get_view_with_id(const std::string &id)
{
  return model_DiagramRef::cast_from(_manager->get_grt()->find_object_by_id(id, "/wb/doc"));
}

#endif // Utilities____



std::string WBContext::create_attached_file(const std::string &group, const std::string &tmpl)
{
  if (group == "script")
    return _file->add_script_file(tmpl);
  else if (group == "note")
    return _file->add_note_file(tmpl);
  else
    throw std::invalid_argument("invalid attachment group name");
}

std::string WBContext::recreate_attached_file(const std::string &name, const std::string &data)
{
  _file->undelete_file(name);
  _file->set_file_contents(name, data);
  return name;
}

void WBContext::save_attached_file_contents(const std::string &name, const char *data, size_t size)
{
  _file->set_file_contents(name, data, size);
}

std::string WBContext::get_attached_file_contents(const std::string &name)
{
  return _file->get_file_contents(name);
}


std::string WBContext::get_attached_file_tmp_path(const std::string &name)
{
  return _file->get_path_for(name);
}


int WBContext::export_attached_file_contents(const std::string &name, const std::string &export_to)
{
  try
  {
    _file->copy_file_to(name, export_to);
  }
  catch (grt::os_error &exc)
  {
    g_warning("Error exporting %s: %s", name.c_str(), exc.what());
    return 0;
  }
  return 1;
}


void WBContext::delete_attached_file(const std::string &name)
{
  _file->delete_file(name);
}


/**
 * Returns the value for a state given by name as string, if it exists or the default value if not.
 */
std::string WBContext::read_state(const std::string &name, const std::string &domain, 
                                   const std::string &default_value)
{
  grt::DictRef dict= get_root()->state();

  return dict.get_string(domain+":"+name, default_value);
}

/**
 * Returns the value for a state given by name as int, if it exists or the default value if not.
 */
int WBContext::read_state(const std::string &name, const std::string &domain, const int &default_value)
{
  grt::DictRef dict= get_root()->state();

  return dict.get_int(domain+":"+name, default_value);
}

/**
 * Returns the value for a state given by name as double, if it exists or the default value if not.
 */
double WBContext::read_state(const std::string &name, const std::string &domain, const double &default_value)
{
  grt::DictRef dict= get_root()->state();

  return dict.get_double(domain+":"+name, default_value);
}

/**
 * Returns the value for a state given by name as bool, if it exists or the default value if not.
 */
bool WBContext::read_state(const std::string &name, const std::string &domain, const bool &default_value)
{
  grt::DictRef dict= get_root()->state();

  return dict.get_int(domain+":"+name, default_value ? 1 : 0) == 1;
}

/**
 * Stores the given string state value in the grt tree.
 */
void WBContext::save_state(const std::string &name, const std::string &domain, const std::string &value)
{
  grt::DictRef dict= get_root()->state();

  // Set new value for the given state name in that domain.
  dict.gset(domain+":"+name, value);
}

/**
 * Stores the given int state value in the grt tree.
 */
void WBContext::save_state(const std::string &name, const std::string &domain, const int &value)
{
  grt::DictRef dict= get_root()->state();

  // Set new value for the given state name in that domain.
  dict.gset(domain+":"+name, value);
}

/**
 * Stores the given double state value in the grt tree.
 */
void WBContext::save_state(const std::string &name, const std::string &domain, const double &value)
{
  grt::DictRef dict= get_root()->state();

  // Set new value for the given state name in that domain.
  dict.gset(domain+":"+name, value);
}

/**
 * Stores the given bool state value in the grt tree.
 */
void WBContext::save_state(const std::string &name, const std::string &domain, const bool &value)
{
  grt::DictRef dict= get_root()->state();

  // Set new value for the given state name in that domain.
  dict.gset(domain+":"+name, value ? 1 : 0);
}

