/**
 * @file  wb_component_physical.cpp
 * @brief 
 */


#include "stdafx.h"

#include "wb_config.h"

#include "wb_component_physical.h"
#include "wb_find_dialog.h"
#include "wb_model_diagram_form.h"
#include "wb_catalog_tree.h"
#include "wb_context_ui.h"
#include "wb_overview_physical.h"

#include "grts/structs.workbench.physical.h"
#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.mysql.h"

#include "grtdb/db_object_helpers.h"

#include "mdc.h"

#include "wbcanvas/workbench_physical_model_impl.h"
#include "wbcanvas/workbench_physical_diagram_impl.h"
#include "wbcanvas/workbench_physical_tablefigure_impl.h"
#include "wbcanvas/workbench_physical_viewfigure_impl.h"
#include "wbcanvas/workbench_physical_routinegroupfigure_impl.h"
#include "wbcanvas/workbench_physical_connection_impl.h"

#define FILE_CONNECTION_LIST "connections.xml"

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


WBComponentPhysical::WBComponentPhysical(WBContext *wb)
: WBComponent(wb)
{
  _catalog_tree= 0;
}


WBComponentPhysical::~WBComponentPhysical()
{
  delete _catalog_tree;
}


void WBComponentPhysical::load_app_options(bool update)
{
  grt::GRT *grt= get_grt();

  if (!update)
  {
    app_ToolbarRef tools_toolbar(app_ToolbarRef::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/tools_toolbar_physical.xml"))));

    _toolbars[tools_toolbar->name()]= tools_toolbar;

    app_ToolbarRef toolbar;
    toolbar= app_ToolbarRef::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_table.xml")));
    _toolbars[toolbar->name()]= toolbar;

    toolbar= app_ToolbarRef::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_view.xml")));
    _toolbars[toolbar->name()]= toolbar;

    toolbar= app_ToolbarRef::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_routinegroup.xml")));
    _toolbars[toolbar->name()]= toolbar;

    toolbar= app_ToolbarRef::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_relationship.xml")));
    _toolbars["main/"WB_TOOL_PREL11_NOID]= toolbar;
    _toolbars["main/"WB_TOOL_PREL1n_NOID]= toolbar;
    _toolbars["main/"WB_TOOL_PREL11]= toolbar;
    _toolbars["main/"WB_TOOL_PREL1n]= toolbar;
    _toolbars["main/"WB_TOOL_PRELnm]= toolbar;
    _toolbars["main/"WB_TOOL_PREL_PICK]= toolbar;

    _shortcuts= grt::ListRef<app_ShortcutItem>::cast_from(
      grt->unserialize(make_path(_wb->get_datadir(),"data/shortcuts_physical.xml")));
  }

  // this needs to be loaded after drivers list has been loaded
  db_mgmt_ManagementRef mgmt= _wb->get_root()->rdbmsMgmt();
  std::string conn_list_xml= make_path(_wb->get_user_datadir(), FILE_CONNECTION_LIST);
  if (g_file_test(conn_list_xml.c_str(), G_FILE_TEST_EXISTS))
  {
    try
    {
      grt::ListRef<db_mgmt_Connection> 
        list(grt::ListRef<db_mgmt_Connection>::cast_from(grt->unserialize(conn_list_xml)));

      if (list.is_valid())
      {
        while (mgmt->storedConns().count() > 0)
          mgmt->storedConns().remove(0);
        for (size_t c= list.count(), i= 0; i < c; i++)
          mgmt->storedConns().insert(list.get(i));
      }
    }
    catch (std::exception &exc)
    {
      grt->send_warning(strfmt("Error loading '%s': %s", conn_list_xml.c_str(), exc.what()));
    }
  }
}


void WBComponentPhysical::save_app_options()
{
  get_grt()->serialize(_wb->get_root()->rdbmsMgmt()->storedConns(),
    make_path(_wb->get_user_datadir(), FILE_CONNECTION_LIST));
}




void WBComponentPhysical::setup_context_grt(grt::GRT *grt, WBOptions *options)
{
  //XXX must be loaded from DB module
  db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(grt->unserialize(make_path(options->basedir, "modules/data/mysql_rdbms_info.xml")));
  
  _wb->get_root()->rdbmsMgmt()->rdbms().insert(rdbms);

  std::string engines;
  // fill engine types list
  Module *module= get_grt()->get_module("DbMySQL");
  if (module)
  {
    grt::ListRef<db_mysql_StorageEngine> engines_ret(grt::ListRef<db_mysql_StorageEngine>::cast_from(module->call_function("getKnownEngines", grt::BaseListRef(get_grt()))));

    for (size_t c= engines_ret.count(), i= 0; i < c; i++)
    {
      engines.append(",").append(engines_ret[i]->name());
    }
    engines= engines.substr(1);
    _wb->get_wb_options().gset("@db.mysql.Table:tableEngine/Items", engines.c_str());
  }
  // fill fk types
  _wb->get_wb_options().gset("@db.ForeignKey:updateRule/Items", "NO ACTION,CASCADE,SET NULL,RESTRICT");
  _wb->get_wb_options().gset("@db.ForeignKey:deleteRule/Items", "NO ACTION,CASCADE,SET NULL,RESTRICT");

  // fill delete object options
  _wb->get_wb_options().gset("@workbench.physical:DeleteObjectConfirmation/Items", 
                             _("Delete Database Object from Catalog:delete,"
                               "Keep Database Object in Catalog:keep,"
                               "Ask:ask"));
}


void WBComponentPhysical::init_catalog_grt(const db_mgmt_RdbmsRef &rdbms,
                                 const std::string &db_version,
                                 workbench_physical_ModelRef &model)
{
  grt::GRT *grt= get_grt();
  
  std::string db_package= rdbms->databaseObjectPackage();

  // assemble struct name for catalog and schema for the requested db type

  std::string catalog_struct= db_package+".Catalog";
  std::string schema_struct= db_package+".Schema";
  
  if (!grt->get_metaclass(catalog_struct) ||
      !grt->get_metaclass(schema_struct))
  {
    // Struct definition for '%s' and/or '%s' cannot be found
    throw grt_runtime_error("Support for RDBMS "+db_package+" not found.",
                            "Struct definition for "+catalog_struct+" and/or "+schema_struct+" could not be found");
  }

  db_CatalogRef catalog(grt->create_object<db_Catalog>(catalog_struct));
      
  catalog->name("default");
  catalog->owner(model);

  //catalog.oldName(catalog.name());

  // set version
  GrtVersionRef version(grt);
  version->name("Version");
  version->owner(catalog);

  char **toks= g_strsplit(db_version.c_str(), ".", 0);

  if (toks[0])
    version->majorNumber(atoi(toks[0]));
  if (toks[1])
    version->minorNumber(atoi(toks[1]));
  if (toks[2])
    version->releaseNumber(atoi(toks[2]));

  g_strfreev(toks);

  append_contents(catalog->simpleDatatypes(), rdbms->simpleDatatypes());
  append_contents(catalog->characterSets(), rdbms->characterSets());

  model->catalog(catalog);

  replace_contents(catalog->userDatatypes(), create_builtin_user_datatypes(catalog, rdbms));
  
  // add listener for any operation on the schema list
  reset_document();
  
  // add standard tag categories
  {
    GrtObjectRef category(grt);
   
    category->name("Business Rule");
    category->owner(model);
    
    model->tagCategories().insert(category);
  }
#if 1
  // create an initial schema
  db_SchemaRef schema(grt->create_object<db_Schema>(schema_struct));
  
  schema->name(StringRef("mydb"));
  schema->owner(catalog);
  schema->defaultCharacterSetName("latin1");
  schema->defaultCollationName("latin1_swedish_ci");

  //schema->oldName(schema->name());

  catalog->schemata().insert(schema);
#endif
}



grt::ListRef<db_UserDatatype> WBComponentPhysical::create_builtin_user_datatypes(const db_CatalogRef &catalog,
                                                                                 const db_mgmt_RdbmsRef &rdbms)
{
  // XXX this doesn't belong here, it should be DB specific, maybe move to a db.mysql module function
  static struct
  {
    const char *oid;
    const char *name;
    const char *sql_def;
  }
  type_init_data[] = {
    // These are type aliases, not UDTs
    {"com.mysql.rdbms.mysql.userdatatype.bool", "BOOL", "TINYINT(1)"},
    {"com.mysql.rdbms.mysql.userdatatype.boolean", "BOOLEAN", "TINYINT(1)"},
    {"com.mysql.rdbms.mysql.userdatatype.fixed", "FIXED", "DECIMAL(10,0)"},
    {"com.mysql.rdbms.mysql.userdatatype.float4", "FLOAT4", "FLOAT"},
    {"com.mysql.rdbms.mysql.userdatatype.float8", "FLOAT8", "DOUBLE"},
    {"com.mysql.rdbms.mysql.userdatatype.int1", "INT1", "TINYINT(4)"},
    {"com.mysql.rdbms.mysql.userdatatype.int2", "INT2", "SMALLINT(6)"},
    {"com.mysql.rdbms.mysql.userdatatype.int3", "INT3", "MEDIUMINT(9)"},
    {"com.mysql.rdbms.mysql.userdatatype.int4", "INT4", "INT(11)"},
    {"com.mysql.rdbms.mysql.userdatatype.int8", "INT8", "BIGINT(20)"},
    {"com.mysql.rdbms.mysql.userdatatype.integer", "INTEGER", "INT(11)"},
    {"com.mysql.rdbms.mysql.userdatatype.longvarbinary", "LONG VARBINARY", "MEDIUMBLOB"},
    {"com.mysql.rdbms.mysql.userdatatype.longvarchar", "LONG VARCHAR", "MEDIUMTEXT"},
    {"com.mysql.rdbms.mysql.userdatatype.long", "LONG", "MEDIUMTEXT"},
    {"com.mysql.rdbms.mysql.userdatatype.middleint", "MIDDLEINT", "MEDIUMINT(9)"},
    {"com.mysql.rdbms.mysql.userdatatype.numeric", "NUMERIC", "DECIMAL(10,0)"},
    {"com.mysql.rdbms.mysql.userdatatype.dec", "DEC", "DECIMAL(10,0)"},
    {"com.mysql.rdbms.mysql.userdatatype.character", "CHARACTER", "CHAR(1)"}
    // End type aliases
  };
    
  grt::ListRef<db_UserDatatype> list(get_grt());
  
  for (size_t i= 0; i < sizeof(type_init_data)/sizeof(*type_init_data); i++)
  {
    std::string type= type_init_data[i].sql_def;
    std::string::size_type paren= type.find('(');
    if (paren != std::string::npos)
      type= type.substr(0, paren);
    
    db_SimpleDatatypeRef simpletype(bec::CatalogHelper::get_datatype(rdbms->simpleDatatypes(), 
                                                                     type));
    
    if (!simpletype.is_valid()) // unlikely
    {
      g_warning("Could not define built-in userdatatype <%s> %s (%s)", 
                type_init_data[i].oid, type_init_data[i].name, type_init_data[i].sql_def);
      continue;
    }
    db_UserDatatypeRef udata(get_grt());
    
    udata->__set_id(type_init_data[i].oid);
    
    udata->owner(catalog);
    udata->name(type_init_data[i].name);
    udata->sqlDefinition(type_init_data[i].sql_def);
    udata->actualType(simpletype);

    list.insert(udata);
  }
  
  return list;
}



void WBComponentPhysical::setup_physical_model(grt::GRT *grt, workbench_DocumentRef &doc, 
                                     const std::string &rdbms_name, const std::string &rdbms_version)
{
  // init physical model
  workbench_physical_ModelRef pmodel(grt);
  pmodel->owner(doc);
  
  pmodel->connectionNotation(_wb->get_wb_options().get_string("DefaultConnectionNotation"));
  pmodel->figureNotation(_wb->get_wb_options().get_string("DefaultFigureNotation"));

  doc->physicalModels().insert(pmodel);

  db_mgmt_ManagementRef mgmt= db_mgmt_ManagementRef::cast_from(grt->get("/wb/rdbmsMgmt"));
  
  // find the rdbms module for the db we want
  db_mgmt_RdbmsRef rdbms;

  rdbms= grt::find_named_object_in_list<db_mgmt_Rdbms>(mgmt->rdbms(), rdbms_name);
  if (!rdbms.is_valid())
  {
    throw grt_runtime_error("Could not locate RDBMS support object for "+rdbms_name,
                            "new_physical(): There is no RDBMS object with the name "+rdbms_name+" in the /rdbmsMgmt/rdbms list.");
  }
  pmodel->rdbms(rdbms);

  init_catalog_grt(rdbms, rdbms_version, pmodel);
}


//--------------------------------------------------------------------------------
// Model Management

grt::ValueRef WBComponentPhysical::add_new_db_schema_grt(grt::GRT *grt,
                                                const workbench_physical_ModelRef &model)
{
  
  db_SchemaRef schema;
  std::string name;
  std::string class_name;
  
  grt::AutoUndo undo(grt);

  class_name= *model->rdbms()->databaseObjectPackage()+".Schema";

  name= grt::get_name_suggestion_for_list_object(
    grt::ObjectListRef::cast_from(model->catalog()->schemata()), "new_schema");

  schema= grt->create_object<db_Schema>(class_name);
  schema->owner(model->catalog());
  schema->name(name);

  schema->createDate(bec::fmttime(0, DATETIME_FMT));
  schema->lastChangeDate(bec::fmttime(0, DATETIME_FMT));

  model->catalog()->schemata().insert(schema);

  undo.end(_("Create New Schema"));

  return schema;
}


void WBComponentPhysical::add_new_db_schema(const workbench_physical_ModelRef &model)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new schema",
                        sigc::bind<workbench_physical_ModelRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_db_schema_grt),
                                                            model));

  if (res.is_valid())
  {
    _wb->show_status_text(strfmt(_("Schema '%s' created."), db_SchemaRef::cast_from(res)->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(res));
  }
  else
    _wb->show_status_text(_("Could not create new schema."));
}


