backend/wbprivate/model/wb_model_diagram_form.cpp (1,199 lines of code) (raw):
/*
* Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "wb_model_diagram_form.h"
#include "workbench/wb_context.h"
#include "wb_component.h"
#include "wb_component_basic.h"
#include "canvas_floater.h"
#include "mdc_back_layer.h"
#include "mforms/form.h"
#include "model/wb_context_model.h"
#include "model/wb_layer_tree.h"
#include "grt/clipboard.h"
#include "wbcanvas/model_figure_impl.h"
#include "wbcanvas/model_layer_impl.h"
#include "wbcanvas/model_diagram_impl.h"
#include "wbcanvas/model_connection_impl.h"
#include "workbench/wb_context_ui.h"
#include "wb_physical_model_diagram_features.h"
#include "base/string_utilities.h"
#include "base/file_utilities.h"
#include "base/log.h"
#include "mforms/menubar.h"
#define DEFAULT_TOOL "basic/select"
DEFAULT_LOG_DOMAIN("ModelDiagram");
using namespace wb;
using namespace bec;
using namespace base;
static const double zoom_steps[] = { 2.0, 1.5, 1.2, 1.0, 0.95, 0.9, 0.85, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 };
//----------------------------------------------------------------------------------------------------------------------
ModelDiagramForm::ModelDiagramForm(WBComponent *owner, const model_DiagramRef &view)
: _catalog_tree(NULL),
_view(NULL),
_owner(owner),
_model_diagram(view),
_mini_view(NULL),
_menu(NULL),
_toolbar(NULL),
_tools_toolbar(NULL),
_options_toolbar(NULL) {
_drag_panning = false;
_space_panning = false;
_highlight_fks = false;
_inline_edit_context = 0;
_update_count = 0;
_layer_tree = 0;
scoped_connect(_model_diagram->signal_list_changed(),
std::bind(&ModelDiagramForm::diagram_changed, this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
_current_mouse_x = -1;
_current_mouse_y = -1;
_main_layer = 0;
_floater_layer = 0;
_badge_layer = 0;
_tool = DEFAULT_TOOL;
_paste_offset = 0;
_shortcuts = WBContextUI::get()->get_command_ui()->get_shortcuts_for_context(WB_CONTEXT_MODEL);
scoped_connect(owner->get_wb()->get_clipboard()->signal_changed(),
std::bind(&ModelDiagramForm::clipboard_changed, this));
_features = new PhysicalModelDiagramFeatures(this);
_options_toolbar = new mforms::ToolBar(mforms::OptionsToolBar);
NotificationCenter::get()->add_observer(this, "GNColorsChanged");
NotificationCenter::get()->add_observer(this, "GNMainFormChanged");
}
//----------------------------------------------------------------------------------------------------------------------
ModelDiagramForm::~ModelDiagramForm() {
NotificationCenter::get()->remove_observer(this);
_idle_node_mark.disconnect();
delete _layer_tree;
delete _options_toolbar;
delete _toolbar;
delete _tools_toolbar;
delete _menu;
delete _mini_view;
delete _features;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info) {
if (name == "GNColorsChanged") {
// Single colors or the entire color scheme changed.
update_toolbar_icons();
}
}
//----------------------------------------------------------------------------------------------------------------------
std::string ModelDiagramForm::get_form_context_name() const {
return WB_CONTEXT_MODEL;
}
//----------------------------------------------------------------------------------------------------------------------
mforms::ToolBar* ModelDiagramForm::get_toolbar() {
if (!_toolbar) {
_toolbar = WBContextUI::get()->get_command_ui()->create_toolbar("data/model_diagram_toolbar.xml");
update_toolbar_icons();
}
return _toolbar;
}
//----------------------------------------------------------------------------------------------------------------------
// Implemented in wb_sql_editor_form_ui.cpp.
extern std::string find_icon_name(std::string icon_name, bool use_win8);
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::update_toolbar_icons() {
if (_toolbar == NULL)
return; // Can happen if the diagram hasn't shown yet.
bool use_win8;
switch (base::Color::get_active_scheme()) {
case base::ColorSchemeStandardWin8:
case base::ColorSchemeStandardWin8Alternate:
use_win8 = true;
break;
default:
use_win8 = false;
}
mforms::ToolBarItem *item = _toolbar->find_item("wb.toggleSidebar");
if (item != NULL) {
item->set_icon(find_icon_name(item->get_icon(), use_win8));
item->set_alt_icon(find_icon_name(item->get_alt_icon(), use_win8));
}
item = _toolbar->find_item("wb.toggleSecondarySidebar");
if (item != NULL) {
item->set_icon(find_icon_name(item->get_icon(), use_win8));
item->set_alt_icon(find_icon_name(item->get_alt_icon(), use_win8));
}
}
//----------------------------------------------------------------------------------------------------------------------
mforms::TreeView *ModelDiagramForm::get_layer_tree() {
if (!_layer_tree) {
_layer_tree = new LayerTree(this, _model_diagram);
_layer_tree->refresh();
}
return _layer_tree;
}
//----------------------------------------------------------------------------------------------------------------------
mforms::ToolBar *ModelDiagramForm::get_tools_toolbar() {
if (!_tools_toolbar) {
_tools_toolbar = new mforms::ToolBar(mforms::ToolPickerToolBar);
app_ToolbarRef toolbar[3];
toolbar[0] = app_ToolbarRef::cast_from(
grt::GRT::get()->unserialize(base::makePath(get_wb()->get_datadir(), "data/tools_toolbar.xml")));
toolbar[1] = get_wb()->get_component_named("basic")->get_tools_toolbar();
toolbar[2] = get_wb()->get_component_named("physical")->get_tools_toolbar();
for (int t = 0; t < 3; t++) {
for (size_t c = toolbar[t]->items().count(), i = 0; i < c; i++) {
app_ToolbarItemRef titem(toolbar[t]->items()[i]);
mforms::ToolBarItem *item;
if (titem->itemType() == "separator")
item = mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem));
else {
item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
std::string iconName = *titem->icon();
std::string darkIcon = *titem->darkIcon();
if (mforms::App::get()->isDarkModeActive() && !darkIcon.empty()) {
item->set_icon(IconManager::get_instance()->get_icon_path(darkIcon));
} else {
item->set_icon(IconManager::get_instance()->get_icon_path(iconName));
}
item->set_name(*titem->accessibilityName());
item->setInternalName(base::split(*titem->command(), ":").back());
scoped_connect(item->signal_activated(), std::bind(&ModelDiagramForm::set_tool, this, item->getInternalName()));
}
std::string shortcut;
for (std::vector<WBShortcut>::const_iterator iter = _shortcuts.begin(); iter != _shortcuts.end(); ++iter) {
if (iter->command == *titem->command()) {
shortcut = iter->shortcut;
break;
}
}
if (shortcut.empty())
item->set_tooltip(titem->tooltip());
else
item->set_tooltip(strfmt("%s (Quick key - press %s)", titem->tooltip().c_str(), shortcut.c_str()));
_tools_toolbar->add_item(item);
if (item->getInternalName() == DEFAULT_TOOL)
item->set_checked(true);
}
toolbar[t]->reset_references();
}
}
return _tools_toolbar;
}
//----------------------------------------------------------------------------------------------------------------------
mforms::ToolBar* ModelDiagramForm::get_options_toolbar() {
update_options_toolbar();
return _options_toolbar;
}
//----------------------------------------------------------------------------------------------------------------------
mforms::MenuBar *ModelDiagramForm::get_menubar() {
if (!_menu) {
_menu = WBContextUI::get()->get_command_ui()->create_menubar_for_context(WB_CONTEXT_MODEL);
scoped_connect(_menu->signal_will_show(), std::bind(&ModelDiagramForm::revalidate_menu, this));
mforms::MenuItem *item = _menu->find_item("wb.edit.editSelectedFigure");
if (item)
item->add_validator(std::bind(&ModelDiagramForm::has_selection, this));
item = _menu->find_item("wb.edit.editSelectedFigureInNewWindow");
if (item)
item->add_validator(std::bind(&ModelDiagramForm::has_selection, this));
}
revalidate_menu();
return _menu;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::revalidate_menu() {
static const char *figure_notations[] = {"workbench/default", "workbench/simple", "workbench/pkonly",
"classic", "idef1x", NULL};
static const char *relationship_notations[] = {"crowsfoot", "classic", "fromcolumn", "uml", "idef1x", NULL};
if (_menu) {
bool has_selection_ = has_selection();
_menu->set_item_enabled("wb.edit.goToNextSelected", has_selection_);
_menu->set_item_enabled("wb.edit.goToPreviousSelected", has_selection_);
_menu->set_item_enabled("wb.edit.selectSimilar", has_selection_);
_menu->set_item_enabled("wb.edit.selectConnected", get_selection().count() == 1);
_menu->set_item_checked("wb.edit.toggleGridAlign",
bec::GRTManager::get()->get_app_option_int("AlignToGrid", 0) != 0);
_menu->set_item_checked("wb.edit.toggleGrid", get_diagram_options().get_int("ShowGrid", 1) != 0);
_menu->set_item_checked("wb.edit.togglePageGrid", get_diagram_options().get_int("ShowPageGrid", 1) != 0);
_menu->set_item_checked("wb.edit.toggleFKHighlight", get_diagram_options().get_int("ShowFKHighlight", 0) != 0);
std::string notation = workbench_physical_ModelRef::cast_from(get_model_diagram()->owner())->figureNotation();
for (int i = 0; figure_notations[i]; i++)
_menu->set_item_checked(strfmt("wb.view.setFigureNotation:%s", figure_notations[i]),
notation == figure_notations[i]);
notation = workbench_physical_ModelRef::cast_from(get_model_diagram()->owner())->connectionNotation();
for (int i = 0; relationship_notations[i]; i++)
_menu->set_item_checked(strfmt("wb.view.setRelationshipNotation:%s", relationship_notations[i]),
notation == relationship_notations[i]);
model_ModelRef model(get_model_diagram()->owner());
for (int i = 1; i < 10; i++) {
_menu->set_item_checked(strfmt("wb.view.setMarker:%i", i), false);
_menu->set_item_enabled(strfmt("wb.view.goToMarker:%i", i), false);
}
for (size_t c = model->markers().count(), i = 0; i < c; i++) {
_menu->set_item_checked(strfmt("wb.view.setMarker:%s", model->markers().get(i)->name().c_str()), true);
_menu->set_item_enabled(strfmt("wb.view.goToMarker:%s", model->markers().get(i)->name().c_str()), true);
}
_menu->find_item("plugins")->validate();
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::update_options_toolbar() {
app_ToolbarRef toolbar = get_wb()->get_component_named("basic")->get_tool_options(get_tool());
_options_toolbar->remove_all();
if (!toolbar.is_valid())
toolbar = get_wb()->get_component_named("physical")->get_tool_options(get_tool());
if (!toolbar.is_valid())
return;
for (size_t c = toolbar->items().count(), i = 0; i < c; i++) {
app_ToolbarItemRef titem(toolbar->items()[i]);
mforms::ToolBarItem *item;
std::string type = titem->itemType();
if (type == "label") {
item = mforms::manage(new mforms::ToolBarItem(mforms::LabelItem));
item->set_text(*titem->command());
} else if (type == "dropdown") {
std::string selected;
std::vector<std::string> items(get_dropdown_items(titem->name(), titem->command(), selected));
if (!items.empty() && !items.front().empty() && items.front()[0] == '#')
item = mforms::manage(new mforms::ToolBarItem(mforms::ColorSelectorItem));
else
item = mforms::manage(new mforms::ToolBarItem(mforms::SelectorItem));
item->set_selector_items(items);
item->set_text(selected);
scoped_connect(item->signal_activated(),
std::bind(&ModelDiagramForm::select_dropdown_item, this, titem->command(), item));
} else if (type == "separator")
item = mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem));
else if (type == "action") {
item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
} else if (type == "check") {
item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
// icon text is used as label for check items
item->set_text(IconManager::get_instance()->get_icon_path(*titem->icon()));
} else if (type == "toggle") {
item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
} else
continue;
if (!titem->icon().empty())
item->set_icon(IconManager::get_instance()->get_icon_path(*titem->icon()));
if (!titem->altIcon().empty())
item->set_alt_icon(IconManager::get_instance()->get_icon_path(*titem->altIcon()));
item->set_name(titem->accessibilityName());
item->setInternalName(titem->name());
item->set_tooltip(titem->tooltip());
_options_toolbar->add_item(item);
}
// don't reset refs here, only do it when closing, since the toolbar object will be used later
// toolbar->reset_references();
}
//----------------------------------------------------------------------------------------------------------------------
std::vector<std::string> ModelDiagramForm::get_dropdown_items(const std::string &name, const std::string &option,
std::string &selected) {
std::vector<std::string> items;
WBComponent *compo;
compo = get_wb()->get_component_named(base::split(name, "/")[0]);
if (compo) {
std::string::size_type p = option.find(':');
if (p != std::string::npos) {
std::string option_name = option.substr(p + 1);
items = compo->get_command_dropdown_items(option_name);
selected = compo->get_command_option_value(option_name);
}
}
return items;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::select_dropdown_item(const std::string &option, mforms::ToolBarItem *item) {
WBComponent *compo;
compo = get_wb()->get_component_named(base::split(item->getInternalName(), "/")[0]);
if (compo) {
std::string::size_type p = option.find(':');
if (p != std::string::npos) {
std::string option_name = option.substr(p + 1);
compo->set_command_option_value(option_name, item->get_text());
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::toggle_checkbox_item(const std::string &name, const std::string &option, bool state) {
WBComponent *compo;
compo = get_wb()->get_component_named(base::split(name, "/")[0]);
if (compo) {
std::string::size_type p = option.find(':');
if (p != std::string::npos) {
std::string option_name = option.substr(p + 1);
compo->set_command_option_value(option, state ? "1" : "0");
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::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));
bec::GRTManager::get()->open_object_editor(object);
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::selection_changed() {
get_wb()->request_refresh(RefreshSelection, "", 0);
if (bec::GRTManager::get()->in_main_thread())
revalidate_menu();
else
bec::GRTManager::get()->run_once_when_idle(std::bind(&ModelDiagramForm::revalidate_menu, this));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::diagram_changed(grt::internal::OwnedList *olist, bool added, const grt::ValueRef &val) {
_idle_node_mark.disconnect();
if (added)
_idle_node_mark =
bec::GRTManager::get()->run_once_when_idle(std::bind(&ModelDiagramForm::mark_catalog_node, this, val, true));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::mark_catalog_node(grt::ValueRef val, bool mark) {
if (model_ObjectRef::can_wrap(val)) {
model_ObjectRef f(model_ObjectRef::cast_from(val));
if (f.is_valid())
_catalog_tree->mark_node(_owner->get_object_for_figure(f), mark);
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::attach_canvas_view(mdc::CanvasView *cview) {
_view = cview;
cview->set_tag(_model_diagram.id());
cview->set_grid_snapping(bec::GRTManager::get()->get_app_option_int("AlignToGrid", 0) != 0);
cview->get_background_layer()->set_grid_visible(_model_diagram->options().get_int("ShowGrid", 1) != 0);
cview->get_background_layer()->set_paper_visible(_model_diagram->options().get_int("ShowPageGrid", 1) != 0);
scoped_connect(cview->get_selection()->signal_begin_dragging(),
std::bind(&ModelDiagramForm::begin_selection_drag, this));
scoped_connect(cview->get_selection()->signal_end_dragging(), std::bind(&ModelDiagramForm::end_selection_drag, this));
scoped_connect(_model_diagram->get_data()->signal_selection_changed(),
std::bind(&ModelDiagramForm::selection_changed, this));
_main_layer = _view->get_current_layer();
_badge_layer = _view->new_layer("badges");
_floater_layer = _view->new_layer("floater");
// force updates
selection_changed();
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::close() {
set_closed(true);
_mini_view->set_active_view(NULL, model_DiagramRef());
if (_mini_view != 0) {
delete _mini_view;
_mini_view = 0;
}
_model_diagram->get_data()->unrealize();
}
//----------------------------------------------------------------------------------------------------------------------
CatalogTreeView *ModelDiagramForm::get_catalog_tree() {
if (_catalog_tree == NULL) {
_catalog_tree = new CatalogTreeView(this);
_catalog_tree->set_activate_callback(
std::bind(&ModelDiagramForm::activate_catalog_tree_item, this, std::placeholders::_1));
}
return _catalog_tree;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::notify_catalog_tree(const CatalogNodeNotificationType ¬ify_type, grt::ValueRef value) {
_idle_node_mark.disconnect(); // if there is pending mark_node, disable it
if (_catalog_tree) {
switch (notify_type) {
case wb::NodeUnmark:
_catalog_tree->mark_node(value, false);
break;
case wb::NodeAddUpdate:
_catalog_tree->add_update_node_caption(value);
break;
case wb::NodeDelete:
_catalog_tree->remove_node(value);
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::refill_catalog_tree() {
if (!_catalog_tree) {
_catalog_tree = new CatalogTreeView(this);
_catalog_tree->set_activate_callback(
std::bind(&ModelDiagramForm::activate_catalog_tree_item, this, std::placeholders::_1));
}
_catalog_tree->refill(true);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_closed(bool flag) {
if (_model_diagram.is_valid())
_model_diagram->closed(flag != 0);
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::is_closed() {
return *_model_diagram->closed() != 0;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_button_callback(
const std::function<bool(ModelDiagramForm *, mdc::MouseButton, bool, Point, mdc::EventState)> &cb) {
_handle_button = cb;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_motion_callback(const std::function<bool(ModelDiagramForm *, Point, mdc::EventState)> &cb) {
_handle_motion = cb;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_reset_tool_callback(const std::function<void(ModelDiagramForm *)> &cb) {
_reset_tool = cb;
}
//----------------------------------------------------------------------------------------------------------------------
/**
* Enables or disables zooming via shortcut and mouse click (similar to Adobe Photoshop).
*
* @param enable If true the tool is enabled otherwise the previous tool is restored.
* @param zoomin If true mouse clicks will zoom into the diagram otherwise zoom out.
*/
void ModelDiagramForm::enable_zoom_click(bool enable, bool zoomin) {
if (!enable) {
_reset_tool(this);
_tool = _old_tool;
_cursor = _old_cursor;
_reset_tool = _old_reset_tool;
_handle_button = _old_handle_button;
_handle_motion = _old_handle_motion;
set_tool(_tool);
return;
}
_old_tool = _tool;
if (zoomin)
_tool = WB_TOOL_ZOOM_IN;
else
_tool = WB_TOOL_ZOOM_OUT;
_old_reset_tool = _reset_tool;
_old_handle_button = _handle_button;
_old_handle_motion = _handle_motion;
_old_cursor = _cursor;
WBComponent *compo = _owner->get_wb()->get_component_named("basic");
compo->setup_canvas_tool(this, _tool);
set_tool(_tool);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::enable_panning(bool flag) {
if (flag) {
_old_tool = _tool;
_old_reset_tool = _reset_tool;
_old_handle_button = _handle_button;
_old_handle_motion = _handle_motion;
_old_cursor = _cursor;
_tool = WB_TOOL_HAND;
WBComponent *compo = _owner->get_wb()->get_component_named("basic");
compo->setup_canvas_tool(this, _tool);
set_tool(_tool);
} else {
_reset_tool(this);
_tool = _old_tool;
_cursor = _old_cursor;
_reset_tool = _old_reset_tool;
_handle_button = _old_handle_button;
_handle_motion = _old_handle_motion;
set_tool(_tool);
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::begin_selection_drag() {
mdc::Selection::ContentType selection(_view->get_selection()->get_contents());
mdc::AreaGroup *root = _view->get_current_layer()->get_root_area_group();
// save current position of everything before dragging around objects
// so that we can store that for undo
{
_old_positions.clear();
grt::ListRef<model_Object> sel(_model_diagram->selection());
for (size_t c = sel.count(), i = 0; i < c; i++) {
if (sel[i]->is_instance(model_Figure::static_class_name())) {
model_FigureRef figure(model_FigureRef::cast_from(sel[i]));
_old_positions[figure.valueptr()].pos = Point(figure->left(), figure->top());
_old_positions[figure.valueptr()].layer_id = figure->layer().id();
} else if (sel[i]->is_instance(model_Layer::static_class_name())) {
model_LayerRef layer(model_LayerRef::cast_from(sel[i]));
_old_positions[layer.valueptr()].pos = Point(layer->left(), layer->top());
}
}
}
// remove objects from respective layers so they can be freely dragged
for (mdc::Selection::ContentType::const_iterator iter = selection.begin(); iter != selection.end(); ++iter) {
// if the layer of the figure is also selected, then leave the figure alone
Point rpos = (*iter)->get_root_position();
if (root != (*iter)->get_parent() && !(*iter)->get_parent()->get_selected()) {
root->add(*iter);
(*iter)->move_to(rpos);
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::end_selection_drag() {
std::string name;
bool moved = false;
int count = 0;
grt::UndoManager *um = grt::GRT::get()->get_undo_manager();
um->begin_undo_group();
// save original position of objects into undo record
grt::ListRef<model_Object> sel(_model_diagram->selection());
for (size_t c = sel.count(), i = 0; i < c; i++) {
if (sel[i]->is_instance(model_Figure::static_class_name())) {
model_FigureRef figure(model_FigureRef::cast_from(sel[i]));
{
Point p = _old_positions[figure.valueptr()].pos;
std::string layer_id = _old_positions[figure.valueptr()].layer_id;
if (*figure->left() != p.x || *figure->top() != p.y || layer_id != figure->layer().id()) {
moved = true;
um->add_undo(new grt::UndoObjectChangeAction(figure, "left", grt::DoubleRef(p.x)));
um->add_undo(new grt::UndoObjectChangeAction(figure, "top", grt::DoubleRef(p.y)));
}
}
name = figure->name();
count++;
} else if (sel[i]->is_instance(model_Layer::static_class_name())) {
model_LayerRef layer(model_LayerRef::cast_from(sel[i]));
{
Point p = _old_positions[layer.valueptr()].pos;
if (*layer->left() != p.x || *layer->top() != p.y) {
moved = true;
um->add_undo(new grt::UndoObjectChangeAction(layer, "left", grt::DoubleRef(p.x)));
um->add_undo(new grt::UndoObjectChangeAction(layer, "top", grt::DoubleRef(p.y)));
}
}
name = layer->name();
count++;
}
}
// change the layer of moved stuff
{
grt::AutoUndo undo;
if (relocate_figures()) {
moved = true;
undo.end("Update Object Layers");
} else
undo.cancel();
}
if (!moved) {
um->cancel_undo_group();
return;
}
if (!name.empty() && count == 1)
um->end_undo_group(strfmt(_("Move %s"), name.c_str()));
else
um->end_undo_group(_("Move Objects"));
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::current_mouse_position(int &x, int &y) {
int w, h;
_view->get_view_size(w, h);
x = _current_mouse_x;
y = _current_mouse_y;
if (x < 0 || y < 0 || x >= w || y >= h)
return false;
return true;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::current_mouse_position(Point &pos) {
int x, y;
bool inside = current_mouse_position(x, y);
pos = _view->window_to_canvas(x, y);
return inside;
}
//----------------------------------------------------------------------------------------------------------------------
mdc::CanvasItem *ModelDiagramForm::get_leaf_item_at(const Point &pos) {
return _view->get_leaf_item_at(pos);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::handle_mouse_button(mdc::MouseButton button, bool press, int x, int y, mdc::EventState state) {
if (_features)
_features->tooltip_cancel();
stop_editing();
Point pos(_view->window_to_canvas(x, y));
if (button == mdc::ButtonRight && !press) {
model_ObjectRef object = get_object_at(pos);
bec::MenuItemList items;
// If the user clicks in a selected object or in the view just show popup.
// If user clicks in an unselected object, select it and show popup.
if (object.is_valid() && _model_diagram->selection().get_index(object) == grt::BaseListRef::npos)
_view->get_selection()->set(_view->get_item_at(pos));
{
std::list<std::string> groups;
groups.push_back("Catalog/*");
groups.push_back("Model/*");
get_wb()->get_model_context()->get_object_list_popup_items(
this, std::vector<bec::NodeId>(), grt::ListRef<GrtObject>::cast_from(_model_diagram->selection()),
get_edit_target_name(), groups, items);
}
// else
//_owner->get_model_popup_items(items);
if (!items.empty()) {
int x, y;
_view->canvas_to_window(pos, x, y);
_context_menu.clear();
_context_menu.add_items_from_list(items);
_context_menu.set_handler(
[](const std::string &str) { wb::WBContextUI::get()->get_command_ui()->activate_command(str); });
_context_menu.popup_at(NULL, x, y);
}
return;
}
// temporarily select Hand tool when middle-button-dragging
if (button == mdc::ButtonMiddle) {
if (press && !_drag_panning && !_space_panning) {
_drag_panning = true;
enable_panning(true);
} else if (!press && _drag_panning) {
_drag_panning = false;
enable_panning(false);
}
}
if (press && button == mdc::ButtonLeft) {
// Handle zoom clicks.
if (_tool == WB_TOOL_ZOOM_IN) {
zoom_in();
return;
} else if (_tool == WB_TOOL_ZOOM_OUT) {
zoom_out();
return;
}
}
if (button != mdc::ButtonLeft && button != mdc::ButtonMiddle)
return;
if (_handle_button && _handle_button(this, button, press, pos, state))
return;
_view->handle_mouse_button(button, press, x, y, state);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::handle_mouse_double_click(mdc::MouseButton button, int x, int y, mdc::EventState state) {
stop_editing();
if (button != mdc::ButtonLeft && button != mdc::ButtonMiddle)
return;
_view->handle_mouse_double_click(button, x, y, state);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::handle_mouse_move(int x, int y, mdc::EventState state) {
Point pos(_view->window_to_canvas(x, y));
_current_mouse_x = x;
_current_mouse_y = y;
if (_handle_motion && _handle_motion(this, pos, state))
return;
_view->handle_mouse_move(x, y, state);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::handle_mouse_leave(int x, int y, mdc::EventState state) {
_view->handle_mouse_leave(x, y, state);
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::handle_key(const mdc::KeyInfo &key, bool press, mdc::EventState state) {
if (press) {
// cancel tooltip on keypress
if (_features)
_features->tooltip_cancel();
for (std::vector<WBShortcut>::const_iterator iter = _shortcuts.begin(); iter != _shortcuts.end(); ++iter) {
if (iter->modifiers == state && iter->key == key) {
if (iter->command.find("tool:") == 0)
set_tool(iter->command.substr(strlen("tool:")));
else if (iter->command == "zoomin")
zoom_in();
else if (iter->command == "zoomout")
zoom_out();
else if (iter->command == "zoomdefault")
set_zoom(1);
else
wb::WBContextUI::get()->get_command_ui()->activate_command(iter->command);
return true;
}
}
if (key.keycode == mdc::KSpace) {
if (state == 0) {
if (!_drag_panning && !_space_panning) {
_space_panning = true;
enable_panning(true);
}
} else if (_tool != WB_TOOL_ZOOM_IN && _tool != WB_TOOL_ZOOM_OUT) {
if (state == mdc::SControlMask) {
enable_zoom_click(true, true);
return true;
} else if (state == mdc::SAltMask) {
enable_zoom_click(true, false);
return true;
}
} else
return true; // Still return true to tell the caller we consumed the input
// (even though only the first appearance, but if we don't it is as if
// we hadn't consumed it at all).
}
if (key.keycode == mdc::KDelete) {
delete_selection();
return true;
}
} else {
// Key release.
if (_space_panning) {
_space_panning = false;
enable_panning(false);
}
if (_tool == WB_TOOL_ZOOM_IN || _tool == WB_TOOL_ZOOM_OUT)
enable_zoom_click(false, false);
}
return _view->handle_key(key, press, state);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_tool(std::string tool) {
if (_tool != DEFAULT_TOOL)
reset_tool(false);
_tool = tool;
WBComponent *compo = _owner->get_wb()->get_component_named(base::split(tool, "/")[0]);
if (compo)
compo->setup_canvas_tool(this, tool);
else
throw std::runtime_error("Invalid tool " + tool);
std::vector<mforms::ToolBarItem *> items = _tools_toolbar->get_items();
for (std::vector<mforms::ToolBarItem *>::iterator item = items.begin(); item != items.end(); ++item) {
if ((*item)->get_type() == mforms::ToggleItem) {
if ((*item)->getInternalName() == _tool)
(*item)->set_checked(true);
else
(*item)->set_checked(false);
}
}
if (_owner->get_wb()->_frontendCallbacks->tool_changed)
_owner->get_wb()->_frontendCallbacks->tool_changed(_view);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::reset_tool(bool notify) {
if (_tools_toolbar) {
mforms::ToolBarItem *item = _tools_toolbar->find_item(_tool);
if (!_tool.empty() && item)
item->set_checked(false);
item = _tools_toolbar->find_item(DEFAULT_TOOL);
if (item)
item->set_checked(true);
}
_tool = DEFAULT_TOOL;
if (_reset_tool)
_reset_tool(this);
_cursor = "";
std::function<bool()> f = []() { return false; };
_handle_button = (std::bind(f));
_handle_motion = (std::bind(f));
_reset_tool = (std::bind(f));
if (notify && _owner->get_wb()->_frontendCallbacks->tool_changed)
_owner->get_wb()->_frontendCallbacks->tool_changed(_view);
}
std::string ModelDiagramForm::get_tool_argument(const std::string &option) {
return _tool_args[option];
}
//----------------------------------------------------------------------------------------------------------------------
/**
* @brief Sets options for the selected tool
*
* Change the value of an option for the currently selected tool.
*
* @param option name of the option
* @param value value of the option
*/
void ModelDiagramForm::set_tool_argument(const std::string &option, const std::string &value) {
_tool_args[option] = value;
_tool_argument_changed(option);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_zoom(double zoom) {
_model_diagram->zoom(zoom);
}
//----------------------------------------------------------------------------------------------------------------------
double ModelDiagramForm::get_zoom() {
return _model_diagram->zoom();
}
//----------------------------------------------------------------------------------------------------------------------
bec::Clipboard *ModelDiagramForm::get_clipboard() {
return bec::GRTManager::get()->get_clipboard();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_undo() {
return grt::GRT::get()->get_undo_manager()->can_undo();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_redo() {
return grt::GRT::get()->get_undo_manager()->can_redo();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_copy() {
return get_copiable_selection().count() > 0;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_select_all() {
return true;
}
//----------------------------------------------------------------------------------------------------------------------
static void check_if_can_paste(WBComponent *compo, const grt::ObjectRef &object, bool *result) {
if (compo->can_paste_object(object))
*result = true;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_paste() {
std::list<grt::ObjectRef> data(get_clipboard()->get_data());
WBContext *wb = _owner->get_wb();
for (std::list<grt::ObjectRef>::iterator iter = data.begin(); iter != data.end(); ++iter) {
if (!(*iter).is_valid()) {
logWarning("copy buffer has null value\n");
return false;
}
bool result = false;
wb->foreach_component(std::bind(check_if_can_paste, std::placeholders::_1, *iter, &result));
if (!result)
return false;
}
return !get_clipboard()->empty();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::can_delete() {
return has_selection();
}
std::string ModelDiagramForm::get_edit_target_name() {
grt::ListRef<model_Object> sel(get_copiable_selection());
if (sel.count() == 0)
return "";
if (sel.count() == 1) {
std::string name;
name = sel[0]->name().c_str();
if (name.empty() && sel[0]->has_member("caption"))
name = sel[0]->get_string_member("caption");
return strfmt("'%s'", name.c_str());
} else
return strfmt(_("%i Selected Figures"), (int)sel.count());
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::undo() {
grt::GRT::get()->get_undo_manager()->undo();
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::redo() {
grt::GRT::get()->get_undo_manager()->redo();
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::cut() {
grt::UndoManager *um = grt::GRT::get()->get_undo_manager();
std::string edit_target_name = get_edit_target_name();
um->begin_undo_group();
copy();
int count = (int)get_copiable_selection().count();
remove_selection();
um->end_undo_group();
um->set_action_description(strfmt(_("Cut %s"), edit_target_name.c_str()));
_owner->get_wb()->_frontendCallbacks->show_status_text(strfmt(_("%i figure(s) cut."), count));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::copy() {
// object must be duplicated on copy because if the object is edited after copy
// whatever is pasted should still be in the same state as it was on copy
grt::ListRef<model_Object> selection(get_copiable_selection());
bec::Clipboard *clip = get_clipboard();
int count = 0;
grt::CopyContext copy_context;
clip->clear();
for (size_t c = selection.count(), i = 0; i < c; i++) {
WBComponent *compo = _owner->get_wb()->get_component_handling(selection.get(i));
if (compo) {
compo->copy_object_to_clipboard(selection.get(i), copy_context);
count++;
}
}
clip->set_content_description(get_edit_target_name());
copy_context.finish();
clip->changed();
_owner->get_wb()->_frontendCallbacks->show_status_text(strfmt(_("%i object(s) copied."), count));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::clipboard_changed() {
_paste_offset = 0;
}
//----------------------------------------------------------------------------------------------------------------------
static void get_component_that_can_paste(WBComponent *compo, const grt::ObjectRef &object, WBComponent **result) {
if (compo->can_paste_object(object))
*result = compo;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::paste() {
// Prevent too many updates.
UpdateLock lock(this);
WBContext *wb = _owner->get_wb();
int count = 0;
int duplicated = 0;
_paste_offset += 20;
_owner->get_wb()->_frontendCallbacks->show_status_text(_("Pasting figures..."));
grt::CopyContext context;
grt::AutoUndo undo;
_model_diagram->unselectAll();
std::list<grt::ObjectRef> data(get_clipboard()->get_data());
// find what objects are in layers so that if they are also selected individually, they don't
// get copied again
std::set<std::string> skip_objects;
for (std::list<grt::ObjectRef>::iterator iter = data.begin(); iter != data.end(); ++iter) {
if (model_LayerRef::can_wrap(*iter)) {
grt::ListRef<model_Figure> figures(model_LayerRef::cast_from(*iter)->figures());
GRTLIST_FOREACH(model_Figure, figures, fig) {
skip_objects.insert((*fig)->id());
}
}
}
for (std::list<grt::ObjectRef>::iterator iter = data.begin(); iter != data.end(); ++iter) {
// paste the object
WBComponent *compo = 0;
wb->foreach_component(std::bind(get_component_that_can_paste, std::placeholders::_1, *iter, &compo));
if (compo) {
if (skip_objects.find((*iter)->id()) == skip_objects.end()) {
compo->paste_object(this, *iter, context);
count++;
}
} else
logWarning("Don't know how to paste %s\n", iter->class_name().c_str());
}
context.finish();
undo.end(strfmt(_("Paste %s"), get_clipboard()->get_content_description().c_str()));
if (duplicated == 0)
_owner->get_wb()->_frontendCallbacks->show_status_text(strfmt(_("%i figure(s) pasted."), count));
else
_owner->get_wb()->_frontendCallbacks->show_status_text(
strfmt(_("%i figure(s) pasted, %i duplicated."), count, duplicated));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::select_all() {
for (size_t c = get_model_diagram()->figures().count(), i = 0; i < c; i++)
get_model_diagram()->selectObject(get_model_diagram()->figures().get(i));
for (size_t c = get_model_diagram()->layers().count(), i = 0; i < c; i++)
get_model_diagram()->selectObject(get_model_diagram()->layers().get(i));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::remove_selection(bool deleteSelection) {
grt::UndoManager *um = grt::GRT::get()->get_undo_manager();
grt::ListRef<model_Object> selection = get_selection();
std::vector<model_ObjectRef> objects;
std::string edit_target_name = get_edit_target_name();
um->begin_undo_group();
for (size_t c = selection.count(), i = 0; i < c; i++) {
if (selection.get(i).is_instance(model_Object::static_class_name()))
objects.push_back(model_ObjectRef::cast_from(selection.get(i)));
}
std::string actionDescription;
std::string statusText;
if (deleteSelection) {
for (size_t c = objects.size(), i = 0; i < c; i++)
_owner->get_wb()->get_model_context()->delete_object(objects[i]);
actionDescription = strfmt(_("Delete %s"), edit_target_name.c_str());
statusText = strfmt(_("%i object(s) deleted."), (int)objects.size());
} else {
for (size_t c = objects.size(), i = 0; i < c; i++)
_owner->get_wb()->get_model_context()->remove_figure(objects[i]);
actionDescription = strfmt(_("Remove %s"), edit_target_name.c_str());
statusText = strfmt(_("%i figure(s) removed. The corresponding DB objects were kept."), (int)objects.size());
}
um->end_undo_group();
um->set_action_description(actionDescription);
_owner->get_wb()->_frontendCallbacks->show_status_text(statusText);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::delete_selection() {
remove_selection(true);
}
//----------------------------------------------------------------------------------------------------------------------
std::string ModelDiagramForm::get_diagram_info_text() {
if (_model_diagram.is_valid())
return strfmt("%i x %i mm", (int)*_model_diagram->width(), (int)*_model_diagram->height());
return "";
}
//----------------------------------------------------------------------------------------------------------------------
std::vector<std::string> ModelDiagramForm::get_accepted_drop_types() {
std::vector<std::string> vec;
vec.push_back(WB_DBOBJECT_DRAG_TYPE);
return vec;
}
//----------------------------------------------------------------------------------------------------------------------
grt::ListRef<model_Object> ModelDiagramForm::get_selection() {
return _model_diagram->selection();
}
//----------------------------------------------------------------------------------------------------------------------
grt::ListRef<model_Object> ModelDiagramForm::get_copiable_selection() {
grt::ListRef<model_Object> sel(_model_diagram->selection());
grt::ListRef<model_Object> copiable(true);
for (size_t c = sel.count(), i = 0; i < c; i++) {
if (!sel.get(i).is_instance(model_Connection::static_class_name()))
copiable.insert(sel[i]);
}
return copiable;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::has_selection() {
return _model_diagram->selection().count() > 0;
}
//----------------------------------------------------------------------------------------------------------------------
static mdc::CanvasItem *extractItem(const model_ObjectRef &object) {
mdc::CanvasItem *item = nullptr;
if (object.is_instance(model_Figure::static_class_name())) {
item = model_FigureRef::cast_from(object)->get_data()->get_canvas_item();
} else if (object.is_instance(model_Connection::static_class_name())) {
item = model_ConnectionRef::cast_from(object)->get_data()->get_canvas_item();
} else if (object.is_instance(model_Layer::static_class_name())) {
item = model_LayerRef::cast_from(object)->get_data()->get_area_group();
} else {
logWarning("Unhandled CanvasItem: %s\n", object.class_name().c_str());
}
return item;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::is_visible(const model_ObjectRef &object, bool partially) {
mdc::CanvasItem *item = extractItem(object);
if (item == nullptr)
return false;
Rect bounds = item->get_root_bounds();
Rect viewport = _view->get_viewport();
if (partially)
return mdc::bounds_intersect(viewport, bounds);
else
return mdc::bounds_contain_bounds(viewport, bounds);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::focus_and_make_visible(const model_ObjectRef &object, bool select) {
mdc::CanvasItem *item = extractItem(object);
if (item) {
mdc::CanvasView *view = item->get_view();
Rect bounds = item->get_root_bounds();
Rect viewport = view->get_viewport();
if (!mdc::bounds_contain_bounds(viewport, bounds)) {
Point offset = viewport.pos;
// get the offset that will move the viewport the least to bring the item inside it
if (bounds.left() < viewport.left())
offset.x = bounds.left() - 20;
else if (bounds.right() > viewport.right())
offset.x = bounds.right() - viewport.width();
if (bounds.top() < viewport.top())
offset.y = bounds.top() - 20;
else if (bounds.bottom() > viewport.bottom())
offset.y = bounds.bottom() - viewport.height();
view->set_offset(offset);
}
view->focus_item(item);
if (select)
view->get_selection()->set(item);
}
}
//----------------------------------------------------------------------------------------------------------------------
static model_ObjectRef search_object_list(const grt::ListRef<model_Object> &objects, size_t starting_index,
const std::string &text) {
for (size_t count = objects.count(), i = starting_index; i < count; i++) {
model_ObjectRef object(objects[i]);
if (strstr(object->name().c_str(), text.c_str()))
return object;
}
return model_ObjectRef();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::search_and_focus_object(const std::string &text) {
if (text.empty())
return false;
grt::ListRef<model_Object> selection(get_selection());
model_ObjectRef selected, found_object;
if (selection.count() > 0)
selected = selection[0];
if (!selected.is_valid() || selected.is_instance(model_Figure::static_class_name())) {
size_t index = 0;
if (selected.is_valid()) {
index = _model_diagram->figures().get_index(selected);
if (index == grt::BaseListRef::npos)
index = 0;
else
index++;
}
found_object = search_object_list(_model_diagram->figures(), index, text);
}
if (!found_object.is_valid() && (!selected.is_valid() || selected.is_instance(model_Connection::static_class_name()))) {
size_t index = 0;
if (selected.is_valid()) {
index = _model_diagram->connections().get_index(selected);
if (index == grt::BaseListRef::npos)
index = 0;
else
index++;
}
found_object = search_object_list(_model_diagram->connections(), index, text);
}
if (!found_object.is_valid() && (!selected.is_valid() || selected.is_instance(model_Layer::static_class_name()))) {
size_t index = 0;
if (selected.is_valid()) {
index = _model_diagram->layers().get_index(selected);
if (index == grt::BaseListRef::npos)
index = 0;
else
index++;
}
found_object = search_object_list(_model_diagram->layers(), index, text);
}
if (found_object.is_valid()) {
bec::GRTManager::get()->replace_status_text(strfmt(_("Found %s '%s'"),
found_object.get_metaclass()->get_attribute("caption").c_str(),
found_object->name().c_str()));
focus_and_make_visible(found_object, true);
return true;
} else {
if (_model_diagram->selection().count() > 0)
bec::GRTManager::get()->replace_status_text(_("No more matches"));
else
bec::GRTManager::get()->replace_status_text(_("No match found"));
}
_model_diagram->selection().remove_all();
return false;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_cursor(const std::string &cursor) {
_cursor = cursor;
}
//----------------------------------------------------------------------------------------------------------------------
model_LayerRef ModelDiagramForm::get_layer_at(const Point &pos, Point &offset) {
model_LayerRef layer;
mdc::AreaGroup *ag = 0;
mdc::CanvasItem *item = _view->get_item_at(pos);
if (!item) {
offset = pos;
return _model_diagram->rootLayer();
}
item = item->get_toplevel();
while (item && (ag = dynamic_cast<mdc::AreaGroup *>(item)) == 0)
item = item->get_parent();
if (ag) {
for (size_t c = _model_diagram->layers().count(), i = 0; i < c; i++) {
layer = _model_diagram->layers().get(i);
if (ag == layer->get_data()->get_area_group()) {
offset = ag->convert_point_from(pos, 0);
return layer;
}
}
}
offset = pos;
return _model_diagram->rootLayer();
}
//----------------------------------------------------------------------------------------------------------------------
model_LayerRef ModelDiagramForm::get_layer_bounding(const Rect &rect, Point &offset) {
grt::ListRef<model_Layer> layers(_model_diagram->layers());
for (grt::ListRef<model_Layer>::const_reverse_iterator layer = layers.rbegin(); layer != layers.rend(); ++layer) {
if ((*layer)->get_data()->get_area_group() &&
mdc::bounds_contain_bounds((*layer)->get_data()->get_area_group()->get_root_bounds(), rect)) {
return *layer;
}
}
return model_LayerRef();
}
//----------------------------------------------------------------------------------------------------------------------
model_ObjectRef ModelDiagramForm::get_object_at(const Point &pos) {
mdc::CanvasItem *item = _view->get_item_at(pos);
if (!item)
return model_ObjectRef();
std::string id = item->get_tag();
if (id.empty())
return model_ObjectRef();
model_ObjectRef object;
object = grt::find_object_in_list(_model_diagram->figures(), id);
if (object.is_valid())
return object;
object = grt::find_object_in_list(_model_diagram->layers(), id);
if (object.is_valid())
return object;
object = grt::find_object_in_list(_model_diagram->connections(), id);
if (object.is_valid())
return object;
return model_ObjectRef();
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::relocate_figures() {
bool relocated = false;
grt::ListRef<model_Figure> figures(_model_diagram->figures());
// relocate all figures in the diagram to the layers they're inside
for (size_t c = figures.count(), i = 0; i < c; i++) {
model_FigureRef figure(figures[i]);
relocated |= _model_diagram->get_data()->update_layer_of_figure(figure);
}
return relocated;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::accepts_drop(int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects) {
return _owner->accepts_drop(this, x, y, type, objects);
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::accepts_drop(int x, int y, const std::string &type, const std::string &text) {
return _owner->accepts_drop(this, x, y, type, text);
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::perform_drop(int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects) {
bool retval = _owner->perform_drop(this, x, y, type, objects);
if (_catalog_tree && retval) // if it was accepted then we can mark all objects
{ // we will do the long way so we will not need to reload the whole tree and remember expanded rows
std::list<GrtObjectRef>::const_iterator it;
for (it = objects.begin(); it != objects.end(); ++it)
_catalog_tree->mark_node(*it);
}
return retval;
}
//----------------------------------------------------------------------------------------------------------------------
bool ModelDiagramForm::perform_drop(int x, int y, const std::string &type, const std::string &text) {
return _owner->perform_drop(this, x, y, type, text);
}
//----------------------------------------------------------------------------------------------------------------------
mdc::Layer *ModelDiagramForm::get_floater_layer() {
return _floater_layer;
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::add_floater(Floater *floater) {
Point pos;
pos.x = _view->get_viewport().right() - 200;
pos.y = _view->get_viewport().top() + 20;
floater->move_to(pos);
_floater_layer->add_item(floater);
}
//----------------------------------------------------------------------------------------------------------------------
WBContext *ModelDiagramForm::get_wb() {
return _owner->get_wb();
}
//----------------------------------------------------------------------------------------------------------------------
// inline editing
void ModelDiagramForm::begin_editing(const Rect &rect, const std::string &text, float text_size, bool multiline) {
if (_inline_edit_context) {
int x, y;
int width, height;
_inline_edit_context->set_font_size(text_size);
_inline_edit_context->set_multiline(multiline);
_view->canvas_to_window(rect, x, y, width, height);
_inline_edit_context->begin_editing(x, y, width, height, text);
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::stop_editing() {
if (_inline_edit_context)
_inline_edit_context->end_editing();
}
//----------------------------------------------------------------------------------------------------------------------
static void forward_edit_finished(const std::string &text, EditFinishReason reason, ModelDiagramForm *form) {
(*form->signal_editing_done())(text, reason);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_inline_editor_context(InlineEditContext *context) {
_inline_edit_context = context;
scoped_connect(_inline_edit_context->signal_edit_finished(),
std::bind(forward_edit_finished, std::placeholders::_1, std::placeholders::_2, this));
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::zoom_in() {
model_DiagramRef view(get_model_diagram());
double zoom = *view->zoom();
for (size_t i = 0; i < sizeof(zoom_steps) / sizeof(*zoom_steps); i++) {
if (zoom >= zoom_steps[i]) {
if (i > 0)
view->zoom(zoom_steps[i - 1]);
return;
}
}
view->zoom(zoom_steps[0]);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::zoom_out() {
model_DiagramRef view(get_model_diagram());
double zoom = *view->zoom();
for (size_t i = 0; i < sizeof(zoom_steps) / sizeof(*zoom_steps); i++) {
if (zoom >= zoom_steps[i]) {
if (i + 1 < sizeof(zoom_steps) / sizeof(*zoom_steps))
view->zoom(zoom_steps[i + 1]);
return;
}
}
view->zoom(zoom_steps[sizeof(zoom_steps) / sizeof(*zoom_steps) - 1]);
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::setup_mini_view(mdc::CanvasView *view) {
if (!_mini_view) {
_mini_view = new MiniView(view->get_current_layer());
view->initialize();
view->get_background_layer()->set_visible(false);
view->set_page_layout(1, 1);
view->set_page_size(view->get_viewable_size());
view->get_current_layer()->add_item(_mini_view);
int w, h;
view->get_view_size(w, h);
_mini_view->set_active_view(get_view(), get_model_diagram());
update_mini_view_size(w, h);
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::update_mini_view_size(int w, int h) {
if (_mini_view) {
mdc::CanvasView *view = _mini_view->get_layer()->get_view();
view->update_view_size(w, h);
view->set_page_size(view->get_viewable_size());
_mini_view->update_size();
}
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::setBackgroundColor(base::Color const& color) {
if (_mini_view != nullptr)
_mini_view->setBackgroundColor(color);
if (_view != nullptr)
_view->setBackgroundColor(color);
}
//----------------------------------------------------------------------------------------------------------------------
std::string ModelDiagramForm::get_title() {
return std::string(_model_diagram->name());
}
//----------------------------------------------------------------------------------------------------------------------
void ModelDiagramForm::set_highlight_fks(bool flag) {
_highlight_fks = flag;
_features->highlight_all_connections(flag);
}
//----------------- UpdateLock -----------------------------------------------------------------------------------------
ModelDiagramForm::UpdateLock::~UpdateLock() {
if (_form->_update_count > 0)
_form->_update_count--;
if (_form->_update_count == 0)
_form->_layer_tree->refresh();
}
//----------------------------------------------------------------------------------------------------------------------