diff --git a/sources/elementslocation.cpp b/sources/elementslocation.cpp index 45353bf64..eb72da9f7 100644 --- a/sources/elementslocation.cpp +++ b/sources/elementslocation.cpp @@ -135,6 +135,22 @@ bool ElementsLocation::addToPath(const QString &string) { return(true); } +/** + @return the location of the parent category, or a copy of this location + when it represents a root category. +*/ +ElementsLocation ElementsLocation::parent() const { + ElementsLocation copy(*this); + QRegExp re1("^([a-z]+://)(.*)/*$"); + if (re1.exactMatch(path_)) { + QString path_proto = re1.capturedTexts().at(1); + QString path_path = re1.capturedTexts().at(2); + QString parent_path = path_path.remove(QRegExp("/*[^/]+$")); + copy.setPath(path_proto + parent_path); + } + return(copy); +} + /** @return le projet de cet emplacement ou 0 si celui-ci n'est pas lie a un projet. diff --git a/sources/elementslocation.h b/sources/elementslocation.h index 4c0508fe9..4b583b315 100644 --- a/sources/elementslocation.h +++ b/sources/elementslocation.h @@ -42,6 +42,7 @@ class ElementsLocation { QString path() const; void setPath(const QString &); bool addToPath(const QString &); + ElementsLocation parent() const; QETProject *project() const; void setProject(QETProject *); bool isNull() const; diff --git a/sources/elementspanel.cpp b/sources/elementspanel.cpp index 383d82a75..8d8e15b85 100644 --- a/sources/elementspanel.cpp +++ b/sources/elementspanel.cpp @@ -26,6 +26,7 @@ #include "fileelementdefinition.h" #include "qeticons.h" #include "templatescollection.h" +#include "treecoloranimation.h" /** This class implements a thread reloading the following elements @@ -381,6 +382,32 @@ void ElementsPanel::panelContentChange() { } } +/** + Inform this panel the project \a project has integrated the element at \a location +*/ +QList ElementsPanel::elementIntegrated(QETProject *project, const ElementsLocation &location) { + // the base implementation simply refreshes the adequate category and returns the list of added locations + QList added_locations = GenericPanel::elementIntegrated(project, location); + if (!added_locations.count()) return(added_locations); + + // the additional job of this method consists in displaying the integrated elements... + if (QTreeWidgetItem *integrated_element_qtwi = itemForElementsLocation(location)) { + ensureHierarchyIsVisible(QList() << integrated_element_qtwi); + scrollToItem(integrated_element_qtwi, QAbstractItemView::PositionAtCenter); + } + + // and make them "flash" (not too obviously though) so the user notices they have been integrated. + QList items; + foreach (ElementsLocation loc, added_locations) { + if (QTreeWidgetItem *added_item = itemForElementsLocation(loc)) { + items << added_item; + } + } + highlightItems(items, this, SLOT(scrollToSelectedItem())); + + return(added_locations); +} + /** Methode permettant d'ajouter un projet au panel d'elements. @param qtwi_parent QTreeWidgetItem parent sous lequel sera insere le projet @@ -774,3 +801,40 @@ void ElementsPanel::ensureHierarchyIsVisible(const QList &ite if (parent_qtwi -> isHidden()) parent_qtwi -> setHidden(false); } } + +/** + Scroll to the currently selected item. +*/ +void ElementsPanel::scrollToSelectedItem() { + QList selected_items = selectedItems(); + if (selected_items.count()) { + scrollToItem(selected_items.first(), QAbstractItemView::PositionAtCenter); + } +} + +/** + Scroll to and highlight \a items. Once the animation is finished, the slot + \a method is called on the object \a receiver. +*/ +void ElementsPanel::highlightItems(const QList &items, const QObject *receiver, const char *method) { + TreeColorAnimation *animation1 = new TreeColorAnimation(items); + animation1 -> setStartValue(QColor(Qt::white)); + animation1 -> setEndValue(QColor(Qt::yellow)); + animation1 -> setDuration(400); + animation1 -> setEasingCurve(QEasingCurve::InQuad); + + TreeColorAnimation *animation2 = new TreeColorAnimation(items); + animation2 -> setStartValue(QColor(Qt::yellow)); + animation2 -> setEndValue(QColor(Qt::white)); + animation2 -> setDuration(500); + animation2 -> setEasingCurve(QEasingCurve::OutInQuint); + + QSequentialAnimationGroup *animation = new QSequentialAnimationGroup(this); + animation -> addAnimation(animation1); + animation -> addAnimation(new QPauseAnimation(700)); + animation -> addAnimation(animation2); + if (receiver) { + connect(animation, SIGNAL(finished()), receiver, method); + } + animation -> start(QAbstractAnimation::DeleteWhenStopped); +} diff --git a/sources/elementspanel.h b/sources/elementspanel.h index 0858d94cd..1b45f1ff6 100644 --- a/sources/elementspanel.h +++ b/sources/elementspanel.h @@ -80,6 +80,8 @@ class ElementsPanel : public GenericPanel { bool scrollToElement(const ElementsLocation &); void applyCurrentFilter(const QList &); void ensureHierarchyIsVisible(const QList &); + void scrollToSelectedItem(); + void highlightItems(const QList &, const QObject * = 0, const char * = 0); protected: void dragEnterEvent(QDragEnterEvent *); @@ -93,6 +95,7 @@ class ElementsPanel : public GenericPanel { protected slots: void firstActivation(); void panelContentChange(); + virtual QList elementIntegrated(QETProject *, const ElementsLocation &); private: QTreeWidgetItem *addProject (QETProject *); diff --git a/sources/genericpanel.cpp b/sources/genericpanel.cpp index 140634fe9..2aabc2aaf 100644 --- a/sources/genericpanel.cpp +++ b/sources/genericpanel.cpp @@ -10,6 +10,7 @@ #include "elementslocation.h" #include "qeticons.h" #include "elementscollectioncache.h" +#include "qetapp.h" /** Constructor @@ -248,6 +249,10 @@ QTreeWidgetItem *GenericPanel::fillProjectItem(QTreeWidgetItem *project_qtwi, QE project, SIGNAL(projectDiagramsOrderChanged(QETProject *, int, int)), this, SLOT (projectDiagramsOrderChanged(QETProject *, int, int)) ); + connect( + project, SIGNAL(elementIntegrated(QETProject *, const ElementsLocation &)), + this, SLOT(elementIntegrated(QETProject *, const ElementsLocation &)) + ); } else { // remove diagrams unknown to the project (presumably removed) removeObsoleteItems(project -> diagrams(), project_qtwi, QET::Diagram, false); @@ -729,6 +734,8 @@ QTreeWidgetItem *GenericPanel::fillElementsCategoryItem(QTreeWidgetItem *categor int index = 0; + category_qtwi -> setData(0, GenericPanel::PanelFlags, (int)options); + if (options & AddChildElementsCategories) { if (!freshly_created) { QList sub_categories; @@ -761,6 +768,23 @@ QTreeWidgetItem *GenericPanel::fillElementsCategoryItem(QTreeWidgetItem *categor return(fillItem(category_qtwi, options, freshly_created)); } +/** + Refresh elements category at \a location. + @return the refreshed tree item +*/ +QTreeWidgetItem *GenericPanel::refreshElementsCategory(const ElementsLocation &location) { + QTreeWidgetItem *item = itemForElementsLocation(location); + if (!item) return(0); + if (item -> type() != QET::ElementsCategory && item -> type() != QET::ElementsCollection) return(0); + QTreeWidgetItem *result = fillElementsCategoryItem( + item, + QETApp::collectionItem(location) -> toCategory(), + PanelOptions(QFlag(item -> data(0, GenericPanel::PanelFlags).toInt())), + false + ); + return(result); +} + /** */ @@ -940,6 +964,27 @@ void GenericPanel::projectDiagramsOrderChanged(QETProject *project, int from, in emit(panelContentChanged()); } +/** + Inform this panel the project \a project has integrated the element at \a location +*/ +QList GenericPanel::elementIntegrated(QETProject *project, const ElementsLocation &location) { + Q_UNUSED(project) + QList added_locations; + + int i = 0; + ElementsLocation loc = location; + // starting from the provided location, goes up into the tree until a displayed location is reached + while (i < 100 && !(itemForElementsLocation(loc))) { + added_locations << loc; + loc = loc.parent(); + ++ i; + } + if (added_locations.count()) { + refreshElementsCategory(loc); + } + return(added_locations); +} + /** Inform this panel the diagram \a diagram has changed its title to \a title. */ diff --git a/sources/genericpanel.h b/sources/genericpanel.h index 868bf6667..606c947b3 100644 --- a/sources/genericpanel.h +++ b/sources/genericpanel.h @@ -59,7 +59,8 @@ class GenericPanel : public QTreeWidget { enum MetaData { Item = Qt::UserRole + 1, AliasItem, - Parent + Parent, + PanelFlags }; // Constructors, destructor @@ -143,6 +144,7 @@ class GenericPanel : public QTreeWidget { virtual QTreeWidgetItem *getItemForElementsCategory(ElementsCategory *, bool * = 0); virtual QTreeWidgetItem *updateElementsCategoryItem(QTreeWidgetItem *, ElementsCategory *, PanelOptions = AddAllChild, bool = false); virtual QTreeWidgetItem *fillElementsCategoryItem (QTreeWidgetItem *, ElementsCategory *, PanelOptions = AddAllChild, bool = false); + virtual QTreeWidgetItem *refreshElementsCategory(const ElementsLocation &); // elements methods public: @@ -164,6 +166,7 @@ class GenericPanel : public QTreeWidget { virtual void diagramAdded(QETProject *, Diagram *); virtual void diagramRemoved(QETProject *, Diagram *); virtual void projectDiagramsOrderChanged(QETProject *, int, int); + virtual QList elementIntegrated(QETProject *, const ElementsLocation &); virtual void diagramTitleChanged(Diagram *, const QString &); virtual void templatesCollectionChanged(TitleBlockTemplatesCollection*, const QString &); virtual void diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &); diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index 37ec97bcb..9a708ea5c 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -629,6 +629,7 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl } // recopie l'element + ElementsLocation result; if (ElementDefinition *existing_elmt = target_cat -> element(integ_item -> pathName())) { // l'element existe deja - on demande au handler ce que l'on doit faire @@ -636,31 +637,31 @@ QString QETProject::integrateElement(const QString &elmt_path, MoveElementsHandl if (action == QET::Ignore) { // il faut conserver et utiliser l'element deja integre - return(existing_elmt -> location().toString()); + result = existing_elmt -> location(); } else if (action == QET::Erase) { // il faut ecraser l'element deja integre BasicMoveElementsHandler *erase_handler = new BasicMoveElementsHandler(); - ElementsLocation result_loc = copyElementWithHandler(integ_elmt, target_cat, erase_handler, error_message); + ElementsLocation result = copyElementWithHandler(integ_elmt, target_cat, erase_handler, error_message); delete erase_handler; - return(result_loc.toString()); } 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(); rename_handler -> setActionIfItemAlreadyExists(QET::Rename); rename_handler -> setNameForRenamingOperation(integ_element_name); - ElementsLocation result_loc = copyElementWithHandler(integ_elmt, target_cat, rename_handler, error_message); + result = copyElementWithHandler(integ_elmt, target_cat, rename_handler, error_message); delete rename_handler; - return(result_loc.toString()); } else { // il faut annuler la pose de l'element - return(QString()); + result = ElementsLocation(); } } else { // integre l'element normalement - ElementsLocation result_loc = copyElementWithHandler(integ_elmt, target_cat, handler, error_message); - return(result_loc.toString()); + result = copyElementWithHandler(integ_elmt, target_cat, handler, error_message); } + + if (!result.isNull()) emit(elementIntegrated(this, result)); + return(result.toString()); } /** diff --git a/sources/qetproject.h b/sources/qetproject.h index d85203eba..c84b5537a 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -129,6 +129,7 @@ class QETProject : public QObject { void diagramAdded(QETProject *, Diagram *); void diagramRemoved(QETProject *, Diagram *); void projectDiagramsOrderChanged(QETProject *, int, int); + void elementIntegrated(QETProject *, const ElementsLocation &); void diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &); void readOnlyChanged(QETProject *, bool); diff --git a/sources/treecoloranimation.cpp b/sources/treecoloranimation.cpp new file mode 100644 index 000000000..a17fd7c3d --- /dev/null +++ b/sources/treecoloranimation.cpp @@ -0,0 +1,35 @@ +#include "treecoloranimation.h" + +/** + Constructor + @param items List of items whose background color will be animated. + @param parent Parent QObject +*/ +TreeColorAnimation::TreeColorAnimation(const QList &items, QObject *parent) : + QVariantAnimation(parent), + items_(items) +{ +} + +/** + Destructor +*/ +TreeColorAnimation::~TreeColorAnimation() { +} + +/** + @return the list of items whose background color will be animated. +*/ +QList TreeColorAnimation::items() const { + return(items_); +} + +/** + Apply the provided color to animated items. + @param color Color to be applied on animated items. +*/ +void TreeColorAnimation::updateCurrentValue(const QVariant &color) { + foreach (QTreeWidgetItem *item, items_) { + item -> setBackgroundColor(0, color.value()); + } +} diff --git a/sources/treecoloranimation.h b/sources/treecoloranimation.h new file mode 100644 index 000000000..7339b2e72 --- /dev/null +++ b/sources/treecoloranimation.h @@ -0,0 +1,43 @@ +/* + Copyright 2006-2012 Xavier Guerrin + 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 . +*/ +#ifndef TREE_COLOR_ANIMATION_H +#define TREE_COLOR_ANIMATION_H +#include + +/** + This class allows animating a background color change for a + set of QTreeWidgetItem. +*/ +class TreeColorAnimation : public QVariantAnimation { + // Constructors, destructor + public: + TreeColorAnimation(const QList &items, QObject * = 0); + virtual ~TreeColorAnimation(); + + // methods + public: + QList items() const; + + protected: + void updateCurrentValue(const QVariant &); + + // attributes + private: + QList items_; ///< Items this object will animate +}; +#endif