grt::ValueRef WBComponentPhysical::delete_db_schema_grt(grt::GRT *grt, const db_SchemaRef &schema, 
                                                          bool check_empty)
{
  std::map<std::string, bool> options;

  options["deletedbobjects"]= false;

  if (check_empty && 
      (schema->tables().count() > 0 || schema->views().count() > 0 || schema->routines().count() > 0))
  {
    grt::DictRef dict(grt);
    
    dict.gset("name", schema->name());
    dict.gset("tables", (long)schema->tables().count());
    dict.gset("views", (long)schema->views().count());
    dict.gset("routines", (long)schema->routines().count());
    
    return dict;
  }

  workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(schema));
  if (model.is_valid())
  {
    workbench_physical_DiagramRef view;

    if (model->catalog()->schemata().get_index(schema) == grt::BaseListRef::npos)
      return grt::ValueRef();

    grt::AutoUndo undo(grt);

    for (size_t vc= model->diagrams().count(), vi= 0; vi < vc; vi++)
    {
      view= model->diagrams().get(vi);
      std::list<model_FigureRef> figures;

      // remove canvas objects for schema contents
      for (size_t c= schema->tables().count(), i= 0; i < c; i++)
      {
        db_TableRef table= schema->tables().get(i);
        model_FigureRef figure= view->getFigureForDBObject(table);
        if (figure.is_valid())
          figures.push_back(figure);
      }
      for (size_t c= schema->views().count(), i= 0; i < c; i++)
      {
        db_ViewRef v= schema->views().get(i);
        model_FigureRef figure= view->getFigureForDBObject(v);
        if (figure.is_valid())
          figures.push_back(figure);
      }
      for (size_t c= schema->routineGroups().count(), i= 0; i < c; i++)
      {
        db_RoutineGroupRef rgroup= schema->routineGroups().get(i); 
        model_FigureRef figure= view->getFigureForDBObject(rgroup);
        if (figure.is_valid())
          figures.push_back(figure);
      }

      for (std::list<model_FigureRef>::const_iterator f= figures.begin(); f != figures.end(); ++f)
        delete_model_object(*f, options);
    }
    // remove schema
    model->catalog()->schemata().remove_value(schema);

    undo.end(_("Delete Schema"));
  }

  return grt::ValueRef();
}


void WBComponentPhysical::delete_db_schema(const db_SchemaRef &schema)
{
  grt::ValueRef result;
  
  _wb->show_status_text(_("Deleting schema..."));

  result= _wb->execute_in_grt_thread("Delete schema",
            sigc::bind<db_SchemaRef, bool>(sigc::mem_fun(this, &WBComponentPhysical::delete_db_schema_grt),
                                          schema, true));

  if (result.is_valid() && result.type() == DictType)
  {
    grt::DictRef info(grt::DictRef::cast_from(result));
    int res;
    std::string objects;

    if (info.get_int("tables") > 0)
      objects+= strfmt("%li tables, ", info.get_int("tables"));
    if (info.get_int("views") > 0)
      objects+= strfmt("%li views, ", info.get_int("views"));
    if (info.get_int("routines") > 0)
      objects+= strfmt("%li routines, ", info.get_int("routines"));

    if (!objects.empty())
      objects= objects.substr(0, objects.length()-2);
    
    res= _wb->confirm_action(_("Delete Schema"),
                        strfmt(_("The schema '%s' contains objects (%s).\n"
                              "Do you want to delete it with all its contents?"),
                            info.get_string("name").c_str(), objects.c_str()),
                        _("Delete"), _("Cancel"), "");
    if (res != 1)
    {
      _wb->show_status_text(_("Delete schema cancelled."));
      return;
    }

    result= _wb->execute_in_grt_thread("Delete schema",
             sigc::bind<db_SchemaRef, bool>(sigc::mem_fun(this, &WBComponentPhysical::delete_db_schema_grt),
                                           schema, false));
  }

  if (!result.is_valid())
    _wb->show_status_text(_("Schema deleted."));
  else
    _wb->show_status_text(_("Could not delete schema."));
}

#include "grts/structs.meta.h"

grt::ValueRef WBComponentPhysical::add_new_db_table_grt(grt::GRT *grt,
                                                  const db_SchemaRef &schema)
{
  grt::AutoUndo undo(grt);

  db_TableRef table= schema->addNewTable(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());

  if (table.has_member("tableEngine"))
    table.set_member("tableEngine", _wb->get_grt_manager()->get_app_option("db.mysql.Table:tableEngine"));

  undo.end(_("Create Table"));
  
  return table;
}



void WBComponentPhysical::add_new_db_table(const db_SchemaRef &schema)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new table",
                        sigc::bind<db_SchemaRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_db_table_grt),
                                                            schema));

  if (res.is_valid())
  {
    db_TableRef table(db_TableRef::cast_from(res));

    _wb->show_status_text(strfmt(_("Table '%s' created in schema '%s'"), table->name().c_str(),
                         table->owner()->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(res));
  }
  else
    _wb->show_status_text(_("Could not create new table."));
}


grt::ValueRef WBComponentPhysical::add_new_db_view_grt(grt::GRT *grt,
                                                         const db_SchemaRef &schema)
{
  grt::AutoUndo undo(grt);

  db_ViewRef view= 
    schema->addNewView(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());

  undo.end(_("Create View"));

  return view;
}



void WBComponentPhysical::add_new_db_view(const db_SchemaRef &schema)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new view",
                        sigc::bind<db_SchemaRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_db_view_grt),
                                                            schema));
  if (res.is_valid())
  {
    db_ViewRef view(db_ViewRef::cast_from(res));

    _wb->show_status_text(strfmt(_("View '%s' created in schema '%s'"), view->name().c_str(),
                         view->owner()->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(res));
  }
  else
    _wb->show_status_text(_("Could not create new view"));
}




grt::ValueRef WBComponentPhysical::add_new_db_routine_group_grt(grt::GRT *grt,
                                                         const db_SchemaRef &schema)
{
  grt::AutoUndo undo(grt);

  db_RoutineGroupRef rgroup=
    schema->addNewRoutineGroup(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());

  undo.end(_("Create Routine Group"));

  return rgroup;
}



void WBComponentPhysical::add_new_db_routine_group(const db_SchemaRef &schema)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new routine group",
                        sigc::bind<db_SchemaRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_db_routine_group_grt),
                                                            schema));
  if (res.is_valid())
  {
    db_RoutineGroupRef rgroup(db_RoutineGroupRef::cast_from(res));

    _wb->show_status_text(strfmt(_("Routine group '%s' created in schema '%s'"), rgroup->name().c_str(),
                         rgroup->owner()->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(res));
  }
  else
    _wb->show_status_text(_("Could not create new routine group"));
}



grt::ValueRef WBComponentPhysical::add_new_db_routine_grt(grt::GRT *grt, const db_SchemaRef &schema)
{
  grt::AutoUndo undo(grt);

  db_RoutineRef routine= 
    schema->addNewRoutine(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());

  undo.end(_("Create Routine"));

  return routine;
}



void WBComponentPhysical::add_new_db_routine(const db_SchemaRef &schema)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new routine",
                        sigc::bind<db_SchemaRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_db_routine_grt),
                                                            schema));
  if (res.is_valid())
  {
    db_RoutineRef routine(db_RoutineRef::cast_from(res));

    _wb->show_status_text(strfmt(_("Routine '%s' created in schema '%s'"), routine->name().c_str(),
                         routine->owner()->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(res));
  }
  else
    _wb->show_status_text(_("Could not create new routine"));
}




grt::ValueRef WBComponentPhysical::add_new_stored_script_grt(grt::GRT *grt, const workbench_physical_ModelRef &model,
                                                                 const std::string &path)
{
  db_ScriptRef script(grt);
  std::string name= "script";
  if (!path.empty())
    name= g_basename(path.c_str());
  script->owner(model);
  script->name(grt::get_name_suggestion_for_list_object(
    grt::ObjectListRef::cast_from(model->scripts()), name, false));
  script->createDate(bec::fmttime(0, DATETIME_FMT));
  script->lastChangeDate(bec::fmttime(0, DATETIME_FMT));
  script->filename(_wb->create_attached_file("script", path));
 
  grt::AutoUndo undo(grt);
  // insertion will trigger the creation of the file
  model->scripts().insert(script);
  if (path.empty())
    undo.end(_("Add SQL Script"));
  else
    undo.end(strfmt(_("Add Script File '%s'"), name.c_str()));

  return script;
}


void WBComponentPhysical::add_new_stored_script(const workbench_physical_ModelRef &model, const std::string &path)
{
  _wb->execute_in_grt_thread("Create new stored script",
    sigc::bind<workbench_physical_ModelRef,std::string>(sigc::mem_fun(this, &WBComponentPhysical::add_new_stored_script_grt),
                                                            model, path));
}


grt::ValueRef WBComponentPhysical::add_new_stored_note_grt(grt::GRT *grt, const workbench_physical_ModelRef &model,
                                                               const std::string &path)
{
  GrtStoredNoteRef note(grt);
  std::string name= _("New Note");
  if (!path.empty())
    name= g_basename(path.c_str());
  note->owner(model);
  note->name(grt::get_name_suggestion_for_list_object(
    grt::ObjectListRef::cast_from(model->notes()), name, false));
  note->createDate(bec::fmttime(0, DATETIME_FMT));
  note->lastChangeDate(bec::fmttime(0, DATETIME_FMT));
  note->filename(_wb->create_attached_file("note", path));

  grt::AutoUndo undo(grt);
  // insertion will trigger the creation of the file
  model->notes().insert(note);
  if (path.empty())
    undo.end(_("Add Text Note"));
  else
    undo.end(strfmt(_("Add Note File '%s'"), name.c_str()));

  return note;
}


