Files
klayout/src/laybasic/layLayerTreeModel.cc
T
Matthias Koefferlein 5c414ef0be Search feature for layer list
To enable the search feature just type into the layer list.

This change set also contains some more fixes:
 * Tab/Backtab now is functional also before the
   Display menu has been used the first time
 * Arrow keys work from the cell list again
2017-03-02 23:45:37 +01:00

823 lines
24 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2017 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 "layLayerTreeModel.h"
#include "layLayoutView.h"
#include "layBitmapsToImage.h"
#include "dbLayoutUtils.h"
#include "tlLog.h"
#include "tlTimer.h"
#include "tlGlobPattern.h"
#include <QTreeView>
#include <QModelIndex>
#include <QString>
#include <string.h>
#include <algorithm>
namespace lay {
// --------------------------------------------------------------------
// EmptyWithinViewCache implementation
EmptyWithinViewCache::EmptyWithinViewCache ()
{
// .. nothing yet ..
}
void
EmptyWithinViewCache::clear ()
{
m_cache.clear();
}
bool
EmptyWithinViewCache::is_empty_within_view (const db::Layout *layout, unsigned int cell_index, const db::Box &box, unsigned int layer)
{
std::pair<cache_t::iterator, bool> c = m_cache.insert (std::make_pair(std::make_pair(std::make_pair(layout, cell_index), box), std::set<unsigned int> ()));
if (c.second) {
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (QObject::tr ("Recomputing layers with shapes in view")));
const db::Cell &cell (layout->cell (cell_index));
// determine layers with shapes on the given layout and within the given box
std::vector <unsigned int> ll;
for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) {
if (cell.bbox ((*l).first).empty ()) {
c.first->second.insert ((*l).first);
} else if (! cell.shapes ((*l).first).begin_touching (box, db::ShapeIterator::All, 0, false).at_end ()) {
;
} else {
ll.push_back ((*l).first);
}
}
m_cells_done.resize (layout->cells (), false);
determine_empty_layers(layout, cell_index, box, ll);
m_cells_done = std::vector<bool> ();
c.first->second.insert (ll.begin (), ll.end ());
}
return c.first->second.find(layer) != c.first->second.end();
}
void
EmptyWithinViewCache::determine_empty_layers (const db::Layout *layout, unsigned int cell_index, const db::Box &box, std::vector<unsigned int> &layers)
{
if (layers.empty ()) {
return;
}
// Hint: this implementation counts all hierarchy levels - also the ones not shown ...
db::box_convert<db::CellInst> bc (*layout);
db::Cell::touching_iterator inst = layout->cell (cell_index).begin_touching (box);
while (! inst.at_end () && ! layers.empty ()) {
db::cell_index_type ci = inst->cell_index ();
if (! m_cells_done [ci]) {
const db::Cell &cell (layout->cell (ci));
if (inst->bbox (bc).inside (box)) {
// The instance is fully inside the search box: remove the non-empty layers from the
// list and mark the cell as "done".
std::vector<unsigned int>::iterator lw = layers.begin ();
for (std::vector<unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (cell.bbox (*l).empty ()) {
*lw++ = *l;
}
}
layers.erase (lw, layers.end ());
m_cells_done [ci] = true;
} else {
// Remove every layer from the list which has at least one shape on it within the box and
// temporarily create a list of layers with only the relevant layers in ll
std::vector<unsigned int> ll;
ll.reserve (layers.size ());
std::vector<unsigned int>::iterator lw = layers.begin ();
for (std::vector<unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (cell.bbox (*l).empty ()) {
*lw++ = *l;
} else {
ll.push_back (*l);
}
}
layers.erase (lw, layers.end ());
if (! ll.empty ()) {
db::CellInstArray::iterator inst_array = inst->cell_inst ().begin_touching (box, bc);
while (! inst_array.at_end () && ! ll.empty ()) {
db::Box new_box = db::Box (inst->complex_trans (*inst_array).inverted () * box);
// remove all layers which became populated in that instance
std::vector<unsigned int>::iterator llw = ll.begin ();
for (std::vector<unsigned int>::const_iterator l = ll.begin (); l != ll.end (); ++l) {
if (cell.shapes (*l).begin_touching (new_box, db::ShapeIterator::All, 0, false).at_end ()) {
*llw++ = *l;
}
}
ll.erase (llw, ll.end ());
determine_empty_layers (layout, inst->cell_index (), new_box, ll);
++inst_array;
}
// Join the lists of remaining layers
layers.insert (layers.end (), ll.begin (), ll.end ());
}
}
}
++inst;
}
}
// --------------------------------------------------------------------
// LayerTreeModel implementation
LayerTreeModel::LayerTreeModel (QWidget *parent, lay::LayoutView *view)
: QAbstractItemModel (parent),
mp_view (view), m_id_start (0), m_id_end (0), m_phase ((unsigned int) -1), m_test_shapes_in_view (false)
{
// .. nothing yet ..
}
LayerTreeModel::~LayerTreeModel ()
{
// .. nothing yet ..
}
void
LayerTreeModel::set_phase (unsigned int ph)
{
m_phase = ph;
}
void
LayerTreeModel::set_font (const QFont &font)
{
m_font = font;
signal_data_changed ();
}
void
LayerTreeModel::set_text_color (QColor color)
{
m_text_color = color;
signal_data_changed ();
}
void
LayerTreeModel::set_background_color (QColor background)
{
m_background_color = background;
signal_data_changed ();
}
Qt::ItemFlags
LayerTreeModel::flags (const QModelIndex &index) const
{
return QAbstractItemModel::flags (index);
}
int
LayerTreeModel::columnCount (const QModelIndex &) const
{
return 2;
}
QVariant
LayerTreeModel::headerData (int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const
{
return QVariant ();
}
int
LayerTreeModel::rowCount (const QModelIndex &parent) const
{
if (mp_view->layer_model_updated ()) {
if (parent.isValid ()) {
lay::LayerPropertiesConstIterator iter (iterator (parent));
if (iter.is_null () || iter.at_end ()) {
return 0;
} else {
return iter->end_children () - iter->begin_children ();
}
} else {
int n = int (mp_view->get_properties ().end_const () - mp_view->get_properties ().begin_const ());
return n;
}
} else {
return 0;
}
}
QModelIndex
LayerTreeModel::index (int row, int column, const QModelIndex &parent) const
{
if (row >= 0 && row < rowCount (parent)) {
if (parent.isValid ()) {
lay::LayerPropertiesConstIterator iter (iterator (parent));
if (iter.is_null () || iter.at_end ()) {
return QModelIndex ();
} else {
iter.down_first_child ();
iter.next_sibling (row);
return createIndex (row, column, (void *) (iter.uint () + m_id_start));
}
} else {
lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ());
iter.next_sibling (row);
return createIndex (row, column, (void *) (iter.uint () + m_id_start));
}
} else {
return QModelIndex ();
}
}
void
LayerTreeModel::clear_locate ()
{
m_selected_indexes.clear ();
m_current_index = m_selected_indexes.begin ();
m_selected_ids.clear ();
signal_data_changed ();
}
QModelIndex
LayerTreeModel::locate_next ()
{
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
++m_current_index;
if (m_current_index == m_selected_indexes.end ()) {
m_current_index = m_selected_indexes.begin ();
}
return *m_current_index;
}
}
QModelIndex
LayerTreeModel::locate_prev ()
{
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
if (m_current_index == m_selected_indexes.begin ()) {
m_current_index = m_selected_indexes.end ();
}
--m_current_index;
return *m_current_index;
}
}
void
LayerTreeModel::search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse)
{
int children = rowCount (parent);
for (int i = 0; i < children; ++i) {
QModelIndex child = index (i, 0, parent);
lay::LayerPropertiesConstIterator iter (iterator (child));
if (!iter.is_null () && !iter.at_end () &&
pattern.match (iter->display_string (mp_view, true /*real*/))) {
m_selected_indexes.push_back (child);
}
if (recurse && iter->has_children ()) {
search_children (pattern, child, recurse);
}
}
}
QModelIndex
LayerTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only)
{
m_selected_indexes.clear ();
tl::GlobPattern p = tl::GlobPattern (std::string (name));
p.set_case_sensitive (case_sensitive);
p.set_exact (!glob_pattern);
p.set_header_match (true);
search_children (p, QModelIndex (), !top_only);
m_selected_ids.clear ();
for (std::vector<QModelIndex>::const_iterator i = m_selected_indexes.begin (); i != m_selected_indexes.end (); ++i) {
m_selected_ids.insert (size_t (i->internalPointer ()));
}
signal_data_changed ();
m_current_index = m_selected_indexes.begin ();
if (m_current_index == m_selected_indexes.end ()) {
return QModelIndex ();
} else {
return *m_current_index;
}
}
void
LayerTreeModel::signal_data_changed ()
{
m_test_shapes_cache.clear ();
emit dataChanged (upperLeft (), bottomRight ());
}
void
LayerTreeModel::signal_begin_layer_changed ()
{
m_id_start = m_id_end; // means: model is invalid
m_test_shapes_cache.clear ();
emit layoutAboutToBeChanged ();
}
void
LayerTreeModel::signal_layer_changed ()
{
// establish a new range of valid iterator indices
m_id_start = m_id_end;
// TODO: is there a more efficient way to compute that?
size_t max_id = 0;
for (lay::LayerPropertiesConstIterator iter = mp_view->get_properties(); ! iter.at_end (); ++iter) {
max_id = std::max (iter.uint (), max_id);
}
m_id_end += max_id + 1;
m_test_shapes_cache.clear ();
emit layoutChanged ();
}
QModelIndex
LayerTreeModel::upperLeft () const
{
if (mp_view->layer_model_updated ()) {
lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ());
iter.next_sibling (0);
return createIndex (0, 0, (void *) (iter.uint () + m_id_start));
} else {
return QModelIndex ();
}
}
QModelIndex
LayerTreeModel::bottomRight () const
{
if (mp_view->layer_model_updated ()) {
// navigate to the last top-level item
lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ());
int n = int (mp_view->get_properties ().end_const () - mp_view->get_properties ().begin_const ()) - 1;
iter.next_sibling (n);
// navigate to the last child
QModelIndex p = createIndex (n, 1, (void *) (iter.uint () + m_id_start));
int nr = 0;
while (p.isValid () && (nr = rowCount (p)) > 0) {
p = index (nr - 1, 0, p);
}
return p;
} else {
return QModelIndex ();
}
}
QModelIndex
LayerTreeModel::parent (const QModelIndex &index) const
{
try {
if (mp_view->layer_model_updated ()) {
lay::LayerPropertiesConstIterator iter (iterator (index));
if (iter.is_null () || iter.at_end ()) {
return QModelIndex ();
} else {
iter.up ();
if (iter.is_null ()) {
return QModelIndex ();
} else {
// It is important that the column index of the parent is 0.
// Otherwise the tree view will not behave as expected.
return createIndex (iter.child_index (), 0, (void *) (iter.uint () + m_id_start));
}
}
} else {
return QModelIndex ();
}
} catch (tl::Exception &ex) {
// this can happen because some internal indices might not be in place properly - in particular the
// element the mouse was over (hover index)
tl::warn << ex.msg ();
return QModelIndex ();
}
}
/**
* @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,
QImage *pimage, const lay::DitherPattern &dither_pattern, const lay::LineStyles &line_styles,
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, pimage, width, height, false, 0);
}
bool
LayerTreeModel::empty_predicate (const QModelIndex &index) const
{
lay::LayerPropertiesConstIterator iter (iterator (index));
if (iter.is_null () || iter.at_end ()) {
return true;
} else if (iter->is_cell_box_layer () || iter->is_standard_layer ()) {
return iter->bbox ().empty ();
} else {
// special purpose layers are always visible
return false;
}
}
bool
LayerTreeModel::empty_within_view_predicate (const QModelIndex &index) const
{
lay::LayerPropertiesConstIterator iter (iterator (index));
if (iter.is_null () || iter.at_end ()) {
return false;
} else if (iter->is_standard_layer ()) {
int cv_index = iter->cellview_index ();
if (! mp_view->cellview (cv_index).is_valid ()) {
return true;
}
const lay::CellView &cv = mp_view->cellview (cv_index);
const db::Layout &layout = cv->layout ();
int layer_id = iter->layer_index ();
if (! layout.is_valid_layer (layer_id)) {
return true;
}
db::cell_index_type ci = cv.cell_index ();
const db::Cell &cell = layout.cell (ci);
db::ICplxTrans ctx_trans = cv.context_trans ();
std::vector<db::DCplxTrans> trans = iter->trans ();
const lay::Viewport &vp = mp_view->viewport ();
db::DCplxTrans vp_trans = vp.trans ();
int width = vp.width ();
int height = vp.height ();
for (std::vector<db::DCplxTrans>::const_iterator t = trans.begin (); t != trans.end (); ++t) {
db::CplxTrans ct = vp_trans * *t * db::CplxTrans(layout.dbu ()) * ctx_trans;
// the following scheme to compute the region avoids problems with accessing designs through very large viewports:
db::Coord lim = std::numeric_limits<db::Coord>::max ();
db::DBox world (ct * db::Box (db::Point (-lim, -lim), db::Point (lim, lim)));
db::Box region = ct.inverted () * (world & db::DBox (db::DPoint (0.0, 0.0), db::DPoint (width, height)));
region &= cell.bbox ();
if (! m_test_shapes_cache.is_empty_within_view (&layout, ci, region, layer_id)) {
return false;
}
}
return true;
} else if (iter->is_cell_box_layer ()) {
// There is no "within view" method for cell frame layers currently.
return iter->bbox ().empty ();
} else {
// Other special purpose layers are always visible
return false;
}
}
QVariant
LayerTreeModel::data (const QModelIndex &index, int role) const
{
if (mp_view->layer_model_updated ()) {
lay::LayerPropertiesConstIterator iter (iterator (index));
if (iter.is_null () || iter.at_end ()) {
return QVariant ();
} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
if (index.column () == 1) {
return QVariant (tl::to_qstring (iter->display_string (mp_view, true /*real*/)));
}
} else if (role == Qt::DecorationRole) {
if (index.column () == 0) {
bool animate_visible = true;
unsigned int di_off = 0;
if (iter->animation (true)) {
if (iter->animation (true) == 1) {
// scrolling
di_off += m_phase;
} else if (iter->animation (true) == 2) {
// blinking
animate_visible = ((m_phase & 1) == 0);
} else {
// inversly blinking
animate_visible = ((m_phase & 1) != 0);
}
}
unsigned int w = 32;
unsigned int h = 16;
if (animate_visible) {
lay::color_t def_color = 0x808080;
lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color;
lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color;
QImage image (w, h, QImage::Format_RGB32);
image.fill (m_background_color.rgb ());
// TODO: adjust the resolution according to the oversampling mode
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 mask_w = 31;
unsigned int mask_all = 0xfffffffe;
unsigned int mask_left = 0x80000000;
unsigned int mask_right = 0x00000002;
unsigned int mask_center = 0x00010000;
if (! iter->visible (true)) {
mask_w = 8;
mask_all = 0xff800000;
mask_left = 0x80000000;
mask_right = 0x00800000;
mask_center = 0x08000000;
// Show the arrow if it is invisible also locally.
if (! iter->visible (false)) {
text.scanline (4) [0] = 0x00008000 << 1;
text.scanline (5) [0] = 0x00018000 << 1;
text.scanline (6) [0] = 0x00038000 << 1;
text.scanline (7) [0] = 0x00078000 << 1;
text.scanline (8) [0] = 0x00038000 << 1;
text.scanline (9) [0] = 0x00018000 << 1;
text.scanline (10) [0] = 0x00008000 << 1;
}
}
if (mp_view->no_stipples ()) {
// Show a partial stipple pattern only for "no stipple" mode
for (unsigned int i = 1; i < h - 2; ++i) {
fill.scanline (i) [0] = 0xff800000;
}
} else {
for (unsigned int i = 1; i < h - 2; ++i) {
fill.scanline (i) [0] = mask_all;
}
}
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;
}
int p0 = lw / 2;
int p1 = (lw - 1) / 2;
if (p0 < 0) {
p0 = 0;
} else if (p0 > 7) {
p0 = 7;
}
if (p1 < 0) {
p1 = 0;
} else if (p1 > 7) {
p1 = 7;
}
int p0x = p0, p1x = p1;
unsigned int ddx = 0;
unsigned int ddy = h - 2 - p1 - p0;
if (iter->xfill (true)) {
ddx = mask_w - p0 - p1 - 1;
}
unsigned int d = ddx / 2;
frame.scanline (p0) [0] = mask_all << p1;
for (unsigned int i = p0; i < h - 2; ++i) {
frame.scanline (i) [0] |= (mask_left >> p0) | (mask_right << p1);
frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x);
while (d < ddx) {
d += ddy;
frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x);
++p0x;
++p1x;
}
if (d >= ddx) {
d -= ddx;
}
}
frame.scanline (h - 2 - p1) [0] = mask_all << p1;
if (! iter->valid (true)) {
text.scanline (4) [0] |= 0x00000c60;
text.scanline (5) [0] |= 0x00000ee0;
text.scanline (6) [0] |= 0x000007c0;
text.scanline (7) [0] |= 0x00000380;
text.scanline (8) [0] |= 0x000007c0;
text.scanline (9) [0] |= 0x00000ee0;
text.scanline (10) [0] |= 0x00000c60;
for (unsigned int i = 3; i < 12; ++i) {
fill.scanline (i) [0] &= ~0x00001ff0;
frame.scanline (i) [0] &= ~0x00001ff0;
}
}
vertex.scanline (h / 2 - 1) [0] = mask_center;
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, mp_view->dither_pattern (), mp_view->line_styles (), w, h);
// create frame
if (lw == 0) {
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), 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, mp_view->dither_pattern (), mp_view->line_styles (), w, h);
}
// create text
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h);
// create vertex
single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, mp_view->dither_pattern (),mp_view->line_styles (), w, h);
QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround
return QVariant (QIcon (pixmap));
} else {
return QVariant (QIcon ());
}
}
} else if (role == Qt::BackgroundRole) {
if (m_selected_ids.find (size_t (index.internalPointer ())) != m_selected_ids.end ()) {
// for selected items pick a color between Highlight and Base
QPalette pl (mp_view->palette ());
QColor c1 = pl.color (QPalette::Highlight);
QColor cb = pl.color (QPalette::Base);
return QVariant (QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2));
} else {
return QVariant ();
}
} else if (role == Qt::TextColorRole || role == Qt::FontRole) {
if (index.column () == 1) {
QColor c1 = m_text_color;
QColor cb = m_background_color;
QColor c0 = QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2);
bool empty;
if (m_test_shapes_in_view) {
empty = empty_within_view_predicate (index);
} else {
empty = iter->bbox ().empty ();
}
// set the color to one with less contrast if there is nothing on this layer here
if (role == Qt::FontRole) {
QFont f = m_font;
f.setBold (! empty);
return QVariant (f);
} else {
return QVariant (empty ? c0 : c1);
}
}
}
}
return QVariant ();
}
lay::LayerPropertiesConstIterator
LayerTreeModel::iterator (const QModelIndex &index) const
{
if (index.isValid ()) {
size_t iter_index = size_t (index.internalPointer ());
if (mp_view->layer_lists () > 0 && iter_index >= m_id_start && iter_index < m_id_end) {
return lay::LayerPropertiesConstIterator (mp_view->get_properties (), iter_index - m_id_start);
}
}
return lay::LayerPropertiesConstIterator ();
}
QModelIndex
LayerTreeModel::index (lay::LayerPropertiesConstIterator iter, int column) const
{
try {
std::vector<int> rows;
while (! iter.is_null ()) {
rows.push_back (int (iter.child_index ()));
iter = iter.parent ();
}
QModelIndex idx;
for (std::vector<int>::reverse_iterator r = rows.rbegin (); r != rows.rend (); ++r) {
idx = index (*r, column, idx);
}
return idx;
} catch (tl::Exception &ex) {
tl::warn << ex.msg ();
return QModelIndex ();
}
}
} // namespace lay