2
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; version 2 of the
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22
#include "wb_config.h"
24
#include "wb_component_physical.h"
25
#include "workbench/wb_find_dialog.h"
26
#include "wb_model_diagram_form.h"
27
#include "wb_catalog_tree.h"
28
#include "workbench/wb_context_ui.h"
29
#include "wb_overview_physical.h"
30
#include "base/wb_iterators.h"
32
#include "grts/structs.workbench.physical.h"
33
#include "grts/structs.db.mgmt.h"
34
#include "grts/structs.db.mysql.h"
36
#include "grtdb/db_helpers.h"
37
#include "grtdb/db_object_helpers.h"
38
#include "grtui/db_conn_be.h"
42
#include "wbcanvas/workbench_physical_model_impl.h"
43
#include "wbcanvas/workbench_physical_diagram_impl.h"
44
#include "wbcanvas/workbench_physical_tablefigure_impl.h"
45
#include "wbcanvas/workbench_physical_viewfigure_impl.h"
46
#include "wbcanvas/workbench_physical_routinegroupfigure_impl.h"
47
#include "wbcanvas/workbench_physical_connection_impl.h"
49
#include "base/string_utilities.h"
51
#define FILE_CONNECTION_LIST "connections.xml"
57
using namespace MySQL::Geometry;
58
using namespace MySQL::Drawing;
60
WBComponentPhysical::WBComponentPhysical(WBContext *wb)
64
base::NotificationCenter::get()->add_observer(this, "GNMainFormChanged");
68
WBComponentPhysical::~WBComponentPhysical()
70
base::NotificationCenter::get()->remove_observer(this);
76
void WBComponentPhysical::load_app_options(bool update)
78
grt::GRT *grt= get_grt();
82
app_ToolbarRef toolbar;
83
toolbar= app_ToolbarRef::cast_from(
84
grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_table.xml")));
85
_toolbars[toolbar->name()]= toolbar;
87
toolbar= app_ToolbarRef::cast_from(
88
grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_view.xml")));
89
_toolbars[toolbar->name()]= toolbar;
91
toolbar= app_ToolbarRef::cast_from(
92
grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_routinegroup.xml")));
93
_toolbars[toolbar->name()]= toolbar;
95
toolbar= app_ToolbarRef::cast_from(
96
grt->unserialize(make_path(_wb->get_datadir(),"data/model_option_toolbar_physical_relationship.xml")));
97
_toolbars["main/"WB_TOOL_PREL11_NOID]= toolbar;
98
_toolbars["main/"WB_TOOL_PREL1n_NOID]= toolbar;
99
_toolbars["main/"WB_TOOL_PREL11]= toolbar;
100
_toolbars["main/"WB_TOOL_PREL1n]= toolbar;
101
_toolbars["main/"WB_TOOL_PRELnm]= toolbar;
102
_toolbars["main/"WB_TOOL_PREL_PICK]= toolbar;
104
_shortcuts= grt::ListRef<app_ShortcutItem>::cast_from(
105
grt->unserialize(make_path(_wb->get_datadir(),"data/shortcuts_physical.xml")));
108
// this needs to be loaded after drivers list has been loaded
109
db_mgmt_ManagementRef mgmt= _wb->get_root()->rdbmsMgmt();
110
std::string conn_list_xml= make_path(_wb->get_user_datadir(), FILE_CONNECTION_LIST);
111
if (g_file_test(conn_list_xml.c_str(), G_FILE_TEST_EXISTS))
115
grt::ListRef<db_mgmt_Connection>
116
list(grt::ListRef<db_mgmt_Connection>::cast_from(grt->unserialize(conn_list_xml)));
120
bool changed = false;
121
while (mgmt->storedConns().count() > 0)
122
mgmt->storedConns().remove(0);
123
for (size_t c= list.count(), i= 0; i < c; i++)
125
db_mgmt_ConnectionRef conn(list.get(i));
127
// starting from 5.2.16 we do not store passwords for MySQL or SSH in the connections file anymore
128
// so we strip the password, update the hostIdentifier field and store the password in the keychain
129
if (*conn->hostIdentifier() == "")
131
conn->hostIdentifier(bec::get_host_identifier_for_connection(conn));
133
// save the MySQL password if its set
134
if (conn->parameterValues().get_string("password") != "")
138
mforms::Utilities::store_password(conn->hostIdentifier(),
139
conn->parameterValues().get_string("userName"),
140
conn->parameterValues().get_string("password"));
142
catch (std::exception &exc)
144
g_warning("Could not store password for %s: %s", conn->hostIdentifier().c_str(), exc.what());
146
conn->parameterValues().gset("password", "");
150
// save the SSH tunnel password if its set
151
if (conn->parameterValues().get_string("sshPassword") != "")
153
if (conn->parameterValues().get_string("sshHost") != "")
155
std::string service = strfmt("ssh@%s", conn->parameterValues().get_string("sshHost").c_str());
158
mforms::Utilities::store_password(service,
159
conn->parameterValues().get_string("sshUserName"),
160
conn->parameterValues().get_string("sshPassword"));
162
catch (std::exception &exc)
164
g_warning("Could not store password for %s: %s", service.c_str(), exc.what());
167
conn->parameterValues().gset("sshPassword", "");
172
mgmt->storedConns().insert(conn);
178
catch (std::exception &exc)
180
grt->send_warning(strfmt("Error loading '%s': %s", conn_list_xml.c_str(), exc.what()));
186
void WBComponentPhysical::save_app_options()
188
get_grt()->serialize(_wb->get_root()->rdbmsMgmt()->storedConns(),
189
make_path(_wb->get_user_datadir(), FILE_CONNECTION_LIST));
195
void WBComponentPhysical::setup_context_grt(grt::GRT *grt, WBOptions *options)
197
//XXX must be loaded from DB module
198
db_mgmt_RdbmsRef rdbms= db_mgmt_RdbmsRef::cast_from(grt->unserialize(make_path(options->basedir, "modules/data/mysql_rdbms_info.xml")));
200
_wb->get_root()->rdbmsMgmt()->rdbms().insert(rdbms);
203
// fill engine types list
204
grt::Module *module= get_grt()->get_module("DbMySQL");
207
grt::ListRef<db_mysql_StorageEngine> engines_ret(grt::ListRef<db_mysql_StorageEngine>::cast_from(module->call_function("getKnownEngines", grt::BaseListRef(get_grt()))));
209
for (size_t c= engines_ret.count(), i= 0; i < c; i++)
211
engines.append(",").append(engines_ret[i]->name());
213
engines= engines.substr(1);
214
// this is also used by WBA
215
_wb->get_wb_options().gset("@db.mysql.Table:tableEngine/Items", engines.c_str());
218
_wb->get_wb_options().gset("@db.ForeignKey:updateRule/Items", "NO ACTION,CASCADE,SET NULL,RESTRICT");
219
_wb->get_wb_options().gset("@db.ForeignKey:deleteRule/Items", "NO ACTION,CASCADE,SET NULL,RESTRICT");
221
// fill delete object options
222
_wb->get_wb_options().gset("@workbench.physical:DeleteObjectConfirmation/Items",
223
_("Delete Database Object from Catalog:delete,"
224
"Keep Database Object in Catalog:keep,"
229
void WBComponentPhysical::init_catalog_grt(const db_mgmt_RdbmsRef &rdbms,
230
const std::string &db_version,
231
workbench_physical_ModelRef &model)
233
grt::GRT *grt= get_grt();
235
std::string db_package= rdbms->databaseObjectPackage();
237
// assemble struct name for catalog and schema for the requested db type
239
std::string catalog_struct= db_package+".Catalog";
240
std::string schema_struct= db_package+".Schema";
242
if (!grt->get_metaclass(catalog_struct) ||
243
!grt->get_metaclass(schema_struct))
245
// Struct definition for '%s' and/or '%s' cannot be found
246
throw grt_runtime_error("Support for RDBMS "+db_package+" not found.",
247
"Struct definition for "+catalog_struct+" and/or "+schema_struct+" could not be found");
250
db_CatalogRef catalog(grt->create_object<db_Catalog>(catalog_struct));
252
catalog->name("default");
253
catalog->owner(model);
255
//catalog.oldName(catalog.name());
258
GrtVersionRef version(grt);
259
version->name("Version");
260
version->owner(catalog);
262
char **toks= g_strsplit(db_version.c_str(), ".", 0);
265
version->majorNumber(atoi(toks[0]));
267
version->minorNumber(atoi(toks[1]));
269
version->releaseNumber(atoi(toks[2]));
273
append_contents(catalog->simpleDatatypes(), rdbms->simpleDatatypes());
274
append_contents(catalog->characterSets(), rdbms->characterSets());
276
model->catalog(catalog);
278
replace_contents(catalog->userDatatypes(), create_builtin_user_datatypes(catalog, rdbms));
280
// add listener for any operation on the schema list
283
// add standard tag categories
285
GrtObjectRef category(grt);
287
category->name("Business Rule");
288
category->owner(model);
290
model->tagCategories().insert(category);
293
// create an initial schema
294
db_SchemaRef schema(grt->create_object<db_Schema>(schema_struct));
296
schema->name(StringRef("mydb"));
297
schema->owner(catalog);
298
schema->defaultCharacterSetName("latin1");
299
schema->defaultCollationName("latin1_swedish_ci");
301
//schema->oldName(schema->name());
303
catalog->schemata().insert(schema);
309
grt::ListRef<db_UserDatatype> WBComponentPhysical::create_builtin_user_datatypes(const db_CatalogRef &catalog,
310
const db_mgmt_RdbmsRef &rdbms)
312
grt::Module *module= get_grt()->get_module("DbMySQL");
315
grt::BaseListRef args(get_grt());
317
grt::ListRef<db_UserDatatype> user_types(grt::ListRef<db_UserDatatype>::cast_from(module->call_function("getDefaultUserDatatypes", args)));
319
if (user_types.is_valid())
321
GRTLIST_FOREACH(db_UserDatatype, user_types, ut)
323
(*ut)->owner(catalog);
329
return grt::ListRef<db_UserDatatype>();
334
void WBComponentPhysical::setup_physical_model(grt::GRT *grt, workbench_DocumentRef &doc,
335
const std::string &rdbms_name, const std::string &rdbms_version)
337
// init physical model
338
workbench_physical_ModelRef pmodel(grt);
341
pmodel->connectionNotation(_wb->get_wb_options().get_string("DefaultConnectionNotation"));
342
pmodel->figureNotation(_wb->get_wb_options().get_string("DefaultFigureNotation"));
344
doc->physicalModels().insert(pmodel);
346
db_mgmt_ManagementRef mgmt= db_mgmt_ManagementRef::cast_from(grt->get("/wb/rdbmsMgmt"));
348
// find the rdbms module for the db we want
349
db_mgmt_RdbmsRef rdbms;
351
rdbms= grt::find_named_object_in_list<db_mgmt_Rdbms>(mgmt->rdbms(), rdbms_name);
352
if (!rdbms.is_valid())
354
throw grt_runtime_error("Could not locate RDBMS support object for "+rdbms_name,
355
"new_physical(): There is no RDBMS object with the name "+rdbms_name+" in the /rdbmsMgmt/rdbms list.");
357
pmodel->rdbms(rdbms);
359
init_catalog_grt(rdbms, rdbms_version, pmodel);
363
//--------------------------------------------------------------------------------
366
grt::ValueRef WBComponentPhysical::add_new_db_schema_grt(grt::GRT *grt,
367
const workbench_physical_ModelRef &model)
372
std::string class_name;
374
grt::AutoUndo undo(grt);
376
class_name= *model->rdbms()->databaseObjectPackage()+".Schema";
378
name= grt::get_name_suggestion_for_list_object(
379
grt::ObjectListRef::cast_from(model->catalog()->schemata()), "new_schema");
381
schema= grt->create_object<db_Schema>(class_name);
382
schema->owner(model->catalog());
385
schema->createDate(bec::fmttime(0, DATETIME_FMT));
386
schema->lastChangeDate(bec::fmttime(0, DATETIME_FMT));
388
model->catalog()->schemata().insert(schema);
390
undo.end(_("Create New Schema"));
396
void WBComponentPhysical::add_new_db_schema(const workbench_physical_ModelRef &model)
400
res= _wb->execute_in_grt_thread("Create new schema",
401
boost::bind(&WBComponentPhysical::add_new_db_schema_grt, this, _1, model));
405
_wb->show_status_text(strfmt(_("Schema '%s' created."), db_SchemaRef::cast_from(res)->name().c_str()));
407
_wb->open_object_editor(GrtObjectRef::cast_from(res));
410
_wb->show_status_text(_("Could not create new schema."));
414
grt::ValueRef WBComponentPhysical::delete_db_schema_grt(grt::GRT *grt, const db_SchemaRef &schema,
417
std::map<std::string, bool> options;
419
options["deletedbobjects"]= false;
422
(schema->tables().count() > 0 || schema->views().count() > 0 || schema->routines().count() > 0))
424
grt::DictRef dict(grt);
426
dict.gset("name", schema->name());
427
dict.gset("tables", (long)schema->tables().count());
428
dict.gset("views", (long)schema->views().count());
429
dict.gset("routines", (long)schema->routines().count());
434
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(schema));
435
if (model.is_valid())
437
workbench_physical_DiagramRef view;
439
if (model->catalog()->schemata().get_index(schema) == grt::BaseListRef::npos)
440
return grt::ValueRef();
442
grt::AutoUndo undo(grt);
444
for (size_t vc= model->diagrams().count(), vi= 0; vi < vc; vi++)
446
view= model->diagrams().get(vi);
447
std::list<model_FigureRef> figures;
449
// remove canvas objects for schema contents
450
for (size_t c= schema->tables().count(), i= 0; i < c; i++)
452
db_TableRef table= schema->tables().get(i);
453
model_FigureRef figure= view->getFigureForDBObject(table);
454
if (figure.is_valid())
455
figures.push_back(figure);
457
for (size_t c= schema->views().count(), i= 0; i < c; i++)
459
db_ViewRef v= schema->views().get(i);
460
model_FigureRef figure= view->getFigureForDBObject(v);
461
if (figure.is_valid())
462
figures.push_back(figure);
464
for (size_t c= schema->routineGroups().count(), i= 0; i < c; i++)
466
db_RoutineGroupRef rgroup= schema->routineGroups().get(i);
467
model_FigureRef figure= view->getFigureForDBObject(rgroup);
468
if (figure.is_valid())
469
figures.push_back(figure);
472
for (std::list<model_FigureRef>::const_iterator f= figures.begin(); f != figures.end(); ++f)
473
delete_model_object(*f, options);
476
model->catalog()->schemata().remove_value(schema);
478
undo.end(_("Delete Schema"));
481
return grt::ValueRef();
485
void WBComponentPhysical::delete_db_schema(const db_SchemaRef &schema)
487
grt::ValueRef result;
489
_wb->show_status_text(_("Deleting schema..."));
491
result= _wb->execute_in_grt_thread("Delete schema",
492
boost::bind(&WBComponentPhysical::delete_db_schema_grt, this, _1, schema, true));
494
if (result.is_valid() && result.type() == DictType)
496
grt::DictRef info(grt::DictRef::cast_from(result));
500
if (info.get_int("tables") > 0)
501
objects+= strfmt("%li tables, ", info.get_int("tables"));
502
if (info.get_int("views") > 0)
503
objects+= strfmt("%li views, ", info.get_int("views"));
504
if (info.get_int("routines") > 0)
505
objects+= strfmt("%li routines, ", info.get_int("routines"));
507
if (!objects.empty())
508
objects= objects.substr(0, objects.length()-2);
510
res= mforms::Utilities::show_message(_("Delete Schema"),
511
strfmt(_("The schema '%s' contains objects (%s).\n"
512
"Do you want to delete it with all its contents?"),
513
info.get_string("name").c_str(), objects.c_str()),
514
_("Delete"), _("Cancel"));
515
if (res != mforms::ResultOk)
517
_wb->show_status_text(_("Delete schema cancelled."));
521
result= _wb->execute_in_grt_thread("Delete schema",
522
boost::bind(&WBComponentPhysical::delete_db_schema_grt, this, _1, schema, false));
525
if (!result.is_valid())
526
_wb->show_status_text(_("Schema deleted."));
528
_wb->show_status_text(_("Could not delete schema."));
531
#include "grts/structs.meta.h"
533
grt::ValueRef WBComponentPhysical::add_new_db_table_grt(grt::GRT *grt,
534
const db_SchemaRef &schema)
536
grt::AutoUndo undo(grt);
538
db_TableRef table= schema->addNewTable(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());
540
if (table.has_member("tableEngine"))
541
table.set_member("tableEngine", _wb->get_grt_manager()->get_app_option("db.mysql.Table:tableEngine"));
543
undo.end(_("Create Table"));
550
void WBComponentPhysical::add_new_db_table(const db_SchemaRef &schema)
554
res= _wb->execute_in_grt_thread("Create new table",
555
boost::bind(&WBComponentPhysical::add_new_db_table_grt, this, _1, schema));
559
db_TableRef table(db_TableRef::cast_from(res));
561
_wb->show_status_text(strfmt(_("Table '%s' created in schema '%s'"), table->name().c_str(),
562
table->owner()->name().c_str()));
564
_wb->open_object_editor(GrtObjectRef::cast_from(res));
567
_wb->show_status_text(_("Could not create new table."));
571
grt::ValueRef WBComponentPhysical::add_new_db_view_grt(grt::GRT *grt,
572
const db_SchemaRef &schema)
574
grt::AutoUndo undo(grt);
577
schema->addNewView(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());
579
undo.end(_("Create View"));
586
void WBComponentPhysical::add_new_db_view(const db_SchemaRef &schema)
590
res= _wb->execute_in_grt_thread("Create new view",
591
boost::bind(&WBComponentPhysical::add_new_db_view_grt, this, _1, schema));
594
db_ViewRef view(db_ViewRef::cast_from(res));
596
_wb->show_status_text(strfmt(_("View '%s' created in schema '%s'"), view->name().c_str(),
597
view->owner()->name().c_str()));
599
_wb->open_object_editor(GrtObjectRef::cast_from(res));
602
_wb->show_status_text(_("Could not create new view"));
608
grt::ValueRef WBComponentPhysical::add_new_db_routine_group_grt(grt::GRT *grt,
609
const db_SchemaRef &schema)
611
grt::AutoUndo undo(grt);
613
db_RoutineGroupRef rgroup=
614
schema->addNewRoutineGroup(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());
616
undo.end(_("Create Routine Group"));
623
void WBComponentPhysical::add_new_db_routine_group(const db_SchemaRef &schema)
627
res= _wb->execute_in_grt_thread("Create new routine group",
628
boost::bind(&WBComponentPhysical::add_new_db_routine_group_grt, this, _1, schema));
631
db_RoutineGroupRef rgroup(db_RoutineGroupRef::cast_from(res));
633
_wb->show_status_text(strfmt(_("Routine group '%s' created in schema '%s'"), rgroup->name().c_str(),
634
rgroup->owner()->name().c_str()));
636
_wb->open_object_editor(GrtObjectRef::cast_from(res));
639
_wb->show_status_text(_("Could not create new routine group"));
644
grt::ValueRef WBComponentPhysical::add_new_db_routine_grt(grt::GRT *grt, const db_SchemaRef &schema)
646
grt::AutoUndo undo(grt);
648
db_RoutineRef routine=
649
schema->addNewRoutine(*_wb->get_parent_for_object<workbench_physical_Model>(schema)->rdbms()->databaseObjectPackage());
651
undo.end(_("Create Routine"));
658
void WBComponentPhysical::add_new_db_routine(const db_SchemaRef &schema)
662
res= _wb->execute_in_grt_thread("Create new routine",
663
boost::bind(&WBComponentPhysical::add_new_db_routine_grt, this, _1, schema));
666
db_RoutineRef routine(db_RoutineRef::cast_from(res));
668
_wb->show_status_text(strfmt(_("Routine '%s' created in schema '%s'"), routine->name().c_str(),
669
routine->owner()->name().c_str()));
671
_wb->open_object_editor(GrtObjectRef::cast_from(res));
674
_wb->show_status_text(_("Could not create new routine"));
680
grt::ValueRef WBComponentPhysical::add_new_stored_script_grt(grt::GRT *grt, const workbench_physical_ModelRef &model,
681
const std::string &path)
683
db_ScriptRef script(grt);
684
std::string name= "script";
686
name= g_basename(path.c_str());
687
script->owner(model);
688
script->name(grt::get_name_suggestion_for_list_object(
689
grt::ObjectListRef::cast_from(model->scripts()), name, false));
690
script->createDate(bec::fmttime(0, DATETIME_FMT));
691
script->lastChangeDate(bec::fmttime(0, DATETIME_FMT));
692
script->filename(_wb->create_attached_file("script", path));
694
grt::AutoUndo undo(grt);
695
// insertion will trigger the creation of the file
696
model->scripts().insert(script);
698
undo.end(_("Add SQL Script"));
700
undo.end(strfmt(_("Add Script File '%s'"), name.c_str()));
706
void WBComponentPhysical::add_new_stored_script(const workbench_physical_ModelRef &model, const std::string &path)
708
_wb->execute_in_grt_thread("Create new stored script",
709
boost::bind(&WBComponentPhysical::add_new_stored_script_grt, this, _1, model, path));
713
grt::ValueRef WBComponentPhysical::add_new_stored_note_grt(grt::GRT *grt, const workbench_physical_ModelRef &model,
714
const std::string &path)
716
GrtStoredNoteRef note(grt);
717
std::string name= _("New Note");
719
name= g_basename(path.c_str());
721
note->name(grt::get_name_suggestion_for_list_object(
722
grt::ObjectListRef::cast_from(model->notes()), name, false));
723
note->createDate(bec::fmttime(0, DATETIME_FMT));
724
note->lastChangeDate(bec::fmttime(0, DATETIME_FMT));
725
note->filename(_wb->create_attached_file("note", path));
727
grt::AutoUndo undo(grt);
728
// insertion will trigger the creation of the file
729
model->notes().insert(note);
731
undo.end(_("Add Text Note"));
733
undo.end(strfmt(_("Add Note File '%s'"), name.c_str()));
739
void WBComponentPhysical::add_new_stored_note(const workbench_physical_ModelRef &model, const std::string &path)
741
_wb->execute_in_grt_thread("Create new stored note",
742
boost::bind(&WBComponentPhysical::add_new_stored_note_grt, this, _1, model, path));
746
void WBComponentPhysical::clone_db_object_to_schema(const db_SchemaRef &schema, const db_DatabaseObjectRef &object,
747
grt::CopyContext &context)
749
grt::AutoUndo undo(get_grt());
751
if (object.is_instance(db_Table::static_class_name()))
753
db_TableRef dbtable(db_TableRef::cast_from(context.copy(object)));
755
if (grt::find_named_object_in_list(schema->tables(), dbtable->name()).is_valid())
756
dbtable->name(grt::get_name_suggestion_for_list_object(schema->tables(),
757
*dbtable->name()+"_copy"));
759
dbtable->owner(schema);
760
dbtable->oldName("");
761
schema->tables().insert(dbtable);
762
undo.end(strfmt(_("Duplicate '%s'"), dbtable->name().c_str()));
764
else if (object.is_instance(db_View::static_class_name()))
766
db_ViewRef dbview(db_ViewRef::cast_from(context.copy(object)));
768
if (grt::find_named_object_in_list(schema->views(), dbview->name()).is_valid())
769
dbview->name(grt::get_name_suggestion_for_list_object(schema->views(),
770
*dbview->name()+"_copy"));
772
dbview->owner(schema);
774
schema->views().insert(dbview);
775
undo.end(strfmt(_("Duplicate '%s'"), dbview->name().c_str()));
777
else if (object.is_instance(db_Routine::static_class_name()))
779
db_RoutineRef dbroutine(db_RoutineRef::cast_from(context.copy(object)));
781
if (grt::find_named_object_in_list(schema->routines(), dbroutine->name()).is_valid())
782
dbroutine->name(grt::get_name_suggestion_for_list_object(schema->routines(),
783
*dbroutine->name()+"_copy"));
785
dbroutine->owner(schema);
786
dbroutine->oldName("");
787
schema->routines().insert(dbroutine);
788
undo.end(strfmt(_("Duplicate '%s'"), dbroutine->name().c_str()));
790
else if (object.is_instance(db_RoutineGroup::static_class_name()))
792
db_RoutineGroupRef dbroutineGroup(db_RoutineGroupRef::cast_from(context.copy(object)));
794
if (grt::find_named_object_in_list(schema->routineGroups(), dbroutineGroup->name()).is_valid())
795
dbroutineGroup->name(grt::get_name_suggestion_for_list_object(schema->routineGroups(),
796
*dbroutineGroup->name()+"_copy"));
798
dbroutineGroup->owner(schema);
799
dbroutineGroup->oldName("");
800
schema->routineGroups().insert(dbroutineGroup);
801
undo.end(strfmt(_("Duplicate '%s'"), dbroutineGroup->name().c_str()));
806
//--------------------------------------------------------------------------------
807
// Canvas Object Management
810
grt::ValueRef WBComponentPhysical::place_db_object_grt(grt::GRT *grt, ModelDiagramForm *view,
812
const db_DatabaseObjectRef &object,
815
workbench_physical_DiagramRef pview(workbench_physical_DiagramRef::cast_from(view->get_model_diagram()));
816
model_FigureRef figure;
817
std::string object_member;
819
if (object.is_instance(db_Table::static_class_name()))
821
figure= pview->placeTable(db_TableRef::cast_from(object), pos.x, pos.y);
822
object_member= "table";
824
else if (object.is_instance(db_View::static_class_name()))
826
figure= pview->placeView(db_ViewRef::cast_from(object), pos.x, pos.y);
827
object_member= "view";
829
else if (object.is_instance(db_RoutineGroup::static_class_name()))
831
figure= pview->placeRoutineGroup(db_RoutineGroupRef::cast_from(object), pos.x, pos.y);
832
object_member= "routineGroup";
835
throw std::invalid_argument("trying to place invalid object on view");
837
grt::AutoUndo undo(get_grt());
839
if ((*figure->color()).empty())
841
if (!view->get_tool_argument(figure.class_name()+":Color").empty())
842
figure->color(grt::StringRef(view->get_tool_argument(figure.class_name()+":Color")));
844
figure->color(_wb->get_wb_options().get_string(figure.class_name()+":Color", ""));
847
if (view->get_model_options().get_int("workbench.physical.ObjectFigure:Expanded",
848
_wb->get_wb_options().get_int("workbench.physical.ObjectFigure:Expanded")))
855
pview->unselectAll();
856
pview->selectObject(figure);
858
undo.end(strfmt(_("Place '%s'"), object->name().c_str()));
864
model_FigureRef WBComponentPhysical::place_db_object(ModelDiagramForm *view, const Point &pos,
865
const db_DatabaseObjectRef &object,
871
r= _wb->execute_in_grt_thread("Place existing object in model",
872
boost::bind(&WBComponentPhysical::place_db_object_grt, this, _1, view, pos, object, select_figure));
874
catch (std::invalid_argument &)
876
_wb->show_status_text(_("Cannot place object."));
877
return model_FigureRef();
879
catch (grt::grt_runtime_error &exc)
881
_wb->show_exception(_("Place Object on Canvas"), exc);
882
return model_FigureRef();
886
_wb->show_status_text(strfmt(_("Placed %s"), GrtObjectRef::cast_from(r)->name().c_str()));
888
_wb->show_status_text(_("Failed placing db object."));
890
return model_FigureRef::cast_from(r);
894
std::vector<std::string> WBComponentPhysical::get_accepted_drop_types() const
896
std::vector<std::string> types;
898
types.push_back(WB_DBOBJECT_DRAG_TYPE);
904
bool WBComponentPhysical::accepts_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
909
if (type == WB_DBOBJECT_DRAG_TYPE)
911
for (std::list<GrtObjectRef>::const_iterator iter= objects.begin();
912
iter != objects.end(); ++iter)
914
if (!(*iter).is_instance(db_DatabaseObject::static_class_name()))
923
bool WBComponentPhysical::perform_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
928
if (type == WB_DBOBJECT_DRAG_TYPE)
930
std::list<db_DatabaseObjectRef> dbobjects;
932
for (std::list<GrtObjectRef>::const_iterator iter= objects.begin();
933
iter != objects.end(); ++iter)
934
dbobjects.push_back(db_DatabaseObjectRef::cast_from((*iter)));
936
interactive_place_db_objects(view, x, y, dbobjects);
944
bool WBComponentPhysical::perform_drop(ModelDiagramForm *view, int x, int y, const std::string &type, const std::string &data)
949
if (type == WB_DBOBJECT_DRAG_TYPE)
951
std::list<db_DatabaseObjectRef> dbobjects;
952
db_CatalogRef catalog= workbench_physical_ModelRef::cast_from(view->get_model_diagram()->owner())->catalog();
954
dbobjects= bec::CatalogHelper::dragdata_to_dbobject_list(catalog, data);
956
interactive_place_db_objects(view, x, y, dbobjects);
965
void WBComponentPhysical::interactive_place_db_objects(ModelDiagramForm *vform, int x, int y,
966
const std::list<db_DatabaseObjectRef> &objects)
969
std::vector<db_TableRef> tables;
973
mforms::Utilities::show_message(_("Cannot Place Object"), _("The dragged object cannot be placed in the diagram."), _("Close"));
977
grt::AutoUndo undo(get_grt());
979
Point op, p= vform->get_view()->window_to_canvas(x, y);
981
Size view_size(vform->get_view()->get_total_view_size());
983
vform->get_model_diagram()->unselectAll();
985
for (std::list<db_DatabaseObjectRef>::const_iterator iter= objects.begin(); iter != objects.end(); ++iter)
987
if (has_figure_for_object_in_active_view(*iter, vform))
991
model_FigureRef figure= place_db_object(vform, p, *iter, false);
993
if (figure.is_valid())
994
vform->get_model_diagram()->selectObject(figure);
998
if (p.x + 100 > view_size.width)
1003
else if (p.y + 100 > view_size.height)
1009
if (p.x + 100 > view_size.width || p.y+100 > view_size.height)
1012
if (iter->is_instance(db_Table::static_class_name()))
1013
tables.push_back(db_TableRef::cast_from(*iter));
1017
undo.end(_("Place object(s) on canvas"));
1019
if (dupes == objects.size())
1022
mforms::Utilities::show_message(_("Cannot Place Object"), _("The object cannot be placed because it's already present in this diagram."), _("Close"));
1024
mforms::Utilities::show_message(_("Cannot Place Objects"), _("The objects cannot be placed because they're already present in this diagram."), _("Close"));
1027
mforms::Utilities::show_message(_("Cannot Place Object(s)"), _("Some of the objects could not be placed because they're already present in this diagram."), _("Close"));
1029
// manually force a refresh since adding a figure doesn't trigger a catalog tree refresh
1031
// catalog tree doesn't need a full refresh, only a redisplay (structure doesn't change,
1032
// only the text), so we can use the RefreshLayer msg to perform a redisplay only
1033
_wb->request_refresh(RefreshSchema, "");
1035
_wb->request_refresh(RefreshSchemaNoReload, "");
1041
grt::ValueRef WBComponentPhysical::place_new_db_object_grt(grt::GRT *grt, ModelDiagramForm *vform,
1043
wb::ObjectType type)
1045
std::string object_struct_name;
1046
db_SchemaRef target_schema;
1047
std::string schema_name;
1049
grt::AutoUndo undo(get_grt());
1051
model_DiagramRef view(vform->get_model_diagram());
1052
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(view));
1057
object_struct_name= workbench_physical_TableFigure::static_class_name();
1060
object_struct_name= workbench_physical_ViewFigure::static_class_name();
1062
case ObjectRoutineGroup:
1063
object_struct_name= workbench_physical_RoutineGroupFigure::static_class_name();
1066
throw std::logic_error("place_db_object() called with invalid tool");
1068
schema_name= vform->get_tool_argument(object_struct_name+std::string(":Schema"));
1070
if (!schema_name.empty())
1072
db_SchemaRef schema(grt::find_named_object_in_list(model->catalog()->schemata(), schema_name));
1073
if (schema.is_valid())
1074
target_schema= schema;
1077
// pick a default schema..
1078
if (!target_schema.is_valid())
1080
if (model->catalog()->schemata().count() == 0)
1082
// if there are no schemas, create a default one
1083
add_new_db_schema_grt(grt, model);
1085
target_schema= model->catalog()->schemata().get(0);
1088
db_DatabaseObjectRef object;
1093
object= db_DatabaseObjectRef::cast_from(add_new_db_table_grt(grt, target_schema));
1096
object= db_DatabaseObjectRef::cast_from(add_new_db_view_grt(grt, target_schema));
1098
case ObjectRoutineGroup:
1099
object= db_DatabaseObjectRef::cast_from(add_new_db_routine_group_grt(grt, target_schema));
1102
throw std::logic_error("place_db_object() called with invalid tool");
1105
std::string collation= vform->get_tool_argument(object_struct_name+":Collation");
1107
if (collation!="" && collation[0] != '*')
1109
if (object.has_member("defaultCollationName"))
1110
object.set_member("defaultCollationName", grt::StringRef(collation));
1112
std::string charset= base::split(collation, "_", 1)[0];
1113
if (object.has_member("defaultCharacterSetName"))
1114
object.set_member("defaultCharacterSetName", grt::StringRef(charset));
1117
std::string engine= vform->get_tool_argument(object_struct_name+":Engine");
1118
if (!engine.empty() && engine[0] != '*')
1120
if (object.has_member("tableEngine"))
1121
object.set_member("tableEngine", grt::StringRef(engine));
1124
grt::ValueRef result= place_db_object_grt(grt, vform, pos, object, true);
1126
undo.end(strfmt(_("Place '%s'"), object->name().c_str()));
1132
void WBComponentPhysical::place_new_db_object(ModelDiagramForm *view, const Point &pos,
1133
wb::ObjectType type)
1137
r= _wb->execute_in_grt_thread("Place object in model",
1138
boost::bind(&WBComponentPhysical::place_new_db_object_grt, this, _1, view, pos, type));
1140
_wb->show_status_text(strfmt(_("Placed new %s"), model_FigureRef::cast_from(r)->name().c_str()));
1142
_wb->show_status_text(_("Failed placing db object."));
1146
bool WBComponentPhysical::create_nm_relationship_grt(grt::GRT *grt, ModelDiagramForm *view,
1147
workbench_physical_TableFigureRef table1,
1148
workbench_physical_TableFigureRef table2,
1149
bool imandatory, bool fmandatory)
1151
grt::AutoUndo undo(grt);
1152
// create the associative table for a n:m relationship
1154
bec::TableHelper::create_associative_table(db_SchemaRef::cast_from(table1->table()->owner()),
1155
table1->table(), table2->table(),
1156
imandatory, fmandatory,
1157
workbench_physical_ModelRef::cast_from(view->get_model_diagram()->owner())->rdbms(),
1158
_wb->get_wb_options(),
1159
view->get_model_diagram()->owner()->options());
1161
if (!atable.is_valid())
1164
// place the assoc table in the view
1167
pos.x= (table1->left() + table2->left()) / 2;
1168
pos.y= (table1->top() + table2->top()) / 2;
1170
place_db_object_grt(grt, view, pos, atable, true);
1172
undo.end(_("Create n:m Relationship"));
1178
WBComponentPhysical::RelationshipToolContext *
1179
WBComponentPhysical::start_relationship(ModelDiagramForm *view, const Point &pos,
1180
RelationshipType type)
1182
RelationshipToolContext *rctx= new RelationshipToolContext(this, view, type);
1188
void WBComponentPhysical::cancel_relationship(ModelDiagramForm *view, RelationshipToolContext *rctx)
1199
void WBComponentPhysical::delete_db_object(const db_DatabaseObjectRef &object)
1201
db_SchemaRef schema(db_SchemaRef::cast_from(object->owner()));
1202
std::map<std::string, bool> options;
1203
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(schema));
1205
// do not delete db object from model object deleter, since that would
1206
// mean deleting it twice and adding 2 entries in the undo stack
1207
options["deletedbobjects"]= false;
1209
//XXX need to look for refs by other objects and show them to user
1210
// and confirm that they should be removed or cancel
1212
if (object.is_instance(db_Table::static_class_name()))
1214
grt::AutoUndo undo(get_grt());
1216
schema->tables().remove_value(db_TableRef::cast_from(object));
1218
if (model.is_valid())
1220
for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin();
1221
view != model->diagrams().end(); ++view)
1223
grt::ListRef<model_Figure> figures((*view)->figures());
1224
for (size_t f= figures.count(); f > 0; --f)
1226
model_FigureRef figure= figures[f-1];
1227
if (figure.is_instance(workbench_physical_TableFigure::static_class_name())
1228
&& workbench_physical_TableFigureRef::cast_from(figure)->table() == object)
1230
delete_model_object(figure, options);
1236
// remove referencing foreign keys
1238
db_TableRef table= db_TableRef::cast_from(object);
1239
grt::ListRef<db_ForeignKey> foreignKeys(db_SchemaRef::cast_from(table->owner())->getForeignKeysReferencingTable(table));
1240
if (0 < foreignKeys.count())
1242
for (grt::ListRef<db_ForeignKey>::const_iterator iter= foreignKeys.begin();
1243
iter != foreignKeys.end(); ++iter)
1245
db_ForeignKeyRef fk(*iter);
1246
db_TableRef ref_table= db_TableRef::cast_from(fk->owner());
1248
// remove corresponding index
1250
grt::ListRef<db_Index> indices= ref_table->indices();
1251
for (size_t count= indices.count(), i= 0; i < count; ++i)
1253
db_IndexRef index= indices.get(i);
1254
if (0 == strcmp(index->indexType().c_str(), "FOREIGN"))
1256
grt::ListRef<db_IndexColumn> index_columns= index->columns();
1257
grt::ListRef<db_Column> fk_columns= fk->columns();
1258
if (index_columns.count() == fk_columns.count())
1261
for (size_t count= index_columns.count(), c= 0; c < count; ++c)
1263
db_ColumnRef index_column= index_columns.get(c)->referencedColumn();
1264
if (index_column.is_valid())
1266
db_ColumnRef fk_column= find_object_in_list(fk_columns, index_column.id());
1267
if (!fk_column.is_valid())
1276
ref_table->indices().remove_value(index);
1283
if (fk->index().is_valid())
1284
ref_table->indices().remove_value(fk->index());
1285
ref_table->foreignKeys().remove_value(fk);
1290
remove_references_to_object(object);
1292
undo.end(_("Delete Table"));
1294
else if (object.is_instance(db_View::static_class_name()))
1296
grt::AutoUndo undo(get_grt());
1298
schema->views().remove_value(db_ViewRef::cast_from(object));
1300
if (model.is_valid())
1302
for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin();
1303
view != model->diagrams().end(); ++view)
1305
for (grt::ListRef<model_Figure>::const_reverse_iterator figure= (*view)->figures().rbegin();
1306
figure != (*view)->figures().rend(); ++figure)
1308
if ((*figure).is_instance(workbench_physical_ViewFigure::static_class_name())
1309
&& workbench_physical_ViewFigureRef::cast_from(*figure)->view() == object)
1311
delete_model_object(*figure, options);
1312
break;//We have deleted figure, figure is invalid from now and may not be incremented
1318
remove_references_to_object(object);
1320
undo.end(_("Delete View"));
1322
else if (object.is_instance(db_RoutineGroup::static_class_name()))
1324
std::set<db_RoutineRef> ungrouped_routines;
1325
db_RoutineGroupRef routine_group(db_RoutineGroupRef::cast_from(object));
1327
// first check if the routines in the rg are in any other routine groups
1328
// if they are in none, check if they should be totally deleted
1330
GRTLIST_FOREACH(db_Routine, routine_group->routines(), routine)
1332
ungrouped_routines.insert(*routine);
1335
if (model.is_valid())
1337
GRTLIST_FOREACH(db_RoutineGroup, schema->routineGroups(), rgroup)
1339
if (*rgroup != routine_group)
1341
GRTLIST_FOREACH(db_Routine, (*rgroup)->routines(), routine)
1343
std::set<db_RoutineRef>::iterator iter;
1345
// remove the routines that are in other groups
1346
if ((iter = ungrouped_routines.find(*routine)) != ungrouped_routines.end())
1347
ungrouped_routines.erase(iter);
1349
if (ungrouped_routines.empty()) break;
1352
if (ungrouped_routines.empty()) break;
1356
grt::AutoUndo undo(get_grt());
1358
if (!ungrouped_routines.empty())
1361
if (routine_group->routines().count() == ungrouped_routines.size())
1362
result= mforms::Utilities::show_message(_("Delete Routines"),
1363
strfmt(_("Would you like to delete the routines contained in group '%s'?"),
1364
routine_group->name().c_str()),
1365
_("Delete"), _("Cancel"), _("Keep"));
1367
result= mforms::Utilities::show_message(_("Delete Routines"),
1368
strfmt(_("There are %zu routines in '%s' that are not in any other group, would you like to delete them?"),
1369
ungrouped_routines.size(), routine_group->name().c_str()),
1370
_("Delete"), _("Cancel"), _("Keep"));
1372
if (result == mforms::ResultCancel)
1377
else if (result == mforms::ResultOk)
1379
// delete all routines
1380
for (base::const_range<std::set<db_RoutineRef> > r(ungrouped_routines); r; ++r)
1382
size_t i = schema->routines().get_index(*r);
1383
if (i != grt::BaseListRef::npos)
1384
schema->routines().remove(i);
1389
schema->routineGroups().remove_value(routine_group);
1391
if (model.is_valid())
1393
for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= model->diagrams().begin();
1394
view != model->diagrams().end(); ++view)
1396
for (grt::ListRef<model_Figure>::const_reverse_iterator figure= (*view)->figures().rbegin();
1397
figure != (*view)->figures().rend(); ++figure)
1399
if ((*figure).is_instance(workbench_physical_RoutineGroupFigure::static_class_name())
1400
&& workbench_physical_RoutineGroupFigureRef::cast_from(*figure)->routineGroup() == object)
1402
delete_model_object(*figure, options);
1403
break;//We have deleted figure, figure is invalid from now and may not be incremented
1409
remove_references_to_object(object);
1411
undo.end(_("Delete Routine Group"));
1413
else if (object.is_instance(db_Routine::static_class_name()))
1415
grt::AutoUndo undo(get_grt());
1416
db_RoutineRef routine(db_RoutineRef::cast_from(object));
1418
schema->routines().remove_value(routine);
1419
// remove from routine groups
1420
GRTLIST_FOREACH(db_RoutineGroup, schema->routineGroups(), rg)
1423
while ((i = (*rg)->routines().get_index(routine)) != grt::BaseListRef::npos)
1425
(*rg)->routines().remove(i);
1429
remove_references_to_object(object);
1431
undo.end(_("Delete Routine"));
1437
bool WBComponentPhysical::delete_model_object(const model_ObjectRef &object, std::map<std::string,bool> &options)
1439
if (object.is_instance(workbench_physical_Connection::static_class_name()))
1441
workbench_physical_ConnectionRef conn(workbench_physical_ConnectionRef::cast_from(object));
1442
db_ForeignKeyRef fk(conn->foreignKey());
1443
db_TableRef table(db_TableRef::cast_from(fk->owner()));
1446
// Check if the fk still belongs to the table.
1447
// If the connection is being deleted together with the table that it points to,
1448
// it will be auto-removed. Depending on the order things get executed and by the
1449
// way this place is reached, the FK might already be gone.
1450
if (table->foreignKeys().get_index(fk) == grt::BaseListRef::npos)
1453
result= mforms::Utilities::show_message(_("Delete Foreign Key Columns"),
1454
_("Please confirm whether columns used by the foreign key should be deleted too.\nColumns used by other foreign keys will be left untouched."),
1455
_("Delete"), _("Cancel"), _("Keep"));
1457
if (result == mforms::ResultCancel)
1460
grt::AutoUndo undo(get_grt());
1462
model_DiagramRef view(conn->owner());
1463
// remove connection
1464
view->removeConnection(conn);
1466
table->removeForeignKey(fk, result == mforms::ResultOk ? true : false);
1468
undo.end(_("Delete Relationship"));
1470
else if (object.is_instance(model_Figure::static_class_name()))
1472
if (options.find("deletedbobjects-canceled") != options.end())
1475
model_FigureRef figure(model_FigureRef::cast_from(object));
1478
if (options.find("deletedbobjects") == options.end())
1480
int ok= mforms::ResultOther;
1481
std::map<std::string, bool>::const_iterator it= options.find("silent-mode");
1482
if((it == options.end()) || (it->second == false))
1484
ok= _wb->execute_in_main_thread<int>("ask delete", boost::bind(&mforms::Utilities::show_message,
1486
_("Should corresponding database objects be deleted with the figures?"),
1487
_("Delete"), _("Skip"), _("Keep")
1492
ok= mforms::ResultOk;
1495
if (ok == mforms::ResultCancel)
1497
options["deletedbobjects-canceled"]= true;
1500
if (ok == mforms::ResultOk)
1501
del_db_object= true;
1503
del_db_object= false;
1505
options["deletedbobjects"]= del_db_object;
1509
del_db_object= options["deletedbobjects"];
1512
grt::AutoUndo undo(get_grt());
1513
// if the figure is a DB object, the DB object is also deleted
1514
if (figure.is_instance(workbench_physical_TableFigure::static_class_name()))
1516
db_TableRef dbtable(workbench_physical_TableFigureRef::cast_from(figure)->table());
1518
workbench_physical_DiagramRef::cast_from(figure->owner())->deleteConnectionsForTable(dbtable);
1520
workbench_physical_TableFigureRef::cast_from(figure)->table(db_TableRef());
1522
// this must be called after the figure->table field is invalidated, otherwise
1523
// delete_db_object() will try to delete the figure again and we get into a loop
1525
delete_db_object(dbtable);
1527
else if (figure.is_instance(workbench_physical_ViewFigure::static_class_name()))
1529
db_ViewRef view(workbench_physical_ViewFigureRef::cast_from(figure)->view());
1531
workbench_physical_ViewFigureRef::cast_from(figure)->view(db_ViewRef());
1534
delete_db_object(view);
1536
else if (figure.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()))
1538
db_RoutineGroupRef rg(workbench_physical_RoutineGroupFigureRef::cast_from(figure)->routineGroup());
1540
workbench_physical_RoutineGroupFigureRef::cast_from(figure)->routineGroup(db_RoutineGroupRef());
1543
delete_db_object(rg);
1548
// removeFigure() will remove anything that depends on the figure automatically, ie connections
1549
workbench_physical_DiagramRef::cast_from(figure->owner())->removeFigure(figure);
1551
undo.end(strfmt(_("Delete '%s' Figure"), figure.get_metaclass()->get_attribute("caption").c_str()));
1561
bool WBComponentPhysical::handles_figure(const model_ObjectRef &figure)
1563
if (figure.is_instance(workbench_physical_TableFigure::static_class_name()) ||
1564
figure.is_instance(workbench_physical_ViewFigure::static_class_name()) ||
1565
figure.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()) ||
1566
figure.is_instance(workbench_physical_Connection::static_class_name()))
1572
model_ObjectRef WBComponentPhysical::clone_object(const model_ObjectRef &object, const model_LayerRef &destlayer,
1573
grt::CopyContext ©_context, bool copy_only)
1575
std::set<std::string> skip;
1576
skip.insert("oldName");
1578
if (object.is_instance(workbench_physical_TableFigure::static_class_name()))
1580
workbench_physical_TableFigureRef table(workbench_physical_TableFigureRef::cast_from(object));
1583
db_TableRef dbtable(db_TableRef::cast_from(copy_context.copy(table->table(),skip)));
1585
copy_context.update_references();
1588
// - uniquefy foreign key names
1589
int max_fk_len = workbench_physical_ModelRef::cast_from(dbtable->owner()->owner()->owner())->rdbms()->maximumIdentifierLength();
1590
grt::ListRef<db_ForeignKey> fks(dbtable->foreignKeys());
1591
std::set<std::string> used_fk_names = bec::SchemaHelper::get_foreign_key_names(db_SchemaRef::cast_from(dbtable->owner()));
1592
for (size_t c= fks.count(), i = 0; i < c; i++)
1594
db_ForeignKeyRef fk(fks[i]);
1596
fk->name(bec::SchemaHelper::get_unique_foreign_key_name(used_fk_names, fk->name(), max_fk_len));
1600
skip.insert("table");
1601
workbench_physical_TableFigureRef copy(workbench_physical_TableFigureRef::cast_from(copy_context.copy(table,skip)));
1602
copy->table(dbtable);
1604
copy_context.update_references();
1606
if (destlayer.is_valid())
1608
copy->owner(destlayer->owner());
1609
copy->layer(destlayer);
1613
if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbtable->owner())->tables(), dbtable->name()).is_valid())
1614
dbtable->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbtable->owner())->tables(),
1615
*dbtable->name()+"_copy"));
1616
copy->name(dbtable->name());
1618
grt::AutoUndo undo(get_grt());
1620
db_SchemaRef::cast_from(dbtable->owner())->tables().insert(dbtable);
1622
destlayer->owner()->addFigure(copy);
1623
workbench_physical_DiagramRef::cast_from(destlayer->owner())->createConnectionsForTable(copy->table());
1625
undo.end(strfmt(_("Duplicate Table '%s'"), copy->name().c_str()));
1630
copy->owner(model_DiagramRef());
1631
copy->layer(model_LayerRef());
1636
else if (object.is_instance(workbench_physical_ViewFigure::static_class_name()))
1638
workbench_physical_ViewFigureRef view(workbench_physical_ViewFigureRef::cast_from(object));
1641
db_ViewRef dbview(db_ViewRef::cast_from(copy_context.copy(view->view())));
1644
skip.insert("view");
1645
workbench_physical_ViewFigureRef copy(workbench_physical_ViewFigureRef::cast_from(copy_context.copy(view)));
1648
if (destlayer.is_valid())
1650
copy->owner(destlayer->owner());
1651
copy->layer(destlayer);
1655
if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbview->owner())->views(), dbview->name()).is_valid())
1656
dbview->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbview->owner())->views(),
1657
*dbview->name()+"_copy"));
1658
copy->name(dbview->name());
1660
grt::AutoUndo undo(get_grt());
1662
db_SchemaRef::cast_from(dbview->owner())->views().insert(dbview);
1664
destlayer->owner()->addFigure(copy);
1666
undo.end(strfmt(_("Duplicate View '%s'"), copy->name().c_str()));
1671
copy->owner(model_DiagramRef());
1672
copy->layer(model_LayerRef());
1676
else if (object.is_instance(workbench_physical_RoutineGroupFigure::static_class_name()))
1678
workbench_physical_RoutineGroupFigureRef routineGroup(workbench_physical_RoutineGroupFigureRef::cast_from(object));
1680
// copy routineGroup
1681
db_RoutineGroupRef dbroutineGroup(db_RoutineGroupRef::cast_from(copy_context.copy(routineGroup->routineGroup())));
1684
skip.insert("routineGroup");
1685
workbench_physical_RoutineGroupFigureRef copy(workbench_physical_RoutineGroupFigureRef::cast_from(copy_context.copy(routineGroup)));
1686
copy->routineGroup(dbroutineGroup);
1688
if (destlayer.is_valid())
1690
copy->owner(destlayer->owner());
1691
copy->layer(destlayer);
1695
if (grt::find_named_object_in_list(db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups(), dbroutineGroup->name()).is_valid())
1696
dbroutineGroup->name(grt::get_name_suggestion_for_list_object(db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups(),
1697
*dbroutineGroup->name()+"_copy"));
1698
copy->name(dbroutineGroup->name());
1700
grt::AutoUndo undo(get_grt());
1702
db_SchemaRef::cast_from(dbroutineGroup->owner())->routineGroups().insert(dbroutineGroup);
1704
destlayer->owner()->addFigure(copy);
1706
undo.end(strfmt(_("Duplicate Routine Group '%s'"), copy->name().c_str()));
1711
copy->owner(model_DiagramRef());
1712
copy->layer(model_LayerRef());
1717
return model_ObjectRef();
1721
bool WBComponentPhysical::can_paste_non_model_object(const grt::ObjectRef &object)
1723
if (db_DatabaseObjectRef::can_wrap(object) &&
1724
!has_figure_for_object_in_active_view(GrtObjectRef::cast_from(object)) &&
1725
(object.is_instance(db_Table::static_class_name()) ||
1726
object.is_instance(db_View::static_class_name()) ||
1727
object.is_instance(db_RoutineGroup::static_class_name())))
1733
void WBComponentPhysical::paste_non_model_object(ModelDiagramForm *view, const grt::ObjectRef &object)
1735
std::list<db_DatabaseObjectRef> objects;
1737
// find the original object
1738
db_DatabaseObjectRef obj(db_DatabaseObjectRef::cast_from(object));
1739
db_SchemaRef schema(db_SchemaRef::cast_from(obj->owner()));
1741
if (obj.is_instance(db_Table::static_class_name()))
1742
objects.push_back(grt::find_named_object_in_list(schema->tables(), *obj->name()));
1743
else if (obj.is_instance(db_View::static_class_name()))
1744
objects.push_back(grt::find_named_object_in_list(schema->views(), *obj->name()));
1745
else if (obj.is_instance(db_RoutineGroup::static_class_name()))
1746
objects.push_back(grt::find_named_object_in_list(schema->routineGroups(), *obj->name()));
1750
if (objects.front().is_valid())
1751
interactive_place_db_objects(view, 10, 10, objects);
1755
std::string WBComponentPhysical::get_object_tooltip(const model_ObjectRef &object, mdc::CanvasItem *item)
1757
if (workbench_physical_TableFigureRef::can_wrap(object))
1759
workbench_physical_TableFigureRef table_figure(workbench_physical_TableFigureRef::cast_from(object));
1760
db_TableRef table(table_figure->table());
1762
if (table.is_valid())
1764
workbench_physical_TableFigure::ImplData *tfig= table_figure->get_data();
1767
db_ColumnRef column(tfig->get_column_at(item));
1770
if (!column.is_valid())
1771
index= tfig->get_index_at(item);
1773
if (column.is_valid())
1778
text.append(*column->name());
1779
if (table->isPrimaryKeyColumn(column))
1780
text.append(" (PK)");
1781
else if (table->isForeignKeyColumn(column))
1784
text.append(" (FK)");
1790
text.append("References: ");
1792
for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
1794
db_ForeignKeyRef fk(table->foreignKeys()[i]);
1795
//ssize_t idx; // get_index returns size_t, so comparing it by >= 0 is always true!!!
1797
const size_t idx= fk->columns().get_index(column);
1798
if (idx != BaseListRef::npos)
1800
if (fk->referencedTable().is_valid() && fk->referencedColumns().get(idx).is_valid())
1802
text.append(fk->referencedTable()->name());
1804
text.append(fk->referencedColumns().get(idx)->name());
1807
text.append("INVALID");
1814
text.append(column->formattedRawType());
1818
if (*column->isNotNull())
1819
flags.append("NOT NULL"); //XXX add a method to db_Column to return a formatted string of all flags
1820
for (size_t c= column->flags().count(), i= 0; i < c; i++)
1822
if (i > 0 || *column->isNotNull())
1824
flags.append(column->flags().get(i).c_str());
1827
text.append("Flags: ").append(flags);
1829
if (*column->defaultValue() != "")
1830
text.append("\nDEFAULT ").append(column->defaultValue().c_str());
1832
/* comments can be too large
1833
if (*column->comment().c_str())
1836
text.append(column->comment());
1842
else if (index.is_valid())
1846
text.append(*index->name()).append(" (").append(index->indexType().c_str()).append(")\n");
1848
for (size_t c= index->columns().count(), i= 0; i < c; i++)
1850
db_IndexColumnRef column(index->columns()[i]);
1852
text.append(" - ").append(column->referencedColumn()->name().c_str()).append("\n");
1857
else if (table.is_valid()) // table
1861
text.append(table->owner()->name());
1863
text.append(table->name());
1866
if (table->foreignKeys().count() > 0)
1868
text.append("References:\n");
1869
for (size_t c= table->foreignKeys().count(), i= 0; i < c; i++)
1871
db_ForeignKeyRef fk(table->foreignKeys()[i]);
1872
if (fk->referencedTable().is_valid())
1875
for (size_t d= fk->columns().count(), j= 0; j < d; j++)
1879
text.append(fk->columns()[j]->name());
1881
text.append(") TO ");
1882
if (fk->referencedTable()->owner() != table->owner())
1884
text.append(fk->referencedTable()->owner()->name());
1887
text.append(fk->referencedTable()->name());
1889
for (size_t d= fk->referencedColumns().count(), j= 0; j < d; j++)
1893
if (!fk->referencedColumns()[j].is_valid())
1894
text.append("INVALID");
1896
text.append(fk->referencedColumns()[j]->name());
1901
text.append(" INVALID\n");
1905
grt::ListRef<db_ForeignKey> foreignKeys(db_SchemaRef::cast_from(table->owner())->getForeignKeysReferencingTable(table));
1906
if (foreignKeys.is_valid() && foreignKeys.count() > 0)
1908
text.append("Referenced By:\n");
1909
for (grt::ListRef<db_ForeignKey>::const_iterator iter= foreignKeys.begin();
1910
iter != foreignKeys.end(); ++iter)
1912
db_ForeignKeyRef fk(*iter);
1914
if (fk->owner()->owner() != table->owner())
1916
text.append(fk->owner()->owner()->name());
1919
text.append(fk->owner()->name());
1921
for (size_t d= fk->columns().count(), j= 0; j < d; j++)
1925
if (fk->columns()[j].is_valid())
1926
text.append(fk->columns()[j]->name());
1928
text.append("INVALiD");
1930
text.append(") TO ");
1933
for (size_t d= fk->referencedColumns().count(), j= 0; j < d; j++)
1937
if (fk->referencedColumns()[j].is_valid())
1938
text.append(fk->referencedColumns()[j]->name());
1940
text.append("INVALID");
1946
/* comments can be too large
1947
if (*table->comment().c_str())
1950
text.append(table->comment());
1959
else if (workbench_physical_ViewFigureRef::can_wrap(object))
1961
workbench_physical_ViewFigureRef figure(workbench_physical_ViewFigureRef::cast_from(object));
1962
if (figure->view().is_valid())
1963
return figure->view()->comment();
1965
else if (workbench_physical_RoutineGroupFigureRef::can_wrap(object))
1967
workbench_physical_RoutineGroupFigureRef figure(workbench_physical_RoutineGroupFigureRef::cast_from(object));
1968
if (figure->routineGroup().is_valid())
1969
return figure->routineGroup()->comment();
1971
else if (workbench_physical_ConnectionRef::can_wrap(object))
1973
workbench_physical_ConnectionRef connection(workbench_physical_ConnectionRef::cast_from(object));
1975
db_ForeignKeyRef fk(connection->foreignKey());
1978
if (fk->owner().is_valid())
1980
text.append(fk->owner()->name()).append("\n");
1981
for (size_t c = fk->columns().count(), i= 0; i < c; i++)
1983
if (fk->columns()[i].is_valid())
1984
text.append(" ").append(fk->columns()[i]->name()).append("\n");
1986
text.append("???\n");
1990
text.append("<references>\n");
1992
if (fk->referencedTable().is_valid())
1994
text.append(fk->referencedTable()->name()).append("\n");
1995
for (size_t c = fk->referencedColumns().count(), i= 0; i < c; i++)
1997
if (fk->referencedColumns()[i].is_valid())
1998
text.append(" ").append(fk->referencedColumns()[i]->name()).append("\n");
2000
text.append("???\n");
2011
GrtObjectRef WBComponentPhysical::get_object_for_figure(const model_ObjectRef &object)
2013
if (workbench_physical_TableFigureRef::can_wrap(object))
2014
return workbench_physical_TableFigureRef::cast_from(object)->table();
2016
else if (workbench_physical_ViewFigureRef::can_wrap(object))
2017
return workbench_physical_ViewFigureRef::cast_from(object)->view();
2019
else if (workbench_physical_RoutineGroupFigureRef::can_wrap(object))
2020
return workbench_physical_RoutineGroupFigureRef::cast_from(object)->routineGroup();
2022
return GrtObjectRef();
2026
void WBComponentPhysical::activate_canvas_object(const model_ObjectRef &figure, bool newwindow)
2028
GrtObjectRef object(get_object_for_figure(figure));
2030
if (object.is_valid())
2031
_wb->open_object_editor(object, newwindow ? bec::ForceNewWindowFlag : bec::NoFlags);
2033
else if (workbench_physical_ConnectionRef::can_wrap(figure))
2034
_wb->open_object_editor(figure, newwindow ? bec::ForceNewWindowFlag : bec::NoFlags);
2038
//TODO sigc _blockable_listeners seened never being filled and thus there is no sence to iterate there
2039
void WBComponentPhysical::block_model_notifications()
2042
//for (std::list<sigc::connection>::iterator iter= _blockable_listeners.begin();
2043
iter != _blockable_listeners.end(); ++iter)
2049
void WBComponentPhysical::unblock_model_notifications()
2052
//for (std::list<sigc::connection>::iterator iter= _blockable_listeners.begin();
2053
iter != _blockable_listeners.end(); ++iter)
2058
//--------------------------------------------------------------------------------
2061
app_ToolbarRef WBComponentPhysical::get_tools_toolbar()
2063
return app_ToolbarRef::cast_from(_wb->get_grt()->unserialize(make_path(_wb->get_datadir(),"data/tools_toolbar_physical.xml")));
2067
app_ToolbarRef WBComponentPhysical::get_tool_options(const std::string &tool)
2069
if (_toolbars.find("options/"+tool) != _toolbars.end())
2070
return _toolbars["options/"+tool];
2071
return app_ToolbarRef();
2075
grt::ListRef<app_ShortcutItem> WBComponentPhysical::get_shortcut_items()
2081
std::vector<std::string> WBComponentPhysical::get_command_dropdown_items(const std::string &option)
2083
std::vector<std::string> items;
2084
ModelDiagramForm *form= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());
2086
if (has_prefix(option, "workbench.physical."))
2088
if (has_suffix(option, ":Color"))
2090
std::string colors= _wb->get_wb_options().get_string("workbench.model.ObjectFigure:ColorList");
2091
std::vector<std::string> colorList;
2093
colorList= base::split(colors, "\n");
2095
if (!colorList.empty())
2097
for (size_t c= colorList.size(), i= 0; i < c; i++)
2099
if (!colorList[i].empty() && colorList[i][0]=='#')
2100
items.push_back(colorList[i]);
2105
items.push_back("#98BFDA");
2106
items.push_back("#FEDE58");
2107
items.push_back("#98D8A5");
2109
items.push_back("#FE9898");
2110
items.push_back("#FE98FE");
2112
items.push_back("#FFFFFF");
2115
std::string selected= form->get_tool_argument(option);
2116
if (selected.empty())
2117
selected= _wb->get_wb_options().get_string(option);
2118
if (selected.empty())
2120
if (!selected.empty() && std::find(items.begin(), items.end(), selected) == items.end())
2121
items.push_back(selected);
2123
form->set_tool_argument(option, selected);
2125
else if (has_suffix(option, ":Schema"))
2127
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));
2129
if (model.is_valid())
2131
for (size_t c= model->catalog()->schemata().count(), i= 0; i < c; i++)
2132
items.push_back(*model->catalog()->schemata().get(i)->name());
2133
std::sort(items.begin(), items.end());
2136
std::string selected= form->get_tool_argument(option);
2137
if (!items.empty() && (selected.empty() || std::find(items.begin(), items.end(), selected) == items.end()))
2140
form->set_tool_argument(option, selected);
2142
else if (has_suffix(option, ":Engine"))
2144
items.push_back("*Default Engine*");
2146
grt::Module *module= get_grt()->get_module("DbMySQL");
2149
grt::ListRef<db_mysql_StorageEngine> engines_ret(grt::ListRef<db_mysql_StorageEngine>::cast_from(module->call_function("getKnownEngines", grt::BaseListRef(get_grt()))));
2151
for (size_t c= engines_ret.count(), i= 0; i < c; i++)
2152
items.push_back(engines_ret[i]->name());
2155
// Table engine might be model specific.
2156
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));
2157
std::string selected;
2159
_wb->get_ui()->get_wb_options_value(model.id(), "db.mysql.Table:tableEngine", selected);
2160
if (selected.empty())
2161
selected= "*Default Engine*";
2163
form->set_tool_argument(option, selected);
2165
else if (has_suffix(option, ":Collation"))
2167
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(form->get_model_diagram()));
2169
if (_collation_list.empty())
2171
items.push_back("*Default Collation*");
2172
if (model.is_valid())
2174
for (size_t c= model->catalog()->characterSets().count(), i= 0; i < c; i++)
2176
db_CharacterSetRef charset(model->catalog()->characterSets().get(i));
2178
for (size_t d= charset->collations().count(), j= 0; j < d; j++)
2179
items.push_back(*charset->collations().get(j));
2181
std::sort(items.begin(), items.end());
2183
_collation_list= items;
2186
items= _collation_list;
2188
std::string selected= form->get_tool_argument(option);
2189
if (selected.empty())
2190
selected= _wb->get_wb_options().get_string(option);
2191
if (selected.empty())
2192
selected= "*Default Collation*";
2194
form->set_tool_argument(option, selected);
2198
throw std::logic_error("Unknown option "+option);
2205
//--------------------------------------------------------------------------------
2209
void WBComponentPhysical::add_schema_listeners(const db_SchemaRef &schema)
2211
// listener for changes in schema itself
2212
_object_listeners[schema.id()]=
2213
schema->signal_changed()->connect(boost::bind(&WBComponentPhysical::schema_member_changed, this, _1, _2, schema));
2215
_schema_content_listeners[schema.id()]= schema->signal_refreshDisplay()->connect
2216
(boost::bind(&WBComponentPhysical::schema_content_object_changed, this, _1));
2218
// for changes in table, view, SP/function, routine (and other) lists
2219
_schema_list_listeners[schema.id()]= schema->signal_list_changed()->connect(
2220
boost::bind(&WBComponentPhysical::schema_object_list_changed, this, _1, _2, _3, schema));
2223
//--------------------------------------------------------------------------------------------------
2226
* Removes all previously set listeners.
2228
void WBComponentPhysical::close_document()
2231
_catalog_object_list_listener.disconnect();
2232
_model_list_listener.disconnect();
2234
// Object listeners.
2235
for (std::map<std::string, boost::signals2::connection>::iterator iter= _object_listeners.begin();
2236
iter != _object_listeners.end(); ++iter)
2237
iter->second.disconnect();
2238
_object_listeners.clear();
2240
// Schema listeners.
2241
for (std::map<std::string, boost::signals2::connection>::iterator iter= _schema_list_listeners.begin();
2242
iter != _schema_list_listeners.end(); ++iter)
2243
iter->second.disconnect();
2244
_schema_list_listeners.clear();
2246
// Figure list listeners.
2247
for (std::map<std::string, boost::signals2::connection>::iterator iter= _figure_list_listeners.begin();
2248
iter != _figure_list_listeners.end(); ++iter)
2249
iter->second.disconnect();
2250
_figure_list_listeners.clear();
2254
//--------------------------------------------------------------------------------------------------
2257
* Refreshes internal state for a document change.
2258
* This will add listeners for a newly created or opened document.
2261
void WBComponentPhysical::reset_document()
2263
workbench_DocumentRef doc(_wb->get_document());
2265
// add listener to all schemas, views etc
2266
for (size_t c= doc->physicalModels().count(), i= 0; i < c; i++)
2268
workbench_physical_ModelRef model(doc->physicalModels()[i]);
2269
if (model.is_valid())
2271
db_CatalogRef catalog(model->catalog());
2273
if (catalog.is_valid())
2274
_catalog_object_list_listener=
2275
catalog->signal_list_changed()->connect(boost::bind(&WBComponentPhysical::catalog_object_list_changed, this, _1, _2, _3, catalog));
2277
for (size_t sc= catalog->schemata().count(), si= 0; si < sc; si++)
2279
db_SchemaRef schema(catalog->schemata().get(si));
2281
add_schema_listeners(schema);
2283
// currently there are only listeners for tables
2284
grt::ListRef<db_Table> tables(schema->tables());
2285
for (size_t tc= tables.count(), ti= 0; ti < tc; ti++)
2287
add_schema_object_listeners(tables[ti]);
2291
for (size_t vi= 0, vc= model->diagrams().count(); vi < vc; vi++)
2293
model_DiagramRef view(model->diagrams().get(i));
2294
_figure_list_listeners[view.id()]=
2295
view->signal_list_changed()->connect(
2296
boost::bind(&WBComponentPhysical::view_object_list_changed, this, _1, _2, _3, view));
2299
_model_list_listener= model->signal_list_changed()->
2300
connect(boost::bind(&WBComponentPhysical::model_object_list_changed, this, _1, _2, _3));
2305
// make sure frontend is refreshed
2306
_wb->request_refresh(RefreshSchemaList, "");
2308
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
2309
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
2313
/** Called when a document is loaded
2315
- Update the userDatatypes list in the document with what we have now
2317
void WBComponentPhysical::document_loaded()
2319
grt::ListRef<workbench_physical_Model> models(_wb->get_document()->physicalModels());
2321
for (grt::ListRef<workbench_physical_Model>::const_iterator pmodel= models.begin();
2322
pmodel != models.end(); ++pmodel)
2324
grt::ListRef<db_UserDatatype> userTypes(create_builtin_user_datatypes((*pmodel)->catalog(), (*pmodel)->rdbms()));
2326
// merge in built-in UDTs with the one from the model, replacing stuff from the model with ours
2327
grt::merge_contents_by_id((*pmodel)->catalog()->userDatatypes(), userTypes, true);
2332
/** Listener for changes in the list of schemas
2334
* Used for attaching listeners to new schemas and content lists.
2336
void WBComponentPhysical::catalog_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value,
2337
const db_CatalogRef &catalog)
2339
if (grt::BaseListRef(list) == catalog->schemata())
2341
// we're called in the GRT thread, so just mark the refresh request
2342
// as pending. This has the bonus that multiple requests will be
2343
// collapsed into 1.
2344
// The requests are actually sent in flush_idle_tasks()
2346
_wb->request_refresh(RefreshSchemaList, "", 0);
2348
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_schema_list();
2353
add_schema_listeners(db_SchemaRef::cast_from(value));
2357
db_SchemaRef schema(db_SchemaRef::cast_from(value));
2359
_wb->request_refresh(RefreshCloseEditor, schema.id());
2361
// remove listeners for the schema
2362
_object_listeners[schema.id()].disconnect();
2363
_schema_list_listeners[schema.id()].disconnect();
2365
_object_listeners.erase(schema.id());
2366
_schema_list_listeners.erase(schema.id());
2370
privilege_list_changed(list, added, value, catalog);
2375
void WBComponentPhysical::schema_member_changed(const std::string &member, const grt::ValueRef &ovalue,
2376
const db_SchemaRef &schema)
2378
_wb->request_refresh(RefreshSchema, "", 0);
2380
if (_wb->get_ui()->get_physical_overview())
2381
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_for_schema(schema, true);
2385
void WBComponentPhysical::add_schema_object_listeners(const grt::ObjectRef &object)
2387
if (object.is_instance(db_Table::static_class_name()))
2389
if (_object_listeners.find(object.id()) != _object_listeners.end())
2390
_object_listeners[object.id()].disconnect();
2391
_object_listeners[object.id()]= db_TableRef::cast_from(object)->signal_foreignKeyChanged()->connect(
2392
boost::bind(&WBComponentPhysical::foreign_key_changed, this, _1));
2397
void WBComponentPhysical::schema_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, const db_SchemaRef &schema)
2399
grt::ObjectRef object(grt::ObjectRef::cast_from(value));
2403
add_schema_object_listeners(object);
2407
// remove old listeners
2408
if (object.is_instance(db_Table::static_class_name()))
2410
_object_listeners[object.id()].disconnect();
2411
_object_listeners.erase(object.id());
2413
_wb->request_refresh(RefreshCloseEditor, object.id());
2416
if (_wb->get_ui()->get_physical_overview())
2417
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_for_schema_object(GrtObjectRef::cast_from(value), false);
2419
_wb->request_refresh(RefreshSchema, "", 0);
2423
void WBComponentPhysical::view_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, const model_DiagramRef &view)
2425
if (list == view->figures().valueptr())
2427
if (handles_figure(model_ObjectRef::cast_from(value)))
2432
// if not undoing/redoing, then auto-create the connection for the table
2433
if (!get_grt()->get_undo_manager()->is_undoing() && !get_grt()->get_undo_manager()->is_redoing())
2435
if (value.is_instance(workbench_physical_TableFigure::static_class_name()))
2437
workbench_physical_TableFigureRef table(workbench_physical_TableFigureRef::cast_from(value));
2439
if (table.table().is_valid())
2441
std::vector<db_TableRef> tbls;
2442
tbls.push_back(table.table());
2444
update_connections_for_new_tables(view, tbls);
2447
get_grt()->send_warning("Table figure added to view has no table db object bound to it.");
2456
// ask for a refresh to update the "present in this view" status in Catalog tree
2457
if (_catalog_tree != NULL)
2458
_catalog_tree->refresh_for_diagram(workbench_physical_DiagramRef::cast_from(view));
2462
// make sure editor for notes and images are closed
2463
// this should be in wb_component_basic.cpp, but since there's no need for anything
2464
// else over there, just put here
2466
_wb->request_refresh(RefreshCloseEditor, grt::ObjectRef::cast_from(value).id());
2469
else if (list == view->layers().valueptr() ||
2470
list == view->connections().valueptr())
2473
_wb->request_refresh(RefreshCloseEditor, grt::ObjectRef::cast_from(value).id());
2478
static void convert_utf16_to_utf8_if_needed(char *&data, size_t &size)
2481
glong iread, iwritten;
2484
if (size >= 2 && (guchar)data[0] == 0xff && (guchar)data[1] == 0xfe)
2486
output= g_utf16_to_utf8((const gunichar2*)data, (glong) size, &iread, &iwritten, NULL);
2498
void WBComponentPhysical::model_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &avalue)
2500
if (avalue.type() == grt::ObjectType)
2504
grt::ObjectRef value(grt::ObjectRef::cast_from(avalue));
2505
std::string group, tmpl;
2507
if (value.is_instance(db_Script::static_class_name()))
2509
group= "@sqlscripts";
2510
tmpl= "script$.sql";
2512
else if (value.is_instance(GrtStoredNote::static_class_name()))
2517
else if (value.is_instance(model_Diagram::static_class_name()))
2519
model_DiagramRef view(model_DiagramRef::cast_from(value));
2521
_figure_list_listeners[view.id()]=
2522
view->signal_list_changed()->connect(
2523
boost::bind(&WBComponentPhysical::view_object_list_changed, this, _1, _2, _3, view));
2525
_wb->get_ui()->get_physical_overview()->send_refresh_diagram(model_DiagramRef());
2531
// check if the filename is in the temp dir, if so, just readd it with old contents
2532
GrtStoredNoteRef object(GrtStoredNoteRef::cast_from(value));
2536
if (object->filename() != "")
2538
if (object->filename().c_str()[0] == '@')
2542
std::string path= make_path(_wb->get_grt_manager()->get_tmp_dir(), object->filename());
2544
if (g_file_get_contents(path.c_str(), &data, &length, NULL))
2546
// we're undoing, load the file and add it back to the model file
2547
object->filename(_wb->recreate_attached_file(object->filename(), data));
2551
get_grt()->send_error(strfmt(_("Error undoing file delete: can't read contents of temporary file '%s'"), path.c_str()));
2555
// load file from scratch
2558
GError *error= NULL;
2560
if (g_file_get_contents(object->filename().c_str(), &data, &length, &error))
2562
convert_utf16_to_utf8_if_needed(data, length);
2564
object->filename(_wb->create_attached_file(group, object->filename()));
2565
_wb->save_attached_file_contents(object->filename(), data, length);
2570
get_grt()->send_error(strfmt(_("Error adding file: can't read contents of file '%s': %s"),
2571
object->filename().c_str(), error->message));
2572
g_error_free(error);
2577
object->filename(_wb->create_attached_file(group, tmpl));
2579
// request refresh of overview
2580
if (value.is_instance(db_Script::static_class_name()))
2581
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
2582
else if (value.is_instance(GrtStoredNote::static_class_name()))
2583
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
2587
grt::ObjectRef value(grt::ObjectRef::cast_from(avalue));
2589
_wb->request_refresh(RefreshCloseEditor, value.id());
2591
if (value.is_instance(GrtStoredNote::static_class_name()))
2593
GrtStoredNoteRef object(GrtStoredNoteRef::cast_from(value));
2595
if (object->filename() != "")
2597
std::string path= make_path(_wb->get_grt_manager()->get_tmp_dir(), object->filename());
2598
gchar *tmp= g_dirname(path.c_str());
2599
g_mkdir_with_parents(tmp, 0700);
2602
// save the file in the temp dir in case an undo delete is requested
2603
std::string data= _wb->get_attached_file_contents(object->filename());
2605
if (!g_file_set_contents(path.c_str(), data.data(), (gssize) data.length(), NULL))
2607
get_grt()->send_error(strfmt(_("Could not save temporary file '%s'"), path.c_str()));
2610
_wb->delete_attached_file(object->filename());
2613
// request refresh of overview
2614
if (value.is_instance(db_Script::static_class_name()))
2615
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_scripts();
2617
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_notes();
2619
else if (value.is_instance(model_Diagram::static_class_name()))
2621
std::string id= grt::ObjectRef::cast_from(value).id();
2623
_figure_list_listeners[id].disconnect();
2624
_figure_list_listeners.erase(id);
2626
_wb->get_ui()->get_physical_overview()->send_refresh_diagram(model_DiagramRef());
2633
void WBComponentPhysical::foreign_key_changed(const db_ForeignKeyRef &fk)
2635
// don't auto-create/remove stuff when undoing/redoing
2636
if (get_grt()->get_undo_manager()->is_undoing() || get_grt()->get_undo_manager()->is_redoing())
2639
bool valid= fk->checkCompleteness() != 0;
2640
grt::ListRef<workbench_physical_Diagram> views(_wb->get_document()->physicalModels()[0]->diagrams());
2642
// go through all views and create/destroy connections if needed
2643
for (grt::ListRef<workbench_physical_Diagram>::const_iterator iter= views.begin();
2644
iter != views.end(); ++iter)
2646
workbench_physical_DiagramRef view(*iter);
2647
workbench_physical_ConnectionRef conn(view->getConnectionForForeignKey(fk));
2649
if (conn.is_valid() != valid)
2651
if (!conn.is_valid())
2652
view->createConnectionForForeignKey(fk);
2654
view->removeConnection(conn);
2660
void WBComponentPhysical::schema_content_object_changed(const db_DatabaseObjectRef &object)
2662
refresh_ui_for_object(object);
2666
void WBComponentPhysical::refresh_ui_for_object(const GrtObjectRef &object)
2668
if (object.is_valid() && object->owner().is_valid())
2670
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(object));
2671
PhysicalOverviewBE *overview= (PhysicalOverviewBE*)((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview());
2673
if (overview->get_model() != model)
2674
throw std::logic_error("code is outdated");
2676
overview->send_refresh_for_schema_object(object, true);
2678
_wb->request_refresh(RefreshSchema, "" /*object.owner().id() not supported yet in fe*/, 0);
2684
****************************************************************************
2685
* @brief Add/remove connections for added/removed table foreign keys
2687
* Updates the connections from a table figure corresponding to the given
2688
* foreign key. This can be called when the list of FKs for a table is
2689
* changed or when a pre-existing table with a fk is added to a view.
2690
* It will also update existing connections in case the referenced table
2693
* @param object the db object
2694
* @param figure the model figure that was assigned to the object
2695
* @param attach whether the figure was attached or detached to the object
2697
* @return true if a change has happened (conn added/remove), false otherwise
2698
****************************************************************************
2700
bool WBComponentPhysical::update_table_fk_connection(const db_TableRef &table, const db_ForeignKeyRef &fk, bool added)
2702
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(table));
2704
if (!model.is_valid() || !model->diagrams().is_valid())
2711
// all FKs from table removed
2716
if (!fk->referencedTable().is_valid() || fk->owner() != table)
2719
// kind of a hack, this is needed because copy_object() will generate
2720
// spurious notifications. eg: in a sync of a model with a relationship,
2721
// the rel will be recreated (because of the copy)
2722
if (added && table->foreignKeys().get_index(fk) == BaseListRef::npos)
2725
grt::ListRef<workbench_physical_Diagram> views(model->diagrams());
2726
grt::GRT *grt= get_grt();
2730
// we have to go through all views in the model and find all table figures
2731
// that correspond to the FK for creating the relationship in all these views
2733
for (size_t c= views.count(), i= 0; i < c; i++)
2735
workbench_physical_DiagramRef view(views[i]);
2737
workbench_physical_TableFigureRef table1(workbench_physical_TableFigureRef::cast_from(view->getFigureForDBObject(table)));
2738
workbench_physical_TableFigureRef table2(workbench_physical_TableFigureRef::cast_from(view->getFigureForDBObject(fk->referencedTable())));
2740
if (table1.is_valid() && table2.is_valid())
2741
{ // both tables in the relationship are in this view, so create the connection
2743
// but 1st check if it already exists
2745
grt::ListRef<model_Connection> connections(view->connections());
2746
workbench_physical_ConnectionRef found;
2748
for (size_t c= connections.count(), i= 0; i < c; i++)
2750
model_ConnectionRef conn(connections[i]);
2751
if (conn.is_instance(workbench_physical_Connection::static_class_name()))
2753
workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(conn));
2755
if (pconn->foreignKey() == fk)
2763
// connection doesnt exist yet, create it
2764
if (!found.is_valid())
2766
workbench_physical_ConnectionRef conn(grt);
2769
conn->startFigure(table1);
2770
conn->endFigure(table2);
2771
conn->caption(fk->name());
2772
conn->foreignKey(fk);
2774
conn->name(grt::get_name_suggestion_for_list_object(view->connections(),
2777
grt::AutoUndo undo(get_grt());
2778
view->connections().insert(conn);
2779
undo.end(_("Create Relationship"));
2785
// connection exists, check if its correct
2786
if (workbench_physical_TableFigureRef::cast_from(found->endFigure())->table() !=
2787
found->foreignKey()->referencedTable())
2789
if (!found->foreignKey()->referencedTable().is_valid())
2791
// referencedTable was unset, so we need to remove the connection
2796
// update the connection with the new end figure
2797
grt::AutoUndo undo(get_grt());
2798
found->endFigure(view->getFigureForDBObject(fk->referencedTable()));
2799
undo.end(_("Update Relationship"));
2809
// remove all connections that correspond to the FK in all views
2811
for (grt::ListRef<workbench_physical_Diagram>::const_iterator view= views.begin();
2812
view != views.end(); ++view)
2814
workbench_physical_TableFigureRef table1(workbench_physical_TableFigureRef::cast_from((*view)->getFigureForDBObject(table)));
2815
grt::ListRef<model_Connection>::const_reverse_iterator end= (*view)->connections().rend();
2817
for (grt::ListRef<model_Connection>::const_reverse_iterator conn= (*view)->connections().rbegin();
2818
conn != end; ++conn)
2820
if ((*conn).is_instance(workbench_physical_Connection::static_class_name()))
2822
workbench_physical_ConnectionRef pconn(workbench_physical_ConnectionRef::cast_from(*conn));
2824
if (pconn->foreignKey() == fk)
2826
grt::AutoUndo undo(get_grt());
2827
(*view)->connections().remove_value(*conn);
2828
undo.end(_("Remove Relationship"));
2841
void WBComponentPhysical::activate_catalog_tree_item(const grt::ValueRef &value)
2843
if (value.is_valid() && db_DatabaseObjectRef::can_wrap(value))
2845
db_DatabaseObjectRef object(db_DatabaseObjectRef::cast_from(value));
2847
_wb->open_object_editor(object);
2852
bool WBComponentPhysical::has_figure_for_object_in_active_view(const GrtObjectRef &object, ModelDiagramForm *vform)
2855
vform= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());
2859
workbench_physical_DiagramRef view(workbench_physical_DiagramRef::cast_from(vform->get_model_diagram()));
2861
if (view->getFigureForDBObject(db_DatabaseObjectRef::cast_from(object)).is_valid())
2868
void WBComponentPhysical::update_catalog_tree()
2870
if (!_catalog_tree) return;
2872
UIForm *form= _wb->get_active_main_form();
2873
model_ModelRef model;
2874
workbench_physical_DiagramRef view;
2878
ModelDiagramForm *vform= dynamic_cast<ModelDiagramForm*>(form);
2881
view= workbench_physical_DiagramRef::cast_from(vform->get_model_diagram());
2882
model= view->owner();
2886
OverviewBE *over= dynamic_cast<OverviewBE*>(form);
2888
model= over->get_model();
2894
_catalog_tree->refresh_for_diagram(view);
2896
if (model.is_valid())
2897
_catalog_tree->set_displayed_value(workbench_physical_ModelRef::cast_from(model)->catalog()->schemata(), "");
2899
_catalog_tree->set_displayed_global_value("", true);
2901
_wb->request_refresh(RefreshSchemaList, "", 0);
2906
bec::ValueTreeBE *WBComponentPhysical::get_catalog_tree()
2910
_catalog_tree= new CatalogTreeBE(get_grt(), this);
2911
_catalog_tree->set_activate_callback(boost::bind(&WBComponentPhysical::activate_catalog_tree_item, this, _1));
2913
update_catalog_tree();
2916
return _catalog_tree;
2920
void WBComponentPhysical::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info)
2922
if (name == "GNMainFormChanged")
2924
update_catalog_tree();
2929
void WBComponentPhysical::setup_canvas_tool(ModelDiagramForm *view, const std::string &tool)
2932
bool relationship= false;
2934
if (tool == WB_TOOL_PTABLE)
2936
view->set_cursor("table");
2937
_wb->show_status_text(_("Select location for new table."));
2939
else if (tool == WB_TOOL_PVIEW)
2941
view->set_cursor("view");
2942
_wb->show_status_text(_("Select location for new view."));
2944
else if (tool == WB_TOOL_PROUTINEGROUP)
2946
view->set_cursor("routine");
2947
_wb->show_status_text(_("Select location for new routine group."));
2949
else if (tool == WB_TOOL_PREL11)
2951
view->set_cursor("rel11");
2952
data= start_relationship(view, Point(), Relationship11Id);
2955
else if (tool == WB_TOOL_PREL1n)
2957
view->set_cursor("rel1n");
2958
data= start_relationship(view, Point(), Relationship1nId);
2961
else if (tool == WB_TOOL_PRELnm)
2963
view->set_cursor("relnm");
2964
data= start_relationship(view, Point(), RelationshipnmId);
2967
else if (tool == WB_TOOL_PREL11_NOID)
2969
view->set_cursor("rel11");
2970
data= start_relationship(view, Point(), Relationship11NonId);
2973
else if (tool == WB_TOOL_PREL1n_NOID)
2975
view->set_cursor("rel1n");
2976
data= start_relationship(view, Point(), Relationship1nNonId);
2979
else if (tool == WB_TOOL_PREL_PICK)
2981
view->set_cursor("rel1n");
2982
data= start_relationship(view, Point(), RelationshipPick);
2987
_wb->show_status_text("Invalid tool "+tool);
2990
view->set_button_callback(boost::bind(&WBComponentPhysical::handle_button_event, this, _1, _2, _3, _4, _5, data));
2992
view->set_reset_tool_callback(
2993
boost::bind(&WBComponentPhysical::cancel_relationship, this, _1,
2994
reinterpret_cast<RelationshipToolContext*>(data)));
2999
bool WBComponentPhysical::handle_button_event(ModelDiagramForm *view, mdc::MouseButton button, bool press,
3000
Point pos, mdc::EventState, void *data)
3002
std::string tool= view->get_tool();
3004
if (button != mdc::ButtonLeft)
3007
mdc::CanvasItem *item;
3008
item= view->get_view()->get_item_at(pos);
3009
if (item && item->get_layer() != view->get_view()->get_current_layer())
3012
if (tool == WB_TOOL_PTABLE)
3016
place_new_db_object(view, pos, ObjectTable);
3017
view->reset_tool(true);
3021
else if (tool == WB_TOOL_PROUTINEGROUP)
3025
place_new_db_object(view, pos, ObjectRoutineGroup);
3026
view->reset_tool(true);
3030
else if (tool == WB_TOOL_PVIEW)
3034
place_new_db_object(view, pos, ObjectView);
3035
view->reset_tool(true);
3039
else if (tool == WB_TOOL_PREL11 || tool == WB_TOOL_PREL1n || tool == WB_TOOL_PRELnm ||
3040
tool == WB_TOOL_PREL11_NOID || tool == WB_TOOL_PREL1n_NOID || tool == WB_TOOL_PREL_PICK)
3044
RelationshipToolContext *rctx= reinterpret_cast<RelationshipToolContext*>(data);
3046
if (rctx->button_press(view, pos))
3047
view->reset_tool(true);
3056
//===================================================================================================
3058
//===================================================================================================
3060
static void refresh_privileges(PhysicalOverviewBE *overview)
3062
overview->send_refresh_users();
3063
overview->send_refresh_roles();
3066
void WBComponentPhysical::privilege_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value,
3067
const db_CatalogRef &catalog)
3069
if (grt::BaseListRef(list) == catalog->users())
3070
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_users();
3071
else if (grt::BaseListRef(list) == catalog->roles())
3072
((PhysicalOverviewBE*)_wb->get_ui()->get_physical_overview())->send_refresh_roles();
3075
grt::ObjectRef object(grt::ObjectRef::cast_from(value));
3076
_wb->request_refresh(RefreshCloseEditor, object.id());
3080
//--------------------------------------------------------------------------------
3081
// Privilege Management
3083
grt::ValueRef WBComponentPhysical::add_new_user_grt(grt::GRT *grt, const workbench_physical_ModelRef &model)
3085
db_CatalogRef catalog;
3088
catalog= model->catalog();
3090
std::string name= grt::get_name_suggestion_for_list_object(grt::ObjectListRef::cast_from(catalog->users()), "user");
3092
user= db_UserRef(grt);
3093
user->owner(catalog);
3096
grt::AutoUndo undo(grt);
3097
catalog->users().insert(user);
3099
undo.end(strfmt(_("Create User '%s'"), user->name().c_str()));
3105
void WBComponentPhysical::add_new_user(const workbench_physical_ModelRef &model)
3109
res= _wb->execute_in_grt_thread("Create new user",
3110
boost::bind(&WBComponentPhysical::add_new_user_grt, this, _1, model));
3113
db_UserRef user(db_UserRef::cast_from(res));
3115
_wb->show_status_text(strfmt(_("User '%s' created"), user->name().c_str()));
3117
_wb->open_object_editor(GrtObjectRef::cast_from(user));
3120
_wb->show_status_text(_("Could not create new user"));
3124
void WBComponentPhysical::remove_user(const db_UserRef &user)
3126
db_CatalogRef catalog(db_CatalogRef::cast_from(user->owner()));
3128
grt::AutoUndo undo(get_grt());
3129
catalog->users().remove_value(user);
3130
undo.end(strfmt(_("Remove User '%s'"), user->name().c_str()));
3132
_wb->show_status_text(strfmt(_("Removed user '%s'"), user->name().c_str()));
3136
grt::ValueRef WBComponentPhysical::add_new_role_grt(grt::GRT *grt, const workbench_physical_ModelRef &model)
3138
db_CatalogRef catalog;
3141
catalog= model->catalog();
3143
std::string name= grt::get_name_suggestion_for_list_object(grt::ObjectListRef::cast_from(catalog->roles()), "role");
3145
role= db_RoleRef(grt);
3146
role->owner(catalog);
3149
grt::AutoUndo undo(get_grt());
3150
catalog->roles().insert(role);
3151
undo.end(strfmt(_("Create Role '%s'"), role->name().c_str()));
3157
void WBComponentPhysical::add_new_role(const workbench_physical_ModelRef &model)
3161
res= _wb->execute_in_grt_thread("Create new role",
3162
boost::bind(&WBComponentPhysical::add_new_role_grt, this, _1, model));
3165
db_RoleRef role(db_RoleRef::cast_from(res));
3167
_wb->show_status_text(strfmt(_("Role '%s' created"), role->name().c_str()));
3169
_wb->open_object_editor(role);
3172
_wb->show_status_text(_("Could not create new role"));
3176
void WBComponentPhysical::remove_role(const db_RoleRef &role)
3178
db_CatalogRef catalog(db_CatalogRef::cast_from(role->owner()));
3180
grt::AutoUndo undo(get_grt());
3181
catalog->roles().remove_value(role);
3182
undo.end(strfmt(_("Remove Role '%s'"), role->name().c_str()));
3184
_wb->show_status_text(strfmt(_("Removed role '%s'"), role->name().c_str()));
3188
void WBComponentPhysical::remove_references_to_object(const db_DatabaseObjectRef &object)
3190
// this is called when a object is removed from the schema.
3192
// remove all role privileges assigned to the object
3193
db_CatalogRef catalog(_wb->get_parent_for_object<db_Catalog>(object));
3195
grt::ListRef<db_Role> roles(catalog->roles());
3197
grt::AutoUndo undo(get_grt());
3199
for (size_t c= roles.count(), i= 0; i < c; i++)
3201
db_RoleRef role(roles[i]);
3203
grt::ListRef<db_RolePrivilege> privs(role->privileges());
3204
std::list<db_RolePrivilegeRef> privs_to_remove;
3205
for (grt::ListRef<db_RolePrivilege>::const_reverse_iterator priv= privs.rbegin();
3206
priv != privs.rend(); ++priv)
3208
if ((*priv)->databaseObject() == object)
3210
privs_to_remove.push_back(*priv);
3213
for(std::list<db_RolePrivilegeRef>::const_iterator It = privs_to_remove.begin(); It != privs_to_remove.end(); ++It)
3214
privs.remove_value(*It);
3216
undo.end_or_cancel_if_empty(_("Remove Object Privileges"));
3219
workbench_physical_ModelRef model(_wb->get_parent_for_object<workbench_physical_Model>(object));
3220
// remove any tags that reference this object
3221
if (model.is_valid())
3223
grt::AutoUndo undo(get_grt());
3225
for (grt::ListRef<meta_Tag>::const_iterator end= model->tags().end(), tag= model->tags().begin();
3228
std::list<meta_TaggedObjectRef> tags_to_remove;
3229
for (grt::ListRef<meta_TaggedObject>::const_reverse_iterator rend= (*tag)->objects().rend(), to= (*tag)->objects().rbegin();
3232
if ((*to)->object() == object)
3234
tags_to_remove.push_back(*to);
3237
for(std::list<meta_TaggedObjectRef>::const_iterator It = tags_to_remove.begin(); It != tags_to_remove.end(); ++It)
3238
(*tag)->objects().remove_value(*It);
3243
undo.end_or_cancel_if_empty(_("Remove Tags for Object"));