void WBComponentPhysical::add_new_stored_note(const workbench_physical_ModelRef &model, const std::string &path)
{
  _wb->execute_in_grt_thread("Create new stored note",
    sigc::bind<workbench_physical_ModelRef,std::string>(sigc::mem_fun(this, &WBComponentPhysical::add_new_stored_note_grt),
                                                            model, path));
}


void WBComponentPhysical::clone_db_object_to_schema(const db_SchemaRef &schema, const db_DatabaseObjectRef &object,
                                                    grt::CopyContext &context)
{
  grt::AutoUndo undo(get_grt());

  if (object.is_instance(db_Table::static_class_name()))
  {
    db_TableRef dbtable(db_TableRef::cast_from(context.copy(object)));

    if (grt::find_named_object_in_list(schema->tables(), dbtable->name()).is_valid())
      dbtable->name(grt::get_name_suggestion_for_list_object(schema->tables(),
        *dbtable->name()+"_copy"));

    dbtable->owner(schema);
    dbtable->oldName("");
    schema->tables().insert(dbtable);
    undo.end(strfmt(_("Duplicate '%s'"), dbtable->name().c_str()));
  }
  else if (object.is_instance(db_View::static_class_name()))
  {
    db_ViewRef dbview(db_ViewRef::cast_from(context.copy(object)));

    if (grt::find_named_object_in_list(schema->views(), dbview->name()).is_valid())
      dbview->name(grt::get_name_suggestion_for_list_object(schema->views(),
        *dbview->name()+"_copy"));

    dbview->owner(schema);
    dbview->oldName("");
    schema->views().insert(dbview);
    undo.end(strfmt(_("Duplicate '%s'"), dbview->name().c_str()));
  }
  else if (object.is_instance(db_Routine::static_class_name()))
  {
    db_RoutineRef dbroutine(db_RoutineRef::cast_from(context.copy(object)));

    if (grt::find_named_object_in_list(schema->routines(), dbroutine->name()).is_valid())
      dbroutine->name(grt::get_name_suggestion_for_list_object(schema->routines(),
        *dbroutine->name()+"_copy"));

    dbroutine->owner(schema);
    dbroutine->oldName("");
    schema->routines().insert(dbroutine);
    undo.end(strfmt(_("Duplicate '%s'"), dbroutine->name().c_str()));
  }
  else if (object.is_instance(db_RoutineGroup::static_class_name()))
  {
    db_RoutineGroupRef dbroutineGroup(db_RoutineGroupRef::cast_from(context.copy(object)));

    if (grt::find_named_object_in_list(schema->routineGroups(), dbroutineGroup->name()).is_valid())
      dbroutineGroup->name(grt::get_name_suggestion_for_list_object(schema->routineGroups(),
        *dbroutineGroup->name()+"_copy"));

    dbroutineGroup->owner(schema);
    dbroutineGroup->oldName("");
    schema->routineGroups().insert(dbroutineGroup);
    undo.end(strfmt(_("Duplicate '%s'"), dbroutineGroup->name().c_str()));
  }
}


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


grt::ValueRef WBComponentPhysical::place_db_object_grt(grt::GRT *grt, ModelDiagramForm *view,
                                                       const mdc::Point &pos,
                                                       const db_DatabaseObjectRef &object,
                                                       bool select_figure)
{
  workbench_physical_DiagramRef pview(workbench_physical_DiagramRef::cast_from(view->get_model_diagram()));
  model_FigureRef figure;
  std::string object_member;

  if (object.is_instance(db_Table::static_class_name()))
  {
    figure= pview->placeTable(db_TableRef::cast_from(object), pos.x, pos.y);
    object_member= "table";
  }
  else if (object.is_instance(db_View::static_class_name()))
  {
    figure= pview->placeView(db_ViewRef::cast_from(object), pos.x, pos.y);
    object_member= "view";
  }
  else if (object.is_instance(db_RoutineGroup::static_class_name()))
  {
    figure= pview->placeRoutineGroup(db_RoutineGroupRef::cast_from(object), pos.x, pos.y);
    object_member= "routineGroup";
  }
  else
    throw std::invalid_argument("trying to place invalid object on view");

  grt::AutoUndo undo(get_grt());

  if ((*figure->color()).empty())
  {
    if (!view->get_tool_argument(figure.class_name()+":Color").empty())
      figure->color(grt::StringRef(view->get_tool_argument(figure.class_name()+":Color")));
    else
      figure->color(_wb->get_wb_options().get_string(figure.class_name()+":Color", ""));
  }

  if (view->get_model_options().get_int("workbench.physical.ObjectFigure:Expanded", 
        _wb->get_wb_options().get_int("workbench.physical.ObjectFigure:Expanded")))
    figure->expanded(1);
  else
    figure->expanded(0);

  if (select_figure)
  {
    pview->unselectAll();
    pview->selectObject(figure);
  }
  undo.end(strfmt(_("Place '%s'"), object->name().c_str()));

  return figure;
}


model_FigureRef WBComponentPhysical::place_db_object(ModelDiagramForm *view, const mdc::Point &pos, 
                                          const db_DatabaseObjectRef &object,
                                          bool select_figure)
{
  grt::ValueRef r;
  
  try {
    r= _wb->execute_in_grt_thread("Place existing object in model",
      sigc::bind<ModelDiagramForm*, mdc::Point, db_DatabaseObjectRef>(
        sigc::mem_fun(this, &WBComponentPhysical::place_db_object_grt), view, pos, object, select_figure));
  }
  catch (std::invalid_argument &)
  {
    _wb->show_status_text(_("Cannot place object."));
    return model_FigureRef();
  }
  catch (grt::grt_runtime_error &exc)
  {
    _wb->show_exception(_("Place Object on Canvas"), exc);
    return model_FigureRef();
  }

  if (r.is_valid())
    _wb->show_status_text(strfmt(_("Placed %s"), GrtObjectRef::cast_from(r)->name().c_str()));
  else
    _wb->show_status_text(_("Failed placing db object."));
  
  return model_FigureRef::cast_from(r);
}


std::vector<std::string> WBComponentPhysical::get_accepted_drop_types() const
{
  std::vector<std::string> types;
  
  types.push_back(WB_DBOBJECT_DRAG_TYPE);

  return types;
}


bool WBComponentPhysical::accepts_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
{
  if (objects.empty())
    return false;

  if (type == WB_DBOBJECT_DRAG_TYPE)
  {
    for (std::list<GrtObjectRef>::const_iterator iter= objects.begin();
      iter != objects.end(); ++iter)
    {
      if (!(*iter).is_instance(db_DatabaseObject::static_class_name()))
        return false;
    }
    return true;
  }
  return false;
}


bool WBComponentPhysical::perform_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
{
  if (objects.empty())
    return false;

  if (type == WB_DBOBJECT_DRAG_TYPE)
  {
    std::list<db_DatabaseObjectRef> dbobjects;

    for (std::list<GrtObjectRef>::const_iterator iter= objects.begin();
      iter != objects.end(); ++iter)
      dbobjects.push_back(db_DatabaseObjectRef::cast_from((*iter)));

    interactive_place_db_objects(view, x, y, dbobjects);
    return true;
  }

  return false;
}


bool WBComponentPhysical::perform_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::string &data)
{
  if (data.empty())
    return false;
  
  if (type == WB_DBOBJECT_DRAG_TYPE)
  {
    std::list<db_DatabaseObjectRef> dbobjects;
    db_CatalogRef catalog= workbench_physical_ModelRef::cast_from(view->get_model_diagram()->owner())->catalog();
    
    dbobjects= bec::CatalogHelper::dragdata_to_dbobject_list(catalog, data);
    
    interactive_place_db_objects(view, x, y, dbobjects);
    
    return true;
  }
  
  return false;
}


void WBComponentPhysical::interactive_place_db_objects(ModelDiagramForm *vform, int x, int y, 
      const std::list<db_DatabaseObjectRef> &objects)
{
  size_t dupes= 0;
  std::vector<db_TableRef> tables;

  if (objects.empty())
  {
    _wb->show_message(_("Cannot Place Object"), _("The dragged object cannot be placed in the diagram."));
    return;
  }

  grt::AutoUndo undo(get_grt());

  mdc::Point op, p= vform->get_view()->window_to_canvas(x, y);
  op= p;
  mdc::Size view_size(vform->get_view()->get_total_view_size());

  vform->get_model_diagram()->unselectAll();
  
  for (std::list<db_DatabaseObjectRef>::const_iterator iter= objects.begin(); iter != objects.end(); ++iter)
  {
    if (has_figure_for_object_in_active_view(*iter, vform))
      dupes++;
    else
    {
      model_FigureRef figure= place_db_object(vform, p, *iter, false);

      if (figure.is_valid())
        vform->get_model_diagram()->selectObject(figure);
      
      p.x+= 20;
      p.y+= 20;
      if (p.x + 100 > view_size.width)
      {
        op.y+= 20;
        p= op;
      }
      else if (p.y + 100 > view_size.height)
      {
        op.y+= 20;
        p= op;
      }

      if (p.x + 100 > view_size.width || p.y+100 > view_size.height)
        p= op;

      if (iter->is_instance(db_Table::static_class_name()))
        tables.push_back(db_TableRef::cast_from(*iter));
    }
  }
  
  undo.end(_("Place object(s) on canvas"));

  if (dupes == objects.size())
  {
    if (dupes == 1)
      _wb->show_message(_("Cannot Place Object"), _("The object cannot be placed because it's already present in this diagram."));
    else
      _wb->show_message(_("Cannot Place Objects"), _("The objects cannot be placed because they're already present in this diagram."));
  }
  else if (dupes > 0)
    _wb->show_message(_("Cannot Place Object(s)"), _("Some of the objects could not be placed because they're already present in this diagram."));

  // manually force a refresh since adding a figure deosnt trigger a catalog tree refresh
  _wb->request_refresh(RefreshSchema, "");
}


grt::ValueRef WBComponentPhysical::place_new_db_object_grt(grt::GRT *grt, ModelDiagramForm *vform, 
                                                             const mdc::Point &pos,
                                                             wb::ObjectType type)
{
  std::string object_struct_name;
  db_SchemaRef target_schema;
  std::string schema_name;

  grt::AutoUndo undo(get_grt());

  model_DiagramRef view(vform->get_model_diagram());
  workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(view));

  switch (type)
  {
  case ObjectTable:
    object_struct_name= workbench_physical_TableFigure::static_class_name();
    break;
  case ObjectView:
    object_struct_name= workbench_physical_ViewFigure::static_class_name();
    break;
  case ObjectRoutineGroup:
    object_struct_name= workbench_physical_RoutineGroupFigure::static_class_name();
    break;
  default:
    throw std::logic_error("place_db_object() called with invalid tool");
  }
  schema_name= vform->get_tool_argument(object_struct_name+std::string(":Schema"));

  if (!schema_name.empty())
  {
    db_SchemaRef schema(grt::find_named_object_in_list(model->catalog()->schemata(), schema_name));
    if (schema.is_valid())
      target_schema= schema;
  }

  // pick a default schema..
  if (!target_schema.is_valid())
  {
    if (model->catalog()->schemata().count() == 0)
    {
      // if there are no schemas, create a default one
      add_new_db_schema_grt(grt, model);
    }
    target_schema= model->catalog()->schemata().get(0);
  }

  db_DatabaseObjectRef object;

  switch (type)
  {
  case ObjectTable:
    object= db_DatabaseObjectRef::cast_from(add_new_db_table_grt(grt, target_schema));
    break;
  case ObjectView:
    object= db_DatabaseObjectRef::cast_from(add_new_db_view_grt(grt, target_schema));
    break;
  case ObjectRoutineGroup:
    object= db_DatabaseObjectRef::cast_from(add_new_db_routine_group_grt(grt, target_schema));
    break;
  default:
    throw std::logic_error("place_db_object() called with invalid tool");
  }

  std::string collation= vform->get_tool_argument(object_struct_name+":Collation");

  if (collation!="" && collation[0] != '*')
  {
    if (object.has_member("defaultCollationName"))
      object.set_member("defaultCollationName", grt::StringRef(collation));

    std::string charset= split_string(collation, "_", 1)[0];
    if (object.has_member("defaultCharacterSetName"))
      object.set_member("defaultCharacterSetName", grt::StringRef(charset));
  }

  std::string engine= vform->get_tool_argument(object_struct_name+":Engine");
  if (!engine.empty() && engine[0] != '*')
  {
    if (object.has_member("tableEngine"))
      object.set_member("tableEngine", grt::StringRef(engine));
  }

  grt::ValueRef result= place_db_object_grt(grt, vform, pos, object, true);

  undo.end(strfmt(_("Place '%s'"), object->name().c_str()));

  return result;
}


