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 }