From d160a5c27c626c8e0609b22a490f9decad996198 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Mar 2017 00:08:17 +0100 Subject: [PATCH] WIP: delete function, installation dialog (first steps) --- src/lay/SaltGrainInstallationDialog.ui | 313 ++++++++++++++++++++++ src/lay/lay.pro | 11 +- src/lay/laySalt.cc | 7 + src/lay/laySalt.h | 25 ++ src/lay/laySaltGrain.cc | 21 +- src/lay/laySaltGrain.h | 6 + src/lay/laySaltGrainInstallationDialog.cc | 101 +++++++ src/lay/laySaltGrainInstallationDialog.h | 76 ++++++ src/lay/laySaltGrains.cc | 28 ++ src/lay/laySaltGrains.h | 15 ++ src/lay/laySaltManagerDialog.cc | 209 ++------------- src/lay/laySaltModel.cc | 207 ++++++++++++++ src/lay/laySaltModel.h | 110 ++++++++ src/unit_tests/laySalt.cc | 9 + 14 files changed, 941 insertions(+), 197 deletions(-) create mode 100644 src/lay/SaltGrainInstallationDialog.ui create mode 100644 src/lay/laySaltGrainInstallationDialog.cc create mode 100644 src/lay/laySaltGrainInstallationDialog.h create mode 100644 src/lay/laySaltModel.cc create mode 100644 src/lay/laySaltModel.h diff --git a/src/lay/SaltGrainInstallationDialog.ui b/src/lay/SaltGrainInstallationDialog.ui new file mode 100644 index 000000000..ab54731cb --- /dev/null +++ b/src/lay/SaltGrainInstallationDialog.ui @@ -0,0 +1,313 @@ + + + SaltGrainInstallationDialog + + + + 0 + 0 + 692 + 440 + + + + Salt Mine Repository Browser + + + + + + Qt::Horizontal + + + + + 4 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Repository + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Install + + + + + + + Qt::Horizontal + + + + 126 + 20 + + + + + + + + + 0 + 0 + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 64 + 64 + + + + false + + + + + + + + + + + + 1 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 4 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Details + + + + + + + true + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Close + + + + + + + + :/add.png:/add.png + + + New + + + New package + + + + + + :/clear.png:/clear.png + + + Delete + + + Delete package + + + + + + :/import.png:/import.png + + + Import + + + Import package + + + + + + lay::DecoratedLineEdit + QLineEdit +
layWidgets.h
+
+ + lay::SaltGrainDetailsTextWidget + QTextBrowser +
laySaltGrainDetailsTextWidget.h
+
+
+ + details_text + + + + + + + button_box + rejected() + SaltGrainInstallationDialog + reject() + + + 559 + 425 + + + 576 + 437 + + + + +
diff --git a/src/lay/lay.pro b/src/lay/lay.pro index 7b684827e..0d1251609 100644 --- a/src/lay/lay.pro +++ b/src/lay/lay.pro @@ -52,7 +52,9 @@ HEADERS = \ laySaltManagerDialog.h \ laySaltGrainDetailsTextWidget.h \ laySaltGrainPropertiesDialog.h \ - laySaltDownloadManager.h + laySaltDownloadManager.h \ + laySaltModel.h \ + laySaltGrainInstallationDialog.h FORMS = \ ClipDialog.ui \ @@ -100,7 +102,8 @@ FORMS = \ MainConfigPage7.ui \ SaltManagerDialog.ui \ SaltGrainPropertiesDialog.ui \ - SaltGrainTemplateSelectionDialog.ui + SaltGrainTemplateSelectionDialog.ui \ + SaltGrainInstallationDialog.ui SOURCES = \ gsiDeclLayApplication.cc \ @@ -150,7 +153,9 @@ SOURCES = \ laySaltManagerDialog.cc \ laySaltGrainDetailsTextWidget.cc \ laySaltGrainPropertiesDialog.cc \ - laySaltDownloadManager.cc + laySaltDownloadManager.cc \ + laySaltModel.cc \ + laySaltGrainInstallationDialog.cc RESOURCES = layBuildInMacros.qrc \ layHelpResources.qrc \ diff --git a/src/lay/laySalt.cc b/src/lay/laySalt.cc index b71cbc107..c16bc8e56 100644 --- a/src/lay/laySalt.cc +++ b/src/lay/laySalt.cc @@ -189,6 +189,7 @@ bool remove_from_collection (SaltGrains &collection, const std::string &name) ++gnext; collection.remove_grain (g, true); res = true; + g = gnext; } } @@ -207,9 +208,15 @@ Salt::remove_grain (const SaltGrain &grain) { tl::info << QObject::tr ("Removing package '%1' ..").arg (tl::to_qstring (grain.name ())); if (remove_from_collection (m_root, grain.name ())) { + tl::info << QObject::tr ("Package '%1' removed.").arg (tl::to_qstring (grain.name ())); + + // NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure + refresh (); + invalidate (); return true; + } else { tl::warn << QObject::tr ("Failed to remove package '%1'.").arg (tl::to_qstring (grain.name ())); return false; diff --git a/src/lay/laySalt.h b/src/lay/laySalt.h index ad578c737..ff16c0280 100644 --- a/src/lay/laySalt.h +++ b/src/lay/laySalt.h @@ -123,6 +123,31 @@ public: */ SaltGrain *grain_by_name (const std::string &name); + /** + * @brief Loads the salt from a "salt mine" file + */ + void load (const std::string &p) + { + m_root.load (p); + } + + /** + * @brief Loads the salt from a "salt mine" stream + */ + void load (tl::InputStream &s) + { + m_root.load (s); + } + + /** + * @brief Saves the salt to a "salt mine" file + * This feature is provided for debugging purposes mainly. + */ + void save (const std::string &p) + { + m_root.save (p); + } + /** * @brief Removes a grain from the salt * diff --git a/src/lay/laySaltGrain.cc b/src/lay/laySaltGrain.cc index 246d44278..61ceb8fef 100644 --- a/src/lay/laySaltGrain.cc +++ b/src/lay/laySaltGrain.cc @@ -297,7 +297,7 @@ struct ImageConverter } }; -static tl::XMLStruct xml_struct ("salt-grain", +static tl::XMLElementList s_xml_elements = tl::make_member (&SaltGrain::name, &SaltGrain::set_name, "name") + tl::make_member (&SaltGrain::version, &SaltGrain::set_version, "version") + tl::make_member (&SaltGrain::title, &SaltGrain::set_title, "title") + @@ -315,8 +315,15 @@ static tl::XMLStruct xml_struct ("salt-grain", tl::make_member (&SaltGrain::Dependency::name, "name") + tl::make_member (&SaltGrain::Dependency::url, "url") + tl::make_member (&SaltGrain::Dependency::version, "version") - ) -); + ); + +static tl::XMLStruct s_xml_struct ("salt-grain", s_xml_elements); + +tl::XMLElementList & +SaltGrain::xml_struct () +{ + return s_xml_elements; +} bool SaltGrain::is_readonly () const @@ -332,7 +339,7 @@ SaltGrain::load (const std::string &p) if (p[0] != ':') { tl::XMLFileSource source (p); - xml_struct.parse (source, *this); + s_xml_struct.parse (source, *this); } else { @@ -346,7 +353,7 @@ SaltGrain::load (const std::string &p) std::string str_data (data.constData (), data.size ()); tl::XMLStringSource source (str_data); - xml_struct.parse (source, *this); + s_xml_struct.parse (source, *this); } } @@ -355,7 +362,7 @@ void SaltGrain::load (tl::InputStream &p) { tl::XMLStreamSource source (p); - xml_struct.parse (source, *this); + s_xml_struct.parse (source, *this); } void @@ -368,7 +375,7 @@ void SaltGrain::save (const std::string &p) const { tl::OutputStream os (p, tl::OutputStream::OM_Plain); - xml_struct.write (os, *this); + s_xml_struct.write (os, *this); } SaltGrain diff --git a/src/lay/laySaltGrain.h b/src/lay/laySaltGrain.h index ce21ae0f1..df30e7f49 100644 --- a/src/lay/laySaltGrain.h +++ b/src/lay/laySaltGrain.h @@ -26,6 +26,7 @@ #include "layCommon.h" #include "tlObject.h" #include "tlStream.h" +#include "tlXMLParser.h" #include #include @@ -350,6 +351,11 @@ public: */ void save (const std::string &file_path) const; + /** + * @brief Gets the XML structure representing a grain + */ + static tl::XMLElementList &xml_struct (); + /** * @brief Compares two version strings * Returns -1 if v1 < v2, 0 if v1 == v2 and 1 if v1 > v2. diff --git a/src/lay/laySaltGrainInstallationDialog.cc b/src/lay/laySaltGrainInstallationDialog.cc new file mode 100644 index 000000000..f12d7f576 --- /dev/null +++ b/src/lay/laySaltGrainInstallationDialog.cc @@ -0,0 +1,101 @@ + +/* + + 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 "laySaltGrainInstallationDialog.h" +#include "laySaltModel.h" +#include "laySaltGrainPropertiesDialog.h" +#include "laySalt.h" +#include "ui_SaltGrainTemplateSelectionDialog.h" +#include "tlString.h" +#include "tlExceptions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace lay +{ + +// -------------------------------------------------------------------------------------- +// SaltGrainInstallation implementation + +SaltGrainInstallationDialog::SaltGrainInstallationDialog (QWidget *parent, lay::Salt *salt) + : QDialog (parent), mp_salt (salt) +{ + Ui::SaltGrainInstallationDialog::setupUi (this); + + // TODO: cache package list + // @@@ + m_salt_mine.load ("/home/matthias/salt.mine"); + // @@@ + + SaltModel *model = new SaltModel (this, &m_salt_mine); + salt_view->setModel (model); + salt_view->setItemDelegate (new SaltItemDelegate (this)); + salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ())); + + connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ())); + connect (mark_button, SIGNAL (clicked ()), this, SLOT (mark ())); + connect (button_box->button (QDialogButtonBox::Apply), SIGNAL (clicked ()), this, SLOT (apply ())); + + current_changed (); +} + +void +SaltGrainInstallationDialog::current_changed () +{ + SaltGrain *g = current_grain (); + details_text->set_grain (g); + details_frame->setEnabled (g != 0); +} + +lay::SaltGrain * +SaltGrainInstallationDialog::current_grain () +{ + SaltModel *model = dynamic_cast (salt_view->model ()); + return model ? model->grain_from_index (salt_view->currentIndex ()) : 0; +} + +void +SaltGrainInstallationDialog::apply () +{ + + // @@@ + +} + +void +SaltGrainInstallationDialog::mark () +{ + + // @@@ + +} + +} diff --git a/src/lay/laySaltGrainInstallationDialog.h b/src/lay/laySaltGrainInstallationDialog.h new file mode 100644 index 000000000..9c7986fb2 --- /dev/null +++ b/src/lay/laySaltGrainInstallationDialog.h @@ -0,0 +1,76 @@ + +/* + + 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 + +*/ + +#ifndef HDR_laySaltGrainInstallationDialog +#define HDR_laySaltGrainInstallationDialog + +#include "laySalt.h" + +#include + +#include "ui_SaltGrainInstallationDialog.h" + +namespace lay +{ + +class Salt; + +/** + * @brief The dialog for managing the Salt ("Packages") + */ +class SaltGrainInstallationDialog + : public QDialog, private Ui::SaltGrainInstallationDialog +{ +Q_OBJECT + +public: + /** + * @brief Constructor + */ + SaltGrainInstallationDialog (QWidget *parent, lay::Salt *salt); + +private slots: + /** + * @brief Called when the currently selected package (grain) has changed + */ + void current_changed (); + + /** + * @brief Called when the Apply button is clicked + */ + void apply (); + + /** + * @brief Called when the Mark button is pressed + */ + void mark (); + +private: + lay::Salt *mp_salt; + lay::Salt m_salt_mine; + + lay::SaltGrain *current_grain (); +}; + +} + +#endif diff --git a/src/lay/laySaltGrains.cc b/src/lay/laySaltGrains.cc index 5c80cc884..221861340 100644 --- a/src/lay/laySaltGrains.cc +++ b/src/lay/laySaltGrains.cc @@ -233,4 +233,32 @@ SaltGrains::from_path (const std::string &path, const std::string &prefix) return grains; } +static tl::XMLElementList s_group_struct = + tl::make_member (&SaltGrains::name, &SaltGrains::set_name, "name") + + tl::make_element (&SaltGrains::begin_collections, &SaltGrains::end_collections, &SaltGrains::add_collection, "group", &s_group_struct) + + tl::make_element (&SaltGrains::begin_grains, &SaltGrains::end_grains, &SaltGrains::add_grain, "salt-grain", SaltGrain::xml_struct ()); + +static tl::XMLStruct s_xml_struct ("salt-mine", s_group_struct); + +void +SaltGrains::load (const std::string &p) +{ + tl::XMLFileSource source (p); + s_xml_struct.parse (source, *this); +} + +void +SaltGrains::load (tl::InputStream &p) +{ + tl::XMLStreamSource source (p); + s_xml_struct.parse (source, *this); +} + +void +SaltGrains::save (const std::string &p) const +{ + tl::OutputStream os (p, tl::OutputStream::OM_Plain); + s_xml_struct.write (os, *this); +} + } diff --git a/src/lay/laySaltGrains.h b/src/lay/laySaltGrains.h index 0387000a1..2dc524a5a 100644 --- a/src/lay/laySaltGrains.h +++ b/src/lay/laySaltGrains.h @@ -174,6 +174,21 @@ public: */ bool is_readonly () const; + /** + * @brief Loads the grain collection from the given path + */ + void load (const std::string &p); + + /** + * @brief Loads the grain collection from the given input stream + */ + void load (tl::InputStream &p); + + /** + * @brief Saves the grain collection to the given file + */ + void save (const std::string &p) const; + /** * @brief Scan grains from a given path * This will scan the grains found within this path and return a collection containing diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index 568040404..c0d0934be 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -21,209 +21,29 @@ */ #include "laySaltManagerDialog.h" +#include "laySaltModel.h" #include "laySaltGrainPropertiesDialog.h" +#include "laySaltGrainInstallationDialog.h" #include "laySalt.h" #include "ui_SaltGrainTemplateSelectionDialog.h" #include "tlString.h" #include "tlExceptions.h" -#include -#include -#include #include #include #include #include #include #include +#include +#include +#include namespace lay { // -------------------------------------------------------------------------------------- -/** - * @brief A model representing the salt grains for a QListView - */ -class SaltModel - : public QAbstractItemModel -{ -public: - SaltModel (QObject *parent, lay::Salt *salt) - : QAbstractItemModel (parent), mp_salt (salt) - { - // .. nothing yet .. - } - - QVariant data (const QModelIndex &index, int role) const - { - if (role == Qt::DisplayRole) { - - const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()]; - - std::string text = ""; - text += "

"; - text += tl::escaped_to_html (g->name ()); - if (!g->version ().empty ()) { - text += " "; - text += tl::escaped_to_html (g->version ()); - } - if (!g->title ().empty ()) { - text += " - "; - text += tl::escaped_to_html (g->title ()); - } - text += "

"; - if (!g->doc ().empty ()) { - text += "

"; - text += tl::escaped_to_html (g->doc ()); - text += "

"; - } - text += ""; - - return tl::to_qstring (text); - - } else if (role == Qt::DecorationRole) { - - int icon_dim = 64; - - const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()]; - if (g->icon ().isNull ()) { - return QIcon (":/salt_icon.png"); - } else { - - QImage img = g->icon (); - if (img.width () == icon_dim && img.height () == icon_dim) { - return QPixmap::fromImage (img); - } else { - - img = img.scaled (QSize (icon_dim, icon_dim), Qt::KeepAspectRatio, Qt::SmoothTransformation); - - QImage final_img (icon_dim, icon_dim, QImage::Format_ARGB32); - final_img.fill (QColor (0, 0, 0, 0)); - QPainter painter (&final_img); - painter.drawImage ((icon_dim - img.width ()) / 2, (icon_dim - img.height ()) / 2, img); - - return QPixmap::fromImage (final_img); - - } - - } - - } else { - return QVariant (); - } - } - - QModelIndex index (int row, int column, const QModelIndex &parent) const - { - if (parent.isValid ()) { - return QModelIndex (); - } else { - return createIndex (row, column, mp_salt->begin_flat () [row]); - } - } - - QModelIndex parent (const QModelIndex & /*index*/) const - { - return QModelIndex (); - } - - int columnCount(const QModelIndex & /*parent*/) const - { - return 1; - } - - int rowCount (const QModelIndex &parent) const - { - if (parent.isValid ()) { - return 0; - } else { - return mp_salt->end_flat () - mp_salt->begin_flat (); - } - } - - SaltGrain *grain_from_index (const QModelIndex &index) const - { - if (index.isValid ()) { - return static_cast (index.internalPointer ()); - } else { - return 0; - } - } - - void update () - { - reset (); - } - -public: - lay::Salt *mp_salt; -}; - -// -------------------------------------------------------------------------------------- - -/** - * @brief A delegate displaying the summary of a grain - */ -class SaltItemDelegate - : public QStyledItemDelegate -{ -public: - SaltItemDelegate (QObject *parent) - : QStyledItemDelegate (parent) - { - // .. nothing yet .. - } - - void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const - { - QStyleOptionViewItemV4 optionV4 = option; - initStyleOption (&optionV4, index); - - QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style (); - - QTextDocument doc; - doc.setHtml (optionV4.text); - - optionV4.text = QString (); - style->drawControl (QStyle::CE_ItemViewItem, &optionV4, painter); - - QAbstractTextDocumentLayout::PaintContext ctx; - - if (optionV4.state & QStyle::State_Selected) { - ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Active, QPalette::HighlightedText)); - } - - QRect textRect = style->subElementRect (QStyle::SE_ItemViewItemText, &optionV4); - painter->save (); - painter->translate (textRect.topLeft ()); - painter->setClipRect (textRect.translated (-textRect.topLeft ())); - doc.documentLayout()->draw (painter, ctx); - painter->restore (); - } - - QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const - { - const int textWidth = 500; - - QStyleOptionViewItemV4 optionV4 = option; - initStyleOption (&optionV4, index); - - const QListView *view = dynamic_cast (optionV4.widget); - QSize icon_size (0, 0); - if (view) { - icon_size = view->iconSize (); - } - - QTextDocument doc; - doc.setHtml (optionV4.text); - doc.setTextWidth (textWidth); - return QSize (textWidth + icon_size.width () + 6, std::max (icon_size.height () + 12, int (doc.size ().height ()))); - } -}; - -// -------------------------------------------------------------------------------------- - /** * @brief A tiny dialog to select a template and a name for the grain */ @@ -384,17 +204,30 @@ END_PROTECTED void SaltManagerDialog::delete_grain () { +BEGIN_PROTECTED - // @@@ + SaltGrain *g = current_grain (); + if (! g) { + throw tl::Exception (tl::to_string (tr ("No package selected to delete"))); + } + if (QMessageBox::question (this, tr ("Delete Package"), tr ("Are you sure to delete package '%1'?").arg (tl::to_qstring (g->name ())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + mp_salt->remove_grain (*g); + } + +END_PROTECTED } void SaltManagerDialog::install_grain () { +BEGIN_PROTECTED - // @@@ + // @@@ TODO: cache this somewhere - don't recreate this dialog always + SaltGrainInstallationDialog inst_dialog (this, mp_salt); + inst_dialog.exec (); +END_PROTECTED } void @@ -405,6 +238,8 @@ SaltManagerDialog::salt_changed () return; } + // NOTE: the disabling of the event handler prevents us from + // letting the model connect to the salt's signal directly. m_current_changed_enabled = false; model->update (); m_current_changed_enabled = true; diff --git a/src/lay/laySaltModel.cc b/src/lay/laySaltModel.cc new file mode 100644 index 000000000..ded796c86 --- /dev/null +++ b/src/lay/laySaltModel.cc @@ -0,0 +1,207 @@ + +/* + + 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 "laySaltModel.h" +#include "laySalt.h" + +#include +#include +#include +#include +#include + +namespace lay +{ + +// -------------------------------------------------------------------------------------- + +SaltItemDelegate::SaltItemDelegate (QObject *parent) + : QStyledItemDelegate (parent) +{ + // .. nothing yet .. +} + +void +SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 optionV4 = option; + initStyleOption (&optionV4, index); + + QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style (); + + QTextDocument doc; + doc.setHtml (optionV4.text); + + optionV4.text = QString (); + style->drawControl (QStyle::CE_ItemViewItem, &optionV4, painter); + + QAbstractTextDocumentLayout::PaintContext ctx; + + if (optionV4.state & QStyle::State_Selected) { + ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Active, QPalette::HighlightedText)); + } + + QRect textRect = style->subElementRect (QStyle::SE_ItemViewItemText, &optionV4); + painter->save (); + painter->translate (textRect.topLeft ()); + painter->setClipRect (textRect.translated (-textRect.topLeft ())); + doc.documentLayout()->draw (painter, ctx); + painter->restore (); +} + +QSize +SaltItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + const int textWidth = 500; + + QStyleOptionViewItemV4 optionV4 = option; + initStyleOption (&optionV4, index); + + const QListView *view = dynamic_cast (optionV4.widget); + QSize icon_size (0, 0); + if (view) { + icon_size = view->iconSize (); + } + + QTextDocument doc; + doc.setHtml (optionV4.text); + doc.setTextWidth (textWidth); + return QSize (textWidth + icon_size.width () + 6, std::max (icon_size.height () + 12, int (doc.size ().height ()))); +} + +// -------------------------------------------------------------------------------------- + +SaltModel::SaltModel (QObject *parent, lay::Salt *salt) + : QAbstractItemModel (parent), mp_salt (salt) +{ + // .. nothing yet .. +} + +QVariant +SaltModel::data (const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + + const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()]; + + std::string text = ""; + text += "

"; + text += tl::escaped_to_html (g->name ()); + if (!g->version ().empty ()) { + text += " "; + text += tl::escaped_to_html (g->version ()); + } + if (!g->title ().empty ()) { + text += " - "; + text += tl::escaped_to_html (g->title ()); + } + text += "

"; + if (!g->doc ().empty ()) { + text += "

"; + text += tl::escaped_to_html (g->doc ()); + text += "

"; + } + text += ""; + + return tl::to_qstring (text); + + } else if (role == Qt::DecorationRole) { + + int icon_dim = 64; + + const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()]; + if (g->icon ().isNull ()) { + return QIcon (":/salt_icon.png"); + } else { + + QImage img = g->icon (); + if (img.width () == icon_dim && img.height () == icon_dim) { + return QPixmap::fromImage (img); + } else { + + img = img.scaled (QSize (icon_dim, icon_dim), Qt::KeepAspectRatio, Qt::SmoothTransformation); + + QImage final_img (icon_dim, icon_dim, QImage::Format_ARGB32); + final_img.fill (QColor (0, 0, 0, 0)); + QPainter painter (&final_img); + painter.drawImage ((icon_dim - img.width ()) / 2, (icon_dim - img.height ()) / 2, img); + + return QPixmap::fromImage (final_img); + + } + + } + + } else { + return QVariant (); + } +} + +QModelIndex +SaltModel::index (int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid ()) { + return QModelIndex (); + } else { + return createIndex (row, column, mp_salt->begin_flat () [row]); + } +} + +QModelIndex +SaltModel::parent (const QModelIndex & /*index*/) const +{ + return QModelIndex (); +} + +int +SaltModel::columnCount(const QModelIndex & /*parent*/) const +{ + return 1; +} + +int +SaltModel::rowCount (const QModelIndex &parent) const +{ + if (parent.isValid ()) { + return 0; + } else { + return mp_salt->end_flat () - mp_salt->begin_flat (); + } +} + +SaltGrain * +SaltModel::grain_from_index (const QModelIndex &index) const +{ + if (index.isValid ()) { + return static_cast (index.internalPointer ()); + } else { + return 0; + } +} + +void +SaltModel::update () +{ + reset (); +} + +} diff --git a/src/lay/laySaltModel.h b/src/lay/laySaltModel.h new file mode 100644 index 000000000..1ffa5f86e --- /dev/null +++ b/src/lay/laySaltModel.h @@ -0,0 +1,110 @@ + +/* + + 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 + +*/ + +#ifndef HDR_laySaltModel +#define HDR_laySaltModel + +#include "layCommon.h" + +#include +#include +#include +#include + +namespace lay +{ + +class Salt; +class SaltGrain; + +/** + * @brief A model representing the salt grains for a QListView + */ +class SaltModel + : public QAbstractItemModel +{ +Q_OBJECT + +public: + /** + * @brief Constructor + */ + SaltModel (QObject *parent, lay::Salt *salt); + + /** + * @brief Implementation of the QAbstractItemModel interface + */ + QVariant data (const QModelIndex &index, int role) const; + + /** + * @brief Implementation of the QAbstractItemModel interface + */ + QModelIndex index (int row, int column, const QModelIndex &parent) const; + + /** + * @brief Implementation of the QAbstractItemModel interface + */ + QModelIndex parent (const QModelIndex & /*index*/) const; + + /** + * @brief Implementation of the QAbstractItemModel interface + */ + int columnCount(const QModelIndex & /*parent*/) const; + + /** + * @brief Implementation of the QAbstractItemModel interface + */ + int rowCount (const QModelIndex &parent) const; + + /** + * @brief Gets the grain pointer from a model index + */ + SaltGrain *grain_from_index (const QModelIndex &index) const; + + /** + * @brief Updates the model + * Needs to be called when the salt has changed. + */ + void update (); + +public: + lay::Salt *mp_salt; +}; + +// -------------------------------------------------------------------------------------- + +/** + * @brief A delegate displaying the summary of a grain + */ +class SaltItemDelegate + : public QStyledItemDelegate +{ +public: + SaltItemDelegate (QObject *parent); + + void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +} + +#endif diff --git a/src/unit_tests/laySalt.cc b/src/unit_tests/laySalt.cc index 9d3ef0a36..8edff9a14 100644 --- a/src/unit_tests/laySalt.cc +++ b/src/unit_tests/laySalt.cc @@ -240,6 +240,15 @@ TEST (3) EXPECT_EQ (grains_to_string (gg), "[a,b,c[c/u,c/c[c/c/v]]]"); EXPECT_EQ (gg.begin_collections ()->path (), tl::to_string (dir_c.absolutePath ())); + std::string gg_path = tmp_file ("gg.tmp"); + gg.save (gg_path); + + lay::SaltGrains ggg; + ggg.load (gg_path); + EXPECT_EQ (grains_to_string (ggg), "[a,b,c[c/u,c/c[c/c/v]]]"); + // NOTE: The path is not set, so this will fail: + // EXPECT_EQ (gg == ggg, true); + gg.remove_grain (gg.begin_grains (), false); EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");