klayout/src/laybasic/layCellTreeModel.cc

821 lines
22 KiB
C++
Raw Normal View History

/*
KLayout Layout Viewer
2017-02-12 15:28:14 +01:00
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 "layCellTreeModel.h"
#include "layLayoutView.h"
#include "tlGlobPattern.h"
#include "dbPCellHeader.h"
#include <QTreeView>
#include <QPalette>
#include <QMimeData>
#include <string.h>
#include <algorithm>
namespace lay {
// --------------------------------------------------------------------
// A compare functor for the cell tree items by area
struct cmp_cell_tree_items_f
{
cmp_cell_tree_items_f (CellTreeModel::Sorting s)
: m_sorting (s)
{ }
bool operator() (const CellTreeItem *a, const CellTreeItem *b)
{
if (m_sorting == CellTreeModel::ByArea) {
if (a->by_area_equal_than (b)) {
return a->by_name_less_than (b);
} else {
return a->by_area_less_than (b);
}
} else if (m_sorting == CellTreeModel::ByAreaReverse) {
if (a->by_area_equal_than (b)) {
return a->by_name_less_than (b);
} else {
return b->by_area_less_than (a);
}
} else {
return a->by_name_less_than (b);
}
}
private:
CellTreeModel::Sorting m_sorting;
};
// --------------------------------------------------------------------
// A compare functor for the cell tree items vs. name
struct cmp_cell_tree_item_vs_name_f
{
bool operator() (const CellTreeItem *a, const char *name)
{
return a->name_less_than (name);
}
};
// --------------------------------------------------------------------
// CellTreeItem implementation
CellTreeItem::CellTreeItem (const db::Layout *layout, CellTreeItem *parent, bool is_pcell, size_t cell_index, bool flat, CellTreeModel::Sorting s)
: mp_layout (layout), mp_parent (parent), m_sorting (s), m_is_pcell (is_pcell), m_index (0), m_children (), m_cell_index (cell_index)
{
if (! flat && ! is_pcell) {
m_child_count = mp_layout->cell (m_cell_index).child_cells ();
} else {
m_child_count = 0;
}
}
CellTreeItem::~CellTreeItem ()
{
for (std::vector<CellTreeItem *>::iterator c = m_children.begin (); c != m_children.end (); ++c) {
delete *c;
}
m_children.clear ();
}
std::string
CellTreeItem::display_text () const
{
if (m_is_pcell) {
return name ();
} else if (mp_layout->is_valid_cell_index (m_cell_index)) {
return mp_layout->cell (m_cell_index).get_display_name ();
} else {
return std::string ();
}
}
int
CellTreeItem::children () const
{
return m_child_count;
}
CellTreeItem *
CellTreeItem::child (int index)
{
if (! m_is_pcell && int (m_children.size ()) <= index) {
// create a list of child sub-item
const db::Cell *cell = & mp_layout->cell (m_cell_index);
m_children.reserve (m_child_count);
for (db::Cell::child_cell_iterator child = cell->begin_child_cells (); ! child.at_end (); ++child) {
CellTreeItem *child_item = new CellTreeItem (mp_layout, this, false, *child, false, m_sorting);
m_children.push_back (child_item);
}
std::sort (m_children.begin (), m_children.end (), cmp_cell_tree_items_f (m_sorting));
for (size_t i = 0; i < m_children.size (); ++i) {
m_children [i]->set_index (i);
}
}
return m_children [index];
}
db::cell_index_type
CellTreeItem::cell_index () const
{
return m_cell_index;
}
CellTreeItem *
CellTreeItem::parent () const
{
return mp_parent;
}
const char *
CellTreeItem::name () const
{
if (! m_is_pcell) {
return mp_layout->cell_name (m_cell_index);
} else {
return mp_layout->pcell_header (m_cell_index)->get_name ().c_str ();
}
}
bool
CellTreeItem::by_name_less_than (const CellTreeItem *b) const
{
#if 0 // with name:
return strcmp (name (), b->name ()) < 0;
#else // with display text:
return display_text () < b->display_text ();
#endif
}
bool
CellTreeItem::name_less_than (const char *n) const
{
#if 0 // with name:
return strcmp (name (), n) < 0;
#else // with display text:
return display_text () < n;
#endif
}
bool
CellTreeItem::name_equals (const char *n) const
{
#if 0 // with name:
return strcmp (name (), n) == 0;
#else // with display text:
return display_text () == n;
#endif
}
bool
CellTreeItem::name_matches (const tl::GlobPattern &p) const
{
#if 0 // with name:
return p.match (name ());
#else // with display text:
return p.match (display_text ());
#endif
}
bool
CellTreeItem::by_area_less_than (const CellTreeItem *b) const
{
if (m_is_pcell || b->is_pcell ()) {
return m_is_pcell > b->is_pcell ();
}
// Hint: since mp_layout == b.mp_layout, not conversion to um^2 is required because of different DBU
return mp_layout->cell (m_cell_index).bbox ().area () < b->mp_layout->cell (b->m_cell_index).bbox ().area ();
}
bool
CellTreeItem::by_area_equal_than (const CellTreeItem *b) const
{
if (m_is_pcell != b->is_pcell ()) {
return false;
}
// Hint: since mp_layout == b.mp_layout, not conversion to um^2 is required because of different DBU
return mp_layout->cell (m_cell_index).bbox ().area () == b->mp_layout->cell (b->m_cell_index).bbox ().area ();
}
// --------------------------------------------------------------------
// CellTreeModel implementation
// Hint: it may happen that the cell tree model gets engaged while the layout is not
// valid ("under construction"). In this case, the model will return defaults or void
// objects.
CellTreeModel::CellTreeModel (QWidget *parent, lay::LayoutView *view, int cv_index, unsigned int flags, const db::Cell *base, Sorting sorting)
: QAbstractItemModel (parent),
m_flags (flags),
m_sorting (sorting),
mp_parent (parent),
mp_view (view),
m_cv_index (cv_index),
mp_base (base)
{
mp_view->cell_visibility_changed_event.add (this, &CellTreeModel::signal_data_changed);
mp_view->cellview_changed_event.add (this, &CellTreeModel::signal_data_changed_with_int);
m_flat = ((flags & Flat) != 0) && ((flags & TopCells) == 0);
m_pad = ((flags & NoPadding) == 0);
mp_layout = & view->cellview (cv_index)->layout ();
tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ()));
build_top_level ();
m_current_index = m_selected_indexes.begin ();
}
CellTreeModel::CellTreeModel (QWidget *parent, db::Layout *layout, unsigned int flags, const db::Cell *base, Sorting sorting)
: QAbstractItemModel (parent),
m_flags (flags),
m_sorting (sorting),
mp_parent (parent),
mp_view (0),
m_cv_index (-1),
mp_base (base)
{
m_flat = ((flags & Flat) != 0) && ((flags & TopCells) == 0);
m_pad = ((flags & NoPadding) == 0);
mp_layout = layout;
tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ()));
build_top_level ();
m_current_index = m_selected_indexes.begin ();
}
CellTreeModel::~CellTreeModel ()
{
clear_top_level ();
}
void
CellTreeModel::set_sorting (Sorting s)
{
if (s != m_sorting) {
m_sorting = s;
// Important: reset here, since otherwise QModelIndex objects with the wrong internalPointer will exist inside the view.
beginResetModel ();
clear_top_level ();
build_top_level ();
endResetModel ();
signal_data_changed ();
}
}
void
CellTreeModel::clear_top_level ()
{
for (std::vector<CellTreeItem *>::iterator c = m_toplevel.begin (); c != m_toplevel.end (); ++c) {
delete *c;
}
m_toplevel.clear ();
}
void
CellTreeModel::build_top_level ()
{
if ((m_flags & Children) != 0) {
m_flat = true; // no "hierarchical children" yet.
if (mp_base) {
m_toplevel.reserve (mp_base->child_cells ());
for (db::Cell::child_cell_iterator child = mp_base->begin_child_cells (); ! child.at_end (); ++child) {
CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *child, true, m_sorting);
m_toplevel.push_back (item);
}
}
} else if ((m_flags & Parents) != 0) {
m_flat = true; // no "hierarchical parents" yet.
if (mp_base) {
m_toplevel.reserve (mp_base->parent_cells ());
for (db::Cell::parent_cell_iterator parent = mp_base->begin_parent_cells (); parent != mp_base->end_parent_cells (); ++parent) {
CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *parent, true, m_sorting);
m_toplevel.push_back (item);
}
}
} else {
if (m_flat) {
m_toplevel.reserve (mp_layout->cells ());
}
db::Layout::top_down_const_iterator top = mp_layout->begin_top_down ();
while (top != mp_layout->end_top_down ()) {
if (m_flat) {
CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *top, true, m_sorting);
m_toplevel.push_back (item);
} else if (mp_layout->cell (*top).is_top ()) {
if ((m_flags & BasicCells) == 0 || ! mp_layout->cell (*top).is_proxy ()) {
CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *top, (m_flags & TopCells) != 0, m_sorting);
m_toplevel.push_back (item);
}
} else {
break;
}
++top;
}
if ((m_flags & BasicCells) != 0) {
for (db::Layout::pcell_iterator pc = mp_layout->begin_pcells (); pc != mp_layout->end_pcells (); ++pc) {
CellTreeItem *item = new CellTreeItem (mp_layout, 0, true, pc->second, true, m_sorting);
m_toplevel.push_back (item);
}
}
}
std::sort (m_toplevel.begin (), m_toplevel.end (), cmp_cell_tree_items_f (m_sorting));
for (size_t i = 0; i < m_toplevel.size (); ++i) {
m_toplevel [i]->set_index (i);
}
}
Qt::ItemFlags
CellTreeModel::flags (const QModelIndex &index) const
{
return Qt::ItemIsDragEnabled | QAbstractItemModel::flags (index);
}
QStringList
CellTreeModel::mimeTypes () const
{
QStringList types;
types << QString::fromUtf8 (lay::drag_drop_mime_type ());
return types;
}
QMimeData *
CellTreeModel::mimeData(const QModelIndexList &indexes) const
{
const db::Cell *c = 0;
for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end () && !c; ++i) {
if (i->isValid()) {
c = cell (*i);
}
}
if (c) {
lay::CellDragDropData data (mp_layout, c->cell_index ());
return data.to_mime_data ();
} else {
return 0;
}
}
int
CellTreeModel::columnCount (const QModelIndex &) const
{
return 1;
}
QVariant
CellTreeModel::data (const QModelIndex &index, int role) const
{
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
if (! item || mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QVariant ();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
if (m_pad) {
return QVariant (tl::to_qstring (" " + item->display_text () + " "));
} else {
return QVariant (tl::to_qstring (item->display_text ()));
}
} else if (role == Qt::FontRole) {
if (! mp_view) {
return QVariant ();
} else {
QFont f (mp_parent->font ());
const lay::CellView::unspecific_cell_path_type &path = mp_view->cellview (m_cv_index).unspecific_path ();
const lay::CellView::specific_cell_path_type &ctx_path = mp_view->cellview (m_cv_index).specific_path ();
if (! path.empty ()) {
if (item->cell_index () == path.back ()) {
if (m_flat) {
f.setBold (true);
} else {
CellTreeItem *it = item;
lay::CellView::unspecific_cell_path_type::const_iterator p = path.end ();
while (it && p != path.begin ()) {
--p;
if (it->cell_index () != *p) {
break;
}
it = it->parent ();
}
if (! it && p == path.begin ()) {
f.setBold (true);
}
}
} else if (! ctx_path.empty () && item->cell_index () == ctx_path.back ().inst_ptr.cell_index ()) {
if (m_flat) {
f.setUnderline (true);
} else {
CellTreeItem *it = item;
lay::CellView::specific_cell_path_type::const_iterator cp = ctx_path.end ();
while (it && cp != ctx_path.begin ()) {
--cp;
if (it->cell_index () != cp->inst_ptr.cell_index ()) {
break;
}
it = it->parent ();
}
if (cp == ctx_path.begin ()) {
lay::CellView::unspecific_cell_path_type::const_iterator p = path.end ();
while (it && p != path.begin ()) {
--p;
if (it->cell_index () != *p) {
break;
}
it = it->parent ();
}
if (! it && p == path.begin ()) {
f.setUnderline (true);
}
}
}
}
}
if (mp_view->is_cell_hidden (item->cell_index (), m_cv_index)) {
f.setStrikeOut (true);
}
return QVariant (f);
}
} else if (role == Qt::BackgroundRole) {
if (m_selected_indexes_set.find (index) != m_selected_indexes_set.end ()) {
// for selected items pick a color between Highlight and Base
QPalette pl (mp_parent->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) {
#if 0 // do strikeout rather than making the color darker
if (! mp_view) {
return QVariant ();
} else {
QPalette pl (mp_parent->palette ());
if (mp_view->is_cell_hidden (item->cell_index (), m_cv_index)) {
QColor c1 = pl.color (QPalette::Text);
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 (pl.color (QPalette::Text));
}
}
#else
return QVariant ();
#endif
} else {
return QVariant ();
}
}
QVariant
CellTreeModel::headerData (int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const
{
return QVariant ();
}
int
CellTreeModel::rowCount (const QModelIndex &parent) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return 0;
} else if (parent.isValid ()) {
CellTreeItem *item = (CellTreeItem *) parent.internalPointer ();
if (! item) {
return 0;
} else if (! mp_layout->is_valid_cell_index (item->cell_index ())) {
// for safety we return 0 children for invalid cells
return 0;
} else {
return int (item->children ());
}
} else {
return int (m_toplevel.size ());
}
}
QModelIndex
CellTreeModel::index (int row, int column, const QModelIndex &parent) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
} else if (parent.isValid ()) {
CellTreeItem *item = (CellTreeItem *) parent.internalPointer ();
if (! item) {
return QModelIndex ();
} else if (! mp_layout->is_valid_cell_index (item->cell_index ())) {
// for safety we don't return valid child indexes for invalid cells
return QModelIndex ();
} else {
return createIndex (row, column, item->child (row));
}
} else if (row >= 0 && row < int (m_toplevel.size ())) {
return createIndex (row, column, m_toplevel [row]);
} else {
return QModelIndex ();
}
}
QModelIndex
CellTreeModel::parent (const QModelIndex &index) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
}
if (! index.isValid ()) {
return index;
}
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
if (! item) {
return QModelIndex ();
}
CellTreeItem *pitem = item->parent ();
if (pitem) {
return createIndex (int (pitem->index ()), index.column (), pitem);
} else {
return QModelIndex ();
}
}
int
CellTreeModel::toplevel_items () const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return 0;
} else {
return int (m_toplevel.size ());
}
}
CellTreeItem *
CellTreeModel::toplevel_item (int index)
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return 0;
} else {
return m_toplevel [index];
}
}
QModelIndex
CellTreeModel::model_index (CellTreeItem *item) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
} else {
return createIndex (int (item->index ()), 0, item);
}
}
bool
CellTreeModel::is_pcell (const QModelIndex &index) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return false;
} else {
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
return item->is_pcell ();
}
}
db::pcell_id_type
CellTreeModel::pcell_id (const QModelIndex &index) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return 0;
} else {
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
return item->cell_index ();
}
}
db::cell_index_type
CellTreeModel::cell_index (const QModelIndex &index) const
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return 0;
} else {
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
return item->cell_index ();
}
}
const db::Cell *
CellTreeModel::cell (const QModelIndex &index) const
{
if (index.isValid () && ! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
return & mp_layout->cell (item->cell_index ());
} else {
return 0;
}
}
const char *
CellTreeModel::cell_name (const QModelIndex &index) const
{
if (index.isValid () && ! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
CellTreeItem *item = (CellTreeItem *) index.internalPointer ();
if (item->is_pcell ()) {
return mp_layout->pcell_header (item->cell_index ())->get_name ().c_str ();
} else {
return mp_layout->cell_name (item->cell_index ());
}
} else {
return 0;
}
}
void
CellTreeModel::clear_locate ()
{
m_selected_indexes.clear ();
m_current_index = m_selected_indexes.begin ();
m_selected_indexes_set.clear ();
signal_data_changed ();
}
QModelIndex
CellTreeModel::locate_next ()
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
}
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
CellTreeModel::locate_prev ()
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
}
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
CellTreeModel::search_children (const tl::GlobPattern &pattern, CellTreeItem *item)
{
int children = item->children ();
for (int i = 0; i < children; ++i) {
CellTreeItem *c = item->child (i);
if (c) {
if (c->name_matches (pattern)) {
m_selected_indexes.push_back (model_index (c));
}
search_children (pattern, c);
}
}
}
QModelIndex
CellTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only)
{
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
}
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);
for (std::vector <CellTreeItem *>::const_iterator lc = m_toplevel.begin (); lc != m_toplevel.end (); ++lc) {
if ((*lc)->name_matches (p)) {
m_selected_indexes.push_back (model_index (*lc));
}
if (! top_only) {
search_children (p, *lc);
}
}
m_selected_indexes_set.clear ();
m_selected_indexes_set.insert (m_selected_indexes.begin (), m_selected_indexes.end ());
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;
}
}
QModelIndex
CellTreeModel::topLeft () const
{
if (m_toplevel.empty () || mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
} else {
return model_index (m_toplevel.front ());
}
}
QModelIndex
CellTreeModel::bottomRight () const
{
if (m_toplevel.empty () || mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
return QModelIndex ();
} else {
QModelIndex p = model_index (m_toplevel.back ());
int nr = 0;
while (p.isValid () && (nr = rowCount (p)) > 0) {
p = index (nr - 1, 0, p);
}
return p;
}
}
} // namespace lay