1 /* This file is part of the KDE project
2  * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
3  * Copyright (C) 2008 Fredy Yanardi <fyanardi@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "KoPADocumentModel.h"
22 
23 #include "KoPADocument.h"
24 #include "KoPAPageBase.h"
25 #include <KoShapePainter.h>
26 #include <KoShapeManager.h>
27 #include <KoShapeContainer.h>
28 #include <KoToolManager.h>
29 #include <KoPageLayout.h>
30 #include <KoCanvasBase.h>
31 #include <KoCanvasController.h>
32 #include <KoSelection.h>
33 #include <KoShapeLayer.h>
34 #include <KoShapeGroup.h>
35 #include <KoShapeGroupCommand.h>
36 #include <KoShapeUngroupCommand.h>
37 #include <KoShapeRenameCommand.h>
38 #include <KoZoomHandler.h>
39 #include <KoPAOdfPageSaveHelper.h>
40 #include <KoDrag.h>
41 #include <KoPAPastePage.h>
42 #include <KoIcon.h>
43 
44 #include <klocalizedstring.h>
45 #include <PageAppDebug.h>
46 
47 #include <QMimeData>
48 #include <QApplication>
49 #include <QClipboard>
50 #include <QMenu>
51 #include <QPainterPath>
52 
53 #include "commands/KoPAPageMoveCommand.h"
54 
KoPADocumentModel(QObject * parent,KoPADocument * document)55 KoPADocumentModel::KoPADocumentModel( QObject* parent, KoPADocument *document )
56 : KoDocumentSectionModel( parent )
57 , m_document(0)
58 , m_master(false)
59 , m_lastContainer( 0 )
60 {
61     setDocument( document );
62 }
63 
supportedDragActions() const64 Qt::DropActions KoPADocumentModel::supportedDragActions() const
65 {
66     return Qt::MoveAction;
67 }
68 
update()69 void KoPADocumentModel::update()
70 {
71     emit layoutAboutToBeChanged();
72     emit layoutChanged();
73     if (m_document) {
74         dataChanged(index(0, 0), index(m_document->pageCount() - 1, columnCount() - 1));
75     }
76 }
77 
rowCount(const QModelIndex & parent) const78 int KoPADocumentModel::rowCount( const QModelIndex &parent ) const
79 {
80     if (!m_document) {
81         return 0;
82     }
83 
84     // check if parent is root node
85     if ( ! parent.isValid() ) {
86         return m_document->pages(m_master).count();
87     }
88 
89     Q_ASSERT(parent.model() == this);
90     Q_ASSERT(parent.internalPointer());
91 
92     KoShapeContainer *parentShape = dynamic_cast<KoShapeContainer*>( (KoShape*)parent.internalPointer() );
93     if ( ! parentShape ) {
94         return 0;
95     }
96 
97     return parentShape->shapeCount();
98 }
99 
columnCount(const QModelIndex &) const100 int KoPADocumentModel::columnCount( const QModelIndex & ) const
101 {
102     return 1;
103 }
104 
index(int row,int column,const QModelIndex & parent) const105 QModelIndex KoPADocumentModel::index( int row, int column, const QModelIndex &parent ) const
106 {
107     if ( !m_document ) {
108         return QModelIndex();
109     }
110 
111     // check if parent is root node
112     if ( ! parent.isValid() ) {
113         if ( row >= 0 && row < m_document->pages(m_master).count() ) {
114             return createIndex( row, column, m_document->pages(m_master).at(row) );
115         } else {
116             return QModelIndex();
117         }
118     }
119 
120     Q_ASSERT(parent.model() == this);
121     Q_ASSERT(parent.internalPointer());
122 
123     KoShapeContainer *parentShape = dynamic_cast<KoShapeContainer*>( (KoShape*)parent.internalPointer() );
124     if ( ! parentShape ) {
125         return QModelIndex();
126     }
127 
128     if ( row < parentShape->shapeCount() ) {
129         return createIndex( row, column, childFromIndex( parentShape, row ) );
130     } else {
131         return QModelIndex();
132     }
133 }
134 
parent(const QModelIndex & child) const135 QModelIndex KoPADocumentModel::parent( const QModelIndex &child ) const
136 {
137     // check if child is root node
138     if ( ! child.isValid() || !m_document ) {
139         return QModelIndex();
140     }
141 
142     Q_ASSERT(child.model() == this);
143     Q_ASSERT(child.internalPointer());
144 
145     KoShape *childShape = static_cast<KoShape*>( child.internalPointer() );
146     if ( ! childShape ) {
147         return QModelIndex();
148     }
149 
150     // get the children's parent shape
151     KoShapeContainer *parentShape = childShape->parent();
152     if ( ! parentShape ) {
153         return QModelIndex();
154     }
155 
156     // get the grandparent to determine the row of the parent shape
157     KoShapeContainer *grandParentShape = parentShape->parent();
158     if ( ! grandParentShape ) {
159         KoPAPageBase* page = dynamic_cast<KoPAPageBase*>( parentShape);
160         return createIndex( m_document->pages(m_master).indexOf( page ), 0, parentShape );
161     }
162 
163     return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape );
164 }
165 
data(const QModelIndex & index,int role) const166 QVariant KoPADocumentModel::data( const QModelIndex &index, int role ) const
167 {
168     if ( ! index.isValid() || !m_document ) {
169         return QVariant();
170     }
171 
172     Q_ASSERT(index.model() == this);
173     Q_ASSERT(index.internalPointer());
174 
175     KoShape *shape = static_cast<KoShape*>( index.internalPointer() );
176 
177     switch (role)
178     {
179         case Qt::DisplayRole:
180         {
181             QString name = shape->name();
182             if ( name.isEmpty() ) {
183                 if ( dynamic_cast<KoPAPageBase *>( shape ) ) {
184                     if (m_document->pageType() == KoPageApp::Slide ) {
185                         name = i18n("Slide %1",  m_document->pageIndex(dynamic_cast<KoPAPageBase *>(shape)) + 1);
186                     } else {
187                         name = i18n("Page  %1", m_document->pageIndex(dynamic_cast<KoPAPageBase *>(shape)) + 1);
188                     }
189                 } else if ( dynamic_cast<KoShapeLayer*>( shape ) ) {
190                     name = i18n("Layer") + QString(" (%1)").arg(shape->zIndex());
191                 } else if ( dynamic_cast<KoShapeGroup*>( shape ) ) {
192                     name = i18n("Group") + QString(" (%1)").arg(shape->zIndex());
193                 } else {
194                     name = i18n("Shape") + QString(" (%1)").arg(shape->zIndex());
195                 }
196             }
197             return name;
198         }
199         case Qt::DecorationRole: return QVariant();//return shape->icon();
200         case Qt::EditRole: return shape->name();
201         case Qt::SizeHintRole:
202         {
203             KoPAPageBase *page = dynamic_cast<KoPAPageBase*>(shape);
204             if (page) { // return actual page size for page
205                 KoPageLayout layout = page->pageLayout();
206                 return QSize(layout.width, layout.height);
207             }
208             else
209                 return shape->size();
210         }
211         case ActiveRole:
212         {
213             KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
214             KoSelection * selection = canvasController->canvas()->shapeManager()->selection();
215             if ( ! selection ) {
216                 return false;
217             }
218 
219             /* KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>( shape );
220             if ( layer )
221                 return (layer == selection->activeLayer() );
222             else */
223             return selection->isSelected( shape );
224         }
225         case PropertiesRole: return QVariant::fromValue( properties( shape ) );
226         case AspectRatioRole:
227         {
228             QTransform matrix = shape->absoluteTransformation( 0 );
229             QRectF bbox = matrix.mapRect( shape->outline().boundingRect() );
230             KoShapeContainer *container = dynamic_cast<KoShapeContainer*>( shape );
231             if ( container ) {
232                 bbox = QRectF();
233                 foreach( KoShape* shape, container->shapes() ) {
234                     bbox = bbox.united( shape->outline().boundingRect() );
235                 }
236             }
237             return qreal(bbox.width()) / bbox.height();
238         }
239         default:
240             if (role >= int(BeginThumbnailRole)) {
241                 return createThumbnail( shape, QSize( role - int(BeginThumbnailRole), role - int(BeginThumbnailRole) ) );
242             } else {
243                 return QVariant();
244             }
245     }
246 }
247 
flags(const QModelIndex & index) const248 Qt::ItemFlags KoPADocumentModel::flags(const QModelIndex &index) const
249 {
250     if ( !m_document ) {
251         return 0;
252     }
253 
254     if ( ! index.isValid() ) {
255         return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
256     }
257 
258     Q_ASSERT(index.model() == this);
259     Q_ASSERT(index.internalPointer());
260 
261     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
262     //if ( dynamic_cast<KoShapeContainer*>( (KoShape*)index.internalPointer() ) )
263         flags |= Qt::ItemIsDropEnabled;
264     return flags;
265 }
266 
setData(const QModelIndex & index,const QVariant & value,int role)267 bool KoPADocumentModel::setData(const QModelIndex &index, const QVariant &value, int role )
268 {
269     if ( ! index.isValid() || !m_document ) {
270         return false;
271     }
272 
273     Q_ASSERT(index.model() == this);
274     Q_ASSERT(index.internalPointer());
275 
276     KoShape *shape = static_cast<KoShape*>( index.internalPointer() );
277     switch (role)
278     {
279         case Qt::DisplayRole:
280         case Qt::EditRole:
281         {
282             KUndo2Command * cmd = new KoShapeRenameCommand( shape, value.toString() );
283             if (dynamic_cast<KoPAPageBase *>(shape)) {
284                 if (m_document->pageType() == KoPageApp::Slide) {
285                     cmd->setText(kundo2_i18n("Rename Slide"));
286                 } else {
287                     cmd->setText(kundo2_i18n("Rename Page"));
288                 }
289             }
290             else if (dynamic_cast<KoShapeLayer *>(shape)) {
291                 cmd->setText(kundo2_i18n("Rename Layer"));
292             }
293             m_document->addCommand( cmd );
294         }   break;
295         case PropertiesRole:
296             setProperties( shape, value.value<PropertyList>());
297             break;
298         case ActiveRole:
299             /* if (value.toBool())
300             {
301                 KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
302                 KoSelection * selection = canvasController->canvas()->shapeManager()->selection();
303 
304                 KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>( shape );
305                 if ( layer && selection ) {
306                     selection->setActiveLayer( layer );
307                 }
308             } */
309             break;
310         default:
311             return false;
312     }
313 
314     emit dataChanged( index, index );
315     return true;
316 }
317 
properties(KoShape * shape) const318 KoDocumentSectionModel::PropertyList KoPADocumentModel::properties( KoShape* shape ) const
319 {
320     PropertyList l;
321 
322     if (KoPAPageBase *page = dynamic_cast<KoPAPageBase *>(shape)) {
323         // The idea is to display the page-number so users know what page-number/slide-number
324         // the shape has also in the case the slide has a name (in which case it's not named
325         // "Slide [slide-number]" any longer.
326         // Maybe we should better use KoTextPage::visiblePageNumber here?
327         l << Property(i18n("Slide"), QString::number(m_document->pageIndex(page) + 1));
328     }
329 
330     l << Property(i18n("Visible"), koIcon("layer-visible-on"), koIcon("layer-visible-off"), shape->isVisible());
331     l << Property(i18n("Locked"), koIcon("object-locked"), koIcon("object-unlocked"), shape->isGeometryProtected());
332     return l;
333 }
334 
setProperties(KoShape * shape,const PropertyList & properties)335 void KoPADocumentModel::setProperties( KoShape* shape, const PropertyList &properties )
336 {
337     bool oldVisibleState = shape->isVisible();
338     bool oldLockedState = shape->isGeometryProtected();
339 
340     shape->setVisible( properties.at( 0 ).state.toBool() );
341     shape->setGeometryProtected( properties.at( 1 ).state.toBool() );
342 
343     if ( ( oldVisibleState != shape->isVisible() ) || ( oldLockedState != shape->isGeometryProtected() ) ) {
344         shape->update();
345     }
346 }
347 
createThumbnail(KoShape * shape,const QSize & thumbSize) const348 QImage KoPADocumentModel::createThumbnail( KoShape* shape, const QSize &thumbSize ) const
349 {
350     QSize size(thumbSize.width(), thumbSize.height());
351     KoShapePainter shapePainter;
352 
353     KoPAPageBase *page = dynamic_cast<KoPAPageBase*>(shape);
354     if (page) { // We create a thumbnail with actual width / height ratio for page
355         KoZoomHandler zoomHandler;
356         KoPageLayout layout = page->pageLayout();
357         qreal ratio = (zoomHandler.resolutionX() * layout.width) / (zoomHandler.resolutionY() * layout.height);
358         if ( ratio > 1 ) {
359             size.setHeight( size.width() / ratio );
360         } else {
361             size.setWidth( size.height() * ratio );
362         }
363         QPixmap pixmap = m_document->pageThumbnail( page, size );
364         return pixmap.toImage();
365     }
366 
367     QList<KoShape*> shapes;
368     KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
369     if (container) {
370         shapes = container->shapes();
371     }
372     shapes.append(shape);
373 
374     shapePainter.setShapes( shapes );
375 
376     QImage thumb( size, QImage::Format_RGB32 );
377     // draw the background of the thumbnail
378     thumb.fill( QColor( Qt::white ).rgb() );
379     shapePainter.paint(thumb);
380 
381     return thumb;
382 }
383 
childFromIndex(KoShapeContainer * parent,int row) const384 KoShape * KoPADocumentModel::childFromIndex( KoShapeContainer *parent, int row ) const
385 {
386     return parent->shapes().at(row);
387 }
388 
indexFromChild(KoShapeContainer * parent,KoShape * child) const389 int KoPADocumentModel::indexFromChild( KoShapeContainer *parent, KoShape *child ) const
390 {
391     if ( !m_document ) {
392         return 0;
393     }
394 
395     return parent->shapes().indexOf( child );
396 }
397 
supportedDropActions() const398 Qt::DropActions KoPADocumentModel::supportedDropActions () const
399 {
400     return Qt::MoveAction | Qt::CopyAction;
401 }
402 
mimeTypes() const403 QStringList KoPADocumentModel::mimeTypes() const
404 {
405     QStringList types;
406     types << QLatin1String("application/x-kopalayermodeldatalist");
407     return types;
408 }
409 
mimeData(const QModelIndexList & indexes) const410 QMimeData * KoPADocumentModel::mimeData( const QModelIndexList & indexes ) const
411 {
412     // check if there is data to encode
413     if ( ! indexes.count() ) {
414         return 0;
415     }
416 
417     // check if we support a format
418     QStringList types = mimeTypes();
419     if ( types.isEmpty() ) {
420         return 0;
421     }
422 
423     QMimeData *data = new QMimeData();
424     QString format = types[0];
425     QByteArray encoded;
426     QDataStream stream(&encoded, QIODevice::WriteOnly);
427 
428     // encode the data
429     QModelIndexList::ConstIterator it = indexes.begin();
430     for( ; it != indexes.end(); ++it) {
431         stream << QVariant::fromValue( qulonglong( it->internalPointer() ) );
432     }
433 
434     data->setData(format, encoded);
435     return data;
436 }
437 
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)438 bool KoPADocumentModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
439 {
440     Q_UNUSED( row );
441     Q_UNUSED( column );
442 
443     // check if the action is supported
444     if ( ! data || action != Qt::MoveAction ) {
445         return false;
446     }
447     // check if the format is supported
448     QStringList types = mimeTypes();
449     if ( types.isEmpty() ) {
450         return false;
451     }
452     QString format = types[0];
453     if ( ! data->hasFormat(format) ) {
454         return false;
455     }
456 
457     QByteArray encoded = data->data( format );
458     QDataStream stream(&encoded, QIODevice::ReadOnly);
459     QList<KoShape*> shapes;
460 
461     // decode the data
462     while( ! stream.atEnd() ) {
463         QVariant v;
464         stream >> v;
465         shapes.append( static_cast<KoShape*>( (void*)v.value<qulonglong>() ) );
466     }
467 
468     QList<KoShape*> toplevelShapes;
469     QList<KoShapeLayer*> layers;
470     QList<KoPAPageBase *> pages;
471     // remove shapes having its parent in the list
472     // and separate the layers
473     foreach( KoShape * shape, shapes ) {
474         // check whether the selection contains page
475         // by the UI rules, the selection should contains page only
476         KoPAPageBase *page = dynamic_cast<KoPAPageBase *>( shape );
477         if ( page ) {
478             pages.append( page );
479             continue;
480         }
481 
482         KoShapeContainer *parentShape = shape->parent();
483         bool hasParentInList = false;
484         while ( parentShape ) {
485             if ( shapes.contains( parentShape ) ) {
486                 hasParentInList = true;
487                 break;
488             }
489             parentShape = parentShape->parent();
490         }
491         if ( hasParentInList ) {
492             continue;
493         }
494 
495         KoShapeLayer * layer = dynamic_cast<KoShapeLayer*>( shape );
496         if ( layer ) {
497             layers.append( layer );
498         } else {
499             toplevelShapes.append( shape );
500         }
501     }
502 
503     // dropping to root, only page(s) is allowed
504     if (!parent.isValid()) {
505         if ( !pages.isEmpty() ) {
506             if ( row < 0 ) {
507                 return false;
508             }
509             KoPAPageBase *after = (row != 0) ? m_document->pageByIndex(row - 1, false) : 0;
510             debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping page(s) as root, moving page(s)";
511             return doDrop(pages, after, action);
512         }
513         else {
514             debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping non-page as root, returning false";
515             return false;
516         }
517     }
518     else if (parent.isValid() && !pages.isEmpty()){
519         if (parent.row() < 0) {
520             return false;
521         }
522         KoPAPageBase *after;
523         if ((m_document->pageIndex(pages.first()) - 1) == parent.row()) {
524             after = (parent.row() != 0) ? m_document->pageByIndex(parent.row() - 1, false) : 0;
525         }
526         else {
527             after = (parent.row() > -1) ? m_document->pageByIndex(parent.row(), false) : 0;
528         }
529         return doDrop(pages, after, action);
530     }
531 
532     KoShape *shape = static_cast<KoShape*>( parent.internalPointer() );
533     KoShapeContainer * container = dynamic_cast<KoShapeContainer*>( shape );
534     if ( container ) {
535         KoShapeGroup * group = dynamic_cast<KoShapeGroup*>( container );
536         if ( group ) {
537             debugPageApp <<"KoPADocumentModel::dropMimeData parent = group";
538             if ( ! toplevelShapes.count() ) {
539                 return false;
540             }
541 
542             emit layoutAboutToBeChanged();
543             beginInsertRows( parent, group->shapeCount(), group->shapeCount()+toplevelShapes.count() );
544 
545             KUndo2Command * cmd = new KUndo2Command();
546             cmd->setText( kundo2_i18n("Reparent shapes") );
547 
548             foreach( KoShape * shape, toplevelShapes ) {
549                 new KoShapeUngroupCommand( shape->parent(), QList<KoShape*>() << shape, QList<KoShape*>(), cmd );
550             }
551 
552             new KoShapeGroupCommand( group, toplevelShapes, cmd );
553             KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
554             canvasController->canvas()->addCommand( cmd );
555 
556             endInsertRows();
557             emit layoutChanged();
558         } else {
559             debugPageApp <<"KoPADocumentModel::dropMimeData parent = container";
560             if ( toplevelShapes.count() ) {
561                 emit layoutAboutToBeChanged();
562                 beginInsertRows( parent, container->shapeCount(), container->shapeCount()+toplevelShapes.count() );
563 
564                 KUndo2Command * cmd = new KUndo2Command();
565                 cmd->setText( kundo2_i18n("Reparent shapes") );
566 
567                 QList<bool> clipped;
568                 QList<bool> inheritsTransform;
569                 foreach( KoShape * shape, toplevelShapes ) {
570                     if ( ! shape->parent() ) {
571                         clipped.append( false );
572                         inheritsTransform.append(false);
573                         continue;
574                     }
575 
576                     clipped.append( shape->parent()->isClipped( shape ) );
577                     inheritsTransform.append(shape->parent()->inheritsTransform(shape));
578                     new KoShapeUngroupCommand( shape->parent(), QList<KoShape*>() << shape, QList<KoShape*>(), cmd );
579                 }
580                 // shapes are dropped on a container, so add them to the container
581                 new KoShapeGroupCommand(container, toplevelShapes, clipped, inheritsTransform, cmd);
582                 KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController();
583                 canvasController->canvas()->addCommand( cmd );
584 
585                 endInsertRows();
586                 emit layoutChanged();
587             } else if ( layers.count() ) {
588                 KoShapeLayer * layer = dynamic_cast<KoShapeLayer*>( container );
589                 if ( ! layer ) {
590                     return false;
591                 }
592 
593                 // TODO layers are dropped on a layer, so change layer ordering
594                 return false;
595             }
596         }
597     } else {
598         debugPageApp <<"KoPADocumentModel::dropMimeData parent = shape";
599         if ( ! toplevelShapes.count() ) {
600             return false;
601         }
602 
603         // TODO shapes are dropped on a shape, reorder them
604         return false;
605     }
606 
607     return true;
608 }
609 
parentIndexFromShape(const KoShape * child)610 QModelIndex KoPADocumentModel::parentIndexFromShape( const KoShape * child )
611 {
612     if ( !m_document ) {
613         return QModelIndex();
614     }
615 
616     // check if child shape is a layer, and return invalid model index if it is
617     const KoShapeLayer *childlayer = dynamic_cast<const KoShapeLayer*>( child );
618     if ( childlayer ) {
619         return QModelIndex();
620     }
621 
622     // get the children's parent shape
623     KoShapeContainer *parentShape = child->parent();
624     if ( ! parentShape ) {
625         return QModelIndex();
626     }
627 
628     // check if the parent is a layer
629     KoShapeLayer *parentLayer = dynamic_cast<KoShapeLayer*>( parentShape );
630 
631 
632     if ( parentLayer ) {
633         KoPAPageBase * page = dynamic_cast<KoPAPageBase*>( parentLayer->parent() );
634         if ( page ) {
635             return createIndex( m_document->pages(m_master).count() - 1 - m_document->pages(m_master).indexOf( page ), 0, parentLayer );
636         }
637     }
638     // get the grandparent to determine the row of the parent shape
639     KoShapeContainer *grandParentShape = parentShape->parent();
640     if ( ! grandParentShape ) {
641         return QModelIndex();
642     }
643 
644     return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape );
645 }
646 
setDocument(KoPADocument * document)647 void KoPADocumentModel::setDocument( KoPADocument* document )
648 {
649     if (m_document == document) {
650         return;
651     }
652 
653     if (m_document) {
654         disconnect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) );
655         disconnect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) );
656         disconnect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) );
657         disconnect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) );
658         disconnect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) );
659     }
660 
661     beginResetModel();
662     m_document = document;
663     endResetModel();
664 
665     if ( m_document ) {
666         connect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) );
667         connect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) );
668         connect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) );
669         connect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) );
670         connect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) );
671     }
672 }
673 
setMasterMode(bool master)674 void KoPADocumentModel::setMasterMode(bool master)
675 {
676     m_master = master;
677     update(); // Rebuild the model
678 }
679 
doDrop(QList<KoPAPageBase * > pages,KoPAPageBase * pageAfter,Qt::DropAction action)680 bool KoPADocumentModel::doDrop(QList<KoPAPageBase *> pages, KoPAPageBase *pageAfter, Qt::DropAction action)
681 {
682     Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
683     bool enableMove = true;
684 
685     foreach (KoPAPageBase *page, pages) {
686         if (!m_document->pages(false).contains(page)) {
687             KoPAPageBase *newPage = page;
688             pages.replace(pages.indexOf(page), newPage);
689             enableMove = false;
690             break;
691         }
692     }
693 
694     if (((modifiers & Qt::ControlModifier) == 0) &&
695         ((modifiers & Qt::ShiftModifier) == 0)) {
696            QMenu popup;
697            QString seq = QKeySequence(Qt::ShiftModifier).toString();
698            seq.chop(1);
699            QAction *popupMoveAction = new QAction(i18n("&Move Here") + '\t' + seq, this);
700            popupMoveAction->setIcon(koIcon("go-jump"));
701            seq = QKeySequence(Qt::ControlModifier).toString();
702            seq.chop(1);
703            QAction *popupCopyAction = new QAction(i18n("&Copy Here") + '\t' + seq, this);
704            popupCopyAction->setIcon(koIcon("edit-copy"));
705            seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString();
706            seq.chop(1);
707            QAction *popupCancelAction = new QAction(i18n("C&ancel") + '\t' + QKeySequence(Qt::Key_Escape).toString(), this);
708            popupCancelAction->setIcon(koIcon("process-stop"));
709 
710            if (enableMove) {
711                popup.addAction(popupMoveAction);
712            }
713            popup.addAction(popupCopyAction);
714            popup.addSeparator();
715            popup.addAction(popupCancelAction);
716 
717            QAction *result = popup.exec(QCursor::pos());
718 
719            if (result == popupCopyAction) {
720                action = Qt::CopyAction;
721            } else if (result == popupMoveAction) {
722                action = Qt::MoveAction;
723            } else {
724                return false;
725            }
726     } else if ((modifiers & Qt::ControlModifier) != 0) {
727         action = Qt::CopyAction;
728     } else if ((modifiers & Qt::ShiftModifier) != 0) {
729         action = Qt::MoveAction;
730     } else {
731         return false;
732     }
733 
734     switch (action) {
735     case Qt::MoveAction: {
736        KoPAPageMoveCommand *command = new KoPAPageMoveCommand(m_document, pages, pageAfter);
737        m_document->addCommand( command );
738        if ((m_document->pageIndex(pageAfter) + pages.count()) < m_document->pageCount()) {
739             emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, pages.count());
740        }
741        return true;
742     }
743     case Qt::CopyAction: {
744        // Copy Pages
745        KoPAOdfPageSaveHelper saveHelper(m_document, pages);
746        KoDrag drag;
747        drag.setOdf(KoOdf::mimeType(m_document->documentType()), saveHelper);
748        drag.addToClipboard();
749        //Paste Pages
750        const QMimeData * data = QApplication::clipboard()->mimeData();
751        static const KoOdf::DocumentType documentTypes[] = { KoOdf::Graphics, KoOdf::Presentation };
752 
753        for (unsigned int i = 0; i < sizeof(documentTypes) / sizeof(KoOdf::DocumentType); ++i) {
754            if (data->hasFormat( KoOdf::mimeType(documentTypes[i]))) {
755                KoPAPastePage paste(m_document, pageAfter);
756                paste.paste(documentTypes[i], data);
757                break;
758            }
759        }
760        emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, sizeof(documentTypes) / sizeof(KoOdf::DocumentType) - 1);
761        return true;
762     }
763     default:
764        qDebug("Unknown action: %d ", (int)action);
765        return false;
766     }
767     return false;
768 }
769