Skip to content
Snippets Groups Projects
Select Git revision
  • windows_ci
  • master default protected
2 results

TreeModel.cc

Blame
  • TreeModel.cc 22.92 KiB
    /*===========================================================================*\
     *                                                                           *
     *                              OpenFlipper                                  *
     *      Copyright (C) 2001-2009 by Computer Graphics Group, RWTH Aachen      *
     *                           www.openflipper.org                             *
     *                                                                           *
     *---------------------------------------------------------------------------*
     *  This file is part of OpenFlipper.                                        *
     *                                                                           *
     *  OpenFlipper 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 3 of           *
     *  the License, or (at your option) any later version with the              *
     *  following exceptions:                                                    *
     *                                                                           *
     *  If other files instantiate templates or use macros                       *
     *  or inline functions from this file, or you compile this file and         *
     *  link it with other files to produce an executable, this file does        *
     *  not by itself cause the resulting executable to be covered by the        *
     *  GNU Lesser General Public License. This exception does not however       *
     *  invalidate any other reasons why the executable file might be            *
     *  covered by the GNU Lesser General Public License.                        *
     *                                                                           *
     *  OpenFlipper 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 LesserGeneral Public          *
     *  License along with OpenFlipper. If not,                                  *
     *  see <http://www.gnu.org/licenses/>.                                      *
     *                                                                           *
    \*===========================================================================*/
    
    /*===========================================================================*\
     *                                                                           *
     *   $Revision$                                                       *
     *   $Author$                                                      *
     *   $Date$                   *
     *                                                                           *
    \*===========================================================================*/
    
    #include <QtGui>
    #include <QBrush>
    
    #include "TreeModel.hh"
    
    #include <iostream>
    
    
    #include <OpenFlipper/common/Types.hh>
    #include <OpenFlipper/BasePlugin/PluginFunctions.hh>
    
    
    //******************************************************************************
    
    /** \brief Constructor
     *
     * @param _parent parent Object
     */
    TreeModel::TreeModel( QObject *_parent) : QAbstractItemModel(_parent)
    {
      rootItem_ = new TreeItem( -1, "ROOT", DATA_UNKNOWN, 0);
    }
    
    
    //******************************************************************************
    
    /** \brief Destructor
     *
     */
    TreeModel::~TreeModel()
    {
    
    }
    
    
    //******************************************************************************
    
    /** \brief Return the number of columns
     *
     * @param unused
     * @return return always 4
     */
    int TreeModel::columnCount(const QModelIndex &/*_parent*/) const
    {
      // Name,Visible,Source,Target -> 4
      return (4);
    }
    
    
    //******************************************************************************
    
    /** \brief Returns the data stored under the given role for the item referred to by the index
     *
     * @param index a ModelIndex that defines the item in the tree
     * @param role defines the kind of data requested
     * @return requested data
     */
    QVariant TreeModel::data(const QModelIndex &index, int role) const
    {
    
      // Skip invalid requests
      if (!index.isValid())
          return QVariant();
    
      // Get the corresponding tree item
      TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
    
      if ( item == rootItem_ ) {
        std::cerr << "Root" << std::endl;
      }
    
      // Set the background color of the objects row
      if ( role == Qt::BackgroundRole ) {
        if ( !item->visible() ) {
          return QVariant( QBrush(QColor(100,100,100) ) );
        }
      }
      
    
    
      switch (index.column() ) {
        // Name
        case 0 :
    
          // If we are setting the name, also add the icon
          if ( role == Qt::DecorationRole ) {
            return QVariant( typeIcon(item->dataType()) );
          }
    
          if (role != Qt::DisplayRole && role != Qt::EditRole )
            return QVariant();
    
          return QVariant(item->name());
    
          break;
        // Visible
        case 1 :
          if (role == Qt::CheckStateRole ) {
            bool visibility = false;
            // visiblity group
            if (item->isGroup()){
              QList< TreeItem* > children = item->getLeafs();
              bool initRound = true;
              for (int i=0; i < children.size(); i++){
                if (initRound){
                  visibility = children[i]->visible();
                  initRound = false;
                }else if (visibility != children[i]->visible())
                  return QVariant(Qt::PartiallyChecked);
              }
            }else
              //visibility item
              visibility = item->visible();
            if (visibility)
              return QVariant(Qt::Checked);
            else
              return QVariant(Qt::Unchecked);
          }
          return QVariant();
    
          break;
        // Source
        case 2 :
          if (role == Qt::CheckStateRole ) {
            bool source = false;
            // source group
            if (item->isGroup()){
              QList< TreeItem* > children = item->getLeafs();
    
              bool initRound = true;
              for (int i=0; i < children.size(); i++){
                if (initRound){
                  source = children[i]->source();
                  initRound = false;
                }else if (source != children[i]->source())
                  return QVariant(Qt::PartiallyChecked);
              }
            }else
              //source item
              source = item->source();
            if (source)
              return QVariant(Qt::Checked);
            else
              return QVariant(Qt::Unchecked);
          }
          return QVariant();
    
        // Target
        case 3 :
          if (role == Qt::CheckStateRole ) {
            bool target = false;
            // target group
            if (item->isGroup()){
              QList< TreeItem* > children = item->getLeafs();
    
              bool initRound = true;
              for (int i=0; i < children.size(); i++){
                if (initRound){
                  target = children[i]->target();
                  initRound = false;
                }else if (target != children[i]->target())
                  return QVariant(Qt::PartiallyChecked);
              }
            }else
              //target item
              target = item->target();
            if (target)
              return QVariant(Qt::Checked);
            else
              return QVariant(Qt::Unchecked);
          }
          return QVariant();
    
          break;
    
        default:
          return QVariant();
      }
    
    }
    
    
    //******************************************************************************
    
    /** \brief Returns the item flags for the given index
     *
     * @param index ModelIndex that defines an item in the tree
     * @return flags for the given ModelIndex
     */
    Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return 0;
    
        Qt::ItemFlags flags = 0;
    
        // Show/Source/Target
        if ( ( index.column() == 1 ) ||
             ( index.column() == 2 ) ||
             ( index.column() == 3 ) )
          flags = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
        else
        if ( index.column() == 0 )
          flags = Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
        else
          flags = Qt::ItemIsEnabled;
    
      // Get the corresponding tree item
      TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
    
      if ( item->isGroup() )
        return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
      else
        return flags | Qt::ItemIsDragEnabled;
    }
    
    
    //******************************************************************************
    
    /** \brief Returns the data in the header
     *
     * @param section the column in the header
     * @param orientation header orientation (only horizontal handled)
     * @param role the role that defines the type of data
     * @return the requested data
     */
    QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                    int role) const
    {
        if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
    
          switch (section) {
            case 0 : return QVariant("Name");
            case 1 : return QVariant("Show");
            case 2 : return QVariant("Source");
            case 3 : return QVariant("Target");
            default :
              return QVariant();
          }
        }
        return QVariant();
    }
    
    
    //******************************************************************************
    
    /** \brief Returns the index of the item in the model specified by the given row, column and parent index.
     *
     * @param row the row
     * @param column the column
     * @param _parent parent item
     * @return corresponding ModelIndex
     */
    QModelIndex TreeModel::index(int row, int column, const QModelIndex &_parent) const
    {
    //     if (!hasIndex(row, column, _parent))
    //         return QModelIndex();
    
        TreeItem *parentItem;
    
        if (!_parent.isValid())
            parentItem = rootItem_;
        else
            parentItem = static_cast<TreeItem*>(_parent.internalPointer());
    
        TreeItem *childItem = parentItem->child(row);
        if (childItem)
            return createIndex(row, column, childItem);
        else
            return QModelIndex();
    }
    
    
    //******************************************************************************
    
    /** \brief Return index of parent item
     *
     * @param index a ModelIndex
     * @return parent of the given ModelIndex
     */
    QModelIndex TreeModel::parent(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QModelIndex();
    
        TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
        TreeItem *parentItem = childItem->parent();
    
        if (parentItem == rootItem_)
            return QModelIndex();
    
        return createIndex(parentItem->row(), 0, parentItem);
    }
    
    
    //******************************************************************************
    
    /** \brief Returns the number of rows under given parent
     *
     * @param _parent parent Item
     * @return number of rows that are children of given parent
     */
    int TreeModel::rowCount(const QModelIndex &_parent) const
    {
        TreeItem *parentItem;
        if (_parent.column() > 0)
            return 0;
    
        if (!_parent.isValid())
            parentItem = rootItem_;
        else
            parentItem = static_cast<TreeItem*>(_parent.internalPointer());
    
        return parentItem->childCount();
    }
    
    
    //******************************************************************************
    
    /** \brief The object with the given id has been changed. Update the model.
     *
     * @param _id  id of an object
     */
    void TreeModel::objectChanged(int _id) {
    
      if ( _id != -1 ){
    
        BaseObject* obj = 0;
        PluginFunctions::getObject(_id, obj);
    
        TreeItem* item = rootItem_->childExists(_id);
    
        //if internal and external representation are both valid
        if (obj != 0 && item != 0){
          //update the name
          if ( obj->name() != item->name() ){
    
            item->name( obj->name() );
    
            QModelIndex index = getModelIndex(item,0);
            if ( index.isValid() )
              emit dataChanged( index, index);
          }
    
          //update visibility
          if ( obj->visible() != item->visible() || obj->isGroup() ){
    
            item->visible( obj->visible() );
    
            QModelIndex index0 = getModelIndex(item,0);
            QModelIndex index1 = getModelIndex(item,3);
    
            if ( index0.isValid() && index1.isValid() ){
              //the whole row has to be updated because of the grey background-color
              emit dataChanged( index0, index1);
              propagateUpwards(item->parent(), 1, obj->visible() );
            }
    
            if ( obj->isGroup() )
              propagateDownwards(item, 1 );
          }
    
          //update source flag
          if ( obj->source() != item->source() || obj->isGroup() ){
    
            item->source( obj->source() );
    
            QModelIndex index = getModelIndex(item,2);
            if ( index.isValid() ){
              emit dataChanged( index, index);
              propagateUpwards(item->parent(), 2, false );
            }
    
            if ( obj->isGroup() )
              propagateDownwards(item, 2 );
          }
    
          //update target flag
          if ( obj->target() != item->target() || obj->isGroup() ){
    
            item->target( obj->target() );
    
            QModelIndex index = getModelIndex(item,3);
            if ( index.isValid() ){
              emit dataChanged( index, index);
              propagateUpwards(item->parent(), 3, false );
            }
    
            if ( obj->isGroup() )
              propagateDownwards(item, 3 );
          }
    
          //update parent
          if ( obj->parent() == PluginFunctions::objectRoot() && isRoot( item->parent() ) ){
            return;
          }else if ( obj->parent() == PluginFunctions::objectRoot() && !isRoot( item->parent() ) ){
            moveItem(item, rootItem_ );
          }else if ( obj->parent()->id() != item->parent()->id() ){
            TreeItem* parent = rootItem_->childExists( obj->parent()->id() );
    
            if (parent != 0)
              moveItem(item, parent );
          }
        }
      }
    }
    
    
    /** \brief The object with the given id has been added. add it to the internal tree
     *
     * @param id_ id of the object
     */
    void TreeModel::objectAdded(BaseObject* _object){
    
      // check if item already in model tree
      // this function can be called by addEmpty and fileOpened
      // both will be called by fileOpened such that the item
      // already exists
      if( rootItem_->childExists( _object->id() )) 
        return;
    
      TreeItem* parent = 0;
      //find the parent
      if ( _object->parent() == PluginFunctions::objectRoot() )
        parent = rootItem_;
      else
        parent = rootItem_->childExists( _object->parent()->id() );
    
      if (parent != 0){
        QModelIndex parentIndex = getModelIndex(parent, 0);
    
        beginInsertRows(parentIndex, parent->childCount(), parent->childCount()); //insert at the bottom
    
          TreeItem* item = new TreeItem( _object->id(), _object->name(), _object->dataType(), parent);
    
          parent->appendChild( item );
    
        endInsertRows();
      }
    
      objectChanged( _object->id() );
    }
    
    /** \brief The object with the given id has been deleted. delete it from the internal tree
     *
     * @param id_ id of the object
     */
    void TreeModel::objectDeleted(int _id){
    
      TreeItem* item = rootItem_->childExists(_id);
    
      if ( item != 0 && !isRoot(item) ){
    
        QModelIndex itemIndex   = getModelIndex(item, 0);
        QModelIndex parentIndex = itemIndex.parent();
    
        beginRemoveRows( parentIndex, itemIndex.row(), itemIndex.row() );
    
        item->parent()->removeChild(item);
        item->deleteSubtree();
    
        delete item;
    
        endRemoveRows();
      }
    }
    
    //******************************************************************************
    
    /** \brief move the item to a new parent
     *
     * @param _item the item
     * @param _parent new parent
     */
    void TreeModel::moveItem(TreeItem* _item, TreeItem* _parent ){
    
      QModelIndex itemIndex   = getModelIndex(_item, 0);
      QModelIndex oldParentIndex = itemIndex.parent();
      QModelIndex newParentIndex = getModelIndex(_parent, 0);
    
      //delete everything at the old location
      beginRemoveRows( oldParentIndex, itemIndex.row(), itemIndex.row() );
    
        _item->parent()->removeChild(_item);
    
      endRemoveRows();
    
      //insert it at the new location
      beginInsertRows(newParentIndex, _parent->childCount(), _parent->childCount() ); //insert at the bottom
        _item->setParent( _parent );
        _parent->appendChild( _item );
      endInsertRows();
    
      emit layoutChanged();
    }
    
    //******************************************************************************
    
    /** \brief Return item at given index
     *
     * @param index a ModelIndex
     * @return item at given index
     */
    TreeItem* TreeModel::getItem(const QModelIndex &index) const
    {
        if (index.isValid()) {
            TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
            if (item) return item;
        }
        return rootItem_;
    }
    
    
    //******************************************************************************
    
    /** \brief Return item-name at given index
     *
     * @param index a ModelIndex
     * @return name of the item at given index
     */
    QString TreeModel::itemName(const QModelIndex &index) const
    {
        if (index.isValid()) {
            TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
            if (item)
              return item->name();
        }
        return "not found";
    }
    
    //******************************************************************************
    
    /** \brief Return item-id at given index
     *
     * @param index a ModelIndex
     * @return item-id at given index
     */
    int TreeModel::itemId(const QModelIndex &index) const
    {
        if (index.isValid()) {
            TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
            if (item)
              return item->id();
        }
        return -1;
    }
    
    //******************************************************************************
    
    /** \brief Return index of given item
     *
     * Warning: Only use this function if you know that all ModelIndices are created
     *
     * @param _object an object
     * @param _column a column
     * @return index of object and column
     */
    QModelIndex TreeModel::getModelIndex(TreeItem* _object, int _column ){
    
      // root item gets an invalid QModelIndex
      if ( _object == rootItem_ )
        return QModelIndex();
    
      QModelIndex index = createIndex(_object->row(), _column, _object);
    
      return index;
    }
    
    
    //******************************************************************************
    
    /** \brief Recursively update a column up to the root of the tree
     *
     * @param _item item to start with
     */
    void TreeModel::propagateUpwards(TreeItem* _item, int _column, bool _value ){
    
      if ( isRoot(_item) || (!_item->isGroup()) )
        return;
    
      if (_column == 1){ //visibility
        _item->visible( _value );
    
        //the whole row has to be updated because of the grey background-color
        QModelIndex index0 = getModelIndex(_item,0);
        QModelIndex index1 = getModelIndex(_item,3);
    
        emit dataChanged( index0, index1);
    
      } else {
    
        QModelIndex index = getModelIndex(_item,_column);
        emit dataChanged(index, index);
      }
    
      propagateUpwards( _item->parent(), _column, _value );
    }
    
    //******************************************************************************
    
    /** \brief Recursively update a column up to the root of the tree
     *
     * @param _item item to start with
     */
    void TreeModel::propagateDownwards(TreeItem* _item, int _column ){
    
      for (int i=0; i < _item->childCount(); i++){
    
        TreeItem* current = _item->child(i);
    
        bool changed = false;
    
        switch ( _column ){
    
          case 1: //VISIBILTY
    
            if ( current->visible() != _item->visible() ){
    
              current->visible( _item->visible() );
              changed = true;
            }
            break;
    
          case 2: //SOURCE
    
            if ( current->source() != _item->source() ){
    
              current->source( _item->source() );
              changed = true;
            }
            break;
    
          case 3: //TARGET
    
            if ( current->target() != _item->target() ){
    
              current->target( _item->target() );
              changed = true;
            }
            break;
    
          default:
            break;
        }
    
        if (changed){
          QModelIndex index = getModelIndex(current,_column);
          emit dataChanged(index, index);
        }
    
        if ( current->isGroup() )
          propagateDownwards(current, _column);
      }
    }
    
    //******************************************************************************
    
    /** \brief Set Data at 'index' to 'value'
     *
     * @param index a ModelIndex defining the positin in the model
     * @param value the new value
     * @param  unused
     * @return return if the data was set successfully
     */
    bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/)
    {
    
      emit dataChangedInside( itemId(index), index.column(), value );
    
      return true;
    }
    
    
    //******************************************************************************
    
    /** \brief return if an object is equal to the root object
     *
     * @param _item the item to be checked
     * @return is it the root object?
     */
    bool TreeModel::isRoot(TreeItem * _item) {
      return ( _item == rootItem_ );
    }
    
    /*******************************************************************************
            drag & drop stuff
     *******************************************************************************/
    
    /** \brief return the supported drop actions
     *
     * @return drop actions
     */
    Qt::DropActions TreeModel::supportedDropActions() const
    {
        return /*Qt::CopyAction |*/ Qt::MoveAction;
    }
    
    
    //******************************************************************************
    
    /** \brief return the mimeType for drag & drop
     *
     * @return the mimeType
     */
    QStringList TreeModel::mimeTypes() const
    {
        QStringList types;
        types << "DataControl/dragDrop";
        return types;
    }
    
    
    //******************************************************************************
    
    /** \brief generate mimeData for given ModelIndexes
     *
     * @param indexes list of ModelIndexes
     * @return the mimeData
     */
    QMimeData* TreeModel::mimeData(const QModelIndexList& indexes) const
    {
        QMimeData *mimeData = new QMimeData();
        QByteArray encodedData;
    
        QDataStream stream(&encodedData, QIODevice::WriteOnly);
    
        QVector< int > rows;
    
        foreach (QModelIndex index, indexes) {
            if (index.isValid()) {
    
              if (!rows.contains( index.row() ) ){
                TreeItem *item = getItem(index);
                stream << item->id();
    
                rows.push_back( index.row() );
              }
            }
        }
    
        mimeData->setData("DataControl/dragDrop", encodedData);
        return mimeData;
    }
    
    
    //******************************************************************************
    
    /** \brief this is called when mimeData is dropped
     *
     * @param data the dropped data
     * @param action the definition of the dropAction which occured
     * @param unused
     * @param unused
     * @param parent parent under which the drop occurred
     * @return returns if the drop was sucessful
     */
    bool TreeModel::dropMimeData(const QMimeData *data,
         Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent)
    {
      if (action == Qt::IgnoreAction)
          return true;
    
      if (!data->hasFormat("DataControl/dragDrop"))
          return false;
    
      QByteArray encodedData = data->data("DataControl/dragDrop");
      QDataStream stream(&encodedData, QIODevice::ReadOnly);
    
      QVector< int > ids;
    
      while (!stream.atEnd()) {
        int id;
        stream >> id;
    
        ids.push_back( id );
      }
    
      if (ids.count() == 0)
        return false;
    
      //get new parent
      TreeItem *newParent = getItem(parent);
    
      if ( newParent == 0 || !newParent->isGroup() )
        return false;
    
      //and move all objects
      for (int i = 0; i < ids.count(); i++){
        //tell the DataControlPlugin to move the corresponding BaseObject
        emit moveBaseObject( ids[i], newParent->id()  );
      }
    
      return true;
    
     }