File indexing completed on 2024-05-19 16:09:36

0001 /* This file is part of the KDE project
0002 *
0003 * Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
0004 *
0005 * This library is free software; you can redistribute it and/or
0006 * modify it under the terms of the GNU Library General Public
0007 * License as published by the Free Software Foundation; either
0008 * version 2 of the License, or (at your option) any later version.
0009 *
0010 * This library is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013 * Library General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU Library General Public License
0016 * along with this library; see the file COPYING.LIB.  If not, write to
0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018 * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KPrSlidesSorterDocumentModel.h"
0022 
0023 #include "KPrViewModeSlidesSorter.h"
0024 #include "KPrDocument.h"
0025 #include "commands/KPrDeleteSlidesCommand.h"
0026 #include "StageDebug.h"
0027 
0028 //Calligra headers
0029 #include <KoPADocument.h>
0030 #include <KoPAPageBase.h>
0031 #include <KoPAViewBase.h>
0032 #include <KoPAView.h>
0033 #include <KoPAOdfPageSaveHelper.h>
0034 #include <KoDrag.h>
0035 #include <KoPAPageMoveCommand.h>
0036 #include <KoShapeRenameCommand.h>
0037 #include <KoIcon.h>
0038 
0039 //Qt Headers
0040 #include <QMimeData>
0041 #include <QApplication>
0042 #include <QMenu>
0043 
0044 KPrSlidesSorterDocumentModel::KPrSlidesSorterDocumentModel(KPrViewModeSlidesSorter *viewModeSlidesSorter, QWidget *parent, KoPADocument *document)
0045    : QAbstractListModel(parent)
0046    , m_viewModeSlidesSorter(viewModeSlidesSorter)
0047 {
0048     setDocument(document);
0049 }
0050 
0051 Qt::DropActions KPrSlidesSorterDocumentModel::supportedDragActions() const
0052 {
0053     return Qt::MoveAction;
0054 }
0055 
0056 KPrSlidesSorterDocumentModel::~KPrSlidesSorterDocumentModel()
0057 {
0058 }
0059 
0060 void KPrSlidesSorterDocumentModel::setDocument(KoPADocument *document)
0061 {
0062     beginResetModel();
0063     m_document = document;
0064     endResetModel();
0065     if (m_document) {
0066         connect(m_document, SIGNAL(pageAdded(KoPAPageBase*)), this, SLOT(update()));
0067         connect(m_document, SIGNAL(pageRemoved(KoPAPageBase*)), this, SLOT(update()));
0068         connect(m_document, SIGNAL(update(KoPAPageBase*)), this, SLOT(update()));
0069     }
0070 }
0071 
0072 QModelIndex KPrSlidesSorterDocumentModel::index(int row, int column, const QModelIndex &parent) const
0073 {
0074     if(!m_document) {
0075         return QModelIndex();
0076     }
0077 
0078     // check if parent is root node
0079     if(!parent.isValid()) {
0080         if(row >= 0 && row < m_document->pages(false).count()) {
0081             return createIndex(row, column, m_document->pages(false).at(row));
0082         }
0083     }
0084     return QModelIndex();
0085 }
0086 
0087 QVariant KPrSlidesSorterDocumentModel::data(const QModelIndex &index, int role) const
0088 {
0089     if (! index.isValid() || !m_document) {
0090         return QVariant();
0091     }
0092 
0093     Q_ASSERT(index.model() == this);
0094     KoPAPageBase *page = pageByIndex(index);
0095 
0096     switch (role) {
0097         case Qt::DisplayRole:
0098         {
0099             QString name = i18n("Unknown");
0100             if (page) {
0101                 name = page->name();
0102                 if (name.isEmpty()) {
0103                     //Default case
0104                     name = i18n("Slide %1", m_document->pageIndex(page) + 1);
0105                 }
0106             }
0107             return name;
0108         }
0109         case Qt::DecorationRole:
0110         {
0111             return QIcon(page->thumbnail(m_viewModeSlidesSorter->iconSize()));
0112         }
0113         case Qt::EditRole:
0114         {
0115             return page->name();
0116         }
0117         default:
0118             return QVariant();
0119     }
0120 }
0121 
0122 bool KPrSlidesSorterDocumentModel::setData(const QModelIndex &index, const QVariant &value, int role)
0123 {
0124     if (!index.isValid() || !m_document) {
0125         return false;
0126     }
0127 
0128     Q_ASSERT(index.model() == this);
0129     Q_ASSERT(index.internalPointer());
0130 
0131     KoShape *shape = static_cast<KoShape*>(index.internalPointer());
0132     switch (role)
0133     {
0134         case Qt::EditRole:
0135         {
0136             KUndo2Command *cmd = new KoShapeRenameCommand(shape, value.toString());
0137             // TODO 2.1 use different text for the command if e.g. it is a page/slide or layer
0138             m_document->addCommand(cmd);
0139             break;
0140         }
0141         default:
0142             return false;
0143     }
0144 
0145     emit dataChanged(index, index);
0146     return true;
0147 }
0148 
0149 int KPrSlidesSorterDocumentModel::rowCount(const QModelIndex &parent) const
0150 {
0151     Q_UNUSED(parent);
0152     if (!m_document) {
0153         return 0;
0154     }
0155     return m_document->pages(false).count();
0156 }
0157 
0158 QStringList KPrSlidesSorterDocumentModel::mimeTypes() const
0159 {
0160     return QStringList() << "application/x-calligra-sliderssorter";
0161 }
0162 
0163 QMimeData * KPrSlidesSorterDocumentModel::mimeData(const QModelIndexList &indexes) const
0164 {
0165     // check if there is data to encode
0166     if (!indexes.count()) {
0167         return 0;
0168     }
0169 
0170     // check if we support a format
0171     QStringList types = mimeTypes();
0172     if (types.isEmpty()) {
0173         return 0;
0174     }
0175 
0176     QMimeData *data = new QMimeData();
0177     QString format = types[0];
0178     QByteArray encoded;
0179     QDataStream stream(&encoded, QIODevice::WriteOnly);
0180 
0181     // encode the data
0182     QModelIndexList::ConstIterator it = indexes.begin();
0183     //  order slides
0184     QMap<int, KoPAPageBase*> map;
0185     for( ; it != indexes.end(); ++it) {
0186         map.insert(m_document->pages(false).indexOf((KoPAPageBase*)it->internalPointer()),
0187                    (KoPAPageBase*)it->internalPointer());
0188     }
0189 
0190     QList<KoPAPageBase *> slides = map.values();
0191 
0192     foreach (KoPAPageBase *slide, slides) {
0193         stream << QVariant::fromValue(qulonglong((void*)slide));
0194     }
0195 
0196     data->setData(format, encoded);
0197     return data;
0198 }
0199 
0200 Qt::DropActions KPrSlidesSorterDocumentModel::supportedDropActions() const
0201 {
0202     return Qt::MoveAction | Qt::CopyAction;
0203 }
0204 
0205 bool KPrSlidesSorterDocumentModel::removeRows(int row, int count, const QModelIndex &parent)
0206 {
0207     bool success = true;
0208     beginRemoveRows(parent,row, row + count- 1);
0209     endRemoveRows();
0210     return success;
0211 }
0212 
0213 Qt::ItemFlags KPrSlidesSorterDocumentModel::flags(const QModelIndex &index) const
0214 {
0215     if (!m_document) {
0216         return 0;
0217     }
0218 
0219     Qt::ItemFlags defaultFlags = QAbstractListModel::flags (index);
0220 
0221     if (index.isValid()) {
0222         return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | defaultFlags;
0223     }
0224     else {
0225         return Qt::ItemIsDropEnabled | defaultFlags;
0226     }
0227 }
0228 
0229 void KPrSlidesSorterDocumentModel::update()
0230 {
0231     emit layoutAboutToBeChanged();
0232     emit layoutChanged();
0233 }
0234 
0235 bool KPrSlidesSorterDocumentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
0236 {
0237     if (action == Qt::IgnoreAction) {
0238         return true;
0239     }
0240 
0241     if (!data->hasFormat("application/x-calligra-sliderssorter") || (column > 0)) {
0242         return false;
0243     }
0244 
0245     QByteArray encoded = data->data("application/x-calligra-sliderssorter");
0246     QDataStream stream(&encoded, QIODevice::ReadOnly);
0247     QList<KoPAPageBase *> slides;
0248 
0249     // decode the data
0250     while (!stream.atEnd()) {
0251         QVariant v;
0252         stream >> v;
0253         slides.append(static_cast<KoPAPageBase*>((void*)v.value<qulonglong>()));
0254     }
0255 
0256     if (slides.empty()) {
0257         return false;
0258     }
0259 
0260     int beginRow;
0261 
0262     if (row != -1) {
0263         beginRow = row;
0264     }
0265     else if (parent.isValid()) {
0266         beginRow = parent.row();
0267     }
0268     else {
0269         beginRow = rowCount(QModelIndex());
0270     }
0271 
0272     KoPAPageBase *pageAfter = 0;
0273 
0274     if ((beginRow - 1) >= 0) {
0275         pageAfter = m_document->pageByIndex(beginRow - 1,false);
0276     }
0277 
0278     if (!slides.empty()) {
0279         doDrop(slides, pageAfter, action);
0280     }
0281 
0282     return true;
0283 }
0284 
0285 void KPrSlidesSorterDocumentModel::doDrop(QList<KoPAPageBase *> slides, KoPAPageBase *pageAfter, Qt::DropAction action)
0286 {
0287      Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
0288      bool enableMove = true;
0289 
0290      foreach (KoPAPageBase *slide, slides) {
0291          if (!m_document->pages(false).contains(slide)) {
0292              KoPAPageBase *newSlide = slide;
0293              slides.replace(slides.indexOf(slide), newSlide);
0294              enableMove = false;
0295              break;
0296          }
0297      }
0298 
0299      if (((modifiers & Qt::ControlModifier) == 0) &&
0300          ((modifiers & Qt::ShiftModifier) == 0)) {
0301             QMenu popup;
0302             QString seq = QKeySequence(Qt::ShiftModifier).toString();
0303             seq.chop(1);
0304             QAction *popupMoveAction = new QAction(i18n("&Move Here") + '\t' + seq, this);
0305             popupMoveAction->setIcon(koIcon("go-jump"));
0306             seq = QKeySequence(Qt::ControlModifier).toString();
0307             seq.chop(1);
0308             QAction *popupCopyAction = new QAction(i18n("&Copy Here") + '\t' + seq, this);
0309             popupCopyAction->setIcon(koIcon("edit-copy"));
0310             seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString();
0311             seq.chop(1);
0312             QAction *popupCancelAction = new QAction(i18n("C&ancel") + '\t' + QKeySequence(Qt::Key_Escape).toString(), this);
0313             popupCancelAction->setIcon(koIcon("process-stop"));
0314 
0315             if (enableMove) {
0316                 popup.addAction(popupMoveAction);
0317             }
0318             popup.addAction(popupCopyAction);
0319             popup.addSeparator();
0320             popup.addAction(popupCancelAction);
0321 
0322             QAction *result = popup.exec(QCursor::pos());
0323 
0324             if (result == popupCopyAction) {
0325                 action = Qt::CopyAction;
0326             }
0327             else if (result == popupMoveAction) {
0328                 action = Qt::MoveAction;
0329             }
0330             else {
0331                 return;
0332             }
0333      }
0334      else if ((modifiers & Qt::ControlModifier) != 0) {
0335          action = Qt::CopyAction;
0336      }
0337      else if ((modifiers & Qt::ShiftModifier) != 0) {
0338          action = Qt::MoveAction;
0339      }
0340      else {
0341          return;
0342      }
0343 
0344     switch (action) {
0345     case Qt::MoveAction: {
0346         //You can't move slides that not belong to the current document
0347         foreach (KoPAPageBase *slide, slides) {
0348             if (!m_document->pages(false).contains(slide)) {
0349                 slides.removeAll(slide);
0350             }
0351         }
0352         if (slides.isEmpty()) {
0353             return;
0354         }
0355         moveSlides(slides, pageAfter);
0356         return;
0357     }
0358     case Qt::CopyAction: {
0359         copySlides(slides);
0360         m_viewModeSlidesSorter->view()->setActivePage(pageAfter);
0361         pasteSlides();
0362         m_viewModeSlidesSorter->view()->setActivePage(slides.first());
0363         m_viewModeSlidesSorter->selectSlides(slides);
0364         return;
0365     }
0366     default:
0367         debugStage << "Unknown action:" << (int)action;
0368         return;
0369     }
0370 }
0371 
0372 KoPAPageBase * KPrSlidesSorterDocumentModel::pageByIndex(const QModelIndex &index) const
0373 {
0374     Q_ASSERT(index.internalPointer());
0375     return static_cast<KoPAPageBase*>(index.internalPointer());
0376 }
0377 
0378 bool KPrSlidesSorterDocumentModel::removeSlides(const QList<KoPAPageBase *> &slides)
0379 {
0380     if (!slides.empty() && m_document->pages().count() > slides.count()) {
0381         KPrDocument *doc = static_cast<KPrDocument *>(m_document);
0382         KUndo2Command *cmd = new KPrDeleteSlidesCommand(doc, slides);
0383         if (cmd) {
0384             removeRows(m_document->pageIndex(slides.first()), slides.count(), QModelIndex());
0385             m_document->addCommand(cmd);
0386             return true;
0387         }
0388     }
0389     return false;
0390 }
0391 
0392 bool KPrSlidesSorterDocumentModel::addNewSlide()
0393 {
0394     KoPAView *view = dynamic_cast<KoPAView *>(m_viewModeSlidesSorter->view());
0395     if (view) {
0396         view->insertPage();
0397         return true;
0398     }
0399     return false;
0400 }
0401 
0402 bool KPrSlidesSorterDocumentModel::copySlides(const QList<KoPAPageBase *> &slides)
0403 {
0404     if (!slides.empty()) {
0405         // Copy Pages
0406         KoPAOdfPageSaveHelper saveHelper(m_document, slides);
0407         KoDrag drag;
0408         drag.setOdf(KoOdf::mimeType(m_document->documentType()), saveHelper);
0409         drag.addToClipboard();
0410         return true;
0411     }
0412     return false;
0413 }
0414 
0415 bool KPrSlidesSorterDocumentModel::pasteSlides()
0416 {
0417     KoPAView *view = dynamic_cast<KoPAView *>(m_viewModeSlidesSorter->view());
0418     if (view) {
0419         view->pagePaste();
0420         return true;
0421     }
0422     return false;
0423 }
0424 
0425 bool KPrSlidesSorterDocumentModel::moveSlides(const QList<KoPAPageBase *> &slides, KoPAPageBase *pageAfter)
0426 {
0427     KoPAPageMoveCommand *command = new KoPAPageMoveCommand(m_document, slides, pageAfter);
0428     if (command) {
0429         m_document->addCommand(command);
0430         m_viewModeSlidesSorter->view()->setActivePage(slides.first());
0431         m_viewModeSlidesSorter->selectSlides(slides);
0432         return true;
0433     }
0434     return false;
0435 }