void WBComponentPhysical::place_new_db_object(ModelDiagramForm *view, const mdc::Point &pos,
                                              wb::ObjectType type)
{
  grt::ValueRef r;
  
  r= _wb->execute_in_grt_thread("Place object in model",
                sigc::bind<ModelDiagramForm*,mdc::Point,wb::ObjectType>
                           (sigc::mem_fun(this, &WBComponentPhysical::place_new_db_object_grt),
                            view,  pos, type));
  if (r.is_valid())
    _wb->show_status_text(strfmt(_("Placed new %s"), model_FigureRef::cast_from(r)->name().c_str()));
  else
    _wb->show_status_text(_("Failed placing db object."));
}


bool WBComponentPhysical::create_nm_relationship_grt(grt::GRT *grt, ModelDiagramForm *view,
                                                   workbench_physical_TableFigureRef table1, 
                                                   workbench_physical_TableFigureRef table2,
                                                   bool imandatory, bool fmandatory)
{
  grt::AutoUndo undo(grt);
  // create the associative table for a n:m relationship
  db_TableRef atable=
    bec::TableHelper::create_associative_table(db_SchemaRef::cast_from(table1->table()->owner()),
                                              table1->table(), table2->table(),
                                              imandatory, fmandatory, 
                                               workbench_physical_ModelRef::cast_from(view->get_model_diagram()->owner())->rdbms(),
                                              _wb->get_wb_options(),
                                              view->get_model_diagram()->owner()->options());

  if (!atable.is_valid())
    return false;

  // place the assoc table in the view
  mdc::Point pos;

  pos.x= (table1->left() + table2->left()) / 2;
  pos.y= (table1->top() + table2->top()) / 2;

  place_db_object_grt(grt, view, pos, atable, true);

  undo.end(_("Create n:m Relationship"));

  return true;
}


WBComponentPhysical::RelationshipToolContext *
WBComponentPhysical::start_relationship(ModelDiagramForm *view, const mdc::Point &pos,
                                           RelationshipType type)
{
  RelationshipToolContext *rctx= new RelationshipToolContext(this, view, type);
  
  return rctx;
}


void WBComponentPhysical::cancel_relationship(ModelDiagramForm *view, RelationshipToolContext *rctx)
{
  if (rctx)
  {
    rctx->cancel();

    delete rctx;
  }  
}


void WBComponentPhysical::delete_db_object(const db_DatabaseObjectRef &object)
{
  db_SchemaRef schema(db_SchemaRef::cast_from(object->owner()));
  std::map<std::string, bool> options;
  workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(schema));

  // do not delete db object from model object deleter, since that would
  // mean deleting it twice and adding 2 entries in the undo stack
  options["deletedbobjects"]= false;

  //XXX need to look for refs by other objects and show them to user
  // and confirm that they should be removed or cancel

  if (object.is_instance(db_Table::static_class_name()))
  {
    grt::AutoUndo undo(get_grt());

    schema->tables().remove_value(db_TableRef::cast_from(object));

    if (model.is_valid())
    {
      for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin();
           view != model->diagrams().end(); ++view)
      {
        grt::ListRef<model_Figure> figures((*view)->figures());
        for (size_t f= figures.count(); f > 0; --f)
        {
          model_FigureRef figure= figures[f-1];
          if (figure.is_instance(workbench_physical_TableFigure::static_class_name())
            && workbench_physical_TableFigureRef::cast_from(figure)->table() == object)
          {
            delete_model_object(figure, options);
          }
        }
      }
    }

    // remove referencing foreign keys
    {
      db_TableRef table= db_TableRef::cast_from(object);
      grt::ListRef<db_ForeignKey> foreignKeys(db_SchemaRef::cast_from(table->owner())->getForeignKeysReferencingTable(table));
      if (0 < foreignKeys.count())
      {
        for (grt::ListRef<db_ForeignKey>::const_iterator iter= foreignKeys.begin(); 
          iter != foreignKeys.end(); ++iter)
        {
          db_ForeignKeyRef fk(*iter);
          db_TableRef ref_table= db_TableRef::cast_from(fk->owner());

          // remove corresponding index
          grt::ListRef<db_Index> indices= ref_table->indices();
          for (size_t count= indices.count(), i= 0; i < count; ++i)
          {
            db_IndexRef index= indices.get(i);
            if (0 == strcmp(index->indexType().c_str(), "FOREIGN"))
            {
              grt::ListRef<db_IndexColumn> index_columns= index->columns();
              grt::ListRef<db_Column> fk_columns= fk->columns();
              if (index_columns.count() == fk_columns.count())
              {
                bool equal= true;
                for (size_t count= index_columns.count(), c= 0; c < count; ++c)
                {
                  db_ColumnRef index_column= index_columns.get(c)->referencedColumn();
                  if (index_column.is_valid())
                  {
                    db_ColumnRef fk_column= find_object_in_list(fk_columns, index_column.id());
                    if (!fk_column.is_valid())
                    {
                      equal= false;
                      break;
                    }
                  }
                }
                if (equal)
                {
                  ref_table->indices().remove_value(index);
                  break;
                }
              }
            }
          }

          ref_table->foreignKeys().remove_value(fk);
        }
      }
    }

    remove_references_to_object(object);

    undo.end(_("Delete Table"));
  }
  else if (object.is_instance(db_View::static_class_name()))
  {
    grt::AutoUndo undo(get_grt());

    schema->views().remove_value(db_ViewRef::cast_from(object));

    if (model.is_valid())
    {
      for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin(); 
           view != model->diagrams().end(); ++view)
      {
        for (grt::ListRef<model_Figure>::const_reverse_iterator figure= (*view)->figures().rbegin(); 
             figure != (*view)->figures().rend(); ++figure)
        {
          if ((*figure).is_instance(workbench_physical_ViewFigure::static_class_name())
            && workbench_physical_ViewFigureRef::cast_from(*figure)->view() == object)
          {
            delete_model_object(*figure, options);
          }
        }
      }
    }
    
    remove_references_to_object(object);

    undo.end(_("Delete View"));
  }
  else if (object.is_instance(db_RoutineGroup::static_class_name()))
  {
    grt::AutoUndo undo(get_grt());
    
    schema->routineGroups().remove_value(db_RoutineGroupRef::cast_from(object));

    if (model.is_valid())
    {
      for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin(); 
           view != model->diagrams().end(); ++view)
      {
        for (grt::ListRef<model_Figure>::const_reverse_iterator figure= (*view)->figures().rbegin(); 
             figure != (*view)->figures().rend(); ++figure)
        {
          if ((*figure).is_instance(workbench_physical_RoutineGroupFigure::static_class_name())
            && workbench_physical_RoutineGroupFigureRef::cast_from(*figure)->routineGroup() == object)
          {
            delete_model_object(*figure, options);
          }
        }
      }
    }

    remove_references_to_object(object);

    undo.end(_("Delete Routine Group"));
  }
  else if (object.is_instance(db_Routine::static_class_name()))
  {
    grt::AutoUndo undo(get_grt());
    schema->routines().remove_value(db_RoutineRef::cast_from(object));
    //XXX remove from routine groups

    remove_references_to_object(object);

    undo.end(_("Delete Routine"));
  }
}



bool WBComponentPhysical::delete_model_object(const model_ObjectRef &object, std::map<std::string,bool> &options)
{
  if (object.is_instance(workbench_physical_Connection::static_class_name()))
  {
    workbench_physical_ConnectionRef conn(workbench_physical_ConnectionRef::cast_from(object));
    db_ForeignKeyRef fk(conn->foreignKey());
    db_TableRef table(db_TableRef::cast_from(fk->owner()));
    int result;

    // check if the fk still belongs to the table
    // if the connection is being deleted together with the table that it points to,
    // it will be auto-removed depdnding on the order things get executed and by the
    // the this place is reached, the FK will be already gone
    if (table->foreignKeys().get_index(fk) == grt::BaseListRef::npos)
      return false;

    result= _wb->confirm_action(_("Delete Foreign Key Columns"),
      _("Please confirm whether columns used by the foreign key should be deleted too."),
      _("Delete"), _("Keep"), _("Cancel"));

    if (result < 0) return false;

    grt::AutoUndo undo(get_grt());
/*
    model_DiagramRef view(conn->owner());
    // remove connection
    view->removeConnection(conn);
*/
    table->removeForeignKey(fk, result == 1 ? true : false);

    undo.end(_("Delete Relationship"));
  }
  else if (object.is_instance(model_Figure::static_class_name()))
  {
    if (options.find("deletedbobjects-canceled") != options.end())
      return false;

    model_FigureRef figure(model_FigureRef::cast_from(object));
    bool del_db_object;

    if (options.find("deletedbobjects") == options.end())
    {
      int ok= 0;
      std::map<std::string, bool>::const_iterator it= options.find("silent-mode");
      if((it == options.end()) || (it->second == false))
      {
        ok= _wb->execute_in_main_thread<int>("ask delete",
          sigc::bind<std::string, std::string, std::string, std::string>(_wb->confirm_action, 
                    _("Delete Object"), 
                    _("Should corresponding database objects be deleted with the figures?"),
                    _("Delete"), _("Keep"), _("Skip")));
      }
      else
      {
        ok= 1;
      }

      if (ok < 0)
      {
        options["deletedbobjects-canceled"]= true;
        return false;
      }
      if (ok == 1)
        del_db_object= true;
      else
        del_db_object= false;

      options["deletedbobjects"]= del_db_object;
    }
    else
    {
      del_db_object= options["deletedbobjects"];
    }

    grt::AutoUndo undo(get_grt());
    // if the figure is a DB object, the DB object is also deleted
    if (figure.is_instance(workbench_physical_TableFigure::static_class_name()))
    {
      db_TableRef dbtable(workbench_physical_TableFigureRef::cast_from(figure)->table());

      workbench_physical_DiagramRef::cast_from(figure->owner())->deleteConnectionsForTable(dbtable);

      workbench_physical_TableFigureRef::cast_from(figure)->table(db_TableRef());

      // this must be called after the figure->table field is invalidated, otherwise
      // delete_db_object() will try to delete the figure again and we get into a loop
      if (del_db_object)
        delete_db_object(dbtable);
    }
    else if (figure.is_instance(workbench_physical_ViewFigure::static_class_name()))
    {      
      db_ViewRef view(workbench_physical_ViewFigureRef::cast_from(figure)->view());

      workbench_physical_ViewFigureRef::cast_from(figure)->view(db_ViewRef());

      if (del_db_object)
        delete_db_object(view);
    }
    else if (figure.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()))
    {
      db_RoutineGroupRef rg(workbench_physical_RoutineGroupFigureRef::cast_from(figure)->routineGroup());

      workbench_physical_RoutineGroupFigureRef::cast_from(figure)->routineGroup(db_RoutineGroupRef());

      if (del_db_object)
        delete_db_object(rg);
    }
    else
      return false;

    // removeFigure() will remove anything that depends on the figure automatically, ie connections
    workbench_physical_DiagramRef::cast_from(figure->owner())->removeFigure(figure);

    undo.end(strfmt(_("Delete '%s' Figure"), figure.get_metaclass()->get_attribute("caption").c_str()));
  }
  return true;
}






