From: nds Date: Thu, 4 Oct 2007 05:41:19 +0000 (+0000) Subject: Gets this file from BR_QT4_dev. X-Git-Tag: TG_Saint_Valentine-Day~32 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=a51a8c5143ebf6da61d8125b3d3504f3c8e381e2;p=modules%2Fgui.git Gets this file from BR_QT4_dev. --- diff --git a/src/SUIT/SUIT_DataObject.cxx b/src/SUIT/SUIT_DataObject.cxx index 186dbe195..26c27a429 100755 --- a/src/SUIT/SUIT_DataObject.cxx +++ b/src/SUIT/SUIT_DataObject.cxx @@ -1,44 +1,61 @@ // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D -// +// // This library 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 +// License as published by the Free Software Foundation; either // version 2.1 of the License. -// -// This library 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 +// +// This library 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 Lesser General Public -// License along with this library; if not, write to the Free Software +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SUIT_DataObject.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// #include "SUIT_DataObject.h" - #include "SUIT_DataObjectKey.h" +SUIT_DataObject::Signal* SUIT_DataObject::mySignal = 0; + /*! - Constructor + \class SUIT_DataObject + \brief Data object representing the data instance in the tree-like hierarchy. + + Data object represents uniform data tree structure recommended to use in the + SUIT-based applications. */ +/*! + \brief Constructor. + + Creates the data object with the specified parent. + To create the top-level object, pass 0 as parameter. + + \param p parent object +*/ SUIT_DataObject::SUIT_DataObject( SUIT_DataObject* p ) : myParent( 0 ), -myOpen( false ), -myCheck( false ), -mySignal( 0 ), -myAutoDel( true ) + myOpen( false ), + myCheck( false ), + myAutoDel( true ) { setParent( p ); + signal()->emitCreated( this ); } /*! - Destructor -*/ + \brief Destructor. + Destroys all the children if "auto-delete children" flag is set. +*/ SUIT_DataObject::~SUIT_DataObject() { SUIT_DataObject* p = myParent; @@ -48,11 +65,7 @@ SUIT_DataObject::~SUIT_DataObject() if ( p ) p->removeChild( this ); - if ( mySignal ) - { - mySignal->emitSignal(); - mySignal->setOwner( 0 ); - } + signal()->emitDestroyed( this ); for ( DataObjectList::iterator it = myChildren.begin(); it != myChildren.end(); ++it ) (*it)->myParent = 0; @@ -62,23 +75,22 @@ SUIT_DataObject::~SUIT_DataObject() for ( DataObjectList::iterator itr = myChildren.begin(); itr != myChildren.end(); ++itr ) delete *itr; } - - delete mySignal; } /*! - Returns the root object. + \brief Get the root object. + \return root object of the data tree */ - SUIT_DataObject* SUIT_DataObject::root() const { return parent() ? parent()->root() : (SUIT_DataObject*)this; } /*! - Returns the first child object. + \brief Get the first child object. + \return first child object or 0 if there are no children + \sa lastChild() */ - SUIT_DataObject* SUIT_DataObject::firstChild() const { SUIT_DataObject* child = 0; @@ -88,9 +100,10 @@ SUIT_DataObject* SUIT_DataObject::firstChild() const } /*! - Returns the last child object. + \brief Get the last child object. + \return last child object or 0 if there are no children + \sa firstChild() */ - SUIT_DataObject* SUIT_DataObject::lastChild() const { SUIT_DataObject* child = 0; @@ -100,54 +113,104 @@ SUIT_DataObject* SUIT_DataObject::lastChild() const } /*! - Returns the number of the child objects. -*/ + \brief Get the number of the columns provided by the data tree. -int SUIT_DataObject::childCount() const + This method can be re-implemented in the subclasses. + Default implementation returns 1 ("Name" column) only. + + \return number of the columns +*/ +int SUIT_DataObject::columnCount() const { - return myChildren.count(); + return 1; // one column ("Name") is provided by default } /*! - Returns the index of the specified object in the child list or -1. + \brief Get column title. + + This method can be re-implemented in the subclasses. + Default implementation returns the title for the first column ("Name") only. + + \param index column index + \return title of the specified column */ +QString SUIT_DataObject::columnTitle( const int index ) const +{ + if ( index == NameIdx ) + return QObject::tr( "NAME_COLUMN" ); // one column ("Name") is provided by default + return QString(); +} -int SUIT_DataObject::childPos( const SUIT_DataObject* obj ) const +/*! + \brief Get column icon. + + This method can be re-implemented in the subclasses. + Default implementation returns null pixmap. + + \param index column index + \return icon of the specified column +*/ +QPixmap SUIT_DataObject::columnIcon( const int /*index*/ ) const { - int res = -1; + return QPixmap(); +} - int i = 0; - for ( DataObjectList::const_iterator it = myChildren.begin(); it != myChildren.end() && res == -1; ++it, i++ ) - { - if ( *it == obj ) - res = i; - } +/*! + \brief Check if the column should appear in the tree view header popup menu + (to show/hide the column). + + Default implementation returns \c false because 'Name' column should be + always visible. + + \param index column index + \return \c true if the column can be shown/hidden +*/ +bool SUIT_DataObject::appropriate( const int /*index*/ ) const +{ + return false; +} - return res; +/*! + \brief Get the number of the child objects. + \return number of the children +*/ +int SUIT_DataObject::childCount() const +{ + return myChildren.count(); } /*! - Returns the child object with specified index. + \brief Get the index of the specified object in the child list. + \param obj child object + \return subobject position or -1 if it does not belong to this object */ +int SUIT_DataObject::childPos( const SUIT_DataObject* obj ) const +{ + return myChildren.indexOf( (SUIT_DataObject*)obj ); +} +/*! + \brief Get child object by the specified index. + \param idx child object index + \return child object or 0 if index is out of range +*/ SUIT_DataObject* SUIT_DataObject::childObject( const int idx ) const { SUIT_DataObject* child = 0; - if ( idx>= 0 && idx < (int)myChildren.count() ) - { - SUIT_DataObject* that = (SUIT_DataObject*)this; - child = that->myChildren.at( idx ); - } + if ( idx >= 0 && idx < myChildren.count() ) + child = myChildren.at( idx ); return child; } /*! - Returns the level of the object in the data tree. - 0 means that object is top-level. -*/ + \brief Get the object level in the tree structure. + Root object has level 0. + + \return object level. +*/ int SUIT_DataObject::level() const { int lev = 0; @@ -160,45 +223,67 @@ int SUIT_DataObject::level() const } /*! - Returns the next data object in the child list of the parent. + \brief Get the position of the data object in its parent's children list + \return data object position */ +int SUIT_DataObject::position() const +{ + return myParent ? myParent->childPos( this ) : 0; +} +/*! + \brief Get the next sibling data object in the children list. + \return child object or 0 if there is no next sibling + \sa prevBrother() +*/ SUIT_DataObject* SUIT_DataObject::nextBrother() const { return myParent ? myParent->childObject( myParent->childPos( this ) + 1 ) : 0; } /*! - Returns the previous data object in the child list of the parent. + \brief Get the previous sibling data object in the children list. + \return child object or 0 if there is no previous sibling + \sa nextBrother() */ - SUIT_DataObject* SUIT_DataObject::prevBrother() const { return myParent ? myParent->childObject( myParent->childPos( this ) - 1 ) : 0; } /*! - Returns 'true' if the object will delete children during destroying + \brief Get "auto-delete children" flag. + \return \c true if the object should delete all its children on destroying + \sa setAutoDeleteChildren() */ - bool SUIT_DataObject::autoDeleteChildren() const { return myAutoDel; } /*! - Specify should the object delete children during destroying -*/ + \brief Set "auto-delete children" flag. + + If this flag is on (default), the object will delete + all its children on destroying. + \param on new flag value + \sa autoDeleteChildren() +*/ void SUIT_DataObject::setAutoDeleteChildren( const bool on ) { myAutoDel = on; } /*! - Returns the list of the child objects. if 'rec' is 'true' then function get all sub children. -*/ + \brief Get all children. + If parameter \a rec is \c true then function collects all + the children recursively. + + \param lst returning list of children + \param rec if \c true collect all children recursively +*/ void SUIT_DataObject::children( DataObjectList& lst, const bool rec ) const { for ( DataObjectList::const_iterator it = myChildren.begin(); it != myChildren.end(); ++it ) @@ -210,9 +295,15 @@ void SUIT_DataObject::children( DataObjectList& lst, const bool rec ) const } /*! - Returns the list of the child objects. if 'rec' is 'true' then function get all sub children. -*/ + \brief Get all children. + \override + + If parameter \a rec is \c true then function collects all + the children recursively. + \param rec if \c true collect all children recursively + \return list of children +*/ DataObjectList SUIT_DataObject::children( const bool rec ) { DataObjectList lst; @@ -221,45 +312,56 @@ DataObjectList SUIT_DataObject::children( const bool rec ) } /*! - Append new child object to the end of the children list + \brief Add new child object to the end of the children list. + \param obj child object being added */ - -void SUIT_DataObject::appendChild( SUIT_DataObject* theObj ) +void SUIT_DataObject::appendChild( SUIT_DataObject* obj ) { - insertChild( theObj, myChildren.count() ); + insertChild( obj, myChildren.count() ); } /*! - Insert new child object to the children list at specified position + \brief Insert new child object to the list of the children. + \param obj child object being added + \param position child position */ - -void SUIT_DataObject::insertChild( SUIT_DataObject* theObj, int thePosition ) +void SUIT_DataObject::insertChild( SUIT_DataObject* obj, int position ) { - if ( !theObj || myChildren.contains( theObj ) ) + if ( !obj || myChildren.contains( obj ) ) return; - int pos = thePosition < 0 ? myChildren.count() : thePosition; - myChildren.insert( qMin( pos, (int)myChildren.count() ), theObj ); - theObj->setParent( this ); + int pos = position < 0 ? myChildren.count() : position; + myChildren.insert( qMin( pos, (int)myChildren.count() ), obj ); + obj->setParent( this ); + signal()->emitInserted( obj, this ); } /*! - Removes the specified child object reference. + \brief Remove the specified child object reference. + \param obj child object being removed + \param del if \c true, the child object is destroyed */ - -void SUIT_DataObject::removeChild( SUIT_DataObject* theObj ) +void SUIT_DataObject::removeChild( SUIT_DataObject* obj, const bool del ) { - if ( !theObj ) + if ( !obj ) return; - if ( myChildren.removeAll( theObj ) ) - theObj->setParent( 0 ); + if ( myChildren.removeAll( obj ) ) { + signal()->emitRemoved( obj, this ); + obj->setParent( 0 ); + + if ( del ) + obj->deleteLater(); + } } /*! - Replaces the specified child object by another object. + \brief Replace the specified child object by another object. + \param src child object being replaced + \param trg new child object + \param del if \c true, the previous object is destroyed + \return \c true if the object has been replaced */ - bool SUIT_DataObject::replaceChild( SUIT_DataObject* src, SUIT_DataObject* trg, const bool del ) { if ( !src || !trg ) @@ -286,9 +388,9 @@ bool SUIT_DataObject::replaceChild( SUIT_DataObject* src, SUIT_DataObject* trg, } /*! - Transfer the all children from specified object 'obj' to self. + \brief Change the parent for all children from specified object to this one. + \param obj object which children to be reparented */ - void SUIT_DataObject::reparentChildren( const SUIT_DataObject* obj ) { if ( !obj ) @@ -301,170 +403,291 @@ void SUIT_DataObject::reparentChildren( const SUIT_DataObject* obj ) } /*! - Set the parent object. Remove itself from current parent children - and append itself to the new parent children list. + \brief Get the parent object. + \return parent object or 0 if this is top-level item */ +SUIT_DataObject* SUIT_DataObject::parent() const +{ + return myParent; +} -void SUIT_DataObject::setParent( SUIT_DataObject* theParent ) +/*! + \brief Change the parent object. + \param p new parent object +*/ +void SUIT_DataObject::setParent( SUIT_DataObject* p ) { - if ( theParent == parent() ) + if ( p == parent() ) return; if ( parent() ) parent()->removeChild( this ); - myParent = theParent; + myParent = p; if ( parent() ) parent()->appendChild( this ); } /*! - Returns the parent object. -*/ + \brief Get data object name. -SUIT_DataObject* SUIT_DataObject::parent() const + This method should be re-implemented in the subclasses. + Default implementation returns null string. + + \return object name +*/ +QString SUIT_DataObject::name() const { - return myParent; + return QString(); } - /*! - Connect to signal destroyed( SUIT_DataObject* ). -*/ + \brief Get object text data for the specified column. -bool SUIT_DataObject::connect( QObject* reciever, const char* slot ) -{ - if ( !reciever || !slot ) - return false; + This method can be re-implemented in the subclasses. + Default implementation returns null string. - if ( !mySignal ) - mySignal = new Signal( this ); + Column with \a index = 0 (NameIdx) is supposed to be used + to get the object name (as it does the default implementation). - QObject::disconnect( mySignal, SIGNAL( destroyed( SUIT_DataObject* ) ), reciever, slot ); - return QObject::connect( mySignal, SIGNAL( destroyed( SUIT_DataObject* ) ), reciever, slot ); + \param index column index + \return object text data +*/ +QString SUIT_DataObject::text( const int index ) const +{ + return index == NameIdx ? name() : QString(); } /*! - Disconnect from signal destroyed( SUIT_DataObject* ). -*/ + \brief Get data object icon for the specified column. -bool SUIT_DataObject::disconnect( QObject* reciever, const char* slot ) -{ - if ( !reciever || !slot ) - return false; + This method can be re-implemented in the subclasses. + Default implementation returns null pixmap. - if ( !mySignal ) - return true; + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). - return QObject::disconnect( mySignal, SIGNAL( destroyed( SUIT_DataObject* ) ), reciever, slot ); + \param index column index + \return object icon for the specified column +*/ +QPixmap SUIT_DataObject::icon( const int /*index*/ ) const +{ + return QPixmap(); } /*! - Returns object name -*/ + \brief Get data object color for the specified column. -void SUIT_DataObject::deleteLater() + This method can be re-implemented in the subclasses. + Default implementation returns null color. + + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). + + \param role color role + \param index column index + \return object color for the specified column +*/ +QColor SUIT_DataObject::color( const ColorRole /*role*/, const int /*index*/ ) const { - if ( !mySignal ) - mySignal = new Signal( this ); - - mySignal->emitSignal(); - mySignal->deleteLater(); + return QColor(); } /*! - Returns object name -*/ + \brief Get data object tooltip for the specified column. -QString SUIT_DataObject::name() const + This method can be re-implemented in the subclasses. + Default implementation returns null string. + + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). + + \param index column index + \return object tooltip for the specified column +*/ +QString SUIT_DataObject::toolTip( const int /*index*/ ) const { - return QString::null; + return QString(); } /*! - Returns object icon -*/ + \brief Get data object status tip for the specified column. + + This method can be re-implemented in the subclasses. + Default implementation returns null string. + + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). -QPixmap SUIT_DataObject::icon() const + \param index column index + \return object status tip for the specified column +*/ +QString SUIT_DataObject::statusTip( const int /*index*/ ) const { - return QPixmap(); + return QString(); } /*! - Returns object text -*/ + \brief Get data object "what's this" information for the + specified column. -QString SUIT_DataObject::text( const int ) const + This method can be re-implemented in the subclasses. + Default implementation returns null string. + + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). + + \param index column index + \return object "what's this" information for the specified column +*/ +QString SUIT_DataObject::whatsThis( const int /*index*/ ) const { - return QString::null; + return QString(); } /*! - Returns object color -*/ + \brief Get data object font for the specified column. -QColor SUIT_DataObject::color( const ColorRole ) const + This method can be re-implemented in the subclasses. + Default implementation returns application default font. + + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). + + \param index column index + \return object font for the specified column +*/ +QFont SUIT_DataObject::font( const int /*index*/ ) const { - return QColor(); + return QFont(); } /*! - Returns object tool tip -*/ + \brief Get data object text alignment for the specified column. + + This method can be re-implemented in the subclasses. + Default implementation returns default alignment which + is Qt:AlignLeft. -QString SUIT_DataObject::toolTip() const + The parameter \a index specifies the column number + (to display, for example, in the tree view widget). + + \param index column index + \return object text alignment flags for the specified column +*/ +int SUIT_DataObject::alignment( const int /*index*/ ) const { - return QString::null; + return Qt::AlignLeft; } /*! - Returns 'true' if it is possible to drag this object -*/ + \brief Check if the object is draggable. + + This method can be re-implemented in the subclasses. + Default implementation returns \c false (all objects could not be dragged). + \return \c true if it is possible to drag this object +*/ bool SUIT_DataObject::isDragable() const { return false; } /*! - Returns 'true' if it is possible to drop an object "obj" to this object. + \brief Check if the drop operation fo this object is possible. + + This method can be re-implemented in the subclasses. + Default implementation returns \c false (drop operation is not allowed). + + \param obj object being dropped + \return \c true if it is possible to drop an object \c obj + to this object */ -bool SUIT_DataObject::isDropAccepted( SUIT_DataObject* ) +bool SUIT_DataObject::isDropAccepted( SUIT_DataObject* /*obj*/ ) { return false; } /*! - Returns type of check possibility. -*/ + \brief Check if this object is enabled. -SUIT_DataObject::CheckType SUIT_DataObject::checkType() const + This method can be re-implemented in the subclasses. + Default implementation returns \c true (all objects are enabled). + + \return \c true if the user can interact with the item +*/ +bool SUIT_DataObject::isEnabled() const { - return None; + return true; } /*! - Returns the checked state of the object. + \brief Check if this object is selectable. + + This method can be re-implemented in the subclasses. + Default implementation returns \c true (all objects are selectable). + + \return \c true if the item can be selected */ +bool SUIT_DataObject::isSelectable() const +{ + return true; +} + +/*! + \brief Check if this object is checkable for the specified column. -bool SUIT_DataObject::isOn() const + This method can be re-implemented in the subclasses. + Default implementation returns \c false (all objects are not checkable). + + \param index column index + \return \c true if the item can be checked or unchecked by the user + \sa isOn(), setOn() +*/ +bool SUIT_DataObject::isCheckable( const int /*index*/ ) const { - return myCheck; + return false; } /*! - Sets the checked state of the object. + \brief Get the checked state of the object (if it is checkable) + for the specified column. + + Default implementation supports the checked state for the first + ("Name") column only. + + \param index column index + \return checked state of the object for the specified column + \sa setOn(), isCheckable() */ +bool SUIT_DataObject::isOn( const int index ) const +{ + return index == NameIdx && myCheck; +} + +/*! + \brief Set the checked state of the object (if it is checkable) + for the specified column. + + Default implementation supports the checked state for the first + ("Name") column only. -void SUIT_DataObject::setOn( const bool on ) + \param on new checked state of the object for the specified column + \param index column index + \sa isOn(), isCheckable() +*/ +void SUIT_DataObject::setOn( const bool on, const int index ) { - myCheck = on; + if ( index == NameIdx ) + myCheck = on; } /*! - \return the opened state of the object (used in Object Browser). + \brief Get the "opened" state of the object. + \return "opened" state of the object + \sa setOpen() */ bool SUIT_DataObject::isOpen() const { @@ -472,7 +695,9 @@ bool SUIT_DataObject::isOpen() const } /*! - Sets the opened state of the object (used in Object Browser). + \brief Set the "opened" state of the object. + \param on new "opened" state of the object + \sa isOpen() */ void SUIT_DataObject::setOpen( const bool on ) { @@ -480,62 +705,264 @@ void SUIT_DataObject::setOpen( const bool on ) } /*! - Returns object personal indentification key. + \brief Check if the specified column supports custom sorting. + + This method can be re-implemented in the subclasses. + Default implementation returns false ("Name" column does not require + custom sorting). + + \param index column index + \return \c true if column sorting should be customized + \sa compare() */ +bool SUIT_DataObject::customSorting( const int /*index*/ ) const +{ + return false; +} + +/*! + \brief Compares data from two items for sorting purposes. + + This method can be re-implemented in the subclasses. + Default implementation returns false ("Name" column does not require + custom sorting). + + This method is called only for those columns for which customSorting() + method returns \c true. + \param left first data to compare + \param right second data to compare + \param index column index + \return result of the comparison + \sa customSorting() +*/ +bool SUIT_DataObject::compare( const QVariant& /*left*/, const QVariant& /*right*/, + const int /*index*/ ) const +{ + return false; +} + +/*! + \brief Get the object unique indentification key. + + This method can be re-implemented in the subclasses. + Default implementation returns 0. + + \return object key +*/ SUIT_DataObjectKey* SUIT_DataObject::key() const { return 0; } /*! - Dump this data object and its children to cout + \brief Get global signal handler. + \return the only instance of the signal handler +*/ +SUIT_DataObject::Signal* SUIT_DataObject::signal() +{ + if ( !mySignal ) + mySignal = new Signal(); + return mySignal; +} + +/*! + \brief Connect to the signal handlerx + \param sig signal name + \param reciever signal receiver object + \param slot slot name + \return \c true if connection is successfull +*/ +bool SUIT_DataObject::connect( const char* sig, QObject* reciever, const char* slot ) +{ + if ( !reciever || !slot ) + return false; + + signal()->disconnect( signal(), sig, reciever, slot ); + return signal()->connect( signal(), sig, reciever, slot ); +} + +/*! + \brief Disconnect from the signal handler + \param sig signal name + \param reciever signal receiver object + \param slot slot name + \return \c true if disconnection is successfull +*/ +bool SUIT_DataObject::disconnect( const char* sig, QObject* reciever, const char* slot ) +{ + if ( !reciever || !slot ) + return false; + return signal()->disconnect( signal(), sig, reciever, slot ); +} + +/*! + \brief Schedule this object for the late deleting. + + The object will be deleted when control returns to the event loop. + Note that entering and leaving a new event loop (e.g., by opening + a modal dialog) will not perform the deferred deletion; for the object + to be deleted, the control must return to the event loop from which + deleteLater() was called. +*/ +void SUIT_DataObject::deleteLater() +{ + if ( parent() ) + parent()->removeChild( this, false ); // to avoid infinite loop! + signal()->deleteLater( this ); +} + +/*! + \brief Dump the object tree recursively to the standard output. + \param indent current indentation level */ void SUIT_DataObject::dump( const int indent ) const { - QString strIndent = QString().fill( ' ', indent ); // indentation string + QString strIndent = QString().fill( ' ', indent ); // indentation string printf( "%s%s\n", strIndent.toLatin1().data(), name().toLatin1().data() ); for ( DataObjectList::const_iterator it = myChildren.begin(); it != myChildren.end(); ++it ) (*it)->dump( indent + 2 ); } /*! - Class: SUIT_DataObject::Signal [Internal] + \class SUIT_DataObject::Signal + \brief Watcher class, responsible for the emitting signals on behalf of + the data objects. + + SUIT_DataObject class does not inherit from QObject for the performance + reasons, so it can not use signals/slots mechanism directly. + Instead it uses the only Signal object to emit the signals when the data + object is created, destroyed, inserted to the parent object or removed + from it. + + If some object needs to handle, for example, data object destroying, it can + use SUIT_DataObject::signal() method to connect the signal: + \code + MyHandler* h = new MyHandler(); + h->connect( SUIT_DataObject::signal(), SIGNAL(destroyed(SUIT_DataObject*)), + h, SLOT(onDestroyed(SUIT_DataObject*)) ); + \endcode + The same can be done by using static method SUIT_DataObject::connect(). + For example, + \code + MyHandler* h = new MyHandler(); + SUIT_DataObject::connect( SIGNAL(destroyed(SUIT_DataObject*)), + h, SLOT(onDestroyed(SUIT_DataObject*))); + \endcode */ -SUIT_DataObject::Signal::Signal( SUIT_DataObject* o ) -: QObject(), -myOwner( o ) +/*! + \brief Constructor. +*/ +SUIT_DataObject::Signal::Signal() +: QObject() { } /*! - Destructor. + \brief Destructor. + + Destroys data object which are scheduled for the deleting with the deleteLater(). */ SUIT_DataObject::Signal::~Signal() { - SUIT_DataObject* o = myOwner; - myOwner = 0; - if ( o ) - { - o->mySignal = 0; - delete o; + for ( DataObjectList::Iterator it = myDelLaterObjects.begin(); + it != myDelLaterObjects.end(); ++it ) { + delete *it; } + myDelLaterObjects.clear(); } /*! - Set owner \a o. + \brief Emit signal about data object creation. + \param object data object being created */ -void SUIT_DataObject::Signal::setOwner( SUIT_DataObject* o ) +void SUIT_DataObject::Signal::emitCreated( SUIT_DataObject* object ) { - myOwner = o; + if ( object ) + emit created( object ); } /*! - emit signal destroed owner. + \brief Emit signal about data object destroying. + \param object data object being destroyed */ -void SUIT_DataObject::Signal::emitSignal() +void SUIT_DataObject::Signal::emitDestroyed( SUIT_DataObject* object ) { - if ( myOwner ) - emit destroyed( myOwner ); + if ( object ) { + if ( myDelLaterObjects.contains( object ) ) + // object is being destroyed after calling deleteLater(): + // the signal has been already emitted from deleteLater() + // we should avoid repeating of the object destroying from + // the Signal destructor + myDelLaterObjects.removeAll( object ); + else + // object is being destroyed directly or via deleteLater() + emit destroyed( object ); + } } + +/*! + \brief Emit signal about data object adding to the parent data object. + \param object data object being added + \param parent parent data object +*/ +void SUIT_DataObject::Signal::emitInserted( SUIT_DataObject* object, SUIT_DataObject* parent ) +{ + emit( inserted( object, parent ) ); +} + +/*! + \brief Emit signal about data object removed from the parent data object. + \param object data object being removed + \param parent parent data object +*/ +void SUIT_DataObject::Signal::emitRemoved( SUIT_DataObject* object, SUIT_DataObject* parent ) +{ + emit( removed( object, parent ) ); +} + +/*! + \brief Schedule data object for the late deleting. + \param object data object to be deleted later +*/ +void SUIT_DataObject::Signal::deleteLater( SUIT_DataObject* object ) +{ + if ( !myDelLaterObjects.contains( object ) ) { + emitDestroyed( object ); + myDelLaterObjects.append( object ); + } +} + +/*! + \brief Updates necessary internal fields of data object +*/ +void SUIT_DataObject::update() +{ +} + +/*! + \fn void SUIT_DataObject::Signal::created( SUIT_DataObject* object ); + \brief Emitted when data object is created. + \param object data object being created +*/ + +/*! + \fn void SUIT_DataObject::Signal::destroyed( SUIT_DataObject* object ); + \brief Emitted when data object is destroyed. + \param object data object being destroyed +*/ + +/*! + \fn void SUIT_DataObject::Signal::inserted( SUIT_DataObject* object, SUIT_DataObject* parent ); + \brief Emitted when data object is inserted to the parent data object. + \param object data object being created + \param parent parent data object +*/ + +/*! + \fn void SUIT_DataObject::Signal::removed( SUIT_DataObject* object, SUIT_DataObject* parent ); + \brief Emitted when data object is removed from the parent data object. + \param object data object being removed + \param parent parent data object +*/ diff --git a/src/SUIT/SUIT_DataObject.h b/src/SUIT/SUIT_DataObject.h index 1f479192c..40916cf03 100755 --- a/src/SUIT/SUIT_DataObject.h +++ b/src/SUIT/SUIT_DataObject.h @@ -16,6 +16,9 @@ // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SUIT_DataObject.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// #ifndef SUIT_DATAOBJECT_H #define SUIT_DATAOBJECT_H @@ -26,6 +29,7 @@ #include #include #include +#include class SUIT_DataObject; class SUIT_DataObjectKey; @@ -36,18 +40,25 @@ typedef QList DataObjectList; #pragma warning( disable:4251 ) #endif -/*! - \class SUIT_DataObject - Data Object represents uniform data tree structure recommended to use in SUIT-based applications - Many of standard classes (DataModel,ObjectBrowser) deal with SUIT_DataObjects -*/ class SUIT_EXPORT SUIT_DataObject { public: class Signal; - typedef enum { None, RadioButton, CheckBox } CheckType; - typedef enum { Text, Base, Foreground, Background, Highlight, HighlightedText } ColorRole; + //! Color role + typedef enum { + Text, //!< editor foreground (text) color + Base, //!< editor background color + Foreground, //!< foreground (text) color + Background, //!< background color + Highlight, //!< highlight background color + HighlightedText //!< highlighted foreground (text) color + } ColorRole; + + //! Column index + enum { + NameIdx //!< name column + }; SUIT_DataObject( SUIT_DataObject* = 0 ); virtual ~SUIT_DataObject(); @@ -56,10 +67,16 @@ public: SUIT_DataObject* lastChild() const; SUIT_DataObject* firstChild() const; + virtual int columnCount() const; + virtual QString columnTitle( const int = NameIdx ) const; + virtual QPixmap columnIcon( const int = NameIdx ) const; + virtual bool appropriate( const int = NameIdx ) const; + int childCount() const; int childPos( const SUIT_DataObject* ) const; SUIT_DataObject* childObject( const int ) const; int level() const; + int position() const; SUIT_DataObject* nextBrother() const; SUIT_DataObject* prevBrother() const; @@ -71,81 +88,96 @@ public: virtual DataObjectList children( const bool = false ); void appendChild( SUIT_DataObject* ); - virtual void removeChild( SUIT_DataObject* ); - virtual void insertChild( SUIT_DataObject*, int thePosition ); + virtual void insertChild( SUIT_DataObject*, int ); + virtual void removeChild( SUIT_DataObject*, const bool = false ); bool replaceChild( SUIT_DataObject*, SUIT_DataObject*, const bool = false ); void reparentChildren( const SUIT_DataObject* ); - virtual QString text( const int ) const; - virtual QColor color( const ColorRole ) const; - - virtual QString name() const; - virtual QPixmap icon() const; - virtual QString toolTip() const; - virtual SUIT_DataObject* parent() const; virtual void setParent( SUIT_DataObject* ); + virtual QString name() const; + virtual QString text( const int = NameIdx ) const; + virtual QPixmap icon( const int = NameIdx ) const; + virtual QColor color( const ColorRole, const int = NameIdx ) const; + virtual QString toolTip( const int = NameIdx ) const; + virtual QString statusTip( const int = NameIdx ) const; + virtual QString whatsThis( const int = NameIdx ) const; + virtual QFont font( const int = NameIdx ) const; + virtual int alignment( const int = NameIdx ) const; + virtual bool isDragable() const; virtual bool isDropAccepted( SUIT_DataObject* obj ); - virtual CheckType checkType() const; + virtual bool isEnabled() const; + virtual bool isSelectable() const; + virtual bool isCheckable( const int = NameIdx ) const; - virtual bool isOn() const; - virtual void setOn( const bool ); + virtual bool isOn( const int = NameIdx ) const; + virtual void setOn( const bool, const int = NameIdx ); virtual bool isOpen() const; virtual void setOpen( const bool ); + virtual void update(); + virtual bool customSorting( const int = NameIdx ) const; + virtual bool compare( const QVariant&, const QVariant&, + const int = NameIdx ) const; + virtual SUIT_DataObjectKey* key() const; - bool connect( QObject*, const char* ); - bool disconnect( QObject*, const char* ); + static Signal* signal(); + static bool connect( const char*, QObject*, const char* ); + static bool disconnect( const char*, QObject*, const char* ); void deleteLater(); - + void dump( const int indent = 2 ) const; // dump to cout private: SUIT_DataObject* myParent; bool myOpen; bool myCheck; - Signal* mySignal; bool myAutoDel; DataObjectList myChildren; + static Signal* mySignal; + friend class SUIT_DataObject::Signal; friend class SUIT_DataObjectIterator; }; -/*! - \class SUIT_DataObject::Signal - Auxiliary class providing functionality to use signals of data object state change - SUIT_DataObject cannot have signals, because it isn't QObject, but - methods connect/disconnect of SUIT_DataObject with help of this it is possible - to emulate Qt signal processing -*/ -class SUIT_DataObject::Signal : public QObject +class SUIT_EXPORT SUIT_DataObject::Signal : public QObject { Q_OBJECT public: - Signal( SUIT_DataObject* ); + Signal(); virtual ~Signal(); - void emitSignal(); - void setOwner( SUIT_DataObject* o ); +private: + void emitCreated( SUIT_DataObject* ); + void emitDestroyed( SUIT_DataObject* ); + void emitInserted( SUIT_DataObject*, SUIT_DataObject* ); + void emitRemoved( SUIT_DataObject*, SUIT_DataObject* ); + + void deleteLater( SUIT_DataObject* ); signals: - void destroyed( SUIT_DataObject* ); + void created( SUIT_DataObject* ); + void destroyed( SUIT_DataObject* ); + void inserted( SUIT_DataObject*, SUIT_DataObject* ); + void removed( SUIT_DataObject*, SUIT_DataObject* ); + + friend class SUIT_DataObject; private: - SUIT_DataObject* myOwner; + DataObjectList myDelLaterObjects; }; #ifdef WIN32 #pragma warning( default:4251 ) #endif -#endif +#endif // SUIT_DATAOBJECT_H diff --git a/src/SUIT/SUIT_TreeModel.cxx b/src/SUIT/SUIT_TreeModel.cxx new file mode 100755 index 000000000..48f486896 --- /dev/null +++ b/src/SUIT/SUIT_TreeModel.cxx @@ -0,0 +1,1387 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library 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 2.1 of the License. +// +// This library 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 Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File: SUIT_TreeModel.cxx +// Author: Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// + +#include "SUIT_TreeModel.h" +#include "SUIT_TreeSync.h" +#include "SUIT_DataObject.h" + +#include + +/*! + \class SUIT_TreeModel::TreeItem + \brief Internal class used for tree view synchronizaton with data object tree. + \internal +*/ + +class SUIT_TreeModel::TreeItem +{ +public: + TreeItem( SUIT_DataObject* obj, TreeItem* parent = 0, TreeItem* after = 0 ); + ~TreeItem(); + + void insertChild( TreeItem* child, TreeItem* after = 0 ); + void removeChild( TreeItem* child ); + SUIT_DataObject* dataObject() const; + TreeItem* parent() const; + int position() const; + int childCount() const; + TreeItem* child( const int i ); + QList children() const; + TreeItem* nextSibling() const; + TreeItem* prevSibling() const; + +private: + TreeItem* myParent; + QList myChildren; + SUIT_DataObject* myObj; +}; + +/*! + \brief Constructor. + \internal + \param obj data object + \param parent parent item + \param after tree item after each this one should be inserted +*/ +SUIT_TreeModel::TreeItem::TreeItem( SUIT_DataObject* obj, + SUIT_TreeModel::TreeItem* parent, + SUIT_TreeModel::TreeItem* after ) +: myParent( parent ), + myObj( obj ) +{ + // Add to the parent's children list + if ( myParent ) + myParent->insertChild( this, after ); +} + +/*! + \brief Destructor. Deletes all child items recursively. + \internal +*/ +SUIT_TreeModel::TreeItem::~TreeItem() +{ + // Ensure that all children are deleted; + // each child removes itself from the children list + while( myChildren.count() ) + delete myChildren.at( 0 ); + + // Remove this item from the parent's children list + if ( myParent ) + myParent->removeChild( this ); +} + +/*! + \brief Insert child item. + \internal + \param child child item being inserted + \param after tree item after each \a child should be inserted +*/ +void SUIT_TreeModel::TreeItem::insertChild( SUIT_TreeModel::TreeItem* child, + SUIT_TreeModel::TreeItem* after ) +{ + if ( !child ) + return; + + int index = after ? myChildren.indexOf( after ) + 1 : 0; + myChildren.insert( index, child ); +} + +/*! + \brief Remove child item. + \internal + \param child child item being removed +*/ +void SUIT_TreeModel::TreeItem::removeChild( SUIT_TreeModel::TreeItem* child ) +{ + if ( !child ) + return; + myChildren.removeAll( child ); +} + +/*! + \brief Get data object. + \internal + \return data object this item is associated to +*/ +SUIT_DataObject* SUIT_TreeModel::TreeItem::dataObject() const +{ + return myObj; +} + +/*! + \brief Get parent item. + \internal + \return parent item +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::TreeItem::parent() const +{ + return myParent; +} + +/*! + \brief Get position of this item in its parent's children list. + \internal + \return item position +*/ +int SUIT_TreeModel::TreeItem::position() const +{ + return myParent->myChildren.indexOf( (TreeItem*)this ); +} + +/*! + \brief Get number of child items. + \internal + \return number of children +*/ +int SUIT_TreeModel::TreeItem::childCount() const +{ + return myChildren.count(); +} + +/*! + \brief Get child item by specified index. + \internal + \param i child item index + \return child item or 0 if \a i is out of range +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::TreeItem::child( const int i ) +{ + return i >= 0 && i < myChildren.count() ? myChildren.at( i ) : 0; +} + +/*! + \brief Get all child items. + \internal + \return list of child items +*/ +QList SUIT_TreeModel::TreeItem::children() const +{ + return myChildren; +} + +/*! + \brief Get next sibling item. + \internal + \return next sibling item or 0 if there are no any +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::TreeItem::nextSibling() const +{ + return parent() ? parent()->child( position()+1 ) : 0; +} + +/*! + \brief Get previous sibling item. + \internal + \return previous sibling item or 0 if there are no any +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::TreeItem::prevSibling() const +{ + return parent() ? parent()->child( position()-1 ) : 0; +} + +/*! + \class SUIT_TreeModel::TreeSync + \brief Functor class for synchronizing data tree and tree model + when the data tree is changed outside the model. + \internal +*/ + +class SUIT_TreeModel::TreeSync +{ +public: + TreeSync( SUIT_TreeModel* ); + bool isEqual( const ObjPtr&, const ItemPtr& ) const; + ObjPtr nullSrc() const; + ItemPtr nullTrg() const; + ItemPtr createItem( const ObjPtr&, const ItemPtr&, const ItemPtr& ) const; + void updateItem( const ObjPtr&, const ItemPtr& ) const; + void deleteItemWithChildren( const ItemPtr& ) const; + QList children( const ObjPtr& ) const; + QList children( const ItemPtr& ) const; + ItemPtr parent( const ItemPtr& ) const; +private: + bool needUpdate( const ItemPtr& ) const; + SUIT_TreeModel* myModel; +}; + +/*! + \brief Constructor. + \internal + \param model tree model +*/ +SUIT_TreeModel::TreeSync::TreeSync( SUIT_TreeModel* model ) +: myModel( model ) +{ +} + +/*! + \brief Check if item corresponds to the specified data object. + \internal + \param obj data object + \param item tree item + \return \c true if item corresponds to the data object +*/ +bool SUIT_TreeModel::TreeSync::isEqual( const ObjPtr& obj, const ItemPtr& item ) const +{ + bool isRoot = obj == myModel->root() && item == myModel->rootItem(), + isEq = obj && item && item->dataObject() == obj; + return isRoot || ( !obj && !item ) || isEq; +} + +/*! + \brief Get null data object. + \internal + \return null data object +*/ +SUIT_TreeModel::ObjPtr SUIT_TreeModel::TreeSync::nullSrc() const +{ + return 0; +} + +/*! + \brief Get null tree item. + \internal + \return null tree item +*/ +SUIT_TreeModel::ItemPtr SUIT_TreeModel::TreeSync::nullTrg() const +{ + return 0; +} + +/*! + \brief Create an item corresponding to the specified data object. + \internal + \param obj data object + \param parent parent tree item + \param after tree item after each new one should be inserted + \return created item +*/ +SUIT_TreeModel::ItemPtr SUIT_TreeModel::TreeSync::createItem( const ObjPtr& obj, + const ItemPtr& parent, + const ItemPtr& after ) const +{ + ItemPtr item = myModel ? myModel->createItem( obj, parent, after ) : 0; + + // Additional actions that can't be performed by the model, e.g. expanded state + if( item ) + obj->update(); + return item; +} + +/*! + \brief Update tree item. + \internal + \param obj reference data object + \param item tree item to be updated +*/ +void SUIT_TreeModel::TreeSync::updateItem( const ObjPtr& obj, const ItemPtr& item ) const +{ + if( obj ) + obj->update(); + if ( item && needUpdate( item ) ) + myModel->updateItem( item ); +} + +/*! + \brief Delete item with all children recursively. + \internal + \param item tree item +*/ +void SUIT_TreeModel::TreeSync::deleteItemWithChildren( const ItemPtr& item ) const +{ + // NOTE: item is deleted inside removeItem()! + myModel->removeItem( item ); +} + +/*! + \brief Get all the children of the specified data object. + \internal + \param obj data object + \return list of the children +*/ +QList SUIT_TreeModel::TreeSync::children( const ObjPtr& obj ) const +{ + QList ch; + if ( obj ) + ch = obj->children(); + return ch; +} + +/*! + \brief Get all the children of the specified tree item. + \internal + \param item tree item + \return list of the children +*/ +QList SUIT_TreeModel::TreeSync::children( const ItemPtr& item ) const +{ + QList ch; + if ( item ) + ch = item->children(); + return ch; +} + +/*! + \brief Get item which is the parent for the specified item. + \internal + \param item tree item + \return parent item +*/ +SUIT_TreeModel::ItemPtr SUIT_TreeModel::TreeSync::parent( const ItemPtr& item ) const +{ + return item ? item->parent() : 0; +} + +/*! + \brief Check if the tree item needs updating. + \internal + \param item tree item to be checked + \return \c true if item needs updating + + \todo finalize this method +*/ +bool SUIT_TreeModel::TreeSync::needUpdate( const ItemPtr& item ) const +{ + bool update = false; + if ( item ) { + SUIT_DataObject* obj = item->dataObject(); + if ( obj ) { + // TODO: find simplified way to check if an item is not up-to-date: + // - use check-sum of various item data + // - use "LastModified" time stamp in data objects and tree items - hardly possible, for sometimes data objects do not know that data changes... + // ... + update = true; // TEMPORARY!!! + // 1. check text +/* update = ( item->text( 0 ) != obj->name() ) || myBrowser->needToUpdateTexts( item ); + + if ( !update ) { + // 2. check pixmap (compare serialNumber()-s) + QPixmap objPix = obj->icon(); + const QPixmap* itemPix = item->pixmap( 0 ); + update = ( objPix.isNull() && ( itemPix && !itemPix->isNull() ) ) || + ( !objPix.isNull() && ( !itemPix || itemPix->isNull() ) ); + if ( !update && !objPix.isNull() && itemPix && !itemPix->isNull() ) { + int aIconW = objPix.width(); + if( aIconW > 20 ) { + QWMatrix aM; + double aScale = 20.0 / aIconW; + aM.scale( aScale, aScale ); + objPix = objPix.xForm( aM ); + } + update = ( objPix.serialNumber() != itemPix->serialNumber() ); + } + }*/ + } + } + return update; +} + +/*! + \class SUIT_TreeModel + \brief Implementation of the model/view API based on the tree of SUIT_DataObject class + instances. + + The SUIT_TreeModel class does not support insertion/removal of rows. It is synchronized + automatically with the tree of data objects used by SUIT-based applications to + expose their data in a hierarchical form to the user. +*/ + +/*! + \brief Constructor. + \param parent parent object +*/ +SUIT_TreeModel::SUIT_TreeModel( QObject* parent ) +: QAbstractItemModel( parent ), + myRoot( 0 ), + myRootItem( 0 ), + myAutoDeleteTree( false ), + myAutoUpdate( true ) +{ + initialize(); +} + +/*! + \brief Constructor. + \param root root data object + \param parent parent object +*/ +SUIT_TreeModel::SUIT_TreeModel( SUIT_DataObject* root, QObject* parent ) +: QAbstractItemModel( parent ), + myRoot( root ), + myRootItem( 0 ), + myAutoDeleteTree( false ), + myAutoUpdate( true ) +{ + initialize(); +} + +/*! + \brief Destructor +*/ +SUIT_TreeModel::~SUIT_TreeModel() +{ + if ( autoDeleteTree() ) { + SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::disconnect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + delete myRoot; + } + + delete myRootItem; +} + +/*! + \brief Get data tree root object. + \return data tree root + \sa setRoot() +*/ +SUIT_DataObject* SUIT_TreeModel::root() const +{ + return myRoot; +} + +/*! + \brief Set data tree root object. + \param r new data tree root + \sa root() +*/ +void SUIT_TreeModel::setRoot( SUIT_DataObject* r ) +{ + if ( root() == r ) + return; + + if ( autoDeleteTree() ) { + SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::disconnect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + delete myRoot; + } + + myRoot = r; + + //initialize(); + reset(); +} + +/*! + \brief Get data for the specified model index and data role. + \param index model index + \param role data role + \return requested data + \sa setData() +*/ +QVariant SUIT_TreeModel::data( const QModelIndex& index, int role ) const +{ + if ( !index.isValid() ) + return QVariant(); + + SUIT_DataObject* obj = object( index ); + + QColor c; + QVariant val; + + if ( obj ) { + switch ( role ) { + case DisplayRole: + // data object text for the specified column + val = obj->text( index.column() ); + break; + case DecorationRole: + // data object icon for the specified column + val = obj->icon( index.column() ); + break; + case ToolTipRole: + // data object tooltip for the specified column + val = obj->toolTip( index.column() ); + break; + case StatusTipRole: + // data object status tip for the specified column + val = obj->statusTip( index.column() ); + break; + case WhatsThisRole: + // data object what's this info for the specified column + val = obj->whatsThis( index.column() ); + break; + case FontRole: + // data object font for the specified column + val = obj->font( index.column() ); + break; + case TextAlignmentRole: + // data object text alignment for the specified column + val = obj->alignment( index.column() ); + break; + case BackgroundRole: + // data background color for the specified column + c = obj->color( SUIT_DataObject::Background, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::Base ); + c.setAlpha( 0 ); + val = c; + break; + case ForegroundRole: + // data foreground (text) color for the specified column + c = obj->color( SUIT_DataObject::Foreground, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::Foreground ); + val = c; + break; + case BaseColorRole: + // editor background color for the specified column + c = obj->color( SUIT_DataObject::Base, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::Base ); + val = c; + break; + case TextColorRole: + // editor foreground (text) color for the specified column + c = obj->color( SUIT_DataObject::Text, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::Text ); + val = c; + break; + case HighlightRole: + // adta object highlighted background color for the specified column + c = obj->color( SUIT_DataObject::Highlight, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::Highlight ); + val = c; + break; + case HighlightedTextRole: + // data object highlighted foreground (text) color for the specified column + c = obj->color( SUIT_DataObject::HighlightedText, index.column() ); + if ( !c.isValid() ) // default value + c = QApplication::palette().color( QPalette::HighlightedText ); + val = c; + break; + case CheckStateRole: + // data object checked state for the specified column + // NOTE! three-state check is not supported currently + if ( obj->isCheckable( index.column() ) ) + val = obj->isOn( index.column() ) ? Qt::Checked : Qt::Unchecked; + break; + case SizeHintRole: + // data size hint + // NOTE! not supported currently + break; + default: + break; + } // ... switch ( role ) ... + } // ... if ( obj ) ... + return val; +} + +/*! + \brief Set data for the specified model index and data role. + \param index model index + \param value new data value + \param role data role + \return \c true if data is set + \sa data() +*/ +bool SUIT_TreeModel::setData( const QModelIndex& index, + const QVariant& value, int role ) +{ + if ( index.isValid() && value.isValid() ) { + SUIT_DataObject* obj = object( index ); + if ( obj ) { + // NOTE! only 'check state' data is supported by default + switch ( role ) { + case CheckStateRole: + // checked state + if ( obj->isCheckable( index.column() ) ) { + obj->setOn( value.toBool(), index.column() ); + emit( dataChanged( index, index ) ); + return true; + } + break; + default: + break; + } + } + } + return QAbstractItemModel::setData( index, value, role ); +} + +/*! + \brief Get data flags for specified model index. + \param index model index + \return data flags +*/ +Qt::ItemFlags SUIT_TreeModel::flags( const QModelIndex& index ) const +{ + if ( !index.isValid() ) + return 0; + + SUIT_DataObject* obj = object( index ); + Qt::ItemFlags f = 0; + + if ( obj ) { + // data object is enabled + if ( obj->isEnabled() ) + f = f | Qt::ItemIsEnabled; + + // data object is selectable + if ( obj->isSelectable() ) + f = f | Qt::ItemIsSelectable; + + // data object is checkable + if ( obj->isCheckable( index.column() ) ) + f = f | Qt::ItemIsUserCheckable; + } + return f; +} + +/*! + \brief Get header data (can be used in any data view). + \param column column number + \param orientation header orientation + \param role data role + \return header data +*/ +QVariant SUIT_TreeModel::headerData( int column, Qt::Orientation orientation, + int role ) const +{ + QVariant d; + // NOTE! only horizontal header is supported + if ( root() && orientation == Qt::Horizontal ) { + switch ( role ) { + case DisplayRole: + // column title + d = root()->columnTitle( column ); + break; + case DecorationRole: + // column icon + d = root()->columnIcon( column ); + break; + case AppropriateRole: + // appropriate flag (can column be hidden via context popup menu) + d = root()->appropriate( column ); + break; + default: + break; + } + } + return d; +} + +/*! + \brief Create model index. + \param row data row + \param column data column + \param parent parent model index + \return model index +*/ +QModelIndex SUIT_TreeModel::index( int row, int column, + const QModelIndex& parent ) const +{ + if ( hasIndex( row, column, parent ) ) { + TreeItem* parentItem = treeItem( parent ); + if ( parentItem ) { + TreeItem* childItem = parentItem->child( row ); + if ( childItem ) + return createIndex( row, column, childItem ); + } + } + return QModelIndex(); +} + +/*! + \brief Get parent model index. + \param index model index + \return parent model index +*/ +QModelIndex SUIT_TreeModel::parent( const QModelIndex& index ) const +{ + if ( !index.isValid() ) + return QModelIndex(); + + TreeItem* childItem = treeItem( index ); + TreeItem* parentItem = childItem ? childItem->parent() : 0; + + if ( !parentItem || parentItem == rootItem() ) + return QModelIndex(); + + return createIndex( parentItem->position(), 0, parentItem ); +} + +/*! + \brief Get number of data columns. + \param parent parent model index (not used) + \return data columns number + \sa rowCount() +*/ +int SUIT_TreeModel::columnCount( const QModelIndex& /*parent*/ ) const +{ + return root() ? root()->columnCount() : 0; +} + +/*! + \brief Get number of data rows (children of the specified model index). + \param parent parent model index + \return data rows (children) number + \sa columnCount() +*/ +int SUIT_TreeModel::rowCount( const QModelIndex& parent ) const +{ + if ( parent.column() > 0 ) + return 0; + + TreeItem* parentItem = treeItem( parent ); + + return parentItem ? parentItem->childCount() : 0; +} + +/*! + \brief Get data object by the specified model index. + \param index model index + \return data object corresponding to the model index +*/ +SUIT_DataObject* SUIT_TreeModel::object( const QModelIndex& index ) const +{ + return object( treeItem( index ) ); +} + +/*! + \brief Get model index by the specified data object. + \param obj data object + \param column data object column + \return model index +*/ +QModelIndex SUIT_TreeModel::index( const SUIT_DataObject* obj, int column ) const +{ + if ( obj == root() ) + return QModelIndex(); + + TreeItem* item = treeItem( obj ); + + return item ? createIndex( item->position(), column, item ) : QModelIndex(); +} + +/*! + \brief Get 'auto-delete data tree' flag value. + \return 'auto-delete data tree' flag value + \sa setAutoDeleteTree() +*/ +bool SUIT_TreeModel::autoDeleteTree() const +{ + return myAutoDeleteTree; +} + +/*! + \brief Set 'auto-delete data tree' flag value. + + If this flag is set to \c true, the data tree is deleted when + the tree model is destroyed. Default value for this flag is \c false. + + \param on 'auto-delete data tree' flag value + \sa autoDeleteTree() +*/ +void SUIT_TreeModel::setAutoDeleteTree( const bool on ) +{ + myAutoDeleteTree = on; +} + +/*! + \brief Get 'auto-update tree' flag value. + \return 'auto-update tree' flag value + \sa setAutoUpdate(), updateTree() +*/ +bool SUIT_TreeModel::autoUpdate() const +{ + return myAutoUpdate; +} + +/*! + \brief Set 'auto-update tree' flag value. + + If this flag is set to \c true (by default), the model is updated + automatically when data tree is changed. + + \param on 'auto-update tree' flag value + \sa autoUpdate(), updateTree() +*/ +void SUIT_TreeModel::setAutoUpdate( const bool on ) +{ + if ( myAutoUpdate == on ) + return; + + SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::disconnect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + myAutoUpdate = on; + + if ( myAutoUpdate ) { + SUIT_DataObject::connect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::connect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + + updateTree(); + } +} + + +/*! + \brief Check if the specified column supports custom sorting. + \param column column index on which data is being sorted + \return \c true if column requires custom sorting + \sa lessThan() +*/ +bool SUIT_TreeModel::customSorting( const int column ) const +{ + return root() ? root()->customSorting( column ) : false; +} + +/*! + \brief Compares two model indexes for the sorting purposes. + + This method is called only for those columns for which customSorting() + method returns \c true. + + \param left first index to compare + \param right second index to compare + \return result of the comparison + \sa customSorting() +*/ +bool SUIT_TreeModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const +{ + QVariant ldata = data( left ); + QVariant rdata = data( right ); + return root() ? root()->compare( ldata, rdata, left.column() ) : false; +} + +/*! + \brief Get item delegate for the model. + \return new item delegate +*/ +QAbstractItemDelegate* SUIT_TreeModel::delegate() const +{ + return new SUIT_ItemDelegate( const_cast( this ) ); +} + +/*! + \brief Update tree model. + + Call this method when data tree is changed outside the model. + If the 'auto-update' flag is set to \c true, the model + is updated automatically when the data tree is changed. + + \param index starting index for the updating + \sa setAutoUpdate() +*/ +void SUIT_TreeModel::updateTree( const QModelIndex& index ) +{ + updateTree( object( index ) ); +} + +/*! + \brief Update tree model. + + Call this method when data tree is changed outside the model. + If the 'auto-update' flag is set to \c true, the model + is updated automatically when the data tree is changed. + + \param obj starting data object for the updating + \sa setAutoUpdate() +*/ +void SUIT_TreeModel::updateTree( SUIT_DataObject* obj ) +{ + if ( !obj ) + obj = root(); + + else if ( obj->root() != root() ) + return; + + synchronize( obj, + treeItem( obj ), + SUIT_TreeModel::TreeSync( this ) ); +} + +/*! + \brief Initialize tree model. +*/ +void SUIT_TreeModel::initialize() +{ + SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::disconnect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + if ( autoUpdate() ) { + SUIT_DataObject::connect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) ); + SUIT_DataObject::connect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ), + this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) ); + } + + myItems.clear(); // ????? is it really necessary + + if ( !myRootItem ) + myRootItem = new TreeItem( 0 ); + + updateTree(); +} + +/*! + \brief Get root tree item. + \return root tree item +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::rootItem() const +{ + return myRootItem; +} + +/*! + \brief Get tree item corresponding to the specified model index. + \param index model index + \return tree item or root item if index is invalid +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::treeItem( const QModelIndex& index ) const +{ + return index.isValid() ? static_cast( index.internalPointer() ) : rootItem(); +} + +/*! + \brief Get tree item corresponding to the specified data object. + \param obj data object + \return tree item or 0 if there is no tree item corresponding to \a obj +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::treeItem( const SUIT_DataObject* obj ) const +{ + TreeItem* item = 0; + + if ( obj == root() ) + item = rootItem(); + else if ( myItems.contains( const_cast( obj ) ) ) + item = myItems[ const_cast( obj ) ]; + + return item; +} + +/*! + \brief Get data object corresponding to the specified tree item. + \param item tree item + \return data object or 0 if there is no data object corresponding to \a item +*/ +SUIT_DataObject* SUIT_TreeModel::object( const SUIT_TreeModel::TreeItem* item ) const +{ + if ( item == rootItem() ) + return root(); + + SUIT_DataObject* obj = item ? item->dataObject() : 0; + return myItems.contains( obj ) ? obj : 0; +} + +/*! + \brief Create an item corresponding to the data object. + \param obj source data object + \param parent parent tree item + \param after tree item after which new item should be inserted + \return created tree item or 0 if item could not be created +*/ +SUIT_TreeModel::TreeItem* SUIT_TreeModel::createItem( SUIT_DataObject* obj, + SUIT_TreeModel::TreeItem* parent, + SUIT_TreeModel::TreeItem* after ) +{ + if ( !obj ) + return 0; + + SUIT_DataObject* parentObj = object( parent ); + QModelIndex parentIdx = index( parentObj ); + + SUIT_DataObject* afterObj = after ? object( after ) : 0; + int row = afterObj ? afterObj->position() + 1 : 0; + + beginInsertRows( parentIdx, row, row ); + + myItems[ obj ] = new TreeItem( obj, parent, after ); + + endInsertRows(); + + return myItems[ obj ]; +} + +/*! + \brief Update tree item. + \param item tree item to be updated +*/ +void SUIT_TreeModel::updateItem( SUIT_TreeModel::TreeItem* item ) +{ + if ( !item ) + return; + + SUIT_DataObject* obj = object( item ); + if ( !obj ) + return; + + // update all columns corresponding to the given data object + QModelIndex firstIdx = index( obj, 0 ); + QModelIndex lastIdx = index( obj, obj->columnCount() - 1 ); + emit dataChanged( firstIdx, lastIdx ); +} + +/*! + \brief Remove tree item (recursively). + \param item tree item to be removed +*/ +void SUIT_TreeModel::removeItem( SUIT_TreeModel::TreeItem* item ) +{ + if ( !item ) + return; + + // Remove list view items from recursively for all children. + // Otherwise, "delete item" line below will destroy all item's children, + // and will contain invalid pointers + while( item->childCount() ) + removeItem( item->child( 0 ) ); + + SUIT_DataObject* obj = object( item ); + + // Warning! obj can be deleted at this point! + + SUIT_DataObject* parentObj = object( item->parent() ); + QModelIndex parentIdx = index( parentObj, 0 ); + int row = item->position(); + + beginRemoveRows( parentIdx, row, row ); + myItems.remove( obj ); + + if ( obj == root() ) + setRoot( 0 ); + else if ( item->parent() ) + item->parent()->removeChild( item ); + + delete item; + + endRemoveRows(); +} + +/*! + \brief Called when the data object is inserted to the tree. + \param object data object being inserted + \param parent parent data object +*/ +void SUIT_TreeModel::onInserted( SUIT_DataObject* /*object*/, SUIT_DataObject* parent ) +{ + if ( autoUpdate() ) + updateTree( parent ); +} + +/*! + \brief Called when the data object is removed from the tree. + \param object data object being removed + \param parent parent data object +*/ +void SUIT_TreeModel::onRemoved( SUIT_DataObject* /*object*/, SUIT_DataObject* parent ) +{ + if ( autoUpdate() ) + updateTree( parent ); +} + +/*! + \class SUIT_ProxyModel + \brief Proxy model which can be used above the SUIT_TreeMovel class + to enable custom sorting/filtering of the data. + + The SUIT_TreeModel class does not support custom sorting/filtering of the data. + To use these features, the SUIT_ProxyModel class can be used as top-level + wrapper for the SUIT_DataObject-based data tree model. +*/ + +/*! + \brief Constructor. + \param parent parent object +*/ +SUIT_ProxyModel::SUIT_ProxyModel( QObject* parent ) +: QSortFilterProxyModel( parent ), + mySortingEnabled( true ) +{ + setSourceModel( new SUIT_TreeModel( this ) ); +} + +/*! + \brief Constructor. + \param root root data object + \param parent parent object +*/ +SUIT_ProxyModel::SUIT_ProxyModel( SUIT_DataObject* root, QObject* parent ) +: QSortFilterProxyModel( parent ), + mySortingEnabled( true ) +{ + setSourceModel( new SUIT_TreeModel( root, this ) ); +} + +/*! + \brief Constructor. + \param model tree model + \param parent parent object +*/ +SUIT_ProxyModel::SUIT_ProxyModel( SUIT_TreeModel* model, QObject* parent ) +: QSortFilterProxyModel( parent ), + mySortingEnabled( true ) +{ + setSourceModel( model ); +} + +/*! + \brief Destructor. +*/ +SUIT_ProxyModel::~SUIT_ProxyModel() +{ +} + +/*! + \brief Get data tree root object. + \return data tree root + \sa setRoot() +*/ +SUIT_DataObject* SUIT_ProxyModel::root() const +{ + return treeModel() ? treeModel()->root() : 0; +} + +/*! + \brief Set data tree root object. + \param r new data tree root + \sa root() +*/ +void SUIT_ProxyModel::setRoot( SUIT_DataObject* r ) +{ + if ( treeModel() ) + treeModel()->setRoot( r ); +} + +/*! + \brief Get data object by the specified model index. + \param index model index + \return data object corresponding to the model index +*/ +SUIT_DataObject* SUIT_ProxyModel::object( const QModelIndex& index ) const +{ + return treeModel() ? treeModel()->object( mapToSource( index ) ) : 0; +} + +/*! + \brief Get model index by the specified data object. + \param obj data object + \param column data object column + \return model index +*/ +QModelIndex SUIT_ProxyModel::index( const SUIT_DataObject* obj, int column ) const +{ + return treeModel() ? mapFromSource( treeModel()->index( obj, column ) ) : QModelIndex(); +} + +/*! + \brief Get 'auto-delete data tree' flag value. + \return 'auto-delete data tree' flag value + \sa setAutoDeleteTree() +*/ +bool SUIT_ProxyModel::autoDeleteTree() const +{ + return treeModel() ? treeModel()->autoDeleteTree() : false; +} + +/*! + \brief Set 'auto-delete data tree' flag value. + + If this flag is set to \c true, the data tree is deleted when + the tree model is destroyed. Default value for this flag is \c false. + + \param on 'auto-delete data tree' flag value + \sa autoDeleteTree() +*/ +void SUIT_ProxyModel::setAutoDeleteTree( const bool on ) +{ + if ( treeModel() ) + treeModel()->setAutoDeleteTree( on ); +} + +/*! + \brief Get 'auto-update tree' flag value. + \return 'auto-update tree' flag value + \sa setAutoUpdate(), updateTree() +*/ +bool SUIT_ProxyModel::autoUpdate() const +{ + return treeModel() ? treeModel()->autoUpdate() : false; +} + +/*! + \brief Set 'auto-update tree' flag value. + + If this flag is set to \c true (by default), the model is updated + automatically when data tree is changed. + + \param on 'auto-update tree' flag value + \sa autoUpdate(), updateTree() +*/ +void SUIT_ProxyModel::setAutoUpdate( const bool on ) +{ + if ( treeModel() ) + treeModel()->setAutoUpdate( on ); +} + +/*! + \brief Check if sorting is enabled. + \return \c true if sorting is enabled + \sa setSortingEnabled() +*/ +bool SUIT_ProxyModel::isSortingEnabled() const +{ + return mySortingEnabled; +} + +/*! + \brief Get item delegate for the model. + \return new item delegate +*/ +QAbstractItemDelegate* SUIT_ProxyModel::delegate() const +{ + return treeModel() ? treeModel()->delegate() : 0; +} + +/*! + \brief Update tree model. + + Call this method when data tree is changed outside the model. + If the 'auto-update' flag is set to \c true, the model + is updated automatically when the data tree is changed. + + \param index starting index for the updating + \sa setAutoUpdate() +*/ +void SUIT_ProxyModel::updateTree( const QModelIndex& index ) +{ + if ( treeModel() ) + treeModel()->updateTree( mapToSource( index ) ); +} + +/*! + \brief Update tree model. + + Call this method when data tree is changed outside the model. + If the 'auto-update' flag is set to \c true, the model + is updated automatically when the data tree is changed. + + \param obj starting data object for the updating + \sa setAutoUpdate() +*/ +void SUIT_ProxyModel::updateTree( SUIT_DataObject* obj ) +{ + if ( treeModel() ) + treeModel()->updateTree( obj ); +} + +/*! + \brief Compares two model indexes for the sorting purposes. + \param left first index to compare + \param right second index to compare + \return result of the comparison +*/ +bool SUIT_ProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const +{ + if ( !isSortingEnabled() && left.isValid() && right.isValid() ) { + return left.row() < right.row(); + } + if ( treeModel() && treeModel()->customSorting( left.column() ) ) { + return treeModel()->lessThan( left, right ); + } + return QSortFilterProxyModel::lessThan( left, right ); +} + +/*! + \brief Enable/disable sorting. + \param enabled new flag state + \sa isSortingEnabled() +*/ +void SUIT_ProxyModel::setSortingEnabled( bool enabled ) +{ + mySortingEnabled = enabled; + clear(); +} + +/* + \brief Get tree model. + \return tree model +*/ +SUIT_TreeModel* SUIT_ProxyModel::treeModel() const +{ + return dynamic_cast( sourceModel() ); +} + +/*! + \class SUIT_ItemDelegate + \brief An SUIT_DataObject-based item delegate class. + + This class can be used to render the SUIT_DataObject-based item + in the widgets like QTreeView and others. + Refer to the Qt 4 documentation, model/view architecture + section for more details). +*/ + +/*! + \brief Constructor. + \param parent parent object +*/ +SUIT_ItemDelegate::SUIT_ItemDelegate( QObject* parent ) +: QItemDelegate( parent ) +{ +} + +/*! + \brief Render the item in the widget. + + Customizes the item colors for the specific roles. + + \param painter painter + \param option painting option + \param index model index being rendered +*/ +void SUIT_ItemDelegate::paint( QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index ) const +{ + QStyleOptionViewItem opt = option; + if ( index.isValid() ) { + // Note: we check into account only custom roles; other roles are process + // correctly by the QItemDelegate class + QVariant val = index.data( SUIT_TreeModel::BaseColorRole ); + if ( val.isValid() && val.value().isValid() ) { + QColor aBase = val.value(); + aBase.setAlpha( 0 ); + opt.palette.setBrush( QPalette::Base, val.value() ); + } + val = index.data( SUIT_TreeModel::TextColorRole ); + if ( val.isValid() && val.value().isValid() ) + opt.palette.setBrush( QPalette::Text, val.value() ); + val = index.data( SUIT_TreeModel::HighlightRole ); + if ( val.isValid() && val.value().isValid() ) + opt.palette.setBrush( QPalette::Highlight, val.value() ); + val = index.data( SUIT_TreeModel::HighlightedTextRole ); + if ( val.isValid() && val.value().isValid() ) + opt.palette.setBrush( QPalette::HighlightedText, val.value() ); + } + QItemDelegate::paint( painter, opt, index ); +} diff --git a/src/SUIT/SUIT_TreeModel.h b/src/SUIT/SUIT_TreeModel.h new file mode 100755 index 000000000..89ca777e2 --- /dev/null +++ b/src/SUIT/SUIT_TreeModel.h @@ -0,0 +1,197 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library 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 2.1 of the License. +// +// This library 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 Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File: SUIT_TreeModel.h +// Author: Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// + +#ifndef SUIT_TREEMODEL_H +#define SUIT_TREEMODEL_H + +#include "SUIT.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#pragma warning( disable:4251 ) +#endif + +class SUIT_DataObject; +class SUIT_TreeModel; + +class SUIT_EXPORT SUIT_TreeModel : public QAbstractItemModel +{ + Q_OBJECT + +private: + class TreeItem; + class TreeSync; + typedef SUIT_DataObject* ObjPtr; + typedef SUIT_TreeModel::TreeItem* ItemPtr; + +public: + //! Data role + typedef enum { + DisplayRole = Qt::DisplayRole, //!< text label + DecorationRole = Qt::DecorationRole, //!< icon + EditRole = Qt::EditRole, //!< edit mode + ToolTipRole = Qt::ToolTipRole, //!< tooltip + StatusTipRole = Qt::StatusTipRole, //!< status tip + WhatsThisRole = Qt::WhatsThisRole, //!< what's this info + FontRole = Qt::FontRole, //!< font + TextAlignmentRole = Qt::TextAlignmentRole, //!< text alignment + BackgroundRole = Qt::BackgroundRole, //!< background color + ForegroundRole = Qt::ForegroundRole, //!< text color + CheckStateRole = Qt::CheckStateRole, //!< check state + SizeHintRole = Qt::SizeHintRole, //!< size hint + BaseColorRole = Qt::UserRole, //!< (editor) background color + TextColorRole, //!< (editor) text color (Qt::UserRole + 1) + HighlightRole, //!< highlight color (Qt::UserRole + 2) + HighlightedTextRole, //!< highlighted text color (Qt::UserRole + 3) + AppropriateRole = Qtx::AppropriateRole //!< appropriate flag (Qt::UserRole + 100) + } Role; + + SUIT_TreeModel( QObject* = 0 ); + SUIT_TreeModel( SUIT_DataObject*, QObject* = 0 ); + ~SUIT_TreeModel(); + + SUIT_DataObject* root() const; + void setRoot( SUIT_DataObject* ); + + virtual QVariant data( const QModelIndex&, int = DisplayRole ) const; + virtual bool setData( const QModelIndex&, const QVariant&, int = EditRole ); + virtual Qt::ItemFlags flags( const QModelIndex& ) const; + virtual QVariant headerData( int, Qt::Orientation, int = Qt::DisplayRole ) const; + + virtual QModelIndex index( int, int, const QModelIndex& = QModelIndex() ) const; + virtual QModelIndex parent( const QModelIndex& ) const; + + virtual int columnCount( const QModelIndex& = QModelIndex() ) const; + virtual int rowCount( const QModelIndex& = QModelIndex() ) const; + + SUIT_DataObject* object( const QModelIndex& = QModelIndex() ) const; + QModelIndex index( const SUIT_DataObject*, int = 0 ) const; + + bool autoDeleteTree() const; + void setAutoDeleteTree( const bool ); + + bool autoUpdate() const; + void setAutoUpdate( const bool ); + + virtual bool customSorting( const int ) const; + virtual bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; + + QAbstractItemDelegate* delegate() const; + +public slots: + virtual void updateTree( const QModelIndex& ); + virtual void updateTree( SUIT_DataObject* = 0 ); + +private: + void initialize(); + + TreeItem* rootItem() const; + TreeItem* treeItem( const QModelIndex& ) const; + TreeItem* treeItem( const SUIT_DataObject* ) const; + SUIT_DataObject* object( const TreeItem* ) const; + + TreeItem* createItem( SUIT_DataObject*, + TreeItem* = 0, + TreeItem* = 0 ); + void updateItem( TreeItem* ); + void removeItem( TreeItem* ); + +private slots: + void onInserted( SUIT_DataObject*, SUIT_DataObject* ); + void onRemoved( SUIT_DataObject*, SUIT_DataObject* ); + +private: + typedef QMap ItemMap; + + SUIT_DataObject* myRoot; + TreeItem* myRootItem; + ItemMap myItems; + bool myAutoDeleteTree; + bool myAutoUpdate; + + friend class SUIT_TreeModel::TreeSync; +}; + +class SUIT_EXPORT SUIT_ProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + SUIT_ProxyModel( QObject* = 0 ); + SUIT_ProxyModel( SUIT_DataObject*, QObject* = 0 ); + SUIT_ProxyModel( SUIT_TreeModel*, QObject* = 0 ); + ~SUIT_ProxyModel(); + + SUIT_DataObject* root() const; + void setRoot( SUIT_DataObject* ); + + SUIT_DataObject* object( const QModelIndex& = QModelIndex() ) const; + QModelIndex index( const SUIT_DataObject*, int = 0 ) const; + + bool autoDeleteTree() const; + void setAutoDeleteTree( const bool ); + + bool autoUpdate() const; + void setAutoUpdate( const bool ); + + bool isSortingEnabled() const; + + QAbstractItemDelegate* delegate() const; + +public slots: + virtual void updateTree( const QModelIndex& ); + virtual void updateTree( SUIT_DataObject* = 0 ); + void setSortingEnabled( bool ); + +protected: + virtual bool lessThan( const QModelIndex&, const QModelIndex& ) const; + +private: + SUIT_TreeModel* treeModel() const; + +private: + bool mySortingEnabled; +}; + +class SUIT_EXPORT SUIT_ItemDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + SUIT_ItemDelegate( QObject* = 0 ); + + virtual void paint( QPainter*, const QStyleOptionViewItem&, + const QModelIndex& ) const; +}; + +#ifdef WIN32 +#pragma warning( default:4251 ) +#endif + +#endif // SUIT_TREEMODEL_H diff --git a/src/SUIT/SUIT_TreeSync.h b/src/SUIT/SUIT_TreeSync.h index 929255b36..ccceeaa0c 100644 --- a/src/SUIT/SUIT_TreeSync.h +++ b/src/SUIT/SUIT_TreeSync.h @@ -16,252 +16,265 @@ // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SUIT_TreeSync.h +// Author : Alexander SOLOVYOV +// -#ifndef SUIT_TREE_SYNC_HEADER -#define SUIT_TREE_SYNC_HEADER +#ifndef SUIT_TREESYNC_H +#define SUIT_TREESYNC_H #include /*! - \struct DiffItem - \brief Struct representing difference between items + \brief The structure representing difference between source and destination items. + + The different combinations of source and target items values imply the different actions + to be performed in the target data tree: + - source item is null, target item is not null : the item should be removed from the target tree + - source item is not null, target item is null : new item should be added to the target tree + - both source and target items are not null : the target item can be updated if necessary + - both source and target items are null : error */ template struct DiffItem { - SrcItem mySrc; - /*! - \var mySrc - if it is null, then this item is to deleted - */ - TrgItem myTrg; - /*! - \var myTrg - if it is null, then this item is to added - if both fields aren't null, then this item is to update - */ + SrcItem mySrc; //!< source tree item + TrgItem myTrg; //!< target tree item }; -/*! - \brief synchronizes two trees -*/ + +// +// Function prototypes. +// + template TrgItem synchronize( const SrcItem&, const TrgItem&, const TreeData& ); -/*! - \brief compares children -*/ template -void diffSiblings( const SrcItem&, const TrgItem&, - QList < DiffItem < SrcItem,TrgItem > >&, - const TreeData& ); +QList< DiffItem > diffSiblings( const SrcItem&, + const TrgItem&, + const TreeData& ); -/*! - \brief create item with children (subtree) -*/ template -TrgItem createSubTree( const SrcItem&, const TrgItem&, const TrgItem&, const bool, const TreeData& ); +TrgItem createSubTree( const SrcItem&, const TrgItem&, const TrgItem&, const TreeData& ); -/*! - \brief find equal element in list -*/ template -const typename QList::const_iterator findEqual( const QList& l, +const typename QList::const_iterator findEqual( const SrcItem& it, const typename QList::const_iterator& first, - const SrcItem& it, + const typename QList::const_iterator& last, const TreeData& td ); - +// +// Function imlpementation. +// /*! - Synchronizes two trees by comparing corresponding items - \param r1 - start item from first tree - \param r2 - start item from second tree - \param td - auxiliary class providing following methods: -
    -
  • bool isEqual( const SrcItem&, const TrgItem& ) const - returns true if items are equal -
  • SrcItem nullSrc() const - returns null SrcItem -
  • TrgItem nullTrg() const - returns null TrgItem -
  • TrgItem createItem( -
      -
    1. const SrcItem& src, - corresponding SrcItem -
    2. const TrgItem& parent, - parent TrgItem -
    3. const TrgItem& after, - TrgItem after that new item must be added -
    4. const bool prepend - whether new item must be added as first -
    - ) const - creates new TrgItem -
  • void updateItem( const TrgItem& ) const - updates TrgItem without recreation -
  • void deleteItemWithChildren( const TrgItem& ) const - deletes TrgItem with all children -
  • void children( const SrcItem&, QList& ) const - fills list with children -
  • void children( const TrgItem&, QList& ) const - fills list with children -
  • SrcItem parent( const SrcItem& ) const - return parent SrcItem -
  • TrgItem parent( const TrgItem& ) const - return parent SrcItem -
