diff --git a/src/lay/SaltManagerDialog.ui b/src/lay/SaltManagerDialog.ui index e369924c9..d16b5af31 100644 --- a/src/lay/SaltManagerDialog.ui +++ b/src/lay/SaltManagerDialog.ui @@ -167,7 +167,7 @@ - true + false diff --git a/src/lay/laySaltGrain.cc b/src/lay/laySaltGrain.cc index 80292e6f1..266dbeff7 100644 --- a/src/lay/laySaltGrain.cc +++ b/src/lay/laySaltGrain.cc @@ -26,6 +26,7 @@ #include #include +#include namespace lay { @@ -46,6 +47,9 @@ SaltGrain::operator== (const SaltGrain &other) const m_url == other.m_url && m_title == other.m_title && m_doc == other.m_doc && + m_doc_url == other.m_doc_url && + m_icon == other.m_icon && + m_screenshot == other.m_screenshot && m_dependencies == other.m_dependencies && m_author == other.m_author && m_author_contact == other.m_author_contact && @@ -90,6 +94,12 @@ SaltGrain::set_doc (const std::string &t) m_doc = t; } +void +SaltGrain::set_doc_url (const std::string &u) +{ + m_doc_url = u; +} + void SaltGrain::set_author (const std::string &a) { @@ -120,6 +130,18 @@ SaltGrain::set_installed_time (const QDateTime &t) m_installed_time = t; } +void +SaltGrain::set_screenshot (const QImage &i) +{ + m_screenshot = i; +} + +void +SaltGrain::set_icon (const QImage &i) +{ + m_icon = i; +} + int SaltGrain::compare_versions (const std::string &v1, const std::string &v2) { @@ -184,17 +206,45 @@ struct TimeConverter } }; +struct ImageConverter +{ + std::string to_string (const QImage &image) const + { + if (image.isNull ()) { + return std::string (); + } else { + QBuffer buffer; + buffer.open (QIODevice::WriteOnly); + image.save (&buffer, "PNG"); + buffer.close (); + return buffer.buffer ().toBase64 ().constData (); + } + } + + void from_string (const std::string &image, QImage &res) const + { + if (image.empty ()) { + res = QImage (); + } else { + res = QImage::fromData (QByteArray::fromBase64 (QByteArray (image.c_str (), image.size ()))); + } + } +}; + static tl::XMLStruct xml_struct ("salt-grain", 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") + tl::make_member (&SaltGrain::doc, &SaltGrain::set_doc, "doc") + + tl::make_member (&SaltGrain::doc_url, &SaltGrain::set_doc_url, "doc-url") + tl::make_member (&SaltGrain::url, &SaltGrain::set_url, "url") + tl::make_member (&SaltGrain::license, &SaltGrain::set_license, "license") + tl::make_member (&SaltGrain::author, &SaltGrain::set_author, "author") + tl::make_member (&SaltGrain::author_contact, &SaltGrain::set_author_contact, "author-contact") + tl::make_member (&SaltGrain::authored_time, &SaltGrain::set_authored_time, "authored-time", TimeConverter ()) + tl::make_member (&SaltGrain::installed_time, &SaltGrain::set_installed_time, "installed-time", TimeConverter ()) + + tl::make_member (&SaltGrain::icon, &SaltGrain::set_icon, "icon", ImageConverter ()) + + tl::make_member (&SaltGrain::screenshot, &SaltGrain::set_screenshot, "screenshot", ImageConverter ()) + tl::make_element (&SaltGrain::begin_dependencies, &SaltGrain::end_dependencies, &SaltGrain::add_dependency, "depends", tl::make_member (&SaltGrain::Dependency::name, "name") + tl::make_member (&SaltGrain::Dependency::url, "url") + diff --git a/src/lay/laySaltGrain.h b/src/lay/laySaltGrain.h index 063bade20..eda1d63bd 100644 --- a/src/lay/laySaltGrain.h +++ b/src/lay/laySaltGrain.h @@ -27,6 +27,7 @@ #include "tlObject.h" #include +#include namespace lay { @@ -115,8 +116,7 @@ public: /** * @brief Gets the documentation text of the grain * - * The documentation text is an XML document using - * KLayout's doc format. + * The documentation text is a brief description. */ const std::string &doc () const { @@ -128,6 +128,21 @@ public: */ void set_doc (const std::string &t); + /** + * @brief Gets the documentation URL of the grain + * + * The documentation URL provides a detailed documentation. + */ + const std::string &doc_url () const + { + return m_doc_url; + } + + /** + * @brief Sets the documentation URL of the grain + */ + void set_doc_url (const std::string &u); + /** * @brief Gets the version of the grain * @@ -209,6 +224,35 @@ public: */ void set_installed_time (const QDateTime &t); + /** + * @brief Gets the icon image for the grain. + * The preferred image size is 64x64 pixels. + * The image may be null image. In this case, a default image is used. + */ + const QImage &icon () const + { + return m_icon; + } + + /** + * @brief Sets icon image + */ + void set_icon (const QImage &i); + + /** + * @brief Gets a screenshot image for documentation. + * The image may be null image. In this case, no screenshot is shown. + */ + const QImage &screenshot () const + { + return m_screenshot; + } + + /** + * @brief Sets screenshot image + */ + void set_screenshot (const QImage &i); + /** * @brief Gets the absolute file path of the installed grain * This is the file path to the grain folder. @@ -327,11 +371,12 @@ private: std::string m_path; std::string m_url; std::string m_title; - std::string m_doc; + std::string m_doc, m_doc_url; std::string m_author; std::string m_author_contact; std::string m_license; QDateTime m_authored_time, m_installed_time; + QImage m_icon, m_screenshot; std::vector m_dependencies; }; diff --git a/src/lay/laySaltGrainDetailsTextWidget.cc b/src/lay/laySaltGrainDetailsTextWidget.cc index ab44d6d22..730a6daeb 100644 --- a/src/lay/laySaltGrainDetailsTextWidget.cc +++ b/src/lay/laySaltGrainDetailsTextWidget.cc @@ -27,6 +27,7 @@ #include #include #include +#include namespace lay { @@ -49,9 +50,54 @@ QVariant SaltGrainDetailsTextWidget::loadResource (int type, const QUrl &url) { if (url.path () == QString::fromUtf8 ("/icon")) { - // @@@ - return QImage (":/salt_icon.png"); - // @@@ + + if (!mp_grain || mp_grain->icon ().isNull ()) { + return QImage (":/salt_icon.png"); + } else { + QImage img = mp_grain->icon (); + if (img.width () != 64) { + return img.scaled (QSize (64, 64), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + } else { + return img; + } + } + + } else if (url.path () == QString::fromUtf8 ("/screenshot")) { + + QImage s = mp_grain->screenshot ().convertToFormat (QImage::Format_ARGB32_Premultiplied); + + QImage smask (s.size (), QImage::Format_ARGB32_Premultiplied); + smask.fill (QColor (0, 0, 0, 0)); + { + int border = 4; + int radius = 6; + + QPainter painter (&smask); + + painter.setRenderHint (QPainter::Antialiasing); + + painter.setCompositionMode (QPainter::CompositionMode_Source); + for (int b = border; b > 0; --b) { + QPen pen (QColor (255, 255, 255, ((border - b + 1) * 255) / border)); + pen.setWidth (b * 2 + 1); + painter.setBrush (Qt::NoBrush); + painter.setPen (pen); + painter.drawRoundedRect (QRectF (border, border, s.width () - 2 * border, s.height () - 2 * border), radius, radius, Qt::AbsoluteSize); + } + + painter.setPen (Qt::white); + painter.setBrush (Qt::white); + painter.drawRoundedRect (QRectF (border, border, s.width () - 2 * border, s.height () - 2 * border), radius, radius, Qt::AbsoluteSize); + } + + { + QPainter painter (&s); + painter.setCompositionMode (QPainter::CompositionMode_DestinationIn); + painter.drawImage (0, 0, smask); + } + + return s; + } else { return QTextBrowser::loadResource (type, url); } @@ -72,8 +118,9 @@ SaltGrainDetailsTextWidget::details_text () stream << ""; - stream << ""; - stream << ""; + stream << "
"; + stream << ""; + stream << "
"; stream << "