bool WBComponentPhysical::handles_figure(const model_ObjectRef &figure)
{
  if (figure.is_instance(workbench_physical_TableFigure::static_class_name()) ||
    figure.is_instance(workbench_physical_ViewFigure::static_class_name()) ||
    figure.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()) ||
    figure.is_instance(workbench_physical_Connection::static_class_name()))
    return true;
  return false;
}


model_ObjectRef WBComponentPhysical::clone_object(const model_ObjectRef &object, const model_LayerRef &destlayer, 
                                               grt::CopyContext &copy_context, bool copy_only)
{
  if (object.is_instance(workbench_physical_TableFigure::static_class_name()))
  {
    workbench_physical_TableFigureRef table(workbench_physical_TableFigureRef::cast_from(object));
    std::set<std::string> skip;

    // copy table
    db_TableRef dbtable(db_TableRef::cast_from(copy_context.copy(table->table())));

    copy_context.update_references();

    // copy figure
    skip.insert("table");
    workbench_physical_TableFigureRef copy(workbench_physical_TableFigureRef::cast_from(copy_context.copy(table)));
    copy->table(dbtable);
    
    copy_context.update_references();

    if (destlayer.is_valid())
    {
      copy->owner(destlayer->owner());
      copy->layer(destlayer);

      if (!copy_only)
      {
        if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbtable->owner())->tables(), dbtable->name()).is_valid())
          dbtable->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbtable->owner())->tables(),
            *dbtable->name()+"_copy"));

        grt::AutoUndo undo(get_grt());

        db_SchemaRef::cast_from(dbtable->owner())->tables().insert(dbtable);

        destlayer->owner()->addFigure(copy);
        workbench_physical_DiagramRef::cast_from(destlayer->owner())->createConnectionsForTable(copy->table());
        
        undo.end(strfmt(_("Duplicate Table '%s'"), copy->name().c_str()));
      }
    }
    else
    {
      copy->owner(model_DiagramRef());
      copy->layer(model_LayerRef());
    }

    return copy;
  }
  else if (object.is_instance(workbench_physical_ViewFigure::static_class_name()))
  {
    workbench_physical_ViewFigureRef view(workbench_physical_ViewFigureRef::cast_from(object));
    std::set<std::string> skip;

    // copy view
    db_ViewRef dbview(db_ViewRef::cast_from(copy_context.copy(view->view())));

    // copy figure
    skip.insert("view");
    workbench_physical_ViewFigureRef copy(workbench_physical_ViewFigureRef::cast_from(copy_context.copy(view)));
    copy->view(dbview);

    if (destlayer.is_valid())
    {
      copy->owner(destlayer->owner());
      copy->layer(destlayer);

      if (!copy_only)
      {
        if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbview->owner())->views(), dbview->name()).is_valid())
          dbview->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbview->owner())->views(),
            *dbview->name()+"_copy"));

        grt::AutoUndo undo(get_grt());

        db_SchemaRef::cast_from(dbview->owner())->views().insert(dbview);

        destlayer->owner()->addFigure(copy);

        undo.end(strfmt(_("Duplicate View '%s'"), copy->name().c_str()));
      }
    }
    else
    {
      copy->owner(model_DiagramRef());
      copy->layer(model_LayerRef());
    }
    return copy;
  }
  else if (object.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()))
  {
    workbench_physical_RoutineGroupFigureRef routineGroup(workbench_physical_RoutineGroupFigureRef::cast_from(object));
    std::set<std::string> skip;

    // copy routineGroup
    db_RoutineGroupRef dbroutineGroup(db_RoutineGroupRef::cast_from(copy_context.copy(routineGroup->routineGroup())));

    // copy figure
    skip.insert("routineGroup");
    workbench_physical_RoutineGroupFigureRef copy(workbench_physical_RoutineGroupFigureRef::cast_from(copy_context.copy(routineGroup)));
    copy->routineGroup(dbroutineGroup);

    if (destlayer.is_valid())
    {
      copy->owner(destlayer->owner());
      copy->layer(destlayer);

      if (!copy_only)
      {
        if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups(), dbroutineGroup->name()).is_valid())
          dbroutineGroup->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups(),
            *dbroutineGroup->name()+"_copy"));

        grt::AutoUndo undo(get_grt());

        db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups().insert(dbroutineGroup);

        destlayer->owner()->addFigure(copy);

        undo.end(strfmt(_("Duplicate Routine Group '%s'"), copy->name().c_str()));
      }
    }
    else
    {
      copy->owner(model_DiagramRef());
      copy->layer(model_LayerRef());
    }
    return copy;
  }

  return model_ObjectRef();
}


bool WBComponentPhysical::can_paste_non_model_object(const grt::ObjectRef &object)
{
  if (db_DatabaseObjectRef::can_wrap(object) && 
    !has_figure_for_object_in_active_view(GrtObjectRef::cast_from(object)) &&
    (object.is_instance(db_Table::static_class_name()) ||
     object.is_instance(db_View::static_class_name()) ||
     object.is_instance(db_RoutineGroup::static_class_name())))
    return true;
  return false;
}


void WBComponentPhysical::paste_non_model_object(ModelDiagramForm *view, const grt::ObjectRef &object)
{
  std::list<db_DatabaseObjectRef> objects;

  // find the original object
  db_DatabaseObjectRef obj(db_DatabaseObjectRef::cast_from(object));
  db_SchemaRef schema(db_SchemaRef::cast_from(obj->owner()));

  if (obj.is_instance(db_Table::static_class_name()))
    objects.push_back(grt::find_named_object_in_list(schema->tables(), *obj->name()));
  else if (obj.is_instance(db_View::static_class_name()))
    objects.push_back(grt::find_named_object_in_list(schema->views(), *obj->name()));
  else if (obj.is_instance(db_RoutineGroup::static_class_name()))
    objects.push_back(grt::find_named_object_in_list(schema->routineGroups(), *obj->name()));
  else
    return;
  
  if (objects.front().is_valid())
    interactive_place_db_objects(view, 10, 10, objects);
}


std::string WBComponentPhysical::get_object_tooltip(const model_ObjectRef &object, mdc::CanvasItem *item)
{
  if (workbench_physical_TableFigureRef::can_wrap(object))
  {
    workbench_physical_TableFigureRef table_figure(workbench_physical_TableFigureRef::cast_from(object));
    db_TableRef table(table_figure->table());

    workbench_physical_TableFigure::ImplData *tfig= table_figure->get_data();
    if (tfig)
    {
      db_ColumnRef column(tfig->get_column_at(item));
      db_IndexRef index;

      if (!column.is_valid())
        index= tfig->get_index_at(item);

      if (column.is_valid())
      {
        std::string text;
        bool isfk= false;

        text.append(*column->name());
        if (table->isPrimaryKeyColumn(column))
          text.append(" (PK)");
        else if (table->isForeignKeyColumn(column))
        {
          isfk= true;
          text.append(" (FK)");
        }
        text.append("\n");

        if (isfk)
        {
          text.append("References: ");

          for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
          {
            db_ForeignKeyRef fk(table->foreignKeys()[i]);
            //ssize_t idx; // get_index returns size_t, so comparing it by >= 0 is always true!!!

            const size_t idx= fk->columns().get_index(column);
            if (idx != BaseListRef::npos)
            {
              if (fk->referencedTable().is_valid() && fk->referencedColumns().get(idx).is_valid())
              {
                text.append(fk->referencedTable()->name());
                text.append(".");
                text.append(fk->referencedColumns().get(idx)->name());
              }
              else
                text.append("INVALID");
              break;
            }
          }
          text.append("\n");
        }

        text.append(column->formattedRawType());
        text.append("\n");
        {
          std::string flags;
          if (*column->isNotNull())
            flags.append("NOT NULL"); //XXX add a method to db_Column to return a formatted string of all flags
          for (size_t c= column->flags().count(), i= 0; i < c; i++)
          {
            if (i > 0 || *column->isNotNull())
              flags.append(", ");
            flags.append(column->flags().get(i).c_str());
          }
          if (!flags.empty())
            text.append("Flags: ").append(flags);
        }
        if (*column->defaultValue() != "")
          text.append("\nDEFAULT ").append(column->defaultValue().c_str());

        /* comments can be too large
        if (*column->comment().c_str())
        {
          text.append("\n");
          text.append(column->comment());
        }
         */

        return text;
      }
      else if (index.is_valid())
      {
        std::string text;

        text.append(*index->name()).append("  (").append(index->indexType().c_str()).append(")\n");

        for (size_t c= index->columns().count(), i= 0; i < c; i++)
        {
          db_IndexColumnRef column(index->columns()[i]);

          text.append("  - ").append(column->referencedColumn()->name().c_str()).append("\n");
        }

        return text;
      }
      else if (table.is_valid()) // table
      {
        std::string text;

        text.append(table->owner()->name());
        text.append(".");
        text.append(table->name());
        text.append("\n");

        if (table->foreignKeys().count() > 0)
        {
          text.append("References:\n");
          for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
          {
            db_ForeignKeyRef fk(table->foreignKeys()[i]);
            if (fk->referencedTable().is_valid())
            {
              text.append("  (");
              for (size_t d= fk->columns().count(), j= 0; j < d; j++)
              {
                if (j > 0)
                  text.append(",");
                text.append(fk->columns()[j]->name());
              }
              text.append(") TO ");
              if (fk->referencedTable()->owner() != table->owner())
              {
                text.append(fk->referencedTable()->owner()->name());
                text.append(".");
              }
              text.append(fk->referencedTable()->name());
              text.append("(");
              for (size_t d= fk->referencedColumns().count(), j= 0; j < d; j++)
              {
                if (j > 0)
                  text.append(",");
                if (!fk->referencedColumns()[j].is_valid())
                  text.append("INVALID");
                else
                  text.append(fk->referencedColumns()[j]->name());
              }
              text.append(")\n");
            }
            else
              text.append("  INVALID\n");
          }
        }

        grt::ListRef<db_ForeignKey> foreignKeys(db_SchemaRef::cast_from(table->owner())->getForeignKeysReferencingTable(table));
        if (foreignKeys.is_valid() && foreignKeys.count() > 0)
        {
          text.append("Referenced By:\n");
          for (grt::ListRef<db_ForeignKey>::const_iterator iter= foreignKeys.begin(); 
            iter != foreignKeys.end(); ++iter)
          {
            db_ForeignKeyRef fk(*iter);
            text.append("  ");
            if (fk->owner()->owner() != table->owner())
            {
              text.append(fk->owner()->owner()->name());
              text.append(".");
            }
            text.append(fk->owner()->name());
            text.append(" (");
            for (size_t d= fk->columns().count(), j= 0; j < d; j++)
            {
              if (j > 0)
                text.append(",");
              if (fk->columns()[j].is_valid())
                text.append(fk->columns()[j]->name());
              else
                text.append("INVALiD");
            }
            text.append(") TO ");
            
            text.append("(");
            for (size_t d= fk->referencedColumns().count(), j= 0; j < d; j++)
            {
              if (j > 0)
                text.append(",");
              if (fk->referencedColumns()[j].is_valid())
                text.append(fk->referencedColumns()[j]->name());
              else
                text.append("INVALID");
            }
            text.append(")\n");
          }
        }

        /* comments can be too large
        if (*table->comment().c_str())
        {
          text.append("\n");
          text.append(table->comment());
        }
         */
        return text;
      }
    }
    return "";
  }
  else if (workbench_physical_ViewFigureRef::can_wrap(object))
    return workbench_physical_ViewFigureRef::cast_from(object)->view()->comment();

  else if (workbench_physical_RoutineGroupFigureRef::can_wrap(object))
    return workbench_physical_RoutineGroupFigureRef::cast_from(object)->routineGroup()->comment();

  return "";
}


