/*
 * Copyright (c) 2008, 2012, 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 "stdafx.h"

#include "wb_context_ui.h"
#include "model/wb_component.h"
#include "model/wb_component_physical.h"

#include "model/wb_model_diagram_form.h"

#include "wb_overview.h"
#include "model/wb_overview_physical.h"
#include "workbench/wb_history_tree.h"
#include "wb_find_dialog.h"
#include "model/wb_user_datatypes.h"
#include "model/wb_diagram_options.h"

#include "grt/editor_base.h"
#include "grt/clipboard.h"
#include "grtui/gui_plugin_base.h"

#include "grt_shell_window.h"
#include "output_view.h"
#include "webbrowser_view.h"

#include "mforms/utilities.h"
#include "mforms/app.h"
#include "mforms/menubar.h"

#include "base/string_utilities.h"
#include "base/log.h"
#include "base/notifications.h"

#include "plugin_install_window.h"

using namespace wb;
using namespace bec;
using namespace base;

DEFAULT_LOG_DOMAIN(DOMAIN_WB_CONTEXT_UI)

WBContextUI::WBContextUI(bool verbose)
  : _wb(new WBContext(this, verbose)), _usertypes_tree(_wb), _command_ui(_wb)
{
  NodeId n; // workaround a bug causing crash when output tab is shown with GLib-ERROR **: The thread system is not yet initialized.

  base::NotificationCenter::get()->add_observer(this, "GNAppClosing");
  
  _catalog_tree= 0;

  _history_tree= 0;

  _shell_window= 0;
  
  _find_dialog= new FindDialogBE(_wb);
  _find_dialog->set_activate_slot(boost::bind(&WBContextUI::activate_find_result, this, _1, _2, _3));

  _output_view= new OutputView(_wb);
  scoped_connect(_output_view->get_be()->signal_show(),boost::bind(&WBContextUI::show_output, this));
    
  _active_form= 0;
  _active_main_form= 0;
  
  _addon_download_window = 0;
  _plugin_install_window = 0;
  
  _last_unsaved_changes_state= false;
  _quitting= false;
  
  _home_screen= NULL;

  // to notify that the save status of the doc has changed
  //_wb->get_grt()->get_undo_manager()->signal_changed().connect(boost::bind(&WBContextUI::history_changed, this));
  scoped_connect(_wb->get_grt()->get_undo_manager()->signal_changed(),boost::bind(&WBContextUI::history_changed, this));
  
  // stuff to do when the active form is switched in the UI (through set_active_form)
  _form_change_signal.connect(boost::bind(&WBContextUI::form_changed, this));
}


WBContextUI::~WBContextUI()
{
  base::NotificationCenter::get()->remove_observer(this);
  
  _wb->do_close_document(true);

  // TODO: is it really necessary to set all member variables to NULL in a dtor?
  delete _addon_download_window;
  _addon_download_window = 0;
  delete _plugin_install_window;
  _plugin_install_window = 0;
  delete _find_dialog;
  _find_dialog = 0;
  delete _history_tree;
  _history_tree = 0;
  if (_home_screen != NULL)
  {
    _home_screen->release();
    _home_screen = NULL;
  }
  delete _shell_window;
  _shell_window = 0;

  delete _wb;
  _wb = 0;
}


void WBContextUI::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info)
{
  if (name == "GNAppClosing")
  {
    home_screen_closing();
  }
}

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

bool WBContextUI::init(WBFrontendCallbacks *callbacks, WBOptions *options)
{
  _initializing = true;
  bool flag = false;
  try
  {
    // this needs to be created after the search paths have been set
    // WBContext::init_ will call get_shell_window() at the right time, which 
    // will trigger the instantiation
    //_shell_window = new GRTShellWindow(_wb->get_grt_manager());
    flag= _wb->init_(callbacks, options);

    // has to be called after WBContext is initialized
    add_backend_builtin_commands();

    // look for auto-save files (must be done at startup to not confuse with autosaves created by ourself)
    WBContextModel::detect_auto_save_files(_wb->get_auto_save_dir());
    WBContextSQLIDE::detect_auto_save_files(_wb->get_auto_save_dir());

    // Finally show initial home screen.
    show_home_screen();
    _initializing = false;
  }
  catch (const std::exception& e)
  {
    _initializing = false;
    log_error("WBContextUI::init, exception '%s'\n", e.what()); // log_error logs to stderr too in debug mode.
  }
  catch (...)
  {
    _initializing = false;
    log_error("Some exception has happened. It was caught at WBContextUI::init.\n");
  }

  return flag;
}

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

GRTShellWindow* WBContextUI::get_shell_window()
{
  if (!_shell_window)
    _shell_window = new GRTShellWindow(_wb);
  return _shell_window;
}

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

void WBContextUI::init_finish(WBOptions *options)
{  
  _wb->init_finish_(options);
}


void WBContextUI::finalize()
{  
  _wb->finalize();
}

bool WBContextUI::request_quit()
{ 
  if (_quitting)
    return true;
  
  if (!_wb->get_grt_manager()->in_main_thread())
    g_warning("request_quit() called in worker thread");

  if (_wb->has_unsaved_changes())
  {
    int answer= mforms::Utilities::show_message(_("Quit Application"),
      _("Do you want to save pending changes to the document?\n\n"
      "If you don't save your changes, they will be lost."),
      _("Save"), _("Cancel"), _("Don't Save"));
    if (answer == mforms::ResultOk)
    {
      if (!_wb->save_changes())
        return false;
    }
    else if (answer == mforms::ResultCancel)
      return false;
    
    _wb->do_close_document(true);
  }

  if (_wb->get_sqlide_context() && !_wb->get_sqlide_context()->request_quit())
    return false;

  if (_shell_window != NULL && !_shell_window->request_quit())
    return false;

  return true;
}


void WBContextUI::perform_quit()
{
  _quitting= true;
  _wb->quit_application();
}


void WBContextUI::reset()
{
  if (!dynamic_cast<OverviewBE*>(_active_form))
    _active_form= 0;
  if (!dynamic_cast<OverviewBE*>(_active_main_form))
    _active_main_form= 0;
  
  scoped_connect(get_physical_overview()->signal_selection_changed(),boost::bind(&WBContextUI::overview_selection_changed, this));
  
  get_physical_overview()->set_model(_wb->get_document()->physicalModels()[0]);
  
  _wb->request_refresh(RefreshSelection, "", 0);

  get_physical_overview()->send_refresh_children(bec::NodeId());
  
  _wb->get_component<WBComponentPhysical>()->update_catalog_tree();

  _usertypes_tree.set_catalog(_wb->get_document()->physicalModels()[0]->catalog());
  _usertypes_tree.set_rdbms(_wb->get_document()->physicalModels()[0]->rdbms());
}


void WBContextUI::history_changed()
{  
  if (_wb->has_unsaved_changes() != _last_unsaved_changes_state)
    _wb->request_refresh(RefreshDocument, "", (NativeHandle)0);

  get_command_ui()->revalidate_edit_menu_items();
  _last_unsaved_changes_state= _wb->has_unsaved_changes();
}

void WBContextUI::update_current_diagram(bec::UIForm *form)
{
  ModelDiagramForm* dform= dynamic_cast<ModelDiagramForm*>(form);
  if (dform)
  {
    model_DiagramRef diagram(dform->get_model_diagram());
    if (diagram.is_valid() && diagram->owner().is_valid())
      diagram->owner()->currentDiagram(diagram);
  }
}

void WBContextUI::overview_selection_changed()
{
  if (get_active_main_form() == get_physical_overview())
  {
    _wb->request_refresh(RefreshSelection, "", (NativeHandle)get_physical_overview()->get_frontend_data());
    (*_command_ui.signal_validate_edit_menu_items())();
  }
}


void WBContextUI::load_app_options(bool update)
{
  if (!update)
    _command_ui.load_data();
}


std::string WBContextUI::get_diagram_path(const model_DiagramRef &diagram)
{
  std::string view_id= diagram.id();

  // look in the physicalModels list for this diagram
  for (size_t c= _wb->get_document()->physicalModels().count(), i= 0; i < c; i++)
  {
    workbench_physical_ModelRef model= _wb->get_document()->physicalModels()[i];
    
    for (size_t d= model->diagrams().count(), j= 0; j < d; j++)
    {
      if (model->diagrams().get(j).id() == view_id)
        return strfmt("/wb/doc/physicalModels/%i/diagrams/%i", (int) i, (int) j);
    }
  }
  return "";
}


static void add_script_file(WBContextUI *wbui)
{
  std::string file= wbui->get_wb()->show_file_dialog("open", _("Add SQL Script File"), "sql");
  if (!file.empty())
  {
    workbench_physical_ModelRef model;

    model= workbench_physical_ModelRef::cast_from(wbui->get_wb()->get_model_context()->get_active_model(false));
    if (model.is_valid())
      wbui->get_wb()->get_component<WBComponentPhysical>()->add_new_stored_script(model, file);
  }
}


static void add_note_file(WBContextUI *wbui)
{
  std::string file= wbui->get_wb()->show_file_dialog("open", _("Add Note File"), "Text Files (*.txt)|*.txt");
  if (!file.empty())
  {
    workbench_physical_ModelRef model;

    model= workbench_physical_ModelRef::cast_from(wbui->get_wb()->get_model_context()->get_active_model(false));
    if (model.is_valid())
      wbui->get_wb()->get_component<WBComponentPhysical>()->add_new_stored_note(model, file);
  }
}


/** builtin: commands for use in menus and toolbars that are handled by ourselves
  */