+ \brief Synchronize two data trees by recurive comparing of the corresponding items. + + \param r1 starting item from the source data tree + \param r2 starting item from the target data tree + \param td functor class + \return the target tree item (updated or just created) corresponding to the starting + data object + + Actual comparing of the items and the syncronization of the trees is performed by the + functor class which is passed as the last parameter of the function. + The functor class should implement the following methods: + - \b bool \b isEqual( \b const \b SrcItem& \p src, \b const \b TrgItem& \p tgt ) \b const; + - \a src source tree item + - \a tgt target tree item + - compares items and returns \c true if the items are equal (correspond to each other) + - \b SrcItem \b nullSrc() \b const; + - returns null source tree itemm + - \b TrgItem \b nullTrg() \b const + - returns null target tree item + - \b TrgItem \b createItem( \b const \b SrcItem& \p src, \b const \b TrgItem& \p parent, \b const \b TrgItem& \p after ) \b const; + - \a src source item + - \a parent parent target item + - \a after target tree item after which new item shoud be inserted (if null, the item is added to the end) + - creates new ite in the target tree which correspond to the source item and returns created item + - \b void \b updateItem( \b const \b SrcItem& \p src, \b const \b TrgItem& \p tgt ) \b const; + - \a src source tree item + - \a tgt the item in the target tree to be updated + - updates target treeitem + - \b void \b deleteItemWithChildren( \b const \b TrgItem& \p tgt ) \b const; + - \a tgt the item in the target tree to be removed + - deletes target tree item (recursively) + - \b QList \b children( \b const \b SrcItem& \p parent ) \b const; + - \a parent the parent item in the source tree + - returns the child items list + - \b QList \b children( \b const \b TrgItem& \p parent ) \b const; + - \a parent the parent item in the target tree + - returns the child items list + - \b TrgItem \b parent( \b const \b TrgItem& \p tgt ) \b const; + - \a tgt target tree item + - returns the item which is parent for the specified source tree item */ template TrgItem synchronize( const SrcItem& r1, const TrgItem& r2, const TreeData& td ) { - if( td.isEqual( r1, r2 ) ) - { + if ( td.isEqual( r1, r2 ) ) { // update items themselves td.updateItem( r1, r2 ); - - // iterate 'siblings' (direct children) - QList< DiffItem< SrcItem, TrgItem > > d; - diffSiblings( r1, r2, d, td ); - + + // iterate through children + QList< DiffItem< SrcItem, TrgItem > > d = diffSiblings( r1, r2, td ); + typename QList< DiffItem< SrcItem, TrgItem > >::const_iterator anIt = d.begin(), aLast = d.end(); TrgItem lastItem = td.nullTrg(); - // TrgItem tail = td.nullTrg(); - for( ; anIt!=aLast; anIt++ ) - { + + for ( ; anIt != aLast; anIt++ ) { const DiffItem& item = *anIt; - if( item.mySrc==td.nullSrc() ) - if( item.myTrg==td.nullTrg() ) + if ( item.mySrc == td.nullSrc() ) { + if ( item.myTrg == td.nullTrg() ) qDebug( "error: both null" ); else - //to delete + // delete item td.deleteItemWithChildren( item.myTrg ); + } else { - if( item.myTrg==td.nullTrg() ) - { - //to add - TrgItem nitem = createSubTree( item.mySrc, r2, lastItem, lastItem==td.nullTrg(), td ); - if( nitem!=td.nullTrg() ) + if ( item.myTrg == td.nullTrg() ) { + // add item (recursively) + TrgItem nitem = createSubTree( item.mySrc, r2, lastItem, td ); + if ( nitem != td.nullTrg() ) lastItem = nitem; } - else - { - //to update - td.updateItem( item.mySrc, item.myTrg ); + else { + // update item synchronize( item.mySrc, item.myTrg, td ); lastItem = item.myTrg; } } } - return r2; } - else - { - TrgItem new_r2 = createSubTree( r1, td.parent( r2 ), r2, false, td ); - if( r2!=td.nullTrg() ) + else { + TrgItem new_r2 = td.nullTrg(); + if ( r1 != td.nullSrc() ) { + // add new item (recursively) + new_r2 = createSubTree( r1, td.parent( r2 ), r2, td ); + } + if ( r2 != td.nullTrg() ) { + // delete old one (if it is not null) td.deleteItemWithChildren( r2 ); + } return new_r2; } } /*! - Finds equal element in list - \return iterator - \param l - list to search - \param first - start iterator - \param it - item to be found - \param td - tree data object (provides auxiliary methods) + \brief Find the item in the target tree which correspond to the specified source tree item. + \param it source item for which correspondence is to be found + \param first iterator pointing to the item in the list \a l from which search shoud be started + \param last iterator pointing to the item in the list \a l the search to be finished at + \param td functor class + \return iterator pointing to the item in the list \l if the correspondence is found or iterator + \a last if the correspondence is not found + \sa synchronize() */ template -const typename QList::const_iterator findEqual( const QList& l, +const typename QList::const_iterator findEqual( const SrcItem& it, const typename QList::const_iterator& first, - const SrcItem& it, + const typename QList::const_iterator& last, const TreeData& td ) { - typename QList::const_iterator cur = first, last = l.end(); - for( ; cur!=last; cur++ ) - if( td.isEqual( it, *cur ) ) + typename QList::const_iterator cur = first; + for ( ; cur != last; cur++ ) { + if ( td.isEqual( it, *cur ) ) return cur; + } return last; } /*! - Compares children of objects src and trg - \param src - SrcItem to be checked - \param trg - TrgItem to be checked - \param d - map of difference to be filled - \param td - tree data object (provides auxiliary methods) + \brief Compare children of the source and target trees to find differences. + \param src parent source item + \param trg parent target item + \param td functor class + \return list of the differences + \sa synchronize() */ template -void diffSiblings( const SrcItem& src, const TrgItem& trg, - QList < DiffItem < SrcItem,TrgItem > >& d, - const TreeData& td ) +QList< DiffItem > diffSiblings( const SrcItem& src, const TrgItem& trg, + const TreeData& td ) { //if( src==td.nullSrc() || trg==td.nullTrg() ) // return; - - QList src_ch; - QList trg_ch; - td.children( src, src_ch ); - td.children( trg, trg_ch ); + + QList< DiffItem > d; + + QList src_ch = td.children( src ); + QList trg_ch = td.children( trg ); typename QList::const_iterator src_it = src_ch.begin(), src_last = src_ch.end(); - typename QList::const_iterator cur = trg_ch.begin(), trg_last = trg_ch.end(); + typename QList::const_iterator cur = trg_ch.begin(), trg_last = trg_ch.end(); - for( ; src_it!=src_last; src_it++ ) - { + for ( ; src_it != src_last; src_it++ ) { typename QList::const_iterator f = - findEqual( trg_ch, cur, *src_it, td ); - if( f!=trg_last ) //is found - { - //mark all items before found as "to be deleted" - for( typename QList::const_iterator it = cur; it!=f; it++ ) - { + findEqual( *src_it, cur, trg_last, td ); + if ( f != trg_last ) { + // target is found + // mark all items before found one as "to be deleted" + for ( typename QList::const_iterator it = cur; it != f; it++ ) { DiffItem ndiff; ndiff.mySrc = td.nullSrc(); - ndiff.myTrg = *it; //to delete; + ndiff.myTrg = *it; // delete item d.append( ndiff ); } cur = f; DiffItem ndiff; ndiff.mySrc = *src_it; - ndiff.myTrg = *cur; //update this item + ndiff.myTrg = *cur; // update this (found) item d.append( ndiff ); cur++; } - else //not found - { + else { + // target is not found DiffItem ndiff; ndiff.mySrc = *src_it; - ndiff.myTrg = td.nullTrg(); //add this item + ndiff.myTrg = td.nullTrg(); // add item d.append( ndiff ); } } - for( ; cur!=trg_last; cur++ ) - { + // delete rest items + for ( ; cur != trg_last; cur++ ) { DiffItem ndiff; ndiff.mySrc = td.nullSrc(); - ndiff.myTrg = *cur; //to delete; + ndiff.myTrg = *cur; // delete item d.append( ndiff ); } + + return d; } /*! - Creates sub-tree - \return root of just created sub-tree - \param src - corresponding SrcItem - \param parent - parent of new TrgItem - \param after - TrgItem, after that new item must be added - \param asFirst - true if TrgItem must be added as first - \param td - tree data object (provides auxiliary methods) + \brief Create an item with all its children recursively in the target tree. + \param src source tree item + \param parent parent item in the target tree + \param after item in the target tree after which new item shoud be inserted + \param td functor class + \return created item + \sa synchronize() */ template TrgItem createSubTree( const SrcItem& src, const TrgItem& parent, - const TrgItem& after, const bool asFirst, - const TreeData& td ) + const TrgItem& after, const TreeData& td ) { - if( src==td.nullSrc() ) + if ( src == td.nullSrc() ) return td.nullTrg(); - TrgItem nitem = td.createItem( src, parent, after, asFirst ); - if( nitem==td.nullTrg() ) + TrgItem nitem = td.createItem( src, parent, after ); + if ( nitem == td.nullTrg() ) return nitem; - QList ch; - td.children( src, ch ); + QList ch = td.children( src ); typename QList::const_iterator anIt = ch.begin(), aLast = ch.end(); - for( ; anIt!=aLast; anIt++ ) - createSubTree( *anIt, nitem, td.nullTrg(), false, td ); + TrgItem last = td.nullTrg(); + for( ; anIt != aLast; anIt++ ) + last = createSubTree( *anIt, nitem, last, td ); return nitem; } -#endif +#endif // SUIT_TREESYNC_H