"; stream << tl::to_qstring (tl::escaped_to_html (g->name ())) << " " << tl::to_qstring (tl::escaped_to_html (g->version ())); @@ -96,8 +143,6 @@ SaltGrainDetailsTextWidget::details_text () stream << "

"; } - stream << "

"; - stream << "


"; if (! g->doc ().empty ()) { stream << tl::to_qstring (tl::escaped_to_html (g->doc ())); @@ -128,16 +173,52 @@ SaltGrainDetailsTextWidget::details_text () stream << "

"; stream << "

"; - if (! g->url ().empty ()) { - stream << "" << QObject::tr ("Documentation link") << ": url ()) << "\">" << tl::to_qstring (tl::escaped_to_html (g->url ())) << ""; + if (! g->license ().empty ()) { + stream << "" << QObject::tr ("License") << ": " << tl::to_qstring (tl::escaped_to_html (g->license ())) << " "; } else { stream << ""; - stream << QObject::tr ("This package does not have a documentation link. " - "Use the <url> element of the specification file or edit the package properties to provide a link."); + stream << QObject::tr ("This package does not have license information. " + "Use the <license> elements of the specification file or edit the package properties to provide license information."); stream << ""; } stream << "

"; + stream << "

"; + if (! g->doc_url ().empty ()) { + stream << "" << QObject::tr ("Documentation link") << ": doc_url ()) << "\">" << tl::to_qstring (tl::escaped_to_html (g->doc_url ())) << ""; + } else { + stream << ""; + stream << QObject::tr ("This package does not have a documentation link. " + "Use the <doc-url> element of the specification file or edit the package properties to provide a link."); + stream << ""; + } + stream << "