GrtObjectRef WBComponentPhysical::get_object_for_figure(const model_ObjectRef &object)
{
  if (workbench_physical_TableFigureRef::can_wrap(object))
    return workbench_physical_TableFigureRef::cast_from(object)->table();

  else if (workbench_physical_ViewFigureRef::can_wrap(object))
    return workbench_physical_ViewFigureRef::cast_from(object)->view();

  else if (workbench_physical_RoutineGroupFigureRef::can_wrap(object))
    return workbench_physical_RoutineGroupFigureRef::cast_from(object)->routineGroup();

  return GrtObjectRef();
}


void WBComponentPhysical::activate_canvas_object(const model_ObjectRef &figure, bool newwindow)
{
  GrtObjectRef object(get_object_for_figure(figure));

  if (object.is_valid())
    _wb->open_object_editor(object, newwindow);

  else if (workbench_physical_ConnectionRef::can_wrap(figure))
    _wb->open_object_editor(figure, newwindow);
}



void WBComponentPhysical::block_model_notifications()
{
  for (std::list<sigc::connection>::iterator iter= _blockable_listeners.begin();
       iter != _blockable_listeners.end(); ++iter)
    iter->block();
}


void WBComponentPhysical::unblock_model_notifications() 
{
  for (std::list<sigc::connection>::iterator iter= _blockable_listeners.begin();
       iter != _blockable_listeners.end(); ++iter)
    iter->unblock();
}

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



grt::ListRef<app_ToolbarItem> WBComponentPhysical::get_toolbar_items(const std::string &toolbar)
{
  if (toolbar == WB_TOOLBAR_OPTIONS)
  {
    ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());

    if (form && _toolbars.find(toolbar+"/"+form->get_tool()) != _toolbars.end())
      return _toolbars[toolbar+"/"+form->get_tool()]->items();
  }
  else
  {
    return _toolbars[toolbar]->items();
  }
  return grt::ListRef<app_ToolbarItem>();
}


grt::ListRef<app_ShortcutItem> WBComponentPhysical::get_shortcut_items()
{
  return _shortcuts;
}


std::vector<std::string> WBComponentPhysical::get_command_dropdown_items(const std::string &option)
{
  std::vector<std::string> items;
  ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());

  if (has_prefix(option, "workbench.physical."))
  {
    if (has_suffix(option, ":Color"))
    {
      std::string colors= _wb->get_wb_options().get_string("workbench.model.ObjectFigure:ColorList");
      std::vector<std::string> colorList;

      colorList= split_string(colors, "\n");

      if (!colorList.empty())
      {
        for (size_t c= colorList.size(), i= 0; i < c; i++)
        {
          if (!colorList[i].empty() && colorList[i][0]=='#')
            items.push_back(colorList[i]);
        }
      }
      else
      {
        items.push_back("#98BFDA");
        items.push_back("#FEDE58");
        items.push_back("#98D8A5");

        items.push_back("#FE9898");
        items.push_back("#FE98FE");

        items.push_back("#FFFFFF");
      }

      std::string selected= form->get_tool_argument(option);
      if (selected.empty())
        selected= _wb->get_wb_options().get_string(option);
      if (selected.empty())
        selected= items[0];
      if (!selected.empty() && std::find(items.begin(), items.end(), selected) == items.end())
        items.push_back(selected);

      form->set_tool_argument(option, selected);
    }
    else if (has_suffix(option, ":Schema"))
    {
      workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));

      if (model.is_valid())
      {
        for (size_t c= model->catalog()->schemata().count(), i= 0; i < c; i++)
          items.push_back(*model->catalog()->schemata().get(i)->name());
        std::sort(items.begin(), items.end());
      }

      std::string selected= form->get_tool_argument(option);
      if (!items.empty() && (selected.empty() || std::find(items.begin(), items.end(), selected) == items.end()))
        selected= items[0];

      form->set_tool_argument(option, selected);
    }
    else if (has_suffix(option, ":Engine"))
    {
      items.push_back("*Default Engine*");

      Module *module= get_grt()->get_module("DbMySQL");
      if (module)
      {
        grt::ListRef<db_mysql_StorageEngine> engines_ret(grt::ListRef<db_mysql_StorageEngine>::cast_from(module->call_function("getKnownEngines", grt::BaseListRef(get_grt()))));

        for (size_t c= engines_ret.count(), i= 0; i < c; i++)
          items.push_back(engines_ret[i]->name());
      }
      
      // Table engine might be model specific.
      workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));
      std::string selected;

      _wb->get_ui()->get_wb_options_value(model.id(), "db.mysql.Table:tableEngine", selected);
      if (selected.empty())
        selected= "*Default Engine*";

      form->set_tool_argument(option, selected);
    }
    else if (has_suffix(option, ":Collation"))
    {
      workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));

      if (_collation_list.empty())
      {
        items.push_back("*Default Collation*");
        if (model.is_valid())
        {
          for (size_t c= model->catalog()->characterSets().count(), i= 0; i < c; i++)
          {
            db_CharacterSetRef charset(model->catalog()->characterSets().get(i));

            for (size_t d= charset->collations().count(), j= 0; j < d; j++)
              items.push_back(*charset->collations().get(j));
          }
          std::sort(items.begin(), items.end());
        }
        _collation_list= items;
      }
      else
        items= _collation_list;

      std::string selected= form->get_tool_argument(option);
      if (selected.empty())
        selected= _wb->get_wb_options().get_string(option);
      if (selected.empty())
        selected= "*Default Collation*";

      form->set_tool_argument(option, selected);
    }
  }
  else
    throw std::logic_error("Unknown option "+option);

  return items;
}



//--------------------------------------------------------------------------------
// Object Listeners


void WBComponentPhysical::add_schema_listeners(const db_SchemaRef &schema)
{
  // listener for changes in schema itself
  _object_listeners[schema.id()]=
    schema->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &WBComponentPhysical::schema_member_changed), schema));

  _schema_content_listeners[schema.id()]=
    schema->signal_refreshDisplay().connect(sigc::mem_fun(this, &WBComponentPhysical::schema_content_object_changed));
  
  // for changes in table, view, SP/function, routine (and other) lists 
  _schema_list_listeners[schema.id()]=
    schema->signal_list_changed().connect(sigc::bind<db_SchemaRef>(sigc::mem_fun(this, &WBComponentPhysical::schema_object_list_changed), schema));
}


/** Resets various listeners and refreshes internal state for a document change
 * 
 * This will remove old signal handlers and add new ones for a newly created
 * or opened document.
 * 
 */ 
void WBComponentPhysical::reset_document()
{
  _wb->get_document()->physicalModels().get(0)->get_data()->set_delegate(_wb);


  // reset listeners
  _catalog_object_list_listener.disconnect();
  _model_list_listener.disconnect();

  // remove old listeners
  for (std::map<std::string, sigc::connection>::iterator iter= _object_listeners.begin();
       iter != _object_listeners.end(); ++iter)
    iter->second.disconnect();
  _object_listeners.clear();

  for (std::map<std::string, sigc::connection>::iterator iter= _schema_list_listeners.begin();
       iter != _schema_list_listeners.end(); ++iter)
    iter->second.disconnect();
  _schema_list_listeners.clear();

  // remove old view listeners
  for (std::map<std::string, sigc::connection>::iterator iter= _figure_list_listeners.begin();
    iter != _figure_list_listeners.end(); ++iter)
    iter->second.disconnect();
  _figure_list_listeners.clear();

  workbench_DocumentRef doc(_wb->get_document());

  // add listener to all schemas, views etc
  for (size_t c= doc->physicalModels().count(), i= 0; i < c; i++)
  {
    workbench_physical_ModelRef model(doc->physicalModels()[i]);
    if (model.is_valid())
    {
      db_CatalogRef catalog(model->catalog());

      if (catalog.is_valid())
        _catalog_object_list_listener= 
        catalog->signal_list_changed().connect(sigc::bind<db_CatalogRef>(sigc::mem_fun(this, &WBComponentPhysical::catalog_object_list_changed), catalog));

      for (size_t sc= catalog->schemata().count(), si= 0; si < sc; si++)
      {
        db_SchemaRef schema(catalog->schemata().get(si));

        add_schema_listeners(schema);

        // currently there are only listeners for tables
        grt::ListRef<db_Table> tables(schema->tables());
        for (size_t tc= tables.count(), ti= 0; ti < tc; ti++)
        {
          add_schema_object_listeners(tables[ti]);
        }
      }

      for (size_t vi= 0, vc= model->diagrams().count(); vi < vc; vi++)
      {
        model_DiagramRef view(model->diagrams().get(i));
        _figure_list_listeners[view.id()]=
          view->signal_list_changed().connect(
              sigc::bind<model_DiagramRef>(sigc::mem_fun(this, &WBComponentPhysical::view_object_list_changed), view));
      }

      _model_list_listener= model->signal_list_changed().
        connect(sigc::mem_fun(this, &WBComponentPhysical::model_object_list_changed));
    }
  }


  // make sure frontend is refreshed
  _wb->request_refresh(RefreshSchemaList, "");

  ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
  ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
}


/** Called when a document is loaded
 
 - Update the userDatatypes list in the document with what we have now
 */
void WBComponentPhysical::document_loaded()
{
  grt::ListRef<workbench_physical_Model> models(_wb->get_document()->physicalModels());
  
  for (grt::ListRef<workbench_physical_Model>::const_iterator pmodel= models.begin();
       pmodel != models.end(); ++pmodel)
  {
    grt::ListRef<db_UserDatatype> userTypes(create_builtin_user_datatypes((*pmodel)->catalog(), (*pmodel)->rdbms()));
    
    // merge in built-in UDTs with the one from the model, replacing stuff from the model with ours
    grt::merge_contents_by_id((*pmodel)->catalog()->userDatatypes(), userTypes, true);
  }
}


/** Listener for changes in the list of schemas
 *
 * Used for attaching listeners to new schemas and content lists.
 */
void WBComponentPhysical::catalog_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value,
                                                      const db_CatalogRef &catalog)
{
  if (grt::BaseListRef(list) == catalog->schemata())
  {
    // we're called in the GRT thread, so just mark the refresh request
    // as pending. This has the bonus that multiple requests will be
    // collapsed into 1.
    // The requests are actually sent in flush_idle_tasks()

    _wb->request_refresh(RefreshSchemaList, "", 0);

    ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_schema_list();
    
    if (added)
    {
      // added new schema
      add_schema_listeners(db_SchemaRef::cast_from(value));
    }
    else
    {
      db_SchemaRef schema(db_SchemaRef::cast_from(value));
      
      _wb->request_refresh(RefreshCloseEditor, schema.id());
      
      // remove listeners for the schema
      _object_listeners[schema.id()].disconnect();
      _schema_list_listeners[schema.id()].disconnect();
      
      _object_listeners.erase(schema.id());
      _schema_list_listeners.erase(schema.id());
    }
  }
  else
    privilege_list_changed(list, added, value, catalog);
}



void WBComponentPhysical::schema_member_changed(const std::string &member, const grt::ValueRef &ovalue,
                                                const db_SchemaRef &schema)
{
  _wb->request_refresh(RefreshSchema, "", 0);

  ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_for_schema(schema, true);
}


void WBComponentPhysical::add_schema_object_listeners(const grt::ObjectRef &object)
{
  if (object.is_instance(db_Table::static_class_name()))
    _object_listeners[object.id()]= db_TableRef::cast_from(object)->signal_foreignKeyChanged().connect(sigc::mem_fun(this, &WBComponentPhysical::foreign_key_changed));
}


void WBComponentPhysical::schema_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, const db_SchemaRef &schema)
{
  grt::ObjectRef object(grt::ObjectRef::cast_from(value));

  if (added)
  {
    add_schema_object_listeners(object);
  }
  else
  {
    // remove old listeners
    if (object.is_instance(db_Table::static_class_name()))
    {
      _object_listeners[object.id()].disconnect();
      _object_listeners.erase(object.id());
    }
    _wb->request_refresh(RefreshCloseEditor, object.id());
  }

  ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_for_schema_object(GrtObjectRef::cast_from(value), false);

  _wb->request_refresh(RefreshSchema, "", 0);
}