void WBContextUI::add_backend_builtin_commands()
{
  _command_ui.add_builtin_command("overview.home", 
                                  boost::bind(&WBContextUI::show_home_screen, this));
  _command_ui.add_builtin_command("show_output_form", 
                                  boost::bind(&WBContextUI::show_output, this));

  _command_ui.add_builtin_command("find_next", 
    boost::bind(&FindDialogBE::find_next, _find_dialog));
  _command_ui.add_builtin_command("find_previous", 
    boost::bind(&FindDialogBE::find_previous, _find_dialog));

  _command_ui.add_builtin_command("add_script_file", 
    boost::bind(add_script_file, this));
  _command_ui.add_builtin_command("add_note_file", 
    boost::bind(add_note_file, this));

  // handle some url opening stuff ourselves through mforms
  _command_ui.add_builtin_command("community_blog",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/", true));
  _command_ui.add_builtin_command("community_faq",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/?page_id=7", true));
  _command_ui.add_builtin_command("community_learn",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/?page_id=10", true));
  _command_ui.add_builtin_command("community_discuss",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/?page_id=16", true));
  _command_ui.add_builtin_command("community_contribute",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/?page_id=17", true));
  _command_ui.add_builtin_command("web_mysql_home",
    boost::bind(&WBContextUI::show_web_page, this, "http://mysql.com/", true));
  _command_ui.add_builtin_command("web_home",
    boost::bind(&WBContextUI::show_web_page, this, "http://mysql.com/products/tools/workbench/", true));
  _command_ui.add_builtin_command("list_bugs",
    boost::bind(&WBContextUI::show_web_page, this, "http://bugs.mysql.com/saved/WB%20bugs", true));
  _command_ui.add_builtin_command("plugin_site",
    boost::bind(&WBContextUI::show_web_page, this, "http://wb.mysql.com/addons/", true));
  _command_ui.add_builtin_command("check_update",
    boost::bind(&WBContextUI::show_web_page, this, 
               base::strfmt("http://wb.mysql.com/workbench/version-check.php?version=%i.%i.%i&revision=%i", 
                            APP_MAJOR_NUMBER, APP_MINOR_NUMBER, APP_RELEASE_NUMBER, APP_REVISION_NUMBER), false));
  _command_ui.add_builtin_command("help_index", boost::bind(&WBContextUI::show_help_index, this));
  _command_ui.add_builtin_command("locate_log_file", boost::bind(&WBContextUI::locate_log_file, this));
  _command_ui.add_builtin_command("show_log_file", boost::bind(&WBContextUI::show_log_file, this));
}

#ifndef ___specialforms

//-----------------------------------------------------------------------------------
// special forms


bec::ValueTreeBE *WBContextUI::get_catalog_tree()
{
  if (!_catalog_tree)
  {
    WBComponentPhysical *compo= _wb->get_component<wb::WBComponentPhysical>();
    _catalog_tree= compo->get_catalog_tree();
  }
  return _catalog_tree;
}


UserDatatypeListBE *WBContextUI::get_usertypes_tree()
{
  return &_usertypes_tree;
}


PhysicalOverviewBE *WBContextUI::get_physical_overview()
{
  return get_wb()->get_model_context() ? get_wb()->get_model_context()->get_overview() : 0;
}

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

void WBContextUI::show_output()
{
  mforms::App::get()->dock_view(_output_view, "maintab");
  mforms::App::get()->set_view_title(_output_view, "Output");
  _output_view->setup_ui();
}

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

/*
 * Displays the web page given by url either in an internal window or an external browser instance.
 */
void WBContextUI::show_web_page(const std::string& url, bool internal_browser)
{
#ifdef WB_USE_INTERNAL_BROWSER
  if (internal_browser)
  {
    WebBrowserView* browser= mforms::manage(new WebBrowserView(this));
    mforms::App::get()->dock_view(browser, "maintab");
    mforms::App::get()->set_view_title(browser, _("Loading web page..."));
    browser->navigate(url);
  }
  else
#endif
    mforms::Utilities::open_url(url);
}

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

void WBContextUI::show_help_index()
{
  GUILock lock(_wb, _("Starting Doc Lib"), _("The MySQL Doc Library is opening currently, "
    "which should be finished in a moment .\n\nPlease stand by..."));

  _wb->execute_plugin("wb.doclib.open");
}

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

void WBContextUI::locate_log_file()
{
  if (!base::Logger::log_dir().empty())
    mforms::Utilities::open_url(base::Logger::log_dir());
}


void WBContextUI::show_log_file()
{
  if (!base::Logger::log_filename().empty())
    mforms::Utilities::open_url(base::Logger::log_filename());
}

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

void WBContextUI::activate_figure(const grt::ValueRef &value)
{
  ModelDiagramForm *form= 0;
  if (model_FigureRef::can_wrap(value))
  {
    model_FigureRef figure(model_FigureRef::cast_from(value));
    form= get_wb()->get_model_context()->get_diagram_form_for_diagram_id(figure->owner().id());
    if (form)
      form->focus_and_make_visible(figure, true);
  }
  else if (model_ConnectionRef::can_wrap(value))
  {
    model_ConnectionRef conn(model_ConnectionRef::cast_from(value));
    ModelDiagramForm *form= get_wb()->get_model_context()->get_diagram_form_for_diagram_id(conn->owner().id());
    if (form)
      form->focus_and_make_visible(conn, true);
  }
  else if (model_LayerRef::can_wrap(value))
  {
    model_LayerRef layer(model_LayerRef::cast_from(value));
    ModelDiagramForm *form= get_wb()->get_model_context()->get_diagram_form_for_diagram_id(layer->owner().id());
    if (form)
      form->focus_and_make_visible(layer, true);
  }
}


void WBContextUI::form_changed()
{  
  _wb->request_refresh(RefreshZoom, "", (NativeHandle)0);
  
  if (_active_form != NULL && _active_form == _home_screen)
  {
    refresh_home_models();
    refresh_home_instances(0);
    refresh_home_connections();
  }

  bec::UIForm *form = get_active_main_form();
  if (form && form->get_menubar())
    form->get_menubar()->validate();
}

HistoryTreeBE *WBContextUI::get_history_tree()
{
  if (!_history_tree)
  {
    _history_tree= new HistoryTreeBE(_wb, _wb->get_grt()->get_undo_manager());
    
    scoped_connect(_wb->get_grt()->get_undo_manager()->signal_changed(),boost::bind(&HistoryTreeBE::tree_changed, _history_tree, -1, bec::NodeId()));
  }
  return _history_tree;
}


bec::ValueInspectorBE *WBContextUI::create_inspector_for_selection(bec::UIForm *form, std::vector<std::string> &items)
{
  grt::ListRef<model_Object> selection;

  //grt::ListRef<model_Object> selection(form->get_selection());
  
  if (dynamic_cast<ModelDiagramForm*>(form))
    selection= dynamic_cast<ModelDiagramForm*>(form)->get_selection();
  else
    return 0;

  if (selection.is_valid() && selection.count()>0)
  {
    if (selection.count()==1)
    {
      items.push_back(strfmt("%s: %s", selection[0]->name().c_str(), selection[0].get_metaclass()->get_attribute("caption").c_str()));

      return ValueInspectorBE::create(_wb->get_grt_manager()->get_grt(), selection[0], false, true);
    }
    else
    {
      std::vector<grt::ObjectRef> list;

      items.push_back(_("Multiple Items"));
      for (size_t c= selection.count(), i= 0; i < c; i++)
      {
        items.push_back(strfmt("%s: %s", selection[i]->name().c_str(), selection[i].get_metaclass()->get_attribute("caption").c_str()));
        list.push_back(selection.get(i));
      }

      return ValueInspectorBE::create(_wb->get_grt_manager()->get_grt(), list);
    }
  }

  return 0;
}


bec::ValueInspectorBE *WBContextUI::create_inspector_for_selection(std::vector<std::string> &items)
{
  std::string res;

  grt::ListRef<GrtObject> selection(get_physical_overview()->get_selection());
  std::string name_mem_name("name");

  if (selection.is_valid() && selection.count()>0)
  {
    if (selection.count()==1)
    {
      GrtObjectRef obj= selection[0];
      if (obj.is_valid() && obj->has_member(name_mem_name))
      {
        items.push_back(strfmt("%s: %s", obj.get_string_member(name_mem_name).c_str(), obj.get_metaclass()->get_attribute("caption").c_str()));

        return ValueInspectorBE::create(_wb->get_grt_manager()->get_grt(), selection[0], false, true);
      }
    }
    else
    {
      std::vector<grt::ObjectRef> list;

      items.push_back(_("Multiple Items"));
      for (size_t c= selection.count(), i= 0; i < c; i++)
      {
        if (!selection[i].is_valid())//skip "Add item" entry in objects list
          continue;
        items.push_back(strfmt("%s: %s", selection[i].get_string_member(name_mem_name).c_str(), selection[i].get_metaclass()->get_attribute("caption").c_str()));
        list.push_back(selection.get(i));
      }

      return ValueInspectorBE::create(_wb->get_grt_manager()->get_grt(), list);
    }
  }

  return 0;
}


static std::list<GrtObjectRef> get_selected_objects_from_form(WBContext *wb, bec::UIForm *form)
{
  std::list<GrtObjectRef> objects;

  if (dynamic_cast<ModelDiagramForm*>(form))
  {
    grt::ListRef<model_Object> selection(dynamic_cast<ModelDiagramForm*>(form)->get_selection());

    if (selection.is_valid())
    {
      for (size_t c= selection.count(), i= 0; i < c; i++)
      {
        model_ObjectRef object(selection[i]);
        WBComponent *comp= wb->get_component_handling(object);
        GrtObjectRef dbobject;

        if (comp) 
          dbobject= comp->get_object_for_figure(object);

        if (dbobject.is_valid())
          objects.push_back(dbobject);
        else 
          objects.push_back(object);
      }
    }
  }
  else if (dynamic_cast<OverviewBE*>(form))
  {
    grt::ListRef<GrtObject> selection(dynamic_cast<OverviewBE*>(form)->get_selection());

    if (selection.is_valid())
    {
      for (size_t c= selection.count(), i= 0; i < c; i++)
      {
        GrtObjectRef object(selection[i]);

        if (object.is_valid())
          objects.push_back(object);
      }
    }
  }
  return objects;
}


std::string WBContextUI::get_description_for_selection(grt::ListRef<GrtObject> &activeObjList, std::vector<std::string> &items)
{
  std::string res;

  if (get_physical_overview() != NULL)
  {
    grt::ListRef<GrtObject> selection(get_physical_overview()->get_selection());
    activeObjList= selection;
    
    std::string comment_mem_name("comment");
    std::string name_mem_name("name");

    if (selection.is_valid() && selection.count()>0)
    {
      if (selection.count()==1)
      {
        GrtObjectRef obj(selection[0]);
        if (obj.is_valid() && obj.has_member(comment_mem_name) && obj.has_member(name_mem_name))
        {
          items.push_back(strfmt("%s: %s", obj->name().c_str(), obj.get_metaclass()->get_attribute("caption").c_str()));
          res= obj.get_string_member(comment_mem_name);
        }
      }
      else
      {
        items.push_back(_("Multiple Items"));
        for (size_t c= selection.count(), i= 0; i < c; i++)
        {
          GrtObjectRef obj(selection[i]);
          if (obj.is_valid() && obj.has_member(comment_mem_name) && obj.has_member(name_mem_name))
          {
            items.push_back(strfmt("%s: %s", obj->name().c_str(), obj.get_metaclass()->get_attribute("caption").c_str()));
            std::string comment= obj.get_string_member(comment_mem_name);
            if (0 == i)
              res= comment;
            else if (0 != res.compare(comment))
              res= "<Multiple Items>\nThat means not all selected items have same comment.\nBeware applying changes will override comments for all selected objects.";
          }
        }
      }
    }
  }
  return res;
}


std::string WBContextUI::get_description_for_selection(bec::UIForm *form, grt::ListRef<GrtObject> &activeObjList, std::vector<std::string> &items)
{
  grt::ListRef<model_Object> selection;

  if (dynamic_cast<ModelDiagramForm*>(form))
    selection= dynamic_cast<ModelDiagramForm*>(form)->get_selection();
  else
    return get_description_for_selection(activeObjList, items);

  std::string res;

  activeObjList= grt::ListRef<model_Object>(selection.get_grt());

  std::string comment_mem_name("comment");
  std::string descr_mem_name("description");

  if (selection.is_valid() && selection.count()>0)
  {
    bool first= true;
    for (size_t c= selection.count(), i= 0; i < c; i++)
    {
      model_ObjectRef figure(selection[i]);
      WBComponent *comp= _wb->get_component_handling(figure);
      GrtObjectRef dbobject;
      if (comp) dbobject= comp->get_object_for_figure(figure);
      
      if (dbobject.is_valid() && dbobject.has_member(comment_mem_name))
      {
        activeObjList.insert(dbobject);
        
        items.push_back(strfmt("%s: %s", figure->name().c_str(), figure->get_metaclass()->get_attribute("caption").c_str()));
        std::string comment= dbobject.get_string_member(comment_mem_name);
        if (first)
          res= comment;
        else if (0 != res.compare(comment))
          res= _("<Multiple Items>\nThat means not all selected items have same comment.\nBeware applying changes will override comments for all selected objects.");

        first= false;
      }
      else if (!dbobject.is_valid() && figure.is_valid() && figure.has_member(descr_mem_name))
      {
        activeObjList.insert(figure);
        
        items.push_back(strfmt("%s: %s", figure->name().c_str(), figure->get_metaclass()->get_attribute("caption").c_str()));
        std::string comment= figure.get_string_member(descr_mem_name);
        if (first)
          res= comment;
        else if (0 != res.compare(comment))
          res= _("<Multiple Items>\nThat means not all selected items have same comment.\nBeware applying changes will override comments for all selected objects.");
        
        first= false;        
      }
    }
    if (items.size() > 1)
      items.insert(items.begin(), _("Multiple Items"));
  }

  return res;
}


void WBContextUI::set_description_for_selection(const grt::ListRef<GrtObject> &objList, const std::string &val)
{
  if (objList.is_valid() && objList.count()>0)
  {
    std::string comment_mem_name("comment");
    std::string descr_mem_name("description");
    
    grt::AutoUndo undo(_wb->get_grt());

    for (size_t c= objList.count(), i= 0; i < c; i++)
    {
      GrtObjectRef obj(objList[i]);
      if (obj.is_valid())
      {
        if (obj.has_member(comment_mem_name))
        {
          obj.set_member(comment_mem_name, grt::StringRef(val));
          get_physical_overview()->send_refresh_for_schema_object(obj, true);
        }
        else
          if (obj.has_member(descr_mem_name))
          {
            obj.set_member(descr_mem_name, grt::StringRef(val));
            get_physical_overview()->send_refresh_for_schema_object(obj, true);
          }
      }
    }

    undo.end(_("Set Object Description"));
  }
}


DiagramOptionsBE *WBContextUI::create_diagram_options_be(mdc::CanvasView *view)
{
  model_DiagramRef model_diagram(get_wb()->get_model_context()->get_active_model_diagram(true));
  
  if (model_diagram.is_valid())
    return new DiagramOptionsBE(view, model_diagram, _wb);
  else
    return 0;
}


std::string WBContextUI::get_active_diagram_info()
{
  wb::ModelDiagramForm *form= dynamic_cast<wb::ModelDiagramForm*>(get_active_main_form());
  
  if (form)
    return form->get_diagram_info_text();
  
  return "";
}


void WBContextUI::activate_find_result(const GrtObjectRef &location, const GrtObjectRef &object, const std::string &text)
{
  _wb->show_status_text(strfmt("Match: %s (%s)", 
    text.c_str(), object.get_metaclass()->get_attribute("caption").c_str()));

  if (location.is_valid())
  {
    model_DiagramRef view;

    if (location.is_instance<model_Diagram>())
      view= model_DiagramRef::cast_from(location);
    else
      view= _wb->get_parent_for_object<model_Diagram>(location);

    if (view.is_valid())
    {
      ModelDiagramForm *form= get_wb()->get_model_context()->get_diagram_form_for_diagram_id(view.id());

      if (form)
      {
        _wb->switched_view(form->get_view());
        form->focus_and_make_visible(model_FigureRef::cast_from(location), true);
      }
    }
  }
}

#endif // ___specialforms


#ifndef ___preferences
//-----------------------------------------------------------------------------------
// utility functions for user preferences

void WBContextUI::get_doc_properties(std::string& caption, std::string& version, std::string& author, std::string& project, std::string& date_created, std::string& date_changed, std::string& description)
{
  app_DocumentInfoRef info= _wb->get_document()->info();

  caption= info->caption();
  version= info->version();
  author= info->author();
  project= info->project();
  date_created= info->dateCreated();
  date_changed= info->dateChanged();
  description= info->description();  
}

void WBContextUI::set_doc_properties(const std::string &caption, const std::string &version, const std::string &author, const std::string &project, const std::string &date_created, const std::string &date_changed, const std::string &description)
{
  app_DocumentInfoRef info= _wb->get_document()->info();

  info->caption(caption);
  info->version(version);
  info->author(author);
  info->project(project);
  info->dateCreated(date_created);
  info->dateChanged(date_changed);
  info->description(description);  
}


std::list<WBPaperSize> WBContextUI::get_paper_sizes(bool descr_in_inches)
{
  std::list<WBPaperSize> sizes;

  grt::ListRef<app_PaperType> types(_wb->get_root()->options()->paperTypes());

  for (size_t c= types.count(), i= 0; i < c; i++)
  {
    WBPaperSize size;
    size.name= types[i]->name();
    size.caption= types[i]->caption();
    size.width= types[i]->width();
    size.height= types[i]->height();
    size.margins_set= types[i]->marginsSet() != 0;
    size.margin_top= types[i]->marginTop();
    size.margin_bottom= types[i]->marginBottom();
    size.margin_left= types[i]->marginLeft();
    size.margin_right= types[i]->marginRight();

    if (descr_in_inches)
      size.description= strfmt("%.2f in x %.2f in", size.width*0.03937, size.height*0.03937);
    else
      size.description= strfmt("%.2f cm x %.2f cm", size.width/10, size.height/10);

    sizes.push_back(size);
  }

  return sizes;
}


bool WBContextUI::add_paper_size(const std::string &name, double width, double height,
                                   bool margins, double margin_top, double margin_bottom,
                                   double margin_left, double margin_right)
{
  if (grt::find_named_object_in_list(_wb->get_root()->options()->paperTypes(), name).is_valid())
    return false;

  app_PaperTypeRef type(_wb->get_grt_manager()->get_grt());
  type->owner(_wb->get_root()->options());
  type->name(name);
  type->width(width);
  type->height(height);
  type->marginsSet(margins?1:0);
  type->marginTop(margin_top);
  type->marginBottom(margin_bottom);
  type->marginLeft(margin_left);
  type->marginRight(margin_right);
  _wb->get_root()->options()->paperTypes().insert(type);

  return true;
}


app_PageSettingsRef WBContextUI::get_page_settings()
{
  if (_wb->get_document().is_valid())
    return _wb->get_document()->pageSettings();
  else
  {
    // XXX add proper initialization for non-trivial types in structs.app.h too.
    app_PageSettingsRef settings= app_PageSettingsRef(_wb->get_grt());
    settings->scale(1);
    settings->paperType(app_PaperTypeRef());

    return settings;
  }
}


grt::DictRef WBContextUI::get_model_options(const std::string &model_id)
{
  grt::ListRef<workbench_physical_Model> pmodels(_wb->get_document()->physicalModels());

  for (size_t c= pmodels.count(), i= 0; i < c; i++)
  {
    if (pmodels.get(i).id() == model_id)
    {
      return pmodels.get(i)->options();
    }
  }
  return grt::DictRef();
}


std::vector<std::string> WBContextUI::get_wb_options_keys(const std::string &model)
{
  std::vector<std::string> keylist;
  grt::DictRef options= _wb->get_wb_options();

  for (grt::DictRef::const_iterator iter= options.begin(); iter != options.end(); ++iter)
  {
    keylist.push_back(iter->first);
  }

  return keylist;
}


bool WBContextUI::get_wb_options_value(const std::string &model, const std::string &key,
                                              std::string &value)
{
  grt::DictRef options= _wb->get_wb_options();
  grt::ValueRef val;

  // If a model is given check if it is set to use global values or its own.
  if (!model.empty())
  {
    grt::DictRef model_options(get_model_options(model));
    int use_global= model_options.get_int("useglobal", 1);

    if (key == "useglobal")
    {
      if (use_global)
        value= "1";
      else
        value= "0";
      return true;
    }

    if (!use_global && model_options.has_key(key))
      val= model_options.get(key);
  }

  if (!val.is_valid() && options.has_key(key))
    val= options.get(key);

  switch(val.type())
  {
  case grt::StringType: 
  case grt::DoubleType:
  case grt::IntegerType:
      value= val.repr();
      return true;
    default: 
      return false;
  }
}


void WBContextUI::set_wb_options_value(const std::string &model, const std::string &key, const std::string &value, 
                                       const grt::Type default_type)
{
  grt::DictRef options;
  grt::Type type;
  if (_wb->get_wb_options().has_key(key))
    type= _wb->get_wb_options().get(key).type();
  else
    type= default_type;

  if (!model.empty())
  {
    options= get_model_options(model);

    options.gset("useglobal", 0);

    if (options.has_key(key))
      type= options.get(key).type();
  }
  
  if (!options.is_valid())
    options= _wb->get_wb_options();

  switch (type)
  {
  case grt::DoubleType:
    {
      grt::DoubleRef v(atof(value.c_str()));
      if (!options.has_key(key) || options.get_double(key) != *v)
        options.set(key, v);
      break;
    }
  case grt::IntegerType:
    {
      grt::IntegerRef v(atoi(value.c_str()));
      if (!options.has_key(key) || options.get_int(key) != *v)
        options.set(key, v);
      break;
    }
  case grt::StringType:
    {
      grt::StringRef v(value);
      if (!options.has_key(key) || options.get_string(key) != *v)
        options.set(key, v);
      break;
    }
  default:
    throw std::runtime_error("No valid grt type specified when setting options value.");
  }
}

void WBContextUI::discard_wb_model_options(const std::string &model)
{
  grt::DictRef opts= get_model_options(model);
  if (opts.is_valid())
  {
    for (grt::DictRef::const_iterator item= opts.begin(); item != opts.end(); ++item)
    {
      opts.set(item->first, grt::ValueRef());
    }
    opts.gset("useglobal", 1);
  }
}


#endif // ___preferences


#ifndef ___forms

//--------------------------------------------------------------------------------
// Form Management


void *WBContextUI::form_destroyed(void *data)
{
  UIForm *form= reinterpret_cast<UIForm*>(data);
  WBContextUI *wb= reinterpret_cast<WBContextUI*>(form->get_owner_data());

  if (wb->_active_form == form)
    wb->set_active_form(0);
  if (wb->_active_main_form == form)
    wb->_active_main_form= 0;

  return 0;
}


std::string WBContextUI::get_active_context(bool main_context)
{
  bec::UIForm *form= main_context ? get_active_main_form() : get_active_form();

  if (form)
    return form->get_form_context_name();
  
  return "";
}


void WBContextUI::set_active_form(bec::UIForm *form)
{
  if (_active_form == form)
    return;
  // register callbacks to form if needed
  
  if (_active_form && form)
    _active_form->remove_destroy_notify_callback(_active_form);
  _active_form= form;
  if (form)
  {
    form->add_destroy_notify_callback(reinterpret_cast<void*>(form), &WBContextUI::form_destroyed);
    form->set_owner_data(reinterpret_cast<void*>(this));
  }

  if (form && form->is_main_form())
  {
    if (_active_main_form != form)
    {
      _active_main_form= form;

      NotificationCenter::get()->send("GNMainFormChanged", 0);
    }
  }

  _form_change_signal(form);
}



bec::UIForm *WBContextUI::get_active_main_form()
{
  return _active_main_form;
}


bec::UIForm *WBContextUI::get_active_form()
{
  return _active_form;
}

#endif // ___forms


#ifndef ___others

//-----------------------------------------------------------------------------------
// other functionality for UI

std::string WBContextUI::get_document_name()
{
  if (_wb->get_filename().empty())
    return "Untitled";
  else
  {
    gchar *bn= g_path_get_basename(_wb->get_filename().c_str());
    std::string title(bn);
    g_free(bn);
    return title;
  }
}


std::string WBContextUI::get_title()
{
  if (_wb->get_model_context())
  {
#ifndef __APPLE__
    if (_wb->has_unsaved_changes())
      return get_document_name() + "* - MySQL Workbench";
    else
#endif
      return get_document_name() + " - MySQL Workbench";
  }
  else
    return "MySQL Workbench";
}

#endif // ___others


void WBContextUI::start_plugin_net_install(const std::string &url)
{
  if (!_addon_download_window)
    _addon_download_window = new AddOnDownloadWindow(this);
  _addon_download_window->install_addon_from_url(url);
}


bool WBContextUI::start_plugin_install(const std::string &path)
{
  if (!_plugin_install_window)
    _plugin_install_window = new PluginInstallWindow(this);
  return _plugin_install_window->install_plugin(path);
}
