mirror of
https://github.com/KLayout/klayout.git
synced 2026-08-30 09:49:21 +02:00
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
823 lines
24 KiB
C++
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
|