klayout/src/lay/layMacroVariableView.cc

313 lines
8.1 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 "layMacroVariableView.h"
#include "tlException.h"
#include "tlScriptError.h"
#include "gsiInspector.h"
#include <cstdio>
namespace
{
/**
* @brief A placeholder item that is expanded on demand
*/
class PlaceholderItem
: public QTreeWidgetItem
{
public:
PlaceholderItem (gsi::Inspector *inspector)
: mp_inspector (inspector)
{
// .. nothing yet ..
}
gsi::Inspector *inspector ()
{
return mp_inspector.get ();
}
private:
std::auto_ptr<gsi::Inspector> mp_inspector;
};
}
namespace lay
{
/**
* @brief Converts a tl::Variant to a nice string
*/
QString
pretty_print (const tl::Variant &v)
{
if (v.is_nil ()) {
return QObject::tr ("(nil)");
} else if (v.is_double ()) {
QString res;
res.sprintf ("%.12g", v.to_double ());
return res;
} else if (v.is_char ()) {
QString details;
details.sprintf ("#%d (0x%x)", v.to_int (), v.to_uint ());
return tl::to_qstring (std::string ("'") + v.to_string () + "' ") + details;
} else if (v.is_ulong () || v.is_long () || v.is_ulonglong () || v.is_longlong ()) {
QString details;
details.sprintf (" (0x%llx)", v.to_ulonglong ());
return tl::to_qstring (v.to_string ()) + details;
} else {
return tl::to_qstring (v.to_parsable_string ());
}
}
/**
* @brief Return an inspector's description
*
* This function also adds an error catch to show evaluation errors
*/
static QString
inspector_description (const gsi::Inspector *inspector)
{
try {
return tl::to_qstring (inspector->description ());
} catch (tl::ScriptError &ex) {
return QObject::tr ("Error") + QString::fromUtf8 (": ") + tl::to_qstring (ex.basic_msg ());
} catch (tl::Exception &ex) {
return QObject::tr ("Error") + QString::fromUtf8 (": ") + tl::to_qstring (ex.msg ());
} catch (...) {
return QObject::tr ("Error (unspecific)");
}
}
/**
* @brief Return an inspected value
*
* This function also adds an error catch to show evaluation errors
*/
static QString
inspector_value (const gsi::Inspector *inspector, int index)
{
try {
return pretty_print (inspector->value (index));
} catch (tl::ScriptError &ex) {
return QObject::tr ("Error") + QString::fromUtf8 (": ") + tl::to_qstring (ex.basic_msg ());
} catch (tl::Exception &ex) {
return QObject::tr ("Error") + QString::fromUtf8 (": ") + tl::to_qstring (ex.msg ());
} catch (...) {
return QObject::tr ("Error (unspecific)");
}
}
/**
* @brief Helper: updates a text and makes it bold if it changed
*/
static void
update_value (QTreeWidgetItem *item, const QString &text, bool fresh)
{
int column = 1;
QFont f (item->font (column));
if (! fresh && item->text (column) != text) {
f.setWeight (QFont::Bold);
} else {
f.setWeight (QFont::Normal);
}
item->setFont (column, f);
item->setText (column, text);
item->setToolTip (column, text);
}
MacroVariableView::MacroVariableView (QWidget *parent)
: QTreeWidget (parent), m_show_all (false)
{
connect (this, SIGNAL (itemExpanded (QTreeWidgetItem *)), this, SLOT (expanded (QTreeWidgetItem *)));
}
void MacroVariableView::set_inspector (gsi::Inspector *inspector)
{
if (inspector != mp_inspector.get ()) {
bool fresh = (! inspector || ! mp_inspector.get () || ! mp_inspector->equiv (inspector));
if (fresh) {
clear ();
}
mp_inspector.reset (inspector);
if (inspector) {
sync (fresh);
}
}
}
void MacroVariableView::sync (bool fresh)
{
sync (invisibleRootItem (), mp_inspector.get (), fresh);
}
void MacroVariableView::expanded (QTreeWidgetItem *item)
{
// Replace the (single) placeholder item by the complete list of items
if (item->childCount () > 0) {
PlaceholderItem *ph = dynamic_cast<PlaceholderItem *> (item->child (0));
if (ph) {
std::auto_ptr<QTreeWidgetItem> ph_taken (item->takeChild (0));
sync (item, ph->inspector (), true);
}
}
}
void MacroVariableView::set_show_all (bool show_all)
{
if (m_show_all != show_all) {
m_show_all = show_all;
if (mp_inspector.get ()) {
sync (true);
}
}
}
void MacroVariableView::sync_item (QTreeWidgetItem *parent, gsi::Inspector *inspector, const QString &key, size_t index, int pos, bool fresh)
{
if (pos == parent->childCount ()) {
QTreeWidgetItem *item = new QTreeWidgetItem ();
item->setText (0, key);
QFont f (item->font (0));
f.setWeight (QFont::Bold);
item->setFont (0, f);
parent->addChild (item);
if (inspector->has_children (index)) {
gsi::Inspector *ci = inspector->child_inspector (index);
item->addChild (new PlaceholderItem (ci));
update_value (item, inspector_description (ci), fresh);
} else {
update_value (item, inspector_value (inspector, index), fresh);
}
} else if (parent->child (pos)->text (0) != key) {
QTreeWidgetItem *item = new QTreeWidgetItem ();
item->setText (0, key);
QFont f (item->font (0));
f.setWeight (QFont::Bold);
item->setFont (0, f);
parent->insertChild (pos, item);
if (inspector->has_children (index)) {
gsi::Inspector *ci = inspector->child_inspector (index);
item->addChild (new PlaceholderItem (ci));
update_value (item, inspector_description (ci), fresh);
} else {
update_value (item, inspector_value (inspector, index), fresh);
}
} else {
QTreeWidgetItem *item = parent->child (pos);
if (inspector->has_children (index)) {
std::auto_ptr<gsi::Inspector> ci (inspector->child_inspector (index));
update_value (item, inspector_description (ci.get ()), false);
if (item->isExpanded ()) {
sync (item, ci.get (), fresh);
} else if (item->childCount () == 0) {
item->addChild (new PlaceholderItem (ci.release ()));
}
} else {
update_value (item, inspector_value (inspector, index), false);
while (item->childCount () > 0) {
delete item->takeChild (0);
}
}
}
}
void MacroVariableView::sync (QTreeWidgetItem *parent, gsi::Inspector *inspector, bool fresh)
{
if (inspector->has_keys ()) {
// collect all top-level items
std::map<QString, size_t> keys;
for (size_t n = inspector->count (); n-- > 0; ) {
gsi::Inspector::Visibility vis = inspector->visibility (n);
if (vis == gsi::Inspector::Always || (m_show_all && vis == gsi::Inspector::IfRequested)) {
QString k = tl::to_qstring (inspector->key (n));
if (k.isEmpty ()) {
k = pretty_print (inspector->keyv (n));
}
keys.insert (std::make_pair (k, n));
}
}
// delete all items which are no longer present
for (int i = 0; i < parent->childCount (); ++i) {
QString key = parent->child (i)->text (0);
if (keys.find (key) == keys.end ()) {
delete parent->takeChild (i);
--i;
}
}
// insert or update new items
int i = 0;
for (std::map<QString, size_t>::const_iterator k = keys.begin (); k != keys.end (); ++k, ++i) {
sync_item (parent, inspector, k->first, k->second, i, fresh);
}
} else {
size_t n = inspector->count ();
// delete all items which are no longer present
while (size_t (parent->childCount ()) > n) {
delete parent->takeChild (n);
}
// insert or update new items
for (size_t i = 0; i < n; ++i) {
sync_item (parent, inspector, QString::fromUtf8("[%1]").arg (i), i, int (i), fresh);
}
}
}
}