diff --git a/src/buddies/unit_tests/buddies_main.cc b/src/buddies/unit_tests/buddies_main.cc index a8bbd4e25..1f26d86f8 100644 --- a/src/buddies/unit_tests/buddies_main.cc +++ b/src/buddies/unit_tests/buddies_main.cc @@ -28,9 +28,8 @@ #include "rba.h" #include "gsiDecl.h" -// On Windows, ruby.h is not compatible with windows.h which is included by utHead - at least not if -// windows.h is included before ruby.h ... #include "tlUnitTest.h" +#include "tlFileUtils.h" void run_rubytest (tl::TestBase * /*_this*/, const std::string &fn) { diff --git a/src/layui/layui/rdbMarkerBrowserPage.cc b/src/layui/layui/rdbMarkerBrowserPage.cc index 52bd2b58a..a5d93ce50 100644 --- a/src/layui/layui/rdbMarkerBrowserPage.cc +++ b/src/layui/layui/rdbMarkerBrowserPage.cc @@ -2001,9 +2001,10 @@ MarkerBrowserPage::update_info_text () info += ""; - if (item->image () != 0) { + QImage image = item->image (); + if (! image.isNull ()) { info += "

Snapshot image
(click to enlarge)

"; - info_text->set_image (*item->image ()); + info_text->set_image (image); } } @@ -2802,7 +2803,7 @@ MarkerBrowserPage::remove_snapshot_button_clicked () if (selected_item->column () == 0) { const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { - mp_database->set_item_image (i, 0); + mp_database->set_item_image (i, QImage ()); } } } @@ -2830,7 +2831,7 @@ MarkerBrowserPage::snapshot_button_clicked () const rdb::Item *i = list_model->item (selected_item->row ()); if (i) { - mp_database->set_item_image (i, new QImage (mp_view->get_screenshot ())); + mp_database->set_item_image (i, QImage (mp_view->get_screenshot ())); markers_list->selectionModel ()->setCurrentIndex (*selected_item, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); update_info_text (); @@ -3125,8 +3126,8 @@ MarkerBrowserPage::info_anchor_clicked (const QUrl &link) QModelIndex current = markers_list->selectionModel ()->currentIndex (); const rdb::Item *i = list_model->item (current.row ()); - if (i && i->image () != 0) { - MarkerBrowserSnapshotView *snapshot_view = new MarkerBrowserSnapshotView (this, *i->image ()); + if (i && i->has_image ()) { + MarkerBrowserSnapshotView *snapshot_view = new MarkerBrowserSnapshotView (this, i->image ()); snapshot_view->exec (); delete snapshot_view; #if QT_VERSION < 0x040300 diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 1a410e670..838e0bc19 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -808,16 +808,19 @@ Class decl_RdbItem ("rdb", "RdbItem", "@brief Sets the tags from a string\n" "@param tags A comma-separated list of tags\n" ) + -#if defined(HAVE_QT) - gsi::method ("image_str", &rdb::Item::image_str, + gsi::method ("has_image?", &rdb::Item::has_image, + "@brief Gets a value indicating that the item has an image attached\n" + "See \\image_str how to obtain the image.\n\n" + "This method has been introduced in version 0.28.\n" + ) + + gsi::method ("image_str", &rdb::Item::image_str, "@brief Gets the image associated with this item as a string\n" - "@return A base64-encoded image file (usually in PNG format)\n" + "@return A base64-encoded image file (in PNG format)\n" ) + gsi::method ("image_str=", &rdb::Item::set_image_str, gsi::arg ("image"), "@brief Sets the image from a string\n" "@param image A base64-encoded image file (preferably in PNG format)\n" ) + -#endif /* Not supported yet: gsi::method ("multiplicity", &rdb::Item::multiplicity, "@brief Gets the item's multiplicity\n" diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 1187bbb27..013cefe7f 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -821,15 +821,7 @@ Item &Item::operator= (const Item &d) m_visited = d.m_visited; m_multiplicity = d.m_multiplicity; m_tag_ids = d.m_tag_ids; - -#if defined(HAVE_QT) - if (mp_image.get ()) { - mp_image.reset (0); - } - if (d.mp_image.get ()) { - mp_image.reset (new QImage (*d.mp_image)); - } -#endif + m_image_str = d.m_image_str; } return *this; @@ -952,48 +944,60 @@ Item::set_tag_str (const std::string &tags) #if defined(HAVE_QT) void -Item::set_image (QImage *image) +Item::set_image (const QImage &image) { - mp_image.reset (image); -} + if (image.isNull ()) { + + m_image_str.clear (); -std::string -Item::image_str () const -{ - if (! mp_image.get ()) { - return std::string (); } else { QByteArray img_data; QBuffer img_io_device (&img_data); - mp_image->save (&img_io_device, "PNG"); + image.save (&img_io_device, "PNG"); - return std::string (img_data.toBase64 ().constData ()); + m_image_str = std::string (img_data.toBase64 ().constData ()); } } -void -Item::set_image_str (const std::string &s) +const QImage +Item::image () const { - if (s.empty ()) { - set_image (0); + if (m_image_str.empty ()) { + + return QImage (); + } else { - QByteArray img_data (QByteArray::fromBase64 (QByteArray::fromRawData (s.c_str (), int (s.size ())))); + QByteArray img_data (QByteArray::fromBase64 (QByteArray::fromRawData (m_image_str.c_str (), int (m_image_str.size ())))); - QImage *image = new QImage (); - if (image->loadFromData (img_data)) { - set_image (image); - } else { - delete image; - set_image (0); - } + QImage image; + image.loadFromData (img_data); + return image; } } #endif +bool +Item::has_image () const +{ + return !m_image_str.empty (); +} + +std::string +Item::image_str () const +{ + return m_image_str; +} + +void +Item::set_image_str (const std::string &s) +{ + m_image_str = s; +} + // ------------------------------------------------------------------------------------------ // Database implementation @@ -1324,13 +1328,20 @@ Database::remove_item_tag (const Item *item, id_type tag) #if defined(HAVE_QT) void -Database::set_item_image (const Item *item, QImage *image) +Database::set_item_image (const Item *item, const QImage &image) { set_modified (); const_cast (item)->set_image (image); } #endif +void +Database::set_item_image_str (const Item *item, const std::string &image_str) +{ + set_modified (); + const_cast (item)->set_image_str (image_str); +} + void Database::set_item_multiplicity (const Item *item, size_t n) { diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index f62637142..a8c513ab9 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -941,35 +941,37 @@ public: #if defined(HAVE_QT) /** - * @brief Get the image object attached to this item + * @brief Gets the image object attached to this item * - * @return The image object or 0 if no object is attached + * @return The image object or an empty image if no image is attached */ - const QImage *image () const - { - return mp_image.get (); - } + const QImage image () const; /** - * @brief Set an image for this item - * - * The image object will become owned by the item + * @brief Set the image for this item */ - void set_image (QImage *image); + void set_image (const QImage &image); +#endif /** - * @brief Get an image as a string (base64 coded) + * @brief Gets a value indicating whether the item has an image + */ + bool has_image () const; + + /** + * @brief Gets the image as a string (PNG, base64 coded) + * + * Note: if neither PNG support for Qt are compiled in, this string will be empty */ std::string image_str () const; /** - * @brief Get an image from a string (base64 coded) + * @brief Gets the image from a string (PNG, base64 coded) * * If the image string is empty, the image will be cleared. * If the image string is not valid, the image will also be cleared. */ void set_image_str (const std::string &s); -#endif /** * @brief Get the database reference @@ -998,9 +1000,7 @@ private: bool m_visited; std::vector m_tag_ids; Database *mp_database; -#if defined(HAVE_QT) - std::unique_ptr mp_image; -#endif + std::string m_image_str; Item (); @@ -2226,9 +2226,14 @@ public: /** * @brief Set the image of an item */ - void set_item_image (const Item *item, QImage *image); + void set_item_image (const Item *item, const QImage &image); #endif + /** + * @brief Set the image string of an item + */ + void set_item_image_str (const Item *item, const std::string &image_str); + /** * @brief Set the multiplicity of an item */ diff --git a/src/rdb/rdb/rdbFile.cc b/src/rdb/rdb/rdbFile.cc index 82ad16dc8..8006be4d5 100644 --- a/src/rdb/rdb/rdbFile.cc +++ b/src/rdb/rdb/rdbFile.cc @@ -106,9 +106,7 @@ make_rdb_structure (rdb::Database *rdb) tl::make_member (&rdb::Item::cell_qname, &rdb::Item::set_cell_qname, "cell") + tl::make_member (&rdb::Item::visited, &rdb::Item::set_visited, "visited") + tl::make_member (&rdb::Item::multiplicity, &rdb::Item::set_multiplicity, "multiplicity") + -#if defined(HAVE_QT) tl::make_member (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") + -#endif tl::make_element (&rdb::Item::values, &rdb::Item::set_values, "values", tl::make_member (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb)) ) diff --git a/src/tl/tl/tlResources.cc b/src/tl/tl/tlResources.cc index c37cb4e5d..f158eda62 100644 --- a/src/tl/tl/tlResources.cc +++ b/src/tl/tl/tlResources.cc @@ -146,7 +146,7 @@ tl::InputStream *get_resource (const char *name) } else { auto stream = new tl::InputStream (rr.first); if (rr.second) { - stream->inflate (); + stream->inflate_always (); } return stream; } diff --git a/src/tl/tl/tlResources.h b/src/tl/tl/tlResources.h index 699e2089b..2c88e8644 100644 --- a/src/tl/tl/tlResources.h +++ b/src/tl/tl/tlResources.h @@ -81,8 +81,8 @@ TL_PUBLIC std::pair get_resource_reader (const char /** * @brief Get resource names matching a glob pattern * - * For example, find_resources("/group/*") will find resources below "group". - * "*" also matches "/", so resources from subgroups will be listed too. + * For example, find_resources("/group*") will find resources whose name start with "group". + * "*" also matches "/"! */ TL_PUBLIC std::vector find_resources (const std::string &pattern); diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 92077af57..80fa4ef1a 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -143,8 +143,39 @@ public: // --------------------------------------------------------------- // InputStream implementation +namespace { + +/** + * @brief A dummy delegate to provide for the case of raw data stashed inside the stream itself + */ +class RawDataDelegate + : public InputStreamBase +{ +public: + RawDataDelegate (const std::string &source) + : m_source (source) + { } + + virtual size_t read (char *, size_t) + { + return 0; + } + + virtual void reset () { } + virtual void close () { } + + virtual std::string source () const { return m_source; } + virtual std::string absolute_path () const { return m_source; } + virtual std::string filename () const { return m_source; } + +public: + std::string m_source; +}; + +} + InputStream::InputStream (InputStreamBase &delegate) - : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0) + : m_pos (0), mp_bptr (0), mp_delegate (&delegate), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -152,7 +183,7 @@ InputStream::InputStream (InputStreamBase &delegate) } InputStream::InputStream (InputStreamBase *delegate) - : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0) + : m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0), m_inflate_always (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -160,7 +191,7 @@ InputStream::InputStream (InputStreamBase *delegate) } InputStream::InputStream (const std::string &abstract_path) - : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0) + : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false) { m_bcap = 4096; // initial buffer capacity m_blen = 0; @@ -175,28 +206,30 @@ InputStream::InputStream (const std::string &abstract_path) #if defined(HAVE_QT) QResource res (tl::to_qstring (abstract_path)); - if (res.size () > 0) { - - QByteArray data; -#if QT_VERSION >= 0x60000 - if (res.compressionAlgorithm () == QResource::ZlibCompression) { -#else - if (res.isCompressed ()) { -#endif - data = qUncompress ((const unsigned char *)res.data (), (int)res.size ()); - } else { - data = QByteArray ((const char *)res.data (), (int)res.size ()); - } - - mp_buffer = new char[data.size ()]; - memcpy (mp_buffer, data.constData (), data.size ()); - - mp_bptr = mp_buffer; - m_bcap = data.size (); - m_blen = m_bcap; - + if (res.size () == 0) { + throw tl::Exception (tl::to_string (tr ("Resource not found: ")) + abstract_path); } + QByteArray data; +#if QT_VERSION >= 0x60000 + if (res.compressionAlgorithm () == QResource::ZlibCompression) { +#else + if (res.isCompressed ()) { +#endif + data = qUncompress ((const unsigned char *)res.data (), (int)res.size ()); + } else { + data = QByteArray ((const char *)res.data (), (int)res.size ()); + } + + mp_buffer = new char[data.size ()]; + memcpy (mp_buffer, data.constData (), data.size ()); + + mp_bptr = mp_buffer; + m_bcap = data.size (); + m_blen = m_bcap; + + mp_delegate = new RawDataDelegate (abstract_path); + #else std::pair rr = tl::get_resource_reader (ex.get ()); @@ -240,7 +273,7 @@ InputStream::InputStream (const std::string &abstract_path) m_owns_delegate = true; if (needs_inflate) { - inflate (); + inflate_always (); } } @@ -441,6 +474,13 @@ InputStream::inflate () mp_inflate = new tl::InflateFilter (*this); } +void +InputStream::inflate_always () +{ + m_inflate_always = true; + reset (); +} + void InputStream::close () { @@ -468,6 +508,8 @@ InputStream::reset () } else { + tl_assert (mp_delegate != 0); + mp_delegate->reset (); m_pos = 0; @@ -481,6 +523,10 @@ InputStream::reset () mp_buffer = new char [m_bcap]; } + + if (m_inflate_always) { + inflate (); + } } // --------------------------------------------------------------- diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 0747a4b7e..9cd53a983 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -466,6 +466,15 @@ public: */ void inflate (); + /** + * @brief Enables "inflate" right from the beginning + * + * Contrary to "inflate" (which is temporary), this version enables + * decompression right from the beginning of the file. It does a "reset" + * implicitly. + */ + void inflate_always (); + /** * @brief Obtain the current file position */ @@ -557,6 +566,7 @@ private: // inflate support InflateFilter *mp_inflate; + bool m_inflate_always; // No copying currently InputStream (const InputStream &); diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index 4bc19ed7f..0f66e0952 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -607,6 +607,14 @@ main_cont (int &argc, char **argv) tl::set_continue_flag (continue_flag); tl::set_debug_mode (debug_mode); + // set some global variables + if (rba::RubyInterpreter::instance ()) { + rba::RubyInterpreter::instance ()->define_variable ("ut_inst_path", tl::get_inst_path ()); + } + if (pya::PythonInterpreter::instance ()) { + pya::PythonInterpreter::instance ()->define_variable ("ut_inst_path", tl::get_inst_path ()); + } + FILE *output_file = 0; try { diff --git a/testdata/buddies/buddies.rb b/testdata/buddies/buddies.rb index 7658e6007..0864169cd 100644 --- a/testdata/buddies/buddies.rb +++ b/testdata/buddies/buddies.rb @@ -32,7 +32,7 @@ load("test_prologue.rb") class Buddies_TestClass < TestBase def buddy_bin(name) - file = File.join(RBA::Application::instance.inst_path, name) + file = File.join($ut_inst_path, name) return file end diff --git a/testdata/ruby/dbGlyphs.rb b/testdata/ruby/dbGlyphs.rb index c7b5792c8..75bb1e3f2 100644 --- a/testdata/ruby/dbGlyphs.rb +++ b/testdata/ruby/dbGlyphs.rb @@ -69,7 +69,7 @@ class DBGlyph_TestClass < TestBase tg.load_from_resource(":/fonts/does_not_exist.gds") assert_equal(false, true) rescue => ex - assert_equal(ex.to_s, "Unable to load font resource from :/fonts/does_not_exist.gds in TextGenerator::load_from_resource") + assert_equal(ex.to_s, "Resource not found: :/fonts/does_not_exist.gds in TextGenerator::load_from_resource") end end