-// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
//
-// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
-// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+// 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 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.
//
-// 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
//
-// 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
+// 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_Session.h"
#include "SUIT_TreeModel.h"
#include "SUIT_TreeSync.h"
#include "SUIT_DataObject.h"
+#include "SUIT_ResourceMgr.h"
#include <QApplication>
#include <QHash>
+#include <QMimeData>
-SUIT_AbstractModel::SUIT_AbstractModel()
+SUIT_AbstractModel::SUIT_AbstractModel() : mySearcher( 0 )
{
}
return dynamic_cast<const QObject*>( this );
}
+SUIT_DataSearcher* SUIT_AbstractModel::searcher() const
+{
+ return mySearcher;
+}
-
-
+void SUIT_AbstractModel::setSearcher( SUIT_DataSearcher* s )
+{
+ mySearcher = s;
+}
/*!
SUIT_DataObject* dataObject() const;
TreeItem* parent() const;
int position() const;
+ void setPosition(int position) {_position=position;};
int childCount() const;
TreeItem* child( const int i );
QList<TreeItem*> children() const;
TreeItem* myParent;
QList<TreeItem*> myChildren;
SUIT_DataObject* myObj;
+ int _position;
};
/*!
\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 )
+ SUIT_TreeModel::TreeItem* parent,
+ SUIT_TreeModel::TreeItem* after )
: myParent( parent ),
- myObj( obj )
+ myObj( obj ),
+ _position(-1)
{
// Add <this> to the parent's children list
if ( myParent )
\param after tree item after each \a child should be inserted
*/
void SUIT_TreeModel::TreeItem::insertChild( SUIT_TreeModel::TreeItem* child,
- SUIT_TreeModel::TreeItem* after )
+ SUIT_TreeModel::TreeItem* after )
{
if ( !child )
return;
- int index = after ? myChildren.indexOf( after ) + 1 : 0;
+ int index = after ? after->position() + 1 : 0;
myChildren.insert( index, child );
}
*/
int SUIT_TreeModel::TreeItem::position() const
{
- return myParent ? myParent->myChildren.indexOf( (TreeItem*)this ) : -1;
+ return _position;
}
/*!
\return created item
*/
SUIT_TreeModel::ItemPtr SUIT_TreeModel::TreeSync::createItem( const ObjPtr& obj,
- const ItemPtr& parent,
- const ItemPtr& after ) const
+ const ItemPtr& parent,
+ const ItemPtr& after ) const
{
ItemPtr item = myModel ? myModel->createItem( obj, parent, after ) : 0;
if( obj )
obj->update();
if ( item && needUpdate( item ) )
- myModel->updateItem( item );
+ myModel->updateItem( item, false );
}
/*!
/* 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() );
- }
+ // 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() );
+ }
}*/
}
}
myRoot( 0 ),
myRootItem( 0 ),
myAutoDeleteTree( false ),
- myAutoUpdate( true )
+ myAutoUpdate( true ),
+ myUpdateModified( false )
{
initialize();
}
myRoot( root ),
myRootItem( 0 ),
myAutoDeleteTree( false ),
- myAutoUpdate( true )
+ myAutoUpdate( true ),
+ myUpdateModified( false )
{
initialize();
}
{
if ( autoDeleteTree() ) {
SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onInserted( 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* ) ) );
+ this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
delete myRoot;
}
void SUIT_TreeModel::registerColumn( const int group_id, const QString& name, const int custom_id )
{
bool found = false;
- for( int i=0, n=myColumns.size(); i<n && !found; i++ )
- if( name==myColumns[i].myName )
- {
- myColumns[i].myIds.insert( group_id, custom_id );
- found = true;
- }
- if( !found )
- {
- ColumnInfo inf;
- inf.myName = name;
- inf.myIds.insert( group_id, custom_id );
- inf.myAppropriate = Qtx::Shown;
- int n = myColumns.size();
- myColumns.resize( n+1 );
- myColumns[n] = inf;
- reset();
+ for ( int i=0, n=myColumns.size(); i<n && !found; i++ ) {
+ if ( name == myColumns[i].myName ) {
+ myColumns[i].myIds.insert( group_id, custom_id );
+ found = true;
+ }
+ }
+ if ( !found ) {
+ ColumnInfo inf;
+ inf.myName = name;
+ inf.myIds.insert( group_id, custom_id );
+ inf.myAppropriate = Qtx::Shown;
+ inf.myHeaderFlags = Qtx::ShowAll;
+ int n = myColumns.size();
+ myColumns.resize( n+1 );
+ myColumns[n] = inf;
+ reset();
}
}
*/
void SUIT_TreeModel::unregisterColumn( const int group_id, const QString& name )
{
- for( int i=0, n=myColumns.size(); i<n; i++ )
- if( myColumns[i].myName==name )
- {
- myColumns[i].myIds.remove( group_id );
- if( myColumns[i].myIds.isEmpty() )
- {
- myColumns.remove( i );
- reset();
- }
- break;
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name ) {
+ myColumns[i].myIds.remove( group_id );
+ if ( myColumns[i].myIds.isEmpty() ) {
+ myColumns.remove( i );
+ reset();
+ }
+ break;
}
+ }
}
/*!
*/
void SUIT_TreeModel::setColumnIcon( const QString& name, const QPixmap& icon )
{
- for( int i=0, n=myColumns.size(); i<n; i++ )
- if( myColumns[i].myName==name )
- {
- myColumns[i].myIcon = icon;
- break;
- }
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name ) {
+ myColumns[i].myIcon = icon;
+ break;
+ }
+ }
}
/*!
QPixmap SUIT_TreeModel::columnIcon( const QString& name ) const
{
QPixmap res;
- for( int i=0, n=myColumns.size(); i<n; i++ )
- if( myColumns[i].myName==name )
- {
- res = myColumns[i].myIcon;
- break;
- }
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name ) {
+ res = myColumns[i].myIcon;
+ break;
+ }
+ }
return res;
}
*/
void SUIT_TreeModel::setAppropriate( const QString& name, const Qtx::Appropriate appr )
{
- for( int i=0, n=myColumns.size(); i<n; i++ )
- if( myColumns[i].myName==name )
- {
- myColumns[i].myAppropriate = appr;
- emit headerDataChanged( Qt::Horizontal, i, i );
- break;
- }
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name && myColumns[i].myAppropriate != appr ) {
+ myColumns[i].myAppropriate = appr;
+ emit headerDataChanged( Qt::Horizontal, i, i );
+ break;
+ }
+ }
}
/*!
Qtx::Appropriate SUIT_TreeModel::appropriate( const QString& name ) const
{
Qtx::Appropriate appr = Qtx::Shown;
- for( int i=0, n=myColumns.size(); i<n; i++ )
- if( myColumns[i].myName==name )
- {
- appr = myColumns[i].myAppropriate;
- break;
- }
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name ) {
+ appr = myColumns[i].myAppropriate;
+ break;
+ }
+ }
return appr;
}
+/*!
+ \brief Set header flags.
+
+ These flags allow show in the header of the column text (name of the column),
+ icon or both text and icon.
+
+ \param name - column name
+ \param flags - header flags
+
+*/
+void SUIT_TreeModel::setHeaderFlags( const QString& name, const Qtx::HeaderViewFlags flags )
+{
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name && myColumns[i].myHeaderFlags != flags ) {
+ myColumns[i].myHeaderFlags = flags;
+ emit headerDataChanged( Qt::Horizontal, i, i );
+ break;
+ }
+ }
+}
+
+/*!
+ \brief Get the header flags.
+
+ These flags allow show in the header of the column text (name of the column),
+ icon or both text and icon.
+
+ \param name - column name
+ \return header flags
+*/
+Qtx::HeaderViewFlags SUIT_TreeModel::headerFlags( const QString& name ) const
+{
+ Qtx::HeaderViewFlags flags;
+ for ( int i = 0, n = myColumns.size(); i < n; i++ ) {
+ if ( myColumns[i].myName == name ) {
+ flags = myColumns[i].myHeaderFlags;
+ break;
+ }
+ }
+ return flags;
+}
+
+/*!
+ \brief Set visibility state of the object.
+
+ \param id - column name
+ \param state - visible state
+*/
+void SUIT_TreeModel::setVisibilityState( const QString& id, Qtx::VisibilityState state )
+{
+ VisibilityMap::const_iterator it = myVisibilityMap.find( id );
+ if ( it != myVisibilityMap.end() && it.value() == state )
+ return;
+
+ bool needSignal = false;
+ if ( state != Qtx::UnpresentableState ) {
+ myVisibilityMap.insert( id, state );
+ needSignal = true;
+ }
+ else {
+ needSignal = myVisibilityMap.remove( id ) > 0;
+ }
+ if ( needSignal ) {
+ QModelIndexList lst;
+ if ( searcher() ) {
+ SUIT_DataObject* o = searcher()->findObject( id );
+ if ( o ) lst << index( o );
+ }
+ else {
+ lst = match( index( 0, root()->customData( Qtx::IdType ).toInt() ), DisplayRole, id, 1, Qt::MatchExactly | Qt::MatchRecursive );
+ }
+ if ( !lst.isEmpty() ) {
+ QModelIndex idx = index( lst.first().row(), SUIT_DataObject::VisibilityId, lst.first().parent() );
+ emit dataChanged( idx, idx );
+ }
+ }
+}
+
+/*!
+ \brief Set visibility state for all objects.
+
+ \param id - column name
+ \param state - visible state
+*/
+void SUIT_TreeModel::setVisibilityStateForAll( Qtx::VisibilityState state )
+{
+ if ( state != Qtx::UnpresentableState ) {
+ VisibilityMap::ConstIterator it = myVisibilityMap.begin();
+ while ( it != myVisibilityMap.end() ) {
+ if ( it.value() != state )
+ setVisibilityState( it.key(), state );
+ it++;
+ }
+ }
+ else {
+ QList<QString> anIds = myVisibilityMap.keys();
+ myVisibilityMap.clear();
+ QList<QString>::ConstIterator it = anIds.begin();
+ while ( it != anIds.end() ) {
+ QModelIndexList lst;
+ if ( searcher() ) {
+ SUIT_DataObject* o = searcher()->findObject( *it );
+ if ( o ) lst << index( o );
+ }
+ else {
+ lst = match( index( 0, root()->customData( Qtx::IdType ).toInt() ), DisplayRole, (*it), 1, Qt::MatchExactly | Qt::MatchRecursive );
+ }
+ if ( !lst.isEmpty() ) {
+ QModelIndex idx = index( lst.first().row(), SUIT_DataObject::VisibilityId ,lst.first().parent() );
+ emit dataChanged( idx, idx );
+ }
+ it++;
+ }
+ }
+}
+
+/*!
+ \brief Get visibility state of the object.
+
+ \param id - column name
+ \return visible state
+*/
+Qtx::VisibilityState SUIT_TreeModel::visibilityState( const QString& id ) const
+{
+ VisibilityMap::const_iterator it = myVisibilityMap.find( id );
+ return it != myVisibilityMap.end() ? it.value() : Qtx::UnpresentableState;
+}
+
/*!
\brief Get data tree root object.
\return data tree root
if ( autoDeleteTree() ) {
SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onInserted( 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* ) ) );
+ this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
delete myRoot;
}
return QVariant();
SUIT_DataObject* obj = object( index );
+ if ( !obj )
+ return QVariant();
QColor c;
QVariant val;
if ( obj )
{
switch ( role )
- {
+ {
case DisplayRole:
// data object text for the specified column
- val = obj->text( id );
+ val = obj->text( id );
break;
- case DecorationRole:
- // data object icon for the specified column
- val = obj->icon( id );
+ case EditRole:
+ // data object text for the specified column (for editor)
+ val = obj->text( id );
break;
+ case DecorationRole: {
+ // icon
+ if ( id == SUIT_DataObject::VisibilityId ) {
+ // for visibility column, icon is defined specifically (using data object id)
+ QString objId = objectId( index );
+ if ( myVisibilityMap.contains( objId ) ) {
+ // visibility status is defined -> return proper icon
+ SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+ val = ( myVisibilityMap.value( objId ) == Qtx::ShownState ) ?
+ resMgr->loadPixmap( "SUIT", tr( "ICON_DATAOBJ_VISIBLE" ) ) :
+ resMgr->loadPixmap( "SUIT", tr( "ICON_DATAOBJ_INVISIBLE" ) );
+ }
+ else {
+ // visibility status is undefined -> no icon
+ val = QIcon();
+ }
+ }
+ else {
+ // for other columns get icon from the object
+ val = obj->icon( id );
+ }
+ break;
+ }
case ToolTipRole:
// data object tooltip for the specified column
val = obj->toolTip( id );
// data background color for the specified column
c = obj->color( SUIT_DataObject::Background, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::Base );
+ c = QApplication::palette().color( QPalette::Base );
c.setAlpha( 0 );
val = c;
break;
// data foreground (text) color for the specified column
c = obj->color( SUIT_DataObject::Foreground, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::Foreground );
+ c = QApplication::palette().color( QPalette::Foreground );
val = c;
break;
case BaseColorRole:
// editor background color for the specified column
c = obj->color( SUIT_DataObject::Base, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::Base );
+ 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, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::Text );
+ 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, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::Highlight );
+ 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, id );
if( !c.isValid() ) // default value
- c = QApplication::palette().color( QPalette::HighlightedText );
+ 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( id ) )
- val = obj->isOn( id ) ? Qt::Checked : Qt::Unchecked;
+ val = obj->isOn( id ) ? Qt::Checked : Qt::Unchecked;
break;
case SizeHintRole:
// data size hint
\sa data()
*/
bool SUIT_TreeModel::setData( const QModelIndex& index,
- const QVariant& value, int role )
+ const QVariant& value, int role )
{
if ( index.isValid() && value.isValid() ) {
SUIT_DataObject* obj = object( index );
// 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 ) );
+ // checked state
+ if ( obj->isCheckable( index.column() ) ) {
+ obj->setOn( value.toBool(), index.column() );
+ emit( dataChanged( index, index ) );
+ return true;
+ }
+ break;
+ case EditRole: {
+ QString val = value.toString();
+ if ( !val.isEmpty() && obj->setName(val) ) {
+ emit( dataChanged( index, index ) );
return true;
}
- break;
+ return false;
+ break;
+ }
default:
- break;
+ break;
}
}
}
*/
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 ) {
+ if (!index.isValid())
+ //return Qt::ItemIsDropEnabled; // items can be dropped into the top level of the model
+ return f;
+
+ SUIT_DataObject* obj = object(index);
+
+ if (obj) {
// data object is enabled
- if ( obj->isEnabled() )
+ if (obj->isEnabled())
f = f | Qt::ItemIsEnabled;
// data object is selectable
- if ( obj->isSelectable() )
+ if (obj->isSelectable())
f = f | Qt::ItemIsSelectable;
// data object is checkable
- if ( obj->isCheckable( index.column() ) )
+ if (obj->isCheckable(index.column()))
f = f | Qt::ItemIsUserCheckable;
+
+ // data object can be renamed
+ if (obj->renameAllowed(index.column()))
+ f = f | Qt::ItemIsEditable;
+
+ // data object can be dragged
+ if (obj->isDraggable())
+ f = f | Qt::ItemIsDragEnabled;
+
+ // another data object(s) can be dropped on this one
+ if (obj->isDropAccepted())
+ f = f | Qt::ItemIsDropEnabled;
}
+
return f;
}
+Qt::DropActions SUIT_TreeModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
/*!
\brief Get header data (can be used in any data view).
\param column column number
if ( root() && orientation == Qt::Horizontal )
{
switch ( role )
- {
+ {
case DisplayRole:
// column title
- d = myColumns[column].myName;
+ if((myColumns[column].myHeaderFlags & Qtx::ShowText) ||
+ (myColumns[column].myHeaderFlags == Qtx::ShowAll))
+ d = myColumns[column].myName;
+ else
+ d = QString();
break;
case DecorationRole:
// column icon
- d = myColumns[column].myIcon;
+ if((myColumns[column].myHeaderFlags & Qtx::ShowIcon) ||
+ (myColumns[column].myHeaderFlags == Qtx::ShowAll))
+ d = myColumns[column].myIcon;
+ else
+ d = QIcon();
break;
case AppropriateRole:
// appropriate flag (can column be hidden via context popup menu)
\return model index
*/
QModelIndex SUIT_TreeModel::index( int row, int column,
- const QModelIndex& parent ) const
+ const QModelIndex& parent ) const
{
if( hasIndex( row, column, parent ) )
{
{
TreeItem* childItem = parentItem->child( row );
if( childItem )
- return createIndex( row, column, childItem );
+ return createIndex( row, column, childItem );
}
}
return QModelIndex();
*/
int SUIT_TreeModel::rowCount( const QModelIndex& parent ) const
{
- if ( parent.column() > 0 )
- return 0;
+ // Commented by rnv in the frame of the
+ // "20830: EDF 1357 GUI : Hide/Show Icon" imp
+ // if ( parent.column() > 0 )
+ // return 0;
TreeItem* parentItem = treeItem( parent );
return;
SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onInserted( 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* ) ) );
+ 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* ) ) );
+ this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) );
SUIT_DataObject::connect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
+ this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
updateTree();
}
}
+/*!
+ \brief Get 'updateModified' flag value.
+ \return 'updateModified' flag value
+*/
+bool SUIT_TreeModel::updateModified() const
+{
+ return myUpdateModified;
+}
+/*!
+ \brief Set 'updateModified' flag value.
+ \param on 'updateModified' flag value
+*/
+void SUIT_TreeModel::setUpdateModified(const bool on)
+{
+ myUpdateModified=on;
+}
/*!
\brief Check if the specified column supports custom sorting.
return root() ? root()->customSorting( column ) : false;
}
+void SUIT_TreeModel::forgetObject( const SUIT_DataObject* obj )
+{
+ removeItem( treeItem( obj ) );
+}
+
/*!
\brief Compares two model indexes for the sorting purposes.
return new SUIT_ItemDelegate( const_cast<SUIT_TreeModel*>( this ) );
}
+
+void SUIT_TreeModel::emitClicked( SUIT_DataObject* obj, const QModelIndex& index) {
+ int obj_group_id = obj->groupId();
+ const ColumnInfo& inf = myColumns[index.column()];
+
+ int id = -1;
+ if( inf.myIds.contains( 0 ) )
+ id = inf.myIds[0];
+ if( inf.myIds.contains( obj_group_id ) )
+ id = inf.myIds[obj_group_id];
+ emit clicked(obj, id);
+}
+
/*!
\brief Update tree model.
updateTree( object( index ) );
}
+
+void SUIT_TreeModel::updateTreeModel(SUIT_DataObject* obj,TreeItem* item)
+{
+ int kobj=0;
+ int kitem=0;
+ int nobjchild=obj->childCount();
+ SUIT_DataObject* sobj=obj->childObject(kobj);
+ TreeItem* sitem = item->child(kitem);
+
+ while(kobj < nobjchild)
+ {
+ if(sitem==0)
+ {
+ //end of item list
+ if(kitem==0)
+ sitem=createItemAtPos(sobj,item,0);
+ else
+ sitem=createItemAtPos(sobj,item,kitem);
+ updateTreeModel(sobj,sitem);
+ kobj++;
+ kitem++;
+ sobj=obj->childObject(kobj);
+ sitem = item->child(kitem);
+ }
+ else if(sitem->dataObject() != sobj)
+ {
+ if(treeItem(sobj))
+ {
+ // item : to remove
+ removeItem(sitem);
+ sitem = item->child(kitem);
+ }
+ else
+ {
+ // obj : new object
+ createItemAtPos(sobj,item,kitem);
+ kobj++;
+ kitem++;
+ sobj=obj->childObject(kobj);
+ sitem = item->child(kitem);
+ }
+ }
+ else
+ {
+ //obj and item are synchronised : go to next ones
+ updateTreeModel(sobj,sitem);
+ if(sobj->modified()) updateItem(sitem, true);
+ if( sobj ) sobj->update();
+ kobj++;
+ kitem++;
+ sobj=obj->childObject(kobj);
+ sitem = item->child(kitem);
+ }
+ }
+ //remove remaining items
+ for(int i = item->childCount(); i > kitem;i--)
+ {
+ sitem = item->child(i-1);
+ removeItem(sitem);
+ }
+}
+
/*!
\brief Update tree model.
else if ( obj->root() != root() )
return;
- synchronize<ObjPtr,ItemPtr,SUIT_TreeModel::TreeSync>( obj,
- treeItem( obj ),
- SUIT_TreeModel::TreeSync( this ) );
+ if(updateModified())
+ {
+ updateTreeModel(obj,treeItem( obj ));
+ }
+ else
+ {
+ synchronize<ObjPtr,ItemPtr,SUIT_TreeModel::TreeSync>( obj,
+ treeItem( obj ),
+ SUIT_TreeModel::TreeSync( this ) );
+ }
emit modelUpdated();
}
void SUIT_TreeModel::initialize()
{
SUIT_DataObject::disconnect( SIGNAL( inserted( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onInserted( 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* ) ) );
+ 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* ) ) );
+ this, SLOT( onInserted( SUIT_DataObject*, SUIT_DataObject* ) ) );
SUIT_DataObject::connect( SIGNAL( removed( SUIT_DataObject*, SUIT_DataObject* ) ),
- this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
+ this, SLOT( onRemoved( SUIT_DataObject*, SUIT_DataObject* ) ) );
}
myItems.clear(); // ????? is it really necessary
myRootItem = new TreeItem( 0 );
registerColumn( 0, QObject::tr( "NAME_COLUMN" ), SUIT_DataObject::NameId );
+
+ QString visCol = QObject::tr( "VISIBILITY_COLUMN" );
+ registerColumn( 0, visCol, SUIT_DataObject::VisibilityId );
+
+ SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+ setColumnIcon( visCol, resMgr->loadPixmap( "SUIT", tr( "ICON_DATAOBJ_VISIBLE" ) ));
+ setHeaderFlags( visCol, Qtx::ShowIcon);
+
updateTree();
}
return myItems.contains( obj ) ? obj : 0;
}
+/*!
+ \brief Get unique object identifier
+
+ Object identifier is customized via the Qtx::IdType custom data
+
+ \param index model index
+ \return object identifier or null string if it isn't specified
+ \sa SUIT_DataObject::customData()
+*/
+QString SUIT_TreeModel::objectId( const QModelIndex& index ) const
+{
+ QString objId;
+ if ( index.isValid() ) {
+ SUIT_DataObject* obj = object( index );
+ if ( obj ) {
+ int anId = obj->customData( Qtx::IdType ).toInt();
+ objId = data( createIndex( index.row(), anId, index.internalPointer() ) ).toString();
+ }
+ }
+ return objId;
+}
+
/*!
\brief Create an item corresponding to the data object.
\param obj source data object
\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 )
+ SUIT_TreeModel::TreeItem* parent,
+ SUIT_TreeModel::TreeItem* after )
{
if ( !obj )
return 0;
myItems[ obj ] = new TreeItem( obj, parent, after );
+ for(int pos=row;pos < parent->childCount();pos++)
+ parent->child(pos)->setPosition(pos);
+
endInsertRows();
+ obj->setModified(false);
+
return myItems[ obj ];
}
+/*!
+ \brief Create an item corresponding to the data object.
+ \param obj source data object
+ \param parent parent tree item
+ \param pos tree item position into which new item should be inserted
+ \return created tree item or 0 if item could not be created
+*/
+SUIT_TreeModel::TreeItem* SUIT_TreeModel::createItemAtPos( SUIT_DataObject* obj,
+ SUIT_TreeModel::TreeItem* parent,
+ int pos )
+{
+ if ( !obj )
+ return 0;
+
+ SUIT_DataObject* parentObj = object( parent );
+ QModelIndex parentIdx = index( parentObj );
+
+ int row = pos ;
+ SUIT_TreeModel::TreeItem* after = pos>0 ? parent->child(pos-1) : 0 ;
+
+ beginInsertRows( parentIdx, row, row );
+
+ SUIT_TreeModel::TreeItem* item = new TreeItem( obj, parent, after );
+ myItems[ obj ] = item;
+
+ for(int pos=row;pos < parent->childCount();pos++)
+ parent->child(pos)->setPosition(pos);
+
+ endInsertRows();
+
+ obj->setModified(false);
+
+ return item;
+}
+
/*!
\brief Update tree item.
\param item tree item to be updated
+ \param emitLayoutChanged if signal about changed layout should be emitted
*/
-void SUIT_TreeModel::updateItem( SUIT_TreeModel::TreeItem* item )
+void SUIT_TreeModel::updateItem( SUIT_TreeModel::TreeItem* item, bool emitLayoutChanged )
{
if ( !item )
return;
return;
// update all columns corresponding to the given data object
+ //emit layoutAboutToBeChanged(); // VSR 25/04/2011: fix crash on delete objects
QModelIndex firstIdx = index( obj, 0 );
QModelIndex lastIdx = index( obj, columnCount() - 1 );
emit dataChanged( firstIdx, lastIdx );
+ obj->setModified(false);
+ if( emitLayoutChanged )
+ emit layoutChanged();
}
/*!
// Warning! obj can be deleted at this point!
- SUIT_DataObject* parentObj = object( item->parent() );
+ TreeItem* parent=item->parent();
+ SUIT_DataObject* parentObj = object( parent );
QModelIndex parentIdx = index( parentObj, 0 );
int row = item->position();
if ( obj == root() )
setRoot( 0 );
- else if ( item->parent() )
- item->parent()->removeChild( item );
+ else if ( parent )
+ {
+ parent->removeChild( item );
+ for(int pos=row;pos < parent->childCount();pos++)
+ parent->child(pos)->setPosition(pos);
+ }
delete item;
updateTree( parent );
}
+/*!
+ \brief Drag and Drop support.
+*/
+QStringList SUIT_TreeModel::mimeTypes() const
+{
+ QStringList types;
+ types << "application/vnd.text.list";
+ return types;
+}
+
+/*!
+ \brief Called when the data objects are exported(dragged) from the tree.
+ \param indexes the list of exported objects
+*/
+QMimeData* SUIT_TreeModel::mimeData( const QModelIndexList& indexes ) const
+{
+ QMimeData* mimeData = new QMimeData();
+ QByteArray encodedData;
+
+ QDataStream stream( &encodedData, QIODevice::WriteOnly );
+
+ foreach ( QModelIndex index, indexes ) {
+ QString id = objectId( index );
+ // we have to check only 0 column in order to avoid repeating items in the drag object
+ // - QTreeView tries to drag indices for all visible columns
+ if ( index.isValid() && index.column() == 0 && !id.isEmpty() )
+ stream << id;
+ }
+
+ mimeData->setData( "application/vnd.text.list", encodedData );
+ return mimeData;
+}
+
+bool SUIT_TreeModel::dropMimeData( const QMimeData* data, Qt::DropAction action,
+ int row, int column, const QModelIndex& parent )
+{
+ if ( action == Qt::IgnoreAction )
+ // do nothing with data
+ return false;
+
+ if ( !data->hasFormat( "application/vnd.text.list" ) )
+ // not supported data dropped
+ return false;
+
+ if ( !parent.isValid() )
+ // dropping into the top level of the model is not allowed
+ return false;
+
+ // get parent object
+ SUIT_DataObject* pobj = object( parent );
+ if ( !pobj )
+ // invalid parent
+ return false;
+
+ // decode mime data and collect data objects being dropped
+ QByteArray encodedData = data->data( "application/vnd.text.list" );
+ QDataStream stream( &encodedData, QIODevice::ReadOnly );
+
+ DataObjectList objects;
+
+ while ( !stream.atEnd() ) {
+ QString id;
+ stream >> id;
+ if ( !id.isEmpty() && searcher() ) {
+ SUIT_DataObject* obj = searcher()->findObject( id );
+ if ( obj ) objects << obj;
+ }
+ }
+
+ // emit signal
+ emit dropped( objects, pobj, row, action );
+
+ // return true if there's any to drop
+ return !objects.isEmpty();
+}
+
/*!
\class SUIT_ProxyModel
- \brief Proxy model which can be used above the SUIT_TreeMovel class
+ \brief Proxy model which can be used above the SUIT_TreeModel class
to enable custom sorting/filtering of the data.
The SUIT_TreeModel class does not support custom sorting/filtering of the data.
{
SUIT_TreeModel* model = new SUIT_TreeModel( this );
connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
+ connect( model, SIGNAL( clicked( SUIT_DataObject*, int ) ), this, SIGNAL(clicked( SUIT_DataObject*, int ) ) );
+ connect( model, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ),
+ this, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ) );
setSourceModel( model );
+ setDynamicSortFilter( true );
}
/*!
{
SUIT_TreeModel* model = new SUIT_TreeModel( root, this );
connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
+ connect( model, SIGNAL( clicked( SUIT_DataObject*, int ) ), this, SIGNAL( clicked( SUIT_DataObject*, int ) ) );
+ connect( model, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ),
+ this, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ) );
setSourceModel( model );
+ setDynamicSortFilter( true );
}
/*!
mySortingEnabled( true )
{
connect( *model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
+ connect( *model, SIGNAL( clicked( SUIT_DataObject*, int ) ), this, SIGNAL( clicked( SUIT_DataObject*, int ) ) );
+ connect( *model, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ),
+ this, SIGNAL( dropped( const QList<SUIT_DataObject*>&, SUIT_DataObject*, int, Qt::DropAction ) ) );
setSourceModel( *model );
+ setDynamicSortFilter( true );
}
/*!
return treeModel() ? treeModel()->autoUpdate() : false;
}
+/*!
+ \brief Get 'updateModified' flag value.
+ \return 'updateModified' flag value
+*/
+bool SUIT_ProxyModel::updateModified() const
+{
+ return treeModel() ? treeModel()->updateModified() : false;
+}
+/*!
+ \brief Set 'updateModified' flag value.
+
+ If this flag is set to \c true (default=false), the model is updated by updateTreeModel that
+ uses the isModified flag to update only modified objects
+
+ \param on 'updateModified' flag value
+*/
+void SUIT_ProxyModel::setUpdateModified( const bool on )
+{
+ if ( treeModel() )
+ treeModel()->setUpdateModified( on );
+}
+
/*!
\brief Set 'auto-update tree' flag value.
return mySortingEnabled;
}
+SUIT_DataSearcher* SUIT_ProxyModel::searcher() const
+{
+ return treeModel() ? treeModel()->searcher() : 0;
+}
+
+void SUIT_ProxyModel::setSearcher( SUIT_DataSearcher* s )
+{
+ if ( treeModel() ) treeModel()->setSearcher( s );
+}
+
/*!
\brief Get item delegate for the model.
\return new item delegate
treeModel()->updateTree( obj );
}
+void SUIT_ProxyModel::forgetObject( const SUIT_DataObject* obj )
+{
+ if ( treeModel() )
+ treeModel()->forgetObject( obj );
+}
+
/*!
\brief Compares two model indexes for the sorting purposes.
\param left first index to compare
return dynamic_cast<SUIT_AbstractModel*>( sourceModel() );
}
+/*!
+ \brief Filter rows
+ \param sourceRow row index of the source data model
+ \param sourceParent parent model index of the source data model
+ \return \c true if the specified row should be filtered out (i.e. not displayed) or \c false otherwise
+*/
+bool SUIT_ProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
+{
+ SUIT_DataObject* o = treeModel()->object( sourceModel()->index( sourceRow, 0, sourceParent ) );
+ SUIT_DataObject* p = o ? o->parent() : 0;
+ return ( !p || p->expandable() ) && o && o->isVisible();
+}
+
/*!
\brief Register new column in the model
\param group_id - unique data object identificator allowing the classification of objects
return treeModel() ? treeModel()->appropriate( name ) : Qtx::Shown;
}
+/*!
+ \brief Set header flags.
+
+ These flags allow show in the header of the column text (name of the column),
+ icon or both text and icon.
+
+ \param name - column name
+ \param flags - header flags
+
+*/
+void SUIT_ProxyModel::setHeaderFlags( const QString& name, const Qtx::HeaderViewFlags flags )
+{
+ if(treeModel())
+ treeModel()->setHeaderFlags(name, flags);
+}
+
+/*!
+ \brief Get the header flags.
+
+ These flags allow show in the header of the column text (name of the column),
+ icon or both text and icon.
+
+ \param name - column name
+ \return header flags
+*/
+Qtx::HeaderViewFlags SUIT_ProxyModel::headerFlags( const QString& name ) const
+{
+ return treeModel() ? treeModel()->headerFlags( name ) : Qtx::ShowAll;
+}
+/*!
+ \brief Set visibility state of the object.
+
+ \param id - column name
+ \param state - visible state
+*/
+void SUIT_ProxyModel::setVisibilityState(const QString& id, Qtx::VisibilityState state)
+{
+ if(treeModel())
+ treeModel()->setVisibilityState(id,state);
+}
+/*!
+ \brief Set visibility state for all objects.
+
+ \param id - column name
+ \param state - visible state
+*/
+void SUIT_ProxyModel::setVisibilityStateForAll(Qtx::VisibilityState state)
+{
+ if(treeModel())
+ treeModel()->setVisibilityStateForAll(state);
+}
+/*!
+ \brief Get visibility state of the object.
+
+ \param id - column name
+ \return visible state
+*/
+Qtx::VisibilityState SUIT_ProxyModel::visibilityState(const QString& id) const
+{
+ return treeModel() ? treeModel()->visibilityState(id) : Qtx::UnpresentableState;
+}
+void SUIT_ProxyModel::emitClicked( SUIT_DataObject* obj, const QModelIndex& index)
+{
+ if(treeModel())
+ treeModel()->emitClicked(obj,index);
+}
/*!
\class SUIT_ItemDelegate
\param index model index being rendered
*/
void SUIT_ItemDelegate::paint( QPainter* painter,
- const QStyleOptionViewItem& option,
- const QModelIndex& index ) const
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index ) const
{
QStyleOptionViewItem opt = option;
if ( index.isValid() ) {
opt.palette.setBrush( QPalette::Highlight, val.value<QColor>() );
val = index.data( SUIT_TreeModel::HighlightedTextRole );
if ( val.isValid() && val.value<QColor>().isValid() )
- opt.palette.setBrush( QPalette::HighlightedText, val.value<QColor>() );
+ opt.palette.setBrush( QPalette::HighlightedText, val.value<QColor>() );
}
QItemDelegate::paint( painter, opt, index );
}
+
+QSize SUIT_ItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
+{
+ QSize size = QItemDelegate::sizeHint ( option, index );
+#if QT_VERSION >= 0x040500
+ size.setHeight( size.height() + 1 );
+#endif
+ return size;
+}