2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
KLayout Layout Viewer
|
2017-02-12 15:28:14 +01:00
|
|
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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>
|
2017-02-28 22:24:14 +01:00
|
|
|
#include <QPalette>
|
2017-02-12 13:21:08 +01:00
|
|
|
#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.
|
|
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if ((m_flags & Parents) != 0) {
|
|
|
|
|
|
|
|
|
|
m_flat = true; // no "hierarchical parents" yet.
|
|
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} 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) {
|
|
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
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));
|
2017-02-12 13:21:08 +01:00
|
|
|
} 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)) {
|
2017-02-28 22:24:14 +01:00
|
|
|
QColor c1 = pl.color (QPalette::Text);
|
|
|
|
|
QColor cb = pl.color (QPalette::Base);
|
2017-02-12 13:21:08 +01:00
|
|
|
return QVariant (QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2));
|
|
|
|
|
} else {
|
2017-02-28 22:24:14 +01:00
|
|
|
return QVariant (pl.color (QPalette::Text));
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#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 ();
|
2017-02-28 22:24:14 +01:00
|
|
|
m_selected_indexes_set.clear ();
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
QModelIndex
|
|
|
|
|
CellTreeModel::locate_prev ()
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
if (mp_layout->under_construction () || (mp_layout->manager () && mp_layout->manager ()->transacting ())) {
|
|
|
|
|
return QModelIndex ();
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2017-02-28 22:24:14 +01:00
|
|
|
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));
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-02-28 22:24:14 +01:00
|
|
|
search_children (pattern, c);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-02-28 22:24:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-28 22:24:14 +01:00
|
|
|
|
|
|
|
|
m_selected_indexes_set.clear ();
|
|
|
|
|
m_selected_indexes_set.insert (m_selected_indexes.begin (), m_selected_indexes.end ());
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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
|