mirror of https://github.com/KLayout/klayout.git
6126 lines
157 KiB
C++
6126 lines
157 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2023 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
#include "laybasicConfig.h"
|
|
|
|
#include "tlInternational.h"
|
|
#include "tlExpression.h"
|
|
#include "tlTimer.h"
|
|
#include "tlString.h"
|
|
#include "tlLog.h"
|
|
#include "tlAssert.h"
|
|
#include "tlExceptions.h"
|
|
#include "tlDeferredExecution.h"
|
|
#include "layLayoutViewBase.h"
|
|
#include "layBitmapsToImage.h"
|
|
#include "layViewOp.h"
|
|
#include "layViewObject.h"
|
|
#include "layConverters.h"
|
|
#include "layMove.h"
|
|
#include "layZoomBox.h"
|
|
#include "layMouseTracker.h"
|
|
#include "layEditable.h"
|
|
#include "layFixedFont.h"
|
|
#include "laySelector.h"
|
|
#include "layLayoutCanvas.h"
|
|
#include "layRedrawThread.h"
|
|
#include "layRedrawThreadWorker.h"
|
|
#include "layParsedLayerSource.h"
|
|
#include "dbClipboard.h"
|
|
#include "dbLayout.h"
|
|
#include "dbLayoutUtils.h"
|
|
#include "dbManager.h"
|
|
#include "dbLibrary.h"
|
|
#include "rdb.h"
|
|
#include "dbLayoutToNetlist.h"
|
|
#include "dbTechnology.h"
|
|
#include "tlXMLParser.h"
|
|
#include "gsi.h"
|
|
|
|
#if defined(HAVE_QT)
|
|
# include <QImageWriter>
|
|
#endif
|
|
|
|
// Enable this if you have both Qt and libpng and want to use libpng for saving images:
|
|
// #define PREFER_LIBPNG_FOR_SAVE 1
|
|
|
|
#include <limits>
|
|
|
|
namespace lay
|
|
{
|
|
|
|
// factor for "zoom in & out"
|
|
const double zoom_factor = 0.7;
|
|
|
|
// factor by which panning is faster in "fast" (+Shift) mode
|
|
const double fast_factor = 3.0;
|
|
|
|
// -------------------------------------------------------------
|
|
|
|
struct OpHideShowCell
|
|
: public db::Op
|
|
{
|
|
OpHideShowCell (lay::CellView::cell_index_type ci, int cv_index, bool show)
|
|
: m_cell_index (ci), m_cellview_index (cv_index), m_show (show)
|
|
{ }
|
|
|
|
lay::CellView::cell_index_type m_cell_index;
|
|
int m_cellview_index;
|
|
bool m_show;
|
|
};
|
|
|
|
struct OpSetDitherPattern
|
|
: public db::Op
|
|
{
|
|
OpSetDitherPattern (const lay::DitherPattern &o, const lay::DitherPattern &n)
|
|
: db::Op (), m_old (o), m_new (n)
|
|
{
|
|
// nothing yet.
|
|
}
|
|
|
|
lay::DitherPattern m_old, m_new;
|
|
};
|
|
|
|
struct OpSetLineStyles
|
|
: public db::Op
|
|
{
|
|
OpSetLineStyles (const lay::LineStyles &o, const lay::LineStyles &n)
|
|
: db::Op (), m_old (o), m_new (n)
|
|
{
|
|
// nothing yet.
|
|
}
|
|
|
|
lay::LineStyles m_old, m_new;
|
|
};
|
|
|
|
struct OpSetLayerProps
|
|
: public db::Op
|
|
{
|
|
OpSetLayerProps (unsigned int li, unsigned int i, const lay::LayerProperties &o, const lay::LayerProperties &n)
|
|
: m_list_index (li), m_index (i), m_old (o), m_new (n)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
size_t m_index;
|
|
lay::LayerProperties m_old, m_new;
|
|
};
|
|
|
|
struct OpSetLayerPropsNode
|
|
: public db::Op
|
|
{
|
|
OpSetLayerPropsNode (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &o, const lay::LayerPropertiesNode &n)
|
|
: m_list_index (li), m_index (i), m_old (o), m_new (n)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
size_t m_index;
|
|
lay::LayerPropertiesNode m_old, m_new;
|
|
};
|
|
|
|
struct OpDeleteLayerList
|
|
: public db::Op
|
|
{
|
|
OpDeleteLayerList (unsigned int li, const lay::LayerPropertiesList &o)
|
|
: m_list_index (li), m_old (o)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
lay::LayerPropertiesList m_old;
|
|
};
|
|
|
|
struct OpInsertLayerList
|
|
: public db::Op
|
|
{
|
|
OpInsertLayerList (unsigned int li, const lay::LayerPropertiesList &n)
|
|
: m_list_index (li), m_new (n)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
lay::LayerPropertiesList m_new;
|
|
};
|
|
|
|
struct OpRenameProps
|
|
: public db::Op
|
|
{
|
|
OpRenameProps (unsigned int li, const std::string &old_name, const std::string &new_name)
|
|
: m_list_index (li), m_old (old_name), m_new (new_name)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
std::string m_old, m_new;
|
|
};
|
|
|
|
struct OpSetAllProps
|
|
: public db::Op
|
|
{
|
|
OpSetAllProps (unsigned int li, const lay::LayerPropertiesList &o, const lay::LayerPropertiesList &n)
|
|
: m_list_index (li), m_old (o), m_new (n)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
unsigned int m_list_index;
|
|
lay::LayerPropertiesList m_old, m_new;
|
|
};
|
|
|
|
struct OpLayerList
|
|
: public db::Op
|
|
{
|
|
enum Mode { Delete, Insert };
|
|
|
|
OpLayerList (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n, Mode m)
|
|
: m_list_index (li), m_index (i), m_mode (m), m_node (n)
|
|
{ }
|
|
|
|
unsigned int m_list_index;
|
|
size_t m_index;
|
|
Mode m_mode;
|
|
lay::LayerPropertiesNode m_node;
|
|
};
|
|
|
|
struct OpInsertLayerProps
|
|
: public OpLayerList
|
|
{
|
|
OpInsertLayerProps (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n)
|
|
: OpLayerList (li, i, n, Insert)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
};
|
|
|
|
struct OpDeleteLayerProps
|
|
: public OpLayerList
|
|
{
|
|
OpDeleteLayerProps (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n)
|
|
: OpLayerList (li, i, n, Delete)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
};
|
|
|
|
// -------------------------------------------------------------
|
|
|
|
const double animation_interval = 0.5;
|
|
|
|
LayoutViewBase::LayoutViewBase (db::Manager *manager, bool editable, lay::Plugin *plugin_parent, unsigned int options)
|
|
: lay::Dispatcher (plugin_parent, false /*not standalone*/),
|
|
mp_ui (0),
|
|
dm_redraw (this, &LayoutViewBase::redraw),
|
|
m_editable (editable),
|
|
m_options (options),
|
|
m_annotation_shapes (manager)
|
|
{
|
|
// either it's us or the parent has a dispatcher
|
|
tl_assert (dispatcher () != 0);
|
|
|
|
init (manager);
|
|
}
|
|
|
|
LayoutViewBase::LayoutViewBase (lay::LayoutView *ui, db::Manager *manager, bool editable, lay::Plugin *plugin_parent, unsigned int options)
|
|
: lay::Dispatcher (plugin_parent, false /*not standalone*/),
|
|
mp_ui (ui),
|
|
dm_redraw (this, &LayoutViewBase::redraw),
|
|
m_editable (editable),
|
|
m_options (options),
|
|
m_annotation_shapes (manager)
|
|
{
|
|
// either it's us or the parent has a dispatcher
|
|
tl_assert (dispatcher () != 0);
|
|
|
|
init (manager);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::copy_from (lay::LayoutViewBase *source)
|
|
{
|
|
m_annotation_shapes = source->m_annotation_shapes;
|
|
|
|
// set the handle reference and clear all cell related stuff
|
|
m_cellviews = source->cellview_list ();
|
|
m_hidden_cells = source->m_hidden_cells;
|
|
|
|
// clear the history, store path and zoom box
|
|
m_display_states.clear ();
|
|
m_display_state_ptr = 0;
|
|
m_synchronous = source->synchronous ();
|
|
m_drawing_workers = source->drawing_workers ();
|
|
|
|
begin_layer_updates ();
|
|
|
|
// duplicate the layer properties
|
|
for (size_t i = 0; i < source->m_layer_properties_lists.size (); ++i) {
|
|
if (i >= m_layer_properties_lists.size ()) {
|
|
m_layer_properties_lists.push_back (new lay::LayerPropertiesList (*source->m_layer_properties_lists [i]));
|
|
} else {
|
|
*m_layer_properties_lists [i] = *source->m_layer_properties_lists [i];
|
|
}
|
|
m_layer_properties_lists [i]->attach_view (this, (unsigned int) i);
|
|
}
|
|
|
|
end_layer_updates ();
|
|
|
|
if (! m_layer_properties_lists.empty ()) {
|
|
mp_canvas->set_dither_pattern (m_layer_properties_lists [0]->dither_pattern ());
|
|
mp_canvas->set_line_styles (m_layer_properties_lists [0]->line_styles ());
|
|
}
|
|
|
|
// copy the title
|
|
m_title = source->m_title;
|
|
|
|
layer_list_changed_event (3);
|
|
|
|
finish_cellviews_changed ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::init (db::Manager *mgr)
|
|
{
|
|
manager (mgr);
|
|
|
|
m_active_cellview_index = -1;
|
|
m_active_cellview_changed_event_enabled = true;
|
|
|
|
m_annotation_shapes.manager (mgr);
|
|
|
|
m_visibility_changed = false;
|
|
m_disabled_edits = 0;
|
|
m_synchronous = false;
|
|
m_drawing_workers = 1;
|
|
m_from_level = 0;
|
|
m_pan_distance = 0.15;
|
|
m_wheel_mode = 0;
|
|
m_paste_display_mode = 2;
|
|
m_guiding_shape_visible = true;
|
|
m_guiding_shape_line_width = 1;
|
|
m_guiding_shape_color = tl::Color ();
|
|
m_guiding_shape_vertex_size = 5;
|
|
m_to_level = 0;
|
|
m_ctx_dimming = 50;
|
|
m_ctx_hollow = false;
|
|
m_child_ctx_dimming = 50;
|
|
m_child_ctx_hollow = false;
|
|
m_child_ctx_enabled = false;
|
|
m_abstract_mode_width = 10.0;
|
|
m_abstract_mode_enabled = false;
|
|
m_box_text_transform = true;
|
|
m_box_font = 0;
|
|
m_min_size_for_label = 16;
|
|
m_cell_box_visible = true;
|
|
m_text_visible = true;
|
|
m_default_font_size = lay::FixedFont::default_font_size ();
|
|
m_text_lazy_rendering = true;
|
|
m_bitmap_caching = true;
|
|
m_show_properties = false;
|
|
m_apply_text_trans = true;
|
|
m_default_text_size = 0.1;
|
|
m_text_font = 0;
|
|
m_show_markers = true;
|
|
m_no_stipples = false;
|
|
m_stipple_offset = true;
|
|
m_fit_new_cell = true;
|
|
m_full_hier_new_cell = false;
|
|
m_clear_ruler_new_cell = false;
|
|
m_dbu_coordinates = false;
|
|
m_absolute_coordinates = false;
|
|
m_drop_small_cells = false;
|
|
m_drop_small_cells_value = 10;
|
|
m_drop_small_cells_cond = DSC_Max;
|
|
m_draw_array_border_instances = false;
|
|
m_dirty = false;
|
|
m_prop_changed = false;
|
|
m_animated = false;
|
|
m_phase = 0;
|
|
m_palette = lay::ColorPalette::default_palette ();
|
|
m_stipple_palette = lay::StipplePalette::default_palette ();
|
|
m_display_state_ptr = 0;
|
|
m_mode = std::numeric_limits<int>::min (); // nothing selected yet.
|
|
mp_tracker = 0;
|
|
mp_zoom_service = 0;
|
|
mp_selection_service = 0;
|
|
mp_move_service = 0;
|
|
m_marker_line_width = 0;
|
|
m_marker_vertex_size = 0;
|
|
m_marker_dither_pattern = 1;
|
|
m_marker_line_style = 0;
|
|
m_marker_halo = true;
|
|
m_transient_selection_mode = true;
|
|
m_sel_inside_pcells = false;
|
|
m_add_other_layers = false;
|
|
m_search_range = 5;
|
|
m_search_range_box = 0;
|
|
|
|
m_layer_properties_lists.push_back (new LayerPropertiesList ());
|
|
m_layer_properties_lists.back ()->attach_view (this, (unsigned int) (m_layer_properties_lists.size () - 1));
|
|
m_current_layer_list = 0;
|
|
|
|
mp_canvas = new lay::LayoutCanvas (this);
|
|
|
|
// occupy services and editables:
|
|
// these services get deleted by the canvas destructor automatically:
|
|
if ((m_options & LV_NoTracker) == 0) {
|
|
mp_tracker = new lay::MouseTracker (this);
|
|
}
|
|
if ((m_options & LV_NoZoom) == 0) {
|
|
mp_zoom_service = new lay::ZoomService (this);
|
|
}
|
|
if ((m_options & LV_NoSelection) == 0) {
|
|
mp_selection_service = new lay::SelectionService (this);
|
|
}
|
|
if ((m_options & LV_NoMove) == 0) {
|
|
mp_move_service = new lay::MoveService (this);
|
|
}
|
|
|
|
create_plugins ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::finish ()
|
|
{
|
|
// if we're the root dispatcher initialize the menu and build the context menus. No other menus are built so far.
|
|
if (dispatcher () == this) {
|
|
init_menu ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::init_menu ()
|
|
{
|
|
make_menu ();
|
|
|
|
// make the plugins create their menu items
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
// TODO: get rid of the const_cast hack
|
|
const_cast <lay::PluginDeclaration *> (&*cls)->init_menu (dispatcher ());
|
|
}
|
|
|
|
// if not in editable mode, hide all entries from "edit_mode" group and show all from the "view_mode" group and vice versa
|
|
std::vector<std::string> edit_mode_grp = menu ()->group ("edit_mode");
|
|
for (std::vector<std::string>::const_iterator g = edit_mode_grp.begin (); g != edit_mode_grp.end (); ++g) {
|
|
menu ()->action (*g)->set_visible (is_editable ());
|
|
}
|
|
std::vector<std::string> view_mode_grp = menu ()->group ("view_mode");
|
|
for (std::vector<std::string>::const_iterator g = view_mode_grp.begin (); g != view_mode_grp.end (); ++g) {
|
|
menu ()->action (*g)->set_visible (! is_editable ());
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::shutdown ()
|
|
{
|
|
// detach all observers
|
|
// This is to prevent signals to partially destroyed observers that own a LayoutViewBase
|
|
layer_list_changed_event.clear ();
|
|
layer_list_deleted_event.clear ();
|
|
layer_list_inserted_event.clear ();
|
|
current_layer_list_changed_event.clear ();
|
|
cell_visibility_changed_event.clear ();
|
|
cellviews_about_to_change_event.clear ();
|
|
cellview_about_to_change_event.clear ();
|
|
cellviews_changed_event.clear ();
|
|
cellview_changed_event.clear ();
|
|
rdb_list_changed_event.clear ();
|
|
l2ndb_list_changed_event.clear ();
|
|
file_open_event.clear ();
|
|
hier_changed_event.clear ();
|
|
geom_changed_event.clear ();
|
|
annotations_changed_event.clear ();
|
|
|
|
// detach ourselves from any observed objects to prevent signals while destroying
|
|
tl::Object::detach_from_all_events ();
|
|
|
|
// remove all rdb's
|
|
while (num_rdbs () > 0) {
|
|
remove_rdb (0);
|
|
}
|
|
|
|
// remove all L2N DB's
|
|
while (num_l2ndbs () > 0) {
|
|
remove_l2ndb (0);
|
|
}
|
|
|
|
// delete layer lists
|
|
std::vector<LayerPropertiesList *> layer_properties_lists;
|
|
layer_properties_lists.swap (m_layer_properties_lists);
|
|
for (std::vector<LayerPropertiesList *>::iterator l = layer_properties_lists.begin (); l != layer_properties_lists.end (); ++l) {
|
|
if (*l) {
|
|
delete *l;
|
|
}
|
|
}
|
|
|
|
// delete all plugins
|
|
std::vector<lay::Plugin *> plugins;
|
|
plugins.swap (mp_plugins);
|
|
for (std::vector<lay::Plugin *>::iterator p = plugins.begin (); p != plugins.end (); ++p) {
|
|
delete *p;
|
|
}
|
|
|
|
// detach from the manager, so we can safely delete the manager
|
|
manager (0);
|
|
|
|
stop ();
|
|
}
|
|
|
|
LayoutViewBase::~LayoutViewBase ()
|
|
{
|
|
shutdown ();
|
|
|
|
// because LayoutViewBase and LayoutCanvas both control lifetimes of
|
|
// ruler objects for example, it is safer to explicitly delete the
|
|
// LayoutCanvas object here:
|
|
delete mp_canvas;
|
|
mp_canvas = 0;
|
|
}
|
|
|
|
void LayoutViewBase::unregister_plugin (lay::Plugin *pi)
|
|
{
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if (pi == *p) {
|
|
mp_plugins.erase (p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayoutViewBase::resize (unsigned int width, unsigned int height)
|
|
{
|
|
mp_canvas->resize (width, height);
|
|
}
|
|
|
|
void LayoutViewBase::update_event_handlers ()
|
|
{
|
|
tl::Object::detach_from_all_events ();
|
|
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
// TODO: get rid of the const_cast hack
|
|
const_cast<lay::PluginDeclaration *> ((*p)->plugin_declaration ())->editable_enabled_changed_event.add (this, &LayoutViewBase::signal_plugin_enabled_changed);
|
|
}
|
|
|
|
for (unsigned int i = 0; i < cellviews (); ++i) {
|
|
cellview (i)->layout ().hier_changed_event.add (this, &LayoutViewBase::signal_hier_changed);
|
|
cellview (i)->layout ().bboxes_changed_event.add (this, &LayoutViewBase::signal_bboxes_from_layer_changed, i);
|
|
cellview (i)->layout ().dbu_changed_event.add (this, &LayoutViewBase::signal_bboxes_changed);
|
|
cellview (i)->layout ().prop_ids_changed_event.add (this, &LayoutViewBase::signal_prop_ids_changed);
|
|
cellview (i)->layout ().layer_properties_changed_event.add (this, &LayoutViewBase::signal_layer_properties_changed);
|
|
cellview (i)->layout ().cell_name_changed_event.add (this, &LayoutViewBase::signal_cell_name_changed);
|
|
cellview (i)->apply_technology_with_sender_event.add (this, &LayoutViewBase::signal_apply_technology);
|
|
}
|
|
|
|
annotation_shapes ().bboxes_changed_any_event.add (this, &LayoutViewBase::signal_annotations_changed);
|
|
|
|
mp_canvas->viewport_changed_event.add (this, &LayoutViewBase::viewport_changed);
|
|
mp_canvas->left_arrow_key_pressed.add (this, &LayoutViewBase::pan_left);
|
|
mp_canvas->up_arrow_key_pressed.add (this, &LayoutViewBase::pan_up);
|
|
mp_canvas->right_arrow_key_pressed.add (this, &LayoutViewBase::pan_right);
|
|
mp_canvas->down_arrow_key_pressed.add (this, &LayoutViewBase::pan_down);
|
|
mp_canvas->left_arrow_key_pressed_with_shift.add (this, &LayoutViewBase::pan_left_fast);
|
|
mp_canvas->up_arrow_key_pressed_with_shift.add (this, &LayoutViewBase::pan_up_fast);
|
|
mp_canvas->right_arrow_key_pressed_with_shift.add (this, &LayoutViewBase::pan_right_fast);
|
|
mp_canvas->down_arrow_key_pressed_with_shift.add (this, &LayoutViewBase::pan_down_fast);
|
|
}
|
|
|
|
void LayoutViewBase::viewport_changed ()
|
|
{
|
|
viewport_changed_event ();
|
|
}
|
|
|
|
bool LayoutViewBase::accepts_drop (const std::string &path_or_url) const
|
|
{
|
|
for (std::vector<lay::Plugin *>::const_iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if ((*p)->accepts_drop (path_or_url)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LayoutViewBase::drop_url (const std::string &path_or_url)
|
|
{
|
|
for (std::vector<lay::Plugin *>::const_iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if ((*p)->accepts_drop (path_or_url)) {
|
|
(*p)->drop_url (path_or_url);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayoutViewBase::clear_plugins ()
|
|
{
|
|
std::vector<lay::Plugin *> plugins;
|
|
mp_plugins.swap (plugins);
|
|
for (std::vector<lay::Plugin *>::iterator p = plugins.begin (); p != plugins.end (); ++p) {
|
|
delete *p;
|
|
}
|
|
mp_active_plugin = 0;
|
|
}
|
|
|
|
void LayoutViewBase::create_plugins (const lay::PluginDeclaration *except_this)
|
|
{
|
|
clear_plugins ();
|
|
|
|
// create the plugins
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
|
|
if (&*cls != except_this) {
|
|
|
|
// TODO: clean solution. The following is a HACK:
|
|
if (cls.current_name () == "ant::Plugin" || cls.current_name () == "img::Plugin") {
|
|
// ant and img are created always
|
|
create_plugin (&*cls);
|
|
} else if ((options () & LV_NoPlugins) == 0) {
|
|
// others: only create unless LV_NoPlugins is set
|
|
create_plugin (&*cls);
|
|
} else if ((options () & LV_NoGrid) == 0 && cls.current_name () == "GridNetPlugin") {
|
|
// except grid net plugin which is created on request
|
|
create_plugin (&*cls);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mode (default_mode ());
|
|
}
|
|
|
|
lay::Plugin *LayoutViewBase::create_plugin (const lay::PluginDeclaration *cls)
|
|
{
|
|
lay::Plugin *p = cls->create_plugin (manager (), dispatcher (), this);
|
|
if (p) {
|
|
|
|
// unhook the plugin from the script side if created there (prevent GC from destroying it)
|
|
p->gsi::ObjectBase::keep ();
|
|
|
|
mp_plugins.push_back (p);
|
|
p->set_plugin_declaration (cls);
|
|
|
|
// enable editable functionality
|
|
if (p->editable_interface ()) {
|
|
enable (p->editable_interface (), cls->editable_enabled ());
|
|
}
|
|
|
|
update_event_handlers ();
|
|
|
|
}
|
|
return p;
|
|
}
|
|
|
|
Plugin *LayoutViewBase::get_plugin_by_name (const std::string &name) const
|
|
{
|
|
lay::PluginDeclaration *decl = 0;
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); !decl && cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
if (cls.current_name () == name) {
|
|
decl = cls.operator-> ();
|
|
}
|
|
}
|
|
|
|
if (decl) {
|
|
for (std::vector<lay::Plugin *>::const_iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if ((*p)->plugin_declaration () == decl) {
|
|
return *p;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_drawing_workers (int workers)
|
|
{
|
|
m_drawing_workers = std::max (0, std::min (100, workers));
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_synchronous (bool s)
|
|
{
|
|
m_synchronous = s;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::message (const std::string & /*s*/, int /*timeout*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_dirty () const
|
|
{
|
|
return m_dirty;
|
|
}
|
|
|
|
std::string
|
|
LayoutViewBase::title () const
|
|
{
|
|
if (! m_title.empty ()) {
|
|
return m_title;
|
|
} else if (cellviews () == 0) {
|
|
return tl::to_string (tr ("<empty>"));
|
|
} else {
|
|
|
|
int cv_index = active_cellview_index ();
|
|
if (cv_index < 0 || cv_index >= int (cellviews ())) {
|
|
cv_index = 0;
|
|
}
|
|
|
|
const lay::CellView &cv0 = cellview (cv_index);
|
|
|
|
std::string t;
|
|
|
|
t += cv0->name ();
|
|
if (cv0->layout ().is_valid_cell_index (cv0.cell_index ())) {
|
|
t += " [";
|
|
t += cv0->layout ().cell_name (cv0.cell_index ());
|
|
t += "]";
|
|
}
|
|
|
|
if (cellviews () > 1) {
|
|
t += " ...";
|
|
}
|
|
|
|
return t;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_title (const std::string &t)
|
|
{
|
|
if (m_title != t) {
|
|
m_title = t;
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::reset_title ()
|
|
{
|
|
if (! m_title.empty ()) {
|
|
m_title = "";
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::configure (const std::string &name, const std::string &value)
|
|
{
|
|
lay::Dispatcher::configure (name, value);
|
|
|
|
if (mp_move_service && mp_move_service->configure (name, value)) {
|
|
return true;
|
|
}
|
|
|
|
if (name == cfg_default_lyp_file) {
|
|
|
|
m_def_lyp_file = value;
|
|
return false; // not taken - let others set it too.
|
|
|
|
} else if (name == cfg_default_add_other_layers) {
|
|
|
|
tl::from_string (value, m_add_other_layers);
|
|
return false; // not taken - let others set it too.
|
|
|
|
} else if (name == cfg_background_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
background_color (color);
|
|
// do not take - let others receive the background color events as well
|
|
return false;
|
|
|
|
} else if (name == cfg_default_font_size) {
|
|
|
|
int df = 0;
|
|
tl::from_string (value, df);
|
|
if (m_default_font_size != df) {
|
|
// keep a shadow state to correctly issue the redraw call
|
|
m_default_font_size = df;
|
|
lay::FixedFont::set_default_font_size (df);
|
|
redraw_later ();
|
|
}
|
|
// do not take - let others have the event for the redraw call
|
|
return false;
|
|
|
|
} else if (name == cfg_bitmap_oversampling) {
|
|
|
|
int os = 1;
|
|
tl::from_string (value, os);
|
|
mp_canvas->set_oversampling (os);
|
|
return true;
|
|
|
|
} else if (name == cfg_highres_mode) {
|
|
|
|
bool hrm = false;
|
|
tl::from_string (value, hrm);
|
|
mp_canvas->set_highres_mode (hrm);
|
|
return true;
|
|
|
|
} else if (name == cfg_image_cache_size) {
|
|
|
|
int sz = 0;
|
|
tl::from_string (value, sz);
|
|
mp_canvas->set_image_cache_size (size_t (sz));
|
|
return true;
|
|
|
|
} else if (name == cfg_global_trans) {
|
|
|
|
tl::Extractor ex (value.c_str ());
|
|
try {
|
|
db::DCplxTrans t;
|
|
ex.read (t);
|
|
set_global_trans (t);
|
|
} catch (...) { }
|
|
return true;
|
|
|
|
} else if (name == cfg_ctx_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
ctx_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_ctx_dimming) {
|
|
|
|
int n;
|
|
tl::from_string (value, n);
|
|
ctx_dimming (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_ctx_hollow) {
|
|
|
|
bool h;
|
|
tl::from_string (value, h);
|
|
ctx_hollow (h);
|
|
return true;
|
|
|
|
} else if (name == cfg_child_ctx_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
child_ctx_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_child_ctx_dimming) {
|
|
|
|
int n;
|
|
tl::from_string (value, n);
|
|
child_ctx_dimming (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_child_ctx_hollow) {
|
|
|
|
bool h;
|
|
tl::from_string (value, h);
|
|
child_ctx_hollow (h);
|
|
return true;
|
|
|
|
} else if (name == cfg_child_ctx_enabled) {
|
|
|
|
bool h;
|
|
tl::from_string (value, h);
|
|
child_ctx_enabled (h);
|
|
return true;
|
|
|
|
} else if (name == cfg_search_range) {
|
|
|
|
unsigned int n;
|
|
tl::from_string (value, n);
|
|
set_search_range (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_search_range_box) {
|
|
|
|
unsigned int n;
|
|
tl::from_string (value, n);
|
|
set_search_range_box (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_abstract_mode_enabled) {
|
|
|
|
bool e;
|
|
tl::from_string (value, e);
|
|
abstract_mode_enabled (e);
|
|
return true;
|
|
|
|
} else if (name == cfg_abstract_mode_width) {
|
|
|
|
double w;
|
|
tl::from_string (value, w);
|
|
abstract_mode_width (w);
|
|
return true;
|
|
|
|
} else if (name == cfg_min_inst_label_size) {
|
|
|
|
int n;
|
|
tl::from_string (value, n);
|
|
min_inst_label_size (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_cell_box_text_font) {
|
|
|
|
int n;
|
|
tl::from_string (value, n);
|
|
cell_box_text_font (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_cell_box_text_transform) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
cell_box_text_transform (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_cell_box_visible) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
cell_box_visible (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_cell_box_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
cell_box_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_text_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
text_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_text_visible) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
text_visible (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_bitmap_caching) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
bitmap_caching (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_text_lazy_rendering) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
text_lazy_rendering (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_show_properties) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
show_properties_as_text (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_apply_text_trans) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
apply_text_trans (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_markers_visible) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
mp_canvas->set_dismiss_view_objects (! flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_no_stipple) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
no_stipples (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_stipple_offset) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
offset_stipples (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_default_text_size) {
|
|
|
|
double sz;
|
|
tl::from_string (value, sz);
|
|
default_text_size (sz);
|
|
return true;
|
|
|
|
} else if (name == cfg_text_font) {
|
|
|
|
int n;
|
|
tl::from_string (value, n);
|
|
text_font (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_full_hier_new_cell) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
full_hier_new_cell (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_fit_new_cell) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
fit_new_cell (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_clear_ruler_new_cell) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
clear_ruler_new_cell (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_abs_units) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
absolute_coordinates (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_guiding_shape_visible) {
|
|
|
|
bool v = false;
|
|
tl::from_string (value, v);
|
|
guiding_shapes_visible (v);
|
|
return true;
|
|
|
|
} else if (name == cfg_guiding_shape_line_width) {
|
|
|
|
int v = 0;
|
|
tl::from_string (value, v);
|
|
guiding_shapes_line_width (v);
|
|
return true;
|
|
|
|
} else if (name == cfg_guiding_shape_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
guiding_shapes_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_guiding_shape_color) {
|
|
|
|
tl::Color color;
|
|
ColorConverter ().from_string (value, color);
|
|
guiding_shapes_color (color);
|
|
return true;
|
|
|
|
} else if (name == cfg_guiding_shape_vertex_size) {
|
|
|
|
int v = 0;
|
|
tl::from_string (value, v);
|
|
guiding_shapes_vertex_size (v);
|
|
return true;
|
|
|
|
} else if (name == cfg_paste_display_mode) {
|
|
|
|
tl::from_string (value, m_paste_display_mode);
|
|
return true;
|
|
|
|
} else if (name == cfg_mouse_wheel_mode) {
|
|
|
|
tl::from_string (value, m_wheel_mode);
|
|
return true;
|
|
|
|
} else if (name == cfg_pan_distance) {
|
|
|
|
double pd;
|
|
tl::from_string (value, pd);
|
|
pan_distance (pd);
|
|
return true;
|
|
|
|
} else if (name == cfg_drawing_workers) {
|
|
|
|
int workers;
|
|
tl::from_string (value, workers);
|
|
set_drawing_workers (workers);
|
|
return true;
|
|
|
|
} else if (name == cfg_drop_small_cells) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
drop_small_cells (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_drop_small_cells_cond) {
|
|
|
|
unsigned int n;
|
|
tl::from_string (value, n);
|
|
drop_small_cells_cond (drop_small_cells_cond_type (n));
|
|
return true;
|
|
|
|
} else if (name == cfg_drop_small_cells_value) {
|
|
|
|
unsigned int n;
|
|
tl::from_string (value, n);
|
|
drop_small_cells_value (n);
|
|
return true;
|
|
|
|
} else if (name == cfg_array_border_instances) {
|
|
|
|
bool f;
|
|
tl::from_string (value, f);
|
|
draw_array_border_instances (f);
|
|
return true;
|
|
|
|
} else if (name == cfg_dbu_units) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
dbu_coordinates (flag);
|
|
return true;
|
|
|
|
} else if (name == cfg_stipple_palette) {
|
|
|
|
lay::StipplePalette palette = lay::StipplePalette::default_palette ();
|
|
|
|
try {
|
|
// empty string means: default palette
|
|
if (! value.empty ()) {
|
|
palette.from_string (value);
|
|
}
|
|
} catch (...) {
|
|
// ignore errors: just reset the palette
|
|
palette = lay::StipplePalette::default_palette ();
|
|
}
|
|
|
|
set_palette (palette);
|
|
|
|
// others need this property too ..
|
|
return false;
|
|
|
|
} else if (name == cfg_line_style_palette) {
|
|
|
|
lay::LineStylePalette palette = lay::LineStylePalette::default_palette ();
|
|
|
|
try {
|
|
// empty string means: default palette
|
|
if (! value.empty ()) {
|
|
palette.from_string (value);
|
|
}
|
|
} catch (...) {
|
|
// ignore errors: just reset the palette
|
|
palette = lay::LineStylePalette::default_palette ();
|
|
}
|
|
|
|
set_palette (palette);
|
|
|
|
// others need this property too ..
|
|
return false;
|
|
|
|
} else if (name == cfg_color_palette) {
|
|
|
|
lay::ColorPalette palette = lay::ColorPalette::default_palette ();
|
|
|
|
try {
|
|
// empty string means: default palette
|
|
if (! value.empty ()) {
|
|
palette.from_string (value);
|
|
}
|
|
} catch (...) {
|
|
// ignore errors: just reset the palette
|
|
palette = lay::ColorPalette::default_palette ();
|
|
}
|
|
|
|
set_palette (palette);
|
|
|
|
// others need this property too ..
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_inside_pcells_mode) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
|
|
if (m_sel_inside_pcells != flag) {
|
|
m_sel_inside_pcells = flag;
|
|
clear_selection ();
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (name == cfg_sel_transient_mode) {
|
|
|
|
bool flag;
|
|
tl::from_string (value, flag);
|
|
m_transient_selection_mode = flag;
|
|
|
|
if (! m_transient_selection_mode) {
|
|
clear_transient_selection ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_color) {
|
|
|
|
tl::Color color;
|
|
lay::ColorConverter ().from_string (value, color);
|
|
|
|
// Change the color
|
|
if (lay::test_and_set (m_marker_color, color)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_line_width) {
|
|
|
|
int lw = 0;
|
|
tl::from_string (value, lw);
|
|
|
|
// Change the line width
|
|
if (lay::test_and_set (m_marker_line_width, lw)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_dither_pattern) {
|
|
|
|
int dp = 0;
|
|
tl::from_string (value, dp);
|
|
|
|
// Change the vertex_size
|
|
if (lay::test_and_set (m_marker_dither_pattern, dp)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_line_style) {
|
|
|
|
int dp = 0;
|
|
tl::from_string (value, dp);
|
|
|
|
// Change the vertex_size
|
|
if (lay::test_and_set (m_marker_line_style, dp)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_vertex_size) {
|
|
|
|
int vs = 0;
|
|
tl::from_string (value, vs);
|
|
|
|
// Change the vertex_size
|
|
if (lay::test_and_set (m_marker_vertex_size, vs)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else if (name == cfg_sel_halo) {
|
|
|
|
bool halo = 0;
|
|
tl::from_string (value, halo);
|
|
|
|
// Change the vertex_size
|
|
if (lay::test_and_set (m_marker_halo, halo)) {
|
|
mp_canvas->update_image ();
|
|
}
|
|
|
|
// do not take - let others receive this configuration as well
|
|
return false;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::config_finalize ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::enable_edits (bool enable)
|
|
{
|
|
// enable or disable these services:
|
|
if (mp_selection_service) {
|
|
mp_selection_service->enable (enable);
|
|
}
|
|
if (mp_move_service) {
|
|
mp_move_service->enable (enable);
|
|
}
|
|
|
|
// enable or disable the services that implement "lay::ViewService"
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
lay::ViewService *svc = (*p)->view_service_interface ();
|
|
if (svc) {
|
|
svc->enable (enable);
|
|
}
|
|
}
|
|
|
|
bool is_enabled = edits_enabled ();
|
|
|
|
if (enable) {
|
|
if (m_disabled_edits > 0) {
|
|
--m_disabled_edits;
|
|
}
|
|
} else {
|
|
++m_disabled_edits;
|
|
}
|
|
|
|
if (edits_enabled () != is_enabled) {
|
|
emit_edits_enabled_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_line_styles (const lay::LineStyles &styles)
|
|
{
|
|
if (mp_canvas->line_styles () != styles) {
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpSetLineStyles (mp_canvas->line_styles (), styles));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
mp_canvas->set_line_styles (styles);
|
|
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->set_line_styles (styles);
|
|
}
|
|
|
|
layer_list_changed_event (1);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_dither_pattern (const lay::DitherPattern &pattern)
|
|
{
|
|
if (mp_canvas->dither_pattern () != pattern) {
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpSetDitherPattern (mp_canvas->dither_pattern (), pattern));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
mp_canvas->set_dither_pattern (pattern);
|
|
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->set_dither_pattern (pattern);
|
|
}
|
|
|
|
layer_list_changed_event (1);
|
|
|
|
}
|
|
}
|
|
|
|
const LayerPropertiesList &
|
|
LayoutViewBase::get_properties (unsigned int index) const
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
static lay::LayerPropertiesList empty;
|
|
return empty;
|
|
} else {
|
|
return *m_layer_properties_lists [index];
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_current_layer_list (unsigned int index)
|
|
{
|
|
if (index != m_current_layer_list && index < layer_lists ()) {
|
|
m_current_layer_list = index;
|
|
current_layer_list_changed_event (index);
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::insert_layer_list (unsigned index, const LayerPropertiesList &props)
|
|
{
|
|
if (index > layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpInsertLayerList (index, props));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
clear_layer_selection ();
|
|
|
|
m_layer_properties_lists.insert (m_layer_properties_lists.begin () + index, new LayerPropertiesList (props));
|
|
m_layer_properties_lists [index]->attach_view (this, index);
|
|
merge_dither_pattern (*m_layer_properties_lists [index]);
|
|
|
|
m_current_layer_list = index;
|
|
current_layer_list_changed_event (index);
|
|
|
|
layer_list_inserted_event (index);
|
|
|
|
redraw ();
|
|
|
|
m_prop_changed = true;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::delete_layer_list (unsigned index)
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpDeleteLayerList (index, *m_layer_properties_lists [index]));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
clear_layer_selection ();
|
|
|
|
delete m_layer_properties_lists [index];
|
|
m_layer_properties_lists.erase (m_layer_properties_lists.begin () + index);
|
|
|
|
if (m_current_layer_list > index) {
|
|
|
|
--m_current_layer_list;
|
|
current_layer_list_changed_event (m_current_layer_list);
|
|
|
|
// don't tell the other observers because effectively nothing has changed.
|
|
|
|
} else if (m_current_layer_list == index) {
|
|
|
|
if (m_current_layer_list > 0) {
|
|
--m_current_layer_list;
|
|
}
|
|
|
|
current_layer_list_changed_event (m_current_layer_list);
|
|
|
|
// the current list has been deleted.
|
|
layer_list_changed_event (3);
|
|
|
|
redraw ();
|
|
|
|
}
|
|
|
|
layer_list_deleted_event (index);
|
|
m_prop_changed = true;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::rename_properties (unsigned int index, const std::string &new_name)
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpRenameProps (index, m_layer_properties_lists [index]->name (), new_name));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
m_layer_properties_lists [index]->set_name (new_name);
|
|
|
|
layer_list_changed_event (4);
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::set_current_layer (unsigned int cv_index, const db::LayerProperties &lp)
|
|
{
|
|
// rename the ones that got shifted.
|
|
lay::LayerPropertiesConstIterator l = begin_layers ();
|
|
while (! l.at_end ()) {
|
|
if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) {
|
|
set_current_layer (l);
|
|
return true;
|
|
}
|
|
++l;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::clear_layer_selection ()
|
|
{
|
|
m_current_layer = lay::LayerPropertiesConstIterator ();
|
|
m_selected_layers.clear ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_current_layer (const lay::LayerPropertiesConstIterator &l)
|
|
{
|
|
m_current_layer = l;
|
|
m_selected_layers.clear ();
|
|
m_selected_layers.push_back (l);
|
|
}
|
|
|
|
lay::LayerPropertiesConstIterator
|
|
LayoutViewBase::current_layer () const
|
|
{
|
|
return m_current_layer;
|
|
}
|
|
|
|
std::vector<lay::LayerPropertiesConstIterator>
|
|
LayoutViewBase::selected_layers () const
|
|
{
|
|
return m_selected_layers;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_selected_layers (const std::vector<lay::LayerPropertiesConstIterator> &sel)
|
|
{
|
|
m_selected_layers = sel;
|
|
if (sel.empty ()) {
|
|
m_current_layer = lay::LayerPropertiesConstIterator ();
|
|
} else {
|
|
m_current_layer = sel.front ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief A helper function to create an image from a single bitmap
|
|
*/
|
|
static void
|
|
single_bitmap_to_image (const lay::ViewOp &view_op, lay::Bitmap &bitmap,
|
|
tl::PixelBuffer *pimage, const lay::DitherPattern &dither_pattern, const lay::LineStyles &line_styles,
|
|
double dpr, unsigned int width, unsigned int height)
|
|
{
|
|
std::vector <lay::ViewOp> view_ops;
|
|
view_ops.push_back (view_op);
|
|
|
|
std::vector <lay::Bitmap *> pbitmaps;
|
|
pbitmaps.push_back (&bitmap);
|
|
|
|
lay::bitmaps_to_image (view_ops, pbitmaps, dither_pattern, line_styles, dpr, pimage, width, height, false, 0);
|
|
}
|
|
|
|
tl::PixelBuffer
|
|
LayoutViewBase::icon_for_layer (const LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr, unsigned int di_off, bool no_state)
|
|
{
|
|
if (dpr < 0.0) {
|
|
dpr = canvas ()->dpr ();
|
|
}
|
|
|
|
int oversampling = canvas () ? canvas ()->oversampling () : 1;
|
|
double gamma = 2.0;
|
|
|
|
bool hrm = canvas () ? canvas ()->highres_mode () : false;
|
|
double dpr_drawing = oversampling * (hrm ? 1.0 : dpr);
|
|
|
|
h = std::max ((unsigned int) 16, h) * oversampling * dpr + 0.5;
|
|
w = std::max ((unsigned int) 16, w) * oversampling * dpr + 0.5;
|
|
|
|
tl::color_t def_color = 0x808080;
|
|
tl::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color;
|
|
tl::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color;
|
|
|
|
tl::PixelBuffer image (w, h);
|
|
image.set_transparent (true);
|
|
image.fill (background_color ().rgb ());
|
|
|
|
// upper scanline is a dummy one
|
|
tl::color_t *sl0 = (uint32_t *) image.scan_line (0);
|
|
for (size_t i = 0; i < w; ++i) {
|
|
*sl0++ = 0;
|
|
}
|
|
|
|
lay::Bitmap fill (w, h, 1.0);
|
|
lay::Bitmap frame (w, h, 1.0);
|
|
lay::Bitmap text (w, h, 1.0);
|
|
lay::Bitmap vertex (w, h, 1.0);
|
|
|
|
unsigned int wp = w - 1;
|
|
|
|
if (! no_state && ! iter->visible (true)) {
|
|
|
|
wp = w / 4;
|
|
|
|
// Show the arrow if it is invisible also locally.
|
|
if (! iter->visible (false)) {
|
|
|
|
unsigned int aw = h / 4;
|
|
unsigned int ap = w / 2 - 1;
|
|
for (unsigned int i = 0; i <= aw; ++i) {
|
|
text.fill (h / 2 - 1 - i, ap, ap + aw - i + 1);
|
|
text.fill (h / 2 - 1 + i, ap, ap + aw - i + 1);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! no_state && no_stipples ()) {
|
|
// Show a partial stipple pattern only for "no stipple" mode
|
|
for (unsigned int i = 1; i < h - 2; ++i) {
|
|
fill.fill (i, w - 1 - w / 4, w);
|
|
}
|
|
} else {
|
|
for (unsigned int i = 1; i < h - 2; ++i) {
|
|
fill.fill (i, w - 1 - wp, w);
|
|
}
|
|
}
|
|
|
|
int lw = iter->width (true);
|
|
if (lw < 0) {
|
|
// default line width is 0 for parents and 1 for leafs
|
|
lw = iter->has_children () ? 0 : 1;
|
|
}
|
|
lw = lw * dpr_drawing + 0.5;
|
|
|
|
int p0 = lw / 2;
|
|
p0 = std::max (0, std::min (int (w / 4 - 1), p0));
|
|
|
|
int p1 = (lw - 1) / 2;
|
|
p1 = std::max (0, std::min (int (w / 4 - 1), p1));
|
|
|
|
int p0x = p0, p1x = p1;
|
|
unsigned int ddx = 0;
|
|
unsigned int ddy = h - 2 - p1 - p0;
|
|
if (iter->xfill (true)) {
|
|
ddx = wp - p0 - p1 - 1;
|
|
}
|
|
unsigned int d = ddx / 2;
|
|
|
|
frame.fill (p0, w - 1 - (wp - p1), w);
|
|
frame.fill (h - 2 - p1, w - 1 - (wp - p1), w);
|
|
|
|
for (unsigned int i = p0; i < h - 2; ++i) {
|
|
|
|
frame.fill (i, w - 1 - p0, w - p0);
|
|
frame.fill (i, w - 1 - (wp - p1), w - (wp - p1));
|
|
frame.fill (i, w - 1 - p0x, w - p0x);
|
|
frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x));
|
|
|
|
while (d < ddx) {
|
|
d += ddy;
|
|
frame.fill (i, w - 1 - p0x, w - p0x);
|
|
frame.fill (i, w - 1 - (wp - p1x), w - (wp - p1x));
|
|
++p0x;
|
|
++p1x;
|
|
}
|
|
|
|
if (d >= ddx) {
|
|
d -= ddx;
|
|
}
|
|
|
|
}
|
|
|
|
if (! no_state && ! iter->valid (true)) {
|
|
|
|
unsigned int bp = w - 1 - ((w * 7) / 8 - 1);
|
|
unsigned int be = bp + h / 2;
|
|
unsigned int bw = h / 4 - 1;
|
|
unsigned int by = h / 2 - 1;
|
|
|
|
for (unsigned int i = 0; i < bw + 2; ++i) {
|
|
fill.clear (by - i, bp - 1, be);
|
|
fill.clear (by + i, bp - 1, be);
|
|
}
|
|
|
|
for (unsigned int i = 0; i < bw; ++i) {
|
|
text.fill (by - i, bp + bw - i - 1, bp + bw - i + 1);
|
|
text.fill (by - i - 1, bp + bw - i - 1, bp + bw - i + 1);
|
|
text.fill (by - i, bp + bw + i, bp + bw + i + 2);
|
|
text.fill (by - i - 1, bp + bw + i, bp + bw + i + 2);
|
|
text.fill (by + i, bp + bw - i - 1, bp + bw - i + 1);
|
|
text.fill (by + i + 1, bp + bw - i - 1, bp + bw - i + 1);
|
|
text.fill (by + i, bp + bw + i, bp + bw + i + 2);
|
|
text.fill (by + i + 1, bp + bw + i, bp + bw + i + 2);
|
|
}
|
|
|
|
}
|
|
|
|
vertex.fill (h / 2 - 1, w - 1 - wp / 2, w - wp / 2);
|
|
|
|
lay::ViewOp::Mode mode = lay::ViewOp::Copy;
|
|
|
|
// create fill
|
|
single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, dither_pattern (), line_styles (), dpr_drawing, w, h);
|
|
// create frame
|
|
if (lw == 0) {
|
|
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, dither_pattern (), line_styles (), dpr_drawing, w, h);
|
|
} else {
|
|
single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, dither_pattern (), line_styles (), dpr_drawing, w, h);
|
|
}
|
|
// create text
|
|
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, dither_pattern (), line_styles (), dpr_drawing, w, h);
|
|
// create vertex
|
|
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? int (9 * dpr_drawing + 0.5) : 0), vertex, &image, dither_pattern (), line_styles (), dpr_drawing, w, h);
|
|
|
|
if (oversampling > 1) {
|
|
tl::PixelBuffer subsampled (image.width () / oversampling, image.height () / oversampling);
|
|
image.subsample (subsampled, oversampling, gamma);
|
|
return subsampled;
|
|
} else {
|
|
return image;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::merge_dither_pattern (lay::LayerPropertiesList &props)
|
|
{
|
|
{
|
|
lay::DitherPattern dp (dither_pattern ());
|
|
|
|
std::map <unsigned int, unsigned int> index_map;
|
|
dp.merge (props.dither_pattern (), index_map);
|
|
|
|
// remap the dither pattern index
|
|
for (lay::LayerPropertiesIterator l = props.begin_recursive (); l != props.end_recursive (); ++l) {
|
|
int dpi = l->dither_pattern (false /*local*/);
|
|
std::map <unsigned int, unsigned int>::iterator m = index_map.find ((unsigned int) dpi);
|
|
if (m != index_map.end ()) {
|
|
l->set_dither_pattern (int (m->second));
|
|
}
|
|
}
|
|
|
|
// install the new custom pattern table
|
|
if (mp_canvas->dither_pattern () != dp) {
|
|
mp_canvas->set_dither_pattern (dp);
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->set_dither_pattern (dp);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
lay::LineStyles ls (line_styles ());
|
|
|
|
std::map <unsigned int, unsigned int> index_map;
|
|
ls.merge (props.line_styles (), index_map);
|
|
|
|
// remap the dither pattern index
|
|
for (lay::LayerPropertiesIterator l = props.begin_recursive (); l != props.end_recursive (); ++l) {
|
|
int lsi = l->line_style (false /*local*/);
|
|
std::map <unsigned int, unsigned int>::iterator m = index_map.find ((unsigned int) lsi);
|
|
if (m != index_map.end ()) {
|
|
l->set_line_style (int (m->second));
|
|
}
|
|
}
|
|
|
|
// install the new custom pattern table
|
|
if (mp_canvas->line_styles () != ls) {
|
|
mp_canvas->set_line_styles (ls);
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->set_line_styles (ls);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::always_show_source () const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::always_show_ld () const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::always_show_layout_index () const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_properties (unsigned int index, const LayerPropertiesList &props)
|
|
{
|
|
// If index is not a valid tab index, don't do anything except for the case of
|
|
// index 0 in which the first entry is created (this can happen as a result of
|
|
// delete_properties).
|
|
if (index >= layer_lists ()) {
|
|
if (index > 0) {
|
|
return;
|
|
} else {
|
|
m_layer_properties_lists.push_back (new LayerPropertiesList ());
|
|
m_layer_properties_lists.back ()->attach_view (this, (unsigned int) (m_layer_properties_lists.size () - 1));
|
|
}
|
|
}
|
|
|
|
// HINT: this method is quite frequently used in an imperative way.
|
|
// Since it has some desired side effects such as forcing a recomputation of the internals,
|
|
// it should be executed in any case, even if props == get_properties ().
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpSetAllProps (index, get_properties (), props));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
if (index == current_layer_list ()) {
|
|
begin_layer_updates ();
|
|
}
|
|
|
|
*m_layer_properties_lists [index] = props;
|
|
m_layer_properties_lists [index]->attach_view (this, index);
|
|
|
|
merge_dither_pattern (*m_layer_properties_lists [index]);
|
|
|
|
if (index == current_layer_list ()) {
|
|
end_layer_updates ();
|
|
layer_list_changed_event (3);
|
|
redraw_later ();
|
|
m_prop_changed = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::expand_properties ()
|
|
{
|
|
expand_properties (std::map<int, int> (), false);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::expand_properties (unsigned int index)
|
|
{
|
|
expand_properties (index, std::map<int, int> (), false);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::expand_properties (const std::map<int, int> &map_cv_index, bool add_default)
|
|
{
|
|
for (unsigned int i = 0; i < cellviews (); ++i) {
|
|
expand_properties (i, map_cv_index, add_default);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::expand_properties (unsigned int index, const std::map<int, int> &map_cv_index, bool add_default)
|
|
{
|
|
if (index < m_layer_properties_lists.size ()) {
|
|
m_layer_properties_lists [index]->expand (map_cv_index, add_default);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::replace_layer_node (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerPropertiesNode &node)
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
// if the source specification changed, a redraw is required
|
|
if (*iter != node) {
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpSetLayerPropsNode (index, (unsigned int) iter.uint (), *iter, node));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
if (index == current_layer_list ()) {
|
|
begin_layer_updates ();
|
|
}
|
|
|
|
LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
|
|
*non_const_iter = node;
|
|
non_const_iter->attach_view (this, index);
|
|
|
|
if (index == current_layer_list ()) {
|
|
end_layer_updates ();
|
|
layer_list_changed_event (2);
|
|
// TODO: check, if redraw is actually necessary (this is complex!)
|
|
redraw_later ();
|
|
m_prop_changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_layer_node_expanded (unsigned int index, const LayerPropertiesConstIterator &iter, bool ex)
|
|
{
|
|
if (ex != iter->expanded ()) {
|
|
|
|
LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
|
|
non_const_iter->set_expanded (ex);
|
|
|
|
if (index == current_layer_list ()) {
|
|
layer_list_changed_event (8 /*expanded state needs update*/);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_properties (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerProperties &props)
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
// if the source specification changed, a redraw is required
|
|
const LayerProperties &l = *iter;
|
|
if (l != props) {
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpSetLayerProps (index, (unsigned int) iter.uint (), l, props));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
bool need_redraw = (l.source (false /*local*/) != props.source (false /*local*/) || l.xfill (false /*local*/) != props.xfill (false /*local*/));
|
|
bool visible_changed = (l.visible (true /*real*/) != props.visible (true /*real*/));
|
|
|
|
LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
|
|
*non_const_iter = props;
|
|
|
|
if (index == current_layer_list ()) {
|
|
|
|
layer_list_changed_event (1);
|
|
|
|
if (need_redraw) {
|
|
redraw_later ();
|
|
}
|
|
|
|
if (visible_changed) {
|
|
m_visibility_changed = true;
|
|
}
|
|
|
|
// perform the callbacks asynchronously to collect the necessary calls instead
|
|
// of executing them immediately.
|
|
m_prop_changed = true;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
const LayerPropertiesNode &
|
|
LayoutViewBase::insert_layer (unsigned int index, const LayerPropertiesConstIterator &before, const LayerPropertiesNode &node)
|
|
{
|
|
tl_assert (index < layer_lists ());
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpInsertLayerProps (index, (unsigned int) before.uint (), node));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
if (index == current_layer_list ()) {
|
|
begin_layer_updates ();
|
|
}
|
|
|
|
const LayerPropertiesNode &ret = m_layer_properties_lists [index]->insert (LayerPropertiesIterator (*m_layer_properties_lists [index], before.uint ()), node);
|
|
|
|
// signal to the observers that something has changed
|
|
if (index == current_layer_list ()) {
|
|
end_layer_updates ();
|
|
layer_list_changed_event (2);
|
|
redraw_later ();
|
|
m_prop_changed = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::delete_layer (unsigned int index, LayerPropertiesConstIterator &iter)
|
|
{
|
|
if (index >= layer_lists ()) {
|
|
return;
|
|
}
|
|
|
|
lay::LayerPropertiesNode orig = *iter;
|
|
|
|
if (index == current_layer_list ()) {
|
|
begin_layer_updates ();
|
|
}
|
|
|
|
// delete the element
|
|
m_layer_properties_lists [index]->erase (LayerPropertiesIterator (*m_layer_properties_lists [index], iter.uint ()));
|
|
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpDeleteLayerProps (index, (unsigned int) iter.uint (), orig));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
// signal to the observers that something has changed
|
|
if (index == current_layer_list ()) {
|
|
end_layer_updates ();
|
|
layer_list_changed_event (2);
|
|
redraw_later ();
|
|
m_prop_changed = true;
|
|
}
|
|
|
|
// invalidate the iterator so it can be used to refer to the next element
|
|
iter.invalidate ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::save_as (unsigned int index, const std::string &filename, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update, int keep_backups)
|
|
{
|
|
tl_assert (index < cellviews ());
|
|
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Saving")));
|
|
cellview (index)->save_as (filename, om, options, update, keep_backups);
|
|
|
|
cellview_changed (index);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redo (db::Op *op)
|
|
{
|
|
tl_assert (! transacting ());
|
|
|
|
OpSetLayerProps *sop = dynamic_cast <OpSetLayerProps *> (op);
|
|
if (sop) {
|
|
if (sop->m_list_index < m_layer_properties_lists.size ()) {
|
|
set_properties (sop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [sop->m_list_index], sop->m_index), sop->m_new);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpSetLayerPropsNode *snop = dynamic_cast <OpSetLayerPropsNode *> (op);
|
|
if (snop) {
|
|
if (snop->m_list_index < m_layer_properties_lists.size ()) {
|
|
replace_layer_node (snop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [snop->m_list_index], snop->m_index), snop->m_new);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpInsertLayerList *ilop = dynamic_cast <OpInsertLayerList *> (op);
|
|
if (ilop) {
|
|
if (ilop->m_list_index <= m_layer_properties_lists.size ()) {
|
|
insert_layer_list (ilop->m_list_index, ilop->m_new);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpDeleteLayerList *dlop = dynamic_cast <OpDeleteLayerList *> (op);
|
|
if (dlop) {
|
|
if (dlop->m_list_index < m_layer_properties_lists.size ()) {
|
|
delete_layer_list (dlop->m_list_index);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpSetAllProps *saop = dynamic_cast <OpSetAllProps *> (op);
|
|
if (saop) {
|
|
if (saop->m_list_index < m_layer_properties_lists.size ()) {
|
|
set_properties (saop->m_list_index, saop->m_new);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpRenameProps *rnop = dynamic_cast <OpRenameProps *> (op);
|
|
if (rnop) {
|
|
if (rnop->m_list_index < m_layer_properties_lists.size ()) {
|
|
rename_properties (rnop->m_list_index, rnop->m_new);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpLayerList *lop = dynamic_cast <OpLayerList *> (op);
|
|
if (lop) {
|
|
if (lop->m_list_index < m_layer_properties_lists.size ()) {
|
|
if (lop->m_mode == OpLayerList::Insert) {
|
|
insert_layer (lop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [lop->m_list_index], lop->m_index), lop->m_node);
|
|
} else {
|
|
lay::LayerPropertiesConstIterator iter (*m_layer_properties_lists [lop->m_list_index], lop->m_index);
|
|
delete_layer (lop->m_list_index, iter);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpSetDitherPattern *stpop = dynamic_cast <OpSetDitherPattern *> (op);
|
|
if (stpop) {
|
|
set_dither_pattern (stpop->m_new);
|
|
return;
|
|
}
|
|
|
|
OpHideShowCell *hscop = dynamic_cast <OpHideShowCell *> (op);
|
|
if (hscop) {
|
|
if (hscop->m_show) {
|
|
show_cell (hscop->m_cell_index, hscop->m_cellview_index);
|
|
} else {
|
|
hide_cell (hscop->m_cell_index, hscop->m_cellview_index);
|
|
}
|
|
return;
|
|
}
|
|
|
|
db::Object::redo (op);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::undo (db::Op *op)
|
|
{
|
|
tl_assert (! transacting ());
|
|
|
|
OpSetLayerProps *sop = dynamic_cast <OpSetLayerProps *> (op);
|
|
if (sop) {
|
|
if (sop->m_list_index < m_layer_properties_lists.size ()) {
|
|
set_properties (sop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [sop->m_list_index], sop->m_index), sop->m_old);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpSetLayerPropsNode *snop = dynamic_cast <OpSetLayerPropsNode *> (op);
|
|
if (snop) {
|
|
if (snop->m_list_index < m_layer_properties_lists.size ()) {
|
|
replace_layer_node (snop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [snop->m_list_index], snop->m_index), snop->m_old);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpInsertLayerList *ilop = dynamic_cast <OpInsertLayerList *> (op);
|
|
if (ilop) {
|
|
if (ilop->m_list_index <= m_layer_properties_lists.size ()) {
|
|
delete_layer_list (ilop->m_list_index);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpDeleteLayerList *dlop = dynamic_cast <OpDeleteLayerList *> (op);
|
|
if (dlop) {
|
|
if (dlop->m_list_index < m_layer_properties_lists.size ()) {
|
|
insert_layer_list (dlop->m_list_index, dlop->m_old);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpSetAllProps *saop = dynamic_cast <OpSetAllProps *> (op);
|
|
if (saop) {
|
|
if (saop->m_list_index < m_layer_properties_lists.size ()) {
|
|
set_properties (saop->m_list_index, saop->m_old);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpRenameProps *rnop = dynamic_cast <OpRenameProps *> (op);
|
|
if (rnop) {
|
|
if (rnop->m_list_index < m_layer_properties_lists.size ()) {
|
|
rename_properties (rnop->m_list_index, rnop->m_old);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpLayerList *lop = dynamic_cast <OpLayerList *> (op);
|
|
if (lop) {
|
|
if (lop->m_list_index < m_layer_properties_lists.size ()) {
|
|
if (lop->m_mode == OpLayerList::Insert) {
|
|
lay::LayerPropertiesConstIterator iter (*m_layer_properties_lists [lop->m_list_index], lop->m_index);
|
|
delete_layer (lop->m_list_index, iter);
|
|
} else {
|
|
insert_layer (lop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [lop->m_list_index], lop->m_index), lop->m_node);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpHideShowCell *hscop = dynamic_cast <OpHideShowCell *> (op);
|
|
if (hscop) {
|
|
|
|
if (hscop->m_show) {
|
|
hide_cell (hscop->m_cell_index, hscop->m_cellview_index);
|
|
} else {
|
|
show_cell (hscop->m_cell_index, hscop->m_cellview_index);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
OpSetDitherPattern *stpop = dynamic_cast <OpSetDitherPattern *> (op);
|
|
if (stpop) {
|
|
set_dither_pattern (stpop->m_old);
|
|
return;
|
|
}
|
|
|
|
db::Object::undo (op);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_hier_changed ()
|
|
{
|
|
// schedule a redraw request for all layers
|
|
redraw_later ();
|
|
// forward this event to our observers
|
|
hier_changed_event ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_bboxes_from_layer_changed (unsigned int cv_index, unsigned int layer_index)
|
|
{
|
|
if (layer_index == std::numeric_limits<unsigned int>::max ()) {
|
|
|
|
// redraw all
|
|
signal_bboxes_changed ();
|
|
|
|
} else {
|
|
|
|
// redraw only the layers required for redrawing
|
|
for (std::vector<lay::RedrawLayerInfo>::const_iterator l = mp_canvas->get_redraw_layers ().begin (); l != mp_canvas->get_redraw_layers ().end (); ++l) {
|
|
if (l->cellview_index == int (cv_index) && l->layer_index == int (layer_index)) {
|
|
redraw_layer ((unsigned int) (l - mp_canvas->get_redraw_layers ().begin ()));
|
|
}
|
|
}
|
|
|
|
// forward this event to our observers
|
|
geom_changed_event ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_bboxes_changed ()
|
|
{
|
|
// schedule a redraw request for all layers
|
|
redraw_later ();
|
|
|
|
// forward this event to our observers
|
|
geom_changed_event ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_cell_name_changed ()
|
|
{
|
|
cell_visibility_changed_event (); // HINT: that is not what actually is intended, but it serves the function ...
|
|
redraw_later (); // needs redraw
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_layer_properties_changed ()
|
|
{
|
|
// recompute the source
|
|
// TODO: this is a side effect of this method - provide a special method for this purpose
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->attach_view (this, i);
|
|
}
|
|
|
|
// schedule a redraw request - since the layer views might not have changed, this is necessary
|
|
redraw_later ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_prop_ids_changed ()
|
|
{
|
|
// inform the layer list observers that they need to recompute the property selectors
|
|
layer_list_changed_event (1);
|
|
|
|
// recompute the source
|
|
// TODO: this is a side effect of this method - provide a special method for this purpose
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
m_layer_properties_lists [i]->attach_view (this, i);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_plugin_enabled_changed ()
|
|
{
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if ((*p)->editable_interface ()) {
|
|
enable ((*p)->editable_interface (), (*p)->plugin_declaration ()->editable_enabled ());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_annotations_changed ()
|
|
{
|
|
// schedule a redraw request for the annotation shapes
|
|
redraw_deco_layer ();
|
|
// forward this event to our observers
|
|
annotations_changed_event ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::finish_cellviews_changed ()
|
|
{
|
|
update_event_handlers ();
|
|
|
|
cellviews_changed_event ();
|
|
|
|
redraw_later ();
|
|
}
|
|
|
|
std::list<lay::CellView>::iterator
|
|
LayoutViewBase::cellview_iter (int cv_index)
|
|
{
|
|
std::list<lay::CellView>::iterator i = m_cellviews.begin ();
|
|
while (cv_index > 0 && i != m_cellviews.end ()) {
|
|
++i;
|
|
--cv_index;
|
|
}
|
|
tl_assert (i != m_cellviews.end ());
|
|
return i;
|
|
}
|
|
|
|
std::list<lay::CellView>::const_iterator
|
|
LayoutViewBase::cellview_iter (int cv_index) const
|
|
{
|
|
std::list<lay::CellView>::const_iterator i = m_cellviews.begin ();
|
|
while (cv_index > 0 && i != m_cellviews.end ()) {
|
|
++i;
|
|
--cv_index;
|
|
}
|
|
tl_assert (i != m_cellviews.end ());
|
|
return i;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::erase_cellview (unsigned int index)
|
|
{
|
|
if (index >= m_cellviews.size ()) {
|
|
return;
|
|
}
|
|
|
|
cancel_esc ();
|
|
|
|
// issue to event that signals a change in the cellviews
|
|
cellviews_about_to_change_event ();
|
|
|
|
// no undo available - clear all transactions
|
|
if (manager ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
begin_layer_updates ();
|
|
|
|
m_cellviews.erase (cellview_iter (int (index)));
|
|
|
|
if (m_hidden_cells.size () > index) {
|
|
m_hidden_cells.erase (m_hidden_cells.begin () + index);
|
|
}
|
|
|
|
if (m_current_cell_per_cellview.size () > index) {
|
|
m_current_cell_per_cellview.erase (m_current_cell_per_cellview.begin () + index);
|
|
}
|
|
|
|
for (unsigned int lindex = 0; lindex < layer_lists (); ++lindex) {
|
|
|
|
// remove all references to the cellview
|
|
m_layer_properties_lists [lindex]->remove_cv_references (index);
|
|
|
|
// rename the ones that got shifted.
|
|
lay::LayerPropertiesConstIterator l = begin_layers (lindex);
|
|
while (! l.at_end ()) {
|
|
lay::ParsedLayerSource source (l->source (false));
|
|
if (source.cv_index () >= int (index)) {
|
|
lay::LayerProperties new_props (*l);
|
|
source.cv_index (source.cv_index () == int (index) ? -1 : source.cv_index () - 1);
|
|
new_props.set_source (source);
|
|
LayerPropertiesIterator non_const_iter (*m_layer_properties_lists [lindex], l.uint ());
|
|
*non_const_iter = new_props;
|
|
}
|
|
++l;
|
|
}
|
|
|
|
}
|
|
|
|
// clear the history
|
|
m_display_states.clear ();
|
|
m_display_state_ptr = 0;
|
|
|
|
end_layer_updates ();
|
|
|
|
// signal to the observers that something has changed
|
|
layer_list_changed_event (3);
|
|
|
|
finish_cellviews_changed ();
|
|
|
|
update_content ();
|
|
|
|
if (m_title.empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::clear_cellviews ()
|
|
{
|
|
// issue to event that signals a change in the cellviews
|
|
cellviews_about_to_change_event ();
|
|
|
|
// no undo available - clear all transactions
|
|
if (manager ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
// clear the layer lists and cellviews
|
|
while (layer_lists () > 0) {
|
|
delete_layer_list (layer_lists () - 1);
|
|
}
|
|
set_properties (lay::LayerPropertiesList ());
|
|
m_cellviews.clear ();
|
|
|
|
m_hidden_cells.clear ();
|
|
m_current_cell_per_cellview.clear ();
|
|
|
|
// clear the history, store path and zoom box
|
|
m_display_states.clear ();
|
|
m_display_state_ptr = 0;
|
|
|
|
finish_cellviews_changed ();
|
|
|
|
if (m_title.empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
const CellView &
|
|
LayoutViewBase::cellview (unsigned int index) const
|
|
{
|
|
static const CellView empty;
|
|
if (index >= m_cellviews.size ()) {
|
|
return empty;
|
|
} else {
|
|
return *cellview_iter (int (index));
|
|
}
|
|
}
|
|
|
|
CellViewRef
|
|
LayoutViewBase::cellview_ref (unsigned int index)
|
|
{
|
|
if (index >= m_cellviews.size ()) {
|
|
return CellViewRef ();
|
|
} else {
|
|
return CellViewRef (cellview_iter (index).operator-> (), this);
|
|
}
|
|
}
|
|
|
|
int
|
|
LayoutViewBase::index_of_cellview (const lay::CellView *cv) const
|
|
{
|
|
int index = 0;
|
|
for (std::list<CellView>::const_iterator i = m_cellviews.begin (); i != m_cellviews.end (); ++i, ++index) {
|
|
if (cv == i.operator-> ()) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_layout (const lay::CellView &cv, unsigned int cvindex)
|
|
{
|
|
// issue to event that signals a change in the cellviews
|
|
cellviews_about_to_change_event ();
|
|
|
|
// no undo available - clear all transactions
|
|
if (manager ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
// signal the change of layer properties to the observer
|
|
layer_list_changed_event (3);
|
|
|
|
// create a new cellview if required
|
|
while (m_cellviews.size () <= cvindex) {
|
|
m_cellviews.push_back (lay::CellView ());
|
|
}
|
|
|
|
// set the handle reference and clear all cell related stuff
|
|
*cellview_iter (cvindex) = cv;
|
|
|
|
// clear the history, store path and zoom box
|
|
clear_states ();
|
|
|
|
finish_cellviews_changed ();
|
|
|
|
// since the hierarchy panel may hold cellviews, we explicitly request an initialization
|
|
// of the tree. This will release such references. This way, set_layout guarantees that
|
|
// the layouts are released as far as possible. This is important for reload () for example.
|
|
update_content_for_cv (cvindex);
|
|
|
|
if (m_title.empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::signal_apply_technology (lay::LayoutHandle *layout_handle)
|
|
{
|
|
// find the cellview which issued the event
|
|
for (unsigned int i = 0; i < cellviews (); ++i) {
|
|
|
|
if (cellview (i).handle () == layout_handle) {
|
|
|
|
cancel_esc ();
|
|
|
|
std::string lyp_file;
|
|
const db::Technology *tech = db::Technologies::instance ()->technology_by_name (cellview (i)->tech_name ());
|
|
if (tech && ! tech->eff_layer_properties_file ().empty ()) {
|
|
lyp_file = tech->eff_layer_properties_file ();
|
|
}
|
|
|
|
if (! lyp_file.empty ()) {
|
|
|
|
// interpolate the layout properties file name
|
|
tl::Eval expr;
|
|
expr.set_var ("layoutfile", cellview (i)->filename ());
|
|
lyp_file = expr.interpolate (lyp_file);
|
|
|
|
// remove all references to the cellview in the layer properties
|
|
for (unsigned int lindex = 0; lindex < layer_lists (); ++lindex) {
|
|
m_layer_properties_lists [lindex]->remove_cv_references (i);
|
|
}
|
|
|
|
// if a layer properties file is set, create the layer properties now
|
|
create_initial_layer_props (i, lyp_file, tech->add_other_layers ());
|
|
|
|
}
|
|
|
|
apply_technology_event (int (i));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::bookmarks (const BookmarkList &b)
|
|
{
|
|
m_bookmarks = b;
|
|
bookmarks_changed ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::bookmark_view (const std::string &name)
|
|
{
|
|
DisplayState state (box (), get_min_hier_levels (), get_max_hier_levels (), cellview_list ());
|
|
m_bookmarks.add (name, state);
|
|
bookmarks_changed ();
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_single_cv_layer_properties_file (const std::string &fn)
|
|
{
|
|
// If the file contains information for a single layout but we have multiple ones,
|
|
// show the dialog to determine what layout to apply the information to.
|
|
std::vector<lay::LayerPropertiesList> props;
|
|
try {
|
|
tl::XMLFileSource in (fn);
|
|
props.push_back (lay::LayerPropertiesList ());
|
|
props.back ().load (in);
|
|
} catch (...) {
|
|
props.clear ();
|
|
tl::XMLFileSource in (fn);
|
|
lay::LayerPropertiesList::load (in, props);
|
|
}
|
|
|
|
// Collect all cv indices in the layer properties
|
|
std::set <int> cv;
|
|
for (std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin (); p != props.end (); ++p) {
|
|
for (lay::LayerPropertiesConstIterator lp = p->begin_const_recursive (); ! lp.at_end (); ++lp) {
|
|
if (! lp->has_children ()) {
|
|
cv.insert (lp->source (true).cv_index ());
|
|
if (cv.size () >= 2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (cv.size () == 1);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::load_layer_props (const std::string &fn)
|
|
{
|
|
do_load_layer_props (fn, false, -1, false);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::load_layer_props (const std::string &fn, bool add_default)
|
|
{
|
|
do_load_layer_props (fn, false, -1, add_default);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::load_layer_props (const std::string &fn, int cv_index, bool add_default)
|
|
{
|
|
do_load_layer_props (fn, true, cv_index, add_default);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_load_layer_props (const std::string &fn, bool map_cv, int cv_index, bool add_default)
|
|
{
|
|
std::vector<lay::LayerPropertiesList> props;
|
|
bool single_list = false;
|
|
|
|
// read the layer properties from the file
|
|
try {
|
|
tl::XMLFileSource in (fn);
|
|
props.push_back (lay::LayerPropertiesList ());
|
|
props.back ().load (in);
|
|
single_list = true;
|
|
} catch (...) {
|
|
props.clear ();
|
|
tl::XMLFileSource in (fn);
|
|
lay::LayerPropertiesList::load (in, props);
|
|
}
|
|
|
|
// expand the wildcards and map to the target cv.
|
|
for (std::vector<lay::LayerPropertiesList>::iterator p = props.begin (); p != props.end (); ++p) {
|
|
std::map <int, int> cv_map;
|
|
if (map_cv) {
|
|
cv_map.insert (std::make_pair (-1, cv_index));
|
|
}
|
|
p->attach_view (this, p - props.begin ());
|
|
p->expand (cv_map, add_default);
|
|
}
|
|
|
|
transaction (tl::to_string (tr ("Load layer properties")));
|
|
|
|
if (single_list) {
|
|
|
|
// a single list will only replace the current tab
|
|
if (map_cv && cv_index >= 0) {
|
|
lay::LayerPropertiesList new_props (get_properties ());
|
|
new_props.remove_cv_references (cv_index);
|
|
new_props.append (props [0]);
|
|
set_properties (new_props);
|
|
} else {
|
|
set_properties (props [0]);
|
|
}
|
|
|
|
} else {
|
|
|
|
for (unsigned int i = 0; i < props.size (); ++i) {
|
|
|
|
if (i < layer_lists ()) {
|
|
|
|
if (map_cv && cv_index >= 0) {
|
|
lay::LayerPropertiesList new_props (get_properties (i));
|
|
new_props.remove_cv_references (cv_index);
|
|
new_props.append (props [i]);
|
|
set_properties (i, new_props);
|
|
} else {
|
|
set_properties (i, props [i]);
|
|
}
|
|
|
|
} else {
|
|
insert_layer_list (i, props [i]);
|
|
}
|
|
|
|
}
|
|
|
|
while (layer_lists () > props.size () && layer_lists () > 1) {
|
|
delete_layer_list (layer_lists () - 1);
|
|
}
|
|
|
|
}
|
|
|
|
commit ();
|
|
|
|
update_content ();
|
|
|
|
tl::log << "Loaded layer properties from " << fn;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::save_layer_props (const std::string &fn)
|
|
{
|
|
tl::OutputStream os (fn, tl::OutputStream::OM_Plain);
|
|
|
|
if (layer_lists () == 1) {
|
|
|
|
// a single list is written in the traditional format
|
|
get_properties ().save (os);
|
|
|
|
} else {
|
|
|
|
// multiple tabs are written in the multi-tab format
|
|
std::vector<lay::LayerPropertiesList> props;
|
|
for (unsigned int i = 0; i < layer_lists (); ++i) {
|
|
props.push_back (get_properties (i));
|
|
}
|
|
|
|
lay::LayerPropertiesList::save (os, props);
|
|
|
|
}
|
|
|
|
tl::log << "Saved layer properties to " << fn;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::add_new_layers (const std::vector <unsigned int> &layer_ids, int cv_index)
|
|
{
|
|
if (cv_index >= 0 && cv_index < int (cellviews ())) {
|
|
|
|
const lay::CellView &cv = cellview (cv_index);
|
|
|
|
// create the layers and do a basic recoloring ..
|
|
lay::LayerPropertiesList new_props (get_properties ());
|
|
|
|
bool was_empty = new_props.begin_const_recursive ().at_end ();
|
|
|
|
// don't create new layers for those, for which there are layers already: compute a
|
|
// set of layers already present
|
|
std::set <db::LayerProperties, db::LPLogicalLessFunc> present_layers;
|
|
for (LayerPropertiesConstIterator lay_iter = begin_layers (); ! lay_iter.at_end (); ++lay_iter) {
|
|
if (! lay_iter->has_children () && lay_iter->cellview_index () == cv_index) {
|
|
present_layers.insert (lay_iter->source (true /*real*/).layer_props ());
|
|
}
|
|
}
|
|
|
|
// determine layers which are new and need to be created
|
|
std::vector <db::LayerProperties> new_layers;
|
|
for (std::vector <unsigned int>::const_iterator l = layer_ids.begin (); l != layer_ids.end (); ++l) {
|
|
const db::LayerProperties &lp = cv->layout ().get_properties (*l);
|
|
if (present_layers.find (lp) == present_layers.end ()) {
|
|
new_layers.push_back (lp);
|
|
}
|
|
}
|
|
|
|
// create them in the sorting order provided by db::LayerProperties
|
|
std::sort (new_layers.begin (), new_layers.end (), db::LPLogicalLessFunc ());
|
|
|
|
// and actually create them
|
|
for (std::vector <db::LayerProperties>::const_iterator l = new_layers.begin (); l != new_layers.end (); ++l) {
|
|
lay::LayerProperties p;
|
|
p.set_source (lay::ParsedLayerSource (*l, cv_index));
|
|
init_layer_properties (p, new_props);
|
|
new_props.push_back (p);
|
|
}
|
|
|
|
set_properties (new_props);
|
|
|
|
if (was_empty) {
|
|
set_current_layer (new_props.begin_const_recursive ());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::init_layer_properties (LayerProperties &p) const
|
|
{
|
|
init_layer_properties (p, get_properties ());
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::init_layer_properties (LayerProperties &p, const LayerPropertiesList &lp_list) const
|
|
{
|
|
tl::color_t c = 0;
|
|
if (m_palette.luminous_colors () > 0) {
|
|
c = m_palette.luminous_color_by_index (p.source (true /*real*/).color_index ());
|
|
}
|
|
|
|
p.set_dither_pattern (m_stipple_palette.standard_stipple_by_index (lp_list.end_const () - lp_list.begin_const ()));
|
|
p.set_fill_color (c);
|
|
p.set_frame_color (c);
|
|
p.set_fill_brightness (0);
|
|
p.set_frame_brightness (0);
|
|
p.set_frame_brightness (0);
|
|
p.set_transparent (false); // :TODO: make variable
|
|
p.set_visible (true);
|
|
p.set_width (1);
|
|
p.set_animation (0);
|
|
p.set_marked (false);
|
|
}
|
|
|
|
#if defined(HAVE_QT)
|
|
QImage
|
|
LayoutViewBase::get_screenshot ()
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->screenshot ().to_image_copy ();
|
|
}
|
|
#endif
|
|
|
|
tl::PixelBuffer
|
|
LayoutViewBase::get_screenshot_pb ()
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->screenshot ();
|
|
}
|
|
|
|
static std::vector<std::pair<std::string, std::string> >
|
|
png_texts (const lay::LayoutViewBase *view, const db::DBox &box)
|
|
{
|
|
std::vector<std::pair<std::string, std::string> > texts;
|
|
|
|
// Unfortunately the PNG writer does not allow writing of long strings.
|
|
// We separate the description into a set of keys:
|
|
|
|
for (unsigned int i = 0; i < view->cellviews (); ++i) {
|
|
if (view->cellview (i).is_valid ()) {
|
|
std::string name = view->cellview (i)->layout ().cell_name (view->cellview (i).cell_index ());
|
|
texts.push_back (std::make_pair (std::string ("Cell") + tl::to_string (int (i) + 1), name));
|
|
}
|
|
}
|
|
|
|
texts.push_back (std::make_pair (std::string ("Rect"), box.to_string ()));
|
|
|
|
return texts;
|
|
}
|
|
|
|
#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE)
|
|
void
|
|
LayoutViewBase::save_screenshot (const std::string &fn)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot")));
|
|
|
|
QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));
|
|
|
|
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, box ());
|
|
for (auto i = texts.begin (); i != texts.end (); ++i) {
|
|
writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second));
|
|
}
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
if (! writer.write (mp_canvas->screenshot ().to_image ())) {
|
|
throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
|
|
}
|
|
|
|
tl::log << "Saved screen shot to " << fn;
|
|
}
|
|
#elif defined(HAVE_PNG)
|
|
void
|
|
LayoutViewBase::save_screenshot (const std::string &fn)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
tl::OutputStream stream (fn);
|
|
tl::PixelBuffer img = mp_canvas->screenshot ();
|
|
img.set_texts (png_texts (this, box ()));
|
|
img.write_png (stream);
|
|
|
|
tl::log << "Saved screen shot to " << fn;
|
|
}
|
|
#else
|
|
void
|
|
LayoutViewBase::save_screenshot (const std::string &)
|
|
{
|
|
throw tl::Exception (tl::to_string (tr ("Unable to write screenshot - PNG library not compiled in")));
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_QT)
|
|
QImage
|
|
LayoutViewBase::get_image (unsigned int width, unsigned int height)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->image (width, height).to_image_copy ();
|
|
}
|
|
#endif
|
|
|
|
tl::PixelBuffer
|
|
LayoutViewBase::get_pixels (unsigned int width, unsigned int height)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->image (width, height);
|
|
}
|
|
|
|
#if defined(HAVE_QT)
|
|
QImage
|
|
LayoutViewBase::get_image_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
|
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box, bool monochrome)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
if (monochrome) {
|
|
return mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box).to_image_copy ();
|
|
} else {
|
|
return mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box).to_image_copy ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
tl::PixelBuffer
|
|
LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
|
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box);
|
|
}
|
|
|
|
tl::BitmapBuffer
|
|
LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int height, int linewidth,
|
|
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image")));
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
return mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box);
|
|
}
|
|
|
|
#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE)
|
|
void
|
|
LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned int height)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save image")));
|
|
|
|
QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));
|
|
|
|
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
|
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
|
|
for (auto i = texts.begin (); i != texts.end (); ++i) {
|
|
writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second));
|
|
}
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
if (! writer.write (mp_canvas->image (width, height).to_image ())) {
|
|
throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
|
|
}
|
|
|
|
tl::log << "Saved image to " << fn;
|
|
}
|
|
#elif defined(HAVE_PNG)
|
|
void
|
|
LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned int height)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save image")));
|
|
|
|
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
tl::OutputStream stream (fn);
|
|
tl::PixelBuffer img = mp_canvas->image (width, height);
|
|
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
|
|
img.set_texts (texts);
|
|
img.write_png (stream);
|
|
|
|
tl::log << "Saved image to " << fn;
|
|
}
|
|
#else
|
|
void
|
|
LayoutViewBase::save_image (const std::string &, unsigned int, unsigned int)
|
|
{
|
|
throw tl::Exception (tl::to_string (tr ("Unable to save image - PNG library not compiled in")));
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_QT) && !defined(PREFER_LIBPNG_FOR_SAVE)
|
|
void
|
|
LayoutViewBase::save_image_with_options (const std::string &fn,
|
|
unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
|
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box, bool monochrome)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save image")));
|
|
|
|
QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));
|
|
|
|
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
|
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
|
|
for (auto i = texts.begin (); i != texts.end (); ++i) {
|
|
writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second));
|
|
}
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
if (monochrome) {
|
|
if (! writer.write (mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box).to_image ())) {
|
|
throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
|
|
}
|
|
} else {
|
|
if (! writer.write (mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box).to_image ())) {
|
|
throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
|
|
}
|
|
}
|
|
|
|
tl::log << "Saved image to " << fn;
|
|
}
|
|
#elif defined(HAVE_PNG)
|
|
void
|
|
LayoutViewBase::save_image_with_options (const std::string &fn,
|
|
unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution,
|
|
tl::Color background, tl::Color foreground, tl::Color active, const db::DBox &target_box, bool monochrome)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save image")));
|
|
|
|
lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
|
|
std::vector<std::pair<std::string, std::string> > texts = png_texts (this, vp.box ());
|
|
|
|
// Execute all deferred methods - ensure there are no pending tasks
|
|
tl::DeferredMethodScheduler::execute ();
|
|
|
|
tl::OutputStream stream (fn);
|
|
if (monochrome) {
|
|
|
|
tl::BitmapBuffer img = mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box);
|
|
img.set_texts (texts);
|
|
img.write_png (stream);
|
|
|
|
} else {
|
|
|
|
tl::PixelBuffer img = mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box);
|
|
img.set_texts (texts);
|
|
img.write_png (stream);
|
|
|
|
}
|
|
|
|
tl::log << "Saved image to " << fn;
|
|
}
|
|
#else
|
|
void
|
|
LayoutViewBase::save_image_with_options (const std::string &,
|
|
unsigned int, unsigned int, int, int, double,
|
|
tl::Color, tl::Color, tl::Color, const db::DBox &, bool)
|
|
{
|
|
throw tl::Exception (tl::to_string (tr ("Unable to save image - PNG library not compiled in")));
|
|
}
|
|
#endif
|
|
|
|
void
|
|
LayoutViewBase::reload_layout (unsigned int cv_index)
|
|
{
|
|
stop ();
|
|
cancel_esc ();
|
|
|
|
// save the current view state
|
|
lay::DisplayState state;
|
|
save_view (state);
|
|
|
|
// this is the cellview at the given index (use a copy since the original is overwritten)
|
|
CellView cvorg = cellview (cv_index);
|
|
|
|
// obtain the original filename
|
|
std::string filename = cvorg->filename ();
|
|
std::string technology = cvorg->tech_name ();
|
|
std::string name = cvorg->name ();
|
|
|
|
// recreate hidden cells by doing a name referencing
|
|
std::vector <std::string> hidden_cells;
|
|
if (m_hidden_cells.size () > cv_index) {
|
|
hidden_cells.reserve (m_hidden_cells [cv_index].size ());
|
|
for (std::set <cell_index_type>::const_iterator ci = m_hidden_cells [cv_index].begin (); ci != m_hidden_cells [cv_index].end (); ++ci) {
|
|
hidden_cells.push_back (std::string (cvorg->layout ().cell_name (*ci)));
|
|
}
|
|
}
|
|
|
|
// Set up a list of present layers
|
|
std::set <db::LayerProperties, db::LPLogicalLessFunc> present_layers;
|
|
for (LayerPropertiesConstIterator lay_iter = begin_layers (); ! lay_iter.at_end (); ++lay_iter) {
|
|
if (! lay_iter->has_children ()) {
|
|
present_layers.insert (lay_iter->source (true /*real*/).layer_props ());
|
|
}
|
|
}
|
|
|
|
std::map <unsigned int, db::LayerProperties> org_layers;
|
|
|
|
for (unsigned int i = 0; i < cvorg->layout ().layers (); ++i) {
|
|
if (cvorg->layout ().is_valid_layer (i)) {
|
|
const db::LayerProperties &p = cvorg->layout ().get_properties (i);
|
|
if (! p.log_equal (db::LayerProperties ())) {
|
|
org_layers.insert (std::make_pair (i, p));
|
|
}
|
|
}
|
|
}
|
|
|
|
lay::LayoutHandle *handle;
|
|
|
|
// reset the layout: create a dummy handle and install this in between
|
|
// this will clear the original layout if not further referenced.
|
|
// Since the dummy layout will act as a placeholder if something goes wrong
|
|
// when reading the file, it must have the layers created as well
|
|
lay::CellView cv_empty;
|
|
|
|
handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
|
|
handle->set_tech_name (technology);
|
|
cv_empty.set (handle);
|
|
|
|
for (std::map <unsigned int, db::LayerProperties>::const_iterator ol = org_layers.begin (); ol != org_layers.end (); ++ol) {
|
|
cv_empty->layout ().insert_layer (ol->first, ol->second);
|
|
}
|
|
cv_empty->rename (name, true);
|
|
|
|
set_layout (cv_empty, cv_index);
|
|
|
|
// create a new handle
|
|
lay::CellView cv;
|
|
handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
|
|
cv.set (handle);
|
|
|
|
try {
|
|
|
|
// re-create the layers required
|
|
for (std::map <unsigned int, db::LayerProperties>::const_iterator ol = org_layers.begin (); ol != org_layers.end (); ++ol) {
|
|
cv->layout ().insert_layer (ol->first, ol->second);
|
|
}
|
|
|
|
{
|
|
tl::log << tl::to_string (tr ("Loading file: ")) << filename;
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Loading")));
|
|
|
|
// Load with the previous options again.
|
|
db::LoadLayoutOptions options (cvorg->load_options ());
|
|
cv->load (cvorg->load_options (), technology);
|
|
}
|
|
|
|
// sort the layout explicitly here. Otherwise it would be done
|
|
// implicitly at some other time. This may throw an exception
|
|
// if the operation was cancelled.
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Sorting")));
|
|
cv->layout ().update ();
|
|
}
|
|
|
|
// print the memory statistics now.
|
|
if (tl::verbosity () >= 31) {
|
|
db::MemStatisticsCollector m (false);
|
|
cv->layout ().mem_stat (&m, db::MemStatistics::LayoutInfo, 0);
|
|
m.print ();
|
|
}
|
|
|
|
// this is required to release every reference to the cv_empty layout
|
|
cv_empty = lay::CellView ();
|
|
|
|
// install the new layout
|
|
cv->rename (name, true);
|
|
set_layout (cv, cv_index);
|
|
|
|
} catch (...) {
|
|
update_content ();
|
|
throw;
|
|
}
|
|
|
|
// recreate the hidden cell indices from the names
|
|
if (m_hidden_cells.size () > cv_index) {
|
|
m_hidden_cells [cv_index].clear ();
|
|
for (std::vector <std::string>::const_iterator cn = hidden_cells.begin (); cn != hidden_cells.end (); ++cn) {
|
|
std::pair<bool, cell_index_type> cid = cv->layout ().cell_by_name (cn->c_str ());
|
|
if (cid.first) {
|
|
m_hidden_cells [cv_index].insert (cid.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
// clear the current cell (NOTE: this is for providing a cell target for some UI functions only)
|
|
if (m_current_cell_per_cellview.size () > cv_index) {
|
|
m_current_cell_per_cellview [cv_index] = cell_path_type ();
|
|
}
|
|
|
|
// Determine which layers to create as new layers. New layer need to be created
|
|
// if these have not been present in the original layout and there are no layer views
|
|
// referring to them.
|
|
std::vector <db::LayerProperties> new_layers;
|
|
for (unsigned int i = 0; i < cv->layout ().layers (); ++i) {
|
|
if (cv->layout ().is_valid_layer (i)) {
|
|
std::map <unsigned int, db::LayerProperties>::iterator ol = org_layers.find (i);
|
|
if (ol == org_layers.end () && present_layers.find (cv->layout ().get_properties (i)) == present_layers.end ()) {
|
|
new_layers.push_back (cv->layout ().get_properties (i));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort (new_layers.begin (), new_layers.end (), db::LPLogicalLessFunc ());
|
|
|
|
// create the layers and do a basic recoloring ..
|
|
lay::LayerPropertiesList new_props (get_properties ());
|
|
|
|
for (std::vector <db::LayerProperties>::const_iterator l = new_layers.begin (); l != new_layers.end (); ++l) {
|
|
lay::LayerProperties p;
|
|
p.set_source (lay::ParsedLayerSource (*l, int (cv_index)));
|
|
init_layer_properties (p, new_props);
|
|
new_props.push_back (p);
|
|
}
|
|
|
|
set_properties (new_props);
|
|
|
|
goto_view (state);
|
|
}
|
|
|
|
static void
|
|
get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers)
|
|
{
|
|
db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file");
|
|
db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers");
|
|
|
|
for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
|
|
if (meta->first == layer_properties_file_name_id) {
|
|
lyp_file = meta->second.value.to_string ();
|
|
}
|
|
if (meta->first == layer_properties_add_other_layers_name_id) {
|
|
try {
|
|
add_other_layers = meta->second.value.to_bool ();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers)
|
|
{
|
|
unsigned int cv_index = 0;
|
|
|
|
try {
|
|
|
|
enable_active_cellview_changed_event (false);
|
|
|
|
stop_redraw ();
|
|
|
|
bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());
|
|
|
|
lay::CellView cv;
|
|
|
|
if (! add_cellview) {
|
|
clear_cellviews ();
|
|
}
|
|
|
|
cv.set (layout_handle);
|
|
|
|
cv->layout ().update ();
|
|
|
|
// select the cell with the largest area as the first top cell
|
|
db::Layout::top_down_const_iterator top = cv->layout ().begin_top_down ();
|
|
for (db::Layout::top_down_const_iterator t = cv->layout ().begin_top_down (); t != cv->layout ().end_top_cells (); ++t) {
|
|
if (cv->layout ().cell (*t).bbox ().area () > cv->layout ().cell (*top).bbox ().area ()) {
|
|
top = t;
|
|
}
|
|
}
|
|
|
|
if (top != cv->layout ().end_top_down ()) {
|
|
std::vector <db::cell_index_type> p;
|
|
p.push_back (*top);
|
|
cv.set_unspecific_path (p);
|
|
}
|
|
|
|
cv_index = cellviews ();
|
|
set_layout (cv, cv_index);
|
|
|
|
if (top != cv->layout ().end_top_cells ()) {
|
|
std::vector <db::cell_index_type> p;
|
|
p.push_back (*top);
|
|
select_cell (p, cv_index);
|
|
}
|
|
|
|
// even if there is no cell, select the cellview item
|
|
// to support applications with an active cellview (that is however invalid)
|
|
set_active_cellview_index (cv_index);
|
|
|
|
if (initialize_layers) {
|
|
|
|
bool add_other_layers = m_add_other_layers;
|
|
|
|
// Use the "layer-properties-file" meta info from the handle to get the layer properties file.
|
|
// If no such file is present, use the default file or the technology specific file.
|
|
std::string lyp_file = m_def_lyp_file;
|
|
const db::Technology *tech = db::Technologies::instance ()->technology_by_name (layout_handle->tech_name ());
|
|
if (tech && ! tech->eff_layer_properties_file ().empty ()) {
|
|
lyp_file = tech->eff_layer_properties_file ();
|
|
add_other_layers = tech->add_other_layers ();
|
|
}
|
|
|
|
// Give the layout object a chance to specify a certain layer property file
|
|
get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
|
|
|
|
// interpolate the layout properties file name
|
|
tl::Eval expr;
|
|
expr.set_var ("layoutfile", layout_handle->filename ());
|
|
lyp_file = expr.interpolate (lyp_file);
|
|
|
|
// create the initial layer properties
|
|
create_initial_layer_props (cv_index, lyp_file, add_other_layers);
|
|
|
|
}
|
|
|
|
// select the first layer if nothing else is selected
|
|
if (cv_index == 0) {
|
|
ensure_layer_selected ();
|
|
}
|
|
|
|
// signal to any observers
|
|
file_open_event ();
|
|
|
|
if (cv->layout ().begin_top_down () != cv->layout ().end_top_down ()) {
|
|
|
|
// do a fit and update layer lists etc.
|
|
zoom_fit ();
|
|
if (set_max_hier) {
|
|
max_hier ();
|
|
}
|
|
update_content ();
|
|
|
|
} else {
|
|
// even if there is no cell, select the cellview item
|
|
// to support applications with an active cellview (that is however invalid)
|
|
set_active_cellview_index (cv_index);
|
|
}
|
|
|
|
enable_active_cellview_changed_event (true);
|
|
|
|
} catch (...) {
|
|
|
|
update_content ();
|
|
|
|
enable_active_cellview_changed_event (true, true);
|
|
throw;
|
|
|
|
}
|
|
|
|
return cv_index;
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::create_layout (const std::string &technology, bool add_cellview, bool initialize_layers)
|
|
{
|
|
const db::Technology *tech = db::Technologies::instance ()->technology_by_name (technology);
|
|
|
|
db::Layout *layout = new db::Layout (m_editable, manager ());
|
|
if (tech) {
|
|
layout->dbu (tech->dbu ());
|
|
}
|
|
|
|
lay::LayoutHandle *handle = new lay::LayoutHandle (layout, "");
|
|
handle->set_tech_name (technology);
|
|
return add_layout (handle, add_cellview, initialize_layers);
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::load_layout (const std::string &filename, const std::string &technology, bool add_cellview)
|
|
{
|
|
return load_layout (filename, db::LoadLayoutOptions (), technology, add_cellview);
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOptions &options, const std::string &technology, bool add_cellview)
|
|
{
|
|
stop ();
|
|
|
|
bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());
|
|
|
|
const db::Technology *tech = db::Technologies::instance ()->technology_by_name (technology);
|
|
|
|
// create a new layout handle
|
|
lay::CellView cv;
|
|
lay::LayoutHandle *handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
|
|
cv.set (handle);
|
|
|
|
unsigned int cv_index;
|
|
db::LayerMap lmap;
|
|
|
|
try {
|
|
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Loading")));
|
|
|
|
// load the file
|
|
{
|
|
tl::log << tl::to_string (tr ("Loading file: ")) << filename << tl::to_string (tr (" with technology: ")) << technology;
|
|
lmap = cv->load (options, technology);
|
|
}
|
|
|
|
// sort the layout explicitly here. Otherwise it would be done
|
|
// implicitly at some other time. This may throw an exception
|
|
// if the operation was cancelled.
|
|
{
|
|
cv->layout ().update ();
|
|
}
|
|
|
|
// print the memory statistics now.
|
|
if (tl::verbosity () >= 31) {
|
|
db::MemStatisticsCollector m (false);
|
|
cv->layout ().mem_stat (&m, db::MemStatistics::LayoutInfo, 0);
|
|
m.print ();
|
|
}
|
|
|
|
// clear the cellviews if required
|
|
if (! add_cellview) {
|
|
clear_cellviews ();
|
|
}
|
|
|
|
// set the new layout as the layout for the last cellview
|
|
cv_index = cellviews ();
|
|
set_layout (cv, cv_index);
|
|
|
|
} catch (...) {
|
|
|
|
update_content ();
|
|
throw;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
enable_active_cellview_changed_event (false);
|
|
|
|
// select the cell with the largest area as the first top cell
|
|
db::Layout::top_down_const_iterator top = cv->layout ().begin_top_down ();
|
|
for (db::Layout::top_down_const_iterator t = cv->layout ().begin_top_down (); t != cv->layout ().end_top_cells (); ++t) {
|
|
if (cv->layout ().cell (*t).bbox ().area () > cv->layout ().cell (*top).bbox ().area ()) {
|
|
top = t;
|
|
}
|
|
}
|
|
if (top != cv->layout ().end_top_cells ()) {
|
|
std::vector <db::cell_index_type> p;
|
|
p.push_back (*top);
|
|
select_cell (p, cv_index);
|
|
}
|
|
|
|
// force "active_cellview_changed" event
|
|
m_active_cellview_index = -1;
|
|
|
|
// even if there is no cell, select the cellview item
|
|
// to support applications with an active cellview (that is however invalid)
|
|
set_active_cellview_index (cv_index);
|
|
|
|
bool add_other_layers = m_add_other_layers;
|
|
|
|
// Use the "layer-properties-file" meta info from the handle to get the layer properties file.
|
|
// If no such file is present, use the default file or the technology specific file.
|
|
std::string lyp_file = m_def_lyp_file;
|
|
if (tech && ! tech->eff_layer_properties_file ().empty ()) {
|
|
lyp_file = tech->eff_layer_properties_file ();
|
|
add_other_layers = tech->add_other_layers ();
|
|
}
|
|
|
|
// Give the layout object a chance to specify a certain layer property file
|
|
get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
|
|
|
|
// interpolate the layout properties file name
|
|
tl::Eval expr;
|
|
expr.set_var ("layoutfile", filename);
|
|
lyp_file = expr.interpolate (lyp_file);
|
|
|
|
// create the initial layer properties
|
|
create_initial_layer_props (cv_index, lyp_file, add_other_layers);
|
|
|
|
// select the first layer if nothing else is selected
|
|
if (cv_index == 0) {
|
|
ensure_layer_selected ();
|
|
}
|
|
|
|
// signal to any observers
|
|
file_open_event ();
|
|
|
|
// do a fit and update layer lists etc.
|
|
zoom_fit ();
|
|
if (set_max_hier) {
|
|
max_hier ();
|
|
}
|
|
update_content ();
|
|
|
|
enable_active_cellview_changed_event (true);
|
|
|
|
} catch (...) {
|
|
|
|
update_content ();
|
|
|
|
enable_active_cellview_changed_event (true, true /*silent*/);
|
|
throw;
|
|
|
|
}
|
|
|
|
return cv_index;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::create_initial_layer_props (int cv_index, const std::string &lyp_file, bool add_missing)
|
|
{
|
|
std::vector<lay::LayerPropertiesList> props;
|
|
bool loaded = false;
|
|
|
|
if (! lyp_file.empty ()) {
|
|
|
|
// read the layer properties from the file
|
|
try {
|
|
|
|
try {
|
|
tl::XMLFileSource in (lyp_file);
|
|
props.push_back (lay::LayerPropertiesList ());
|
|
props.back ().load (in);
|
|
loaded = true;
|
|
} catch (...) {
|
|
props.clear ();
|
|
tl::XMLFileSource in (lyp_file);
|
|
tl::log << tl::to_string (tr ("Loading layer properties file: ")) << lyp_file;
|
|
lay::LayerPropertiesList::load (in, props);
|
|
loaded = true;
|
|
}
|
|
|
|
} catch (tl::Exception &ex) {
|
|
tl::warn << tl::to_string (tr ("Initialization of layers failed: ")) << ex.msg ();
|
|
} catch (...) {
|
|
tl::warn << tl::to_string (tr ("Initialization of layers failed: unspecific error"));
|
|
}
|
|
|
|
}
|
|
|
|
std::map <int, int> cv_map;
|
|
cv_map.insert (std::make_pair (-1, cv_index));
|
|
|
|
if (! loaded) {
|
|
|
|
props.clear ();
|
|
props.push_back (lay::LayerPropertiesList ());
|
|
|
|
} else {
|
|
|
|
// do't map cv's if the input file is a multi-cv one.
|
|
std::set <int> cv;
|
|
for (std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin (); p != props.end (); ++p) {
|
|
for (lay::LayerPropertiesConstIterator lp = p->begin_const_recursive (); ! lp.at_end (); ++lp) {
|
|
if (! lp->has_children ()) {
|
|
cv.insert (lp->source (true).cv_index ());
|
|
if (cv.size () >= 2) {
|
|
cv_map.clear ();
|
|
cv_map.insert (std::make_pair (cv_index, cv_index));
|
|
// erase the others:
|
|
cv_map.insert (std::make_pair (-1, -2));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// expand the wildcards and map to the target cv.
|
|
for (std::vector<lay::LayerPropertiesList>::iterator p = props.begin (); p != props.end (); ++p) {
|
|
p->attach_view (this, p - props.begin ());
|
|
p->expand (cv_map, add_missing || !loaded);
|
|
}
|
|
|
|
merge_layer_props (props);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::merge_layer_props (const std::vector<lay::LayerPropertiesList> &props)
|
|
{
|
|
lay::LayerPropertiesList p0;
|
|
if (layer_lists () > 0) {
|
|
p0 = get_properties (0);
|
|
}
|
|
|
|
// merge the new layer views into the present ones
|
|
// If the specific list is a single list (no tabs), it is merged into every tab present.
|
|
if (props.size () == 1) {
|
|
|
|
for (size_t n = 0; n < layer_lists () || n == 0; ++n) {
|
|
|
|
std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin ();
|
|
|
|
if (n < layer_lists ()) {
|
|
lay::LayerPropertiesList new_props (get_properties ((unsigned int) n));
|
|
new_props.append (*p);
|
|
if (! p->name ().empty ()) {
|
|
new_props.set_name (p->name ());
|
|
}
|
|
set_properties ((unsigned int) n, new_props);
|
|
} else {
|
|
|
|
lay::LayerPropertiesList new_props = p0;
|
|
new_props.append (*p);
|
|
if (! p->name ().empty ()) {
|
|
new_props.set_name (p->name ());
|
|
}
|
|
insert_layer_list ((unsigned int) n, new_props);
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
size_t n = 0;
|
|
for (std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin (); p != props.end (); ++p, ++n) {
|
|
|
|
if (n < layer_lists ()) {
|
|
lay::LayerPropertiesList new_props (get_properties ((unsigned int) n));
|
|
new_props.append (*p);
|
|
if (! p->name ().empty ()) {
|
|
new_props.set_name (p->name ());
|
|
}
|
|
set_properties ((unsigned int) n, new_props);
|
|
} else {
|
|
lay::LayerPropertiesList new_props = p0;
|
|
new_props.append (*p);
|
|
if (! p->name ().empty ()) {
|
|
new_props.set_name (p->name ());
|
|
}
|
|
insert_layer_list ((unsigned int) n, new_props);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pop_state ()
|
|
{
|
|
if (m_display_state_ptr > 0) {
|
|
m_display_states.erase (m_display_states.begin () + m_display_state_ptr, m_display_states.end ());
|
|
--m_display_state_ptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::clear_states ()
|
|
{
|
|
m_display_states.clear ();
|
|
m_display_state_ptr = 0;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::store_state ()
|
|
{
|
|
// erase all states after the current position
|
|
if (m_display_state_ptr + 1 < m_display_states.size ()) {
|
|
m_display_states.erase (m_display_states.begin () + m_display_state_ptr + 1, m_display_states.end ());
|
|
}
|
|
|
|
// save the state
|
|
DisplayState state (box (), get_min_hier_levels (), get_max_hier_levels (), m_cellviews);
|
|
m_display_states.push_back (state);
|
|
|
|
m_display_state_ptr = (unsigned int) (m_display_states.size () - 1);
|
|
}
|
|
|
|
db::DBox
|
|
LayoutViewBase::box () const
|
|
{
|
|
return mp_canvas->viewport ().box ();
|
|
}
|
|
|
|
LayoutView *
|
|
LayoutViewBase::get_ui ()
|
|
{
|
|
return mp_ui;
|
|
}
|
|
|
|
// NOTE: this methods needs to be called "frequently"
|
|
void
|
|
LayoutViewBase::timer ()
|
|
{
|
|
bool dirty = false;
|
|
for (std::list<lay::CellView>::const_iterator i = m_cellviews.begin (); i != m_cellviews.end () && ! dirty; ++i) {
|
|
dirty = (*i).is_valid () && (*i)->layout ().is_editable () && (*i)->is_dirty ();
|
|
}
|
|
|
|
if (dirty != m_dirty) {
|
|
m_dirty = dirty;
|
|
emit_dirty_changed ();
|
|
}
|
|
|
|
if (m_prop_changed) {
|
|
do_prop_changed ();
|
|
m_prop_changed = false;
|
|
}
|
|
|
|
tl::Clock current_time = tl::Clock::current ();
|
|
if ((current_time - m_last_checked).seconds () > animation_interval) {
|
|
m_last_checked = current_time;
|
|
if (m_animated) {
|
|
set_view_ops ();
|
|
do_set_phase (int (m_phase));
|
|
if (m_animated) {
|
|
++m_phase;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::force_update_content ()
|
|
{
|
|
set_view_ops ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::update_content ()
|
|
{
|
|
if (is_activated ()) {
|
|
set_view_ops ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_fit_sel ()
|
|
{
|
|
db::DBox bbox = selection_bbox ();
|
|
if (! bbox.empty ()) {
|
|
bbox = db::DBox (bbox.left () - 0.025 * bbox.width (), bbox.bottom () - 0.025 * bbox.height (),
|
|
bbox.right () + 0.025 * bbox.width (), bbox.top () + 0.025 * bbox.height ());
|
|
zoom_box (bbox);
|
|
}
|
|
}
|
|
|
|
db::DBox
|
|
LayoutViewBase::full_box () const
|
|
{
|
|
// compute the bounding box over all layers
|
|
// this will trigger the update procedures of the layout objects if not done yet ..
|
|
|
|
db::DBox bbox;
|
|
|
|
for (LayerPropertiesConstIterator l = get_properties ().begin_const_recursive (); ! l.at_end (); ++l) {
|
|
bbox += l->bbox ();
|
|
}
|
|
|
|
for (lay::AnnotationShapes::iterator a = annotation_shapes ().begin (); ! a.at_end (); ++a) {
|
|
bbox += a->box ();
|
|
}
|
|
|
|
if (bbox.empty ()) {
|
|
bbox = db::DBox (0, 0, 0, 0); // default box
|
|
} else {
|
|
bbox = db::DBox (bbox.left () - 0.025 * bbox.width (), bbox.bottom () - 0.025 * bbox.height (),
|
|
bbox.right () + 0.025 * bbox.width (), bbox.top () + 0.025 * bbox.height ());
|
|
}
|
|
|
|
return bbox;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_fit ()
|
|
{
|
|
mp_canvas->zoom_box (full_box (), true /*precious*/);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ensure_selection_visible ()
|
|
{
|
|
ensure_visible (selection_bbox ());
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ensure_visible (const db::DBox &bbox)
|
|
{
|
|
db::DBox new_box = bbox + viewport ().box ();
|
|
mp_canvas->zoom_box (new_box);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_box_and_set_hier_levels (const db::DBox &bbox, const std::pair<int, int> &levels)
|
|
{
|
|
mp_canvas->zoom_box (bbox);
|
|
set_hier_levels_basic (levels);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_box (const db::DBox &bbox)
|
|
{
|
|
mp_canvas->zoom_box (bbox);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_global_trans (const db::DCplxTrans &trans)
|
|
{
|
|
mp_canvas->set_global_trans (trans);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_trans (const db::DCplxTrans &trans)
|
|
{
|
|
mp_canvas->zoom_trans (trans);
|
|
store_state ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_left ()
|
|
{
|
|
shift_window (1.0, -m_pan_distance, 0.0);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_right ()
|
|
{
|
|
shift_window (1.0, m_pan_distance, 0.0);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_up ()
|
|
{
|
|
shift_window (1.0, 0.0, m_pan_distance);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_down ()
|
|
{
|
|
shift_window (1.0, 0.0, -m_pan_distance);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_left_fast ()
|
|
{
|
|
shift_window (1.0, -m_pan_distance * fast_factor, 0.0);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_right_fast ()
|
|
{
|
|
shift_window (1.0, m_pan_distance * fast_factor, 0.0);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_up_fast ()
|
|
{
|
|
shift_window (1.0, 0.0, m_pan_distance * fast_factor);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_down_fast ()
|
|
{
|
|
shift_window (1.0, 0.0, -m_pan_distance * fast_factor);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_center (const db::DPoint &p)
|
|
{
|
|
db::DBox b = mp_canvas->viewport ().box ();
|
|
db::DVector d (b.width () * 0.5, b.height () * 0.5);
|
|
zoom_box (db::DBox (p - d, p + d));
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_in ()
|
|
{
|
|
zoom_by (zoom_factor);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_out ()
|
|
{
|
|
zoom_by (1.0 / zoom_factor);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::zoom_by (double f)
|
|
{
|
|
db::DBox b = mp_canvas->viewport ().box ();
|
|
|
|
db::DPoint c = b.center ();
|
|
if (mp_canvas->mouse_in_window ()) {
|
|
c = mp_canvas->mouse_position_um ();
|
|
}
|
|
|
|
zoom_box ((b.moved (db::DPoint () - c) * f).moved (c - db::DPoint ()));
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::shift_window (double f, double dx, double dy)
|
|
{
|
|
db::DBox b = mp_canvas->viewport ().box ();
|
|
|
|
db::DPoint s = mp_canvas->viewport ().global_trans ().inverted () * db::DPoint (dx, dy);
|
|
db::DPoint c = b.center () + db::DVector (b.width () * s.x (), b.height () * s.y ());
|
|
|
|
double w = b.width () * f;
|
|
double h = b.height () * f;
|
|
|
|
db::DVector d (w * 0.5, h * 0.5);
|
|
|
|
zoom_box (db::DBox (c - d, c + d));
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::goto_window (const db::DPoint &p, double s)
|
|
{
|
|
if (s > 1e-6) {
|
|
db::DBox b (p.x () - s * 0.5, p.y () - s * 0.5, p.x () + s * 0.5, p.y () + s * 0.5);
|
|
zoom_box (b);
|
|
} else {
|
|
db::DBox b (box ());
|
|
b.move (p - b.center ());
|
|
zoom_box (b);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redraw_layer (unsigned int index)
|
|
{
|
|
do_redraw (index);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redraw_cell_boxes ()
|
|
{
|
|
do_redraw (lay::draw_boxes_queue_entry);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redraw_deco_layer ()
|
|
{
|
|
// redraw background annotations (images etc.)
|
|
mp_canvas->touch_bg ();
|
|
|
|
// redraw other annotations:
|
|
do_redraw (lay::draw_custom_queue_entry);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redraw_later ()
|
|
{
|
|
dm_redraw ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::redraw ()
|
|
{
|
|
std::vector <lay::RedrawLayerInfo> layers;
|
|
|
|
size_t nlayers = 0;
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children ()) {
|
|
++nlayers;
|
|
}
|
|
}
|
|
layers.reserve (nlayers);
|
|
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children ()) {
|
|
layers.push_back (RedrawLayerInfo (*l));
|
|
}
|
|
}
|
|
|
|
mp_canvas->redraw_new (layers);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cancel_edits ()
|
|
{
|
|
// cancel all drag and pending edit operations such as move operations.
|
|
mp_canvas->drag_cancel ();
|
|
lay::Editables::cancel_edits ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cancel ()
|
|
{
|
|
// cancel all drags and pending edit operations such as move operations.
|
|
cancel_edits ();
|
|
// re-enable edit mode
|
|
enable_edits (true);
|
|
// and clear the selection
|
|
clear_selection ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cancel_esc ()
|
|
{
|
|
cancel ();
|
|
switch_mode (default_mode ());
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::goto_view (const DisplayState &state)
|
|
{
|
|
mp_canvas->zoom_box (state.box ());
|
|
|
|
std::list <lay::CellView> cellviews;
|
|
for (unsigned int i = 0; i < m_cellviews.size (); ++i) {
|
|
cellviews.push_back (state.cellview (i, cellview_iter (i)->operator-> ()));
|
|
}
|
|
|
|
select_cellviews (cellviews);
|
|
|
|
if (state.min_hier () <= state.max_hier ()) {
|
|
set_hier_levels_basic (std::make_pair (state.min_hier (), state.max_hier ()));
|
|
}
|
|
|
|
update_content ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::save_view (DisplayState &state) const
|
|
{
|
|
state = DisplayState (box (), get_min_hier_levels (), get_max_hier_levels (), m_cellviews);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_redraw (int layer)
|
|
{
|
|
std::vector<int> layers;
|
|
layers.push_back (layer);
|
|
|
|
mp_canvas->redraw_selected (layers);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_prop_changed ()
|
|
{
|
|
if (m_visibility_changed) {
|
|
|
|
// change visibility and redraw exposed layers
|
|
std::vector<bool> visibility;
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children ()) {
|
|
visibility.push_back (l->visible (true /*real*/));
|
|
}
|
|
}
|
|
mp_canvas->change_visibility (visibility);
|
|
|
|
m_visibility_changed = false;
|
|
|
|
}
|
|
|
|
update_content ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_view_ops ()
|
|
{
|
|
bool bright_background = (mp_canvas->background_color ().to_mono ());
|
|
int brightness_for_context = ((bright_background ? m_ctx_dimming : -m_ctx_dimming) * 256) / 100;
|
|
int brightness_for_child_context = ((bright_background ? m_child_ctx_dimming : -m_child_ctx_dimming) * 256) / 100;
|
|
|
|
// count the layers to be able to reserve the number of view_ops
|
|
size_t nlayers = 0;
|
|
for (LayerPropertiesConstIterator lp = get_properties ().begin_const_recursive (); !lp.at_end (); ++lp) {
|
|
if (! lp->has_children ()) {
|
|
++nlayers;
|
|
}
|
|
}
|
|
|
|
std::vector <lay::ViewOp> view_ops;
|
|
view_ops.reserve (nlayers * planes_per_layer + special_planes_before + special_planes_after);
|
|
|
|
tl::Color box_color;
|
|
if (! m_box_color.is_valid ()) {
|
|
box_color = mp_canvas->foreground_color ();
|
|
} else {
|
|
box_color = m_box_color;
|
|
}
|
|
|
|
// cell boxes
|
|
if (m_cell_box_visible) {
|
|
|
|
lay::ViewOp vop;
|
|
|
|
// context level
|
|
if (m_ctx_color.is_valid ()) {
|
|
vop = lay::ViewOp (m_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
|
|
} else {
|
|
vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0);
|
|
}
|
|
|
|
// fill, frame, text, vertex
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
|
|
// child level
|
|
if (m_child_ctx_color.is_valid ()) {
|
|
vop = lay::ViewOp (m_child_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
|
|
} else {
|
|
vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0);
|
|
}
|
|
|
|
// fill, frame, text, vertex
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
|
|
// current level
|
|
vop = lay::ViewOp (box_color.rgb (), lay::ViewOp::Copy, 0, 0, 0);
|
|
|
|
// fill, frame, text, vertex
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (vop);
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
|
|
} else {
|
|
// invisible
|
|
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; ++i) { // frame, fill, vertex, text
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
}
|
|
}
|
|
|
|
// sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout
|
|
tl_assert (view_ops.size () == (size_t)cell_box_planes);
|
|
|
|
// produce the ViewOps for the guiding shapes
|
|
|
|
tl::color_t gs_color = box_color.rgb ();
|
|
if (m_guiding_shape_color.is_valid ()) {
|
|
gs_color = m_guiding_shape_color.rgb ();
|
|
}
|
|
|
|
for (int ctx = 0; ctx < 3; ++ctx) { // 0 (context), 1 (child), 2 (current)
|
|
|
|
lay::ViewOp::Mode mode = lay::ViewOp::Copy;
|
|
|
|
tl::color_t fill_color, frame_color, text_color;
|
|
int dp = 1; // no stipples for guiding shapes
|
|
|
|
if (ctx == 0) {
|
|
|
|
// context planes
|
|
if (m_ctx_color.is_valid ()) {
|
|
frame_color = text_color = fill_color = m_ctx_color.rgb ();
|
|
} else {
|
|
frame_color = text_color = fill_color = lay::LayerProperties::brighter (gs_color, brightness_for_context);
|
|
}
|
|
|
|
if (m_ctx_hollow) {
|
|
dp = 1;
|
|
}
|
|
|
|
} else if (ctx == 1) {
|
|
|
|
// child level planes (if used)
|
|
if (m_child_ctx_color.is_valid ()) {
|
|
frame_color = text_color = fill_color = m_child_ctx_color.rgb ();
|
|
} else {
|
|
frame_color = text_color = fill_color = lay::LayerProperties::brighter (gs_color, brightness_for_child_context);
|
|
}
|
|
|
|
if (m_child_ctx_hollow) {
|
|
dp = 1;
|
|
}
|
|
|
|
} else {
|
|
|
|
// current level planes
|
|
frame_color = text_color = fill_color = gs_color;
|
|
|
|
}
|
|
|
|
if (m_guiding_shape_visible) {
|
|
|
|
// fill
|
|
view_ops.push_back (lay::ViewOp (fill_color, mode, 0, dp, 0)); // fill
|
|
|
|
// frame
|
|
view_ops.push_back (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Rect, m_guiding_shape_line_width));
|
|
|
|
// text
|
|
if (m_text_visible) {
|
|
view_ops.push_back (lay::ViewOp (text_color, mode, 0, 0, 0));
|
|
} else {
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
}
|
|
|
|
// vertex
|
|
view_ops.push_back (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Rect, m_guiding_shape_vertex_size /*mark size*/)); // vertex
|
|
|
|
} else {
|
|
view_ops.push_back (lay::ViewOp ());
|
|
view_ops.push_back (lay::ViewOp ());
|
|
view_ops.push_back (lay::ViewOp ());
|
|
view_ops.push_back (lay::ViewOp ());
|
|
}
|
|
|
|
}
|
|
|
|
// sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout
|
|
tl_assert (view_ops.size () == (size_t)special_planes_before);
|
|
|
|
bool animated = false;
|
|
|
|
for (int ctx = 0; ctx < 3; ++ctx) { // 0 (context), 1 (child), 2 (current)
|
|
|
|
unsigned int ilayer = 0;
|
|
for (LayerPropertiesConstIterator lp = get_properties ().begin_const_recursive (); !lp.at_end (); ++lp, ++ilayer) {
|
|
|
|
// because accessing the LayerPropertiesNode with lp->... is not quite efficient, we get the pointer here:
|
|
const lay::LayerPropertiesNode *l = &*lp;
|
|
|
|
if (l->has_children ()) {
|
|
continue;
|
|
}
|
|
|
|
bool animate_visible = true;
|
|
unsigned int di_off = m_stipple_offset ? ilayer : 0;
|
|
|
|
if (l->animation (true /*real*/)) {
|
|
|
|
animated = true;
|
|
if (! m_animated) {
|
|
m_animated = true;
|
|
m_phase = 0;
|
|
}
|
|
|
|
if (l->animation (true /*real*/) == 1) {
|
|
// scrolling
|
|
di_off += m_phase;
|
|
} else if (l->animation (true /*real*/) == 2) {
|
|
// blinking
|
|
animate_visible = ((m_phase & 1) == 0);
|
|
} else {
|
|
// inversely blinking
|
|
animate_visible = ((m_phase & 1) != 0);
|
|
}
|
|
|
|
}
|
|
|
|
if (l->visible (true /*real*/) && animate_visible) {
|
|
|
|
lay::ViewOp::Mode mode = lay::ViewOp::Copy;
|
|
if (l->transparent (true /*real*/)) {
|
|
if (bright_background) {
|
|
mode = lay::ViewOp::And;
|
|
} else {
|
|
mode = lay::ViewOp::Or;
|
|
}
|
|
}
|
|
|
|
tl::color_t fill_color, frame_color, text_color;
|
|
int dp = m_no_stipples ? 1 : l->dither_pattern (true /*real*/);
|
|
int ls = l->line_style (true /*real*/);
|
|
|
|
if (ctx == 0) {
|
|
|
|
// context planes
|
|
if (m_ctx_color.is_valid ()) {
|
|
frame_color = text_color = fill_color = m_ctx_color.rgb ();
|
|
} else {
|
|
fill_color = l->eff_fill_color_brighter (true /*real*/, brightness_for_context);
|
|
frame_color = l->eff_frame_color_brighter (true /*real*/, brightness_for_context);
|
|
if (m_text_color.is_valid ()) {
|
|
text_color = lay::LayerProperties::brighter (m_text_color.rgb (), brightness_for_context);
|
|
} else {
|
|
text_color = frame_color;
|
|
}
|
|
}
|
|
|
|
if (m_ctx_hollow) {
|
|
dp = 1;
|
|
}
|
|
|
|
} else if (ctx == 1) {
|
|
|
|
// child level planes (if used)
|
|
if (m_child_ctx_color.is_valid ()) {
|
|
frame_color = text_color = fill_color = m_child_ctx_color.rgb ();
|
|
} else {
|
|
fill_color = l->eff_fill_color_brighter (true /*real*/, brightness_for_child_context);
|
|
frame_color = l->eff_frame_color_brighter (true /*real*/, brightness_for_child_context);
|
|
if (m_text_color.is_valid ()) {
|
|
text_color = lay::LayerProperties::brighter (m_text_color.rgb (), brightness_for_child_context);
|
|
} else {
|
|
text_color = frame_color;
|
|
}
|
|
}
|
|
|
|
if (m_child_ctx_hollow) {
|
|
dp = 1;
|
|
}
|
|
|
|
} else {
|
|
|
|
// current level planes
|
|
fill_color = l->eff_fill_color (true /*real*/);
|
|
frame_color = l->eff_frame_color (true /*real*/);
|
|
if (m_text_color.is_valid ()) {
|
|
text_color = m_text_color.rgb ();
|
|
} else {
|
|
text_color = frame_color;
|
|
}
|
|
|
|
}
|
|
|
|
// fill
|
|
view_ops.push_back (lay::ViewOp (fill_color, mode, 0, dp, di_off)); // fill
|
|
|
|
// frame
|
|
int lw = l->width (true /*real*/);
|
|
if (lw < 0) {
|
|
// default line width is 0 for parents and 1 for leafs
|
|
lw = l->has_children () ? 0 : 1;
|
|
}
|
|
view_ops.push_back (lay::ViewOp (frame_color, mode, ls, 0, 0, lay::ViewOp::Rect, lw));
|
|
|
|
// text
|
|
if (m_text_visible) {
|
|
view_ops.push_back (lay::ViewOp (text_color, mode, 0, 0, 0));
|
|
} else {
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
}
|
|
// vertex
|
|
view_ops.push_back (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, l->marked (true /*real*/) ? 9/*mark size*/ : 0)); // vertex
|
|
|
|
} else {
|
|
for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
|
|
view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! animated) {
|
|
m_animated = false;
|
|
m_phase = 0;
|
|
}
|
|
|
|
mp_canvas->set_view_ops (view_ops);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::guiding_shapes_visible (bool v)
|
|
{
|
|
if (v != m_guiding_shape_visible) {
|
|
m_guiding_shape_visible = v;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::guiding_shapes_color (tl::Color c)
|
|
{
|
|
if (c != m_guiding_shape_color) {
|
|
m_guiding_shape_color = c;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::guiding_shapes_line_width (int v)
|
|
{
|
|
if (v != m_guiding_shape_line_width) {
|
|
m_guiding_shape_line_width = v;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::guiding_shapes_vertex_size (int v)
|
|
{
|
|
if (v != m_guiding_shape_vertex_size) {
|
|
m_guiding_shape_vertex_size = v;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::draw_array_border_instances (bool m)
|
|
{
|
|
if (m != m_draw_array_border_instances) {
|
|
m_draw_array_border_instances = m;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::drop_small_cells (bool m)
|
|
{
|
|
if (m != m_drop_small_cells) {
|
|
m_drop_small_cells = m;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::drop_small_cells_value (unsigned int s)
|
|
{
|
|
if (s != m_drop_small_cells_value) {
|
|
m_drop_small_cells_value = s;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::drop_small_cells_cond (drop_small_cells_cond_type t)
|
|
{
|
|
if (t != m_drop_small_cells_cond) {
|
|
m_drop_small_cells_cond = t;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cell_box_color (tl::Color c)
|
|
{
|
|
if (c != m_box_color) {
|
|
m_box_color = c;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cell_box_text_transform (bool xform)
|
|
{
|
|
if (xform != m_box_text_transform) {
|
|
m_box_text_transform = xform;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cell_box_text_font (unsigned int f)
|
|
{
|
|
if (f != m_box_font) {
|
|
m_box_font = f;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::set_hier_levels_basic (std::pair<int, int> l)
|
|
{
|
|
if (l != get_hier_levels ()) {
|
|
|
|
m_from_level = l.first;
|
|
m_to_level = l.second;
|
|
|
|
// notify all connected observers
|
|
hier_levels_changed_event ();
|
|
|
|
redraw ();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_hier_levels (std::pair<int, int> l)
|
|
{
|
|
if (set_hier_levels_basic (l)) {
|
|
store_state ();
|
|
}
|
|
}
|
|
|
|
std::pair<int, int>
|
|
LayoutViewBase::get_hier_levels () const
|
|
{
|
|
return std::make_pair (m_from_level, m_to_level);
|
|
}
|
|
|
|
/**
|
|
* @brief set the maximum hierarchy level to the number of levels available
|
|
*/
|
|
void
|
|
LayoutViewBase::max_hier ()
|
|
{
|
|
// determine the maximum level of hierarchies
|
|
int max_level = max_hier_level ();
|
|
|
|
// and set the levels
|
|
if (max_level > 0) {
|
|
set_hier_levels (std::make_pair (std::min (m_from_level, max_level), max_level));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief determine the maximum hierarchy level
|
|
*/
|
|
int
|
|
LayoutViewBase::max_hier_level () const
|
|
{
|
|
int max_level = 0;
|
|
for (std::list <CellView>::const_iterator cv = m_cellviews.begin (); cv != m_cellviews.end (); ++cv) {
|
|
if (cv->is_valid ()) {
|
|
int nl = cv->ctx_cell ()->hierarchy_levels () + 1;
|
|
if (nl > max_level) {
|
|
max_level = nl;
|
|
}
|
|
}
|
|
}
|
|
return max_level;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns a value indicating whether the maximum level is shown
|
|
*/
|
|
bool
|
|
LayoutViewBase::has_max_hier () const
|
|
{
|
|
int ml = max_hier_level ();
|
|
return ml > 0 && m_to_level >= ml;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_palette (const lay::ColorPalette &p)
|
|
{
|
|
m_palette = p;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_palette (const lay::StipplePalette &p)
|
|
{
|
|
m_stipple_palette = p;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_palette (const lay::LineStylePalette &p)
|
|
{
|
|
m_line_style_palette = p;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ctx_color (tl::Color c)
|
|
{
|
|
if (c != m_ctx_color) {
|
|
m_ctx_color = c;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ctx_dimming (int d)
|
|
{
|
|
if (d != m_ctx_dimming) {
|
|
m_ctx_dimming = d;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ctx_hollow (bool h)
|
|
{
|
|
if (h != m_ctx_hollow) {
|
|
m_ctx_hollow = h;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::child_ctx_color (tl::Color c)
|
|
{
|
|
if (c != m_child_ctx_color) {
|
|
m_child_ctx_color = c;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::child_ctx_dimming (int d)
|
|
{
|
|
if (d != m_child_ctx_dimming) {
|
|
m_child_ctx_dimming = d;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::child_ctx_hollow (bool h)
|
|
{
|
|
if (h != m_child_ctx_hollow) {
|
|
m_child_ctx_hollow = h;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::child_ctx_enabled (bool f)
|
|
{
|
|
if (f != m_child_ctx_enabled) {
|
|
m_child_ctx_enabled = f;
|
|
update_content ();
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::abstract_mode_width (double w)
|
|
{
|
|
if (fabs (w - m_abstract_mode_width) > 1e-6) {
|
|
m_abstract_mode_width = w;
|
|
if (m_abstract_mode_enabled) {
|
|
redraw ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::abstract_mode_enabled (bool e)
|
|
{
|
|
if (e != m_abstract_mode_enabled) {
|
|
m_abstract_mode_enabled = e;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
tl::Color
|
|
LayoutViewBase::default_background_color ()
|
|
{
|
|
return tl::Color (0, 0, 0); // black.
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_set_background_color (tl::Color /*color*/, tl::Color /*contrast*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::background_color (tl::Color c)
|
|
{
|
|
if (c == mp_canvas->background_color ()) {
|
|
return;
|
|
}
|
|
|
|
// replace by "real" background color if required
|
|
if (! c.is_valid ()) {
|
|
c = default_background_color ();
|
|
}
|
|
|
|
tl::Color contrast;
|
|
if (c.to_mono ()) {
|
|
contrast = tl::Color (0, 0, 0);
|
|
} else {
|
|
contrast = tl::Color (255, 255, 255);
|
|
}
|
|
|
|
do_set_background_color (c, contrast);
|
|
|
|
if (mp_selection_service) {
|
|
mp_selection_service->set_colors (c, contrast);
|
|
}
|
|
if (mp_zoom_service) {
|
|
mp_zoom_service->set_colors (c, contrast);
|
|
}
|
|
|
|
// Set the color for all ViewService interfaces
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
lay::ViewService *svc = (*p)->view_service_interface ();
|
|
if (svc) {
|
|
svc->set_colors (c, contrast);
|
|
}
|
|
}
|
|
|
|
mp_canvas->set_colors (c, contrast, mp_canvas->active_color ());
|
|
|
|
update_content ();
|
|
|
|
background_color_changed_event ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::dbu_coordinates (bool f)
|
|
{
|
|
m_dbu_coordinates = f;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::absolute_coordinates (bool f)
|
|
{
|
|
m_absolute_coordinates = f;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cellviews_fit (const std::list <CellView> &cvs)
|
|
{
|
|
if (m_cellviews != cvs) {
|
|
|
|
for (int index = 0; index < int (m_cellviews.size ()); ++index) {
|
|
cellview_about_to_change_event (index);
|
|
}
|
|
|
|
cellviews_about_to_change_event ();
|
|
|
|
set_min_hier_levels (0);
|
|
cancel_esc ();
|
|
m_cellviews = cvs;
|
|
zoom_fit ();
|
|
finish_cellviews_changed ();
|
|
|
|
for (int index = 0; index < int (m_cellviews.size ()); ++index) {
|
|
cellview_changed (index);
|
|
}
|
|
|
|
update_content ();
|
|
|
|
} else {
|
|
zoom_fit ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cellview_changed (unsigned int index)
|
|
{
|
|
update_content_for_cv (index);
|
|
|
|
cellview_changed_event (index);
|
|
|
|
if (m_title.empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
|
|
const lay::CellView &
|
|
LayoutViewBase::active_cellview () const
|
|
{
|
|
return cellview ((unsigned int) active_cellview_index ());
|
|
}
|
|
|
|
lay::CellViewRef
|
|
LayoutViewBase::active_cellview_ref ()
|
|
{
|
|
return cellview_ref ((unsigned int) active_cellview_index ());
|
|
}
|
|
|
|
int
|
|
LayoutViewBase::active_cellview_index () const
|
|
{
|
|
return m_active_cellview_index;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_active_cellview_index (int index)
|
|
{
|
|
if (index >= 0 && index < int (cellviews ())) {
|
|
if (m_active_cellview_index != index) {
|
|
m_active_cellview_index = index;
|
|
active_cellview_changed (index);
|
|
}
|
|
} else {
|
|
m_active_cellview_index = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::selected_cells_paths (int /*cv_index*/, std::vector<cell_path_type> & /*paths*/) const
|
|
{
|
|
// TODO: not implemented yet as there is no setter so far.
|
|
// (but it is implemented in the UI version where it is bound to the hierarchy control panel)
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::current_cell_path (int cv_index, cell_path_type &path) const
|
|
{
|
|
if (cv_index >= 0 && cv_index < int (m_current_cell_per_cellview.size ())) {
|
|
path = m_current_cell_per_cellview [cv_index];
|
|
} else {
|
|
path = cell_path_type ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_current_cell_path (int cv_index, const cell_path_type &path)
|
|
{
|
|
if (cv_index >= 0) {
|
|
while (cv_index <= int (m_current_cell_per_cellview.size ())) {
|
|
m_current_cell_per_cellview.push_back (cell_path_type ());
|
|
}
|
|
m_current_cell_per_cellview [cv_index] = path;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_change_active_cellview ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::enable_active_cellview_changed_event (bool enable, bool silent)
|
|
{
|
|
if (m_active_cellview_changed_event_enabled == enable) {
|
|
return;
|
|
}
|
|
|
|
m_active_cellview_changed_event_enabled = enable;
|
|
if (enable) {
|
|
|
|
if (!silent && ! m_active_cellview_changed_events.empty ()) {
|
|
|
|
// deliver stored events
|
|
|
|
// we need to cancel pending drawing or dragging operations to reflect the new cellview (different target, may have different technology etc.)
|
|
cancel_esc ();
|
|
|
|
// we need to setup the editor option pages because the technology may have changed
|
|
do_change_active_cellview ();
|
|
|
|
active_cellview_changed_event ();
|
|
for (std::set<int>::const_iterator i = m_active_cellview_changed_events.begin (); i != m_active_cellview_changed_events.end (); ++i) {
|
|
active_cellview_changed_with_index_event (*i);
|
|
}
|
|
|
|
// Because the title reflects the active one, emit a title changed event
|
|
if (title_string ().empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_active_cellview_changed_events.clear ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::active_cellview_changed (int index)
|
|
{
|
|
if (m_active_cellview_changed_event_enabled) {
|
|
|
|
// we need to cancel pending drawing or dragging operations to reflect the new cellview (different target, may have different technology etc.)
|
|
cancel_esc ();
|
|
|
|
// we need to setup the editor option pages because the technology may have changed
|
|
do_change_active_cellview ();
|
|
|
|
active_cellview_changed_event ();
|
|
active_cellview_changed_with_index_event (index);
|
|
|
|
// Because the title reflects the active one, emit a title changed event
|
|
if (title_string ().empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
|
|
} else {
|
|
m_active_cellview_changed_events.insert (index);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cell_dispatch (const cell_path_type &path, int cellview_index)
|
|
{
|
|
bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());
|
|
if (m_clear_ruler_new_cell) {
|
|
|
|
// This is a HACK, but the clean solution would be to provide a new editable
|
|
// method like "clear_annotations":
|
|
lay::Plugin *antPlugin = get_plugin_by_name ("ant::Plugin");
|
|
if (antPlugin) {
|
|
antPlugin->menu_activated ("ant::clear_all_rulers_internal");
|
|
}
|
|
|
|
}
|
|
|
|
if (m_fit_new_cell) {
|
|
select_cell_fit (path, cellview_index);
|
|
} else {
|
|
select_cell (path, cellview_index);
|
|
}
|
|
set_current_cell_path (cellview_index, path);
|
|
if (set_max_hier) {
|
|
max_hier ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cell_fit (const cell_path_type &path, int index)
|
|
{
|
|
if (index >= 0 && int (m_cellviews.size ()) > index && (cellview_iter (index)->specific_path ().size () > 0 || cellview_iter (index)->unspecific_path () != path)) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
set_min_hier_levels (0);
|
|
cancel ();
|
|
cellview_iter (index)->set_specific_path (lay::CellView::specific_cell_path_type ());
|
|
cellview_iter (index)->set_unspecific_path (path);
|
|
set_active_cellview_index (index);
|
|
redraw ();
|
|
zoom_fit ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cell_fit (cell_index_type cell_index, int index)
|
|
{
|
|
if (index >= 0 && int (m_cellviews.size ()) > index && cellview_iter (index)->cell_index () != cell_index) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
set_min_hier_levels (0);
|
|
cancel ();
|
|
cellview_iter (index)->set_cell (cell_index);
|
|
set_active_cellview_index (index);
|
|
redraw ();
|
|
zoom_fit ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cellviews (const std::list <CellView> &cvs)
|
|
{
|
|
if (m_cellviews != cvs) {
|
|
|
|
for (int index = 0; index < int (m_cellviews.size ()); ++index) {
|
|
cellview_about_to_change_event (index);
|
|
}
|
|
cellviews_about_to_change_event ();
|
|
|
|
set_min_hier_levels (0);
|
|
cancel_esc ();
|
|
m_cellviews = cvs;
|
|
redraw ();
|
|
|
|
cellviews_changed_event ();
|
|
for (int index = 0; index < int (m_cellviews.size ()); ++index) {
|
|
cellview_changed (index);
|
|
}
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cellview (int index, const CellView &cv)
|
|
{
|
|
if (index < 0 || index >= int (m_cellviews.size ())) {
|
|
return;
|
|
}
|
|
|
|
if (*cellview_iter (index) != cv) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
cancel_esc ();
|
|
*cellview_iter (index) = cv;
|
|
redraw ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cell (const cell_path_type &path, int index)
|
|
{
|
|
if (index >= 0 && int (m_cellviews.size ()) > index && (cellview_iter (index)->specific_path ().size () > 0 || cellview_iter (index)->unspecific_path () != path)) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
set_min_hier_levels (0);
|
|
cancel ();
|
|
cellview_iter (index)->set_specific_path (lay::CellView::specific_cell_path_type ());
|
|
cellview_iter (index)->set_unspecific_path (path);
|
|
set_active_cellview_index (index);
|
|
redraw ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::select_cell (cell_index_type cell_index, int index)
|
|
{
|
|
if (index >= 0 && int (m_cellviews.size ()) > index && (! cellview_iter (index)->is_valid () || cellview_iter (index)->cell_index () != cell_index)) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
set_min_hier_levels (0);
|
|
cancel ();
|
|
cellview_iter (index)->set_cell (cell_index);
|
|
set_active_cellview_index (index);
|
|
redraw ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_cell_hidden (cell_index_type ci, int cellview_index) const
|
|
{
|
|
if (int (m_hidden_cells.size ()) > cellview_index && cellview_index >= 0) {
|
|
return m_hidden_cells [cellview_index].find (ci) != m_hidden_cells [cellview_index].end ();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const std::set<LayoutViewBase::cell_index_type> &
|
|
LayoutViewBase::hidden_cells (int cellview_index) const
|
|
{
|
|
if (int (m_hidden_cells.size ()) > cellview_index && cellview_index >= 0) {
|
|
return m_hidden_cells[cellview_index];
|
|
} else {
|
|
static std::set<cell_index_type> empty_set;
|
|
return empty_set;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::hide_cell (cell_index_type ci, int cellview_index)
|
|
{
|
|
if (cellview_index < 0) {
|
|
return;
|
|
}
|
|
while (int (m_hidden_cells.size ()) <= cellview_index) {
|
|
m_hidden_cells.push_back (std::set <cell_index_type> ());
|
|
}
|
|
if (m_hidden_cells [cellview_index].insert (ci).second) {
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpHideShowCell (ci, cellview_index, false /*=hide*/));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
cell_visibility_changed_event ();
|
|
redraw (); // needs redraw
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::show_cell (cell_index_type ci, int cellview_index)
|
|
{
|
|
if (cellview_index < 0) {
|
|
return;
|
|
}
|
|
if (int (m_hidden_cells.size ()) > cellview_index) {
|
|
if (m_hidden_cells [cellview_index].erase (ci) > 0) {
|
|
if (transacting ()) {
|
|
manager ()->queue (this, new OpHideShowCell (ci, cellview_index, true /*=show*/));
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
cell_visibility_changed_event ();
|
|
redraw (); // needs redraw
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::show_all_cells (int cv_index)
|
|
{
|
|
if (cv_index < 0 || cv_index >= int (m_hidden_cells.size ())) {
|
|
return;
|
|
}
|
|
|
|
if (! m_hidden_cells [cv_index].empty ()) {
|
|
if (transacting ()) {
|
|
for (std::set<cell_index_type>::const_iterator ci = m_hidden_cells [cv_index].begin (); ci != m_hidden_cells [cv_index].end (); ++ci) {
|
|
manager ()->queue (this, new OpHideShowCell (*ci, cv_index, true /*=show*/));
|
|
}
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
m_hidden_cells [cv_index].clear ();
|
|
cell_visibility_changed_event ();
|
|
redraw (); // needs redraw
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::show_all_cells ()
|
|
{
|
|
bool any = false;
|
|
|
|
for (unsigned int i = 0; i < m_hidden_cells.size (); ++i) {
|
|
if (! m_hidden_cells [i].empty ()) {
|
|
if (transacting ()) {
|
|
for (std::set<cell_index_type>::const_iterator ci = m_hidden_cells [i].begin (); ci != m_hidden_cells [i].end (); ++ci) {
|
|
manager ()->queue (this, new OpHideShowCell (*ci, i, true /*=show*/));
|
|
}
|
|
} else if (manager () && ! replaying ()) {
|
|
manager ()->clear ();
|
|
}
|
|
m_hidden_cells [i].clear ();
|
|
any = true;
|
|
}
|
|
}
|
|
|
|
if (any) {
|
|
cell_visibility_changed_event ();
|
|
redraw (); // needs redraw
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::min_inst_label_size (int px)
|
|
{
|
|
if (m_min_size_for_label != px) {
|
|
m_min_size_for_label = px;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::text_visible (bool vis)
|
|
{
|
|
if (m_text_visible != vis) {
|
|
m_text_visible = vis;
|
|
update_content ();
|
|
redraw (); // required because we do some optimizations is text is not visible ..
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::show_properties_as_text (bool sp)
|
|
{
|
|
if (m_show_properties != sp) {
|
|
m_show_properties = sp;
|
|
redraw (); // required because we do some optimizations is text is not visible ..
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::bitmap_caching (bool l)
|
|
{
|
|
if (m_bitmap_caching != l) {
|
|
m_bitmap_caching = l;
|
|
redraw (); // required because we do some optimizations is text is not visible ..
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::text_lazy_rendering (bool l)
|
|
{
|
|
if (m_text_lazy_rendering != l) {
|
|
m_text_lazy_rendering = l;
|
|
redraw (); // required because we do some optimizations is text is not visible ..
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cell_box_visible (bool vis)
|
|
{
|
|
if (m_cell_box_visible != vis) {
|
|
m_cell_box_visible = vis;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::text_font (unsigned int f)
|
|
{
|
|
if (m_text_font != f) {
|
|
m_text_font = f;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::default_text_size (double fs)
|
|
{
|
|
if (m_default_text_size != fs) {
|
|
m_default_text_size = fs;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::clear_ruler_new_cell (bool f)
|
|
{
|
|
m_clear_ruler_new_cell = f;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::full_hier_new_cell (bool f)
|
|
{
|
|
m_full_hier_new_cell = f;
|
|
}
|
|
|
|
double
|
|
LayoutViewBase::pan_distance () const
|
|
{
|
|
return m_pan_distance;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::pan_distance (double pd)
|
|
{
|
|
m_pan_distance = pd;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::fit_new_cell (bool f)
|
|
{
|
|
m_fit_new_cell = f;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::apply_text_trans (bool f)
|
|
{
|
|
if (m_apply_text_trans != f) {
|
|
m_apply_text_trans = f;
|
|
redraw ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::offset_stipples (bool f)
|
|
{
|
|
if (m_stipple_offset != f) {
|
|
m_stipple_offset = f;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::no_stipples (bool f)
|
|
{
|
|
if (m_no_stipples != f) {
|
|
m_no_stipples = f;
|
|
do_set_no_stipples (f);
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::show_markers (bool f)
|
|
{
|
|
if (m_show_markers != f) {
|
|
m_show_markers = f;
|
|
mp_canvas->update_image ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::text_color (tl::Color c)
|
|
{
|
|
if (m_text_color != c) {
|
|
m_text_color = c;
|
|
update_content ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::has_selection ()
|
|
{
|
|
return lay::Editables::has_selection ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_paste ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::paste ()
|
|
{
|
|
clear_selection ();
|
|
|
|
{
|
|
db::Transaction trans (manager (), tl::to_string (tr ("Paste")));
|
|
|
|
// let the receivers sort out who is pasting what ..
|
|
do_paste ();
|
|
lay::Editables::paste ();
|
|
}
|
|
|
|
// if we change the state, save it before
|
|
store_state ();
|
|
|
|
db::DBox sel_bbox = selection_bbox ();
|
|
if (! sel_bbox.empty ()) {
|
|
if (m_paste_display_mode == 1) {
|
|
// just make selection visible, i.e. shift window somewhat
|
|
pan_center (sel_bbox.center ());
|
|
} else if (m_paste_display_mode == 2) {
|
|
// or: make selection fit into the screen
|
|
zoom_fit_sel ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::paste_interactive ()
|
|
{
|
|
clear_selection ();
|
|
|
|
std::unique_ptr<db::Transaction> trans (new db::Transaction (manager (), tl::to_string (tr ("Paste and move"))));
|
|
|
|
{
|
|
// let the receivers sort out who is pasting what ..
|
|
do_paste ();
|
|
lay::Editables::paste ();
|
|
}
|
|
|
|
// temporarily close the transaction and pass to the move service for appending it's own
|
|
// operations.
|
|
trans->close ();
|
|
|
|
if (mp_move_service->begin_move (trans.release (), false)) {
|
|
switch_mode (-1); // move mode
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::copy ()
|
|
{
|
|
cancel_edits ();
|
|
if (! lay::Editables::has_selection ()) {
|
|
// try to use the transient selection for the real one
|
|
lay::Editables::transient_to_selection ();
|
|
}
|
|
|
|
lay::Editables::copy ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::cut ()
|
|
{
|
|
cancel_edits ();
|
|
if (! lay::Editables::has_selection ()) {
|
|
// try to use the transient selection for the real one
|
|
lay::Editables::transient_to_selection ();
|
|
}
|
|
|
|
db::Transaction trans (manager (), tl::to_string (tr ("Cut")));
|
|
lay::Editables::cut ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::remove_unused_layers ()
|
|
{
|
|
bool any_deleted;
|
|
do {
|
|
|
|
std::vector <lay::LayerPropertiesConstIterator> sel;
|
|
|
|
lay::LayerPropertiesConstIterator l = begin_layers ();
|
|
while (! l.at_end ()) {
|
|
if (! l->has_children () && l->bbox ().empty ()) {
|
|
sel.push_back (l);
|
|
}
|
|
++l;
|
|
}
|
|
|
|
std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ());
|
|
any_deleted = false;
|
|
for (std::vector<lay::LayerPropertiesConstIterator>::iterator s = sel.begin (); s != sel.end (); ++s) {
|
|
delete_layer (*s);
|
|
any_deleted = true;
|
|
}
|
|
|
|
} while (any_deleted);
|
|
|
|
emit_layer_order_changed ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::add_missing_layers ()
|
|
{
|
|
std::set <lay::ParsedLayerSource> present;
|
|
LayerPropertiesConstIterator l = begin_layers ();
|
|
while (! l.at_end ()) {
|
|
if (! l->has_children ()) {
|
|
present.insert (l->source (true /*real*/));
|
|
}
|
|
++l;
|
|
}
|
|
|
|
std::vector <lay::ParsedLayerSource> actual;
|
|
for (unsigned int cv = 0; cv < cellviews (); ++cv) {
|
|
const db::Layout &layout = cellview (cv)->layout ();
|
|
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
|
if (layout.is_valid_layer (l)) {
|
|
actual.push_back (lay::ParsedLayerSource (layout.get_properties (l), cv));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort (actual.begin (), actual.end ());
|
|
|
|
for (std::vector <lay::ParsedLayerSource>::const_iterator a = actual.begin (); a != actual.end (); ++a) {
|
|
if (present.find (*a) == present.end ()) {
|
|
lay::LayerPropertiesNode node;
|
|
node.attach_view (this, current_layer_list ());
|
|
node.set_source (*a);
|
|
init_layer_properties (node);
|
|
insert_layer (end_layers (), node);
|
|
}
|
|
}
|
|
|
|
emit_layer_order_changed ();
|
|
}
|
|
|
|
LayerState
|
|
LayoutViewBase::layer_snapshot () const
|
|
{
|
|
LayerState state;
|
|
LayerPropertiesConstIterator l = begin_layers ();
|
|
while (! l.at_end ()) {
|
|
if (! l->has_children ()) {
|
|
state.present.insert (l->source (true /*real*/));
|
|
}
|
|
++l;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::current_layer_changed_slot (const lay::LayerPropertiesConstIterator &iter)
|
|
{
|
|
current_layer_changed_event (iter);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::add_new_layers (const LayerState &state)
|
|
{
|
|
std::vector <lay::ParsedLayerSource> actual;
|
|
for (unsigned int cv = 0; cv < cellviews (); ++cv) {
|
|
const db::Layout &layout = cellview (cv)->layout ();
|
|
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
|
if (layout.is_valid_layer (l)) {
|
|
actual.push_back (lay::ParsedLayerSource (layout.get_properties (l), cv));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort (actual.begin (), actual.end ());
|
|
|
|
bool needs_update = false;
|
|
|
|
for (std::vector <lay::ParsedLayerSource>::const_iterator a = actual.begin (); a != actual.end (); ++a) {
|
|
if (state.present.find (*a) == state.present.end ()) {
|
|
needs_update = true;
|
|
lay::LayerPropertiesNode node;
|
|
node.attach_view (this, current_layer_list ());
|
|
node.set_source (*a);
|
|
// HINT: in editable mode it is desireable to present all layers because otherwise they cannot be
|
|
// made visible to populate them.
|
|
if (is_editable () || ! node.bbox ().empty ()) {
|
|
init_layer_properties (node);
|
|
insert_layer (end_layers (), node);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (needs_update) {
|
|
emit_layer_order_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::prev_display_state ()
|
|
{
|
|
if (m_display_state_ptr > 0) {
|
|
m_display_state_ptr--;
|
|
goto_view (m_display_states [m_display_state_ptr]);
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::has_prev_display_state ()
|
|
{
|
|
return m_display_state_ptr > 0;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::next_display_state ()
|
|
{
|
|
if (m_display_state_ptr + 1 < m_display_states.size ()) {
|
|
m_display_state_ptr++;
|
|
goto_view (m_display_states [m_display_state_ptr]);
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::has_next_display_state ()
|
|
{
|
|
return m_display_state_ptr + 1 < m_display_states.size ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::current_pos (double /*x*/, double /*y*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::stop_redraw ()
|
|
{
|
|
dm_redraw.cancel ();
|
|
mp_canvas->stop_redraw ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::free_resources ()
|
|
{
|
|
mp_canvas->free_resources ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::stop ()
|
|
{
|
|
stop_redraw ();
|
|
deactivate_all_browsers ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::begin_layer_updates ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::end_layer_updates ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::ensure_layer_selected ()
|
|
{
|
|
if (current_layer () == lay::LayerPropertiesConstIterator ()) {
|
|
const lay::LayerPropertiesList &lp = get_properties ();
|
|
lay::LayerPropertiesConstIterator li = lp.begin_const_recursive ();
|
|
while (! li.at_end () && li->has_children ()) {
|
|
++li;
|
|
}
|
|
if (! li.at_end ()) {
|
|
set_current_layer (li);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_set_no_stipples (bool /*no_stipples*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::do_set_phase (int /*phase*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::deactivate_all_browsers ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_activated () const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::switch_mode (int m)
|
|
{
|
|
mode (m);
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::mode (int m)
|
|
{
|
|
if (m != m_mode) {
|
|
|
|
m_mode = m;
|
|
mp_active_plugin = 0;
|
|
|
|
if (m > 0) {
|
|
|
|
for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
|
|
if ((*p)->plugin_declaration ()->id () == m) {
|
|
mp_active_plugin = *p;
|
|
mp_canvas->activate ((*p)->view_service_interface ());
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if (m == 0 && mp_selection_service) {
|
|
mp_canvas->activate (mp_selection_service);
|
|
} else if (m == -1 && mp_move_service) {
|
|
mp_canvas->activate (mp_move_service);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_move_mode () const
|
|
{
|
|
return m_mode == -1;
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_selection_mode () const
|
|
{
|
|
return m_mode == 0;
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::intrinsic_mouse_modes (std::vector<std::string> *descriptions)
|
|
{
|
|
if (descriptions) {
|
|
descriptions->push_back ("select\t" + tl::to_string (tr ("Select")) + "<:select_24px.png>");
|
|
descriptions->push_back ("move\t" + tl::to_string (tr ("Move")) + "<:move_24px.png>");
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
int
|
|
LayoutViewBase::default_mode ()
|
|
{
|
|
return 0; // TODO: any generic scheme? is select, should be ruler..
|
|
}
|
|
|
|
static std::string
|
|
name_from_title (const std::string &title)
|
|
{
|
|
std::string s = title;
|
|
std::string::size_type tab = s.find ('\t');
|
|
if (tab != std::string::npos) {
|
|
s = std::string (s, 0, tab);
|
|
}
|
|
std::string::size_type colon = s.find (':');
|
|
if (colon != std::string::npos) {
|
|
s = std::string (s, 0, colon);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static bool
|
|
edit_mode_from_title (const std::string &title)
|
|
{
|
|
std::string s = title;
|
|
std::string::size_type tab = s.find ('\t');
|
|
if (tab != std::string::npos) {
|
|
s = std::string (s, 0, tab);
|
|
}
|
|
std::vector<std::string> parts = tl::split (s, ":");
|
|
for (auto i = parts.begin (); i != parts.end (); ++i) {
|
|
if (*i == "edit_mode") {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string>
|
|
LayoutViewBase::mode_names () const
|
|
{
|
|
std::vector<std::string> names;
|
|
|
|
std::vector<std::string> intrinsic_modes;
|
|
intrinsic_mouse_modes (&intrinsic_modes);
|
|
for (auto i = intrinsic_modes.begin (); i != intrinsic_modes.end (); ++i) {
|
|
names.push_back (name_from_title (*i));
|
|
}
|
|
|
|
for (auto i = mp_plugins.begin (); i != mp_plugins.end (); ++i) {
|
|
std::string title;
|
|
if ((*i) && (*i)->plugin_declaration () && (*i)->plugin_declaration ()->implements_mouse_mode (title)) {
|
|
if (is_editable () || !edit_mode_from_title (title)) {
|
|
names.push_back (name_from_title (title));
|
|
}
|
|
}
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
std::string
|
|
LayoutViewBase::mode_name () const
|
|
{
|
|
if (m_mode <= 0) {
|
|
|
|
std::vector<std::string> intrinsic_modes;
|
|
intrinsic_mouse_modes (&intrinsic_modes);
|
|
|
|
if (int (intrinsic_modes.size ()) > -m_mode) {
|
|
return name_from_title (intrinsic_modes [-m_mode]);
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto i = mp_plugins.begin (); i != mp_plugins.end (); ++i) {
|
|
std::string title;
|
|
if ((*i) && (*i)->plugin_declaration () && (*i)->plugin_declaration ()->id () == m_mode && (*i)->plugin_declaration ()->implements_mouse_mode (title)) {
|
|
return name_from_title (title);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return std::string ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::switch_mode (const std::string &name)
|
|
{
|
|
std::vector<std::string> intrinsic_modes;
|
|
intrinsic_mouse_modes (&intrinsic_modes);
|
|
for (auto i = intrinsic_modes.begin (); i != intrinsic_modes.end (); ++i) {
|
|
if (name_from_title (*i) == name) {
|
|
switch_mode (int (0 - int (i - intrinsic_modes.begin ())));
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (auto i = mp_plugins.begin (); i != mp_plugins.end (); ++i) {
|
|
std::string title;
|
|
if ((*i) && (*i)->plugin_declaration () && (*i)->plugin_declaration ()->implements_mouse_mode (title)) {
|
|
if (name_from_title (title) == name) {
|
|
switch_mode ((*i)->plugin_declaration ()->id ());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::string>
|
|
LayoutViewBase::menu_symbols ()
|
|
{
|
|
// TODO: currently these are all symbols from all plugins
|
|
return lay::PluginDeclaration::menu_symbols ();
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::menu_activated (const std::string &symbol)
|
|
{
|
|
// Try the plugin declarations if the view is the top-level dispatcher
|
|
if (dispatcher () == this) {
|
|
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
|
if (cls->menu_activated (symbol)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// distribute the menu item call on the plugins - one should take it.
|
|
for (std::vector<lay::Plugin *>::const_iterator p = plugins ().begin (); p != plugins ().end (); ++p) {
|
|
(*p)->menu_activated (symbol);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::update_content_for_cv (int /*cellview_index*/)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::rename_cellview (const std::string &name, int cellview_index)
|
|
{
|
|
if (cellview_index >= 0 && cellview_index < int (m_cellviews.size ())) {
|
|
if ((*cellview_iter (cellview_index))->name () != name) {
|
|
(*cellview_iter (cellview_index))->rename (name);
|
|
update_content_for_cv (cellview_index);
|
|
if (m_title.empty ()) {
|
|
emit_title_changed ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<db::DCplxTrans>
|
|
LayoutViewBase::cv_transform_variants (int cv_index) const
|
|
{
|
|
std::set<db::DCplxTrans> trns_variants;
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children ()) {
|
|
int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
|
|
if (cv_index < int (cellviews ()) && cvi == cv_index) {
|
|
trns_variants.insert (l->trans ().begin (), l->trans ().end ());
|
|
}
|
|
}
|
|
}
|
|
return std::vector<db::DCplxTrans> (trns_variants.begin (), trns_variants.end ());
|
|
}
|
|
|
|
std::vector<db::DCplxTrans>
|
|
LayoutViewBase::cv_transform_variants (int cv_index, unsigned int layer) const
|
|
{
|
|
if (cellview (cv_index)->layout ().is_valid_layer (layer)) {
|
|
std::set<db::DCplxTrans> trns_variants;
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children () && l->layer_index () == int (layer)) {
|
|
int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
|
|
if (cv_index < int (cellviews ()) && cvi == cv_index) {
|
|
trns_variants.insert (l->trans ().begin (), l->trans ().end ());
|
|
}
|
|
}
|
|
}
|
|
return std::vector<db::DCplxTrans> (trns_variants.begin (), trns_variants.end ());
|
|
} else {
|
|
// may happen if the layer is a guiding shape layer for example
|
|
return cv_transform_variants (cv_index);
|
|
}
|
|
}
|
|
|
|
std::map<unsigned int, std::vector<db::DCplxTrans> >
|
|
LayoutViewBase::cv_transform_variants_by_layer (int cv_index) const
|
|
{
|
|
std::map<unsigned int, std::vector<db::DCplxTrans> > tv_map;
|
|
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children () && l->layer_index () >= 0) {
|
|
int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
|
|
if (cv_index < int (cellviews ()) && cvi == cv_index) {
|
|
std::vector<db::DCplxTrans> &v = tv_map.insert (std::make_pair (l->layer_index (), std::vector<db::DCplxTrans> ())).first->second;
|
|
v.insert (v.end (), l->trans ().begin (), l->trans ().end ());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (std::map<unsigned int, std::vector<db::DCplxTrans> >::iterator m = tv_map.begin (); m != tv_map.end (); ++m) {
|
|
std::sort (m->second.begin (), m->second.end ());
|
|
m->second.erase (std::unique (m->second.begin (), m->second.end ()), m->second.end ());
|
|
}
|
|
|
|
return tv_map;
|
|
}
|
|
|
|
std::set< std::pair<db::DCplxTrans, int> >
|
|
LayoutViewBase::cv_transform_variants () const
|
|
{
|
|
std::set< std::pair<db::DCplxTrans, int> > box_variants;
|
|
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
|
|
if (! l->has_children ()) {
|
|
unsigned int cv_index = l->cellview_index () >= 0 ? (unsigned int) l->cellview_index () : 0;
|
|
if (cv_index < cellviews ()) {
|
|
for (std::vector<db::DCplxTrans>::const_iterator t = l->trans ().begin (); t != l->trans ().end (); ++t) {
|
|
box_variants.insert (std::make_pair (*t, cv_index));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return box_variants;
|
|
}
|
|
|
|
db::InstElement
|
|
LayoutViewBase::ascend (int index)
|
|
{
|
|
tl_assert (int (m_cellviews.size ()) > index && cellview_iter (index)->is_valid ());
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
lay::CellView::specific_cell_path_type spath (cellview_iter (index)->specific_path ());
|
|
if (spath.empty ()) {
|
|
return db::InstElement ();
|
|
} else {
|
|
|
|
cancel ();
|
|
db::InstElement ret = spath.back ();
|
|
spath.pop_back ();
|
|
cellview_iter (index)->set_specific_path (spath);
|
|
|
|
store_state ();
|
|
redraw ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
return ret;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::descend (const std::vector<db::InstElement> &path, int index)
|
|
{
|
|
if (! path.empty () && index >= 0 && int (m_cellviews.size ()) > index && cellview_iter (index)->is_valid ()) {
|
|
|
|
cellview_about_to_change_event (index);
|
|
|
|
cancel ();
|
|
|
|
lay::CellView::specific_cell_path_type spath (cellview_iter (index)->specific_path ());
|
|
spath.insert (spath.end (), path.begin (), path.end ());
|
|
cellview_iter (index)->set_specific_path (spath);
|
|
|
|
store_state ();
|
|
redraw ();
|
|
|
|
cellview_changed (index);
|
|
|
|
update_content ();
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutViewBase::is_editable () const
|
|
{
|
|
return m_editable;
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::search_range ()
|
|
{
|
|
return m_search_range;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_search_range (unsigned int sr)
|
|
{
|
|
m_search_range = sr;
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::search_range_box ()
|
|
{
|
|
return m_search_range_box;
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::set_search_range_box (unsigned int sr)
|
|
{
|
|
m_search_range_box = sr;
|
|
}
|
|
|
|
db::cell_index_type
|
|
LayoutViewBase::new_cell (int cv_index, const std::string &cell_name)
|
|
{
|
|
db::cell_index_type new_ci (0);
|
|
|
|
if (cv_index >= 0 && int (m_cellviews.size ()) > cv_index) {
|
|
|
|
db::Layout &layout = cellview (cv_index)->layout ();
|
|
if (! cell_name.empty () && layout.cell_by_name (cell_name.c_str ()).first) {
|
|
throw tl::Exception (tl::to_string (tr ("A cell with that name already exists: %s")), cell_name);
|
|
}
|
|
|
|
transaction (tl::to_string (tr ("New cell")));
|
|
new_ci = layout.add_cell (cell_name.empty () ? 0 : cell_name.c_str ());
|
|
commit ();
|
|
|
|
}
|
|
|
|
return new_ci;
|
|
}
|
|
|
|
template <class T, class Iter>
|
|
static void make_unique_name (T *object, Iter from, Iter to)
|
|
{
|
|
std::string n (object->name ());
|
|
int nn = 0;
|
|
|
|
do {
|
|
|
|
bool found = n.empty ();
|
|
for (Iter i = from; i != to && !found; ++i) {
|
|
if ((*i)->name () == n) {
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (! found) {
|
|
break;
|
|
}
|
|
|
|
n = object->name () + tl::sprintf ("[%d]", ++nn);
|
|
|
|
} while (1);
|
|
|
|
object->set_name (n);
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::add_l2ndb (db::LayoutToNetlist *l2ndb)
|
|
{
|
|
make_unique_name (l2ndb, m_l2ndbs.begin (), m_l2ndbs.end ());
|
|
m_l2ndbs.push_back (l2ndb);
|
|
|
|
// Mark this object as owned by us (for GSI)
|
|
l2ndb->keep ();
|
|
|
|
l2ndb_list_changed_event ();
|
|
|
|
return (unsigned int)(m_l2ndbs.size () - 1);
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::replace_l2ndb (unsigned int db_index, db::LayoutToNetlist *l2ndb)
|
|
{
|
|
tl_assert (l2ndb != 0);
|
|
|
|
if (db_index < m_l2ndbs.size ()) {
|
|
|
|
// keep the name as it is used for reference in the browser for example
|
|
std::string n = m_l2ndbs [db_index]->name ();
|
|
l2ndb->set_name (n);
|
|
|
|
delete m_l2ndbs [db_index];
|
|
m_l2ndbs [db_index] = l2ndb;
|
|
|
|
// Mark this object as owned by us (for GSI)
|
|
l2ndb->keep ();
|
|
|
|
l2ndb_list_changed_event ();
|
|
|
|
return db_index;
|
|
|
|
} else {
|
|
return add_l2ndb (l2ndb);
|
|
}
|
|
}
|
|
|
|
db::LayoutToNetlist *
|
|
LayoutViewBase::get_l2ndb (int index)
|
|
{
|
|
if (index >= 0 && index < int (m_l2ndbs.size ())) {
|
|
return m_l2ndbs [index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const db::LayoutToNetlist *
|
|
LayoutViewBase::get_l2ndb (int index) const
|
|
{
|
|
if (index >= 0 && index < int (m_l2ndbs.size ())) {
|
|
return m_l2ndbs [index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::remove_l2ndb (unsigned int index)
|
|
{
|
|
if (index < (unsigned int) (m_l2ndbs.size ())) {
|
|
delete m_l2ndbs [index];
|
|
m_l2ndbs.erase (m_l2ndbs.begin () + index);
|
|
l2ndb_list_changed_event ();
|
|
}
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::add_rdb (rdb::Database *rdb)
|
|
{
|
|
make_unique_name (rdb, m_rdbs.begin (), m_rdbs.end ());
|
|
m_rdbs.push_back (rdb);
|
|
|
|
// Mark this object as owned by us (for GSI)
|
|
rdb->keep ();
|
|
|
|
rdb_list_changed_event ();
|
|
|
|
return (unsigned int)(m_rdbs.size () - 1);
|
|
}
|
|
|
|
unsigned int
|
|
LayoutViewBase::replace_rdb (unsigned int db_index, rdb::Database *rdb)
|
|
{
|
|
tl_assert (rdb != 0);
|
|
|
|
if (db_index < m_rdbs.size ()) {
|
|
|
|
// keep name because it's used for reference in the browser for example
|
|
std::string n = m_rdbs [db_index]->name ();
|
|
rdb->set_name (n);
|
|
|
|
delete m_rdbs [db_index];
|
|
m_rdbs [db_index] = rdb;
|
|
|
|
// Mark this object as owned by us (for GSI)
|
|
rdb->keep ();
|
|
|
|
rdb_list_changed_event ();
|
|
|
|
return db_index;
|
|
|
|
} else {
|
|
return add_rdb (rdb);
|
|
}
|
|
}
|
|
|
|
rdb::Database *
|
|
LayoutViewBase::get_rdb (int index)
|
|
{
|
|
if (index >= 0 && index < int (m_rdbs.size ())) {
|
|
return m_rdbs [index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const rdb::Database *
|
|
LayoutViewBase::get_rdb (int index) const
|
|
{
|
|
if (index >= 0 && index < int (m_rdbs.size ())) {
|
|
return m_rdbs [index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayoutViewBase::remove_rdb (unsigned int index)
|
|
{
|
|
if (index < (unsigned int) (m_rdbs.size ())) {
|
|
delete m_rdbs [index];
|
|
m_rdbs.erase (m_rdbs.begin () + index);
|
|
rdb_list_changed_event ();
|
|
}
|
|
}
|
|
|
|
} // namespace lay
|