diff --git a/components/models/ContentsModel.cpp b/components/models/ContentsModel.cpp index f7cf60e286a..7c4a3aeea4c 100644 --- a/components/models/ContentsModel.cpp +++ b/components/models/ContentsModel.cpp @@ -1,188 +1,194 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "ContentsModel.h" #include #include #include #include "Document.h" #include "PresentationContentsModelImpl.h" #include "SpreadsheetContentsModelImpl.h" #include "TextContentsModelImpl.h" using namespace Calligra::Components; class ContentsModel::Private { public: Private() : useToC{false}, impl{nullptr}, document{nullptr}, thumbnailSize{128, 128} { } bool useToC; ContentsModelImpl* impl; Document* document; QSize thumbnailSize; }; ContentsModel::ContentsModel(QObject* parent) : QAbstractListModel{parent}, d{new Private} { } ContentsModel::~ContentsModel() { delete d; } QVariant ContentsModel::data(const QModelIndex& index, int role) const { if(!d->impl || !index.isValid()) return QVariant(); return d->impl->data(index.row(), static_cast(role)); } int ContentsModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); if(d->impl) { return d->impl->rowCount(); } return 0; } Document* ContentsModel::document() const { return d->document; } void ContentsModel::setDocument(Document* newDocument) { if(newDocument != d->document) { if(d->document) { disconnect(d->document, &Document::statusChanged, this, &ContentsModel::updateImpl); } d->document = newDocument; connect(d->document, &Document::statusChanged, this, &ContentsModel::updateImpl); updateImpl(); emit documentChanged(); } } QSize ContentsModel::thumbnailSize() const { return d->thumbnailSize; } void ContentsModel::setThumbnailSize(const QSize& newValue) { if(newValue != d->thumbnailSize) { d->thumbnailSize = newValue; if(d->impl) { d->impl->setThumbnailSize(newValue); emit dataChanged(index(0), index(d->impl->rowCount() - 1), QVector{} << ThumbnailRole); } emit thumbnailSizeChanged(); } } void ContentsModel::setUseToC(bool newValue) { beginResetModel(); if(d->impl) d->impl->setUseToC(newValue); emit useToCChanged(); endResetModel(); } bool ContentsModel::useToC() const { return d->useToC; } QImage ContentsModel::thumbnail(int index, int width) const { if(!d->impl) return QImage{}; if(index < 0 || index >= d->impl->rowCount()) return QImage{}; return d->impl->thumbnail(index, width); } void ContentsModel::updateImpl() { beginResetModel(); if(d->impl) { delete d->impl; } if(d->document && d->document->status() == DocumentStatus::Loaded) { switch(d->document->documentType()) { case DocumentType::TextDocument: { auto textImpl = new TextContentsModelImpl{d->document->koDocument(), dynamic_cast(d->document->canvas())}; d->impl = textImpl; connect(textImpl, &TextContentsModelImpl::listContentsCompleted, this, &ContentsModel::reset); break; } case DocumentType::Spreadsheet: d->impl = new SpreadsheetContentsModelImpl{d->document->koDocument()}; break; case DocumentType::Presentation: d->impl = new PresentationContentsModelImpl{d->document->koDocument()}; break; default: d->impl = nullptr; break; } } else { d->impl = nullptr; } if(d->impl) { d->impl->setThumbnailSize(d->thumbnailSize); d->impl->setUseToC(d->useToC); } endResetModel(); } +void ContentsModel::reset() +{ + beginResetModel(); + endResetModel(); +} + QHash ContentsModel::roleNames() const { QHash roleNames; roleNames.insert(TitleRole, "title"); roleNames.insert(LevelRole, "level"); roleNames.insert(ThumbnailRole, "thumbnail"); roleNames.insert(ContentIndexRole, "contentIndex"); return roleNames; } diff --git a/components/models/ContentsModel.h b/components/models/ContentsModel.h index 9dcb3d40a84..81623d47a40 100644 --- a/components/models/ContentsModel.h +++ b/components/models/ContentsModel.h @@ -1,110 +1,111 @@ /* * This file is part of the KDE project * * Copyright (C) 2013 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef CALLIGRA_COMPONENTS_CONTENTSMODEL_H #define CALLIGRA_COMPONENTS_CONTENTSMODEL_H #include #include namespace Calligra { namespace Components { class Document; class ContentsModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(Calligra::Components::Document* document READ document WRITE setDocument NOTIFY documentChanged) /** * \property thumbnailSize * \brief The size of thumbnails this model creates. * * \default 128x128 * \get thumbnailSize() const * \set setthumbnailSize() * \notify thumbnailSizeChanged() */ Q_PROPERTY(QSize thumbnailSize READ thumbnailSize WRITE setThumbnailSize NOTIFY thumbnailSizeChanged) Q_PROPERTY(bool useToC READ useToC WRITE setUseToC NOTIFY useToCChanged) public: enum Role { TitleRole = Qt::UserRole + 1, LevelRole, ThumbnailRole, ContentIndexRole, }; explicit ContentsModel(QObject* parent = 0); virtual ~ContentsModel(); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; virtual int rowCount(const QModelIndex& parent = QModelIndex{}) const Q_DECL_OVERRIDE; Document* document() const; void setDocument(Document* newDocument); /** * Getter for property #thumbnailSize. */ QSize thumbnailSize() const; /** * Setter for property #thumbnailSize. */ void setThumbnailSize(const QSize& newValue); void setUseToC(bool newValue); bool useToC() const; /** * Render a thumbnail at a specified width. * * \param index The index of the item to render a thumbnail of. * \param width The width to render the thumbnail at. * \return A thumbnail or an empty image when the thumbnail could not * be rendered. */ Q_INVOKABLE QImage thumbnail(int index, int width) const; Q_SIGNALS: void documentChanged(); /** * Notify signal for property #thumbnailSize. */ void thumbnailSizeChanged(); void useToCChanged(); protected: virtual QHash roleNames() const; private Q_SLOTS: void updateImpl(); + void reset(); private: class Private; Private* const d; }; } // Namespace Components } // Namespace Calligra #endif // CALLIGRA_COMPONENTS_CONTENTSMODEL_H diff --git a/karbon/ui/dockers/KarbonLayerModel.cpp b/karbon/ui/dockers/KarbonLayerModel.cpp index 586f92f72ba..8d24856d93e 100644 --- a/karbon/ui/dockers/KarbonLayerModel.cpp +++ b/karbon/ui/dockers/KarbonLayerModel.cpp @@ -1,530 +1,535 @@ /* This file is part of the KDE project * Copyright (C) 2007-2008,2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KarbonLayerModel.h" #include "KarbonUiDebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KoShapeContainer *shapeToContainer(KoShape *shape) { KoShapeGroup *group = dynamic_cast(shape); if (group) return group; KoShapeLayer *layer = dynamic_cast(shape); if (layer) return layer; return 0; } KarbonLayerModel::KarbonLayerModel(QObject * parent) : KoDocumentSectionModel(parent), m_document(0) { - setSupportedDragActions(Qt::MoveAction); +} + +Qt::DropActions KarbonLayerModel::supportedDragActions() const +{ + return Qt::MoveAction; } void KarbonLayerModel::update() { emit layoutAboutToBeChanged(); emit layoutChanged(); } void KarbonLayerModel::setDocument(KarbonDocument * newDocument) { + beginResetModel(); m_document = newDocument; - reset(); + endResetModel(); } int KarbonLayerModel::rowCount(const QModelIndex &parent) const { if (! m_document) return 0; // check if parent is root node if (! parent.isValid()) return m_document->layers().count(); Q_ASSERT(parent.model() == this); Q_ASSERT(parent.internalPointer()); KoShapeContainer *parentShape = shapeToContainer((KoShape*)parent.internalPointer()); if (! parentShape) return 0; return parentShape->shapeCount(); } int KarbonLayerModel::columnCount(const QModelIndex &) const { if (! m_document) return 0; else return 1; } QModelIndex KarbonLayerModel::index(int row, int column, const QModelIndex &parent) const { if (! m_document) return QModelIndex(); // check if parent is root node if (! parent.isValid()) { if (row >= 0 && row < m_document->layers().count()) return createIndex(row, column, m_document->layers().at(row)); else return QModelIndex(); } Q_ASSERT(parent.model() == this); Q_ASSERT(parent.internalPointer()); KoShapeContainer *parentShape = shapeToContainer((KoShape*)parent.internalPointer()); if (! parentShape) return QModelIndex(); if (row < parentShape->shapeCount()) return createIndex(row, column, childFromIndex(parentShape, row)); else return QModelIndex(); } QModelIndex KarbonLayerModel::parent(const QModelIndex &child) const { // check if child is root node if (! m_document || ! child.isValid()) return QModelIndex(); Q_ASSERT(child.model() == this); Q_ASSERT(child.internalPointer()); KoShape *childShape = static_cast(child.internalPointer()); if (! childShape) return QModelIndex(); return parentIndexFromShape(childShape); } QVariant KarbonLayerModel::data(const QModelIndex &index, int role) const { if (! index.isValid()) return QVariant(); Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); KoShape *shape = static_cast(index.internalPointer()); switch (role) { case Qt::DisplayRole: { QString name = shape->name(); if (name.isEmpty()) { if (dynamic_cast(shape)) name = i18n("Layer"); else if (dynamic_cast(shape)) name = i18nc("A group of shapes", "Group"); else name = i18n("Shape"); } return name; } case Qt::DecorationRole: return QVariant(); case Qt::EditRole: return shape->name(); case Qt::SizeHintRole: return shape->size(); case ActiveRole: { KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection * selection = canvasController->canvas()->shapeManager()->selection(); if (! selection) return false; KoShapeLayer *layer = dynamic_cast(shape); if (layer) return (layer == selection->activeLayer()); else return selection->isSelected(shape); } case PropertiesRole: return QVariant::fromValue(properties(shape)); case AspectRatioRole: { QTransform matrix = shape->absoluteTransformation(0); QRectF bbox = matrix.mapRect(shape->outline().boundingRect()); KoShapeContainer *container = dynamic_cast(shape); if (container) { bbox = QRectF(); foreach(KoShape* shape, container->shapes()) { bbox = bbox.united(shape->outline().boundingRect()); } } return qreal(bbox.width()) / bbox.height(); } default: if (role >= int(BeginThumbnailRole)) return createThumbnail(shape, QSize(role - int(BeginThumbnailRole), role - int(BeginThumbnailRole))); else return QVariant(); } } Qt::ItemFlags KarbonLayerModel::flags(const QModelIndex &index) const { if (! index.isValid()) return 0; Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; //if( dynamic_cast( (KoShape*)index.internalPointer() ) ) flags |= Qt::ItemIsDropEnabled; return flags; } bool KarbonLayerModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (! index.isValid()) return false; Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); KoShape *shape = static_cast(index.internalPointer()); switch (role) { case Qt::DisplayRole: case Qt::EditRole: shape->setName(value.toString()); break; case PropertiesRole: setProperties(shape, value.value()); // fall through case ActiveRole: { KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection * selection = canvasController->canvas()->shapeManager()->selection(); KoShapeLayer *layer = dynamic_cast(shape); if (layer && selection) selection->setActiveLayer(layer); } break; default: return false; } emit dataChanged(index, index); return true; } KoDocumentSectionModel::PropertyList KarbonLayerModel::properties(KoShape* shape) const { PropertyList l; l << Property(i18nc("Visibility state of the shape", "Visible"), koIcon("layer-visible-on"), koIcon("layer-visible-off"), shape->isVisible()); l << Property(i18nc("Lock state of the shape", "Locked"), koIcon("object-locked"), koIcon("object-unlocked"), shape->isGeometryProtected()); l << Property(i18nc("The z-index of the shape", "zIndex"), QString("%1").arg(shape->zIndex())); l << Property(i18nc("The opacity of the shape", "Opacity"), QString("%1").arg(1.0 - shape->transparency())); l << Property(i18nc("Clipped state of the shape", "Clipped"), shape->clipPath() ? i18n("yes") : i18n("no")); return l; } void KarbonLayerModel::setProperties(KoShape* shape, const PropertyList &properties) { bool oldVisibleState = shape->isVisible(); bool oldLockedState = shape->isGeometryProtected(); bool newVisibleState = properties.at(0).state.toBool(); bool newLockedState = properties.at(1).state.toBool(); shape->setVisible(newVisibleState); shape->setGeometryProtected(newLockedState); KoShapeContainer * container = dynamic_cast(shape); if (container) lockRecursively(container, newLockedState); else shape->setSelectable(!newLockedState); if ((oldVisibleState != shape->isVisible()) || (oldLockedState != shape->isGeometryProtected())) shape->update(); } void KarbonLayerModel::lockRecursively(KoShapeContainer *container, bool lock) { if (!container) return; if (!lock) { container->setSelectable(!container->isGeometryProtected()); } else { container->setSelectable(!lock); } foreach(KoShape *shape, container->shapes()) { KoShapeContainer * shapeContainer = dynamic_cast(shape); if (shapeContainer) { lockRecursively(shapeContainer, lock); } else { if (!lock) { shape->setSelectable(!shape->isGeometryProtected()); } else { shape->setSelectable(!lock); } } } } QImage KarbonLayerModel::createThumbnail(KoShape* shape, const QSize &thumbSize) const { KoShapePainter painter; QList shapes; shapes.append(shape); KoShapeContainer * container = dynamic_cast(shape); if (container) shapes.append(container->shapes()); painter.setShapes(shapes); QImage thumb(thumbSize, QImage::Format_RGB32); // draw the background of the thumbnail thumb.fill(QColor(Qt::white).rgb()); QRect imageRect = thumb.rect(); // use 2 pixel border around the content imageRect.adjust(2, 2, -2, -2); QPainter p(&thumb); painter.paint(p, imageRect, painter.contentRect()); return thumb; } KoShape * KarbonLayerModel::childFromIndex(KoShapeContainer *parent, int row) const { return parent->shapes().at(row); } int KarbonLayerModel::indexFromChild(KoShapeContainer *parent, KoShape *child) const { return parent->shapes().indexOf(child); } Qt::DropActions KarbonLayerModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } QStringList KarbonLayerModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-karbonlayermodeldatalist"); return types; } QMimeData * KarbonLayerModel::mimeData(const QModelIndexList & indexes) const { // check if there is data to encode if (! indexes.count()) return 0; // check if we support a format QStringList types = mimeTypes(); if (types.isEmpty()) return 0; QMimeData *data = new QMimeData(); QString format = types[0]; QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); // encode the data QModelIndexList::ConstIterator it = indexes.begin(); for (; it != indexes.end(); ++it) stream << QVariant::fromValue(qulonglong(it->internalPointer())); data->setData(format, encoded); return data; } bool KarbonLayerModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) { Q_UNUSED(row); Q_UNUSED(column); // check if the action is supported if (! data || action != Qt::MoveAction) return false; // check if the format is supported QStringList types = mimeTypes(); if (types.isEmpty()) return false; QString format = types[0]; if (! data->hasFormat(format)) return false; QByteArray encoded = data->data(format); QDataStream stream(&encoded, QIODevice::ReadOnly); QList shapes; // decode the data while (! stream.atEnd()) { QVariant v; stream >> v; shapes.append(static_cast((void*)v.value())); } // no shapes to drop, exit gracefully if (shapes.count() == 0) return false; QList toplevelShapes; QList layers; // remove shapes having its parent in the list // and separate the layers foreach(KoShape * shape, shapes) { KoShapeContainer * parent = shape->parent(); bool hasParentInList = false; while (parent) { if (shapes.contains(parent)) { hasParentInList = true; break; } parent = parent->parent(); } if (hasParentInList) continue; KoShapeLayer * layer = dynamic_cast(shape); if (layer) layers.append(layer); else toplevelShapes.append(shape); } if (! parent.isValid()) { debugKarbonUi << "KarbonLayerModel::dropMimeData parent = root"; return false; } KoShape *shape = static_cast(parent.internalPointer()); KoShapeContainer * container = dynamic_cast(shape); if (container) { KoShapeGroup * group = dynamic_cast(container); KoShapeLayer * layer = dynamic_cast(container); if (layer && layers.count()) { debugKarbonUi << "dropping layer on a layer (not implemented yet)"; // TODO layers are dropped on a layer, so change layer ordering return false; } else if (group || layer) { debugKarbonUi << "dropping on group or layer"; if (! toplevelShapes.count()) return false; emit layoutAboutToBeChanged(); beginInsertRows(parent, container->shapeCount(), container->shapeCount() + toplevelShapes.count()); KUndo2Command * cmd = new KUndo2Command(); cmd->setText(kundo2_i18n("Reparent shapes")); QList clipped, inheritTransform; foreach(KoShape * shape, toplevelShapes) { new KoShapeUngroupCommand(shape->parent(), QList() << shape, QList(), cmd); clipped.append(false); inheritTransform.append(false); } if (group) { new KoShapeGroupCommand(group, toplevelShapes, cmd); } else if (layer) { new KoShapeGroupCommand(layer, toplevelShapes, clipped, inheritTransform, cmd); } KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); canvasController->canvas()->addCommand(cmd); endInsertRows(); emit layoutChanged(); } else { debugKarbonUi << "dropping on unhandled container (" << container->shapeId() << ")"; // every other container we don't want to handle return false; } } else { debugKarbonUi << "KarbonLayerModel::dropMimeData parent = shape"; if (! toplevelShapes.count()) return false; // TODO shapes are dropped on a shape, reorder them return false; } return true; } QModelIndex KarbonLayerModel::parentIndexFromShape(const KoShape * child) const { if (! m_document) return QModelIndex(); // check if child shape is a layer, and return invalid model index if it is const KoShapeLayer *childlayer = dynamic_cast(child); if (childlayer) return QModelIndex(); // get the children's parent shape KoShapeContainer *parentShape = child->parent(); if (! parentShape) return QModelIndex(); // check if the parent is a layer KoShapeLayer *parentLayer = dynamic_cast(parentShape); if (parentLayer) return createIndex(m_document->layers().indexOf(parentLayer), 0, parentShape); // get the grandparent to determine the row of the parent shape KoShapeContainer *grandParentShape = parentShape->parent(); if (! grandParentShape) return QModelIndex(); return createIndex(indexFromChild(grandParentShape, parentShape), 0, parentShape); } // kate: replace-tabs on; space-indent on; indent-width 4; mixedindent off; indent-mode cstyle; diff --git a/karbon/ui/dockers/KarbonLayerModel.h b/karbon/ui/dockers/KarbonLayerModel.h index 525d572e652..dce143be01d 100644 --- a/karbon/ui/dockers/KarbonLayerModel.h +++ b/karbon/ui/dockers/KarbonLayerModel.h @@ -1,80 +1,81 @@ /* This file is part of the KDE project * Copyright (C) 2007-2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KARBONLAYERMODEL_H #define KARBONLAYERMODEL_H #include #include class KarbonDocument; class KoShape; class KoShapeContainer; class QAbstractItemModel; class KarbonLayerModel : public KoDocumentSectionModel { Q_OBJECT public: /// Constructs a new layer model using the specified documents data explicit KarbonLayerModel(QObject *parent = 0); /// Sets a new document to show contents of void setDocument(KarbonDocument * newDocument); // from QAbstractItemModel virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &child) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual Qt::DropActions supportedDropActions() const; virtual QStringList mimeTypes() const; virtual QMimeData * mimeData(const QModelIndexList & indexes) const; virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent); + virtual Qt::DropActions supportedDragActions() const; public Q_SLOTS: /// Triggers an update of the complete model void update(); private: /// Returns properties of the given shape PropertyList properties(KoShape* shape) const; /// Sets the properties on the given shape void setProperties(KoShape* shape, const PropertyList &properties); /// Creates a thumbnail image with the specified size from the given shape QImage createThumbnail(KoShape* shape, const QSize &thumbSize) const; /// Returns the child shape with the given index from the parent shape KoShape * childFromIndex(KoShapeContainer *parent, int row) const; /// Returns the zero based index of a child shape within its parent shape int indexFromChild(KoShapeContainer *parent, KoShape *child) const; /// Returns the parent model index from the given child shape QModelIndex parentIndexFromShape(const KoShape * child) const; /// Recursively locks children of the specified shape container void lockRecursively(KoShapeContainer *container, bool lock); QPointer m_document; ///< the underlying data structure }; #endif // KARBONLAYERMODEL_H diff --git a/libs/pageapp/KoPADocumentModel.cpp b/libs/pageapp/KoPADocumentModel.cpp index 444fc8da163..d5fd933f7ae 100644 --- a/libs/pageapp/KoPADocumentModel.cpp +++ b/libs/pageapp/KoPADocumentModel.cpp @@ -1,761 +1,767 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2008 Fredy Yanardi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoPADocumentModel.h" #include "KoPADocument.h" #include "KoPAPageBase.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "commands/KoPAPageMoveCommand.h" KoPADocumentModel::KoPADocumentModel( QObject* parent, KoPADocument *document ) : KoDocumentSectionModel( parent ) , m_document(0) , m_master(false) , m_lastContainer( 0 ) { setDocument( document ); - setSupportedDragActions( Qt::MoveAction ); +} + +Qt::DropActions KoPADocumentModel::supportedDragActions() const +{ + return Qt::MoveAction; } void KoPADocumentModel::update() { emit layoutAboutToBeChanged(); emit layoutChanged(); if (m_document) { dataChanged(index(0, 0), index(m_document->pageCount() - 1, columnCount() - 1)); } } int KoPADocumentModel::rowCount( const QModelIndex &parent ) const { if (!m_document) { return 0; } // check if parent is root node if ( ! parent.isValid() ) { return m_document->pages(m_master).count(); } Q_ASSERT(parent.model() == this); Q_ASSERT(parent.internalPointer()); KoShapeContainer *parentShape = dynamic_cast( (KoShape*)parent.internalPointer() ); if ( ! parentShape ) { return 0; } return parentShape->shapeCount(); } int KoPADocumentModel::columnCount( const QModelIndex & ) const { return 1; } QModelIndex KoPADocumentModel::index( int row, int column, const QModelIndex &parent ) const { if ( !m_document ) { return QModelIndex(); } // check if parent is root node if ( ! parent.isValid() ) { if ( row >= 0 && row < m_document->pages(m_master).count() ) { return createIndex( row, column, m_document->pages(m_master).at(row) ); } else { return QModelIndex(); } } Q_ASSERT(parent.model() == this); Q_ASSERT(parent.internalPointer()); KoShapeContainer *parentShape = dynamic_cast( (KoShape*)parent.internalPointer() ); if ( ! parentShape ) { return QModelIndex(); } if ( row < parentShape->shapeCount() ) { return createIndex( row, column, childFromIndex( parentShape, row ) ); } else { return QModelIndex(); } } QModelIndex KoPADocumentModel::parent( const QModelIndex &child ) const { // check if child is root node if ( ! child.isValid() || !m_document ) { return QModelIndex(); } Q_ASSERT(child.model() == this); Q_ASSERT(child.internalPointer()); KoShape *childShape = static_cast( child.internalPointer() ); if ( ! childShape ) { return QModelIndex(); } // get the children's parent shape KoShapeContainer *parentShape = childShape->parent(); if ( ! parentShape ) { return QModelIndex(); } // get the grandparent to determine the row of the parent shape KoShapeContainer *grandParentShape = parentShape->parent(); if ( ! grandParentShape ) { KoPAPageBase* page = dynamic_cast( parentShape); return createIndex( m_document->pages(m_master).indexOf( page ), 0, parentShape ); } return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape ); } QVariant KoPADocumentModel::data( const QModelIndex &index, int role ) const { if ( ! index.isValid() || !m_document ) { return QVariant(); } Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); KoShape *shape = static_cast( index.internalPointer() ); switch (role) { case Qt::DisplayRole: { QString name = shape->name(); if ( name.isEmpty() ) { if ( dynamic_cast( shape ) ) { if (m_document->pageType() == KoPageApp::Slide ) { name = i18n("Slide %1", m_document->pageIndex(dynamic_cast(shape)) + 1); } else { name = i18n("Page %1", m_document->pageIndex(dynamic_cast(shape)) + 1); } } else if ( dynamic_cast( shape ) ) { name = i18n("Layer") + QString(" (%1)").arg(shape->zIndex()); } else if ( dynamic_cast( shape ) ) { name = i18n("Group") + QString(" (%1)").arg(shape->zIndex()); } else { name = i18n("Shape") + QString(" (%1)").arg(shape->zIndex()); } } return name; } case Qt::DecorationRole: return QVariant();//return shape->icon(); case Qt::EditRole: return shape->name(); case Qt::SizeHintRole: { KoPAPageBase *page = dynamic_cast(shape); if (page) { // return actual page size for page KoPageLayout layout = page->pageLayout(); return QSize(layout.width, layout.height); } else return shape->size(); } case ActiveRole: { KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection * selection = canvasController->canvas()->shapeManager()->selection(); if ( ! selection ) { return false; } /* KoShapeLayer *layer = dynamic_cast( shape ); if ( layer ) return (layer == selection->activeLayer() ); else */ return selection->isSelected( shape ); } case PropertiesRole: return QVariant::fromValue( properties( shape ) ); case AspectRatioRole: { QTransform matrix = shape->absoluteTransformation( 0 ); QRectF bbox = matrix.mapRect( shape->outline().boundingRect() ); KoShapeContainer *container = dynamic_cast( shape ); if ( container ) { bbox = QRectF(); foreach( KoShape* shape, container->shapes() ) { bbox = bbox.united( shape->outline().boundingRect() ); } } return qreal(bbox.width()) / bbox.height(); } default: if (role >= int(BeginThumbnailRole)) { return createThumbnail( shape, QSize( role - int(BeginThumbnailRole), role - int(BeginThumbnailRole) ) ); } else { return QVariant(); } } } Qt::ItemFlags KoPADocumentModel::flags(const QModelIndex &index) const { if ( !m_document ) { return 0; } if ( ! index.isValid() ) { return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; } Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; //if ( dynamic_cast( (KoShape*)index.internalPointer() ) ) flags |= Qt::ItemIsDropEnabled; return flags; } bool KoPADocumentModel::setData(const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() || !m_document ) { return false; } Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); KoShape *shape = static_cast( index.internalPointer() ); switch (role) { case Qt::DisplayRole: case Qt::EditRole: { KUndo2Command * cmd = new KoShapeRenameCommand( shape, value.toString() ); if (dynamic_cast(shape)) { if (m_document->pageType() == KoPageApp::Slide) { cmd->setText(kundo2_i18n("Rename Slide")); } else { cmd->setText(kundo2_i18n("Rename Page")); } } else if (dynamic_cast(shape)) { cmd->setText(kundo2_i18n("Rename Layer")); } m_document->addCommand( cmd ); } break; case PropertiesRole: setProperties( shape, value.value()); break; case ActiveRole: /* if (value.toBool()) { KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); KoSelection * selection = canvasController->canvas()->shapeManager()->selection(); KoShapeLayer *layer = dynamic_cast( shape ); if ( layer && selection ) { selection->setActiveLayer( layer ); } } */ break; default: return false; } emit dataChanged( index, index ); return true; } KoDocumentSectionModel::PropertyList KoPADocumentModel::properties( KoShape* shape ) const { PropertyList l; if (KoPAPageBase *page = dynamic_cast(shape)) { // The idea is to display the page-number so users know what page-number/slide-number // the shape has also in the case the slide has a name (in which case it's not named // "Slide [slide-number]" any longer. // Maybe we should better use KoTextPage::visiblePageNumber here? l << Property(i18n("Slide"), QString::number(m_document->pageIndex(page) + 1)); } l << Property(i18n("Visible"), koIcon("layer-visible-on"), koIcon("layer-visible-off"), shape->isVisible()); l << Property(i18n("Locked"), koIcon("object-locked"), koIcon("object-unlocked"), shape->isGeometryProtected()); return l; } void KoPADocumentModel::setProperties( KoShape* shape, const PropertyList &properties ) { bool oldVisibleState = shape->isVisible(); bool oldLockedState = shape->isGeometryProtected(); shape->setVisible( properties.at( 0 ).state.toBool() ); shape->setGeometryProtected( properties.at( 1 ).state.toBool() ); if ( ( oldVisibleState != shape->isVisible() ) || ( oldLockedState != shape->isGeometryProtected() ) ) { shape->update(); } } QImage KoPADocumentModel::createThumbnail( KoShape* shape, const QSize &thumbSize ) const { QSize size(thumbSize.width(), thumbSize.height()); KoShapePainter shapePainter; KoPAPageBase *page = dynamic_cast(shape); if (page) { // We create a thumbnail with actual width / height ratio for page KoZoomHandler zoomHandler; KoPageLayout layout = page->pageLayout(); qreal ratio = (zoomHandler.resolutionX() * layout.width) / (zoomHandler.resolutionY() * layout.height); if ( ratio > 1 ) { size.setHeight( size.width() / ratio ); } else { size.setWidth( size.height() * ratio ); } QPixmap pixmap = m_document->pageThumbnail( page, size ); return pixmap.toImage(); } QList shapes; KoShapeContainer *container = dynamic_cast(shape); if (container) { shapes = container->shapes(); } shapes.append(shape); shapePainter.setShapes( shapes ); QImage thumb( size, QImage::Format_RGB32 ); // draw the background of the thumbnail thumb.fill( QColor( Qt::white ).rgb() ); shapePainter.paint(thumb); return thumb; } KoShape * KoPADocumentModel::childFromIndex( KoShapeContainer *parent, int row ) const { return parent->shapes().at(row); } int KoPADocumentModel::indexFromChild( KoShapeContainer *parent, KoShape *child ) const { if ( !m_document ) { return 0; } return parent->shapes().indexOf( child ); } Qt::DropActions KoPADocumentModel::supportedDropActions () const { return Qt::MoveAction | Qt::CopyAction; } QStringList KoPADocumentModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-kopalayermodeldatalist"); return types; } QMimeData * KoPADocumentModel::mimeData( const QModelIndexList & indexes ) const { // check if there is data to encode if ( ! indexes.count() ) { return 0; } // check if we support a format QStringList types = mimeTypes(); if ( types.isEmpty() ) { return 0; } QMimeData *data = new QMimeData(); QString format = types[0]; QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); // encode the data QModelIndexList::ConstIterator it = indexes.begin(); for( ; it != indexes.end(); ++it) { stream << QVariant::fromValue( qulonglong( it->internalPointer() ) ); } data->setData(format, encoded); return data; } bool KoPADocumentModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) { Q_UNUSED( row ); Q_UNUSED( column ); // check if the action is supported if ( ! data || action != Qt::MoveAction ) { return false; } // check if the format is supported QStringList types = mimeTypes(); if ( types.isEmpty() ) { return false; } QString format = types[0]; if ( ! data->hasFormat(format) ) { return false; } QByteArray encoded = data->data( format ); QDataStream stream(&encoded, QIODevice::ReadOnly); QList shapes; // decode the data while( ! stream.atEnd() ) { QVariant v; stream >> v; shapes.append( static_cast( (void*)v.value() ) ); } QList toplevelShapes; QList layers; QList pages; // remove shapes having its parent in the list // and separate the layers foreach( KoShape * shape, shapes ) { // check whether the selection contains page // by the UI rules, the selection should contains page only KoPAPageBase *page = dynamic_cast( shape ); if ( page ) { pages.append( page ); continue; } KoShapeContainer *parentShape = shape->parent(); bool hasParentInList = false; while ( parentShape ) { if ( shapes.contains( parentShape ) ) { hasParentInList = true; break; } parentShape = parentShape->parent(); } if ( hasParentInList ) { continue; } KoShapeLayer * layer = dynamic_cast( shape ); if ( layer ) { layers.append( layer ); } else { toplevelShapes.append( shape ); } } // dropping to root, only page(s) is allowed if (!parent.isValid()) { if ( !pages.isEmpty() ) { if ( row < 0 ) { return false; } KoPAPageBase *after = (row != 0) ? m_document->pageByIndex(row - 1, false) : 0; debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping page(s) as root, moving page(s)"; return doDrop(pages, after, action); } else { debugPageApp << "KoPADocumentModel::dropMimeData parent = root, dropping non-page as root, returning false"; return false; } } else if (parent.isValid() && !pages.isEmpty()){ if (parent.row() < 0) { return false; } KoPAPageBase *after; if ((m_document->pageIndex(pages.first()) - 1) == parent.row()) { after = (parent.row() != 0) ? m_document->pageByIndex(parent.row() - 1, false) : 0; } else { after = (parent.row() > -1) ? m_document->pageByIndex(parent.row(), false) : 0; } return doDrop(pages, after, action); } KoShape *shape = static_cast( parent.internalPointer() ); KoShapeContainer * container = dynamic_cast( shape ); if ( container ) { KoShapeGroup * group = dynamic_cast( container ); if ( group ) { debugPageApp <<"KoPADocumentModel::dropMimeData parent = group"; if ( ! toplevelShapes.count() ) { return false; } emit layoutAboutToBeChanged(); beginInsertRows( parent, group->shapeCount(), group->shapeCount()+toplevelShapes.count() ); KUndo2Command * cmd = new KUndo2Command(); cmd->setText( kundo2_i18n("Reparent shapes") ); foreach( KoShape * shape, toplevelShapes ) { new KoShapeUngroupCommand( shape->parent(), QList() << shape, QList(), cmd ); } new KoShapeGroupCommand( group, toplevelShapes, cmd ); KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); canvasController->canvas()->addCommand( cmd ); endInsertRows(); emit layoutChanged(); } else { debugPageApp <<"KoPADocumentModel::dropMimeData parent = container"; if ( toplevelShapes.count() ) { emit layoutAboutToBeChanged(); beginInsertRows( parent, container->shapeCount(), container->shapeCount()+toplevelShapes.count() ); KUndo2Command * cmd = new KUndo2Command(); cmd->setText( kundo2_i18n("Reparent shapes") ); QList clipped; QList inheritsTransform; foreach( KoShape * shape, toplevelShapes ) { if ( ! shape->parent() ) { clipped.append( false ); inheritsTransform.append(false); continue; } clipped.append( shape->parent()->isClipped( shape ) ); inheritsTransform.append(shape->parent()->inheritsTransform(shape)); new KoShapeUngroupCommand( shape->parent(), QList() << shape, QList(), cmd ); } // shapes are dropped on a container, so add them to the container new KoShapeGroupCommand(container, toplevelShapes, clipped, inheritsTransform, cmd); KoCanvasController * canvasController = KoToolManager::instance()->activeCanvasController(); canvasController->canvas()->addCommand( cmd ); endInsertRows(); emit layoutChanged(); } else if ( layers.count() ) { KoShapeLayer * layer = dynamic_cast( container ); if ( ! layer ) { return false; } // TODO layers are dropped on a layer, so change layer ordering return false; } } } else { debugPageApp <<"KoPADocumentModel::dropMimeData parent = shape"; if ( ! toplevelShapes.count() ) { return false; } // TODO shapes are dropped on a shape, reorder them return false; } return true; } QModelIndex KoPADocumentModel::parentIndexFromShape( const KoShape * child ) { if ( !m_document ) { return QModelIndex(); } // check if child shape is a layer, and return invalid model index if it is const KoShapeLayer *childlayer = dynamic_cast( child ); if ( childlayer ) { return QModelIndex(); } // get the children's parent shape KoShapeContainer *parentShape = child->parent(); if ( ! parentShape ) { return QModelIndex(); } // check if the parent is a layer KoShapeLayer *parentLayer = dynamic_cast( parentShape ); if ( parentLayer ) { KoPAPageBase * page = dynamic_cast( parentLayer->parent() ); if ( page ) { return createIndex( m_document->pages(m_master).count() - 1 - m_document->pages(m_master).indexOf( page ), 0, parentLayer ); } } // get the grandparent to determine the row of the parent shape KoShapeContainer *grandParentShape = parentShape->parent(); if ( ! grandParentShape ) { return QModelIndex(); } return createIndex( indexFromChild( grandParentShape, parentShape ), 0, parentShape ); } void KoPADocumentModel::setDocument( KoPADocument* document ) { if (m_document == document) { return; } + if (m_document) { disconnect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) ); disconnect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) ); disconnect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) ); disconnect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) ); disconnect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) ); } + + beginResetModel(); m_document = document; + endResetModel(); if ( m_document ) { connect( m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()) ); connect( m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()) ); connect( m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()) ); connect( m_document, SIGNAL(shapeAdded(KoShape*)), this, SLOT(update()) ); connect( m_document, SIGNAL(shapeRemoved(KoShape*)), this, SLOT(update()) ); } - - reset(); } void KoPADocumentModel::setMasterMode(bool master) { m_master = master; update(); // Rebuild the model } bool KoPADocumentModel::doDrop(QList pages, KoPAPageBase *pageAfter, Qt::DropAction action) { Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); bool enableMove = true; foreach (KoPAPageBase *page, pages) { if (!m_document->pages(false).contains(page)) { KoPAPageBase *newPage = page; pages.replace(pages.indexOf(page), newPage); enableMove = false; break; } } if (((modifiers & Qt::ControlModifier) == 0) && ((modifiers & Qt::ShiftModifier) == 0)) { QMenu popup; QString seq = QKeySequence(Qt::ShiftModifier).toString(); seq.chop(1); QAction *popupMoveAction = new QAction(i18n("&Move Here") + '\t' + seq, this); popupMoveAction->setIcon(koIcon("go-jump")); seq = QKeySequence(Qt::ControlModifier).toString(); seq.chop(1); QAction *popupCopyAction = new QAction(i18n("&Copy Here") + '\t' + seq, this); popupCopyAction->setIcon(koIcon("edit-copy")); seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString(); seq.chop(1); QAction *popupCancelAction = new QAction(i18n("C&ancel") + '\t' + QKeySequence(Qt::Key_Escape).toString(), this); popupCancelAction->setIcon(koIcon("process-stop")); if (enableMove) { popup.addAction(popupMoveAction); } popup.addAction(popupCopyAction); popup.addSeparator(); popup.addAction(popupCancelAction); QAction *result = popup.exec(QCursor::pos()); if (result == popupCopyAction) { action = Qt::CopyAction; } else if (result == popupMoveAction) { action = Qt::MoveAction; } else { return false; } } else if ((modifiers & Qt::ControlModifier) != 0) { action = Qt::CopyAction; } else if ((modifiers & Qt::ShiftModifier) != 0) { action = Qt::MoveAction; } else { return false; } switch (action) { case Qt::MoveAction: { KoPAPageMoveCommand *command = new KoPAPageMoveCommand(m_document, pages, pageAfter); m_document->addCommand( command ); if ((m_document->pageIndex(pageAfter) + pages.count()) < m_document->pageCount()) { emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, pages.count()); } return true; } case Qt::CopyAction: { // Copy Pages KoPAOdfPageSaveHelper saveHelper(m_document, pages); KoDrag drag; drag.setOdf(KoOdf::mimeType(m_document->documentType()), saveHelper); drag.addToClipboard(); //Paste Pages const QMimeData * data = QApplication::clipboard()->mimeData(); static const KoOdf::DocumentType documentTypes[] = { KoOdf::Graphics, KoOdf::Presentation }; for (unsigned int i = 0; i < sizeof(documentTypes) / sizeof(KoOdf::DocumentType); ++i) { if (data->hasFormat( KoOdf::mimeType(documentTypes[i]))) { KoPAPastePage paste(m_document, pageAfter); paste.paste(documentTypes[i], data); break; } } emit requestPageSelection(m_document->pageIndex(pageAfter) + 1, sizeof(documentTypes) / sizeof(KoOdf::DocumentType) - 1); return true; } default: qDebug("Unknown action: %d ", (int)action); return false; } return false; } diff --git a/libs/pageapp/KoPADocumentModel.h b/libs/pageapp/KoPADocumentModel.h index 4ce8ea944ae..d3c500a247d 100644 --- a/libs/pageapp/KoPADocumentModel.h +++ b/libs/pageapp/KoPADocumentModel.h @@ -1,95 +1,96 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOPADOCUMENTMODEL_H #define KOPADOCUMENTMODEL_H #include class KoPADocument; class KoShape; class KoShapeContainer; class KoPAPageBase; #include "kopageapp_export.h" /** * Model class for the document structure widget (dsw). The dsw can * show the structure of a document as a three, as thumbnails or as * a list. * * XXX: implement beginInsertRows, endInsertRows, beginRemoveRows * and endRemoveRows to make the widget react to page insertions * and deletions. */ class KOPAGEAPP_EXPORT KoPADocumentModel : public KoDocumentSectionModel { Q_OBJECT public: /// Constructs a new document section model using the specified documents data explicit KoPADocumentModel( QObject* parent, KoPADocument *document = 0 ); /// Set the document used in the model void setDocument(KoPADocument* document); /// Set the mode to show, master page or normal page void setMasterMode(bool master); // from QAbstractItemModel virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent( const QModelIndex &child ) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual Qt::DropActions supportedDropActions() const; virtual QStringList mimeTypes() const; virtual QMimeData * mimeData( const QModelIndexList & indexes ) const; virtual bool dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ); + virtual Qt::DropActions supportedDragActions() const; public Q_SLOTS: /// Triggers an update of the complete model void update(); Q_SIGNALS: void requestPageSelection(int start, int count); private: /// Returns properties of the given shape PropertyList properties( KoShape* shape ) const; /// Sets the properties on the given shape void setProperties( KoShape* shape, const PropertyList &properties ); /// Creates a thumbnail image with the specified size from the given shape QImage createThumbnail( KoShape* shape, const QSize &thumbSize ) const; /// Returns the child shape with the given index from the parent shape KoShape * childFromIndex( KoShapeContainer *parent, int row ) const; /// Returns the zero based index of a child shape within its parent shape int indexFromChild( KoShapeContainer *parent, KoShape *child ) const; /// Returns the parent model index from the given child shape QModelIndex parentIndexFromShape( const KoShape * child ); /// Creates a context menu when dropping pages to choose between copy or move action. bool doDrop(QList pages, KoPAPageBase *pageAfter, Qt::DropAction action); KoPADocument *m_document; ///< the undelying data structure bool m_master; mutable QList m_childs; mutable KoShapeContainer *m_lastContainer; }; #endif // KODOCUMENTMODEL_H diff --git a/stage/part/KPrCustomSlideShowsModel.cpp b/stage/part/KPrCustomSlideShowsModel.cpp index d424715d8f3..2a4ed4a4de5 100644 --- a/stage/part/KPrCustomSlideShowsModel.cpp +++ b/stage/part/KPrCustomSlideShowsModel.cpp @@ -1,404 +1,406 @@ /* This file is part of the KDE project * Copyright (C) 2011 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KPrCustomSlideShowsModel.h" //Calligra headers #include "KPrCustomSlideShows.h" #include "KPrDocument.h" #include "KoPAPageBase.h" #include "commands/KPrEditCustomSlideShowsCommand.h" #include "commands/KPrAddCustomSlideShowCommand.h" #include "commands/KPrDelCustomSlideShowCommand.h" #include "commands/KPrRenameCustomSlideShowCommand.h" //KF5 headers #include //Qt headers #include #include #include #include KPrCustomSlideShowsModel::KPrCustomSlideShowsModel(KPrDocument *document, QObject *parent) : QAbstractListModel(parent) , m_customSlideShows(document->customSlideShows()) , m_iconSize(QSize(200,200)) , m_document(document) { connect(m_customSlideShows, SIGNAL(updated()), this, SLOT(updateModel())); } KPrCustomSlideShowsModel::~KPrCustomSlideShowsModel(){ } QVariant KPrCustomSlideShowsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || !m_customSlideShows || m_activeCustomSlideShowName.isEmpty()) { return QVariant(); } Q_ASSERT(index.model() == this); KoPAPageBase *page = m_customSlideShows->pageByIndex(m_activeCustomSlideShowName, index.row()); switch (role) { case Qt::DisplayRole: { QString name = i18n("Unknown"); if (page) { name = page->name(); if (name.isEmpty()) { //Default case name = i18n("Slide %1", index.row()); } } return name; } case Qt::DecorationRole: { return QIcon(page->thumbnail(m_iconSize)); } default: return QVariant(); } } int KPrCustomSlideShowsModel::rowCount(const QModelIndex &parent) const { if (!m_activeCustomSlideShowName.isEmpty()) { if (!parent.isValid()) { return m_customSlideShows->getByName(m_activeCustomSlideShowName).count(); } } return 0; } Qt::ItemFlags KPrCustomSlideShowsModel::flags(const QModelIndex &index) const { if (m_activeCustomSlideShowName.isEmpty()) { return 0; } Qt::ItemFlags defaultFlags = QAbstractListModel::flags (index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } else { return Qt::ItemIsDropEnabled | defaultFlags; } } QModelIndex KPrCustomSlideShowsModel::index(int row, int column, const QModelIndex &parent) const { if (m_activeCustomSlideShowName.isEmpty()) { return QModelIndex(); } // check if parent is root node if (!parent.isValid()) { if (row >= 0 && row < rowCount(QModelIndex())) { return createIndex(row, column, m_customSlideShows->pageByIndex(m_activeCustomSlideShowName, row)); } } return QModelIndex(); } QStringList KPrCustomSlideShowsModel::mimeTypes() const { return QStringList() << "application/x-calligra-customslideshows"; } QMimeData * KPrCustomSlideShowsModel::mimeData(const QModelIndexList &indexes) const { // check if there is data to encode if (! indexes.count()) { return 0; } // check if we support a format const QStringList types = mimeTypes(); if (types.isEmpty()) { return 0; } QMimeData *data = new QMimeData(); QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); // encode the data & order slides QModelIndexList::ConstIterator it = indexes.begin(); QMap map; for( ; it != indexes.end(); ++it) { map.insert(m_customSlideShows->indexByPage(m_activeCustomSlideShowName, (KoPAPageBase*)it->internalPointer()), (KoPAPageBase*)it->internalPointer()); } QList slides = map.values(); foreach (KoPAPageBase *slide, slides) { stream << QVariant::fromValue(qulonglong((void*)slide)); } data->setData(types[0], encoded); return data; } Qt::DropActions KPrCustomSlideShowsModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool KPrCustomSlideShowsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { return true; } if (data->hasFormat("application/x-calligra-sliderssorter") || data->hasFormat("application/x-calligra-customslideshows")) { if (column > 0) { return false; } QList slides; int beginRow = 0; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } if (data->hasFormat("application/x-calligra-sliderssorter")) { QByteArray encoded = data->data("application/x-calligra-sliderssorter"); slides = decodeSlidesList(encoded); if (slides.empty()) { return false; } //perform action doCustomSlideShowAction(KPrCustomSlideShowsModel::SlidesAdd, slides, QList(), beginRow); } else if (data->hasFormat("application/x-calligra-customslideshows")) { QByteArray encoded = data->data("application/x-calligra-customslideshows"); slides = decodeSlidesList(encoded); if (slides.empty()) { return false; } //perform action doCustomSlideShowAction(KPrCustomSlideShowsModel::SlidesMove, slides, QList(), beginRow); } return true; } return false; } QList KPrCustomSlideShowsModel::decodeSlidesList(const QByteArray &encoded) { QList slides; QDataStream stream(encoded); // decode the data while(!stream.atEnd()) { QVariant v; stream >> v; slides.append(static_cast((void*)v.value())); } return slides; } void KPrCustomSlideShowsModel::setCustomSlideShows(KPrCustomSlideShows *customShows) { + beginResetModel(); m_customSlideShows = customShows; m_activeCustomSlideShowName.clear(); - reset(); + endResetModel(); } QString KPrCustomSlideShowsModel::activeCustomSlideShow() const { return m_activeCustomSlideShowName; } void KPrCustomSlideShowsModel::setActiveSlideShow(const QString &name) { if (!m_customSlideShows || (m_activeCustomSlideShowName == name)) { return; } if (m_customSlideShows->names().contains(name)) { + beginResetModel(); m_activeCustomSlideShowName = name; - reset(); + endResetModel(); } } void KPrCustomSlideShowsModel::setActiveSlideShow(int index) { if (!m_customSlideShows) { return; } QString name = m_customSlideShows->names().value(index); setActiveSlideShow(name); } void KPrCustomSlideShowsModel::setIconSize(const QSize &size) { m_iconSize = size; } QStringList KPrCustomSlideShowsModel::customShowsNamesList() const { if (m_customSlideShows) { return m_customSlideShows->names(); } return QStringList(); } void KPrCustomSlideShowsModel::setDocument(KPrDocument *document) { m_document = document; setCustomSlideShows(document->customSlideShows()); } void KPrCustomSlideShowsModel::removeSlidesByIndexes(const QModelIndexList &pageIndexes) { QList slides; QList indexesList; foreach (const QModelIndex &index, pageIndexes) { indexesList.append(index.row()); } doCustomSlideShowAction(KPrCustomSlideShowsModel::SlidesDelete, slides, indexesList); } void KPrCustomSlideShowsModel::addSlides(const QList &pages, const int &row) { doCustomSlideShowAction(KPrCustomSlideShowsModel::SlidesAdd, pages, QList(), row); } bool KPrCustomSlideShowsModel::doCustomSlideShowAction(const CustomShowActions &action, const QList &slides, QList indexes, int beginRow) { bool updated = false; int start = beginRow; //get the slideshow if (m_activeCustomSlideShowName.isEmpty()) { return false; } QList selectedSlideShow = m_customSlideShows->getByName(m_activeCustomSlideShowName); if (action == KPrCustomSlideShowsModel::SlidesAdd) { //insert the slides on the current custom show int i = beginRow; foreach(KoPAPageBase *page, slides) { selectedSlideShow.insert(i, page); i++; } updated = true; } else if (action == KPrCustomSlideShowsModel::SlidesMove) { //move the slides on the current custom show // slides order within the slides list is important to get the expected behaviour if (beginRow >= selectedSlideShow.count()) { beginRow = selectedSlideShow.count(); } int i = 0; foreach(KoPAPageBase *page, slides) { int from = selectedSlideShow.indexOf(page); if (from < beginRow) { selectedSlideShow.move(from, beginRow - 1); start--; } else { selectedSlideShow.move(from, beginRow + i); i++; } } updated = true; } else if (action == KPrCustomSlideShowsModel::SlidesDelete) { //delete de slides on the current custom show //delete command use indexes because the custom show could have //more than one copy of the same slide. qSort(indexes); int i = 0; foreach(int row, indexes) { selectedSlideShow.removeAt(row - i); i++; } updated = true; } else { updated = false; } if (updated) { //update the SlideShow with the resulting list KPrEditCustomSlideShowsCommand *command = new KPrEditCustomSlideShowsCommand( m_document, m_activeCustomSlideShowName, selectedSlideShow); m_document->addCommand(command); emit selectPages(start, slides.count()); } return updated; } void KPrCustomSlideShowsModel::addNewCustomShow(const QString &name) { KPrAddCustomSlideShowCommand *command = new KPrAddCustomSlideShowCommand(m_document, this, name); m_document->addCommand(command); } void KPrCustomSlideShowsModel::renameCustomShow(const QString &oldName, const QString &newName) { KPrRenameCustomSlideShowCommand *command = new KPrRenameCustomSlideShowCommand(m_document, this, oldName, newName); m_document->addCommand(command); } void KPrCustomSlideShowsModel::removeCustomShow(const QString &name) { KPrDelCustomSlideShowCommand *command = new KPrDelCustomSlideShowCommand(m_document, this, name); m_document->addCommand(command); } void KPrCustomSlideShowsModel::updateCustomSlideShowsList(const QString &name) { m_activeCustomSlideShowName.clear(); setActiveSlideShow(name); emit customSlideShowsChanged(); } void KPrCustomSlideShowsModel::updateModel() { emit layoutAboutToBeChanged(); emit layoutChanged(); } diff --git a/stage/part/KPrSlidesSorterDocumentModel.cpp b/stage/part/KPrSlidesSorterDocumentModel.cpp index 6ebcc7ac1e2..fe61251cc40 100644 --- a/stage/part/KPrSlidesSorterDocumentModel.cpp +++ b/stage/part/KPrSlidesSorterDocumentModel.cpp @@ -1,431 +1,435 @@ /* This file is part of the KDE project * * Copyright (C) 2011 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KPrSlidesSorterDocumentModel.h" #include "KPrViewModeSlidesSorter.h" #include "KPrDocument.h" #include "commands/KPrDeleteSlidesCommand.h" #include "StageDebug.h" //Calligra headers #include #include #include #include #include #include #include #include #include //Qt Headers #include #include #include KPrSlidesSorterDocumentModel::KPrSlidesSorterDocumentModel(KPrViewModeSlidesSorter *viewModeSlidesSorter, QWidget *parent, KoPADocument *document) : QAbstractListModel(parent) , m_viewModeSlidesSorter(viewModeSlidesSorter) { setDocument(document); - setSupportedDragActions(Qt::MoveAction); +} + +Qt::DropActions KPrSlidesSorterDocumentModel::supportedDragActions() const +{ + return Qt::MoveAction; } KPrSlidesSorterDocumentModel::~KPrSlidesSorterDocumentModel() { } void KPrSlidesSorterDocumentModel::setDocument(KoPADocument *document) { + beginResetModel(); m_document = document; + endResetModel(); if (m_document) { connect(m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update())); connect(m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update())); connect(m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update())); } - - reset(); } QModelIndex KPrSlidesSorterDocumentModel::index(int row, int column, const QModelIndex &parent) const { if(!m_document) { return QModelIndex(); } // check if parent is root node if(!parent.isValid()) { if(row >= 0 && row < m_document->pages(false).count()) { return createIndex(row, column, m_document->pages(false).at(row)); } } return QModelIndex(); } QVariant KPrSlidesSorterDocumentModel::data(const QModelIndex &index, int role) const { if (! index.isValid() || !m_document) { return QVariant(); } Q_ASSERT(index.model() == this); KoPAPageBase *page = pageByIndex(index); switch (role) { case Qt::DisplayRole: { QString name = i18n("Unknown"); if (page) { name = page->name(); if (name.isEmpty()) { //Default case name = i18n("Slide %1", m_document->pageIndex(page) + 1); } } return name; } case Qt::DecorationRole: { return QIcon(page->thumbnail(m_viewModeSlidesSorter->iconSize())); } case Qt::EditRole: { return page->name(); } default: return QVariant(); } } bool KPrSlidesSorterDocumentModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_document) { return false; } Q_ASSERT(index.model() == this); Q_ASSERT(index.internalPointer()); KoShape *shape = static_cast(index.internalPointer()); switch (role) { case Qt::EditRole: { KUndo2Command *cmd = new KoShapeRenameCommand(shape, value.toString()); // TODO 2.1 use different text for the command if e.g. it is a page/slide or layer m_document->addCommand(cmd); break; } default: return false; } emit dataChanged(index, index); return true; } int KPrSlidesSorterDocumentModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if (!m_document) { return 0; } return m_document->pages(false).count(); } QStringList KPrSlidesSorterDocumentModel::mimeTypes() const { return QStringList() << "application/x-calligra-sliderssorter"; } QMimeData * KPrSlidesSorterDocumentModel::mimeData(const QModelIndexList &indexes) const { // check if there is data to encode if (!indexes.count()) { return 0; } // check if we support a format QStringList types = mimeTypes(); if (types.isEmpty()) { return 0; } QMimeData *data = new QMimeData(); QString format = types[0]; QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); // encode the data QModelIndexList::ConstIterator it = indexes.begin(); // order slides QMap map; for( ; it != indexes.end(); ++it) { map.insert(m_document->pages(false).indexOf((KoPAPageBase*)it->internalPointer()), (KoPAPageBase*)it->internalPointer()); } QList slides = map.values(); foreach (KoPAPageBase *slide, slides) { stream << QVariant::fromValue(qulonglong((void*)slide)); } data->setData(format, encoded); return data; } Qt::DropActions KPrSlidesSorterDocumentModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool KPrSlidesSorterDocumentModel::removeRows(int row, int count, const QModelIndex &parent) { bool success = true; beginRemoveRows(parent,row, row + count- 1); endRemoveRows(); return success; } Qt::ItemFlags KPrSlidesSorterDocumentModel::flags(const QModelIndex &index) const { if (!m_document) { return 0; } Qt::ItemFlags defaultFlags = QAbstractListModel::flags (index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | defaultFlags; } else { return Qt::ItemIsDropEnabled | defaultFlags; } } void KPrSlidesSorterDocumentModel::update() { emit layoutAboutToBeChanged(); emit layoutChanged(); } bool KPrSlidesSorterDocumentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat("application/x-calligra-sliderssorter") || (column > 0)) { return false; } QByteArray encoded = data->data("application/x-calligra-sliderssorter"); QDataStream stream(&encoded, QIODevice::ReadOnly); QList slides; // decode the data while (!stream.atEnd()) { QVariant v; stream >> v; slides.append(static_cast((void*)v.value())); } if (slides.empty()) { return false; } int beginRow; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } KoPAPageBase *pageAfter = 0; if ((beginRow - 1) >= 0) { pageAfter = m_document->pageByIndex(beginRow - 1,false); } if (!slides.empty()) { doDrop(slides, pageAfter, action); } return true; } void KPrSlidesSorterDocumentModel::doDrop(QList slides, KoPAPageBase *pageAfter, Qt::DropAction action) { Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); bool enableMove = true; foreach (KoPAPageBase *slide, slides) { if (!m_document->pages(false).contains(slide)) { KoPAPageBase *newSlide = slide; slides.replace(slides.indexOf(slide), newSlide); enableMove = false; break; } } if (((modifiers & Qt::ControlModifier) == 0) && ((modifiers & Qt::ShiftModifier) == 0)) { QMenu popup; QString seq = QKeySequence(Qt::ShiftModifier).toString(); seq.chop(1); QAction *popupMoveAction = new QAction(i18n("&Move Here") + '\t' + seq, this); popupMoveAction->setIcon(koIcon("go-jump")); seq = QKeySequence(Qt::ControlModifier).toString(); seq.chop(1); QAction *popupCopyAction = new QAction(i18n("&Copy Here") + '\t' + seq, this); popupCopyAction->setIcon(koIcon("edit-copy")); seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString(); seq.chop(1); QAction *popupCancelAction = new QAction(i18n("C&ancel") + '\t' + QKeySequence(Qt::Key_Escape).toString(), this); popupCancelAction->setIcon(koIcon("process-stop")); if (enableMove) { popup.addAction(popupMoveAction); } popup.addAction(popupCopyAction); popup.addSeparator(); popup.addAction(popupCancelAction); QAction *result = popup.exec(QCursor::pos()); if (result == popupCopyAction) { action = Qt::CopyAction; } else if (result == popupMoveAction) { action = Qt::MoveAction; } else { return; } } else if ((modifiers & Qt::ControlModifier) != 0) { action = Qt::CopyAction; } else if ((modifiers & Qt::ShiftModifier) != 0) { action = Qt::MoveAction; } else { return; } switch (action) { case Qt::MoveAction: { //You can't move slides that not belong to the current document foreach (KoPAPageBase *slide, slides) { if (!m_document->pages(false).contains(slide)) { slides.removeAll(slide); } } if (slides.isEmpty()) { return; } moveSlides(slides, pageAfter); return; } case Qt::CopyAction: { copySlides(slides); m_viewModeSlidesSorter->view()->setActivePage(pageAfter); pasteSlides(); m_viewModeSlidesSorter->view()->setActivePage(slides.first()); m_viewModeSlidesSorter->selectSlides(slides); return; } default: debugStage << "Unknown action:" << (int)action; return; } } KoPAPageBase * KPrSlidesSorterDocumentModel::pageByIndex(const QModelIndex &index) const { Q_ASSERT(index.internalPointer()); return static_cast(index.internalPointer()); } bool KPrSlidesSorterDocumentModel::removeSlides(const QList &slides) { if (!slides.empty() && m_document->pages().count() > slides.count()) { KPrDocument *doc = static_cast(m_document); KUndo2Command *cmd = new KPrDeleteSlidesCommand(doc, slides); if (cmd) { removeRows(m_document->pageIndex(slides.first()), slides.count(), QModelIndex()); m_document->addCommand(cmd); return true; } } return false; } bool KPrSlidesSorterDocumentModel::addNewSlide() { KoPAView *view = dynamic_cast(m_viewModeSlidesSorter->view()); if (view) { view->insertPage(); return true; } return false; } bool KPrSlidesSorterDocumentModel::copySlides(const QList &slides) { if (!slides.empty()) { // Copy Pages KoPAOdfPageSaveHelper saveHelper(m_document, slides); KoDrag drag; drag.setOdf(KoOdf::mimeType(m_document->documentType()), saveHelper); drag.addToClipboard(); return true; } return false; } bool KPrSlidesSorterDocumentModel::pasteSlides() { KoPAView *view = dynamic_cast(m_viewModeSlidesSorter->view()); if (view) { view->pagePaste(); return true; } return false; } bool KPrSlidesSorterDocumentModel::moveSlides(const QList &slides, KoPAPageBase *pageAfter) { KoPAPageMoveCommand *command = new KoPAPageMoveCommand(m_document, slides, pageAfter); if (command) { m_document->addCommand(command); m_viewModeSlidesSorter->view()->setActivePage(slides.first()); m_viewModeSlidesSorter->selectSlides(slides); return true; } return false; } diff --git a/stage/part/KPrSlidesSorterDocumentModel.h b/stage/part/KPrSlidesSorterDocumentModel.h index f12ff71e84e..9f0a6097d53 100644 --- a/stage/part/KPrSlidesSorterDocumentModel.h +++ b/stage/part/KPrSlidesSorterDocumentModel.h @@ -1,136 +1,137 @@ /* This file is part of the KDE project * * Copyright (C) 2011 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPRSLIDESSORTERDOCUMENTMODEL_H #define KPRSLIDESSORTERDOCUMENTMODEL_H #include class KPrViewModeSlidesSorter; class KoPADocument; class KoPAPageBase; /** * Class meant to hold the model for Slides Sorter * This model lets display slides as thumbnails in a List view, using standard QT * view/model framework. It supports copy and move of slides, and include a context * menu to present options when dropping. */ class KPrSlidesSorterDocumentModel: public QAbstractListModel { Q_OBJECT public: KPrSlidesSorterDocumentModel(KPrViewModeSlidesSorter *viewModeSlidesSorter, QWidget *parent, KoPADocument *document = 0); ~KPrSlidesSorterDocumentModel(); /** * Set the current document * @param document a KoPADocument that holds current document */ void setDocument(KoPADocument *document); virtual QModelIndex index(int row, int column, const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); int rowCount(const QModelIndex &parent) const; virtual QStringList mimeTypes() const; virtual QMimeData* mimeData(const QModelIndexList &indexes) const; + virtual Qt::DropActions supportedDragActions() const; virtual Qt::DropActions supportedDropActions() const; virtual bool removeRows(int row, int count, const QModelIndex &parent); virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual bool dropMimeData (const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); /** * Drop selected slides (copy/move) if a modifier key is pressed * or display a context menu with alternatives. * @param slides list of slides to be dropped * @param pageAfter destination of the drop * @param action the drop action */ void doDrop(QList slides, KoPAPageBase *pageAfter, Qt::DropAction action); /** * @brief Return the page that match the give index in the list * / @param index the index of the slide to be retrived * @return a Page in the document if it was found or a null pointer if not. */ KoPAPageBase* pageByIndex(const QModelIndex &index) const; /** * @brief Deletes a given list of slides from the current document * * @param slides list of slides to be removed * @return true if the command execution was successful */ bool removeSlides(const QList &slides); /** * @brief Add a new slide after the current active page * * @return true if the command execution was successful */ bool addNewSlide(); /** * @brief copy a given list of slides * * @param slides list of slides to be copied * @return true if the command execution was successful */ bool copySlides(const QList &slides); /** * @brief Paste slides from clipboard * * @return true if the command execution was successful */ bool pasteSlides(); /** * @brief Moves a given list of slides after pageAfter slide * * @param slides list of slides to be moved * @param pageAfter indicates where the slides will be moved * @return true if the command execution was successful */ bool moveSlides(const QList &slides, KoPAPageBase *pageAfter); public Q_SLOTS: /** emit signals indicating a change in the model layout or items */ void update(); private: //A reference to current document KoPADocument *m_document; //A reference to Slides sorter class KPrViewModeSlidesSorter *m_viewModeSlidesSorter; }; #endif // KPRSLIDESSORTERDOCUMENTMODEL_H diff --git a/stage/part/tools/animationtool/KPrCollectionItemModel.cpp b/stage/part/tools/animationtool/KPrCollectionItemModel.cpp index f5196acef38..1e8d8f01d4f 100644 --- a/stage/part/tools/animationtool/KPrCollectionItemModel.cpp +++ b/stage/part/tools/animationtool/KPrCollectionItemModel.cpp @@ -1,78 +1,83 @@ /* This file is part of the KDE project * Copyright (C) 2012 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // Heavily based in CollectionItemModel work of Peter Simonsson #include "KPrCollectionItemModel.h" #include "StageDebug.h" KPrCollectionItemModel::KPrCollectionItemModel(QObject* parent) : QAbstractListModel(parent) { - setSupportedDragActions(Qt::IgnoreAction); +} + +Qt::DropActions KPrCollectionItemModel::supportedDragActions() const +{ + return Qt::IgnoreAction; } QVariant KPrCollectionItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() > m_animationClassList.count()) return QVariant(); switch(role) { case Qt::ToolTipRole: return m_animationClassList[index.row()].toolTip; case Qt::DecorationRole: return m_animationClassList[index.row()].icon; case Qt::UserRole: return m_animationClassList[index.row()].id; case Qt::DisplayRole: return m_animationClassList[index.row()].name; default: return QVariant(); } return QVariant(); } int KPrCollectionItemModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_animationClassList.count(); } void KPrCollectionItemModel::setAnimationClassList(const QVector &newlist) { + beginResetModel(); m_animationClassList = newlist; - reset(); + endResetModel(); } KoXmlElement KPrCollectionItemModel::animationContext(const QModelIndex &index) const { return m_animationClassList.value(index.row()).animationContext; } Qt::ItemFlags KPrCollectionItemModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index); } diff --git a/stage/part/tools/animationtool/KPrCollectionItemModel.h b/stage/part/tools/animationtool/KPrCollectionItemModel.h index afac1691878..29fd58bcc59 100644 --- a/stage/part/tools/animationtool/KPrCollectionItemModel.h +++ b/stage/part/tools/animationtool/KPrCollectionItemModel.h @@ -1,76 +1,77 @@ /* This file is part of the KDE project * Copyright (C) 2012 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // Heavily based in CollectionItemModel work of Peter Simonsson #ifndef KPRCOLLECTIONITEMMODEL_H #define KPRCOLLECTIONITEMMODEL_H #include #include #include #include #include /** * Struct containing the information stored in CollectionItemModel item */ struct KPrCollectionItem { QString id; //animation id QString name; //animation name (text to be displayed on animations view) QString toolTip; // text of animation tool tip QIcon icon; // icon of the animation type KoXmlElement animationContext; //xml data used to instantiate animations of this type }; Q_DECLARE_TYPEINFO(KPrCollectionItem, Q_MOVABLE_TYPE); /** Model used to store predefined animations data */ class KPrCollectionItemModel : public QAbstractListModel { Q_OBJECT public: explicit KPrCollectionItemModel(QObject *parent = 0); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual Qt::DropActions supportedDragActions() const; /** * @brief Set the list of KoCollectionItem to be stored in the model * * @param List of KPrCollectionItem */ void setAnimationClassList(const QVector &newlist); QVector animationClassList() const {return m_animationClassList;} /** * @brief Return the xml context for the animation on index * * @param index of the animation */ KoXmlElement animationContext(const QModelIndex &index) const; private: QVector m_animationClassList; QString m_family; }; #endif // KPRCOLLECTIONITEMMODEL_H