void WBComponentPhysical::view_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, const model_DiagramRef &view)
{
  if (list == view->figures().valueptr())
  {
    if (handles_figure(model_ObjectRef::cast_from(value)))
    {
      if (added)
      {
#if 0
        // if not undoing/redoing, then auto-create the connection for the table
        if (!get_grt()->get_undo_manager()->is_undoing() && !get_grt()->get_undo_manager()->is_redoing())
        {
          if (value.is_instance(workbench_physical_TableFigure::static_class_name()))
          {
            workbench_physical_TableFigureRef table(workbench_physical_TableFigureRef::cast_from(value));
            
            if (table.table().is_valid())
            {
              std::vector<db_TableRef> tbls;
              tbls.push_back(table.table());
              
              update_connections_for_new_tables(view, tbls);
            }
            else
              get_grt()->send_warning("Table figure added to view has no table db object bound to it.");
          }
        }
#endif
      }
      else
      {
      }
      
      // ask for a refresh to update the "present in this view" status in Catalog tree
      if (_catalog_tree != NULL)
        _catalog_tree->refresh_for_diagram(workbench_physical_DiagramRef::cast_from(view));
    }
    else
    {
      // make sure editor for notes and images are closed
      // this should be in wb_component_basic.cpp, but since there's no need for anything
      // else over there, just put here
      if (!added)
        _wb->request_refresh(RefreshCloseEditor, grt::ObjectRef::cast_from(value).id());
    }
  }
}


static void convert_utf16_to_utf8_if_needed(char *&data, size_t &size)
{
  gchar *output;
  glong iread, iwritten;

  // check if utf16
  if (size >= 2 && (guchar)data[0] == 0xff && (guchar)data[1] == 0xfe)
  {
    output= g_utf16_to_utf8((const gunichar2*)data, (glong) size, &iread, &iwritten, NULL);
    if (output)
    {
      g_free(data);
      data= output;
      size= iwritten;
    }
  }
}



void WBComponentPhysical::model_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &avalue)
{
  if (avalue.type() == grt::ObjectType)
  {
    if (added)
    {
      grt::ObjectRef value(grt::ObjectRef::cast_from(avalue));
      std::string group, tmpl;
      
      if (value.is_instance(db_Script::static_class_name()))
      {
        group= "@sqlscripts";
        tmpl= "script$.sql";
      }
      else if (value.is_instance(GrtStoredNote::static_class_name()))
      {
        group= "@notes";
        tmpl= "note$.txt";
      }
      else if (value.is_instance(model_Diagram::static_class_name()))
      {
        model_DiagramRef view(model_DiagramRef::cast_from(value));
        
        _figure_list_listeners[view.id()]=
        view->signal_list_changed().connect(
                                            sigc::bind<model_DiagramRef>(sigc::mem_fun(this, &WBComponentPhysical::view_object_list_changed), view));
        
        return;
      }
      else
        return;
      
      // check if the filename is in the temp dir, if so, just readd it with old contents
      GrtStoredNoteRef object(GrtStoredNoteRef::cast_from(value));
      
      
#if 0
      if (object->filename() != "")
      {
        if (object->filename().c_str()[0] == '@')
        {
          gchar *data;
          gsize length;
          std::string path= make_path(_wb->get_grt_manager()->get_tmp_dir(), object->filename());
          
          if (g_file_get_contents(path.c_str(), &data, &length, NULL))
          {
            // we're undoing, load the file and add it back to the model file
            object->filename(_wb->recreate_attached_file(object->filename(), data));
            g_free(data);
          }
          else
            get_grt()->send_error(strfmt(_("Error undoing file delete: can't read contents of temporary file '%s'"), path.c_str()));
        }
        else
        {
          // load file from scratch
          gchar *data;
          gsize length;
          GError *error= NULL;
          
          if (g_file_get_contents(object->filename().c_str(), &data, &length, &error))
          {
            convert_utf16_to_utf8_if_needed(data, length);
            
            object->filename(_wb->create_attached_file(group, object->filename()));
            _wb->save_attached_file_contents(object->filename(), data, length);
            g_free(data);
          }
          else
          {
            get_grt()->send_error(strfmt(_("Error adding file: can't read contents of file '%s': %s"), 
                                      object->filename().c_str(), error->message));
            g_error_free(error);
          }
        }
      }
      else
        object->filename(_wb->create_attached_file(group, tmpl));
#endif
      // request refresh of overview
      if (value.is_instance(db_Script::static_class_name()))
        ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
      else if (value.is_instance(GrtStoredNote::static_class_name()))
        ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
    }
    else // !added
    {
      grt::ObjectRef value(grt::ObjectRef::cast_from(avalue));
      
      _wb->request_refresh(RefreshCloseEditor, value.id());
      
      if (value.is_instance(GrtStoredNote::static_class_name()))
      {
        GrtStoredNoteRef object(GrtStoredNoteRef::cast_from(value));
#if 0
        if (object->filename() != "")
        {
          std::string path= make_path(_wb->get_grt_manager()->get_tmp_dir(), object->filename());
          gchar *tmp= g_dirname(path.c_str());
          g_mkdir_with_parents(tmp, 0700);
          g_free(tmp);
          
          // save the file in the temp dir in case an undo delete is requested
          std::string data= _wb->get_attached_file_contents(object->filename());
          
          if (!g_file_set_contents(path.c_str(), data.data(), (gssize) data.length(), NULL))
          {
            get_grt()->send_error(strfmt(_("Could not save temporary file '%s'"), path.c_str()));
          }
          
          _wb->delete_attached_file(object->filename());
        }
#endif
        // request refresh of overview
        if (value.is_instance(db_Script::static_class_name()))
          ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
        else
          ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
      }
      else if (value.is_instance(model_Diagram::static_class_name()))
      {
        std::string id= grt::ObjectRef::cast_from(value).id();
        
        _figure_list_listeners[id].disconnect();
        
        _figure_list_listeners.erase(id);
      }
    }
  }
}


void WBComponentPhysical::foreign_key_changed(const db_ForeignKeyRef &fk)
{
  // don't auto-create/remove stuff when undoing/redoing
  if (get_grt()->get_undo_manager()->is_undoing() || get_grt()->get_undo_manager()->is_redoing())
    return;
  
  bool valid= fk->checkCompleteness() != 0;
  grt::ListRef<workbench_physical_Diagram> views(_wb->get_document()->physicalModels()[0]->diagrams());
  
  // go through all views and create/destroy connections if needed
  for (grt::ListRef<workbench_physical_Diagram>::const_iterator iter= views.begin();
       iter != views.end(); ++iter)
  {
    workbench_physical_DiagramRef view(*iter);
    workbench_physical_ConnectionRef conn(view->getConnectionForForeignKey(fk));

    if (conn.is_valid() != valid)
    {
      if (!conn.is_valid())
        view->createConnectionForForeignKey(fk);
      else
        view->removeConnection(conn);
    }
  }
}


void WBComponentPhysical::schema_content_object_changed(const db_DatabaseObjectRef &object)
{
  refresh_ui_for_object(object);
}


void WBComponentPhysical::refresh_ui_for_object(const GrtObjectRef &object)
{
  if (object.is_valid() && object->owner().is_valid())
  {
    workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(object));
    PhysicalOverviewBE *overview= (PhysicalOverviewBE*)((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview());

    if (overview->get_model() != model)
      throw std::logic_error("code is outdated");

    overview->send_refresh_for_schema_object(object, true);

    _wb->request_refresh(RefreshSchema, "" /*object.owner().id() not supported yet in fe*/, 0);
  }
}


/** 
 ****************************************************************************
 * @brief Add/remove connections for added/removed table foreign keys
 *
 * Updates the connections from a table figure corresponding to the given
 * foreign key. This can be called when the list of FKs for a table is 
 * changed or when a pre-existing table with a fk is added to a view.
 * It will also update existing connections in case the referenced table
 * of a fk changes.
 * 
 * @param object the db object
 * @param figure the model figure that was assigned to the object
 * @param attach whether the figure was attached or detached to the object 
 *
 * @return true if a change has happened (conn added/remove), false otherwise
 ****************************************************************************
 */
bool WBComponentPhysical::update_table_fk_connection(const db_TableRef &table, const db_ForeignKeyRef &fk, bool added)
{
  workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(table));

  if (!model.is_valid() || !model->diagrams().is_valid())
    return false;

  if (!fk.is_valid())
  {
    if (!added)
    {
      // all FKs from table removed
    }
  }
  else
  {
    if (!fk->referencedTable().is_valid() || fk->owner() != table)
      return false;

    // kind of a hack, this is needed because copy_object() will generate
    // spurious notifications. eg: in a sync of a model with a relationship,
    // the rel will be recreated (because of the copy)
    if (added && table->foreignKeys().get_index(fk) == BaseListRef::npos)
      return false;
    
    grt::ListRef<workbench_physical_Diagram> views(model->diagrams());
    grt::GRT *grt= get_grt();

    if (added)
    {
      // we have to go through all views in the model and find all table figures
      // that correspond to the FK for creating the relationship in all these views

      for (size_t c= views.count(), i= 0; i < c; i++)
      {
        workbench_physical_DiagramRef view(views[i]);

        workbench_physical_TableFigureRef table1(workbench_physical_TableFigureRef::cast_from(view->getFigureForDBObject(table)));
        workbench_physical_TableFigureRef table2(workbench_physical_TableFigureRef::cast_from(view->getFigureForDBObject(fk->referencedTable())));

        if (table1.is_valid() && table2.is_valid())
        { // both tables in the relationship are in this view, so create the connection
           
          // but 1st check if it already exists

          grt::ListRef<model_Connection> connections(view->connections());
          workbench_physical_ConnectionRef found;

          for (size_t c= connections.count(), i= 0; i < c; i++)
          {
            model_ConnectionRef conn(connections[i]);
            if (conn.is_instance(workbench_physical_Connection::static_class_name()))
            {
              workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(conn));

              if (pconn->foreignKey() == fk)
              {
                found= pconn;
                break;
              }
            }
          }

          // connection doesnt exist yet, create it
          if (!found.is_valid())
          {
            workbench_physical_ConnectionRef conn(grt);
            
            conn->owner(view);
            conn->startFigure(table1);
            conn->endFigure(table2);
            conn->caption(fk->name());
            conn->foreignKey(fk);

            conn->name(grt::get_name_suggestion_for_list_object(view->connections(),
                      "connection"));

            grt::AutoUndo undo(get_grt());
            view->connections().insert(conn);
            undo.end(_("Create Relationship"));

            return true;
          }
          else
          {
            // connection exists, check if its correct
            if (workbench_physical_TableFigureRef::cast_from(found->endFigure())->table() !=
              found->foreignKey()->referencedTable())
            {
              if (!found->foreignKey()->referencedTable().is_valid())
              {
                // referencedTable was unset, so we need to remove the connection
                
              }
              else
              {
                // update the connection with the new end figure
                grt::AutoUndo undo(get_grt());
                found->endFigure(view->getFigureForDBObject(fk->referencedTable()));
                undo.end(_("Update Relationship"));
              }
              return true;
            }
          }
        }
      }
    }
    else
    {
      // remove all connections that correspond to the FK in all views

      for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= views.begin(); 
           view != views.end(); ++view)
      {
        workbench_physical_TableFigureRef table1(workbench_physical_TableFigureRef::cast_from((*view)->getFigureForDBObject(table)));
        grt::ListRef<model_Connection>::const_reverse_iterator end= (*view)->connections().rend();
        
        for (grt::ListRef<model_Connection>::const_reverse_iterator conn= (*view)->connections().rbegin(); 
             conn != end; ++conn)
        {
          if ((*conn).is_instance(workbench_physical_Connection::static_class_name()))
          {
            workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(*conn));

            if (pconn->foreignKey() == fk)
            {
              grt::AutoUndo undo(get_grt());
              (*view)->connections().remove_value(*conn);
              undo.end(_("Remove Relationship"));

              return true;
            }
          }
        }
      }
    }
  }
  return false;
}


