diff --git a/sources/ElementsCollection/elementcollectionitem.cpp b/sources/ElementsCollection/elementcollectionitem.cpp index a8979010f..8a49d25f9 100644 --- a/sources/ElementsCollection/elementcollectionitem.cpp +++ b/sources/ElementsCollection/elementcollectionitem.cpp @@ -81,6 +81,20 @@ ElementCollectionItem *ElementCollectionItem::child(int row) { return m_child_items.value(row); } +/** + * @brief ElementCollectionItem::childWithCollectionName + * Return the child with the collection name @name, else return nullptr + * @param name + * @return + */ +ElementCollectionItem *ElementCollectionItem::childWithCollectionName(QString name) const +{ + foreach (ElementCollectionItem *eci, m_child_items) + if (eci->collectionName() == name) return eci; + + return nullptr; +} + /** * @brief ElementCollectionItem::childCount * @return the number of childs of this item @@ -167,6 +181,14 @@ QString ElementCollectionItem::name() { return m_name; } +/** + * @brief ElementCollectionItem::collectionName + * @return The collection name of this item + */ +QString ElementCollectionItem::collectionName() const { + return QString(); +} + /** * @brief ElementCollectionItem::isDir * @return true if this item represent a directory @@ -205,6 +227,43 @@ QList ElementCollectionItem::items() const return list; } +/** + * @brief ElementCollectionItem::elementsChild + * @return All elements child of this item + */ +QList ElementCollectionItem::elementsChild() const +{ + QList list; + foreach (ElementCollectionItem *eci, m_child_items) + if (eci->isElement()) + list.append(eci); + + return list; +} + +/** + * @brief ElementCollectionItem::directoriesChild + * @return All directories child of this item + */ +QList ElementCollectionItem::directoriesChild() const +{ + QList list; + foreach (ElementCollectionItem *eci, m_child_items) + if (eci->isDir()) + list.append(eci); + + return list; +} + +/** + * @brief ElementCollectionItem::indexOfChild + * @param child + * @return the index of child or -1 if not found + */ +int ElementCollectionItem::indexOfChild(ElementCollectionItem *child) const { + return m_child_items.indexOf(child); +} + /** * @brief ElementCollectionItem::canRemoveContent * @return true if this item can remove the content that he represent diff --git a/sources/ElementsCollection/elementcollectionitem.h b/sources/ElementsCollection/elementcollectionitem.h index 9e276848e..7491cc4dc 100644 --- a/sources/ElementsCollection/elementcollectionitem.h +++ b/sources/ElementsCollection/elementcollectionitem.h @@ -43,6 +43,7 @@ class ElementCollectionItem bool removeChild (int row, int count); bool insertChild (int row, ElementCollectionItem *item); ElementCollectionItem *child(int row); + ElementCollectionItem *childWithCollectionName(QString name) const; int childCount() const; int columnCount() const; virtual QVariant data(int column, int role); @@ -53,11 +54,16 @@ class ElementCollectionItem ElementCollectionItem *parent(); int row() const; virtual QString name(); + virtual QString collectionName() const; virtual bool isDir() const; virtual bool isElement() const; virtual bool isValid() const; virtual QList items() const; + QList elementsChild() const; + QList directoriesChild() const; + int indexOfChild(ElementCollectionItem *child) const; + virtual bool canRemoveContent(); virtual bool removeContent(); diff --git a/sources/ElementsCollection/elementlocation.cpp b/sources/ElementsCollection/elementlocation.cpp index 758f4f719..bcf8191a5 100644 --- a/sources/ElementsCollection/elementlocation.cpp +++ b/sources/ElementsCollection/elementlocation.cpp @@ -227,8 +227,23 @@ bool ElementLocation::isDirectory() const { * @brief ElementLocation::collectionPath * @return the colletion relative to the collection */ -QString ElementLocation::collectionPath() const { - return m_collection_path; +/** + * @brief ElementLocation::collectionPath + * Return the path of the represented element relative to collection + * if @protocol is true the path is prepended by the collection type (common://, custom:// or embed://) + * else if false, only the collection path is returned without the collection type. + * @param protocol + * @return the path + */ +QString ElementLocation::collectionPath(bool protocol) const +{ + if (protocol) + return m_collection_path; + else + { + QString path = m_collection_path; + return path.remove(QRegularExpression("common://|custom://|embed://")); + } } /** diff --git a/sources/ElementsCollection/elementlocation.h b/sources/ElementsCollection/elementlocation.h index fd3ea21ad..0bca646ed 100644 --- a/sources/ElementsCollection/elementlocation.h +++ b/sources/ElementsCollection/elementlocation.h @@ -43,8 +43,9 @@ class ElementLocation void setProject(QETProject *project); bool isElement() const; bool isDirectory() const; + bool isFileSystem() const; - QString collectionPath() const; + QString collectionPath(bool protocol = true) const; QString fileSystemPath() const; QETProject *project() const; diff --git a/sources/ElementsCollection/elementscollectionmodel.cpp b/sources/ElementsCollection/elementscollectionmodel.cpp index 65494b804..85b1fdbd3 100644 --- a/sources/ElementsCollection/elementscollectionmodel.cpp +++ b/sources/ElementsCollection/elementscollectionmodel.cpp @@ -20,6 +20,7 @@ #include "qetapp.h" #include "fileelementcollectionitem.h" #include "xmlprojectelementcollectionitem.h" +#include "qetproject.h" /** * @brief ElementsCollectionModel::ElementsCollectionModel @@ -281,6 +282,8 @@ bool ElementsCollectionModel::addProject(QETProject *project) XmlProjectElementCollectionItem *xpeci = new XmlProjectElementCollectionItem(project, m_root_item); bool r = m_root_item->insertChild(row, xpeci); endInsertRows(); + connect(project, &QETProject::elementIntegratedToCollection, this, &ElementsCollectionModel::elementIntegratedToCollection); + return r; } @@ -293,6 +296,7 @@ bool ElementsCollectionModel::removeProject(QETProject *project) if (removeRows(row, 1, QModelIndex())) { m_project_list.removeOne(project); + disconnect(project, &QETProject::elementIntegratedToCollection, this, &ElementsCollectionModel::elementIntegratedToCollection); return true; } else @@ -306,3 +310,46 @@ bool ElementsCollectionModel::removeProject(QETProject *project) QList ElementsCollectionModel::project() const { return m_project_list; } + +/** + * @brief ElementsCollectionModel::itemForProject + * @param project + * @return the root item of project @project, or nullptr if not found. + */ +XmlProjectElementCollectionItem *ElementsCollectionModel::itemForProject(QETProject *project) +{ + if (!m_project_list.contains(project)) return nullptr; + QModelIndex index_ = index(m_project_list.indexOf(project), 0); + if (!index_.isValid()) return nullptr; + + XmlProjectElementCollectionItem *xpeci = static_cast(index_.internalPointer()); + if (xpeci) + return xpeci; + else + return nullptr; +} + +/** + * @brief ElementsCollectionModel::elementAddedToEmbeddedCollection + * When an element is added to embedded collection of a project, + * this method create and display the new element + * @param project -The project where new element was added. + * @param path -The path of the new element in the embedded collection of project + */ +void ElementsCollectionModel::elementIntegratedToCollection(QETProject *project, QString path) +{ + XmlProjectElementCollectionItem *xpeci = itemForProject(project); + if (!xpeci) return; + + QString collection_name; + XmlProjectElementCollectionItem *parent_xpeci = xpeci->lastItemForPath(path, collection_name); + if (parent_xpeci) + { + int new_row = parent_xpeci->rowForInsertItem(collection_name); + if (new_row <= -1) return; + QModelIndex parent_index = createIndex(parent_xpeci->row(), 0, parent_xpeci); + beginInsertRows(parent_index, new_row, new_row); + parent_xpeci->insertNewItem(collection_name); + endInsertRows(); + } +} diff --git a/sources/ElementsCollection/elementscollectionmodel.h b/sources/ElementsCollection/elementscollectionmodel.h index 6125f86b4..3d8410ed0 100644 --- a/sources/ElementsCollection/elementscollectionmodel.h +++ b/sources/ElementsCollection/elementscollectionmodel.h @@ -23,6 +23,7 @@ class ElementCollectionItem; class QETProject; class QList; +class XmlProjectElementCollectionItem; /** * @brief The ElementsCollectionModel class @@ -58,6 +59,10 @@ class ElementsCollectionModel : public QAbstractItemModel bool removeProject(QETProject *project); QList project() const; + private: + XmlProjectElementCollectionItem *itemForProject(QETProject *project); + void elementIntegratedToCollection (QETProject *project, QString path); + private: ElementCollectionItem *m_root_item; QList m_project_list; diff --git a/sources/ElementsCollection/xmlelementcollection.cpp b/sources/ElementsCollection/xmlelementcollection.cpp index d73f4442a..809e3c491 100644 --- a/sources/ElementsCollection/xmlelementcollection.cpp +++ b/sources/ElementsCollection/xmlelementcollection.cpp @@ -17,6 +17,8 @@ */ #include "xmlelementcollection.h" #include "nameslist.h" +#include "elementlocation.h" +#include "qetxml.h" /** * @brief XmlElementCollection::XmlElementCollection @@ -83,23 +85,63 @@ QDomElement XmlElementCollection::root() const { return m_dom_document.documentElement(); } +/** + * @brief XmlElementCollection::importCategory + * @return The QDomElement import (the begining of a xml collection) or + * a null QDomElement if doesn't exist. + */ +QDomElement XmlElementCollection::importCategory() const { + return root().firstChildElement("category"); +} + /** * @brief XmlElementCollection::childs * @param parent_element * @return All childs element in the @parent_element tree */ -QDomNodeList XmlElementCollection::childs(const QDomElement &parent_element) +QDomNodeList XmlElementCollection::childs(const QDomElement &parent_element) const { if (parent_element.ownerDocument() != m_dom_document) return QDomNodeList(); return parent_element.childNodes(); } /** - * @brief XmlElementCollection::directory + * @brief XmlElementCollection::child + * If parent_element have child element with name "child_name", return it, else return a null QDomElement. + * Only search for element with tag-name "category" and "element" (if child_name end with ".elmt") + * @param parent_element : the parent DomElement where we search for child. + * @parent_element must be a child node of this XmlElementCollection. + * @param child_name : name of child to search. + * @return The child QDomElement or a null QDomElement if not found + */ +QDomElement XmlElementCollection::child(const QDomElement &parent_element, const QString &child_name) const +{ + if (parent_element.ownerDocument() != m_dom_document && parent_element.tagName() != "category") return QDomElement(); + + //Get all childs element of parent_element + QDomNodeList node_list; + if (child_name.endsWith(".elmt")) + node_list = parent_element.elementsByTagName("element"); + else + node_list = parent_element.elementsByTagName("category"); + + if (node_list.isEmpty()) return QDomElement(); + + for (int i=0 ; i XmlElementCollection::directory(const QDomElement &parent_element) +QList XmlElementCollection::directories(const QDomElement &parent_element) { QList directory_list; QDomNodeList node_list = childs(parent_element); @@ -207,3 +249,102 @@ QDomElement XmlElementCollection::directory(const QString &path) return dom_element; } + +/** + * @brief XmlElementCollection::addElement + * Add the element at path @path to this xml collection. + * The path must be a common or custom collection (a file system element). + * @param path, path of the element + * @return the xml collection path of the added item or a null QString if element can't be added. + */ +QString XmlElementCollection::addElement(const QString &path) +{ + + ElementLocation location(path); + if (!location.isElement() || location.fileSystemPath().isEmpty()) return QString(); + if (exist(QString("import/" + location.collectionPath(false)))) return QString(); + + + + QDir dir(location.fileSystemPath().remove(location.collectionPath(false))); + if (!dir.exists()) return QString(); + + QDomElement parent_element = importCategory(); + if (parent_element.isNull()) return QString(); + + QStringList str_list = location.collectionPath(false).split("/"); + if (str_list.isEmpty()) return QString(); + + QString collection_path(parent_element.attribute("name")); + + foreach(QString str, str_list) + { + QDomElement child_element = child(parent_element, str); + + //Child doesn't exist + if (child_element.isNull()) + { + if (str.endsWith(".elmt")) + { + QFile element_file(dir.filePath(str)); + if (!element_file.exists()) return QString(); + + QDomElement element_dom = QETXML::fileSystemElementToXmlCollectionElement(m_dom_document, element_file); + if (element_dom.isNull()) return QString(); + + parent_element.appendChild(element_dom); + parent_element = element_dom; + } + else + { + //Dir doesn't exist. + if (!dir.cd(str)) return QString(); + QDomElement dir_element = QETXML::fileSystemDirToXmlCollectionDir(m_dom_document, dir); + //Creation of a xml collection dir failed + if (dir_element.isNull()) return QString(); + + parent_element.appendChild(dir_element); + parent_element = dir_element; + } + } + //Child exist + else + { + if (!dir.cd(str)) return QString(); + parent_element = child_element; + } + + collection_path.append("/"+str); + } + + emit elementAdded(collection_path); + return collection_path; +} + +/** + * @brief XmlElementCollection::exist + * Return true if the path @path exist in this collection + * @param path + * @return + */ +bool XmlElementCollection::exist(const QString &path) +{ + QStringList str_list = path.split("/"); + if (str_list.isEmpty()) return false; + + //The first category of a XmlElementCollection is always "import" + if (str_list.first() != "import") return false; + str_list.removeFirst(); + + QDomElement parent_element = importCategory(); + foreach (QString str, str_list) + { + QDomElement child_element = child(parent_element, str); + if (child_element.isNull()) + return false; + else + parent_element = child_element; + } + + return true; +} diff --git a/sources/ElementsCollection/xmlelementcollection.h b/sources/ElementsCollection/xmlelementcollection.h index 870e795b9..f9155ad0d 100644 --- a/sources/ElementsCollection/xmlelementcollection.h +++ b/sources/ElementsCollection/xmlelementcollection.h @@ -22,6 +22,7 @@ #include class QDomElement; +class QFile; /** * @brief The XmlElementCollection class @@ -34,19 +35,28 @@ class XmlElementCollection : public QObject XmlElementCollection (QObject *parent = nullptr); XmlElementCollection (const QDomElement &dom_element, QObject *parent = nullptr); QDomElement root() const; - QDomNodeList childs(const QDomElement &parent_element); - QList directory(const QDomElement &parent_element); + QDomElement importCategory() const; + QDomNodeList childs(const QDomElement &parent_element) const; + QDomElement child(const QDomElement &parent_element, const QString &child_name) const; + QList directories(const QDomElement &parent_element); QList elements(const QDomElement &parent_element); QDomElement element(const QString &path); QDomElement directory(const QString &path); + QString addElement (const QString &path); + bool exist (const QString &path); signals: + /** + * @brief elementAdded + * This signal is emited when a element is added to this collection + * @param collection_path, the path of element in this collection + */ + void elementAdded(QString collection_path); public slots: private: QDomDocument m_dom_document; - }; #endif // XMLELEMENTCOLLECTION_H diff --git a/sources/ElementsCollection/xmlprojectelementcollectionitem.cpp b/sources/ElementsCollection/xmlprojectelementcollectionitem.cpp index d5043770f..a6f54327b 100644 --- a/sources/ElementsCollection/xmlprojectelementcollectionitem.cpp +++ b/sources/ElementsCollection/xmlprojectelementcollectionitem.cpp @@ -216,7 +216,6 @@ QString XmlProjectElementCollectionItem::collectionPath() const else { XmlProjectElementCollectionItem *parent = static_cast(m_parent_item); - if (parent->isCollectionRoot()) return parent->collectionPath() + m_dom_element.attribute("name"); else @@ -240,19 +239,123 @@ QString XmlProjectElementCollectionItem::embeddedPath() const XmlProjectElementCollectionItem *parent = static_cast(m_parent_item); if (parent->isCollectionRoot()) - return parent->embeddedPath() + m_dom_element.attribute("name"); + return parent->embeddedPath() + collectionName(); else - return parent->embeddedPath() + "/" + m_dom_element.attribute("name"); + return parent->embeddedPath() + "/" + collectionName(); } } +/** + * @brief XmlProjectElementCollectionItem::collectionName + * @return The collection name of this item + */ +QString XmlProjectElementCollectionItem::collectionName() const { + return m_dom_element.attribute("name"); +} + +/** + * @brief XmlProjectElementCollectionItem::lastItemForPath + * Return the last existing item in this XmlProjectElementCollectionItem hierarchy according to the given path. + * Next_item is the first non existing item in this hierarchy according to the given path. + * @param path : The path to find last item. The path must be in form : path/otherPath/myElement.elmt. + * @param next_item : The first item that not exist in this hierarchy + * @return : The last item that exist in this hierarchy, or nullptr can't find (an error was occurred, or path already exist) + */ +XmlProjectElementCollectionItem *XmlProjectElementCollectionItem::lastItemForPath(const QString &path, QString &next_item) +{ + QStringList str_list = path.split("/"); + if (str_list.isEmpty()) return nullptr; + + XmlProjectElementCollectionItem *xpeci = this; + foreach (QString str, str_list) + { + ElementCollectionItem *eci = xpeci->childWithCollectionName(str); + if (!eci) + { + next_item = str; + return xpeci; + } + else + xpeci = static_cast(eci); + } + + return nullptr; +} + +/** + * @brief XmlProjectElementCollectionItem::rowForInsertItem + * Return the row for insert a new child item to this item with name @collection_name. + * If row can't be found (collection_name is null, or already exist) return -1; + * @param path + * @return + */ +int XmlProjectElementCollectionItem::rowForInsertItem(const QString &collection_name) +{ + if (collection_name.isEmpty()) return -1; + + QList child; + //The item to insert is an element we search from element child + if (collection_name.endsWith(".elmt")) + { + child = elementsChild(); + //There isn't element, we insert at last position + if (child.isEmpty()) + return childCount(); + } + //The item is a directory, we search from directory child + else + { + child = directoriesChild(); + //There isn't directory, we insert at first position + if(child.isEmpty()) + return 0; + } + + foreach (ElementCollectionItem *eci, child) + if (eci->collectionName() > collection_name) + return indexOfChild(eci); + + return childCount(); +} + +/** + * @brief XmlProjectElementCollectionItem::insertNewItem + * When this XmlProjectElementCollectionItem is already created, we must to use this method for insert a new item. + * Befor use this, see rowForInsertItem and lastItemForPath + * @param collection_name : the collection name to search in the child of QDomElement. + */ +void XmlProjectElementCollectionItem::insertNewItem(const QString &collection_name) +{ + if (collection_name.isEmpty()) return; + + QDomNodeList node_list; + if (collection_name.endsWith(".elmt")) + node_list = m_dom_element.elementsByTagName("element"); + else + node_list = m_dom_element.elementsByTagName("category"); + + QDomElement child_element; + for(int i=0 ; i dom_category = m_project->embeddedElementCollection()->directory(m_dom_element); + QList dom_category = m_project->embeddedElementCollection()->directories(m_dom_element); std::sort(dom_category.begin(), dom_category.end(), [](QDomElement a, QDomElement b){return (a.attribute("name") < b.attribute("name"));}); foreach (QDomElement element, dom_category) diff --git a/sources/ElementsCollection/xmlprojectelementcollectionitem.h b/sources/ElementsCollection/xmlprojectelementcollectionitem.h index 231c82606..6cdf82635 100644 --- a/sources/ElementsCollection/xmlprojectelementcollectionitem.h +++ b/sources/ElementsCollection/xmlprojectelementcollectionitem.h @@ -53,6 +53,12 @@ class XmlProjectElementCollectionItem : public ElementCollectionItem virtual bool isElement() const; QString collectionPath() const; QString embeddedPath() const; + virtual QString collectionName() const; + + XmlProjectElementCollectionItem *lastItemForPath(const QString &path, QString &next_item); + int rowForInsertItem(const QString &collection_name); + void insertNewItem(const QString &collection_name); + private: void populate(); diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index 2d378f74e..7c20e3956 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -706,7 +706,8 @@ QString QETProject::integrateElement(const QString &elmt_location, QString &erro */ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandler *handler, QString &error_message) { // on s'assure que le projet a une categorie dediee aux elements importes automatiquement - if (!ensureIntegrationCategoryExists()) { + if (!ensureIntegrationCategoryExists()) + { error_message = tr("Impossible de créer la catégorie pour l'intégration des éléments"); return(QString()); } @@ -717,7 +718,8 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl // accede a l'element a integrer ElementsCollectionItem *integ_item = QETApp::collectionItem(ElementsLocation::locationFromString(elmt_path)); ElementDefinition *integ_elmt = integ_item ? integ_item -> toElement() : 0; - if (!integ_item || !integ_elmt) { + if (!integ_item || !integ_elmt) + { error_message = tr("Impossible d'accéder à l'élément à intégrer"); return(QString()); } @@ -725,16 +727,21 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl // recopie l'arborescence de l'element de facon non recursive QList integ_par_cat = integ_elmt -> parentCategories(); ElementsCategory *target_cat = integ_cat; - foreach(ElementsCategory *par_cat, integ_par_cat) { + foreach(ElementsCategory *par_cat, integ_par_cat) + { if (par_cat -> isRootCategory()) continue; - if (ElementsCategory *existing_cat = target_cat -> category(par_cat -> pathName())) { + if (ElementsCategory *existing_cat = target_cat -> category(par_cat -> pathName())) + { // la categorie cible existe deja : on continue la progression target_cat = existing_cat; - } else { + } + else + { // la categorie cible n'existe pas : on la cree par recopie ElementsCollectionItem *result_cat = par_cat -> copy(target_cat, handler, false); - if (!result_cat || !result_cat -> isCategory()) { + if (!result_cat || !result_cat -> isCategory()) + { error_message = QString(tr("Un problème s'est produit pendant la copie de la catégorie %1")).arg(par_cat -> location().toString()); return(QString()); } @@ -744,20 +751,26 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl // recopie l'element ElementsLocation result; - if (ElementDefinition *existing_elmt = target_cat -> element(integ_item -> pathName())) { + if (ElementDefinition *existing_elmt = target_cat -> element(integ_item -> pathName())) + { // l'element existe deja - on demande au handler ce que l'on doit faire QET::Action action = handler -> elementAlreadyExists(integ_elmt, existing_elmt); - if (action == QET::Ignore) { + if (action == QET::Ignore) + { // il faut conserver et utiliser l'element deja integre result = existing_elmt -> location(); - } else if (action == QET::Erase) { + } + else if (action == QET::Erase) + { // il faut ecraser l'element deja integre BasicMoveElementsHandler *erase_handler = new BasicMoveElementsHandler(); result = copyElementWithHandler(integ_elmt, target_cat, erase_handler, error_message); delete erase_handler; - } else if (action == QET::Rename) { + } + else if (action == QET::Rename) + { // il faut faire cohabiter les deux elements en renommant le nouveau QString integ_element_name = handler -> nameForRenamingOperation(); BasicMoveElementsHandler *rename_handler = new BasicMoveElementsHandler(); @@ -765,13 +778,19 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl rename_handler -> setNameForRenamingOperation(integ_element_name); result = copyElementWithHandler(integ_elmt, target_cat, rename_handler, error_message); delete rename_handler; - } else { + } + else + { // il faut annuler la pose de l'element result = ElementsLocation(); } - } else { + } + else + { // integre l'element normalement result = copyElementWithHandler(integ_elmt, target_cat, handler, error_message); + QString xml_path = m_elements_collection->addElement(elmt_path); + if (!xml_path.isNull()) emit elementIntegratedToCollection(this, xml_path); } if (!result.isNull()) emit(elementIntegrated(this, result)); diff --git a/sources/qetproject.h b/sources/qetproject.h index 12b4c89a9..becd48fc5 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -164,6 +164,7 @@ class QETProject : public QObject void readOnlyChanged(QETProject *, bool); void reportPropertiesChanged(QString); void XRefPropertiesChanged (); + void elementIntegratedToCollection(QETProject *project, QString path); private slots: void updateDiagramsFolioData();