"; + + if (! g->screenshot ().isNull ()) { + stream << "
"; + stream << "

" << QObject::tr ("Screenshot") << "

"; + } + + stream << "
"; + stream << "

" << QObject::tr ("Installation") << "

"; + + stream << "

" << QObject::tr ("Installation path: ") << "" << tl::to_qstring (tl::escaped_to_html (g->path ())) << "

"; + if (! g->url ().empty ()) { + stream << "

" << QObject::tr ("Download URL: ") << "" << tl::to_qstring (tl::escaped_to_html (g->url ())) << "

"; + } + if (! g->installed_time ().isNull ()) { + stream << "

" << QObject::tr ("Installed: ") << "" << g->installed_time ().toString () << "

"; + } + if (! g->dependencies ().empty ()) { + stream << "

" << QObject::tr ("Depends on: ") << "

"; + for (std::vector::const_iterator d = g->dependencies ().begin (); d != g->dependencies ().end (); ++d) { + stream << "    " << tl::to_qstring (tl::escaped_to_html (d->name)) << " " << tl::to_qstring (tl::escaped_to_html (d->url)) << "
"; + } + stream << "

"; + } + + stream << ""; + stream << ""; stream.flush (); diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index 9606164f3..8da3b9934 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -80,9 +80,17 @@ public: } else if (role == Qt::DecorationRole) { - // @@@ - return QIcon (":/salt_icon.png"); - // @@@ + const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()]; + if (g->icon ().isNull ()) { + return QIcon (":/salt_icon.png"); + } else { + QPixmap px = QPixmap::fromImage (g->icon ()); + if (px.width () == 64) { + return px; + } else { + return px.scaled (QSize (64, 64), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + } + } } else { return QVariant (); diff --git a/src/unit_tests/laySalt.cc b/src/unit_tests/laySalt.cc index 75d7986ca..ae5581e72 100644 --- a/src/unit_tests/laySalt.cc +++ b/src/unit_tests/laySalt.cc @@ -99,6 +99,8 @@ TEST (1) EXPECT_EQ (g.title (), "title"); g.set_doc ("doc"); EXPECT_EQ (g.doc (), "doc"); + g.set_doc_url ("doc-url"); + EXPECT_EQ (g.doc_url (), "doc-url"); g.set_author ("me"); EXPECT_EQ (g.author (), "me"); g.set_author_contact ("ac");