diff --git a/README.md b/README.md index cd8563ef9..7916d8dfb 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,22 @@ ![GitHub Repo forks](https://img.shields.io/github/forks/OpenBoard-org/openboard) # OpenBoard [![latest release](https://img.shields.io/github/v/release/OpenBoard-org/openboard.svg)]() -[![Commits since last release](https://img.shields.io/github/commits-since/OpenBoard-org/openboard/v1.7.2/dev)]() +[![Commits since last release](https://img.shields.io/github/commits-since/OpenBoard-org/openboard/v1.7.3/dev)]() [![Github Repo Contributors](https://img.shields.io/github/contributors/OpenBoard-org/openboard.svg)]() -[![downloads v1.7.2](https://img.shields.io/github/downloads/OpenBoard-org/openboard/v1.7.2/total)]() +[![downloads v1.7.3](https://img.shields.io/github/downloads/OpenBoard-org/openboard/v1.7.3/total)]() [![Github All Releases](https://img.shields.io/github/downloads/OpenBoard-org/OpenBoard/total.svg)]() OpenBoard is an open source cross-platform interactive white board application designed primarily for use in schools. It was originally forked from Open-Sankoré, which was itself based on Uniboard. ### Installing -1.7.2 installers are available for Windows, macOS and Debian on the [Download's page](https://github.com/OpenBoard-org/OpenBoard/wiki/Downloads). +1.7.3 installers are available for Windows, macOS and Debian on the [Download's page](https://github.com/OpenBoard-org/OpenBoard/wiki/Downloads). ### Supported platforms | Version | officially maintained platforms | branch | |------------|--------------------------------------------------------|----| -| 1.7.2 (latest stable) | Windows 10+, macOS 12+ (for both `x64_64` and `arm64`), Debian 12 | `master` | -| 1.7.3 (active development) | Windows 10+, macOS 12+ (for both `x64_64` and `arm64`), Debian 12 | `dev` | +| 1.7.3 (latest stable) | Windows 10+, macOS 12+ (for both `x64_64` and `arm64`), Debian 12 | `master` | +| 1.7.4 (active development) | Windows 10+, macOS 12+ (for both `x64_64` and `arm64`), Debian 12 | `dev` | ### Communnity-driven packages On Linux, Debian is the only officially maintained platform. For other platforms, you can thank the awesome community of OpenBoard that provides community-driven packages on a number of other distributions. Check on [this page](https://github.com/OpenBoard-org/OpenBoard/wiki/Downloads) to see if you find what you're looking for. If you actually want to provide support and to be referenced on this page, please open an issue with the relevant information, and we'll be glad to add your contribution. diff --git a/src/adaptors/UBExportDocumentSetAdaptor.cpp b/src/adaptors/UBExportDocumentSetAdaptor.cpp index 752b5ea38..847e84728 100644 --- a/src/adaptors/UBExportDocumentSetAdaptor.cpp +++ b/src/adaptors/UBExportDocumentSetAdaptor.cpp @@ -38,7 +38,6 @@ #include "globals/UBGlobals.h" #include "core/UBPersistenceManager.h" -#include "core/UBForeignObjectsHandler.h" #ifdef Q_OS_OSX #include @@ -164,16 +163,9 @@ bool UBExportDocumentSetAdaptor::addDocumentToZip(const QModelIndex &pIndex, UBD } std::shared_ptrpDocumentProxy = model->proxyForIndex(parentIndex); - if (pDocumentProxy) { - -// Q_ASSERT(QFileInfo(pDocumentProxy->persistencePath()).exists()); -// UBForeighnObjectsHandler cleaner; -// cleaner.cure(pDocumentProxy->persistencePath()); - - //UniboardSankoreTransition document; + if (pDocumentProxy) + { QString documentPath(pDocumentProxy->persistencePath()); - //document.checkDocumentDirectory(documentPath); - QDir documentDir = QDir(pDocumentProxy->persistencePath()); QuaZipFile zipFile(&zip); UBFileSystemUtils::compressDirInZip(documentDir, QFileInfo(documentPath).fileName() + "/", &zipFile, false); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4ac038907..6bbca8173 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -13,8 +13,6 @@ target_sources(${PROJECT_NAME} PRIVATE UBDownloadManager.h UBDownloadThread.cpp UBDownloadThread.h - UBForeignObjectsHandler.cpp - UBForeignObjectsHandler.h UBIdleTimer.cpp UBIdleTimer.h UBMimeData.cpp diff --git a/src/core/UBForeignObjectsHandler.cpp b/src/core/UBForeignObjectsHandler.cpp deleted file mode 100644 index 1ad3ee982..000000000 --- a/src/core/UBForeignObjectsHandler.cpp +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright (C) 2015-2022 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 "UBForeignObjectsHandler.h" - -#include -#include -#include "UBSettings.h" - -const QString tVideo = "video"; -const QString tAudio = "audio"; -const QString tImage = "image"; -const QString tForeignObject = "foreignObject"; -const QString tTeacherGuide = "teacherGuide"; -const QString tMedia = "media"; -const QString tGroups = "groups"; - -const QString aHref = "xlink:href"; -const QString aType = "ub:type"; -const QString aReqExt = "requiredExtensions"; -const QString aSrc = "ub:src"; -const QString aMediaType = "mediaType"; -const QString aRelativePath = "relativePath"; -const QString aActionMedia = "ub:actionFirstParameter"; - -const QString vText = "text"; -const QString vReqExt = "http://ns.adobe.com/pdf/1.3/"; - -const QString wgtSuff = ".wgt"; -const QString thumbSuff = ".png"; - -const QString scanDirs = "audios,images,videos,teacherGuideObjects,widgets"; -const QStringList trashFilter = QStringList() << "*.swf"; - - -static QString strIdFrom(const QString &filePath) -{ - if ((filePath).isEmpty()) { - return QString(); - } - - static const QRegularExpression rx("\\{.(?!.*\\{).*\\}"); - QRegularExpressionMatch match = rx.match(filePath); - if (!match.hasMatch()) { - return QString(); - } - - return match.captured(); -} - -static bool rm_r(const QString &rmPath) -{ - QFileInfo fi(rmPath); - if (!fi.exists()) { - qDebug() << rmPath << "does not exist"; - return false; - } else if (fi.isFile()) { - if (!QFile::remove(rmPath)) { - qDebug() << "can't remove file" << rmPath; - return false; - } - return true; - } else if (fi.isDir()) { - QFileInfoList fList = QDir(rmPath).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); - foreach (QFileInfo sub, fList) { - rm_r(sub.absoluteFilePath()); - } - if (!QDir().rmdir(rmPath)) { - qDebug() << "can't remove dir" << rmPath; - return false; - } - return true; - } - return false; -} - -static bool cp_rf(const QString &what, const QString &where) -{ - QFileInfo whatFi(what); - QFileInfo whereFi = QFileInfo(where); - - if (!whatFi.exists()) { - qDebug() << what << "does not exist" << Q_FUNC_INFO; - return false; - } else if (whatFi.isFile()) { - QString whereDir = where.section("/", 0, -2, QString::SectionSkipEmpty | QString::SectionIncludeLeadingSep); - QString newFilePath = where; - if (!whereFi.exists()) { - QDir().mkpath(whereDir); - } else if (whereFi.isDir()) { - newFilePath = whereDir + "/" + whatFi.fileName(); - } - if (QFile::exists(newFilePath)) { - QFile::remove(newFilePath); - } - if (!QFile::copy(what, newFilePath)) { - qDebug() << "can't copy" << what << "to" << where << Q_FUNC_INFO; - return false; - } - return true; - } else if (whatFi.isDir()) { - - if (whereFi.isFile() && whereFi.fileName().toLower() == whatFi.fileName().toLower()) { - qDebug() << "can't copy dir" << what << "to file" << where << Q_FUNC_INFO; - return false; - } else if (whereFi.isDir()) { - rm_r(where); - } - - QDir().mkpath(where); - - QFileInfoList fList = QDir(what).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); - foreach (QFileInfo sub, fList) { - if (!cp_rf(sub.absoluteFilePath(), where + "/" + sub.fileName())) - return false; - } - return true; - } - return true; -} - -static QString thumbFileNameFrom(const QString &filePath) -{ - if (filePath.isEmpty()) { - return QString(); - } - - static const QRegularExpression braces("[\\{\\}]"); - QString thumbPath = filePath; - thumbPath.replace(braces, "").replace(wgtSuff, thumbSuff); - - return thumbPath; -} - - -QString svgPageName(int pageIndex) -{ - return QString("page%1.svg").arg(pageIndex, 3, 10, QLatin1Char('0')); -} - -static QDomDocument createDomFromSvg(const QString &svgUrl) -{ - Q_ASSERT(QFile::exists(svgUrl)); - QString mFoldersXmlStorageName = svgUrl; - - if (QFileInfo(mFoldersXmlStorageName).exists()) { - QDomDocument xmlDom; - QFile inFile(mFoldersXmlStorageName); - if (inFile.open(QIODevice::ReadOnly)) { - QString domString(inFile.readAll()); - - int errorLine = 0; int errorColumn = 0; - QString errorStr; - - if (xmlDom.setContent(domString, &errorStr, &errorLine, &errorColumn)) { - return xmlDom; - } else { - qDebug() << "Error reading content of " << mFoldersXmlStorageName << '\n' - << "Error:" << inFile.errorString() - << "Line:" << errorLine - << "Column:" << errorColumn; - } - inFile.close(); - } else { - qDebug() << "Error reading" << mFoldersXmlStorageName << '\n' - << "Error:" << inFile.errorString(); - } - } - - return QDomDocument(); -} - -class Cleaner -{ -public: - void cure(const QUrl &dir) - { - mCurrentDir = dir.toLocalFile(); - cleanTrash(); - - // Gathering information from svg files - QFileInfoList svgInfos = QDir(mCurrentDir).entryInfoList(QStringList() << "*.svg", QDir::NoDotAndDotDot | QDir::Files); - foreach (QFileInfo svgInfo, svgInfos) { - cureIdsFromSvgDom(createDomFromSvg(svgInfo.absoluteFilePath())); - } - - fitIdsFromFileSystem(); - QVector deleteCandidates; - findRedundandElements(deleteCandidates); - - foreach (QString key, deleteCandidates) { - QString delPath = mPresentIdsMap.value(key); - if (delPath.isNull()) { - continue; - } else if (delPath.endsWith(wgtSuff)) { //remove corresponding thumb - QString thumbPath = thumbFileNameFrom(delPath); - - //N/C - NNE - 20140417 - if (QFile::exists(thumbPath)) { - rm_r(thumbPath); - } - } - rm_r(delPath); - // Clear parent dir if empty - QDir dir(delPath); - dir.cdUp(); - if (dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { - dir.rmdir(dir.absolutePath()); - } - } - - qDebug() << "Ok on cure"; - } - -private: - void cleanTrash() - { - QFileInfoList ifs = QDir(mCurrentDir).entryInfoList(trashFilter, QDir::NoDotAndDotDot | QDir::Files); - foreach (QFileInfo ifo, ifs) { - rm_r(ifo.absoluteFilePath()); - } - } - - void cureIdsFromSvgDom(const QDomDocument &dom) - { - Q_ASSERT(!dom.isNull()); - - QDomElement nextElement = dom.documentElement().firstChildElement(); - while (!nextElement.isNull()) { - QString nextTag = nextElement.tagName(); - qDebug() << "Tag name of the next parsed element is" << nextTag; - if (nextTag == tGroups) - { - nextElement = nextElement.firstChildElement("group"); - } - - invokeFromText(nextTag, nextElement); - nextElement = nextElement.nextSiblingElement(); - } - } - - void fitIdsFromFileSystem() - { - QString absPrefix = mCurrentDir + "/"; - QStringList dirsList = scanDirs.split(",", UB::SplitBehavior::SkipEmptyParts); - foreach (QString dirName, dirsList) { - QString absPath = absPrefix + dirName; - if (!QFile::exists(absPath)) { - continue; - } - fitIdsFromDir(absPath); - } - - } - - void fitIdsFromDir(const QString &scanDir) - { - QFileInfoList fileList = QDir(scanDir).entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); - foreach (QFileInfo nInfo, fileList) { - QString uid = strIdFrom(nInfo.fileName()); - if (uid.isNull()) { - continue; - } - mPresentIdsMap.insert(uid, nInfo.absoluteFilePath()); - } - } - - void findRedundandElements(QVector &v) - { - // Taking information from the physical file system - QStringList domIds = mDomIdsMap.keys(); - QStringList presentIds = mPresentIdsMap.keys(); - v.resize(qMax(domIds.count(), presentIds.count())); - QVector::iterator it_diff; - - it_diff=std::set_symmetric_difference(domIds.begin(), domIds.end() - , presentIds.begin(), presentIds.end() - , v.begin()); - v.resize(it_diff - v.begin()); - } - - void invokeFromText(const QString &what, const QDomElement &element) - { - if (what == tVideo - || what == tAudio - || what == tImage) { - mediaToContainer(element); - } else if (what == tForeignObject) { - foreingObjectToContainer(element); - - //N/C - NNE - 20140317 - cleanObjectFolder(element); - - //N/C - NNE - 20140520 - //foreign object may referer resource which are not present in the svg - addResourceIdToSvg(element); - } else if (what == tTeacherGuide) { - teacherGuideToContainer(element); - } - - pullActionFromElement(element); - } - - // N/C - NNE - 20140317 : When export, reduce the size of the ubz file - void cleanObjectFolder(const QDomElement &element) - { - //QDomElement preference = element.firstChildElement("ub:preference"); - - //N/C - NNE - 20141021 - QDomNodeList childrenNode = element.elementsByTagName("ub:preference"); - - QVector objectsIdUsed; - - for(int i = 0; i < childrenNode.size(); i++){ - QDomElement preference = childrenNode.at(i).toElement(); - - if(!preference.isNull()){ - QString value = preference.attribute("value"); - - int findPos = value.indexOf("objects/"); - int endPos; - - //find all objects used - while(findPos != -1){ - endPos = value.indexOf("\"", findPos); - objectsIdUsed << value.mid(findPos, endPos - findPos); - findPos = value.indexOf("objects/", endPos); - } - } - } - //N/C - NNE - 20141021 : END - - - QString path = element.attribute(aSrc); - QString objectsFolderPath = mCurrentDir + "/" + path + "/objects/"; - - QDir dir(objectsFolderPath); - dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot); - - //then check all files in the objects directory - //delete the file not used (not in te objectIdUsed variable) - QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); i++) { - QFileInfo fileInfo = list.at(i); - - if(!objectsIdUsed.contains("objects/"+fileInfo.fileName())){ - QFile(fileInfo.filePath()).remove(); - } - - } - } - // N/C - NNE - 20140317 : END - - //N/C - NNE - 20140520 - void addResourceIdToSvg(const QDomElement& element) - { - QDomElement textContent = element.firstChildElement("itemTextContent"); - - QString value = textContent.text(); - - int findPos = value.indexOf("images/"); - int endPos; - - //find all objects used - while(findPos != -1){ - endPos = value.indexOf("\"", findPos); - - QString path = value.mid(findPos, endPos - findPos); - - QString uuid = path.split("/").at(1).split(".").at(0); - - mDomIdsMap.insert(uuid, path); - - findPos = value.indexOf("images/", endPos); - } - } - //N/C - NNE - 20140520 : END - - void pullActionFromElement(const QDomElement &element) - { - if (!element.hasAttribute(aActionMedia)) { - return; - } - - QString path = element.attribute(aActionMedia); - if (path.isNull()) { - return; - } - - QString uid = strIdFrom(path); - if (uid.isNull()) { - return; - } - - mDomIdsMap.insert(uid, path); - } - - void teacherGuideToContainer(const QDomElement &element) - { - QDomElement nMediaElement = element.firstChildElement(tMedia); - while (!nMediaElement.isNull()) { - - QString path = nMediaElement.attribute(aRelativePath); - if (path.isNull()) { - continue; - } - - QString uid = strIdFrom(path); - if (uid.isNull()) { - return; - } - mDomIdsMap.insert(uid, path); - - nMediaElement = nMediaElement.nextSiblingElement(tMedia); - } - } - - void mediaToContainer(const QDomElement &element) - { - QString path = element.attribute(aHref); - if (path.isNull()) { - return; - } - QString uid = strIdFrom(path); - if (uid.isNull()) { - return; - } - mDomIdsMap.insert(uid, path); - } - - void foreingObjectToContainer(const QDomElement &element) - { - QString type = element.attribute(aType); - if (type == vText) { // We don't have to care of the text object - return; - } - - QString path = element.attribute(aSrc); - if (path.isNull()) { - return; - } - - QString uid = strIdFrom(path); - if (uid.isNull()) { - return; - } - - mDomIdsMap.insert(uid, path); - } - -private: - QString mCurrentDir; - QDomDocument mSvgData; - QMap mDomIdsMap; - QMap mPresentIdsMap; -}; - -class PageCopier -{ -public: - void copyPage (const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex) - { - mFromDir = fromDir.toLocalFile(); - mToDir = toDir.toLocalFile(); - mFromIndex = fromIndex; - mToIndex = toIndex; - - QString svgFrom = mFromDir + "/" + svgPageName(fromIndex); - QString svgTo = toDir.toLocalFile() + "/" + svgPageName(toIndex); - QDomDocument dd = createDomFromSvg(svgFrom); - QFile fl(svgTo); - if (!fl.open(QIODevice::WriteOnly)) { - qDebug() << Q_FUNC_INFO << "can't open" << fl.fileName() << "for writing"; - return; - } - cureIdsFromSvgDom(dd); - - QTextStream str(&fl); - dd.save(str, 0); - fl.close(); - qDebug() << Q_FUNC_INFO; - } - -private: - void cureIdsFromSvgDom(const QDomDocument &dom) - { - Q_ASSERT(!dom.isNull()); - - QDomElement nextElement = dom.documentElement().firstChildElement(); - while (!nextElement.isNull( )) { - qDebug() << "Tag name of the next parsed element is" << nextElement.tagName(); - QString nextTag = nextElement.tagName(); - cureFromText(nextTag, nextElement); - nextElement = nextElement.nextSiblingElement(); - } - } - - void cureFromText(const QString &tagName, QDomElement element) - { - if (tagName == tVideo - || tagName == tAudio - || tagName == tImage) { - QString newRelative = cureNCopy(element.attribute(aHref)); - element.setAttribute(aHref, newRelative); - if (element.hasAttribute(aActionMedia)) { - QString newActionPath = cureNCopy(element.attribute(aActionMedia)); - element.setAttribute(aActionMedia, newActionPath); - } - } else if (tagName == tForeignObject) { - //Pdf object is a special case. Detect if it ends with #reference - QString reqExt = element.attribute(aReqExt); - if (reqExt == vReqExt) { //pdf reference - QString ref = element.attribute(aHref); - int i = ref.indexOf("#page"); - QString dest = ref.replace(i, ref.length()-i, ""); - if (!QFileInfo::exists(dest)) - cureNCopy(dest, false); - if (ref.isEmpty()) { - return; - } - static const QRegularExpression pdfReference("^(.*pdf\\#page\\=).*$"); - ref.replace(pdfReference, QString("\\1%1").arg(mToIndex)); - return; - } - - QString type = element.attribute(aType); - if (type == vText) { // We don't have to care of the text object - if (element.hasAttribute(aActionMedia)) { - QString newRelative = cureNCopy(element.attribute(aActionMedia)); - element.setAttribute(aActionMedia, newRelative); - } - return; - } - QString newRelative = cureNCopy(element.attribute(aSrc)); - element.setAttribute(aSrc, newRelative); - } else if (tagName == tTeacherGuide) { - QDomElement nMediaElement = element.firstChildElement(tMedia); - while (!nMediaElement.isNull()) { - QString newRelative = cureNCopy(nMediaElement.attribute(aRelativePath)); - nMediaElement.setAttribute(aRelativePath, newRelative); - nMediaElement = nMediaElement.nextSiblingElement(tMedia); - } - } - } - - QString cureNCopy(const QString &relativePath, bool createNewUuid=true) - { - QString relative = relativePath; - if (createNewUuid) - { - QUuid newUuid = QUuid::createUuid(); - static const QRegularExpression bracedUuid("\\{.*\\}"); - QString newPath = relative.replace(bracedUuid, newUuid.toString()); - - cp_rf(mFromDir + "/" + relativePath, mToDir + "/" + newPath); - - return newPath; - } - else - { - cp_rf(mFromDir + "/" + relativePath, mToDir + "/" + relativePath); - return relativePath; - } - } - -private: - QString mFromDir; - QString mToDir; - int mFromIndex; - int mToIndex; -}; - -class UBForeighnObjectsHandlerPrivate { - UBForeighnObjectsHandlerPrivate(UBForeighnObjectsHandler *pq) - : q(pq) - { - } - -public: - void cure(const QUrl &dir) - { - Cleaner *cleaner = new Cleaner; - cleaner->cure(dir); - delete cleaner; - cleaner = 0; - } - - void copyPage (const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex) - { - PageCopier *copier = new PageCopier; - copier->copyPage(fromDir, fromIndex, toDir, toIndex); - delete copier; - copier = 0; - } - -private: - UBForeighnObjectsHandler *q; - friend class UBForeighnObjectsHandler; -}; - -UBForeighnObjectsHandler::UBForeighnObjectsHandler() - : d(new UBForeighnObjectsHandlerPrivate(this)) -{ - -} - -UBForeighnObjectsHandler::~UBForeighnObjectsHandler() -{ - delete d; -} - -void UBForeighnObjectsHandler::cure(const QList &dirs) -{ - foreach (QUrl dir, dirs) { - cure(dir); - } -} - -void UBForeighnObjectsHandler::cure(const QUrl &dir) -{ - d->cure(dir); -} - -void UBForeighnObjectsHandler::copyPage(const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex) -{ - d->copyPage(fromDir, fromIndex, toDir, toIndex); -} - diff --git a/src/core/UBForeignObjectsHandler.h b/src/core/UBForeignObjectsHandler.h deleted file mode 100644 index 7d03464b9..000000000 --- a/src/core/UBForeignObjectsHandler.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2015-2022 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 . - */ - -#ifndef UBFOREIGHNOBJECTSHANDLER_H -#define UBFOREIGHNOBJECTSHANDLER_H - -#include -#include -#include - -class UBForeighnObjectsHandlerPrivate; - -class UBForeighnObjectsHandler -{ -public: - UBForeighnObjectsHandler(); - ~UBForeighnObjectsHandler(); - - void cure(const QList &dirs); - void cure(const QUrl &dir); - - void copyPage(const QUrl &fromDir, int fromIndex, - const QUrl &toDir, int toIndex); - -private: - UBForeighnObjectsHandlerPrivate *d; - - friend class UBForeighnObjectsHandlerPrivate; -}; - -#endif // UBFOREIGHNOBJECTSHANDLER_H diff --git a/src/core/UBPersistenceManager.cpp b/src/core/UBPersistenceManager.cpp index 1b309d789..f1585a9ee 100644 --- a/src/core/UBPersistenceManager.cpp +++ b/src/core/UBPersistenceManager.cpp @@ -42,7 +42,6 @@ #include "core/UBApplication.h" #include "core/UBSettings.h" #include "core/UBSetting.h" -#include "core/UBForeignObjectsHandler.h" #include "document/UBDocumentProxy.h" @@ -955,42 +954,6 @@ void UBPersistenceManager::duplicateDocumentScene(std::shared_ptr from, int fromIndex, std::shared_ptr to, int toIndex) -{ - if (from == to && toIndex <= fromIndex) { - qDebug() << "operation is not supported" << Q_FUNC_INFO; - return; - } - - checkIfDocumentRepositoryExists(); - - for (int i = to->pageCount(); i > toIndex; i--) { - renamePage(to, i - 1, i); - mSceneCache.moveScene(to, i - 1, i); - } - - UBForeighnObjectsHandler hl; - hl.copyPage(QUrl::fromLocalFile(from->persistencePath()), fromIndex, - QUrl::fromLocalFile(to->persistencePath()), toIndex); - - to->incPageCount(); - - QString thumbTmp(from->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.thumbnail.jpg", fromIndex)); - QString thumbTo(to->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); - ctrl->TreeViewSelectionChanged(ctrl->firstSelectedTreeIndex(), QModelIndex()); - -// emit documentSceneCreated(to, toIndex + 1); -} - std::shared_ptr UBPersistenceManager::createDocumentSceneAt(std::shared_ptr proxy, int index, bool useUndoRedoStack) { diff --git a/src/core/UBPersistenceManager.h b/src/core/UBPersistenceManager.h index 73d32b80f..06210dc0c 100644 --- a/src/core/UBPersistenceManager.h +++ b/src/core/UBPersistenceManager.h @@ -102,8 +102,6 @@ class UBPersistenceManager : public QObject virtual void duplicateDocumentScene(std::shared_ptr pDocumentProxy, int index); - virtual void copyDocumentScene(std::shared_ptrfrom, int fromIndex, std::shared_ptrto, int toIndex); - virtual void persistDocumentScene(std::shared_ptr pDocumentProxy, std::shared_ptr pScene, const int pSceneIndex, bool isAnAutomaticBackup = false, bool forceImmediateSaving = false); virtual std::shared_ptr createDocumentSceneAt(std::shared_ptr pDocumentProxy, int index, bool useUndoRedoStack = true); diff --git a/src/core/core.pri b/src/core/core.pri index 293e143e6..5732e8bdf 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -15,8 +15,7 @@ HEADERS += src/core/UB.h \ src/core/UBDownloadManager.h \ src/core/UBDownloadThread.h \ src/core/UBTextTools.h \ - src/core/UBPersistenceWorker.h \ - $$PWD/UBForeignObjectsHandler.h + src/core/UBPersistenceWorker.h SOURCES += src/core/main.cpp \ src/core/UBShortcutManager.cpp \ @@ -34,5 +33,4 @@ SOURCES += src/core/main.cpp \ src/core/UBDownloadManager.cpp \ src/core/UBDownloadThread.cpp \ src/core/UBTextTools.cpp \ - src/core/UBPersistenceWorker.cpp \ - $$PWD/UBForeignObjectsHandler.cpp + src/core/UBPersistenceWorker.cpp diff --git a/src/document/UBDocumentController.cpp b/src/document/UBDocumentController.cpp index 7706ef94b..c1fdebbd4 100644 --- a/src/document/UBDocumentController.cpp +++ b/src/document/UBDocumentController.cpp @@ -41,7 +41,6 @@ #include "core/UBSettings.h" #include "core/UBSetting.h" #include "core/UBMimeData.h" -#include "core/UBForeignObjectsHandler.h" #include "adaptors/UBExportPDF.h" #include "adaptors/UBThumbnailAdaptor.h" @@ -524,7 +523,7 @@ QVariant UBDocumentTreeModel::data(const QModelIndex &index, int role) const return QBrush(0xD9DFEB); } - if (mHighLighted.isValid() && index == mHighLighted) { + if (mHighLighted.isValid() && index.row() == mHighLighted.row()) { return QBrush(0x6682B5); } } @@ -691,110 +690,24 @@ QDateTime UBDocumentTreeModel::findCatalogCreationDate(UBDocumentTreeNode *node) QStringList UBDocumentTreeModel::mimeTypes() const { - QStringList types; - types << "text/uri-list" << "image/png" << "image/tiff" << "image/gif" << "image/jpeg"; + static const QStringList types{UBApplication::mimeTypeUniboardDocument}; return types; } QMimeData *UBDocumentTreeModel::mimeData (const QModelIndexList &indexes) const { - UBDocumentTreeMimeData *mimeData = new UBDocumentTreeMimeData(); - QList indexList; - QList urlList; + QModelIndexList indexList; - 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)); - std::shared_ptr 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()) - { - std::shared_ptr fromProxy = sourceItem.documentProxy(); - int fromIndex = sourceItem.sceneIndex(); - int toIndex = targetDocProxy->pageCount(); - - UBPersistenceManager::persistenceManager()->copyDocumentScene(fromProxy, fromIndex, - targetDocProxy, toIndex); - } - - QApplication::restoreOverrideCursor(); - - UBApplication::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) + for (const auto& index : indexes) { - //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); - } + // only add indexes for first column + if (index.isValid() && index.column() == 0) + { + indexList.append(index); } } - Q_UNUSED(action) - Q_UNUSED(row) - Q_UNUSED(column) - Q_UNUSED(parent) - - return true; + return new UBDocumentTreeMimeData(indexList); } bool UBDocumentTreeModel::removeRows(int row, int count, const QModelIndex &parent) @@ -1456,6 +1369,37 @@ bool UBDocumentTreeModel::nodeLessThan(const UBDocumentTreeNode *firstIndex, con return firstIndex->nodeName() < secondIndex->nodeName(); } +void UBDocumentTreeModel::setHighLighted(const QModelIndex& newHighLighted) +{ + QModelIndex from; + QModelIndex to; + + if (mHighLighted.isValid()) + { + from = mHighLighted.siblingAtColumn(0); + to = mHighLighted.siblingAtColumn(1); + }; + + if (newHighLighted.isValid()) + { + if (!from.isValid()) + { + from = newHighLighted.siblingAtColumn(0); + } + + to = newHighLighted.siblingAtColumn(1); + } + + mHighLighted = newHighLighted; + + if (from.row() > to.row()) + { + std::swap(from, to); + } + + emit dataChanged(from, to, {Qt::BackgroundRole}); +} + UBDocumentTreeModel::~UBDocumentTreeModel() { delete mRootNode; @@ -1530,89 +1474,69 @@ void UBDocumentTreeView::mousePressEvent(QMouseEvent *event) void UBDocumentTreeView::dragEnterEvent(QDragEnterEvent *event) { - QTreeView::dragEnterEvent(event); - event->accept(); - event->acceptProposedAction(); + if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage)) + { + // accept copying a page + event->setDropAction(Qt::CopyAction); + event->accept(); + } + else if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardDocument)) + { + // accept moving a document + event->setDropAction(Qt::MoveAction); + event->accept(); + } } 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()); + baseModel()->setHighLighted(QModelIndex()); update(); } void UBDocumentTreeView::dragMoveEvent(QDragMoveEvent *event) { - QModelIndex index; - if (selectedIndexes().count() > 0) + if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage)) { - index = selectedIndexes().first(); - } - #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QPoint eventPos = event->position().toPoint(); + QPoint eventPos = event->position().toPoint(); #else - QPoint eventPos = event->pos(); + QPoint eventPos = event->pos(); #endif - bool acceptIt = isAcceptable(index, indexAt(eventPos)); - - 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()); - } + UBDocumentTreeModel* docModel{baseModel()}; QModelIndex targetIndex = mapIndexToSource(indexAt(eventPos)); - if (!docModel || !docModel->isDocument(targetIndex) || docModel->inTrash(targetIndex)) { - event->ignore(); - event->setDropAction(Qt::IgnoreAction); + if (!targetIndex.isValid() || !docModel || !docModel->isDocument(targetIndex) || docModel->inTrash(targetIndex)) + { + event->ignore(visualRect(targetIndex)); if (docModel) { docModel->setHighLighted(QModelIndex()); } - - acceptIt = false; - } else { + } + else + { + event->setDropAction(Qt::CopyAction); + event->accept(visualRect(targetIndex)); docModel->setHighLighted(targetIndex); - acceptIt = true; } - updateIndexEnvirons(indexAt(eventPos)); } - QTreeView::dragMoveEvent(event); - - event->setAccepted(acceptIt); + else if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardDocument)) + { + event->accept(); + QTreeView::dragMoveEvent(event); + } } 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()); - } + UBDocumentTreeModel* docModel{baseModel()}; #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) QPoint eventPos = event->position().toPoint(); @@ -1620,9 +1544,6 @@ void UBDocumentTreeView::dropEvent(QDropEvent *event) QPoint eventPos = event->pos(); #endif QModelIndex targetIndex = mapIndexToSource(indexAt(eventPos)); - QModelIndexList dropIndex = mapIndexesToSource(selectionModel()->selectedRows(0)); - - bool isUBPage = event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage); //issue 1629 - NNE - 20131212 bool targetIsInTrash = docModel && (docModel->inTrash(targetIndex) || docModel->trashIndex() == targetIndex); @@ -1631,7 +1552,7 @@ void UBDocumentTreeView::dropEvent(QDropEvent *event) if (!targetIsInMyDocuments && !targetIsInTrash) return; - if (isUBPage) + if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardPage)) { std::shared_ptr targetDocProxy = docModel->proxyData(targetIndex); @@ -1712,20 +1633,24 @@ void UBDocumentTreeView::dropEvent(QDropEvent *event) UBApplication::showMessage(tr("%1 pages copied", "", total).arg(total), false); } - else + else if (event->mimeData()->hasFormat(UBApplication::mimeTypeUniboardDocument)) { - if(targetIsInTrash) + const auto* documentMimeData = dynamic_cast(event->mimeData()); + QModelIndexList dropIndex = documentMimeData->indexes(); + + if (targetIsInTrash) { UBApplication::documentController->moveIndexesToTrash(dropIndex, docModel); - }else{ + } + else + { docModel->moveIndexes(dropIndex, targetIndex); } } + UBSortFilterProxyModel *proxy = dynamic_cast(model()); expand(proxy->mapFromSource(targetIndex)); - QTreeView::dropEvent(event); - UBApplication::documentController->pageSelectionChanged(); } @@ -1739,16 +1664,10 @@ void UBDocumentTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int sta QTreeView::rowsAboutToBeRemoved(parent, start, end); } -bool UBDocumentTreeView::isAcceptable(const QModelIndex &dragIndex, const QModelIndex &atIndex) +UBDocumentTreeModel* UBDocumentTreeView::baseModel() const { - QModelIndex dragIndexSource = mapIndexToSource(dragIndex); - QModelIndex atIndexSource = mapIndexToSource(atIndex); - - if (!dragIndexSource.isValid()) { - return false; - } - - return true; + UBSortFilterProxyModel *proxy = dynamic_cast(model()); + return dynamic_cast(proxy ? proxy->sourceModel() : model()); } Qt::DropAction UBDocumentTreeView::acceptableAction(const QModelIndex &dragIndex, const QModelIndex &atIndex) @@ -1756,14 +1675,6 @@ Qt::DropAction UBDocumentTreeView::acceptableAction(const QModelIndex &dragIndex 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) { @@ -1775,23 +1686,6 @@ QModelIndex UBDocumentTreeView::mapIndexToSource(const QModelIndex &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) @@ -2295,6 +2189,7 @@ void UBDocumentController::setupViews() mDocumentUI->documentTreeView->setItemDelegate(new UBDocumentTreeItemDelegate(this)); mDocumentUI->documentTreeView->setDragEnabled(true); mDocumentUI->documentTreeView->setAcceptDrops(true); + mDocumentUI->documentTreeView->setDefaultDropAction(Qt::MoveAction); mDocumentUI->documentTreeView->viewport()->setAcceptDrops(true); mDocumentUI->documentTreeView->setDropIndicatorShown(true); mDocumentUI->documentTreeView->header()->setStretchLastSection(false); @@ -4257,3 +4152,14 @@ void UBDocumentController::clearThumbnailsSelection() mDocumentUI->thumbnailWidget->clearSelection(); pageSelectionChanged(); } + +UBDocumentTreeMimeData::UBDocumentTreeMimeData(const QModelIndexList& pIndexes) + : mIndexes{pIndexes} +{ + setData(UBApplication::mimeTypeUniboardDocument, {}); +} + +QModelIndexList UBDocumentTreeMimeData::indexes() const +{ + return mIndexes; +} diff --git a/src/document/UBDocumentController.h b/src/document/UBDocumentController.h index 2599338f3..a90edcae9 100644 --- a/src/document/UBDocumentController.h +++ b/src/document/UBDocumentController.h @@ -192,14 +192,12 @@ class UBDocumentTreeModel : public QAbstractItemModel { Qt::DropActions supportedDropActions() const {return Qt::MoveAction | Qt::CopyAction;} QStringList mimeTypes() const; QMimeData *mimeData (const QModelIndexList &indexes) const; - bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); bool removeRows(int row, int count, const QModelIndex &parent); bool containsDocuments(const QModelIndex& index); QModelIndex indexForNode(UBDocumentTreeNode *pNode) const; QPersistentModelIndex persistentIndexForNode(UBDocumentTreeNode *pNode); -// bool insertRow(int row, const QModelIndex &parent); QPersistentModelIndex copyIndexToNewParent(const QModelIndex &source, const QModelIndex &newParent, eCopyMode pMode = aReference); @@ -241,7 +239,7 @@ class UBDocumentTreeModel : public QAbstractItemModel { QPersistentModelIndex untitledDocumentsIndex() const {return mMyDocuments;} UBDocumentTreeNode *nodeFromIndex(const QModelIndex &pIndex) const; static bool nodeLessThan(const UBDocumentTreeNode *firstIndex, const UBDocumentTreeNode *secondIndex); - void setHighLighted(const QModelIndex &newHighLighted) {mHighLighted = newHighLighted;} + void setHighLighted(const QModelIndex &newHighLighted); QModelIndex highLighted() {return mHighLighted;} std::shared_ptr findDocumentByFolderName(QString folderName) const; std::shared_ptr findDocumentByFolderName(UBDocumentTreeNode* node, QString folderName) const; @@ -291,12 +289,12 @@ class UBDocumentTreeMimeData : public QMimeData { Q_OBJECT - public: - QList indexes() const {return mIndexes;} - void setIndexes (const QList &pIndexes) {mIndexes = pIndexes;} +public: + UBDocumentTreeMimeData(const QModelIndexList& pIndexes); + QModelIndexList indexes() const; - private: - QList mIndexes; +private: + QModelIndexList mIndexes; }; class UBDocumentTreeView : public QTreeView @@ -308,7 +306,6 @@ class UBDocumentTreeView : public QTreeView //N/C - NNE - 20140404 QModelIndex mapIndexToSource(const QModelIndex &index); - QModelIndexList mapIndexesToSource(const QModelIndexList &indexes); public slots: void setSelectedAndExpanded(const QModelIndex &pIndex, bool pExpand = true, bool pEdit = false); @@ -328,9 +325,8 @@ public slots: void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); private: - bool isAcceptable(const QModelIndex &dragIndex, const QModelIndex &atIndex); + UBDocumentTreeModel* baseModel() const; Qt::DropAction acceptableAction(const QModelIndex &dragIndex, const QModelIndex &atIndex); - void updateIndexEnvirons(const QModelIndex &index); }; class UBValidator : public QValidator diff --git a/src/gui/UBDocumentThumbnailWidget.cpp b/src/gui/UBDocumentThumbnailWidget.cpp index cb2aeabed..f511bb31e 100644 --- a/src/gui/UBDocumentThumbnailWidget.cpp +++ b/src/gui/UBDocumentThumbnailWidget.cpp @@ -92,7 +92,7 @@ void UBDocumentThumbnailWidget::mouseMoveEvent(QMouseEvent *event) drag->setPixmap(sceneItem->pixmap().scaledToWidth(100)); drag->setHotSpot(QPoint(drag->pixmap().width()/2, drag->pixmap().height() / 2)); - drag->exec(Qt::MoveAction); + drag->exec({Qt::MoveAction, Qt::CopyAction}); } UBDocumentThumbnailsView::mouseMoveEvent(event); diff --git a/version.txt b/version.txt index 325b1309c..a2d396059 100644 --- a/version.txt +++ b/version.txt @@ -1,5 +1,5 @@ VERSION_MAJ = 1 VERSION_MIN = 7 -VERSION_PATCH = 2 +VERSION_PATCH = 3 VERSION_TYPE = r # a = alpha, b = beta, rc = release candidate, r = release, other => error -VERSION_BUILD = 241104 # for non-release builds +VERSION_BUILD = 241217 # for non-release builds