/* Copyright 2006-2013 The QElectroTech Team This file is part of QElectroTech. QElectroTech is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. QElectroTech 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 General Public License for more details. You should have received a copy of the GNU General Public License along with QElectroTech. If not, see . */ #include "elementdefinition.h" #include "elementscollection.h" #include "moveelementshandler.h" #include "moveelementsdescription.h" /** @return true si l'element est rattache a une collection d'elements Un element appartenant a une collection a forcement un chemin virtuel. */ bool ElementDefinition::hasParentCategory() { return(parent_category_); } /** @return la categorie a laquelle appartient cet element */ ElementsCategory *ElementDefinition::parentCategory() { return(parent_category_); } /** @return la liste des categories parentes de cet item. */ QList ElementDefinition::parentCategories() { QList cat_list; if (ElementsCategory *par_cat = parentCategory()) { cat_list << par_cat -> parentCategories() << par_cat; } return(cat_list); } /** @return true si l'element est rattache a une collection d'elements Un element appartenant a une collection a forcement un chemin virtuel. */ bool ElementDefinition::hasParentCollection() { return(parent_collection_); } /** @param other_item Autre item @return true si other_item est parent (direct ou indirect) de cet item, false sinon */ bool ElementDefinition::isChildOf(ElementsCollectionItem *other_item) { // soit l'autre item est le parent direct de cet element if (ElementsCategory *other_item_cat = other_item -> toCategory()) { if (other_item_cat == parentCategory()) { return(true); } } // soit il est un parent indirect, auquel cas, on peut demander a la categorie parente de repondre a la question if (ElementsCategory *parent_cat = parentCategory()) { return(parent_cat -> isChildOf(other_item)); } // arrive ici, l'autre item n'est pas parent de cet item return(false); } /** @return la collection d'element a laquelle appartient cet element */ ElementsCollection *ElementDefinition::parentCollection() { return(parent_collection_); } /** @return le projet auquel appartient cette categorie, si celle-ci appartient a une collection. */ QETProject *ElementDefinition::project() { if (hasParentCollection()) { return(parentCollection() -> project()); } return(0); } /** Ne fait rien ; le projet doit etre defini au niveau d'une collection */ void ElementDefinition::setProject(QETProject *) { } /** @return le protocole utilise par la collection a laquelle appartient cet element */ QString ElementDefinition::protocol() { // il n'est pas possible d'avoir un protocole sans appartenir a une collection if (!hasParentCollection()) return(QString()); return(parentCollection() -> protocol()); } /** Ne fait rien */ void ElementDefinition::setProtocol(const QString &) { } /** @return le chemin virtuel complet de cet element (protocole + chemin) */ QString ElementDefinition::fullVirtualPath() { // il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection if (!hasParentCollection()) return(QString()); return(protocol() + "://" + virtualPath()); } /** @return l'emplacement de l'element */ ElementsLocation ElementDefinition::location() { return(ElementsLocation(fullVirtualPath(), project())); } /** @return une liste vide - un element ne possede pas de categorie */ QList ElementDefinition::categories() { return(QList()); } /** @return toujours 0 - un element ne possede pas de categorie */ ElementsCategory *ElementDefinition::category(const QString &) { return(0); } /** @return toujours 0 - un element ne possede pas de categorie */ ElementsCategory *ElementDefinition::createCategory(const QString &) { return(0); } /** @return une liste contenant seulement cet element */ QList ElementDefinition::elements() { return(QList() << this); } /** @return cet element si path est vide, 0 sinon */ ElementDefinition *ElementDefinition::element(const QString &path) { if (path.isEmpty()) return(this); return(0); } /** @return toujours 0 - un element n'en cree pas d'autre */ ElementDefinition *ElementDefinition::createElement(const QString &) { return(0); } /** @return always true - an element contains nothing but itself */ bool ElementDefinition::isEmpty() { return(false); } /** @return always 1 */ int ElementDefinition::count() { return(1); } /** @return toujours 0 - un element n'est pas une collection */ ElementsCollection *ElementDefinition::toCollection() { return(0); } /** @return la categorie parente de cet element */ ElementsCategory *ElementDefinition::toCategory() { return(parentCategory()); } /** @return toujours 0 - un element n'est pas une categorie */ ElementsCategory *ElementDefinition::toPureCategory() { return(0); } /** @return un pointeur ElementDefinition * sur cet element */ ElementDefinition *ElementDefinition::toElement() { return(this); } /** @return true si cette definition d'element est egale (en termes de contenu) a la definition d'element other, false sinon. */ bool ElementDefinition::equals(ElementDefinition &other) { /* Pour le moment, cette methode compare simplement l'export au format texte des documents XML. Cela peut entrainer de faux positifs. Exemple : un espace de plus ou de moins dans le XML n'en change pas forcement la semantique. Mais cela changera l'export au format texte. */ QDomDocument this_xml_document; this_xml_document.appendChild(this_xml_document.importNode(xml(), true)); QString this_text = this_xml_document.toString(0); QDomDocument other_xml_document; other_xml_document.appendChild(other_xml_document.importNode(other.xml(), true)); QString other_text = other_xml_document.toString(0); return(other_text == this_text); } /** @param target_category Categorie cible pour la copie ; elle doit exister @param handler Gestionnaire d'erreurs a utiliser pour effectuer la copie @param deep_copy Argument ignore - une copie "recursive" n'a pas de sens pour un element @return La copie de l'element ou 0 si le processus a echoue */ ElementsCollectionItem *ElementDefinition::copy(ElementsCategory *target_category, MoveElementsHandler *handler, bool deep_copy) { Q_UNUSED(deep_copy); if (!target_category) return(0); // echec si le path name de cet element est vide QString elmt_name(pathName()); if (elmt_name.isEmpty()) return(0); // cree une description du mouvement a effectuer MoveElementsDescription mvt_desc; mvt_desc.setDestinationParentCategory(target_category); // on tente une copie avec le meme nom interne mvt_desc.setOriginalDestinationInternalName(pathName()); mvt_desc.setFinalDestinationInternalName(pathName()); mvt_desc.setHandler(handler); copy(&mvt_desc); return(mvt_desc.createdItem()); } /** Methode privee effectuant une copie de cet element a partir d'une description du mouvement. @param mvt_desc Description du mouvement */ void ElementDefinition::copy(MoveElementsDescription *mvt_desc) { // quelques pointeurs pour simplifier l'ecriture de la methode MoveElementsHandler *handler = mvt_desc -> handler(); ElementsCategory *target_category = mvt_desc -> destinationParentCategory(); ElementDefinition *element_copy = 0; // verifie que la categorie parente cible est accessible en lecture if (!target_category -> isReadable()) { if (!handler) { return; } else { do { QET::Action todo = handler -> categoryIsNotReadable(target_category); // on agit en fonction de la reponse du handler if (todo == QET::Abort) { mvt_desc -> abort(); return; } else if (todo == QET::Ignore || todo == QET::Managed) { return; } else if (todo == QET::Retry || todo == QET::Erase) { // reessayer = repasser dans la boucle } else if (todo == QET::Rename) { // cas non gere } } while (!target_category -> isReadable()); } } // verifie que la categorie parente cible est accessible en ecriture if (!target_category -> isWritable()) { if (!handler) { return; } else { do { QET::Action todo = handler -> categoryIsNotWritable(target_category); // on agit en fonction de la reponse du handler if (todo == QET::Abort) { mvt_desc -> abort(); return; } else if (todo == QET::Ignore || todo == QET::Managed) { return; } else if (todo == QET::Retry || todo == QET::Erase) { // reessayer = repasser dans la boucle } else if (todo == QET::Rename) { // cas non gere } } while (!target_category -> isWritable()); } } // verifie que la cible n'existe pas deja if ((element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName()))) { if (!handler) { return; } else { do { // la cible existe deja : on demande au Handler ce qu'on doit faire QET::Action todo = handler -> elementAlreadyExists(this, element_copy); // on agit en fonction de la reponse du handler if (todo == QET::Abort) { mvt_desc -> abort(); return; } else if (todo == QET::Ignore || todo == QET::Managed) { return; } else if (todo == QET::Erase) { break; } else if (todo == QET::Rename) { mvt_desc -> setFinalDestinationInternalName(handler -> nameForRenamingOperation()); } } while ((element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName()))); } } /* A ce stade, on peut creer l'element cible : soit il n'existe pas, soit on a l'autorisation de l'ecraser */ // si la cible existe deja, verifie qu'elle est accessible en ecriture element_copy = target_category -> element(mvt_desc -> finalDestinationInternalName()); if (element_copy && !element_copy -> isWritable()) { if (!handler) { return; } else { do { // la cible n'est pas accessible en ecriture : on demande au Handler ce qu'on doit faire QET::Action todo = handler -> elementIsNotWritable(element_copy); // on agit en fonction de la reponse du handler if (todo == QET::Abort) { mvt_desc -> abort(); return; } else if (todo == QET::Ignore || todo == QET::Managed) { return; } else if (todo == QET::Retry || todo == QET::Erase) { // reessayer = repasser dans la boucle } else if (todo == QET::Rename) { // cas non gere } } while (!element_copy -> isWritable()); } } // cree l'element cible element_copy = target_category -> createElement(mvt_desc -> finalDestinationInternalName()); if (!element_copy) { if (handler) { handler -> errorWithAnElement(this, tr("L'\351l\351ment cible n'a pu \352tre cr\351\351.")); } return; } // recopie la definition de l'element element_copy -> setXml(xml()); element_copy -> write(); mvt_desc -> setCreatedItem(element_copy); } /** @param target_category Categorie cible pour le deplacement ; elle doit exister @param handler Gestionnaire d'erreurs a utiliser pour effectuer le deplacement @return L'element apres deplacement ou 0 si le processus a echoue */ ElementsCollectionItem *ElementDefinition::move(ElementsCategory *target_category, MoveElementsHandler *handler) { if (!target_category) return(0); // echec si le path name de cet element est vide QString elmt_name(pathName()); if (elmt_name.isEmpty()) return(0); // cree une description du mouvement a effectuer MoveElementsDescription mvt_desc; mvt_desc.setDestinationParentCategory(target_category); // on tente un deplacement avec le meme nom interne mvt_desc.setOriginalDestinationInternalName(pathName()); mvt_desc.setFinalDestinationInternalName(pathName()); mvt_desc.setHandler(handler); move(&mvt_desc); return(mvt_desc.createdItem()); } /** Methode privee effectuant un delacement de cet element a partir d'une description du mouvement. Pour etre plus exact, cette methode effectue d'abord une copie de l'element, puis, si celle-ci a reussi, il supprime l'element d'origine. @param mvt_desc Description du mouvement */ void ElementDefinition::move(MoveElementsDescription *mvt_desc) { // effectue une copie de l'element copy(mvt_desc); ElementsCollectionItem *item_copy = mvt_desc -> createdItem(); if (!item_copy) return; ElementDefinition *element_copy = item_copy -> toElement(); if (!element_copy) return; // supprime cet element MoveElementsHandler *handler = mvt_desc -> handler(); // cet element doit etre accessible en ecriture pour etre supprime if (!isWritable()) { if (!handler) { return; } else { do { // on demande au Handler ce qu'on doit faire QET::Action todo = handler -> elementIsNotWritable(this); // on agit en fonction de la reponse du handler if (todo == QET::Abort) { mvt_desc -> abort(); return; } else if (todo == QET::Ignore || todo == QET::Managed) { return; } else if (todo == QET::Retry || todo == QET::Erase) { // reessayer = repasser dans la boucle } else if (todo == QET::Rename) { // cas non gere } } while (!isWritable()); } } // supprime cet element (sinon il ne s'agirait que d'une copie, pas d'un deplacement) bool element_deletion = remove(); mvt_desc -> setSourceItemDeleted(element_deletion); if (!element_deletion && handler) { handler -> errorWithAnElement(this, tr("La suppression de cet \351l\351ment a \351chou\351.")); } } /** Cette methode n'a aucun effet @return toujours true */ bool ElementDefinition::removeContent() { return(true); }