void WBComponentPhysical::activate_catalog_tree_item(const grt::ValueRef &value)
{
  if (value.is_valid() && db_DatabaseObjectRef::can_wrap(value))
  {
    db_DatabaseObjectRef object(db_DatabaseObjectRef::cast_from(value));

    _wb->open_object_editor(object);
  }
}


bool WBComponentPhysical::has_figure_for_object_in_active_view(const GrtObjectRef &object, ModelDiagramForm *vform)
{
  if (!vform)
    vform= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());

  if (vform)
  {
    workbench_physical_DiagramRef view(workbench_physical_DiagramRef::cast_from(vform->get_model_diagram()));

    if (view->getFigureForDBObject(db_DatabaseObjectRef::cast_from(object)).is_valid())
      return true;
  }
  return false;
}


void WBComponentPhysical::update_catalog_tree()
{  
  if (!_catalog_tree) return;

  UIForm *form= _wb->get_active_main_form();
  model_ModelRef model;
  workbench_physical_DiagramRef view;

  if (form)
  {
    ModelDiagramForm *vform= dynamic_cast<ModelDiagramForm*>(form);
    if (vform)
    {
      view= workbench_physical_DiagramRef::cast_from(vform->get_model_diagram());
      model= view->owner();
    }
    else
    {
      OverviewBE *over= dynamic_cast<OverviewBE*>(form);
      if (over)
        model= over->get_model();
    }
  }

  _catalog_tree->refresh_for_diagram(view);

  if (model.is_valid())
    _catalog_tree->set_displayed_value(workbench_physical_ModelRef::cast_from(model)->catalog()->schemata(), "");
  else
    _catalog_tree->set_displayed_global_value("", true);
  
  _wb->request_refresh(RefreshSchemaList, "", 0);
}



bec::ValueTreeBE *WBComponentPhysical::get_catalog_tree()
{
  if (!_catalog_tree)
  {
    _catalog_tree= new CatalogTreeBE(get_grt(), this);
    _catalog_tree->set_activate_callback(sigc::mem_fun(this, &WBComponentPhysical::activate_catalog_tree_item));

    _wb->get_ui()->signal_main_form_change().connect(sigc::hide(sigc::mem_fun(this, &WBComponentPhysical::update_catalog_tree)));

    update_catalog_tree();
  }

  return _catalog_tree;
}


void WBComponentPhysical::setup_canvas_tool(ModelDiagramForm *view, const std::string &tool)
{
  void *data= 0;
  bool relationship= false;

  if (tool == WB_TOOL_PTABLE)
  {
    view->set_cursor("table");
    _wb->show_status_text(_("Select location for new table."));
  }
  else if (tool == WB_TOOL_PVIEW)
  {
    view->set_cursor("view");
    _wb->show_status_text(_("Select location for new view."));
  }
  else if (tool == WB_TOOL_PROUTINEGROUP)
  {
    view->set_cursor("routine");
    _wb->show_status_text(_("Select location for new routine group."));
  }
  else if (tool == WB_TOOL_PREL11)
  {
    view->set_cursor("rel11");
    data= start_relationship(view, mdc::Point(), Relationship11Id);
    relationship= true;
  }
  else if (tool == WB_TOOL_PREL1n)
  {
    view->set_cursor("rel1n");
    data= start_relationship(view, mdc::Point(), Relationship1nId);
    relationship= true;
  }
  else if (tool == WB_TOOL_PRELnm)
  {
    view->set_cursor("relnm");
    data= start_relationship(view, mdc::Point(), RelationshipnmId);
    relationship= true;
  }
  else if (tool == WB_TOOL_PREL11_NOID)
  {
    view->set_cursor("rel11");
    data= start_relationship(view, mdc::Point(), Relationship11NonId);
    relationship= true;
  }
  else if (tool == WB_TOOL_PREL1n_NOID)
  {
    view->set_cursor("rel1n");
    data= start_relationship(view, mdc::Point(), Relationship1nNonId);
    relationship= true;
  }
  else if (tool == WB_TOOL_PREL_PICK)
  {
    view->set_cursor("rel1n");
    data= start_relationship(view, mdc::Point(), RelationshipPick);
    relationship= true;
  }
  else
  {
    _wb->show_status_text("Invalid tool "+tool);
    return;
  }
  view->set_button_callback(sigc::bind<void*>(sigc::mem_fun(this, &WBComponentPhysical::handle_button_event), data));
  if (relationship)
    view->set_reset_tool_callback(
      sigc::bind<RelationshipToolContext*>(sigc::mem_fun(this, &WBComponentPhysical::cancel_relationship), 
        reinterpret_cast<RelationshipToolContext*>(data)));
}



bool WBComponentPhysical::handle_button_event(ModelDiagramForm *view, mdc::MouseButton button, bool press, 
                                      mdc::Point pos, mdc::EventState, void *data)
{
  std::string tool= view->get_tool();

  if (button != mdc::ButtonLeft)
    return false;

  mdc::CanvasItem *item;
  item= view->get_view()->get_item_at(pos);
  if (item && item->get_layer() != view->get_view()->get_current_layer())
    return false;

  if (tool == WB_TOOL_PTABLE)
  {
    if (press)
    {
      place_new_db_object(view, pos, ObjectTable);
      view->reset_tool(true);
      return true;
    }
  }
  else if (tool == WB_TOOL_PROUTINEGROUP)
  {
    if (press)
    {
      place_new_db_object(view, pos, ObjectRoutineGroup);
      view->reset_tool(true);
      return true;
    }
  }
  else if (tool == WB_TOOL_PVIEW)
  {
    if (press)
    {
      place_new_db_object(view, pos, ObjectView);
      view->reset_tool(true);
      return true;
    }
  }
  else if (tool == WB_TOOL_PREL11 || tool == WB_TOOL_PREL1n || tool == WB_TOOL_PRELnm ||
    tool == WB_TOOL_PREL11_NOID || tool == WB_TOOL_PREL1n_NOID || tool == WB_TOOL_PREL_PICK)
  {
    if (press)
    {
      RelationshipToolContext *rctx= reinterpret_cast<RelationshipToolContext*>(data);

      if (rctx->button_press(view, pos))
        view->reset_tool(true);
      return true;
    }
  }

  return false;
}


//===================================================================================================
// Privilege Stuff
//===================================================================================================

static void refresh_privileges(PhysicalOverviewBE *overview)
{
  overview->send_refresh_users();
  overview->send_refresh_roles();
}

void WBComponentPhysical::privilege_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value,
                                                 const db_CatalogRef &catalog)
{
  if (grt::BaseListRef(list) == catalog->users())
    ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_users();
  else if (grt::BaseListRef(list) == catalog->roles())
    ((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_roles();
  if (!added)
  {
    grt::ObjectRef object(grt::ObjectRef::cast_from(value));
    _wb->request_refresh(RefreshCloseEditor, object.id());
  }
}

//--------------------------------------------------------------------------------
// Privilege Management

grt::ValueRef WBComponentPhysical::add_new_user_grt(grt::GRT *grt, const workbench_physical_ModelRef &model)
{
  db_CatalogRef catalog;
  db_UserRef user;

  catalog= model->catalog();

  std::string name= grt::get_name_suggestion_for_list_object(grt::ObjectListRef::cast_from(catalog->users()), "user");

  user= db_UserRef(grt);
  user->owner(catalog);
  user->name(name);

  grt::AutoUndo undo(grt);
  catalog->users().insert(user);

  undo.end(strfmt(_("Create User '%s'"), user->name().c_str()));

  return user;
}


void WBComponentPhysical::add_new_user(const workbench_physical_ModelRef &model)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new user",
                        sigc::bind<workbench_physical_ModelRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_user_grt),
                                                            model));
  if (res.is_valid())
  {
    db_UserRef user(db_UserRef::cast_from(res));

    _wb->show_status_text(strfmt(_("User '%s' created"), user->name().c_str()));

    _wb->open_object_editor(GrtObjectRef::cast_from(user));
  }
  else
    _wb->show_status_text(_("Could not create new user"));
}


void WBComponentPhysical::remove_user(const db_UserRef &user)
{
  db_CatalogRef catalog(db_CatalogRef::cast_from(user->owner()));

  grt::AutoUndo undo(get_grt());
  catalog->users().remove_value(user);
  undo.end(strfmt(_("Remove User '%s'"), user->name().c_str()));

  _wb->show_status_text(strfmt(_("Removed user '%s'"), user->name().c_str()));
}


grt::ValueRef WBComponentPhysical::add_new_role_grt(grt::GRT *grt, const workbench_physical_ModelRef &model)
{
  db_CatalogRef catalog;
  db_RoleRef role;

  catalog= model->catalog();

  std::string name= grt::get_name_suggestion_for_list_object(grt::ObjectListRef::cast_from(catalog->roles()), "role");

  role= db_RoleRef(grt);
  role->owner(catalog);
  role->name(name);

  grt::AutoUndo undo(get_grt());
  catalog->roles().insert(role);
  undo.end(strfmt(_("Create Role '%s'"), role->name().c_str()));

  return role;
}


void WBComponentPhysical::add_new_role(const workbench_physical_ModelRef &model)
{
  grt::ValueRef res;

  res= _wb->execute_in_grt_thread("Create new role",
                        sigc::bind<workbench_physical_ModelRef>(sigc::mem_fun(this, &WBComponentPhysical::add_new_role_grt),
                                                            model));
  if (res.is_valid())
  {
    db_RoleRef role(db_RoleRef::cast_from(res));

    _wb->show_status_text(strfmt(_("Role '%s' created"), role->name().c_str()));

    _wb->open_object_editor(role);
  }
  else
    _wb->show_status_text(_("Could not create new role"));
}


void WBComponentPhysical::remove_role(const db_RoleRef &role)
{
  db_CatalogRef catalog(db_CatalogRef::cast_from(role->owner()));

  grt::AutoUndo undo(get_grt());
  catalog->roles().remove_value(role);
  undo.end(strfmt(_("Remove Role '%s'"), role->name().c_str()));

  _wb->show_status_text(strfmt(_("Removed role '%s'"), role->name().c_str()));
}


void WBComponentPhysical::remove_references_to_object(const db_DatabaseObjectRef &object)
{
  // this is called when a object is removed from the schema.

  // remove all role privileges assigned to the object
  db_CatalogRef catalog(_wb->get_parent_for_object<db_Catalog>(object));
  
  grt::ListRef<db_Role> roles(catalog->roles());
  {
    grt::AutoUndo undo(get_grt());
    
    for (size_t c= roles.count(), i= 0; i < c; i++)
    {
      db_RoleRef role(roles[i]);
      
      grt::ListRef<db_RolePrivilege> privs(role->privileges());
      for (grt::ListRef<db_RolePrivilege>::const_reverse_iterator priv= privs.rbegin(); 
           priv != privs.rend(); ++priv)
      {
        if ((*priv)->databaseObject() == object)
        {
          privs.remove_value(*priv);
        }
      }
    }
    undo.end_or_cancel_if_empty(_("Remove Object Privileges"));
  }
  
  workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(object));
  // remove any tags that reference this object
  if (model.is_valid())
  {
    grt::AutoUndo undo(get_grt());
    
    for (grt::ListRef<meta_Tag>::const_iterator end= model->tags().end(), tag= model->tags().begin();
         tag != end; ++tag)
    {
      	for (grt::ListRef<meta_TaggedObject>::const_reverse_iterator rend= (*tag)->objects().rend(), to= (*tag)->objects().rbegin();
             to != rend; ++to)
	{
          if ((*to)->object() == object)
          {
            (*tag)->objects().remove_value(*to);
          }
	}
    }
  
    undo.end_or_cancel_if_empty(_("Remove Tags for Object"));
  }
}

