diff --git a/BasePlugin/MetadataInterface.hh b/BasePlugin/MetadataInterface.hh
new file mode 100644
index 0000000000000000000000000000000000000000..962e58303d95f01d8604f345e3d0ee2e42f47b14
--- /dev/null
+++ b/BasePlugin/MetadataInterface.hh
@@ -0,0 +1,135 @@
+/*===========================================================================*\
+* *
+* OpenFlipper *
+* Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen *
+* www.openflipper.org *
+* *
+*--------------------------------------------------------------------------- *
+* This file is part of OpenFlipper. *
+* *
+* OpenFlipper is free software: you can redistribute it and/or modify *
+* it under the terms of the GNU Lesser General Public License as *
+* published by the Free Software Foundation, either version 3 of *
+* the License, or (at your option) any later version with the *
+* following exceptions: *
+* *
+* If other files instantiate templates or use macros *
+* or inline functions from this file, or you compile this file and *
+* link it with other files to produce an executable, this file does *
+* not by itself cause the resulting executable to be covered by the *
+* GNU Lesser General Public License. This exception does not however *
+* invalidate any other reasons why the executable file might be *
+* covered by the GNU Lesser General Public License. *
+* *
+* OpenFlipper 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 Lesser General Public License for more details. *
+* *
+* You should have received a copy of the GNU LesserGeneral Public *
+* License along with OpenFlipper. If not, *
+* see . *
+* *
+\*===========================================================================*/
+
+/*===========================================================================*\
+* *
+* $Revision: $ *
+* $LastChangedBy: $ *
+* $Date: $ *
+* *
+\*===========================================================================*/
+
+#ifndef METADATAINTERFACE_HH
+#define METADATAINTERFACE_HH
+
+#include
+
+#include
+#include
+
+#if QT_VERSION >= 0x050000
+#include
+#endif
+
+
+
+/** \file MetadataInterface.hh
+ *
+ * Interface implementing slots related to the deserialization of meta data
+ * (usually encountered in screenshots).
+ */
+
+/** \brief Enables implementers to react on deserialization of meta data.
+ *
+ * Using functions such as BaseObject::getCommentByKey() plugins can attach
+ * meta data to objects. This meta data is currently only serialized when
+ * taking viewer snapshots. "Plugin-DeserializeScreenshotMetadata" allows
+ * deserializing this metadata from a viewer snapshot. (In the future
+ * serialization and deserialization of meta data may occur in other contexts
+ * as well.)
+ *
+ * Whenever meta gets deserialized the slots in this interface will get
+ * triggered. Please refer to the documentation of the individual slots
+ * to find out which specific signal suits your needs.
+ *
+ * The slots are always triggered in the order
+ * slotGenericMetadataDeserialized(), slotObjectMetadataDeserialized(),
+ * slotObjectMetadataDeserializedJson().
+ */
+class MetadataInterface {
+ private slots:
+
+ /**
+ * This low-level signal is very inconvenient to use and chances are you
+ * want to use one of the other signals.
+ *
+ * Triggered for every top level meta data entry. Currently the only top
+ * level meta data entries created by OpenFlipper are "Mesh Comments",
+ * "Mesh Materials" and "View". "Mesh Comments" contains the raw
+ * concatenation of all object meta data and is very cumbersome to
+ * parse. In most cases it's a better idea to use
+ * objectMetadataDeserialized().
+ */
+ virtual void slotGenericMetadataDeserialized(
+ QString key, QString value) {};
+
+ /**
+ * Triggered for every piece of object specific meta data encapsulated
+ * in "Mesh Comments".
+ */
+ virtual void slotObjectMetadataDeserialized(
+ QString object_name, QString value) {};
+
+#if QT_VERSION >= 0x050000
+ /**
+ * Triggered for every piece of object specific meta data encapsulated
+ * in "Mesh Comments" if it is valid JSON.
+ */
+ virtual void slotObjectMetadataDeserializedJson(
+ QString object_name, QJsonDocument value) {};
+#endif
+
+ signals:
+ /**
+ * Can be called by anyone who deserializes meta data, e.g. from a
+ * viewer snapshot PNG file. The appropriate slots within
+ * this interface will be triggered.
+ */
+ virtual void metadataDeserialized(
+ const QVector > &data) = 0;
+
+ public:
+
+ /// Destructor
+ virtual ~MetadataInterface() {};
+
+};
+
+#if QT_VERSION >= 0x050000
+Q_DECLARE_INTERFACE(MetadataInterface,"OpenFlipper.MetadataInterface_qt5/1.0")
+#else
+Q_DECLARE_INTERFACE(MetadataInterface,"OpenFlipper.MetadataInterface_qt4/1.0")
+#endif
+
+#endif // METADATAINTERFACE_HH
diff --git a/BasePlugin/PluginFunctions.hh b/BasePlugin/PluginFunctions.hh
index 5516c26b7a5b2f305fb63da1f248f05c8f6a613a..1bcc06365331b1ac171bb400d7feb2516e4d8286 100644
--- a/BasePlugin/PluginFunctions.hh
+++ b/BasePlugin/PluginFunctions.hh
@@ -670,6 +670,6 @@ QString getSaveFileName(const QString &configProperty,
QString * selectedFilter = 0, QFileDialog::Options options = 0,
const QString & defaultSuffix = QString() );
-}
+} /* namespace PluginFunctions */
#endif //PLUGINFUNCTIONS_HH
diff --git a/Core/Core.hh b/Core/Core.hh
index d90f882e7b0d56b8e2b41c4be6ee705b2dc3d273..4a3acf170c76a16781b39e18712da06624bdd306 100644
--- a/Core/Core.hh
+++ b/Core/Core.hh
@@ -1251,6 +1251,23 @@ private slots:
void showReducedMenuBar(bool reduced);
+ //===========================================================================
+ /** @name Metadata support
+ * @{ */
+ //===========================================================================
+ signals:
+ void genericMetadataDeserialized(QString key, QString value);
+ void objectMetadataDeserialized(QString object_name, QString value);
+#if QT_VERSION >= 0x050000
+ void objectMetadataDeserializedJson(
+ QString object_name, QJsonDocument value);
+#endif
+
+ private slots:
+ void slotMetadataDeserialized(
+ const QVector > &data);
+
+
private :
/// Core scripting engine
QScriptEngine scriptEngine_;
diff --git a/Core/PluginCommunication.cc b/Core/PluginCommunication.cc
index ae078e291a230a1f28914e87a1e0de948ff8c2e3..fa3ac6b758d00e15caa3b4fb9393d7f9ee6df2af 100644
--- a/Core/PluginCommunication.cc
+++ b/Core/PluginCommunication.cc
@@ -64,6 +64,8 @@
#include
+#include
+
//== IMPLEMENTATION ==========================================================
//========================================================================================
@@ -446,6 +448,71 @@ void Core::slotGetCurrentRenderer(unsigned int _viewer, QString& _rendererName)
_rendererName = renderManager().active(_viewer)->name;
}
+void Core::slotMetadataDeserialized(
+ const QVector > &data) {
+
+ QString obj_metadata;
+ for (QVector >::const_iterator
+ it = data.begin(); it != data.end(); ++it) {
+ if (it->first == "Mesh Comments")
+ obj_metadata = it->second;
+
+ emit genericMetadataDeserialized(it->first, it->second);
+ }
+ /*
+ * Now handle object metadata.
+ */
+ QRegExp re_begin("BEGIN Comments for object \"([^\\n]*)\"");
+ QRegExp re_end("\\nEND Comments for object \"([^\\n]*)\"");
+ enum STATE {
+ STATE_SEARCH_BEGIN,
+ STATE_CONSUME_INNER,
+ STATE_EOS,
+ };
+
+ int cursor = 0;
+ QString current_object_name;
+ for (STATE state = STATE_SEARCH_BEGIN; state != STATE_EOS; ) {
+ switch (state) {
+ case STATE_SEARCH_BEGIN:
+ cursor = re_begin.indexIn(obj_metadata, cursor);
+ if (cursor == -1) {
+ state = STATE_EOS;
+ break;
+ }
+ current_object_name = re_begin.cap(1);
+ cursor += re_begin.matchedLength();
+ state = STATE_CONSUME_INNER;
+ break;
+ case STATE_CONSUME_INNER:
+ {
+ int next = re_end.indexIn(obj_metadata, cursor);
+ if (next == -1) {
+ state = STATE_EOS;
+ break;
+ }
+
+ const QStringRef value = obj_metadata.midRef(cursor, next - cursor);
+
+ emit objectMetadataDeserialized(current_object_name, value.toString());
+#if QT_VERSION >= 0x050000
+ QJsonParseError json_error;
+ QJsonDocument json_doc =
+ QJsonDocument::fromJson(value.toUtf8(), &json_error);
+ if (json_error.error == QJsonParseError::NoError) {
+ emit objectMetadataDeserializedJson(
+ current_object_name, json_doc);
+ }
+#endif
+ cursor = next + re_end.matchedLength();
+ state = STATE_SEARCH_BEGIN;
+ break;
+ }
+ default:
+ throw std::logic_error("metadataDeserialized(): Invalid state.");
+ }
+ }
+}
//=============================================================================
diff --git a/Core/PluginLoader.cc b/Core/PluginLoader.cc
index 185fa7a25e3f1255465978a03b0fe966b7c12358..ecb1c67875e22d951924606c3cf37ea619b14e0c 100644
--- a/Core/PluginLoader.cc
+++ b/Core/PluginLoader.cc
@@ -94,6 +94,7 @@
#include "OpenFlipper/BasePlugin/SelectionInterface.hh"
#include "OpenFlipper/BasePlugin/TypeInterface.hh"
#include "OpenFlipper/BasePlugin/PluginConnectionInterface.hh"
+#include "OpenFlipper/BasePlugin/MetadataInterface.hh"
#include "OpenFlipper/common/RendererInfo.hh"
@@ -2254,6 +2255,28 @@ void Core::loadPlugin(const QString& _filename,const bool _silent, QString& _lic
this , SLOT( slotEmptyObjectAdded ( int ) ),Qt::DirectConnection);
}
+ MetadataInterface* metadataPlugin = qobject_cast< MetadataInterface * >(plugin);
+ if ( metadataPlugin ) {
+ if (checkSlot(plugin, "slotGenericMetadataDeserialized(QString,QString)")) {
+ connect(this, SIGNAL(genericMetadataDeserialized(QString, QString)),
+ plugin, SLOT(slotGenericMetadataDeserialized(QString, QString)));
+ }
+ if (checkSlot(plugin, "slotObjectMetadataDeserialized(QString,QString)")) {
+ connect(this, SIGNAL(objectMetadataDeserialized(QString, QString)),
+ plugin, SLOT(slotObjectMetadataDeserialized(QString, QString)));
+ }
+#if QT_VERSION >= 0x050000
+ if (checkSlot(plugin, "slotObjectMetadataDeserializedJson(QString,QJsonDocument)")) {
+ connect(this, SIGNAL(objectMetadataDeserializedJson(QString, QJsonDocument)),
+ plugin, SLOT(slotObjectMetadataDeserializedJson(QString, QJsonDocument)));
+ }
+#endif
+ if (checkSignal(plugin, "metadataDeserialized(QVector >)")) {
+ connect(plugin, SIGNAL(metadataDeserialized(QVector >)),
+ this, SLOT(slotMetadataDeserialized(QVector >)));
+ }
+ }
+
emit log(LOGOUT,"================================================================================");
}
diff --git a/common/BaseObject.hh b/common/BaseObject.hh
index 6814f19dba792e5d0cdcc0f9117967e1cae54aef..493b80bd9ed79fe476232009a44953ca1bbc86f5 100644
--- a/common/BaseObject.hh
+++ b/common/BaseObject.hh
@@ -70,6 +70,10 @@
#include
#include
#include "perObjectData.hh"
+#if QT_VERSION >= 0x050000
+#include
+#include
+#endif
//== TYPEDEFS =================================================================
@@ -575,10 +579,31 @@ class DLLEXPORTONLY BaseObject : public QObject {
result.append(QString("BEGIN Comments for object \"%1\"").arg(name()));
+ /*
+ * Compose JSON parsable object.
+ */
+#if QT_VERSION >= 0x050000
+ QJsonObject comment_obj;
+ for (QMap::const_iterator it = commentsByKey_.begin(), it_end = commentsByKey_.end();
+ it != it_end; ++it) {
+
+ QJsonParseError json_error;
+ QString test_json_str = QString::fromUtf8("{\"test\": %1}").arg(it.value());
+ QByteArray test_json_ba = test_json_str.toUtf8();
+ QJsonDocument test_json = QJsonDocument::fromJson(test_json_ba, &json_error);
+ if (json_error.error != QJsonParseError::NoError) {
+ comment_obj[it.key()] = it.value();
+ } else {
+ comment_obj[it.key()] = test_json.object().value("test");
+ }
+ }
+ result.append(QString::fromUtf8(QJsonDocument(comment_obj).toJson(QJsonDocument::Indented)));
+#else
for (QMap::const_iterator it = commentsByKey_.begin(), it_end = commentsByKey_.end();
it != it_end; ++it) {
result.append(QString("%1: %2").arg(it.key(), it.value()));
}
+#endif
result.append(QString("END Comments for object \"%1\"\n").arg(name()));