Xcos MVC: implement state / dstate / blocktype
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / ports_management.hxx
index 492be05..8a549a4 100644 (file)
 
 #include <string>
 #include <vector>
+#include <algorithm>
 
 #include "internal.hxx"
+#include "double.hxx"
+#include "string.hxx"
 
 #include "Controller.hxx"
-#include "GraphicsAdapter.hxx"
 
 namespace org_scilab_modules_scicos
 {
@@ -30,21 +32,432 @@ namespace view_scilab
 /*
  * Return a Scilab encoded value for a property.
  */
-types::InternalType* get_ports_property(const GraphicsAdapter& adaptor, object_properties_t port_kind, const Controller& controller, object_properties_t p);
+template<typename Adaptor, object_properties_t p>
+types::InternalType* get_ports_property(const Adaptor& adaptor, object_properties_t port_kind, const Controller& controller)
+{
+    model::Block* adaptee = adaptor.getAdaptee();
+
+    // Retrieve the identifiers
+    std::vector<ScicosID> ids;
+    controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
+
+    // Translate identifiers: shared variables
+    int i = 0;
+    size_t datatypeIndex = 0;
+    // Translate identifiers to return values
+    switch (p)
+    {
+        case STYLE:
+        case LABEL:
+        {
+            types::String* o = new types::String(ids.size(), 1);
+            for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+            {
+                std::string s;
+                controller.getObjectProperty(*it, PORT, p, s);
+                o->set(i, s.data());
+            }
+            return o;
+        }
+        case DATATYPE_ROWS:
+            datatypeIndex = 0;
+        // no break
+        case DATATYPE_COLS:
+            datatypeIndex = 1;
+        // no break
+        case DATATYPE_TYPE:
+        {
+            datatypeIndex = 2;
+
+            types::Double* o = new types::Double(ids.size(), 1);
+            for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+            {
+                std::vector<int> v;
+                controller.getObjectProperty(*it, PORT, DATATYPE, v);
+                o[i] = v[datatypeIndex];
+            }
+            return o;
+        }
+        case IMPLICIT:
+        {
+            static const wchar_t E[] = L"E";
+            static const wchar_t I[] = L"I";
+            types::String* o = new types::String(ids.size(), 1);
+            for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+            {
+                bool v;
+                controller.getObjectProperty(*it, PORT, p, v);
+                o->set(i, (v == false) ? E : I);
+            }
+            return o;
+        }
+        case CONNECTED_SIGNALS:
+        {
+            double* v;
+            types::Double* o = new types::Double(ids.size(), 1, &v);
+
+            ScicosID diagram;
+            controller.getObjectProperty(adaptee->id(), adaptee->kind(), PARENT_DIAGRAM, diagram);
+
+            std::vector<ScicosID> children;
+            controller.getObjectProperty(diagram, DIAGRAM, CHILDREN, children);
+
+            for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+            {
+                ScicosID id;
+                controller.getObjectProperty(*it, PORT, p, id);
+
+                std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
+
+                if (found != children.end())
+                {
+                    v[i] = std::distance(found, children.begin());
+                }
+                else
+                {
+                    v[i] = 0;
+                }
+            }
+            return o;
+        }
+        default:
+            return 0;
+    }
+}
 
 /*
  * Set a Scilab encoded values as a property.
  *
  * \note this method will return false if one of the ports does not exist
  */
-bool set_ports_property(const GraphicsAdapter& adaptor, object_properties_t port_kind, Controller& controller, object_properties_t p, types::InternalType* v);
+template<typename Adaptor, object_properties_t p>
+bool set_ports_property(const Adaptor& adaptor, object_properties_t port_kind, Controller& controller, types::InternalType* v)
+{
+    model::Block* adaptee = adaptor.getAdaptee();
+
+    // Retrieve the ports identifiers
+    std::vector<ScicosID> ids;
+    controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, ids);
+
+    if (v->getType() == types::InternalType::ScilabString)
+    {
+        types::String* current = v->getAs<types::String>();
+        if (current->getCols() != 0 && current->getCols() != 1)
+        {
+            return false;
+        }
+
+        size_t rows = current->getRows();
+        if (rows != ids.size())
+        {
+            return false;
+        }
+
+        // Translate identifiers: shared variables
+        int i = 0;
+        // Translate identifiers from values
+        switch (p)
+        {
+            case STYLE:
+            case LABEL:
+            {
+                for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+                {
+                    char* c_str = wide_string_to_UTF8(current->get(i));
+                    controller.setObjectProperty(*it, PORT, p, std::string(c_str));
+                    FREE(c_str);
+                }
+                return true;
+            }
+            case IMPLICIT:
+            {
+                static const wchar_t E[] = L"E";
+                static const wchar_t I[] = L"I";
+
+                for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+                {
+                    if (current->get(i) == I)
+                    {
+                        controller.setObjectProperty(*it, PORT, p, true);
+                    }
+                    else if (current->get(i) == E)
+                    {
+                        controller.setObjectProperty(*it, PORT, p, false);
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            default:
+                return false;
+        }
+    }
+    else if (v->getType() == types::InternalType::ScilabDouble)
+    {
+        types::Double* current = v->getAs<types::Double>();
+        if (current->getCols() != 0 && current->getCols() != 1)
+        {
+            return false;
+        }
+
+        size_t rows = current->getRows();
+        if (rows != ids.size())
+        {
+            return false;
+        }
+
+        // Translate identifiers: shared variables
+        int i = 0;
+        size_t datatypeIndex = 0;
+        // Translate identifiers from values
+        switch (p)
+        {
+            case STYLE:
+            case LABEL:
+                // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
+                return true;
+
+            case DATATYPE_ROWS:
+                datatypeIndex = 0;
+            // no break
+            case DATATYPE_COLS:
+                datatypeIndex = 1;
+            // no break
+            case DATATYPE_TYPE:
+            {
+                datatypeIndex = 2;
+
+                for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
+                {
+                    std::vector<int> v;
+                    controller.getObjectProperty(*it, PORT, DATATYPE, v);
+
+                    double data = current->get(i);
+                    if (std::floor(data) != data)
+                    {
+                        return false;
+                    }
+
+                    v[datatypeIndex] = static_cast<int>(data);
+                    controller.setObjectProperty(*it, PORT, DATATYPE, v);
+                }
+                return true;
+            }
+
+            case IMPLICIT:
+                // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
+                return true;
+        }
+
+    }
+    return false;
+}
+
+template<typename Adaptor, object_properties_t p>
+bool fillNewPorts(std::vector<int>& newPorts, const std::vector<ScicosID>& children, double* d)
+{
+    for (std::vector<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
+    {
+
+        if (p == CONNECTED_SIGNALS)   // the associated link must exist
+        {
+            if (0 > *d && *d >= children.size())
+            {
+                return false;
+            }
+        } // no check is performed for other properties as newPorts will contains value not index
+
+        *it = static_cast<int>(*d);
+    }
+    return true;
+}
+
+template<typename Adaptor, object_properties_t p>
+void updateNewPort(ScicosID oldPort, int newPort, Controller& controller,
+                   std::vector<ScicosID>& children, std::vector<ScicosID>& deletedObjects)
+{
+    if (p == CONNECTED_SIGNALS)
+    {
+        // update signal and manage deconnection, using newPort as a children index
+        ScicosID oldSignal;
+        controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, oldSignal);
+        ScicosID newSignal = children[newPort];
+        if (oldSignal != newSignal)
+        {
+            // disconnect the old link
+            ScicosID oldSignalSrc;
+            controller.getObjectProperty(oldSignal, LINK, SOURCE_PORT, oldSignalSrc);
+            ScicosID oldSignalDst;
+            controller.getObjectProperty(oldSignal, LINK, DESTINATION_PORT, oldSignalDst);
+            ScicosID unconnected = 0;
+            if (oldSignalSrc == oldPort)
+            {
+                controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
+            }
+            else   // oldSignalDst == oldPort
+            {
+                controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
+            }
+            // Link de-association is not performed as the link will be removed
+            // connect the new link
+            controller.setObjectProperty(newSignal, LINK, SOURCE_PORT, 0);
+            controller.setObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, newSignal);
+            children.erase(std::find(children.begin(), children.end(), oldSignal));
+            deletedObjects.push_back(oldSignal);
+        }
+    }
+    else
+    {
+        // update the p property, using newPort as a value
+        controller.setObjectProperty(oldPort, PORT, p, newPort);
+    }
+}
+
+template<typename Adaptor, object_properties_t p>
+bool addNewPort(ScicosID newPortID, int newPort, const std::vector<ScicosID>& children,        Controller& controller)
+{
+    bool status = true;
+    if (p == CONNECTED_SIGNALS)
+    {
+        // set the connected signal if applicable, using newPort as a children index
+        if (newPort != 0)
+        {
+            ScicosID signal = children[newPort];
+            status = controller.setObjectProperty(newPortID, PORT, CONNECTED_SIGNALS, signal);
+        }
+    }
+    else
+    {
+        // set the requested property, using newPort as a value
+        status = controller.setObjectProperty(newPortID, PORT, p, newPort);
+    }
+
+    return status;
+}
 
 /**
  * Update the ports with a specific property.
  *
  * Create ports if needed, remove ports if needed and set a default property on each port.
  */
-bool update_ports_with_property(const GraphicsAdapter& adaptor, object_properties_t port_kind, Controller& controller, object_properties_t p, types::InternalType* v);
+template<typename Adaptor, object_properties_t p>
+bool update_ports_property(const Adaptor& adaptor, object_properties_t port_kind,  Controller& controller, types::InternalType* v)
+{
+    model::Block* adaptee = adaptor.getAdaptee();
+
+    if (v->getType() != types::InternalType::ScilabDouble)
+    {
+        return false;
+    }
+    types::Double* value = v->getAs<types::Double>();
+
+    ScicosID parentDiagram;
+    controller.getObjectProperty(adaptee->id(), BLOCK, PARENT_DIAGRAM, parentDiagram);
+
+    std::vector<ScicosID> children;
+    if (parentDiagram != 0)
+    {
+        controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
+    }
+
+    std::vector<int> newPorts = std::vector<int>(value->getSize());
+
+    // retrieve old data
+    std::vector<ScicosID> oldPorts;
+    controller.getObjectProperty(adaptee->id(), adaptee->kind(), port_kind, oldPorts);
+    std::vector<ScicosID> previousPorts = oldPorts;
+
+    double* d = value->getReal();
+    if (!fillNewPorts<Adaptor, p>(newPorts, children, d))
+    {
+        return false;
+    }
+
+    std::vector<ScicosID> deletedObjects;
+
+    // updated ports
+    while (!oldPorts.empty() && !newPorts.empty())
+    {
+        ScicosID oldPort = oldPorts.back();
+        oldPorts.pop_back();
+        int newPort = newPorts.back();
+        newPorts.pop_back();
+
+        updateNewPort<Adaptor, p>(oldPort, newPort, controller, children, deletedObjects);
+    }
+
+    // removed ports
+    if (!oldPorts.empty())
+    {
+        previousPorts.erase(previousPorts.begin() + oldPorts.size(), previousPorts.end());
+
+        while (!oldPorts.empty())
+        {
+            ScicosID oldPort = oldPorts.back();
+            oldPorts.pop_back();
+
+            ScicosID signal;
+            controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, signal);
+            if (signal != 0)
+            {
+                // the link is connected, disconnect the other side
+                ScicosID oldSignalSrc;
+                controller.getObjectProperty(signal, LINK, SOURCE_PORT, oldSignalSrc);
+                ScicosID oldSignalDst;
+                controller.getObjectProperty(signal, LINK, DESTINATION_PORT, oldSignalDst);
+
+                ScicosID unconnected = 0;
+                if (oldSignalSrc == oldPort)
+                {
+                    controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
+                }
+                else     // oldSignalDst == oldPort
+                {
+                    controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
+                }
+
+                children.erase(std::find(children.begin(), children.end(), signal));
+                deletedObjects.push_back(signal);
+            }
+
+            deletedObjects.push_back(oldPort);
+        }
+
+        controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
+    }
+
+    // added ports
+    if (!newPorts.empty())
+    {
+        while (!newPorts.empty())
+        {
+            int newPort = newPorts.back();
+            oldPorts.pop_back();
+
+            ScicosID id = controller.createObject(PORT);
+            controller.setObjectProperty(id, PORT, SOURCE_BLOCK, adaptee->id());
+            addNewPort<Adaptor, p>(id, newPort, children, controller);
+            previousPorts.push_back(id);
+        }
+
+        controller.setObjectProperty(adaptee->id(), BLOCK, port_kind, previousPorts);
+    }
+
+    // remove objects from the model after de-association
+    if (parentDiagram != 0)
+    {
+        controller.setObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
+    }
+    for (std::vector<ScicosID>::iterator it = deletedObjects.begin(); it != deletedObjects.end(); ++it)
+    {
+        controller.deleteObject(*it);
+    }
+
+    return true;
+}
 
 
 } /* view_scilab */