File indexing completed on 2024-05-26 16:15:04

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
0003  * Copyright (C) 2008 Fredy Yanardi <fyanardi@gmail.com>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KoPADocumentModel.h"
0022 
0023 #include "KoPADocument.h"
0024 #include "KoPAPageBase.h"
0025 #include <KoShapePainter.h>
0026 #include <KoShapeManager.h>
0027 #include <KoShapeContainer.h>
0028 #include <KoToolManager.h>
0029 #include <KoPageLayout.h>
0030 #include <KoCanvasBase.h>
0031 #include <KoCanvasController.h>
0032 #include <KoSelection.h>
0033 #include <KoShapeLayer.h>
0034 #include <KoShapeGroup.h>
0035 #include <KoShapeGroupCommand.h>
0036 #include <KoShapeUngroupCommand.h>
0037 #include <KoShapeRenameCommand.h>
0038 #include <KoZoomHandler.h>
0039 #include <KoPAOdfPageSaveHelper.h>
0040 #include <KoDrag.h>
0041 #include <KoPAPastePage.h>
0042 #include <KoIcon.h>
0043 
0044 #include <klocalizedstring.h>
0045 #include <PageAppDebug.h>
0046 
0047 #include <QMimeData>
0048 #include <QApplication>
0049 #include <QClipboard>
0050 #include <QMenu>
0051 #include <QPainterPath>
0052 
0053 #include "commands/KoPAPageMoveCommand.h"
0054 
0055 KoPADocumentModel::KoPADocumentModel( QObject* parent, KoPADocument *document )
0056 : KoDocumentSectionModel( parent )
0057 , m_document(0)
0058 , m_master(false)
0059 , m_lastContainer( 0 )
0060 {
0061     setDocument( document );
0062 }
0063 
0064 Qt::DropActions KoPADocumentModel::supportedDragActions() const
0065 {
0066     return Qt::MoveAction;
0067 }
0068 
0069 void KoPADocumentModel::update()
0070 {
0071     emit layoutAboutToBeChanged();
0072     emit layoutChanged();
0073     if (m_document) {
0074         dataChanged(index(0, 0), index(m_document->pageCount() - 1, columnCount() - 1));
0075     }
0076 }
0077 
0078 int KoPADocumentModel::rowCount( const QModelIndex &parent ) const
0079 {
0080     if (!m_document) {
0081         return 0;
0082     }
0083 
0084     // check if parent is root node
0085     if ( ! parent.isValid() ) {
0086         return m_document->pages(m_master).count();
0087     }
0088 
0089     Q_ASSERT(parent.model() == this);
0090     Q_ASSERT(parent.internalPointer());
0091 
0092     KoShapeContainer *parentShape = dynamic_cast<KoShapeContainer*>( (KoShape*)parent.internalPointer() );
0093     if ( ! parentShape ) {
0094         return 0;
0095     }
0096 
0097     return parentShape->shapeCount();
0098 }
0099 
0100 int KoPADocumentModel::columnCount( const QModelIndex & ) const
0101 {
0102     return 1;
0103 }
0104 
0105 QModelIndex KoPADocumentModel::index( int row, int column, const QModelIndex &parent ) const
0106 {
0107     if ( !m_document ) {
0108         return QModelIndex();
0109     }
0110 
0111     // check if parent is root node
0112     if ( ! parent.isValid() ) {
0113         if ( row >= 0 && row < m_document->pages(m_master).count() ) {
0114             return createIndex( row, column, m_document->pages(m_master).at(row) );
0115         } else {
0116             return QModelIndex();
0117         }
0118     }
0119 
0120     Q_ASSERT(parent.model() == this);
0121     Q_ASSERT(parent.internalPointer());
0122 
0123     KoShapeContainer *parentShape = dynamic_cast<KoShapeContainer*>( (KoShape*)parent.internalPointer() );
0124     if ( ! parentShape ) {
0125         return QModelIndex();
0126     }
0127 
0128     if ( row < parentShape->shapeCount() ) {
0129         return createIndex( row, column, childFromIndex( parentShape, row ) );
0130     } else {
0131         return QModelIndex();
0132     }
0133 }
0134 
0135 QModelIndex KoPADocumentModel::parent( const QModelIndex &child ) const
0136 {
0137     // check if child is root node
0138     if ( ! child.isValid() || !m_document ) {
0139         return QModelIndex();
0140     }
0141 
0142     Q_ASSERT(child.model() == this);
0143     Q_ASSERT(child.internalPointer());
0144 
0145     KoShape *childShape = static_cast<KoShape*>( child.internalPointer() );
0146     if ( ! childShape ) {
0147         return QModelIndex();
0148     }
0149 
0150     // get the children's parent shape
0151     KoShapeContainer *parentShape = childShape->parent();
0152     if ( ! parentShape ) {
0153         return QModelIndex();
0154     }
0155 
0156     // get the grandparent to determine the row of the parent shape
0157     KoShapeContainer *grandParentShape = parentShape->parent();
0158     if ( ! grandParentShape ) {
0159         KoPAPageBase* page = dynamic_cast<KoPAPageBase*>( parentShape);
0160         return createIndex( m_document->pages(m_master).indexOf( page ), 0, parentShape );
0161     }
0162 
0163     return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape );
0164 }
0165 
0166 QVariant KoPADocumentModel::data( const QModelIndex &index, int role ) const
0167 {
0168     if ( ! index.isValid() || !m_document ) {
0169         return QVariant();
0170     }
0171 
0172     Q_ASSERT(index.model() == this);
0173     Q_ASSERT(index.internalPointer());
0174 
0175     KoShape *shape = static_cast<KoShape*>( index.internalPointer() );
0176 
0177     switch (role)
0178     {
0179         case Qt::DisplayRole:
0180         {
0181             QString name = shape->name();
0182             if ( name.isEmpty() ) {
0183                 if ( dynamic_cast<KoPAPageBase *>( shape ) ) {
0184                     if (m_document->pageType() == KoPageApp::Slide ) {
0185                         name = i18n("Slide %1",  m_document->pageIndex(dynamic_cast<KoPAPageBase *>(shape)) + 1);
0186                     } else {
0187                         name = i18n("Page  %1", m_document->pageIndex(dynamic_cast<KoPAPageBase *>(shape)) + 1);
0188                     }
0189                 } else if ( dynamic_cast<KoShapeLayer*>( shape ) ) {
0190                     name = i18n("Layer") + QString(" (%1)").arg(shape->zIndex());
0191                 } else if ( dynamic_cast<KoShapeGroup*>( shape ) ) {
0192                     name = i18n("Group") + QString(" (%1)").arg(shape->zIndex());
0193                 } else {
0194                     name = i18n("Shape") + QString(" (%1)").arg(shape->zIndex());
0195                 }
0196             }
0197             return name;
0198         }
0199         case Qt::DecorationRole: return QVariant();//return shape->icon();
0200         case Qt::EditRole: return shape->name();
0201         case Qt::SizeHintRole:
0202         {
0203             KoPAPageBase *page = dynamic_cast<KoPAPageBase*>(shape);
0204             if (page) { // return actual page size for page
0205                 KoPageLayout layout = page->pageLayout();
0206                 return QSize(layout.width, layout.height);
0207             }
0208             else
0209                 return shape->size();
0210         }
0211         case ActiveRole:
0212         {
0213             KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
0214             KoSelection * selection = canvasController->canvas()->shapeManager()->selection();
0215             if ( ! selection ) {
0216                 return false;
0217             }
0218 
0219             /* KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>( shape );
0220             if ( layer )
0221                 return (layer == selection->activeLayer() );
0222             else */
0223             return selection->isSelected( shape );
0224         }
0225         case PropertiesRole: return QVariant::fromValue( properties( shape ) );
0226         case AspectRatioRole:
0227         {
0228             QTransform matrix = shape->absoluteTransformation( 0 );
0229             QRectF bbox = matrix.mapRect( shape->outline().boundingRect() );
0230             KoShapeContainer *container = dynamic_cast<KoShapeContainer*>( shape );
0231             if ( container ) {
0232                 bbox = QRectF();
0233                 foreach( KoShape* shape, container->shapes() ) {
0234                     bbox = bbox.united( shape->outline().boundingRect() );
0235                 }
0236             }
0237             return qreal(bbox.width()) / bbox.height();
0238         }
0239         default:
0240             if (role >= int(BeginThumbnailRole)) {
0241                 return createThumbnail( shape, QSize( role - int(BeginThumbnailRole), role - int(BeginThumbnailRole) ) );
0242             } else {
0243                 return QVariant();
0244             }
0245     }
0246 }
0247 
0248 Qt::ItemFlags KoPADocumentModel::flags(const QModelIndex &index) const
0249 {
0250     if ( !m_document ) {
0251         return 0;
0252     }
0253 
0254     if ( ! index.isValid() ) {
0255         return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
0256     }
0257 
0258     Q_ASSERT(index.model() == this);
0259     Q_ASSERT(index.internalPointer());
0260 
0261     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
0262     //if ( dynamic_cast<KoShapeContainer*>( (KoShape*)index.internalPointer() ) )
0263         flags |= Qt::ItemIsDropEnabled;
0264     return flags;
0265 }
0266 
0267 bool KoPADocumentModel::setData(const QModelIndex &index, const QVariant &value, int role )
0268 {
0269     if ( ! index.isValid() || !m_document ) {
0270         return false;
0271     }
0272 
0273     Q_ASSERT(index.model() == this);
0274     Q_ASSERT(index.internalPointer());
0275 
0276     KoShape *shape = static_cast<KoShape*>( index.internalPointer() );
0277     switch (role)
0278     {
0279         case Qt::DisplayRole:
0280         case Qt::EditRole:
0281         {
0282             KUndo2Command * cmd = new KoShapeRenameCommand( shape, value.toString() );
0283             if (dynamic_cast<KoPAPageBase *>(shape)) {
0284                 if (m_document->pageType() == KoPageApp::Slide) {
0285                     cmd->setText(kundo2_i18n("Rename Slide"));
0286                 } else {
0287                     cmd->setText(kundo2_i18n("Rename Page"));
0288                 }
0289             }
0290             else if (dynamic_cast<KoShapeLayer *>(shape)) {
0291                 cmd->setText(kundo2_i18n("Rename Layer"));
0292             }
0293             m_document->addCommand( cmd );
0294         }   break;
0295         case PropertiesRole:
0296             setProperties( shape, value.value<PropertyList>());
0297             break;
0298         case ActiveRole:
0299             /* if (value.toBool())
0300             {
0301                 KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
0302                 KoSelection * selection = canvasController->canvas()->shapeManager()->selection();
0303 
0304                 KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>( shape );
0305                 if ( layer && selection ) {
0306                     selection->setActiveLayer( layer );
0307                 }
0308             } */
0309             break;
0310         default:
0311             return false;
0312     }
0313 
0314     emit dataChanged( index, index );
0315     return true;
0316 }
0317 
0318 KoDocumentSectionModel::PropertyList KoPADocumentModel::properties( KoShape* shape ) const
0319 {
0320     PropertyList l;
0321 
0322     if (KoPAPageBase *page = dynamic_cast<KoPAPageBase *>(shape)) {
0323         // The idea is to display the page-number so users know what page-number/slide-number
0324         // the shape has also in the case the slide has a name (in which case it's not named
0325         // "Slide [slide-number]" any longer.
0326         // Maybe we should better use KoTextPage::visiblePageNumber here?
0327         l << Property(i18n("Slide"), QString::number(m_document->pageIndex(page) + 1));
0328     }
0329 
0330     l << Property(i18n("Visible"), koIcon("layer-visible-on"), koIcon("layer-visible-off"), shape->isVisible());
0331     l << Property(i18n("Locked"), koIcon("object-locked"), koIcon("object-unlocked"), shape->isGeometryProtected());
0332     return l;
0333 }
0334 
0335 void KoPADocumentModel::setProperties( KoShape* shape, const PropertyList &properties )
0336 {
0337     bool oldVisibleState = shape->isVisible();
0338     bool oldLockedState = shape->isGeometryProtected();
0339 
0340     shape->setVisible( properties.at( 0 ).state.toBool() );
0341     shape->setGeometryProtected( properties.at( 1 ).state.toBool() );
0342 
0343     if ( ( oldVisibleState != shape->isVisible() ) || ( oldLockedState != shape->isGeometryProtected() ) ) {
0344         shape->update();
0345     }
0346 }
0347 
0348 QImage KoPADocumentModel::createThumbnail( KoShape* shape, const QSize &thumbSize ) const
0349 {
0350     QSize size(thumbSize.width(), thumbSize.height());
0351     KoShapePainter shapePainter;
0352 
0353     KoPAPageBase *page = dynamic_cast<KoPAPageBase*>(shape);
0354     if (page) { // We create a thumbnail with actual width / height ratio for page
0355         KoZoomHandler zoomHandler;
0356         KoPageLayout layout = page->pageLayout();
0357         qreal ratio = (zoomHandler.resolutionX() * layout.width) / (zoomHandler.resolutionY() * layout.height);
0358         if ( ratio > 1 ) {
0359             size.setHeight( size.width() / ratio );
0360         } else {
0361             size.setWidth( size.height() * ratio );
0362         }
0363         QPixmap pixmap = m_document->pageThumbnail( page, size );
0364         return pixmap.toImage();
0365     }
0366 
0367     QList<KoShape*> shapes;
0368     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
0369     if (container) {
0370         shapes = container->shapes();
0371     }
0372     shapes.append(shape);
0373 
0374     shapePainter.setShapes( shapes );
0375 
0376     QImage thumb( size, QImage::Format_RGB32 );
0377     // draw the background of the thumbnail
0378     thumb.fill( QColor( Qt::white ).rgb() );
0379     shapePainter.paint(thumb);
0380 
0381     return thumb;
0382 }
0383 
0384 KoShape * KoPADocumentModel::childFromIndex( KoShapeContainer *parent, int row ) const
0385 {
0386     return parent->shapes().at(row);
0387 }
0388 
0389 int KoPADocumentModel::indexFromChild( KoShapeContainer *parent, KoShape *child ) const
0390 {
0391     if ( !m_document ) {
0392         return 0;
0393     }
0394 
0395     return parent->shapes().indexOf( child );
0396 }
0397 
0398 Qt::DropActions KoPADocumentModel::supportedDropActions () const
0399 {
0400     return Qt::MoveAction | Qt::CopyAction;
0401 }
0402 
0403 QStringList KoPADocumentModel::mimeTypes() const
0404 {
0405     QStringList types;
0406     types << QLatin1String("application/x-kopalayermodeldatalist");
0407     return types;
0408 }
0409 
0410 QMimeData * KoPADocumentModel::mimeData( const QModelIndexList & indexes ) const
0411 {
0412     // check if there is data to encode
0413     if ( ! indexes.count() ) {
0414         return 0;
0415     }
0416 
0417     // check if we support a format
0418     QStringList types = mimeTypes();
0419     if ( types.isEmpty() ) {
0420         return 0;
0421     }
0422 
0423     QMimeData *data = new QMimeData();
0424     QString format = types[0];
0425     QByteArray encoded;
0426     QDataStream stream(&encoded, QIODevice::WriteOnly);
0427 
0428     // encode the data
0429     QModelIndexList::ConstIterator it = indexes.begin();
0430     for( ; it != indexes.end(); ++it) {
0431         stream << QVariant::fromValue( qulonglong( it->internalPointer() ) );
0432     }
0433 
0434     data->setData(format, encoded);
0435     return data;
0436 }
0437 
0438 bool KoPADocumentModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
0439 {
0440     Q_UNUSED( row );
0441     Q_UNUSED( column );
0442 
0443     // check if the action is supported
0444     if ( ! data || action != Qt::MoveAction ) {
0445         return false;
0446     }
0447     // check if the format is supported
0448     QStringList types = mimeTypes();
0449     if ( types.isEmpty() ) {
0450         return false;
0451     }
0452     QString format = types[0];
0453     if ( ! data->hasFormat(format) ) {
0454         return false;
0455     }
0456 
0457     QByteArray encoded = data->data( format );
0458     QDataStream stream(&encoded, QIODevice::ReadOnly);
0459     QList<KoShape*> shapes;
0460 
0461     // decode the data
0462     while( ! stream.atEnd() ) {
0463         QVariant v;
0464         stream >> v;
0465         shapes.append( static_cast<KoShape*>( (void*)v.value<qulonglong>() ) );
0466     }
0467 
0468     QList<KoShape*> toplevelShapes;
0469     QList<KoShapeLayer*> layers;
0470     QList<KoPAPageBase *> pages;
0471     // remove shapes having its parent in the list
0472     // and separate the layers
0473     foreach( KoShape * shape, shapes ) {
0474         // check whether the selection contains page
0475         // by the UI rules, the selection should contains page only
0476         KoPAPageBase *page = dynamic_cast<KoPAPageBase *>( shape );
0477         if ( page ) {
0478             pages.append( page );
0479             continue;
0480         }
0481 
0482         KoShapeContainer *parentShape = shape->parent();
0483         bool hasParentInList = false;
0484         while ( parentShape ) {
0485             if ( shapes.contains( parentShape ) ) {
0486                 hasParentInList = true;
0487                 break;
0488             }
0489             parentShape = parentShape->parent();
0490         }
0491         if ( hasParentInList ) {
0492             continue;
0493         }
0494 
0495         KoShapeLayer * layer = dynamic_cast<KoShapeLayer*>( shape );
0496         if ( layer ) {
0497             layers.append( layer );
0498         } else {
0499             toplevelShapes.append( shape );
0500         }
0501     }
0502 
0503     // dropping to root, only page(s) is allowed
0504     if (!parent.isValid()) {
0505         if ( !pages.isEmpty() ) {
0506             if ( row < 0 ) {
0507                 return false;
0508             }
0509             KoPAPageBase *after = (row != 0) ? m_document->pageByIndex(row - 1, false) : 0;
0510             debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping page(s) as root, moving page(s)";
0511             return doDrop(pages, after, action);
0512         }
0513         else {
0514             debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping non-page as root, returning false";
0515             return false;
0516         }
0517     }
0518     else if (parent.isValid() && !pages.isEmpty()){
0519         if (parent.row() < 0) {
0520             return false;
0521         }
0522         KoPAPageBase *after;
0523         if ((m_document->pageIndex(pages.first()) - 1) == parent.row()) {
0524             after = (parent.row() != 0) ? m_document->pageByIndex(parent.row() - 1, false) : 0;
0525         }
0526         else {
0527             after = (parent.row() > -1) ? m_document->pageByIndex(parent.row(), false) : 0;
0528         }
0529         return doDrop(pages, after, action);
0530     }
0531 
0532     KoShape *shape = static_cast<KoShape*>( parent.internalPointer() );
0533     KoShapeContainer * container = dynamic_cast<KoShapeContainer*>( shape );
0534     if ( container ) {
0535         KoShapeGroup * group = dynamic_cast<KoShapeGroup*>( container );
0536         if ( group ) {
0537             debugPageApp <<"KoPADocumentModel::dropMimeData parent = group";
0538             if ( ! toplevelShapes.count() ) {
0539                 return false;
0540             }
0541 
0542             emit layoutAboutToBeChanged();
0543             beginInsertRows( parent, group->shapeCount(), group->shapeCount()+toplevelShapes.count() );
0544 
0545             KUndo2Command * cmd = new KUndo2Command();
0546             cmd->setText( kundo2_i18n("Reparent shapes") );
0547 
0548             foreach( KoShape * shape, toplevelShapes ) {
0549                 new KoShapeUngroupCommand( shape->parent(), QList<KoShape*>() << shape, QList<KoShape*>(), cmd );
0550             }
0551 
0552             new KoShapeGroupCommand( group, toplevelShapes, cmd );
0553             KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
0554             canvasController->canvas()->addCommand( cmd );
0555 
0556             endInsertRows();
0557             emit layoutChanged();
0558         } else {
0559             debugPageApp <<"KoPADocumentModel::dropMimeData parent = container";
0560             if ( toplevelShapes.count() ) {
0561                 emit layoutAboutToBeChanged();
0562                 beginInsertRows( parent, container->shapeCount(), container->shapeCount()+toplevelShapes.count() );
0563 
0564                 KUndo2Command * cmd = new KUndo2Command();
0565                 cmd->setText( kundo2_i18n("Reparent shapes") );
0566 
0567                 QList<bool> clipped;
0568                 QList<bool> inheritsTransform;
0569                 foreach( KoShape * shape, toplevelShapes ) {
0570                     if ( ! shape->parent() ) {
0571                         clipped.append( false );
0572                         inheritsTransform.append(false);
0573                         continue;
0574                     }
0575 
0576                     clipped.append( shape->parent()->isClipped( shape ) );
0577                     inheritsTransform.append(shape->parent()->inheritsTransform(shape));
0578                     new KoShapeUngroupCommand( shape->parent(), QList<KoShape*>() << shape, QList<KoShape*>(), cmd );
0579                 }
0580                 // shapes are dropped on a container, so add them to the container
0581                 new KoShapeGroupCommand(container, toplevelShapes, clipped, inheritsTransform, cmd);
0582                 KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
0583                 canvasController->canvas()->addCommand( cmd );
0584 
0585                 endInsertRows();
0586                 emit layoutChanged();
0587             } else if ( layers.count() ) {
0588                 KoShapeLayer * layer = dynamic_cast<KoShapeLayer*>( container );
0589                 if ( ! layer ) {
0590                     return false;
0591                 }
0592 
0593                 // TODO layers are dropped on a layer, so change layer ordering
0594                 return false;
0595             }
0596         }
0597     } else {
0598         debugPageApp <<"KoPADocumentModel::dropMimeData parent = shape";
0599         if ( ! toplevelShapes.count() ) {
0600             return false;
0601         }
0602 
0603         // TODO shapes are dropped on a shape, reorder them
0604         return false;
0605     }
0606 
0607     return true;
0608 }
0609 
0610 QModelIndex KoPADocumentModel::parentIndexFromShape( const KoShape * child )
0611 {
0612     if ( !m_document ) {
0613         return QModelIndex();
0614     }
0615 
0616     // check if child shape is a layer, and return invalid model index if it is
0617     const KoShapeLayer *childlayer = dynamic_cast<const KoShapeLayer*>( child );
0618     if ( childlayer ) {
0619         return QModelIndex();
0620     }
0621 
0622     // get the children's parent shape
0623     KoShapeContainer *parentShape = child->parent();
0624     if ( ! parentShape ) {
0625         return QModelIndex();
0626     }
0627 
0628     // check if the parent is a layer
0629     KoShapeLayer *parentLayer = dynamic_cast<KoShapeLayer*>( parentShape );
0630 
0631 
0632     if ( parentLayer ) {
0633         KoPAPageBase * page = dynamic_cast<KoPAPageBase*>( parentLayer->parent() );
0634         if ( page ) {
0635             return createIndex( m_document->pages(m_master).count() - 1 - m_document->pages(m_master).indexOf( page ), 0, parentLayer );
0636         }
0637     }
0638     // get the grandparent to determine the row of the parent shape
0639     KoShapeContainer *grandParentShape = parentShape->parent();
0640     if ( ! grandParentShape ) {
0641         return QModelIndex();
0642     }
0643 
0644     return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape );
0645 }
0646 
0647 void KoPADocumentModel::setDocument( KoPADocument* document )
0648 {
0649     if (m_document == document) {
0650         return;
0651     }
0652 
0653     if (m_document) {
0654         disconnect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) );
0655         disconnect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) );
0656         disconnect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) );
0657         disconnect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) );
0658         disconnect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) );
0659     }
0660 
0661     beginResetModel();
0662     m_document = document;
0663     endResetModel();
0664 
0665     if ( m_document ) {
0666         connect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) );
0667         connect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) );
0668         connect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) );
0669         connect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) );
0670         connect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) );
0671     }
0672 }
0673 
0674 void KoPADocumentModel::setMasterMode(bool master)
0675 {
0676     m_master = master;
0677     update(); // Rebuild the model
0678 }
0679 
0680 bool KoPADocumentModel::doDrop(QList<KoPAPageBase *> pages, KoPAPageBase *pageAfter, Qt::DropAction action)
0681 {
0682     Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
0683     bool enableMove = true;
0684 
0685     foreach (KoPAPageBase *page, pages) {
0686         if (!m_document->pages(false).contains(page)) {
0687             KoPAPageBase *newPage = page;
0688             pages.replace(pages.indexOf(page), newPage);
0689             enableMove = false;
0690             break;
0691         }
0692     }
0693 
0694     if (((modifiers & Qt::ControlModifier) == 0) &&
0695         ((modifiers & Qt::ShiftModifier) == 0)) {
0696            QMenu popup;
0697            QString seq = QKeySequence(Qt::ShiftModifier).toString();
0698            seq.chop(1);
0699            QAction *popupMoveAction = new QAction(i18n("&Move Here") + '\t' + seq, this);
0700            popupMoveAction->setIcon(koIcon("go-jump"));
0701            seq = QKeySequence(Qt::ControlModifier).toString();
0702            seq.chop(1);
0703            QAction *popupCopyAction = new QAction(i18n("&Copy Here") + '\t' + seq, this);
0704            popupCopyAction->setIcon(koIcon("edit-copy"));
0705            seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString();
0706            seq.chop(1);
0707            QAction *popupCancelAction = new QAction(i18n("C&ancel") + '\t' + QKeySequence(Qt::Key_Escape).toString(), this);
0708            popupCancelAction->setIcon(koIcon("process-stop"));
0709 
0710            if (enableMove) {
0711                popup.addAction(popupMoveAction);
0712            }
0713            popup.addAction(popupCopyAction);
0714            popup.addSeparator();
0715            popup.addAction(popupCancelAction);
0716 
0717            QAction *result = popup.exec(QCursor::pos());
0718 
0719            if (result == popupCopyAction) {
0720                action = Qt::CopyAction;
0721            } else if (result == popupMoveAction) {
0722                action = Qt::MoveAction;
0723            } else {
0724                return false;
0725            }
0726     } else if ((modifiers & Qt::ControlModifier) != 0) {
0727         action = Qt::CopyAction;
0728     } else if ((modifiers & Qt::ShiftModifier) != 0) {
0729         action = Qt::MoveAction;
0730     } else {
0731         return false;
0732     }
0733 
0734     switch (action) {
0735     case Qt::MoveAction: {
0736        KoPAPageMoveCommand *command = new KoPAPageMoveCommand(m_document, pages, pageAfter);
0737        m_document->addCommand( command );
0738        if ((m_document->pageIndex(pageAfter) + pages.count()) < m_document->pageCount()) {
0739             emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, pages.count());
0740        }
0741        return true;
0742     }
0743     case Qt::CopyAction: {
0744        // Copy Pages
0745        KoPAOdfPageSaveHelper saveHelper(m_document, pages);
0746        KoDrag drag;
0747        drag.setOdf(KoOdf::mimeType(m_document->documentType()), saveHelper);
0748        drag.addToClipboard();
0749        //Paste Pages
0750        const QMimeData * data = QApplication::clipboard()->mimeData();
0751        static const KoOdf::DocumentType documentTypes[] = { KoOdf::Graphics, KoOdf::Presentation };
0752 
0753        for (unsigned int i = 0; i < sizeof(documentTypes) / sizeof(KoOdf::DocumentType); ++i) {
0754            if (data->hasFormat( KoOdf::mimeType(documentTypes[i]))) {
0755                KoPAPastePage paste(m_document, pageAfter);
0756                paste.paste(documentTypes[i], data);
0757                break;
0758            }
0759        }
0760        emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, sizeof(documentTypes) / sizeof(KoOdf::DocumentType) - 1);
0761        return true;
0762     }
0763     default:
0764        qDebug("Unknown action: %d ", (int)action);
0765        return false;
0766     }
0767     return false;
0768 }