/* * Copyright (C) 2015-2018 Département de l'Instruction Publique (DIP-SEM) * * Copyright (C) 2013 Open Education Foundation * * Copyright (C) 2010-2013 Groupement d'Intérêt Public pour * l'Education Numérique en Afrique (GIP ENA) * * This file is part of OpenBoard. * * OpenBoard is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License, * with a specific linking exception for the OpenSSL project's * "OpenSSL" library (or with modified versions of it that use the * same license as the "OpenSSL" library). * * OpenBoard 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBoard. If not, see . */ #include "UBDocumentController.h" #include #include #include "frameworks/UBFileSystemUtils.h" #include "frameworks/UBStringUtils.h" #include "frameworks/UBPlatformUtils.h" #include "core/UBApplication.h" #include "core/UBPersistenceManager.h" #include "core/UBDocumentManager.h" #include "core/UBApplicationController.h" #include "core/UBSettings.h" #include "core/UBSetting.h" #include "core/UBMimeData.h" #include "core/UBForeignObjectsHandler.h" #include "adaptors/UBExportPDF.h" #include "adaptors/UBThumbnailAdaptor.h" #include "adaptors/UBMetadataDcSubsetAdaptor.h" #include "board/UBBoardController.h" #include "board/UBBoardPaletteManager.h" #include "board/UBDrawingController.h" #include "gui/UBThumbnailView.h" #include "gui/UBMousePressFilter.h" #include "gui/UBMessageWindow.h" #include "gui/UBMainWindow.h" #include "gui/UBDocumentToolsPalette.h" #include "domain/UBGraphicsScene.h" #include "domain/UBGraphicsSvgItem.h" #include "domain/UBGraphicsPixmapItem.h" #include "document/UBDocumentProxy.h" #include "ui_documents.h" #include "ui_mainWindow.h" #include "core/memcheck.h" static bool lessThan(UBDocumentTreeNode *lValue, UBDocumentTreeNode *rValue) { if (lValue->nodeType() == UBDocumentTreeNode::Catalog) { if (rValue->nodeType() == UBDocumentTreeNode::Catalog) { return lValue->nodeName() < rValue->nodeName(); } else { return true; } } else { if (rValue->nodeType() == UBDocumentTreeNode::Catalog) { return false; } else { Q_ASSERT(lValue->proxyData()); Q_ASSERT(rValue->proxyData()); //N/C - NNE - 20140402 : Default order set to alphabetical order //return lValue->nodeName() < rValue->nodeName(); QDateTime lTime = lValue->proxyData()->documentDate(); QDateTime rTime = rValue->proxyData()->documentDate(); return lTime > rTime; } } return false; } UBDocumentReplaceDialog::UBDocumentReplaceDialog(const QString &pIncommingName, const QStringList &pFileList, QWidget *parent, Qt::WindowFlags pFlags) : QDialog(parent, pFlags) , mFileNameList(pFileList) , mIncommingName(pIncommingName) , acceptText(tr("Accept")) , replaceText(tr("Replace")) , cancelText(tr("Cancel")) , mLabelText(0) { this->setStyleSheet("background:white;"); QVBoxLayout *mainLayout = new QVBoxLayout(this); QVBoxLayout *labelLayout = new QVBoxLayout(); mLabelText = new QLabel(labelTextWithName(pIncommingName), this); mLineEdit = new QLineEdit(this); mLineEdit->setText(pIncommingName); mLineEdit->selectedText(); labelLayout->addWidget(mLabelText); labelLayout->addWidget(mLineEdit); QHBoxLayout *buttonLayout = new QHBoxLayout(); acceptButton = new QPushButton(acceptText, this); QPushButton *cancelButton = new QPushButton(cancelText, this); buttonLayout->addWidget(acceptButton); buttonLayout->addWidget(cancelButton); mainLayout->addLayout(labelLayout); mainLayout->addLayout(buttonLayout); acceptButton->setEnabled(false); connect(acceptButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(mLineEdit, SIGNAL(textEdited(QString)), this, SLOT(reactOnTextChanged(QString))); reactOnTextChanged(mIncommingName); } void UBDocumentReplaceDialog::setRegexp(const QRegExp pRegExp) { mValidator->setRegExp(pRegExp); } bool UBDocumentReplaceDialog::validString(const QString &pStr) { Q_UNUSED(pStr); return mLineEdit->hasAcceptableInput(); } void UBDocumentReplaceDialog::setFileNameAndList(const QString &fileName, const QStringList &pLst) { mFileNameList = pLst; mIncommingName = fileName; mLabelText->setText(labelTextWithName(fileName)); mLineEdit->setText(fileName); mLineEdit->selectAll(); mLineEdit->selectedText(); } QString UBDocumentReplaceDialog::labelTextWithName(const QString &documentName) const { return tr("The name %1 is allready used.\nKeeping this name will replace the document.\nProviding a new name will create a new document.") .arg(documentName); } void UBDocumentReplaceDialog::accept() { QDialog::accept(); } void UBDocumentReplaceDialog::reject() { mLineEdit->clear(); emit closeDialog(); QDialog::reject(); } void UBDocumentReplaceDialog::reactOnTextChanged(const QString &pStr) { // if !mFileNameList.contains(pStr.trimmed(), Qt::CaseSensitive) if (!validString(pStr)) { acceptButton->setEnabled(false); mLineEdit->setStyleSheet("background:#FFB3C8;"); acceptButton->setEnabled(false); } else if (mFileNameList.contains(pStr.trimmed(), Qt::CaseSensitive)) { acceptButton->setEnabled(true); mLineEdit->setStyleSheet("background:#FFB3C8;"); acceptButton->setText(replaceText); } else { acceptButton->setEnabled(true); mLineEdit->setStyleSheet("background:white;"); acceptButton->setText(acceptText); } } UBDocumentTreeNode::UBDocumentTreeNode(Type pType, const QString &pName, const QString &pDisplayName, UBDocumentProxy *pProxy ) : mType(pType) , mName(pName) , mDisplayName(pDisplayName) , mProxy(pProxy) { if (pDisplayName.isEmpty()) { mDisplayName = mName; } mParent = 0; } void UBDocumentTreeNode::addChild(UBDocumentTreeNode *pChild) { if (pChild) { mChildren += pChild; pChild->mParent = this; } } void UBDocumentTreeNode::insertChild(int pIndex, UBDocumentTreeNode *pChild) { if (pChild) { mChildren.insert(pIndex, pChild); pChild->mParent = this; } } void UBDocumentTreeNode::moveChild(UBDocumentTreeNode *child, int index, UBDocumentTreeNode *newParent) { int childIndex = mChildren.indexOf(child); if (childIndex == -1) { return; } newParent->insertChild(index, child); mChildren.removeAt(childIndex); } void UBDocumentTreeNode::removeChild(int index) { if (index < 0 || index > mChildren.count() - 1) { return; } UBDocumentTreeNode *curChild = mChildren[index]; while (curChild->mChildren.count()) { curChild->removeChild(0); } mChildren.removeAt(index); delete curChild; } UBDocumentTreeNode *UBDocumentTreeNode::clone() { return new UBDocumentTreeNode(this->mType , this->mName , this->mDisplayName , this->mProxy ? new UBDocumentProxy(*this->mProxy) : 0); } QString UBDocumentTreeNode::dirPathInHierarchy() { QString result; UBDocumentTreeNode *curNode = this; //protect the 2nd level items while (curNode->parentNode() && !curNode->isTopLevel()) { result.prepend(curNode->parentNode()->nodeName() + "/"); curNode = curNode->parentNode(); } if (result.endsWith("/")) { result.truncate(result.count() - 1); } return result; } UBDocumentTreeNode::~UBDocumentTreeNode() { foreach (UBDocumentTreeNode *curChildren, mChildren) { delete(curChildren); curChildren = 0; } if (mProxy) delete mProxy; } //issue 1629 - NNE - 20131105 bool UBDocumentTreeNode::findNode(UBDocumentTreeNode *node) { UBDocumentTreeNode *parent = node->parentNode(); bool hasFound = false; while(parent){ if(parent == this){ hasFound = true; break; } parent = parent->parentNode(); } return hasFound; } UBDocumentTreeNode *UBDocumentTreeNode::nextSibling() { UBDocumentTreeNode *parent = this->parentNode(); UBDocumentTreeNode *nextSibling = NULL; int myIndex = parent->children().indexOf(this); int indexOfNextSibling = myIndex + 1; if(indexOfNextSibling < parent->children().size()){ nextSibling = parent->children().at(indexOfNextSibling); } return nextSibling; } UBDocumentTreeNode *UBDocumentTreeNode::previousSibling() { UBDocumentTreeNode *parent = this->parentNode(); UBDocumentTreeNode *previousSibling = NULL; int myIndex = parent->children().indexOf(this); int indexOfPreviousSibling = myIndex - 1; if(indexOfPreviousSibling >= 0){ previousSibling = parent->children().at(indexOfPreviousSibling); } return previousSibling; } //issue 1629 - NNE - 20131105 : END UBDocumentTreeModel::UBDocumentTreeModel(QObject *parent) : QAbstractItemModel(parent) , mRootNode(0) , mCurrentNode(nullptr) { UBDocumentTreeNode *rootNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, "root"); QString trashName = UBSettings::trashedDocumentGroupNamePrefix; UBDocumentTreeNode *myDocsNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, UBPersistenceManager::myDocumentsName, tr("My documents")); rootNode->addChild(myDocsNode); //UBDocumentTreeNode *modelsNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, UBPersistenceManager::modelsName, tr("Models")); //rootNode->addChild(modelsNode); UBDocumentTreeNode *trashNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, trashName, tr("Trash")); rootNode->addChild(trashNode); //UBDocumentTreeNode *untitledDocumentsNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, UBPersistenceManager::untitledDocumentsName, tr("Untitled documents")); //myDocsNode->addChild(untitledDocumentsNode); setRootNode(rootNode); mRoot = index(0, 0, QModelIndex()); mMyDocuments = index(0, 0, QModelIndex()); mTrash = index(1, 0, QModelIndex()); mUntitledDocuments = index(0, 0, mMyDocuments); mAscendingOrder = true; } QModelIndex UBDocumentTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!mRootNode || row < 0 || column < 0) { return QModelIndex(); } UBDocumentTreeNode *nodeParent = nodeFromIndex(parent); if (!nodeParent || row > nodeParent->children().count() - 1) { return QModelIndex(); } UBDocumentTreeNode *requiredNode = nodeParent->children().at(row); if(!requiredNode) { return QModelIndex(); } QModelIndex resIndex = createIndex(row, column, requiredNode); return resIndex; } QModelIndex UBDocumentTreeModel::parent(const QModelIndex &child) const { UBDocumentTreeNode *nodeChild = nodeFromIndex(child); if (!nodeChild) { return QModelIndex(); } UBDocumentTreeNode *nodeParent = nodeChild->parentNode(); if (!nodeParent) { return QModelIndex(); } UBDocumentTreeNode *nodePreParent = nodeParent->parentNode(); if (!nodePreParent) { return QModelIndex(); } int row = nodePreParent->children().indexOf(nodeParent); QModelIndex resIndex = createIndex(row, 0, nodeParent); return resIndex; } int UBDocumentTreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) { return 0; } UBDocumentTreeNode *nodeParent = nodeFromIndex(parent); if (!nodeParent) { return 0; } return nodeParent->children().count(); } int UBDocumentTreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) //N/C - NNE - 20140403 return 3; } QVariant UBDocumentTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } UBDocumentTreeNode *dataNode = nodeFromIndex(index); if (!dataNode) return QVariant(); if(role == Qt::DisplayRole){ if(index.column() == 0){ return dataNode->displayName(); }else{ UBDocumentProxy *proxy = proxyForIndex(index); QString displayText = ""; if(proxy){ QDateTime d; if(index.column() == 1){ d = proxy->documentDate(); }else if(index.column() == 2){ d = proxy->lastUpdate(); } displayText = d.toString("dd/MM/yyyy hh:mm"); } return displayText; } } if(role == UBDocumentTreeModel::CreationDate){ return findNodeDate(dataNode, UBSettings::documentDate); } if(role == UBDocumentTreeModel::UpdateDate){ return findNodeDate(dataNode, UBSettings::documentUpdatedAt); } if(role == Qt::BackgroundRole){ if (isConstant(index)) { return QBrush(0xD9DFEB); } if (mHighLighted.isValid() && index == mHighLighted) { return QBrush(0x6682B5); } } if(role == Qt::UserRole +1){ return QVariant::fromValue(dataNode); } if (index.column() == 0) { switch (role) { case (Qt::DecorationRole) : if (mCurrentNode && mCurrentNode == dataNode) { return QIcon(":images/currentDocument.png"); } else { if (index == trashIndex()) { return QIcon(":images/trash.png"); } else if (isConstant(index)) { return QIcon(":images/libpalette/ApplicationsCategory.svg"); } switch (static_cast(dataNode->nodeType())) { case UBDocumentTreeNode::Catalog : return QIcon(":images/folder.png"); case UBDocumentTreeNode::Document : return QIcon(":images/toolbar/board.png"); } } break; case (Qt::FontRole) : if (isConstant(index)) { QFont font; font.setBold(true); return font; } break; case (Qt::ForegroundRole) : if (isConstant(index)) { return QColor(Qt::darkGray); } break; } } return QVariant(); } bool UBDocumentTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { switch (role) { case Qt::EditRole: if (!index.isValid() || value.toString().isEmpty()) { return false; } QString fullNewName = value.toString(); if (isCatalog(index)) { fullNewName.replace('/', '-'); } setNewName(index, fullNewName); return true; } return QAbstractItemModel::setData(index, value, role); } Qt::ItemFlags UBDocumentTreeModel::flags (const QModelIndex &index) const { Qt::ItemFlags resultFlags = QAbstractItemModel::flags(index); UBDocumentTreeNode *indexNode = nodeFromIndex(index); if ( index.isValid() ) { if (!indexNode->isRoot() && !isConstant(index)) { if (!inTrash(index)) { resultFlags |= Qt::ItemIsEditable; } resultFlags |= Qt::ItemIsDragEnabled; } if (indexNode->nodeType() == UBDocumentTreeNode::Catalog) { resultFlags |= Qt::ItemIsDropEnabled; } } return resultFlags; } //N/C - NNE -20140407 QDateTime UBDocumentTreeModel::findNodeDate(UBDocumentTreeNode *node, QString type) const { if(type == UBSettings::documentDate){ return findCatalogCreationDate(node); }else if(type == UBSettings::documentUpdatedAt){ return findCatalogUpdatedDate(node); } return QDateTime(); } QDateTime UBDocumentTreeModel::findCatalogUpdatedDate(UBDocumentTreeNode *node) const { UBDocumentProxy *proxy = node->proxyData(); if(proxy){ return proxy->metaData(UBSettings::documentUpdatedAt).toDateTime(); }else if(node->children().size() > 0){ QDateTime d = findCatalogUpdatedDate(node->children().at(0)); for(int i = 1; i < node->children().size(); i++){ QDateTime dChild = findCatalogUpdatedDate(node->children().at(i)); if(dChild != QDateTime()){ if(mAscendingOrder){ d = qMin(d, dChild); }else{ d = qMax(d, dChild); } } } return d; } return QDateTime(); } QDateTime UBDocumentTreeModel::findCatalogCreationDate(UBDocumentTreeNode *node) const { UBDocumentProxy *proxy = node->proxyData(); if(proxy){ return proxy->metaData(UBSettings::documentDate).toDateTime(); }else if(node->children().size() > 0){ QDateTime d = findCatalogCreationDate(node->children().at(0)); for(int i = 1; i < node->children().size(); i++){ QDateTime dChild = findCatalogCreationDate(node->children().at(i)); if(dChild != QDateTime()){ if(mAscendingOrder){ d = qMin(d, dChild); }else{ d = qMax(d, dChild); } } } return d; } return QDateTime(); } //N/C - NNE -20140407 : END QStringList UBDocumentTreeModel::mimeTypes() const { QStringList types; types << "text/uri-list" << "image/png" << "image/tiff" << "image/gif" << "image/jpeg"; return types; } QMimeData *UBDocumentTreeModel::mimeData (const QModelIndexList &indexes) const { UBDocumentTreeMimeData *mimeData = new UBDocumentTreeMimeData(); QList indexList; QList urlList; foreach (QModelIndex index, indexes) { if (index.isValid()) { indexList.append(index); urlList.append(QUrl()); } } #if defined(Q_OS_OSX) #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) if (QOperatingSystemVersion::current().majorVersion() == 10 && QOperatingSystemVersion::current().minorVersion() < 15) /* <= Mojave */ mimeData->setUrls(urlList); #endif #else mimeData->setUrls(urlList); #endif mimeData->setIndexes(indexList); return mimeData; } bool UBDocumentTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { return false; } if (data->hasFormat(UBApplication::mimeTypeUniboardPage)) { UBDocumentTreeNode *curNode = nodeFromIndex(index(row - 1, column, parent)); UBDocumentProxy *targetDocProxy = curNode->proxyData(); const UBMimeData *ubMime = qobject_cast (data); if (!targetDocProxy || !ubMime || !ubMime->items().count()) { qDebug() << "an error ocured while parsing " << UBApplication::mimeTypeUniboardPage; return false; } // int count = 0; int total = ubMime->items().size(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); foreach (UBMimeDataItem sourceItem, ubMime->items()) { UBDocumentProxy *fromProxy = sourceItem.documentProxy(); int fromIndex = sourceItem.sceneIndex(); int toIndex = targetDocProxy->pageCount(); UBPersistenceManager::persistenceManager()->copyDocumentScene(fromProxy, fromIndex, targetDocProxy, toIndex); } QApplication::restoreOverrideCursor(); UBApplication::applicationController->showMessage(tr("%1 pages copied", "", total).arg(total), false); return true; } const UBDocumentTreeMimeData *mimeData = qobject_cast(data); if (!mimeData) { qDebug() << "Incorrect mimeData, only internal one supported"; return false; } if (!parent.isValid()) { return false; } UBDocumentTreeNode *newParentNode = nodeFromIndex(parent); if (!newParentNode) { qDebug() << "incorrect incoming parent node;"; return false; } QList incomingIndexes = mimeData->indexes(); foreach (QModelIndex curIndex, incomingIndexes) { //Issue N/C - NNE - 20140528 : use just the index on the first column if(curIndex.column() == 0){ QModelIndex clonedTopLevel = copyIndexToNewParent(curIndex, parent, action == Qt::MoveAction ? aReference : aContentCopy); if (nodeFromIndex(curIndex) == mCurrentNode && action == Qt::MoveAction) { emit currentIndexMoved(clonedTopLevel, curIndex); } } } Q_UNUSED(action) Q_UNUSED(row) Q_UNUSED(column) Q_UNUSED(parent) return true; } bool UBDocumentTreeModel::removeRows(int row, int count, const QModelIndex &parent) { if (row < 0 || row + count > rowCount(parent)) return false; beginRemoveRows( parent, row, row + count - 1); UBDocumentTreeNode *parentNode = nodeFromIndex(parent); for (int i = row; i < row + count; i++) { UBDocumentTreeNode *curChildNode = parentNode->children().at(i); QModelIndex curChildIndex = parent.child(i, 0); if (curChildNode) { if (rowCount(curChildIndex)) { while (rowCount(curChildIndex)) { removeRows(0, 1, curChildIndex); } } } mNewDocuments.removeAll(curChildNode->proxyData()); parentNode->removeChild(i); } endRemoveRows(); return true; } bool UBDocumentTreeModel::containsDocuments(const QModelIndex &index) { for (int i = 0; i < rowCount(index); i++) { QModelIndex child = this->index(i, 0, index); if (isCatalog(child)) { if (containsDocuments(child)) { return true; } } else if (isDocument(child)) { return true; } else { qDebug() << "Who the hell are you ?"; } } return false; } QModelIndex UBDocumentTreeModel::indexForNode(UBDocumentTreeNode *pNode) const { if (pNode == 0) { return QModelIndex(); } return pIndexForNode(QModelIndex(), pNode); } QPersistentModelIndex UBDocumentTreeModel::persistentIndexForNode(UBDocumentTreeNode *pNode) { return QPersistentModelIndex(indexForNode(pNode)); } UBDocumentTreeNode *UBDocumentTreeModel::findProxy(UBDocumentProxy *pSearch, UBDocumentTreeNode *pParent) const { foreach (UBDocumentTreeNode *curNode, pParent->children()) { if (UBDocumentTreeNode::Catalog != curNode->nodeType()) { if (curNode->proxyData()->theSameDocument(pSearch)) return curNode; } else if (curNode->children().count()) { UBDocumentTreeNode *recursiveDescendResult = findProxy(pSearch, curNode); if (recursiveDescendResult) return findProxy(pSearch, curNode); } } return 0; } QModelIndex UBDocumentTreeModel::pIndexForNode(const QModelIndex &parent, UBDocumentTreeNode *pNode) const { for (int i = 0; i < rowCount(parent); i++) { QModelIndex curIndex = index(i, 0, parent); if (curIndex.internalPointer() == pNode) { return curIndex; } else if (rowCount(curIndex) > 0) { QModelIndex recursiveDescendIndex = pIndexForNode(curIndex, pNode); if (recursiveDescendIndex.isValid()) { return recursiveDescendIndex; } } } return QModelIndex(); } //N/C - NNE - 20140411 void UBDocumentTreeModel::copyIndexToNewParent(const QModelIndexList &list, const QModelIndex &newParent, eCopyMode pMode) { for(int i = 0; i < list.size(); i++){ if(list.at(i).column() == 0){ copyIndexToNewParent(list.at(i), newParent, pMode); } } } //N/C - NNE - 20140411 : END QPersistentModelIndex UBDocumentTreeModel::copyIndexToNewParent(const QModelIndex &source, const QModelIndex &newParent, eCopyMode pMode) { UBDocumentTreeNode *nodeParent = nodeFromIndex(newParent); UBDocumentTreeNode *nodeSource = nodeFromIndex(source); if (!nodeParent || !nodeSource) { return QModelIndex(); } //beginInsertRows(newParent, rowCount(newParent), rowCount(newParent)); UBDocumentTreeNode *clonedNodeSource = 0; switch (static_cast(pMode)) { case aReference: clonedNodeSource = nodeSource->clone(); if (mNewDocuments.contains(nodeSource->proxyData())) { //update references for session documents mNewDocuments << clonedNodeSource->proxyData(); UBPersistenceManager::persistenceManager()->reassignDocProxy(clonedNodeSource->proxyData(), nodeSource->proxyData()); } break; case aContentCopy: UBDocumentProxy* duplicatedProxy = 0; if (nodeSource->nodeType() == UBDocumentTreeNode::Document && nodeSource->proxyData()) { duplicatedProxy = UBPersistenceManager::persistenceManager()->duplicateDocument(nodeSource->proxyData()); duplicatedProxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(duplicatedProxy); } clonedNodeSource = new UBDocumentTreeNode(nodeSource->nodeType() , nodeSource->nodeName() , nodeSource->displayName() , duplicatedProxy); break; } // Determine whether to provide a name with postfix if the name in current level already exists QString newName = clonedNodeSource->nodeName(); if ((source.parent() != newParent || pMode != aReference) && (newParent != trashIndex() || !inTrash(newParent))) { newName = adjustNameForParentIndex(newName, newParent); clonedNodeSource->setNodeName(newName); } if (clonedNodeSource->proxyData()) { clonedNodeSource->proxyData()->setMetaData(UBSettings::documentGroupName, virtualPathForIndex(newParent)); clonedNodeSource->proxyData()->setMetaData(UBSettings::documentName, newName); UBPersistenceManager::persistenceManager()->persistDocumentMetadata(clonedNodeSource->proxyData()); } addNode(clonedNodeSource, newParent); //endInsertRows(); QPersistentModelIndex newParentIndex = createIndex(rowCount(newParent), 0, clonedNodeSource); if (rowCount(source)) { for (int i = 0; i < rowCount(source); i++) { QModelIndex curNewParentIndexChild = source.child(i, 0); copyIndexToNewParent(curNewParentIndexChild, newParentIndex, pMode); } } return newParentIndex; } //N/C - NNE - 20140409 void UBDocumentTreeModel::moveIndexes(const QModelIndexList &source, const QModelIndex &destination) { //Issue N/C - NNE - 20140528 QModelIndex destinationParent = destination; while(!isCatalog(destinationParent)){ destinationParent = destinationParent.parent(); } UBDocumentTreeNode *newParentNode = nodeFromIndex(destinationParent); bool hasOneInsertion = false; for(int i = 0; i < source.size(); i++){ UBDocumentTreeNode *sourceNode = nodeFromIndex(source.at(i)); QModelIndex s = source.at(i); if(newParentNode == sourceNode->parentNode() || sourceNode->findNode(newParentNode)) continue; if(s.internalId() != destinationParent.internalId()){ int sourceIndex = source.at(i).row(); int destIndex = positionForParent(sourceNode, newParentNode); beginMoveRows(s.parent(), sourceIndex, sourceIndex, destinationParent, destIndex); fixNodeName(s, destinationParent); sourceNode->parentNode()->moveChild(sourceNode, destIndex, newParentNode); updateIndexNameBindings(sourceNode); hasOneInsertion = true; } } if(hasOneInsertion) endMoveRows(); } //N/C - NNE - 20140409 void UBDocumentTreeModel::moveIndex(const QModelIndex &what, const QModelIndex &destination) { QModelIndexList list; list.push_back(what); moveIndexes(list, destination); } void UBDocumentTreeModel::setCurrentDocument(UBDocumentProxy *pDocument) { UBDocumentTreeNode *testCurNode = findProxy(pDocument, mRootNode); if (testCurNode) { setCurrentNode(testCurNode); } } QModelIndex UBDocumentTreeModel::indexForProxy(UBDocumentProxy *pSearch) const { UBDocumentTreeNode *proxy = findProxy(pSearch, mRootNode); if (!proxy) { return QModelIndex(); } return indexForNode(proxy); } void UBDocumentTreeModel::setRootNode(UBDocumentTreeNode *pRoot) { mRootNode = pRoot; //reset(); } UBDocumentProxy *UBDocumentTreeModel::proxyForIndex(const QModelIndex &pIndex) const { UBDocumentTreeNode *node = nodeFromIndex(pIndex); if (!node) { return 0; } return node->proxyData(); } QString UBDocumentTreeModel::virtualDirForIndex(const QModelIndex &pIndex) const { QString result; UBDocumentTreeNode *curNode = nodeFromIndex(pIndex); //protect the 2nd level items while (curNode->parentNode() && !curNode->isTopLevel()) { result.prepend(curNode->parentNode()->nodeName() + "/"); curNode = curNode->parentNode(); } if (result.endsWith("/")) { result.truncate(result.count() - 1); } return result; } QString UBDocumentTreeModel::virtualPathForIndex(const QModelIndex &pIndex) const { UBDocumentTreeNode *curNode = nodeFromIndex(pIndex); Q_ASSERT(curNode); return virtualDirForIndex(pIndex) + "/" + curNode->nodeName(); } QList UBDocumentTreeModel::nodeChildrenFromIndex(const QModelIndex &pIndex) const { UBDocumentTreeNode *node = nodeFromIndex(pIndex); if (node) return node->children(); else return QList(); } QStringList UBDocumentTreeModel::nodeNameList(const QModelIndex &pIndex, bool distinctNodeType) const { QStringList result; UBDocumentTreeNode *catalog = nodeFromIndex(pIndex); if (catalog->nodeType() != UBDocumentTreeNode::Catalog) { return QStringList(); } foreach (UBDocumentTreeNode *curNode, catalog->children()) { if (distinctNodeType) { if (curNode->nodeType() == UBDocumentTreeNode::Catalog) { result << "folder - " + curNode->nodeName(); } else { result << curNode->nodeName(); } } else { result << curNode->nodeName(); } } return result; } bool UBDocumentTreeModel::newNodeAllowed(const QModelIndex &pSelectedIndex) const { if (!pSelectedIndex.isValid()) { return false; } if (inTrash(pSelectedIndex) || pSelectedIndex == trashIndex()) { return false; } return true; } QModelIndex UBDocumentTreeModel::goTo(const QString &dir) { QStringList pathList = dir.split("/", QString::SkipEmptyParts); if (pathList.isEmpty()) { return untitledDocumentsIndex(); } if (pathList.first() != UBPersistenceManager::myDocumentsName && pathList.first() != UBSettings::trashedDocumentGroupNamePrefix && pathList.first() != UBPersistenceManager::modelsName) { pathList.prepend(UBPersistenceManager::myDocumentsName); } QModelIndex parentIndex; bool searchingNode = true; while (!pathList.isEmpty()) { QString curLevelName = pathList.takeFirst(); if (searchingNode) { searchingNode = false; int irowCount = rowCount(parentIndex); for (int i = 0; i < irowCount; ++i) { QModelIndex curChildIndex = index(i, 0, parentIndex); UBDocumentTreeNode* currentNode = nodeFromIndex(curChildIndex); if (currentNode->nodeName() == curLevelName && currentNode->nodeType() == UBDocumentTreeNode::Catalog) { searchingNode = true; parentIndex = curChildIndex; break; } } } if (!searchingNode) { UBDocumentTreeNode *newChild = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, curLevelName); parentIndex = addNode(newChild, parentIndex); } } return parentIndex; } bool UBDocumentTreeModel::inTrash(const QModelIndex &index) const { return isDescendantOf(index, trashIndex()); } bool UBDocumentTreeModel::inUntitledDocuments(const QModelIndex &index) const { return isDescendantOf(index, untitledDocumentsIndex()); } //N/C - NNE - 20140408 bool UBDocumentTreeModel::inMyDocuments(const QModelIndex &index) const { return isDescendantOf(index, myDocumentsIndex()); } //N/C - NNE - 20140408 : END void UBDocumentTreeModel::addDocument(UBDocumentProxy *pProxyData, const QModelIndex &pParent) { if (!pProxyData) { return; } QString docName = pProxyData->metaData(UBSettings::documentName).toString(); QString docGroupName = pProxyData->metaData(UBSettings::documentGroupName).toString(); if (docName.isEmpty()) { return; } QModelIndex lParent = pParent; UBDocumentTreeNode *freeNode = new UBDocumentTreeNode(UBDocumentTreeNode::Document , docName , QString() , pProxyData); if (!pParent.isValid()) { lParent = goTo(docGroupName); } addNode(freeNode, lParent); } void UBDocumentTreeModel::addNewDocument(UBDocumentProxy *pProxyData, const QModelIndex &pParent) { addDocument(pProxyData, pParent); mNewDocuments << pProxyData; } QModelIndex UBDocumentTreeModel::addCatalog(const QString &pName, const QModelIndex &pParent) { if (pName.isEmpty() || !pParent.isValid()) { return QModelIndex(); } UBDocumentTreeNode *catalogNode = new UBDocumentTreeNode(UBDocumentTreeNode::Catalog, pName); return addNode(catalogNode, pParent); } void UBDocumentTreeModel::setNewName(const QModelIndex &index, const QString &newName) { if (!index.isValid()) { return; } UBDocumentTreeNode *indexNode = nodeFromIndex(index); QString magicSeparator = "+!##s"; if (isCatalog(index)) { QString fullNewName = newName; if (!newName.contains(magicSeparator)) { indexNode->setNodeName(fullNewName); QString virtualDir = virtualDirForIndex(index); fullNewName.prepend(virtualDir.isEmpty() ? "" : virtualDir + magicSeparator); } for (int i = 0; i < rowCount(index); i++) { QModelIndex subIndex = this->index(i, 0, index); setNewName(subIndex, fullNewName + magicSeparator + subIndex.data().toString()); } } else if (isDocument(index)) { Q_ASSERT(indexNode->proxyData()); int prefixIndex = newName.lastIndexOf(magicSeparator); if (prefixIndex != -1) { QString newDocumentGroupName = newName.left(prefixIndex).replace(magicSeparator, "/"); indexNode->proxyData()->setMetaData(UBSettings::documentGroupName, newDocumentGroupName); } else { indexNode->setNodeName(newName); indexNode->proxyData()->setMetaData(UBSettings::documentName, newName); } UBPersistenceManager::persistenceManager()->persistDocumentMetadata(indexNode->proxyData()); } } QString UBDocumentTreeModel::adjustNameForParentIndex(const QString &pName, const QModelIndex &pIndex) { int i = 0; QString newName = pName; QStringList siblingNames = nodeNameList(pIndex); while (siblingNames.contains(newName)) { newName = pName + " " + QVariant(++i).toString(); } return newName; } void UBDocumentTreeModel::fixNodeName(const QModelIndex &source, const QModelIndex &dest) { // Determine whether to provide a name with postfix if the name in current level allready exists UBDocumentTreeNode *srcNode = nodeFromIndex(source); Q_ASSERT(srcNode); QString newName = srcNode->nodeName(); if (source.parent() != dest && (dest != trashIndex() || !inTrash(dest))) { newName = adjustNameForParentIndex(newName, dest); srcNode->setNodeName(newName); nodeFromIndex(source)->setNodeName(newName); } } void UBDocumentTreeModel::updateIndexNameBindings(UBDocumentTreeNode *nd) { Q_ASSERT(nd); if (nd->nodeType() == UBDocumentTreeNode::Catalog) { foreach (UBDocumentTreeNode *lnd, nd->children()) { updateIndexNameBindings(lnd); } } else if (nd->proxyData()) { nd->proxyData()->setMetaData(UBSettings::documentGroupName, virtualPathForIndex(indexForNode(nd->parentNode()))); nd->proxyData()->setMetaData(UBSettings::documentName, nd->nodeName()); nd->proxyData()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBPersistenceManager::persistenceManager()->persistDocumentMetadata(nd->proxyData()); } } bool UBDocumentTreeModel::isDescendantOf(const QModelIndex &pPossibleDescendant, const QModelIndex &pPossibleAncestor) const { if (!pPossibleDescendant.isValid()) { return false; } QModelIndex ancestor = pPossibleDescendant; while (ancestor.parent().isValid()) { ancestor = ancestor.parent(); if (ancestor == pPossibleAncestor) { return true; } } return false; } QModelIndex UBDocumentTreeModel::addNode(UBDocumentTreeNode *pFreeNode, const QModelIndex &pParent, eAddItemMode pMode) { UBDocumentTreeNode *tstParent = nodeFromIndex(pParent); if (!pParent.isValid() || tstParent->nodeType() != UBDocumentTreeNode::Catalog) { return QModelIndex(); } int newIndex = pMode == aDetectPosition ? positionForParent(pFreeNode, tstParent): tstParent->children().size(); beginInsertRows(pParent, newIndex, newIndex); tstParent->insertChild(newIndex, pFreeNode); endInsertRows(); return createIndex(newIndex, 0, pFreeNode); } int UBDocumentTreeModel::positionForParent(UBDocumentTreeNode *pFreeNode, UBDocumentTreeNode *pParentNode) { Q_ASSERT(pFreeNode); Q_ASSERT(pParentNode); Q_ASSERT(pParentNode->nodeType() == UBDocumentTreeNode::Catalog); int c = -1; int childCount = pParentNode->children().count(); while (c <= childCount) { if (++c == childCount || lessThan(pFreeNode, pParentNode->children().at(c))) { break; } } return c == -1 ? childCount : c; } UBDocumentTreeNode *UBDocumentTreeModel::nodeFromIndex(const QModelIndex &pIndex) const { if (pIndex.isValid()) { return static_cast(pIndex.internalPointer()); } else { return mRootNode; } } bool UBDocumentTreeModel::nodeLessThan(const UBDocumentTreeNode *firstIndex, const UBDocumentTreeNode *secondIndex) { return firstIndex->nodeName() < secondIndex->nodeName(); } UBDocumentTreeModel::~UBDocumentTreeModel() { delete mRootNode; } UBDocumentTreeView::UBDocumentTreeView(QWidget *parent) : QTreeView(parent) { setObjectName("UBDocumentTreeView"); setRootIsDecorated(true); setSelectionBehavior(SelectRows); } void UBDocumentTreeView::setSelectedAndExpanded(const QModelIndex &pIndex, bool pExpand, bool pEdit) { if (!pIndex.isValid()) { return; } QModelIndex indexCurrentDoc = pIndex; clearSelection(); UBSortFilterProxyModel *proxy = dynamic_cast(model()); QItemSelectionModel::SelectionFlags sel = pExpand ? QItemSelectionModel::Select : QItemSelectionModel::Deselect; setCurrentIndex(indexCurrentDoc); selectionModel()->setCurrentIndex(proxy->mapFromSource(indexCurrentDoc), QItemSelectionModel::SelectCurrent); selectionModel()->select(proxy->mapFromSource(indexCurrentDoc), QItemSelectionModel::Rows | sel); while (indexCurrentDoc.parent().isValid()) { setExpanded(indexCurrentDoc.parent(), pExpand); indexCurrentDoc = indexCurrentDoc.parent(); } scrollTo(proxy->mapFromSource(pIndex)); if (pEdit) edit(proxy->mapFromSource(pIndex)); } void UBDocumentTreeView::onModelIndexChanged(const QModelIndex &pNewIndex, const QModelIndex &pOldIndex) { Q_UNUSED(pOldIndex) //N/C - NNE - 20140407 QModelIndex indexSource = mapIndexToSource(pNewIndex); setSelectedAndExpanded(indexSource, true); } void UBDocumentTreeView::hSliderRangeChanged(int min, int max) { Q_UNUSED(min); Q_UNUSED(max); QScrollBar *hScroller = horizontalScrollBar(); if (hScroller) { hScroller->triggerAction(QAbstractSlider::SliderToMaximum); } } void UBDocumentTreeView::mousePressEvent(QMouseEvent *event) { QTreeView::mousePressEvent(event); UBApplication::documentController->clearThumbnailsSelection(); } void UBDocumentTreeView::dragEnterEvent(QDragEnterEvent *event) { QTreeView::dragEnterEvent(event); event->accept(); event->acceptProposedAction(); } void UBDocumentTreeView::dragLeaveEvent(QDragLeaveEvent *event) { Q_UNUSED(event); UBDocumentTreeModel *docModel = 0; UBSortFilterProxyModel *proxy = dynamic_cast(model()); if(proxy){ docModel = dynamic_cast(proxy->sourceModel()); }else{ docModel = dynamic_cast(model()); } docModel->setHighLighted(QModelIndex()); update(); } void UBDocumentTreeView::dragMoveEvent(QDragMoveEvent *event) { QModelIndex index; if (selectedIndexes().count() > 0) { index = selectedIndexes().first(); } bool acceptIt = isAcceptable(index, indexAt(event->pos())); if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage)) { UBSortFilterProxyModel *proxy = dynamic_cast(model()); UBDocumentTreeModel *docModel = 0; if(proxy){ docModel = dynamic_cast(proxy->sourceModel()); }else{ docModel = dynamic_cast(model()); } QModelIndex targetIndex = mapIndexToSource(indexAt(event->pos())); if (!docModel || !docModel->isDocument(targetIndex) || docModel->inTrash(targetIndex)) { event->ignore(); event->setDropAction(Qt::IgnoreAction); docModel->setHighLighted(QModelIndex()); acceptIt = false; } else { docModel->setHighLighted(targetIndex); acceptIt = true; } updateIndexEnvirons(indexAt(event->pos())); } QTreeView::dragMoveEvent(event); event->setAccepted(acceptIt); } void UBDocumentTreeView::dropEvent(QDropEvent *event) { event->ignore(); event->setDropAction(Qt::IgnoreAction); UBDocumentTreeModel *docModel = 0; //N/C - NNE - 20140408 UBSortFilterProxyModel *proxy = dynamic_cast(model()); if(proxy){ docModel = dynamic_cast(proxy->sourceModel()); } QModelIndex targetIndex = mapIndexToSource(indexAt(event->pos())); QModelIndexList dropIndex = mapIndexesToSource(selectedIndexes()); //clear the selection right after //selectionModel()->clearSelection(); bool isUBPage = event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage); //issue 1629 - NNE - 20131212 bool targetIsInTrash = docModel->inTrash(targetIndex) || docModel->trashIndex() == targetIndex; bool targetIsInMyDocuments = docModel->inMyDocuments(targetIndex) || docModel->myDocumentsIndex() == targetIndex; if (!targetIsInMyDocuments && !targetIsInTrash) return; if (isUBPage) { UBDocumentProxy *targetDocProxy = docModel->proxyData(targetIndex); const UBMimeData *ubMime = qobject_cast (event->mimeData()); if (!targetDocProxy || !ubMime || !ubMime->items().count()) { qDebug() << "an error ocured while parsing " << UBApplication::mimeTypeUniboardPage; QTreeView::dropEvent(event); return; } int count = 0; int total = ubMime->items().size(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); foreach (UBMimeDataItem sourceItem, ubMime->items()) { UBDocumentProxy *fromProxy = sourceItem.documentProxy(); int fromIndex = sourceItem.sceneIndex(); int toIndex = targetDocProxy->pageCount(); count++; UBApplication::applicationController->showMessage(tr("Copying page %1/%2").arg(count).arg(total), true); // TODO UB 4.x Move following code to some controller class UBGraphicsScene *scene = UBPersistenceManager::persistenceManager()->loadDocumentScene(sourceItem.documentProxy(), sourceItem.sceneIndex()); if (scene) { UBGraphicsScene* sceneClone = scene->sceneDeepCopy(); UBDocumentProxy *targetDocProxy = docModel->proxyForIndex(targetIndex); foreach (QUrl relativeFile, scene->relativeDependencies()) { QString source = scene->document()->persistencePath() + "/" + relativeFile.toString(); QString target = targetDocProxy->persistencePath() + "/" + relativeFile.toString(); QString sourceDecoded = scene->document()->persistencePath() + "/" + relativeFile.toString(QUrl::DecodeReserved); QString targetDecoded = targetDocProxy->persistencePath() + "/" + relativeFile.toString(QUrl::DecodeReserved); if(QFileInfo(source).isDir()) UBFileSystemUtils::copyDir(source,target); else{ QFileInfo fi(targetDecoded); QDir d = fi.dir(); d.mkpath(d.absolutePath()); QFile::copy(sourceDecoded, targetDecoded); } } UBPersistenceManager::persistenceManager()->insertDocumentSceneAt(targetDocProxy, sceneClone, targetDocProxy->pageCount()); QString thumbTmp(fromProxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.thumbnail.jpg", fromIndex)); QString thumbTo(targetDocProxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.thumbnail.jpg", toIndex)); QFile::remove(thumbTo); QFile::copy(thumbTmp, thumbTo); Q_ASSERT(QFileInfo(thumbTmp).exists()); Q_ASSERT(QFileInfo(thumbTo).exists()); auto pix = std::make_shared(thumbTmp); UBDocumentController *ctrl = UBApplication::documentController; ctrl->addPixmapAt(pix, toIndex); if (UBApplication::boardController->selectedDocument() == targetDocProxy) { UBApplication::boardController->insertThumbPage(toIndex); } } QApplication::restoreOverrideCursor(); docModel->setHighLighted(QModelIndex()); } UBApplication::applicationController->showMessage(tr("%1 pages copied", "", total).arg(total), false); UBApplication::documentController->TreeViewSelectionChanged(UBApplication::documentController->firstSelectedTreeIndex(), QModelIndex()); } else { if(targetIsInTrash) { if (!UBApplication::mainWindow->yesNoQuestion(tr("Remove Item"), tr("Are you sure you want to remove the selected item(s) ?"))) return; UBApplication::documentController->moveIndexesToTrash(dropIndex, docModel); }else{ docModel->moveIndexes(dropIndex, targetIndex); } } expand(proxy->mapFromSource(targetIndex)); QTreeView::dropEvent(event); UBApplication::documentController->pageSelectionChanged(); } void UBDocumentTreeView::paintEvent(QPaintEvent *event) { QTreeView::paintEvent(event); } void UBDocumentTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { QTreeView::rowsAboutToBeRemoved(parent, start, end); } bool UBDocumentTreeView::isAcceptable(const QModelIndex &dragIndex, const QModelIndex &atIndex) { QModelIndex dragIndexSource = mapIndexToSource(dragIndex); QModelIndex atIndexSource = mapIndexToSource(atIndex); if (!dragIndexSource.isValid()) { return false; } return true; } Qt::DropAction UBDocumentTreeView::acceptableAction(const QModelIndex &dragIndex, const QModelIndex &atIndex) { return Qt::MoveAction; } void UBDocumentTreeView::updateIndexEnvirons(const QModelIndex &index) { QRect updateRect = visualRect(index); const int multipler = 3; updateRect.adjust(0, -updateRect.height() * multipler, 0, updateRect.height() * multipler); update(updateRect); } //N/C - NNE - 20140404 QModelIndex UBDocumentTreeView::mapIndexToSource(const QModelIndex &index) { UBSortFilterProxyModel *proxy = dynamic_cast(model()); if(proxy){ return proxy->mapToSource(index); } return index; } QModelIndexList UBDocumentTreeView::mapIndexesToSource(const QModelIndexList &indexes) { UBSortFilterProxyModel *proxy = dynamic_cast(model()); if(proxy){ QModelIndexList list; for(int i = 0; i < indexes.size(); i++){ list.push_back(proxy->mapToSource(indexes.at(i))); } return list; } return indexes; } //N/C - NNE - 20140404 : END UBDocumentTreeItemDelegate::UBDocumentTreeItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void UBDocumentTreeItemDelegate::commitAndCloseEditor() { QLineEdit *lineEditor = dynamic_cast(sender()); if (lineEditor) { if (lineEditor->hasAcceptableInput()) { emit commitData(lineEditor); //emit closeEditor(lineEditor); } emit UBApplication::documentController->reorderDocumentsRequested(); } } void UBDocumentTreeItemDelegate::processChangedText(const QString &str) const { QLineEdit *editor = qobject_cast(sender()); if (editor) { if (editor->validator()) { int pos = 0; if (editor->validator()->validate(const_cast(str), pos) != QValidator::Acceptable) { editor->setStyleSheet("background-color: #FFB3C8;"); } else { editor->setStyleSheet("background-color: #FFFFFF;"); } } } } bool UBDocumentTreeItemDelegate::validateString(const QString &str) const { return !mExistingFileNames.contains(str); } QWidget *UBDocumentTreeItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); //N/C - NNE - 20140407 : Add the test for the index column. if(index.column() == 0){ mExistingFileNames.clear(); const UBDocumentTreeModel *docModel = 0; const UBSortFilterProxyModel *proxy = dynamic_cast(index.model()); if(proxy){ docModel = dynamic_cast(proxy->sourceModel()); }else{ docModel = dynamic_cast(index.model()); } QModelIndex sourceIndex = proxy->mapToSource(index); if (docModel) { mExistingFileNames = docModel->nodeNameList(sourceIndex.parent()); mExistingFileNames.removeOne(sourceIndex.data().toString()); UBDocumentTreeNode* sourceNode = docModel->nodeFromIndex(sourceIndex); if (sourceNode) { QLineEdit *nameEditor = new QLineEdit(parent); QList nodeChildren = docModel->nodeChildrenFromIndex(sourceIndex.parent()); nodeChildren.removeOne(sourceNode); UBValidator* validator = new UBValidator(nodeChildren, sourceNode->nodeType()); nameEditor->setValidator(validator); connect(nameEditor, SIGNAL(editingFinished()), this, SLOT(commitAndCloseEditor())); connect(nameEditor, SIGNAL(textChanged(QString)), this, SLOT(processChangedText(QString))); return nameEditor; } } return nullptr; } //N/C - NNe - 20140407 : the other column are not editable. return 0; } void UBDocumentTreeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.column() == 0) { QLineEdit *lineEditor = qobject_cast(editor); lineEditor->setText(index.data().toString()); lineEditor->selectAll(); } } void UBDocumentTreeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *lineEditor = qobject_cast(editor); if (lineEditor) { int pos; QString input = lineEditor->text(); if (lineEditor->validator()) { if (lineEditor->validator()->validate(input, pos) == QValidator::Acceptable) { model->setData(index, input); } } } } void UBDocumentTreeItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &index) const { QStyledItemDelegate::paint(painter, option, index); } UBDocumentController::UBDocumentController(UBMainWindow* mainWindow) : UBDocumentContainer(mainWindow->centralWidget()) , mSelectionType(None) , mParentWidget(mainWindow->centralWidget()) , mBoardController(UBApplication::boardController) , mDocumentUI(0) , mMainWindow(mainWindow) , mDocumentWidget(0) , mIsClosing(false) , mToolsPalette(0) , mToolsPalettePositionned(false) , mTrashTi(0) , mDocumentTrashGroupName(tr("Trash")) , mDefaultDocumentGroupName(tr("Untitled Documents")) , mCurrentTreeDocument(0) , mCurrentIndexMoved(false) { setupViews(); setupToolbar(); connect(this, SIGNAL(exportDone()), mMainWindow, SLOT(onExportDone())); connect(this, SIGNAL(documentThumbnailsUpdated(UBDocumentContainer*)), this, SLOT(refreshDocumentThumbnailsView(UBDocumentContainer*))); //connect(this, SIGNAL(documentPageInserted(int)), this, SLOT(insertThumbnail(int))); connect(this, SIGNAL(documentPageUpdated(int)), this, SLOT(updateThumbnail(int))); connect(this, SIGNAL(documentPageRemoved(int)), this, SLOT(removeThumbnail(int))); connect(this, SIGNAL(documentPageMoved(int, int)), this, SLOT(moveThumbnail(int, int))); connect(this, SIGNAL(reorderDocumentsRequested()), this, SLOT(reorderDocuments())); } UBDocumentController::~UBDocumentController() { if (mDocumentUI) delete mDocumentUI; } void UBDocumentController::createNewDocument() { UBPersistenceManager *pManager = UBPersistenceManager::persistenceManager(); UBDocumentTreeModel *docModel = pManager->mDocumentTreeStructureModel; QModelIndex selectedIndex = firstSelectedTreeIndex(); if (!selectedIndex.isValid()) selectedIndex = docModel->myDocumentsIndex(); QString groupName = docModel->isCatalog(selectedIndex) ? docModel->virtualPathForIndex(selectedIndex) : docModel->virtualDirForIndex(selectedIndex); QDateTime now = QDateTime::currentDateTime(); QString documentName = ""; if (docModel->isCatalog(selectedIndex)) { documentName = docModel->adjustNameForParentIndex(now.toString(Qt::SystemLocaleShortDate), selectedIndex); } else { documentName = docModel->adjustNameForParentIndex(now.toString(Qt::SystemLocaleShortDate), selectedIndex.parent()); } UBDocumentProxy *document = pManager->createDocument(groupName, documentName); selectDocument(document, true, false, true); if (document) pManager->mDocumentTreeStructureModel->markDocumentAsNew(document); pageSelectionChanged(); } void UBDocumentController::selectDocument(UBDocumentProxy* proxy, bool setAsCurrentDocument, const bool onImport, const bool editMode) { if (proxy==NULL) { setDocument(NULL); return; } if (setAsCurrentDocument) { UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->setCurrentDocument(proxy); QModelIndex indexCurrentDoc = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->indexForProxy(proxy); if (indexCurrentDoc.isValid()) { mDocumentUI->documentTreeView->setSelectedAndExpanded(indexCurrentDoc, true, editMode); if (proxy != mBoardController->selectedDocument()) // only if wanted Document is different from document actually on Board, // ALTI/AOU - 20140217 { //issue 1629 - NNE - 20131105 : When set a current document, change in the board controller mBoardController->setActiveDocumentScene(proxy, 0, true, onImport); } } else { qWarning() << "an issue occured while trying to select current index in document tree"; } } setDocument(proxy); } void UBDocumentController::createNewDocumentGroup() { UBPersistenceManager *pManager = UBPersistenceManager::persistenceManager(); UBDocumentTreeModel *docModel = pManager->mDocumentTreeStructureModel; QModelIndex selectedIndex = firstSelectedTreeIndex(); if (!selectedIndex.isValid()) { selectedIndex = docModel->myDocumentsIndex(); } QModelIndex parentIndex = docModel->isCatalog(selectedIndex) ? selectedIndex : selectedIndex.parent(); QString newFolderName = docModel->adjustNameForParentIndex(tr("New Folder"), parentIndex); QModelIndex newIndex = docModel->addCatalog(newFolderName, parentIndex); mDocumentUI->documentTreeView->setSelectedAndExpanded(newIndex, true, true); pageSelectionChanged(); } UBDocumentProxy* UBDocumentController::selectedDocumentProxy() { return UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->proxyForIndex(firstSelectedTreeIndex()); } QList UBDocumentController::selectedProxies() { QList result; foreach (QModelIndex curIndex, mapIndexesToSource(mDocumentUI->documentTreeView->selectionModel()->selectedIndexes())) { result << UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->proxyForIndex(curIndex); } return result; } QModelIndexList UBDocumentController::selectedTreeIndexes() { return mapIndexesToSource(mDocumentUI->documentTreeView->selectionModel()->selectedRows(0)); } UBDocumentProxy* UBDocumentController::firstSelectedTreeProxy() { return selectedProxies().count() ? selectedProxies().first() : 0; } void UBDocumentController::TreeViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(previous) UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndex current_index = mapIndexToSource(current); //N/C - NNE - 20140414 //if the selection contains more than one object, don't show the thumbnail. //We have just to pass a null proxy to disable the display of thumbnail UBDocumentProxy *currentDocumentProxy = 0; if(current_index.isValid() && mDocumentUI->documentTreeView->selectionModel()->selectedRows(0).size() == 1) { currentDocumentProxy = docModel->proxyData(current_index); setDocument(currentDocumentProxy, false); } //N/C - NNE - 20140414 : END if (mCurrentIndexMoved) { if (docModel->isDocument(current_index)) { docModel->setCurrentDocument(currentDocumentProxy); } else if (docModel->isCatalog(current_index)) { docModel->setCurrentDocument(0); } mCurrentIndexMoved = false; } } //N/C - NNE - 20140402 : workaround for using a proxy model QModelIndex UBDocumentController::mapIndexToSource(const QModelIndex &index) { UBSortFilterProxyModel *proxy = dynamic_cast(mDocumentUI->documentTreeView->model()); if(proxy){ return proxy->mapToSource(index); } return index; } QModelIndexList UBDocumentController::mapIndexesToSource(const QModelIndexList &indexes) { UBSortFilterProxyModel *proxy = dynamic_cast(mDocumentUI->documentTreeView->model()); if(proxy){ QModelIndexList list; for(int i = 0; i < indexes.size(); i++){ list.push_back(proxy->mapToSource(indexes.at(i))); } return list; } return indexes; } //N/C - NNE - 20140402 : END void UBDocumentController::TreeViewSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected) int nbIndexes = selected.indexes().count(); if (nbIndexes) { QModelIndexList list = mDocumentUI->documentTreeView->selectionModel()->selectedRows(); //if multi-selection if(list.count() > 1) { for (int i=0; i < list.count() ; i++) { QModelIndex newSelectedRow = list.at(i); QModelIndex parent = newSelectedRow.parent(); bool isParentIsSelected = false; while(parent.isValid()) { isParentIsSelected |= (list.indexOf(parent) != -1); if(isParentIsSelected) break; parent = parent.parent(); } if(!isParentIsSelected) TreeViewSelectionChanged(newSelectedRow, QModelIndex()); else mDocumentUI->documentTreeView->selectionModel()->select(newSelectedRow, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); } } else { TreeViewSelectionChanged(selected.indexes().at(0), QModelIndex()); } } } void UBDocumentController::itemSelectionChanged(LastSelectedElementType newSelection) { mSelectionType = newSelection; updateActions(); } void UBDocumentController::setupViews() { if (!mDocumentWidget) { mDocumentWidget = new QWidget(mMainWindow->centralWidget()); mMainWindow->addDocumentsWidget(mDocumentWidget); mDocumentUI = new Ui::documents(); mDocumentUI->setupUi(mDocumentWidget); int thumbWidth = UBSettings::settings()->documentThumbnailWidth->get().toInt(); mDocumentUI->documentZoomSlider->setValue(thumbWidth); mDocumentUI->thumbnailWidget->setThumbnailWidth(thumbWidth); connect(mDocumentUI->documentZoomSlider, SIGNAL(valueChanged(int)), this, SLOT(documentZoomSliderValueChanged(int))); connect(mMainWindow->actionOpen, SIGNAL(triggered()), this, SLOT(openSelectedItem())); connect(mMainWindow->actionNewFolder, SIGNAL(triggered()), this, SLOT(createNewDocumentGroup())); connect(mMainWindow->actionNewDocument, SIGNAL(triggered()), this, SLOT(createNewDocument())); connect(mMainWindow->actionImport, SIGNAL(triggered(bool)), this, SLOT(importFile())); QMenu* addMenu = new QMenu(mDocumentWidget); mAddFolderOfImagesAction = addMenu->addAction(tr("Add Folder of Images")); mAddImagesAction = addMenu->addAction(tr("Add Images")); mAddFileToDocumentAction = addMenu->addAction(tr("Add Pages from File")); connect(mAddFolderOfImagesAction, SIGNAL(triggered(bool)), this, SLOT(addFolderOfImages())); connect(mAddFileToDocumentAction, SIGNAL(triggered(bool)), this, SLOT(addFileToDocument())); connect(mAddImagesAction, SIGNAL(triggered(bool)), this, SLOT(addImages())); foreach (QWidget* menuWidget, mMainWindow->actionDocumentAdd->associatedWidgets()) { QToolButton *tb = qobject_cast(menuWidget); if (tb && !tb->menu()) { tb->setObjectName("ubButtonMenu"); tb->setPopupMode(QToolButton::InstantPopup); QMenu* menu = new QMenu(mDocumentWidget); menu->addAction(mAddFolderOfImagesAction); menu->addAction(mAddImagesAction); menu->addAction(mAddFileToDocumentAction); tb->setMenu(menu); } } QMenu* exportMenu = new QMenu(mDocumentWidget); UBDocumentManager *documentManager = UBDocumentManager::documentManager(); for (int i = 0; i < documentManager->supportedExportAdaptors().length(); i++) { UBExportAdaptor* adaptor = documentManager->supportedExportAdaptors()[i]; QAction *currentExportAction = exportMenu->addAction(adaptor->exportName()); currentExportAction->setData(i); connect(currentExportAction, SIGNAL(triggered (bool)), this, SLOT(exportDocument())); exportMenu->addAction(currentExportAction); adaptor->setAssociatedAction(currentExportAction); } foreach (QWidget* menuWidget, mMainWindow->actionExport->associatedWidgets()) { QToolButton *tb = qobject_cast(menuWidget); if (tb && !tb->menu()) { tb->setObjectName("ubButtonMenu"); tb->setPopupMode(QToolButton::InstantPopup); tb->setMenu(exportMenu); } } #ifdef Q_OS_OSX mMainWindow->actionDelete->setShortcut(QKeySequence(Qt::Key_Backspace)); #else mMainWindow->actionDelete->setShortcut(QKeySequence(Qt::Key_Delete)); #endif connect(mMainWindow->actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedItem())); connect(mMainWindow->actionDuplicate, SIGNAL(triggered()), this, SLOT(duplicateSelectedItem())); connect(mMainWindow->actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedItem())); connect(mMainWindow->actionAddToWorkingDocument, SIGNAL(triggered()), this, SLOT(addToDocument())); UBDocumentTreeModel *model = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; mSortFilterProxyModel = new UBSortFilterProxyModel(); mSortFilterProxyModel->setSourceModel(model); // sort documents according to preferences int sortKind = UBSettings::settings()->documentSortKind->get().toInt(); int sortOrder = UBSettings::settings()->documentSortOrder->get().toInt(); // update icon and button mDocumentUI->sortKind->setCurrentIndex(sortKind); if (sortOrder == UBDocumentController::DESC) mDocumentUI->sortOrder->setChecked(true); mDocumentUI->documentTreeView->setModel(mSortFilterProxyModel); mDocumentUI->documentTreeView->setItemDelegate(new UBDocumentTreeItemDelegate(this)); mDocumentUI->documentTreeView->setDragEnabled(true); mDocumentUI->documentTreeView->setAcceptDrops(true); mDocumentUI->documentTreeView->viewport()->setAcceptDrops(true); mDocumentUI->documentTreeView->setDropIndicatorShown(true); mDocumentUI->documentTreeView->header()->setStretchLastSection(false); mDocumentUI->documentTreeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); mDocumentUI->documentTreeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); mDocumentUI->documentTreeView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); const int splitterLeftSize = UBSettings::settings()->documentSplitterLeftSize->get().toInt(); const int splitterRightSize = UBSettings::settings()->documentSplitterRightSize->get().toInt(); QList splitterSizes; splitterSizes.append(splitterLeftSize); splitterSizes.append(splitterRightSize); mDocumentUI->splitter->setSizes(splitterSizes); //mDocumentUI->documentTreeView->hideColumn(1); mDocumentUI->documentTreeView->hideColumn(2); sortDocuments(sortKind, sortOrder); connect(mDocumentUI->sortKind, SIGNAL(activated(int)), this, SLOT(onSortKindChanged(int))); connect(mDocumentUI->sortOrder, SIGNAL(toggled(bool)), this, SLOT(onSortOrderChanged(bool))); connect(mDocumentUI->splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(onSplitterMoved(int, int))); connect(mDocumentUI->documentTreeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(TreeViewSelectionChanged(QItemSelection,QItemSelection))); connect(UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel, SIGNAL(indexChanged(QModelIndex,QModelIndex)) ,mDocumentUI->documentTreeView, SLOT(onModelIndexChanged(QModelIndex,QModelIndex))); connect(UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel, SIGNAL(currentIndexMoved(QModelIndex,QModelIndex)) ,this, SLOT(currentIndexMoved(QModelIndex,QModelIndex))); connect(mDocumentUI->thumbnailWidget, SIGNAL(sceneDropped(UBDocumentProxy*, int, int)), this, SLOT(moveSceneToIndex ( UBDocumentProxy*, int, int))); connect(mDocumentUI->thumbnailWidget, SIGNAL(resized()), this, SLOT(thumbnailViewResized())); connect(mDocumentUI->thumbnailWidget, SIGNAL(mouseDoubleClick(QGraphicsItem*,int)), this, SLOT(thumbnailPageDoubleClicked(QGraphicsItem*,int))); connect(mDocumentUI->thumbnailWidget, SIGNAL(mouseClick(QGraphicsItem*, int)), this, SLOT(pageClicked(QGraphicsItem*, int))); mDocumentUI->thumbnailWidget->setBackgroundBrush(UBSettings::documentViewLightColor); #ifdef Q_WS_MACX mMessageWindow = new UBMessageWindow(NULL); #else mMessageWindow = new UBMessageWindow(mDocumentUI->thumbnailWidget); #endif mMessageWindow->setCustomPosition(true); mMessageWindow->hide(); } } void UBDocumentController::refreshDateColumns() { if (UBSettings::settings()->documentSortKind->get().toInt() == UBDocumentController::Alphabetical) { if (!UBSettings::settings()->showDateColumnOnAlphabeticalSort->get().toBool()) { mDocumentUI->documentTreeView->hideColumn(1); mDocumentUI->documentTreeView->hideColumn(2); } else { mDocumentUI->documentTreeView->showColumn(1); mDocumentUI->documentTreeView->hideColumn(2); } } } void UBDocumentController::reorderDocuments() { int kindIndex = mDocumentUI->sortKind->currentIndex(); int orderIndex = mDocumentUI->sortOrder->isChecked() ? UBDocumentController::DESC : UBDocumentController::ASC; sortDocuments(kindIndex, orderIndex); } void UBDocumentController::sortDocuments(int kind, int order) { Qt::SortOrder sortOrder = Qt::AscendingOrder; if(order == UBDocumentController::DESC) sortOrder = Qt::DescendingOrder; if(kind == UBDocumentController::CreationDate){ mSortFilterProxyModel->setSortRole(UBDocumentTreeModel::CreationDate); mSortFilterProxyModel->sort(1, sortOrder); mDocumentUI->documentTreeView->showColumn(1); mDocumentUI->documentTreeView->hideColumn(2); }else if(kind == UBDocumentController::UpdateDate){ mSortFilterProxyModel->setSortRole(UBDocumentTreeModel::UpdateDate); mSortFilterProxyModel->sort(2, sortOrder); mDocumentUI->documentTreeView->hideColumn(1); mDocumentUI->documentTreeView->showColumn(2); }else{ mSortFilterProxyModel->setSortRole(Qt::DisplayRole); mSortFilterProxyModel->sort(0, sortOrder); if (!UBSettings::settings()->showDateColumnOnAlphabeticalSort->get().toBool()) { mDocumentUI->documentTreeView->hideColumn(1); mDocumentUI->documentTreeView->hideColumn(2); } } mDocumentUI->documentTreeView->setSelectedAndExpanded(firstSelectedTreeIndex(), true); } void UBDocumentController::onSortOrderChanged(bool order) { int kindIndex = mDocumentUI->sortKind->currentIndex(); int orderIndex = order ? UBDocumentController::DESC : UBDocumentController::ASC; sortDocuments(kindIndex, orderIndex); UBSettings::settings()->documentSortOrder->setInt(orderIndex); } void UBDocumentController::onSortKindChanged(int index) { int orderIndex = mDocumentUI->sortOrder->isChecked() ? UBDocumentController::DESC : UBDocumentController::ASC; sortDocuments(index, orderIndex); UBSettings::settings()->documentSortKind->setInt(index); } void UBDocumentController::onSplitterMoved(int size, int index) { Q_UNUSED(index); UBSettings::settings()->documentSplitterLeftSize->setInt(size); UBSettings::settings()->documentSplitterRightSize->setInt(controlView()->size().width()-size); } QWidget* UBDocumentController::controlView() { return mDocumentWidget; } void UBDocumentController::setupToolbar() { UBApplication::app()->insertSpaceToToolbarBeforeAction(mMainWindow->documentToolBar, mMainWindow->actionBoard); connect(mMainWindow->actionDocumentTools, SIGNAL(triggered()), this, SLOT(toggleDocumentToolsPalette())); } void UBDocumentController::setupPalettes() { mToolsPalette = new UBDocumentToolsPalette(controlView()); mToolsPalette->hide(); bool showToolsPalette = !mToolsPalette->isEmpty(); mMainWindow->actionDocumentTools->setVisible(showToolsPalette); if (showToolsPalette) { mMainWindow->actionDocumentTools->trigger(); } } void UBDocumentController::show() { selectDocument(mBoardController->selectedDocument()); reorderDocuments(); pageSelectionChanged(); if(!mToolsPalette) setupPalettes(); } void UBDocumentController::hide() { // NOOP } void UBDocumentController::openSelectedItem() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QList selectedItems = mDocumentUI->thumbnailWidget->selectedItems(); if (selectedItems.count() > 0) { UBSceneThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); if (thumb) { UBDocumentProxy* proxy = thumb->proxy(); if (proxy && isOKToOpenDocument(proxy)) { mBoardController->setActiveDocumentScene(proxy, thumb->sceneIndex()); UBApplication::applicationController->showBoard(); } } } else { UBDocumentProxy* proxy = selectedDocumentProxy(); if (proxy && isOKToOpenDocument(proxy)) { mBoardController->setActiveDocumentScene(proxy); UBApplication::applicationController->showBoard(); } } QApplication::restoreOverrideCursor(); } void UBDocumentController::duplicateSelectedItem() { if (UBApplication::applicationController->displayMode() != UBApplicationController::Document) return; if (mSelectionType == Page) { QList selectedItems = mDocumentUI->thumbnailWidget->selectedItems(); QList selectedSceneIndexes; foreach (QGraphicsItem *item, selectedItems) { UBSceneThumbnailPixmap *thumb = dynamic_cast(item); if (thumb) { UBDocumentProxy *proxy = thumb->proxy(); if (proxy) { int sceneIndex = thumb->sceneIndex(); selectedSceneIndexes << sceneIndex; } } } if (selectedSceneIndexes.count() > 0) { duplicatePages(selectedSceneIndexes); if (selectedDocument() == selectedDocumentProxy()) { reloadThumbnails(); } selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(selectedDocument()); int selectedThumbnail = selectedSceneIndexes.last() + selectedSceneIndexes.size(); mDocumentUI->thumbnailWidget->selectItemAt(selectedThumbnail); int sceneCount = selectedSceneIndexes.count(); showMessage(tr("duplicated %1 page","duplicated %1 pages",sceneCount).arg(sceneCount), false); if (selectedDocument() == mBoardController->selectedDocument()) { mBoardController->setActiveDocumentScene(selectedThumbnail); mBoardController->reloadThumbnails(); } } } else { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndex selectedIndex = firstSelectedTreeIndex(); Q_ASSERT(!docModel->isConstant(selectedIndex) && !docModel->inTrash(selectedIndex)); showMessage(tr("Duplicating Document %1").arg(""), true); docModel->copyIndexToNewParent(selectedIndex, selectedIndex.parent(), UBDocumentTreeModel::aContentCopy); showMessage(tr("Document %1 copied").arg(""), false); } emit reorderDocumentsRequested(); pageSelectionChanged(); } void UBDocumentController::deleteSelectedItem() { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; if (!UBApplication::mainWindow->yesNoQuestion(tr("Remove Item"), tr("Are you sure you want to remove the selected item(s) ?"))) return; QModelIndexList indexes = selectedTreeIndexes(); if (indexes.size() > 1) { deleteMultipleItems(indexes, docModel); } else if (indexes.size() == 1) { deleteSingleItem(indexes.at(0), docModel); } pageSelectionChanged(); } void UBDocumentController::deleteMultipleItems(QModelIndexList indexes, UBDocumentTreeModel* docModel) { DeletionType deletionForSelection = deletionTypeForSelection(mSelectionType, indexes.at(0), docModel); switch (deletionForSelection) { case DeletePage: { deletePages(mDocumentUI->thumbnailWidget->selectedItems()); break; } case MoveToTrash: { moveIndexesToTrash(indexes, docModel); break; } case CompleteDelete: { for (int i =0; i < indexes.size(); i++) { deleteIndexAndAssociatedData(indexes.at(i)); } emit documentThumbnailsUpdated(this); break; } case EmptyFolder: { for (int i =0; i < indexes.size(); i++) { QModelIndex currentIndex = indexes.at(i); if (currentIndex == docModel->myDocumentsIndex()) { //Emptying "My documents". Keeping Untitled Documents int startInd = 0; while (docModel->rowCount(currentIndex)) { QModelIndex testSubINdecurrentIndex = docModel->index(startInd, 0, currentIndex); if (testSubINdecurrentIndex == docModel->untitledDocumentsIndex()) { emptyFolder(testSubINdecurrentIndex, MoveToTrash); startInd++; continue; } if (!testSubINdecurrentIndex.isValid()) { break; } docModel->moveIndex(testSubINdecurrentIndex, docModel->trashIndex()); } //issue 1629 - NNE - 20131105 //Here, we are sure that the current scene has been deleted createNewDocumentInUntitledFolder(); } else { //issue 1629 - NNE - 20131105 //Check if we will delete the current scene UBDocumentTreeNode *currentNode = docModel->nodeFromIndex(currentIndex); bool deleteCurrentScene = currentNode->findNode(docModel->nodeFromIndex(docModel->currentIndex())); emptyFolder(currentIndex, MoveToTrash); //Empty constant folder if(deleteCurrentScene) createNewDocumentInUntitledFolder(); } } break; } case EmptyTrash: { for (int i=0; i < indexes.size(); i++) { emptyFolder(indexes.at(i), CompleteDelete); // Empty trash folder } break; } } } void UBDocumentController::deleteSingleItem(QModelIndex currentIndex, UBDocumentTreeModel* docModel) { DeletionType deletionForSelection = deletionTypeForSelection(mSelectionType, currentIndex, docModel); switch (deletionForSelection) { case DeletePage: { deletePages(mDocumentUI->thumbnailWidget->selectedItems()); break; } case MoveToTrash: { moveToTrash(currentIndex, docModel); break; } case CompleteDelete: { deleteIndexAndAssociatedData(currentIndex); emit documentThumbnailsUpdated(this); break; } case EmptyFolder: { if (currentIndex == docModel->myDocumentsIndex()) { //Emptying "My documents". Keeping Untitled Documents int startInd = 0; while (docModel->rowCount(currentIndex)) { QModelIndex testSubINdecurrentIndex = docModel->index(startInd, 0, currentIndex); if (testSubINdecurrentIndex == docModel->untitledDocumentsIndex()) { emptyFolder(testSubINdecurrentIndex, MoveToTrash); startInd++; continue; } if (!testSubINdecurrentIndex.isValid()) { break; } docModel->moveIndex(testSubINdecurrentIndex, docModel->trashIndex()); } //issue 1629 - NNE - 20131105 //Here, we are sure that the current scene has been deleted createNewDocumentInUntitledFolder(); } else { //issue 1629 - NNE - 20131105 //Check if we will delete the current scene UBDocumentTreeNode *currentNode = docModel->nodeFromIndex(currentIndex); bool deleteCurrentScene = currentNode->findNode(docModel->nodeFromIndex(docModel->currentIndex())); emptyFolder(currentIndex, MoveToTrash); //Empty constant folder if(deleteCurrentScene) createNewDocumentInUntitledFolder(); } break; } case EmptyTrash: { emptyFolder(currentIndex, CompleteDelete); // Empty trash folder break; } } } //N/C - NNE - 20140410 void UBDocumentController::moveIndexesToTrash(const QModelIndexList &list, UBDocumentTreeModel *docModel) { QModelIndex currentScene = docModel->indexForNode(docModel->currentNode()); //check if the current scene is selected QItemSelectionModel *selectionModel = mDocumentUI->documentTreeView->selectionModel(); bool deleteCurrentScene = selectionModel->isSelected(mSortFilterProxyModel->mapFromSource(currentScene)); //check if the current scene is in the hierarchy if(!deleteCurrentScene){ for(int i = 0; i < list.size(); i++){ deleteCurrentScene = docModel->isDescendantOf(currentScene, list.at(i)); if(deleteCurrentScene){ break; } } } QModelIndex proxyMapCurentScene = mSortFilterProxyModel->mapFromSource(currentScene); if(deleteCurrentScene){ QModelIndex sibling = findPreviousSiblingNotSelected(proxyMapCurentScene, selectionModel); if(sibling.isValid()){ QModelIndex sourceSibling = mSortFilterProxyModel->mapToSource(sibling); UBDocumentProxy *proxy = docModel->proxyForIndex(sourceSibling); if (proxy) { selectDocument(proxy,true); deleteCurrentScene = false; } }else{ sibling = findNextSiblingNotSelected(proxyMapCurentScene, selectionModel); if(sibling.isValid()){ QModelIndex sourceSibling = mSortFilterProxyModel->mapToSource(sibling); UBDocumentProxy *proxy = docModel->proxyForIndex(sourceSibling); if (proxy) { selectDocument(proxy,true); deleteCurrentScene = false; } } } } else { UBDocumentProxy* proxy = docModel->proxyForIndex(currentScene); selectDocument(proxy, true); } docModel->moveIndexes(list, docModel->trashIndex()); if(deleteCurrentScene){ createNewDocumentInUntitledFolder(); } //selectionModel->clearSelection(); } //N/C - NNE - 20140410 : END QModelIndex UBDocumentController::findPreviousSiblingNotSelected(const QModelIndex &index, QItemSelectionModel *selectionModel) { QModelIndex sibling = index.sibling(index.row() - 1, 0); if(sibling.isValid()) { if(!parentIsSelected(sibling, selectionModel) && !selectionModel->isSelected(sibling)) { QModelIndex model = mSortFilterProxyModel->mapToSource(sibling); if(UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->isCatalog(model)) { return findPreviousSiblingNotSelected(sibling, selectionModel); } else { return sibling; } } else { return findPreviousSiblingNotSelected(sibling, selectionModel); } }else{ //if the parent exist keep searching, else stop the search QModelIndex parent = index.model()->parent(index); if(parent.isValid()) { return findPreviousSiblingNotSelected(parent, selectionModel); } else { return QModelIndex(); } } } QModelIndex UBDocumentController::findNextSiblingNotSelected(const QModelIndex &index, QItemSelectionModel *selectionModel) { QModelIndex sibling = index.sibling(index.row() + 1, 0); if(sibling.isValid()) { if(!parentIsSelected(sibling, selectionModel) && !selectionModel->isSelected(sibling)) { QModelIndex model = mSortFilterProxyModel->mapToSource(sibling); if(UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->isCatalog(model)) { return findNextSiblingNotSelected(sibling, selectionModel); } else { return sibling; } } else { return findNextSiblingNotSelected(sibling, selectionModel); } } else { //if the parent exist keep searching, else stop the search QModelIndex parent = index.parent(); if(parent.isValid()) { return findNextSiblingNotSelected(parent, selectionModel); } else { return QModelIndex(); } } } bool UBDocumentController::parentIsSelected(const QModelIndex& child, QItemSelectionModel *selectionModel) { QModelIndex parent = child.parent(); while(parent.isValid()){ if(selectionModel->isSelected(parent)){ return true; } parent = parent.parent(); } return false; } //issue 1629 - NNE - 20131212 void UBDocumentController::moveToTrash(QModelIndex &index, UBDocumentTreeModel* docModel) { QModelIndexList list; list.push_back(index); moveIndexesToTrash(list, docModel); } //issue 1629 - NNE - 20131212 : END void UBDocumentController::deleteDocumentsInFolderOlderThan(const QModelIndex &index, const int days) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndexList list; for (int i = 0; i < docModel->rowCount(index); i++) { list << docModel->index(i, 0, index); } foreach (QModelIndex child, list) { UBDocumentProxy *documentProxy= docModel->proxyForIndex(child); if (documentProxy) { if (documentProxy->lastUpdate().date() < QDateTime::currentDateTime().addDays(-days).date()) { UBPersistenceManager::persistenceManager()->deleteDocument(documentProxy); } } else { if (docModel->isCatalog(child)) { deleteDocumentsInFolderOlderThan(child, days); } } } } void UBDocumentController::deleteEmptyFolders(const QModelIndex &index) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndexList list; for (int i = 0; i < docModel->rowCount(index); i++) { list << docModel->index(i, 0, index); } if (list.length() > 0) { foreach (QModelIndex child, list) { if (docModel->isCatalog(child)) { if (!docModel->containsDocuments(child)) { deleteIndexAndAssociatedData(child); } } } } else { if (docModel->isCatalog(index)) { deleteIndexAndAssociatedData(index); } } } void UBDocumentController::emptyFolder(const QModelIndex &index, DeletionType pDeletionType) { // Issue NC - CFA - 20131029 : ajout d'une popup de confirmation pour la suppression definitive // if(pDeletionType == CompleteDelete && !UBApplication::mainWindow->yesNoQuestion(tr("Empty the trash"),tr("You're about to empty the trash.") +"\n\n" + tr("Are you sure ?"))) // return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; if (!docModel->isCatalog(index)) { return; } while (docModel->rowCount(index)) { QModelIndex subIndex = docModel->index(0, 0, index); switch (pDeletionType) { case MoveToTrash : docModel->moveIndex(subIndex, docModel->trashIndex()); break; case CompleteDelete : deleteIndexAndAssociatedData(subIndex); break; default: break; } } QApplication::restoreOverrideCursor(); // Fin issue NC - CFA - 20131029 } void UBDocumentController::deleteIndexAndAssociatedData(const QModelIndex &pIndex) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; while (docModel->rowCount(pIndex)) { QModelIndex subIndex = docModel->index(0, 0, pIndex); deleteIndexAndAssociatedData(subIndex); } //N/C - NNE - 20140408 UBDocumentProxy *proxyData = nullptr; if(pIndex.column() == 0) { if (docModel->isDocument(pIndex)) { proxyData = docModel->proxyData(pIndex); if (selectedDocument() == proxyData) { setDocument(nullptr); } if (proxyData) { UBPersistenceManager::persistenceManager()->deleteDocument(proxyData); } } } if (proxyData) { // need to recall indexForProxy as rows could have changed when performing a multiple deletion QModelIndex indexForProxy = docModel->indexForProxy(proxyData); if (!docModel->removeRow(indexForProxy.row(), indexForProxy.parent())) { qDebug() << "could not remove row (r:" << indexForProxy.row() << "p:" << indexForProxy.parent() << ")"; } } else { if (docModel->isCatalog(pIndex)) { if (!docModel->removeRow(pIndex.row(), pIndex.parent())) { qDebug() << "could not remove row (r:" << pIndex.row() << "p:" << pIndex.parent() << ")"; } } } } void UBDocumentController::exportDocument() { QAction *currentExportAction = qobject_cast(sender()); QVariant actionData = currentExportAction->data(); UBExportAdaptor* selectedExportAdaptor = UBDocumentManager::documentManager()->supportedExportAdaptors()[actionData.toInt()]; UBDocumentProxy* proxy = firstSelectedTreeProxy(); selectedExportAdaptor->persist(proxy); emit exportDone(); } void UBDocumentController::exportDocumentSet() { } void UBDocumentController::documentZoomSliderValueChanged (int value) { mDocumentUI->thumbnailWidget->setThumbnailWidth(value); UBSettings::settings()->documentThumbnailWidth->set(value); } void UBDocumentController::importFile() { UBDocumentManager *docManager = UBDocumentManager::documentManager(); QString defaultPath = UBSettings::settings()->lastImportFilePath->get().toString(); if(defaultPath.isDetached()) defaultPath = UBSettings::settings()->userDocumentDirectory(); QString filePath = QFileDialog::getOpenFileName(mParentWidget, tr("Open Supported File"), defaultPath, docManager->importFileFilter()); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::processEvents(); QFileInfo fileInfo(filePath); if (fileInfo.suffix().toLower() == "ubx") { UBPersistenceManager::persistenceManager()->createDocumentProxiesStructure(docManager->importUbx(filePath, UBSettings::userDocumentDirectory()), true); emit documentThumbnailsUpdated(this); // some documents might have been overwritten while not having the same page count } else { UBSettings::settings()->lastImportFilePath->set(QVariant(fileInfo.absolutePath())); if (filePath.length() > 0) { UBDocumentProxy* createdDocument = 0; QApplication::processEvents(); QFile selectedFile(filePath); UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndex selectedIndex = firstSelectedTreeIndex(); QString groupName = ""; if (selectedIndex.isValid()) { groupName = docModel->isCatalog(selectedIndex) ? docModel->virtualPathForIndex(selectedIndex) : docModel->virtualDirForIndex(selectedIndex); } showMessage(tr("Importing file %1...").arg(fileInfo.baseName()), true); createdDocument = docManager->importFile(selectedFile, groupName); if (createdDocument) { selectDocument(createdDocument, true, true, true); } else { showMessage(tr("Failed to import file ... ")); } } } QApplication::restoreOverrideCursor(); } void UBDocumentController::addFolderOfImages() { UBDocumentProxy* document = selectedDocumentProxy(); if (document) { QString defaultPath = UBSettings::settings()->lastImportFolderPath->get().toString(); QString imagesDir = QFileDialog::getExistingDirectory(mParentWidget, tr("Import all Images from Folder"), defaultPath); QDir parentImageDir(imagesDir); parentImageDir.cdUp(); UBSettings::settings()->lastImportFolderPath->set(QVariant(parentImageDir.absolutePath())); if (imagesDir.length() > 0) { QDir dir(imagesDir); int importedImageNumber = UBDocumentManager::documentManager()->addImageDirToDocument(dir, document); if (importedImageNumber == 0) { showMessage(tr("Folder does not contain any image files")); UBApplication::applicationController->showDocument(); } else { document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); reloadThumbnails(); if (selectedDocument() == UBApplication::boardController->selectedDocument()) UBApplication::boardController->reloadThumbnails(); pageSelectionChanged(); } } } } void UBDocumentController::addFileToDocument() { UBDocumentProxy* document = selectedDocumentProxy(); if (document) { addFileToDocument(document); } } bool UBDocumentController::addFileToDocument(UBDocumentProxy* document) { QString defaultPath = UBSettings::settings()->lastImportFilePath->get().toString(); QString filePath = QFileDialog::getOpenFileName(mParentWidget, tr("Open Supported File"), defaultPath, UBDocumentManager::documentManager()->importFileFilter(true)); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::processEvents(); QFileInfo fileInfo(filePath); UBSettings::settings()->lastImportFilePath->set(QVariant(fileInfo.absolutePath())); bool success = false; if (filePath.length() > 0) { QApplication::processEvents(); // NOTE: We performed this just a few lines before. Is it really necessary to do it again here?? QFile selectedFile(filePath); showMessage(tr("Importing file %1...").arg(fileInfo.baseName()), true); QStringList fileNames; fileNames << filePath; success = UBDocumentManager::documentManager()->addFilesToDocument(document, fileNames); if (success) { document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); reloadThumbnails(); if (selectedDocument() == UBApplication::boardController->selectedDocument()) UBApplication::boardController->reloadThumbnails(); pageSelectionChanged(); } else { showMessage(tr("Failed to import file ... ")); } } QApplication::restoreOverrideCursor(); return success; } void UBDocumentController::moveSceneToIndex(UBDocumentProxy* proxy, int source, int target) { UBPersistenceManager::persistenceManager()->moveSceneToIndex(proxy, source, target); proxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(proxy); UBDocumentContainer::moveThumbPage(source, target); if (UBApplication::boardController->selectedDocument() == selectedDocument()) { UBApplication::boardController->moveThumbPage(source, target); } mDocumentUI->thumbnailWidget->hightlightItem(target); //mBoardController->setActiveDocumentScene(target); } void UBDocumentController::insertThumbnail(int index, const QPixmap& pix) { QGraphicsPixmapItem *newThumbnail = new UBSceneThumbnailPixmap(pix, selectedDocument(), index); // deleted by the tree widget mDocumentUI->thumbnailWidget->insertThumbnail(index, newThumbnail); } void UBDocumentController::updateThumbnail(int index) { auto pix = UBApplication::boardController->pageAt(index); mDocumentUI->thumbnailWidget->updateThumbnailPixmap(index, *pix); } void UBDocumentController::removeThumbnail(int index) { mDocumentUI->thumbnailWidget->removeThumbnail(index); } void UBDocumentController::moveThumbnail(int from, int to) { mDocumentUI->thumbnailWidget->moveThumbnail(from, to); } void UBDocumentController::thumbnailViewResized() { int maxWidth = qMin(UBSettings::maxThumbnailWidth, mDocumentUI->thumbnailWidget->width()); mDocumentUI->documentZoomSlider->setMaximum(maxWidth); } void UBDocumentController::pageSelectionChanged() { if (mIsClosing) return; UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; bool pageSelected = mDocumentUI->thumbnailWidget->selectedItems().count() > 0; bool docSelected = docModel->isDocument(firstSelectedTreeIndex()); bool folderSelected = docModel->isCatalog(firstSelectedTreeIndex()); if (pageSelected) itemSelectionChanged(Page); else if (docSelected) itemSelectionChanged(Document); else if (folderSelected) itemSelectionChanged(Folder); else itemSelectionChanged(None); } void UBDocumentController::documentSceneChanged(UBDocumentProxy* proxy, int pSceneIndex) { Q_UNUSED(pSceneIndex); QModelIndexList sel = mDocumentUI->documentTreeView->selectionModel()->selectedRows(0); QModelIndex selection; if(sel.count() > 0){ selection = sel.first(); } TreeViewSelectionChanged(selection, QModelIndex()); } void UBDocumentController::thumbnailPageDoubleClicked(QGraphicsItem* item, int index) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; QModelIndex selectedIndex = firstSelectedTreeIndex(); if (selectedIndex.isValid()) { if (docModel->inTrash(selectedIndex)) { return; } } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); UBSceneThumbnailPixmap* thumb = qgraphicsitem_cast (item); if (thumb) { UBDocumentProxy* proxy = thumb->proxy(); if (proxy && isOKToOpenDocument(proxy)) { mBoardController->setActiveDocumentScene(proxy, index); UBApplication::applicationController->showBoard(); } } QApplication::restoreOverrideCursor(); } void UBDocumentController::pageClicked(QGraphicsItem* item, int index) { Q_UNUSED(item); Q_UNUSED(index); pageSelectionChanged(); } void UBDocumentController::addToDocument() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QList selectedItems = mDocumentUI->thumbnailWidget->selectedItems(); if (selectedItems.count() > 0) { int oldActiveSceneIndex = mBoardController->activeSceneIndex(); QList > pageInfoList; foreach (QGraphicsItem* item, selectedItems) { UBSceneThumbnailPixmap* thumb = dynamic_cast (item); if (thumb && thumb->proxy()) { QPair pageInfo(thumb->proxy(), thumb->sceneIndex()); pageInfoList << pageInfo; } } for (int i = 0; i < pageInfoList.length(); i++) { mBoardController->addScene(pageInfoList.at(i).first, pageInfoList.at(i).second, false); } int newActiveSceneIndex = selectedItems.count() == mBoardController->selectedDocument()->pageCount() ? 0 : oldActiveSceneIndex + 1; mDocumentUI->thumbnailWidget->selectItemAt(newActiveSceneIndex, false); selectDocument(mBoardController->selectedDocument()); mBoardController->selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(mBoardController->selectedDocument()); mBoardController->reloadThumbnails(); UBApplication::applicationController->showBoard(); mBoardController->setActiveDocumentScene(newActiveSceneIndex); } QApplication::restoreOverrideCursor(); } void UBDocumentController::renameSelectedItem() { if (mDocumentUI->documentTreeView->currentIndex().isValid()) { mDocumentUI->documentTreeView->edit(mDocumentUI->documentTreeView->currentIndex()); } } bool UBDocumentController::isOKToOpenDocument(UBDocumentProxy* proxy) { //check version QString docVersion = proxy->metaData(UBSettings::documentVersion).toString(); if (docVersion.isEmpty() || docVersion.startsWith("4.1") || docVersion.startsWith("4.2") || docVersion.startsWith("4.3") || docVersion.startsWith("4.4") || docVersion.startsWith("4.5") || docVersion.startsWith("4.6") || docVersion.startsWith("4.8")) // TODO UB 4.7 update if necessary { return true; } else { if (UBApplication::mainWindow->yesNoQuestion(tr("Open Document"), tr("The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed?") .arg(proxy->metaData(UBSettings::documentName).toString()) .arg(docVersion))) { return true; } else { return false; } } } void UBDocumentController::showMessage(const QString& message, bool showSpinningWheel) { if (mMessageWindow) { int margin = UBSettings::boardMargin; QRect newSize = mDocumentUI->thumbnailWidget->geometry(); #ifdef Q_WS_MACX QPoint point(newSize.left() + margin, newSize.bottom() - mMessageWindow->height() - margin); mMessageWindow->move(mDocumentUI->thumbnailWidget->mapToGlobal(point)); #else mMessageWindow->move(margin, newSize.height() - mMessageWindow->height() - margin); #endif mMessageWindow->showMessage(message, showSpinningWheel); } } void UBDocumentController::hideMessage() { if (mMessageWindow) mMessageWindow->hideMessage(); } void UBDocumentController::addImages() { UBDocumentProxy* document = selectedDocumentProxy(); if (document) { QString defaultPath = UBSettings::settings()->lastImportFolderPath->get().toString(); QString extensions; foreach (QString ext, UBSettings::settings()->imageFileExtensions) { extensions += " *."; extensions += ext; } QStringList images = QFileDialog::getOpenFileNames(mParentWidget, tr("Add all Images to Document"), defaultPath, tr("All Images (%1)").arg(extensions)); if (images.length() > 0) { QFileInfo firstImage(images.at(0)); UBSettings::settings()->lastImportFolderPath->set(QVariant(firstImage.absoluteDir().absolutePath())); int importedImageNumber = UBDocumentManager::documentManager()->addFilesToDocument(document, images); if (importedImageNumber == 0) { UBApplication::showMessage(tr("Selection does not contain any image files!")); UBApplication::applicationController->showDocument(); } else { document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); reloadThumbnails(); if (selectedDocument() == UBApplication::boardController->selectedDocument()) UBApplication::boardController->reloadThumbnails(); pageSelectionChanged(); } } } } void UBDocumentController::toggleDocumentToolsPalette() { if (!mToolsPalette->isVisible() && !mToolsPalettePositionned) { mToolsPalette->adjustSizeAndPosition(); int left = controlView()->width() - 20 - mToolsPalette->width(); int top = (controlView()->height() - mToolsPalette->height()) / 2; mToolsPalette->setCustomPosition(true); mToolsPalette->move(left, top); mToolsPalettePositionned = true; } bool visible = mToolsPalette->isVisible(); mToolsPalette->setVisible(!visible); } void UBDocumentController::cut() { // TODO - implemented me } void UBDocumentController::copy() { // TODO - implemented me } void UBDocumentController::paste() { // TODO - implemented me } void UBDocumentController::focusChanged(QWidget *old, QWidget *current) { UBDocumentTreeModel *treeModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; if (current == mDocumentUI->thumbnailWidget) { if (mDocumentUI->thumbnailWidget->selectedItems().count() > 0) mSelectionType = Page; else mSelectionType = None; } else if (current == mDocumentUI->documentTreeView) { if (treeModel->isDocument(firstSelectedTreeIndex())) mSelectionType = Document; else if (treeModel->isCatalog(firstSelectedTreeIndex())) mSelectionType = Folder; else mSelectionType = None; } else if (current == mDocumentUI->documentZoomSlider) { if (mDocumentUI->thumbnailWidget->selectedItems().count() > 0) mSelectionType = Page; else mSelectionType = None; } } void UBDocumentController::updateActions() { if (mIsClosing) return; UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; //N/C - NNE - 20140408 QModelIndexList list = mDocumentUI->documentTreeView->selectionModel()->selectedRows(0); //if in multi selection, juste activate the actionDelete if(list.count() > 1){ mMainWindow->actionNewDocument->setEnabled(false); mMainWindow->actionNewFolder->setEnabled(false); mMainWindow->actionExport->setEnabled(false); mMainWindow->actionDuplicate->setEnabled(false); mMainWindow->actionOpen->setEnabled(false); mMainWindow->actionRename->setEnabled(false); mMainWindow->actionAddToWorkingDocument->setEnabled(false); mMainWindow->actionDocumentAdd->setEnabled(false); mMainWindow->actionImport->setEnabled(false); mMainWindow->actionDelete->setEnabled(true); return; } //N/C - NNE - 20140408 : END #ifdef Q_OS_OSX mMainWindow->actionDelete->setShortcut(QKeySequence(Qt::Key_Backspace)); #else mMainWindow->actionDelete->setShortcut(QKeySequence(Qt::Key_Delete)); #endif QModelIndex selectedIndex = firstSelectedTreeIndex(); UBDocumentProxy *selectedProxy = docModel->proxyData(selectedIndex); int pageCount = -1; if (selectedProxy) { pageCount = selectedProxy->pageCount(); } bool pageSelected = false; bool groupSelected = false; bool docSelected = false; if (mSelectionType == Page) { pageSelected = true; } else { if (docModel->isDocument(firstSelectedTreeIndex())) { docSelected = true; } else if (docModel->isCatalog(firstSelectedTreeIndex())) { groupSelected = true; } } bool trashSelected = docModel->inTrash(selectedIndex) || selectedIndex == docModel->trashIndex() ? true : false; mMainWindow->actionNewDocument->setEnabled(docModel->newNodeAllowed(selectedIndex)); mMainWindow->actionNewFolder->setEnabled(docModel->newNodeAllowed(selectedIndex)); mMainWindow->actionExport->setEnabled((docSelected || pageSelected || groupSelected) && !trashSelected); updateExportSubActions(selectedIndex); bool firstSceneSelected = false; bool everyPageSelected = false; if (docSelected) { mMainWindow->actionDuplicate->setEnabled(!trashSelected); } else if (pageSelected) { QList selection = mDocumentUI->thumbnailWidget->selectedItems(); if(pageCount == 1) { mMainWindow->actionDuplicate->setEnabled(!trashSelected && pageCanBeDuplicated(UBDocumentContainer::pageFromSceneIndex(0))); } else { for (int i = 0; i < selection.count() && !firstSceneSelected; i += 1) { if (qgraphicsitem_cast(selection.at(i))->sceneIndex() == 0) { mMainWindow->actionDuplicate->setEnabled(!trashSelected && pageCanBeDuplicated(UBDocumentContainer::pageFromSceneIndex(0))); firstSceneSelected = true; break; } } if (!firstSceneSelected) { mMainWindow->actionDuplicate->setEnabled(!trashSelected); } } } else { mMainWindow->actionDuplicate->setEnabled(false); } mMainWindow->actionOpen->setEnabled((docSelected || pageSelected) && !trashSelected); mMainWindow->actionRename->setEnabled(docModel->isOkToRename(selectedIndex)); mMainWindow->actionAddToWorkingDocument->setEnabled(pageSelected && !(selectedProxy == mBoardController->selectedDocument()) && !trashSelected); DeletionType deletionForSelection = deletionTypeForSelection(mSelectionType, selectedIndex, docModel); mMainWindow->actionDelete->setEnabled(deletionForSelection != NoDeletion); switch (static_cast(deletionForSelection)) { case MoveToTrash : if (mSelectionType == Folder) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-folder.png")); mMainWindow->actionDelete->setText(tr("Delete")); } else if (mSelectionType == Document) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document.png")); mMainWindow->actionDelete->setText(tr("Trash")); } else if (mSelectionType == Page) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); mMainWindow->actionDelete->setText(tr("Trash")); } else {//can happen ? mMainWindow->actionDelete->setIcon(QIcon(":/images/trash.png")); mMainWindow->actionDelete->setText(tr("Trash")); } break; case DeletePage : mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); mMainWindow->actionDelete->setText(tr("Trash")); break; case CompleteDelete : if (mSelectionType == Folder) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-folder.png")); mMainWindow->actionDelete->setText(tr("Delete")); } else { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-document.png")); mMainWindow->actionDelete->setText(tr("Delete")); } break; case EmptyFolder : if (firstSelectedTreeIndex() == docModel->myDocumentsIndex()) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-my-documents.png")); mMainWindow->actionDelete->setText(tr("Empty")); } else { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-folder.png")); mMainWindow->actionDelete->setText(tr("Delete")); } break; case EmptyTrash : mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-empty.png")); mMainWindow->actionDelete->setText(tr("Empty")); break; case NoDeletion : default: if (mSelectionType == Folder) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-folder.png")); mMainWindow->actionDelete->setText(tr("Delete")); } else if (mSelectionType == Document) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-document.png")); mMainWindow->actionDelete->setText(tr("Delete")); } else if (mSelectionType == Page) { mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); mMainWindow->actionDelete->setText(tr("Delete")); } break; } mMainWindow->actionDocumentAdd->setEnabled((docSelected || pageSelected) && !trashSelected); mMainWindow->actionImport->setEnabled(!trashSelected); } void UBDocumentController::updateExportSubActions(const QModelIndex &selectedIndex) { UBDocumentManager *documentManager = UBDocumentManager::documentManager(); for (int i = 0; i < documentManager->supportedExportAdaptors().length(); i++) { UBExportAdaptor* adaptor = documentManager->supportedExportAdaptors()[i]; if (adaptor->associatedAction()) { adaptor->associatedAction()->setEnabled(adaptor->associatedActionactionAvailableFor(selectedIndex)); } } } void UBDocumentController::currentIndexMoved(const QModelIndex &newIndex, const QModelIndex &PreviousIndex) { Q_UNUSED(newIndex); Q_UNUSED(PreviousIndex); UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; UBDocumentProxy *newProxy = docModel->proxyData(newIndex); if (newProxy) { UBDocumentProxy *cp = new UBDocumentProxy(*newProxy); // we cannot use newProxy because it will be destroyed later pureSetDocument(cp); mBoardController->pureSetDocument(cp); mBoardController->pureSetDocument(newProxy); } mCurrentIndexMoved = true; } void UBDocumentController::deletePages(QList itemsToDelete) { if (itemsToDelete.count() > 0) { QList sceneIndexes; UBDocumentProxy* proxy = 0; foreach (QGraphicsItem* item, itemsToDelete) { UBSceneThumbnailPixmap* thumb = dynamic_cast (item); if (thumb) { proxy = thumb->proxy(); if (proxy) { sceneIndexes.append(thumb->sceneIndex()); } } } UBDocumentContainer::deletePages(sceneIndexes); if (mBoardController->selectedDocument() == selectedDocument()) { std::sort(sceneIndexes.begin(), sceneIndexes.end(), std::greater<>()); for (auto index : sceneIndexes) mBoardController->deleteThumbPage(index); } proxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(proxy); int minIndex = proxy->pageCount() - 1; foreach (int i, sceneIndexes) minIndex = qMin(i, minIndex); if (mBoardController->activeSceneIndex() > minIndex) { mBoardController->setActiveSceneIndex(minIndex); } mDocumentUI->thumbnailWidget->selectItemAt(minIndex); mBoardController->setActiveDocumentScene(minIndex); } } int UBDocumentController::getSelectedItemIndex() { QList selectedItems = mDocumentUI->thumbnailWidget->selectedItems(); if (selectedItems.count() > 0) { UBSceneThumbnailPixmap* thumb = dynamic_cast (selectedItems.last()); return thumb->sceneIndex(); } else return -1; } bool UBDocumentController::pageCanBeMovedUp(int page) { return page >= 1; } bool UBDocumentController::pageCanBeMovedDown(int page) { return page < selectedDocument()->pageCount() - 1; } bool UBDocumentController::pageCanBeDuplicated(int page) { return page != 0; } bool UBDocumentController::pageCanBeDeleted(int page) { return page != 0; } void UBDocumentController::setDocument(UBDocumentProxy *document, bool forceReload) { UBDocumentContainer::setDocument(document, forceReload); } QModelIndex UBDocumentController::firstSelectedTreeIndex() { return selectedTreeIndexes().count() ? selectedTreeIndexes().first() : QModelIndex(); } UBDocumentController::DeletionType UBDocumentController::deletionTypeForSelection(LastSelectedElementType pTypeSelection , const QModelIndex &selectedIndex , UBDocumentTreeModel *docModel) const { if (pTypeSelection == Page) { if (everySceneSelected()) { return NoDeletion; } if (!firstAndOnlySceneSelected()) { return DeletePage; } } else if (docModel->isConstant(selectedIndex)) { if (selectedIndex == docModel->trashIndex()) { if (docModel->rowCount(selectedIndex) > 0) return EmptyTrash; else return NoDeletion; } if (selectedIndex.isValid()) return EmptyFolder; else return NoDeletion; } else if (pTypeSelection != None) { if (docModel->inTrash(selectedIndex)) { return CompleteDelete; } else { return MoveToTrash; } } return NoDeletion; } bool UBDocumentController::everySceneSelected() const { QList selection = mDocumentUI->thumbnailWidget->selectedItems(); if (selection.count() > 0) { UBSceneThumbnailPixmap* p = dynamic_cast(selection.at(0)); if (p) { return (selection.count() == p->proxy()->pageCount()); } } return false; } bool UBDocumentController::firstAndOnlySceneSelected() const { QList selection = mDocumentUI->thumbnailWidget->selectedItems(); for(int i = 0; i < selection.count(); i += 1) { UBSceneThumbnailPixmap* p = dynamic_cast(selection.at(i)); if (p) { int pageCount = p->proxy()->pageCount(); if (pageCount > 1) //not the only scene { return false; } else { if (p->sceneIndex() == 0) { return true; //the first and only scene } } } } return false; } void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer* source) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; UBDocumentProxy *currentDocumentProxy = selectedDocument(); QModelIndex current = docModel->indexForProxy(currentDocumentProxy); if (!current.isValid()) { mDocumentUI->thumbnailWidget->setGraphicsItems(QList() , QList() , QStringList() , UBApplication::mimeTypeUniboardPage); QApplication::restoreOverrideCursor(); return; } if (currentDocumentProxy) { UBThumbnailAdaptor::load(currentDocumentProxy, documentThumbs()); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QList items; QList itemsPath; QGraphicsPixmapItem *selection = 0; QStringList labels; if (currentDocumentProxy) { for (int i = 0; i < currentDocumentProxy->pageCount(); i++) { UBApplication::showMessage(tr("Refreshing Document Thumbnails View (%1/%2)").arg(i+1).arg(source->selectedDocument()->pageCount())); auto pix = documentThumbs().at(i); QGraphicsPixmapItem *pixmapItem = new UBSceneThumbnailPixmap(*pix, currentDocumentProxy, i); // deleted by the tree widget if (currentDocumentProxy == mBoardController->selectedDocument() && mBoardController->activeSceneIndex() == i) { selection = pixmapItem; } items << pixmapItem; int pageIndex = pageFromSceneIndex(i); if(pageIndex) labels << tr("Page %1").arg(pageIndex); else labels << tr("Title page"); itemsPath.append(QUrl::fromLocalFile(currentDocumentProxy->persistencePath() + QString("/pages/%1").arg(UBDocumentContainer::pageFromSceneIndex(i)))); } } mDocumentUI->thumbnailWidget->setGraphicsItems(items, itemsPath, labels, UBApplication::mimeTypeUniboardPage); if (docModel->inTrash(current)) { mDocumentUI->thumbnailWidget->setDragEnabled(false); } else { mDocumentUI->thumbnailWidget->setDragEnabled(true); } mDocumentUI->thumbnailWidget->ensureVisible(0, 0, 10, 10); if (selection) { UBSceneThumbnailPixmap *currentSceneThumbnailPixmap = dynamic_cast(selection); if (currentSceneThumbnailPixmap) mDocumentUI->thumbnailWidget->hightlightItem(currentSceneThumbnailPixmap->sceneIndex()); } QApplication::restoreOverrideCursor(); } void UBDocumentController::createNewDocumentInUntitledFolder() { UBPersistenceManager *pManager = UBPersistenceManager::persistenceManager(); UBDocumentTreeModel *docModel = pManager->mDocumentTreeStructureModel; QString groupName = docModel->virtualPathForIndex(docModel->untitledDocumentsIndex()); UBDocumentProxy *document = pManager->createDocument(groupName); selectDocument(document); if (document) pManager->mDocumentTreeStructureModel->markDocumentAsNew(document); pageSelectionChanged(); } void UBDocumentController::collapseAll() { //disable the animations because the view port will be in a invalid state mDocumentUI->documentTreeView->setAnimated(false); mDocumentUI->documentTreeView->collapseAll(); QPersistentModelIndex untiltedDocumentIndex = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->untitledDocumentsIndex(); QPersistentModelIndex myDocumentIndex = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->myDocumentsIndex(); UBSortFilterProxyModel *proxy = dynamic_cast(mDocumentUI->documentTreeView->model()); if(proxy){ mDocumentUI->documentTreeView->setExpanded(proxy->mapFromSource(myDocumentIndex), true); mDocumentUI->documentTreeView->setExpanded(proxy->mapFromSource(untiltedDocumentIndex), true); }else{ mDocumentUI->documentTreeView->setExpanded(myDocumentIndex, true); mDocumentUI->documentTreeView->setExpanded(untiltedDocumentIndex, true); } mDocumentUI->documentTreeView->setAnimated(true); } //N/C - NNE - 20140513 void UBDocumentController::expandAll() { //disable the animations because the view port will be in a invalid state mDocumentUI->documentTreeView->setAnimated(false); mDocumentUI->documentTreeView->expandAll(); mDocumentUI->documentTreeView->setAnimated(true); } void UBDocumentController::clearThumbnailsSelection() { mDocumentUI->thumbnailWidget->clearSelection(); pageSelectionChanged(); }