diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..309871f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "pandoc-slave"] + path = pandoc-slave + url = https://github.com/PLLUG/pandoc-slave.git diff --git a/appveyor.yml b/appveyor.yml index bf22d63..7190df4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,17 +1,24 @@ version: 0.0.1-{build}-{branch} + +install: + - cmd: cd %APPVEYOR_BUILD_FOLDER%\pllug-presentation-system + - ps: Start-FileDownload 'https://www.qpm.io/download/v0.10.0/windows_386/qpm.exe' pull_requests: do_not_increment_build_number: true environment: QT_INSTALL_ROOT: C:\Qt\5.7\msvc2015_64 build_script: - cmd: >- - %QT_INSTALL_ROOT%\bin\qtenv2.bat "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 cd %APPVEYOR_BUILD_FOLDER%\pllug-presentation-system + git submodule -q update --init + + qpm install + qmake CONFIG+=release nmake diff --git a/pandoc-slave b/pandoc-slave new file mode 160000 index 0000000..691a507 --- /dev/null +++ b/pandoc-slave @@ -0,0 +1 @@ +Subproject commit 691a5075dddae18122d75f57c034fcac8fb53370 diff --git a/pllug-presentation-system/main.cpp b/pllug-presentation-system/main.cpp index 152d43d..e43456a 100644 --- a/pllug-presentation-system/main.cpp +++ b/pllug-presentation-system/main.cpp @@ -1,15 +1,43 @@ #include #include +#include +#include +#include +#include + +#include "domdocumentdivider.h" +#include "htmlimport.h" +#include "presentationelement.h" +#include "presentationelementfactory.h" +#include "presentationmodel.h" +#include "slideproxymodel.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); + QFile htmlFile("D:/Qt_projects/SUMMERCAMP2016/pps2/pllug-presentation-system-2/tests/auto/resources/multiple_slides.html"); + htmlFile.open(QFile::ReadOnly); + QByteArray input = htmlFile.readAll(); + htmlFile.close(); + std::shared_ptr factoryPtr(new PresentationElementFactory()); + HtmlImport importObject(factoryPtr); + QList elements = importObject.import(input); + DomDocumentDivider divider; + std::unique_ptr presentation = divider.import(elements); + + PresentationModel presentationModel; + presentationModel.setPresentation(presentation.release()); + SlideProxyModel slideModel; + slideModel.setSourceModel(&presentationModel); + slideModel.setSlideNumber(0); QQmlApplicationEngine engine; engine.addImportPath(QStringLiteral("qrc:/")); - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + engine.rootContext()->setContextProperty("slideModel", &slideModel); + engine.rootContext()->setContextProperty("presentationModel", &presentationModel); + engine.load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); } diff --git a/pllug-presentation-system/pllug-presentation-system.pro b/pllug-presentation-system/pllug-presentation-system.pro index 48f752b..971a5a5 100644 --- a/pllug-presentation-system/pllug-presentation-system.pro +++ b/pllug-presentation-system/pllug-presentation-system.pro @@ -18,3 +18,6 @@ QML_IMPORT_PATH = include(deployment.pri) include($$PWD/vendor/vendor.pri) +# Pandoc-slave +include($$PWD/../pandoc-slave/pandoc-slave/pandoc-slave/pandoc-slave.pri) + diff --git a/pllug-presentation-system/presentation-data/documentimportstrategy.h b/pllug-presentation-system/presentation-data/documentimportstrategy.h index 5fa1b62..1988a8e 100644 --- a/pllug-presentation-system/presentation-data/documentimportstrategy.h +++ b/pllug-presentation-system/presentation-data/documentimportstrategy.h @@ -5,7 +5,6 @@ #include class Presentation; -class QByteArray; class PresentationElementFactory; class PresentationElement; diff --git a/pllug-presentation-system/presentation-data/domdocumentdivider.cpp b/pllug-presentation-system/presentation-data/domdocumentdivider.cpp index 0e053fd..417557a 100644 --- a/pllug-presentation-system/presentation-data/domdocumentdivider.cpp +++ b/pllug-presentation-system/presentation-data/domdocumentdivider.cpp @@ -115,7 +115,8 @@ std::unique_ptr DomDocumentDivider::divideSlides(const QList DomDocumentDivider::divideSlide(Slide *slide, int elementsNum) const { // TODO: Nothing done here yet, as we don't know how to divide a single slide - QList smallerSlides; - smallerSlides.push_back(slide); - return smallerSlides; + Q_UNUSED(elementsNum); + QList rSmallerSlides; + rSmallerSlides.push_back(slide); + return rSmallerSlides; } diff --git a/pllug-presentation-system/presentation-data/htmlimport.cpp b/pllug-presentation-system/presentation-data/htmlimport.cpp index 9baeb6f..877a6d0 100644 --- a/pllug-presentation-system/presentation-data/htmlimport.cpp +++ b/pllug-presentation-system/presentation-data/htmlimport.cpp @@ -1,10 +1,11 @@ #include "htmlimport.h" #include "abstractpresentationelementfactory.h" -#include "presentationElement.h" +#include "presentationelement.h" #include #include #include + HtmlImport::HtmlImport(std::shared_ptr presentationElementFactory) : mPresentationElementFactory(presentationElementFactory) { diff --git a/pllug-presentation-system/presentation-data/iroleprovider.h b/pllug-presentation-system/presentation-data/iroleprovider.h new file mode 100644 index 0000000..4c4960f --- /dev/null +++ b/pllug-presentation-system/presentation-data/iroleprovider.h @@ -0,0 +1,13 @@ +#ifndef IROLEPROVIDER_H +#define IROLEPROVIDER_H + +#include +#include + +class IRoleProvider +{ +public: + virtual QHash roleNameByValue() const = 0; +}; + +#endif // IROLEPROVIDER_H diff --git a/pllug-presentation-system/presentation-data/presentation-data.pri b/pllug-presentation-system/presentation-data/presentation-data.pri index 1829ab8..8bc7af7 100644 --- a/pllug-presentation-system/presentation-data/presentation-data.pri +++ b/pllug-presentation-system/presentation-data/presentation-data.pri @@ -9,7 +9,10 @@ SOURCES += \ $$PWD/paragraph.cpp \ $$PWD/separator.cpp \ $$PWD/htmlimport.cpp \ - $$PWD/presentationelementfactory.cpp + $$PWD/presentationelementfactory.cpp \ + $$PWD/presentationmodel.cpp \ + $$PWD/slideproxymodel.cpp \ + $$PWD/roleprovider.cpp HEADERS += \ $$PWD/presentation.h \ @@ -24,6 +27,10 @@ HEADERS += \ $$PWD/separator.h \ $$PWD/htmlimport.h \ $$PWD/abstractpresentationelementfactory.h \ - $$PWD/presentationelementfactory.h + $$PWD/presentationelementfactory.h \ + $$PWD/presentationmodel.h \ + $$PWD/slideproxymodel.h \ + $$PWD/iroleprovider.h \ + $$PWD/roleprovider.h INCLUDEPATH += $$PWD diff --git a/pllug-presentation-system/presentation-data/presentation.cpp b/pllug-presentation-system/presentation-data/presentation.cpp index 85d57a5..272497e 100644 --- a/pllug-presentation-system/presentation-data/presentation.cpp +++ b/pllug-presentation-system/presentation-data/presentation.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include /*! * \brief Public constructor. */ @@ -62,6 +62,16 @@ void Presentation::appendSlide(std::unique_ptr slide) Slide *Presentation::slide(int index) const { - return mSlideList[index]; + Slide *rSlide {}; + if(index >= 0 && index < mSlideList.count()) + { + rSlide = mSlideList[index]; + } + else + { + qWarning() << "Warning: Invalid slide index."; + rSlide = new Slide(); + } + return rSlide; } diff --git a/pllug-presentation-system/presentation-data/presentation.h b/pllug-presentation-system/presentation-data/presentation.h index 5dcb89d..d9c8cd3 100644 --- a/pllug-presentation-system/presentation-data/presentation.h +++ b/pllug-presentation-system/presentation-data/presentation.h @@ -2,7 +2,6 @@ #define PRESENTATION_H #include - #include #include "slide.h" @@ -27,6 +26,7 @@ class Presentation void appendSlide(std::unique_ptr slide); Slide *slide(int index) const; + private: QVector mSlideList; }; diff --git a/pllug-presentation-system/presentation-data/presentationelement.cpp b/pllug-presentation-system/presentation-data/presentationelement.cpp index 1508b7b..641a01a 100644 --- a/pllug-presentation-system/presentation-data/presentationelement.cpp +++ b/pllug-presentation-system/presentation-data/presentationelement.cpp @@ -14,6 +14,26 @@ PresentationElement::PresentationElement(const QString &html) { } +int PresentationElement::x() const +{ + return 1; +} + +int PresentationElement::y() const +{ + return 1; +} + +int PresentationElement::width() const +{ + return 1; +} + +int PresentationElement::height() const +{ + return 1; +} + QString PresentationElement::toHtml() const { return mContent; diff --git a/pllug-presentation-system/presentation-data/presentationelement.h b/pllug-presentation-system/presentation-data/presentationelement.h index 2bcb136..55292b9 100644 --- a/pllug-presentation-system/presentation-data/presentationelement.h +++ b/pllug-presentation-system/presentation-data/presentationelement.h @@ -14,6 +14,10 @@ class PresentationElement PresentationElement(const QString &html); virtual ~PresentationElement() = 0; + int x() const; + int y() const; + int width() const; + int height() const; QString toHtml() const; protected: diff --git a/pllug-presentation-system/presentation-data/presentationelementfactory.cpp b/pllug-presentation-system/presentation-data/presentationelementfactory.cpp index 41127ce..da5fb6f 100644 --- a/pllug-presentation-system/presentation-data/presentationelementfactory.cpp +++ b/pllug-presentation-system/presentation-data/presentationelementfactory.cpp @@ -1,38 +1,33 @@ #include "presentationelementfactory.h" - -#include -#include -#include #include "presentationelement.h" #include "header.h" #include "paragraph.h" #include "separator.h" +#include +#include +#include + std::unique_ptr PresentationElementFactory::create(const QString &html) const { QRegularExpression re("(h[1-6]|hr|p)"); QRegularExpressionMatch match = re.match(html); + std::unique_ptr rElement(new Separator("
")); if(match.hasMatch()) { QString tag = match.captured(); if(tag == "p") { - return std::make_unique(html); + rElement.reset(new Paragraph(html)); } - else if(tag[0] == 'h') + else if(tag == "hr") { - if(tag[1] == 'r') - { - return std::make_unique(html); - } - else - { - return std::make_unique
(html); - } + rElement.reset(new Separator(html)); + } + else + { + rElement.reset(new Header(html)); } } - else - { - return nullptr; - } + return rElement; } diff --git a/pllug-presentation-system/presentation-data/presentationmodel.cpp b/pllug-presentation-system/presentation-data/presentationmodel.cpp new file mode 100644 index 0000000..aff8c36 --- /dev/null +++ b/pllug-presentation-system/presentation-data/presentationmodel.cpp @@ -0,0 +1,139 @@ +#include "presentationmodel.h" + +PresentationModel::PresentationModel(QObject *parent) + :QAbstractItemModel(parent) +{ +} + +void PresentationModel::setPresentation(Presentation *presentation) +{ + mPresentation = presentation; +} + +int PresentationModel::rowCount(const QModelIndex &parent) const +{ + int rRowCount = 0; + if (parent.column() > 0) + return rRowCount; + + if(parent.isValid()) + { + Slide *slide = static_cast(parent.internalPointer()); + if(slide) + { + rRowCount = slide->elementsCount(); + } + else + { + rRowCount = mPresentation->slideCount(); + } + } + else + { + rRowCount = mPresentation->slideCount(); + } + + return rRowCount; +} + +int PresentationModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QVariant PresentationModel::data(const QModelIndex &index, int role) const +{ + QVariant rData; + if(index.isValid()) + { + Slide *slide = static_cast(index.internalPointer()); + if(slide) + { + switch(role) + { + // case Roles::X : + // { + // rData = QString::number(element->x()); + // break; + // } + // case Roles::Y : + // { + // rData = QString::number(element->y()); + // break; + // } + // case Roles::Width : + // { + // rData = QString::number(element->width()); + // break; + // } + // case Roles::Height : + // { + // rData = QString::number(element->height()); + // break; + // } + case Roles::Html : + { + rData = slide->toHtml(); + break; + } + } + } + } + return rData; +} + +QVariant PresentationModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + Q_UNUSED(role); + QVariant rHeaderData; + if(orientation == Qt::Horizontal) + { + rHeaderData = tr("Slides"); + } + return rHeaderData; +} + +QModelIndex PresentationModel::index(int row, int column, const QModelIndex &parent) const +{ + QModelIndex rIndex; + if (parent.column() > 0) + return rIndex; + + if(parent.isValid()) + { + Slide *slide = static_cast(parent.internalPointer()); + if(slide) + { + rIndex = createIndex(row, column, slide->element(row)); + } + else + { + rIndex = createIndex(row, column, mPresentation->slide(row)); + } + } + else + { + rIndex = createIndex(row, column, mPresentation->slide(row)); + } + return rIndex; +} + +QModelIndex PresentationModel::parent(const QModelIndex &index) const +{ + // TODO: Implement this method. + Q_UNUSED(index); + return QModelIndex(); +} + +QHash PresentationModel::roleNames() const +{ + QHash rHash; + rHash.insert(Roles::X, "X"); + rHash.insert(Roles::Y, "Y"); + rHash.insert(Roles::Width, "Width"); + rHash.insert(Roles::Height, "Height"); + rHash.insert(Roles::Html, "Html"); + return rHash; +} diff --git a/pllug-presentation-system/presentation-data/presentationmodel.h b/pllug-presentation-system/presentation-data/presentationmodel.h new file mode 100644 index 0000000..fc49e19 --- /dev/null +++ b/pllug-presentation-system/presentation-data/presentationmodel.h @@ -0,0 +1,36 @@ +#ifndef PRESENTATIONMODEL_H +#define PRESENTATIONMODEL_H + +#include +#include +#include +#include "slide.h" +#include "presentation.h" + +class PresentationModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit PresentationModel(QObject *parent = nullptr); + + void setPresentation(Presentation *presentation); + + virtual int rowCount(const QModelIndex &parent) const override; + virtual int columnCount(const QModelIndex &parent) const override; + virtual QVariant data(const QModelIndex &index, int role) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + virtual QModelIndex parent(const QModelIndex &index) const override; + virtual QHash roleNames() const override; + + enum Roles + { + X, Y, Width, Height, Html + }; + +private: + Presentation *mPresentation; +}; + +#endif // PRESENTATIONMODEL_H diff --git a/pllug-presentation-system/presentation-data/roleprovider.cpp b/pllug-presentation-system/presentation-data/roleprovider.cpp new file mode 100644 index 0000000..8bf0475 --- /dev/null +++ b/pllug-presentation-system/presentation-data/roleprovider.cpp @@ -0,0 +1,11 @@ +#include "roleprovider.h" + +QHash RoleProvider::roleNameByValue() const +{ + return mRoleNamesByValue; +} + +void RoleProvider::addRole(int val, const QByteArray &name) +{ + mRoleNamesByValue.insert(val, name); +} diff --git a/pllug-presentation-system/presentation-data/roleprovider.h b/pllug-presentation-system/presentation-data/roleprovider.h new file mode 100644 index 0000000..0b8753b --- /dev/null +++ b/pllug-presentation-system/presentation-data/roleprovider.h @@ -0,0 +1,16 @@ +#ifndef ROLEPROVIDER_H +#define ROLEPROVIDER_H + +#include "iroleprovider.h" + +class RoleProvider : public IRoleProvider +{ +public: + virtual QHash roleNameByValue() const; + void addRole(int val, const QByteArray &name); + +private: + QHash mRoleNamesByValue; +}; + +#endif // ROLEPROVIDER_H diff --git a/pllug-presentation-system/presentation-data/separator.h b/pllug-presentation-system/presentation-data/separator.h index 759216d..7b2de60 100644 --- a/pllug-presentation-system/presentation-data/separator.h +++ b/pllug-presentation-system/presentation-data/separator.h @@ -3,6 +3,7 @@ #include "presentationelement.h" class QString; + class Separator : public PresentationElement { public: diff --git a/pllug-presentation-system/presentation-data/slide.cpp b/pllug-presentation-system/presentation-data/slide.cpp index 77827a5..ae478d0 100644 --- a/pllug-presentation-system/presentation-data/slide.cpp +++ b/pllug-presentation-system/presentation-data/slide.cpp @@ -1,6 +1,8 @@ #include "slide.h" - +#include #include +#include "separator.h" +#include "presentationelementfactory.h" /*! * \brief Public constructor. @@ -23,7 +25,7 @@ Slide::~Slide() */ Slide::Slide(const Slide &other) { - Q_UNUSED(other) + *this = other; } /*! @@ -31,8 +33,12 @@ Slide::Slide(const Slide &other) */ Slide &Slide::operator=(const Slide &other) { - Q_UNUSED(other) - + qDeleteAll(mElementsList); + PresentationElementFactory factory; + for(auto elem: other.mElementsList) + { + mElementsList.append(factory.create(elem->toHtml()).release()); + } return *this; } @@ -57,5 +63,25 @@ void Slide::addElement(std::unique_ptr element) PresentationElement *Slide::element(int index) const { - return mElementsList[index]; + PresentationElement *rElement {}; + if(index >= 0 && index < mElementsList.count()) + { + rElement = mElementsList[index]; + } + else + { + qWarning() << "Warning: Invalid slide element index."; + rElement = new Separator("
"); + } + return rElement; +} + +QString Slide::toHtml() const +{ + QString rHtml; + for(int i = 0; i < elementsCount(); ++i) + { + rHtml.append(element(i)->toHtml()); + } + return rHtml; } diff --git a/pllug-presentation-system/presentation-data/slide.h b/pllug-presentation-system/presentation-data/slide.h index c4a42a1..df27f42 100644 --- a/pllug-presentation-system/presentation-data/slide.h +++ b/pllug-presentation-system/presentation-data/slide.h @@ -2,7 +2,6 @@ #define SLIDE_H #include - #include #include "presentationelement.h" @@ -27,6 +26,8 @@ class Slide PresentationElement* element(int index) const; + QString toHtml() const; + private: QVector mElementsList; }; diff --git a/pllug-presentation-system/presentation-data/slideproxymodel.cpp b/pllug-presentation-system/presentation-data/slideproxymodel.cpp new file mode 100644 index 0000000..8105f60 --- /dev/null +++ b/pllug-presentation-system/presentation-data/slideproxymodel.cpp @@ -0,0 +1,180 @@ +#include "slideproxymodel.h" +#include "presentationelement.h" +#include "presentationmodel.h" + +SlideProxyModel::SlideProxyModel(QObject *parent): + QAbstractProxyModel(parent), + mSlideNumber(0) +{ +} + +int SlideProxyModel::slideNumber() const +{ + return mSlideNumber; +} + +void SlideProxyModel::setSlideNumber(int slideNumber) +{ + if(slideNumber >= 0 && slideNumber < sourceModel()->rowCount()) + { + beginResetModel(); + mSlideNumber = slideNumber; + endResetModel(); + } +} + +int SlideProxyModel::slideCount() const +{ + return sourceModel()->rowCount(); +} + +QModelIndex SlideProxyModel::index(int row, int column, const QModelIndex &parent) const +{ + QModelIndex rIndex; + if (parent.column() > 0) + return rIndex; + + if(parent.isValid()) + { + Slide *slide = static_cast(parent.internalPointer()); + if(slide) + { + rIndex = createIndex(row, column, slide->element(row)); + } + + } + else + { + Slide *slide = static_cast(sourceModel()->index(mSlideNumber, 0).internalPointer()); + rIndex = createIndex(row, column, slide->element(row)); + } + + return rIndex; +} + +QModelIndex SlideProxyModel::parent(const QModelIndex &child) const +{ + // TODO: Implement this method + Q_UNUSED(child); + return QModelIndex(); +} + +int SlideProxyModel::rowCount(const QModelIndex &parent) const +{ + int rCount = 0; + if(parent.isValid()) + { + Slide* slide = static_cast(parent.internalPointer()); + if(slide) + { + rCount = slide->elementsCount(); + } + else + { + rCount = sourceModel()->rowCount(sourceModel()->index(mSlideNumber, 0)); + } + } + else + { + rCount = sourceModel()->rowCount(sourceModel()->index(mSlideNumber, 0)); + } + return rCount; +} + +int SlideProxyModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QModelIndex SlideProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + QModelIndex rIndex; + if(sourceIndex.isValid()) + { + rIndex = index(sourceIndex.row(),sourceIndex.column()); + } + return rIndex; +} + +QModelIndex SlideProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + QModelIndex rIndex; + if(proxyIndex.isValid()) + { + rIndex = sourceModel()->index(proxyIndex.row(),proxyIndex.column()); + } + return rIndex; +} + +QVariant SlideProxyModel::data(const QModelIndex &proxyIndex, int role) const +{ + QVariant rData; + if(proxyIndex.isValid()) + { + PresentationElement *element = static_cast(proxyIndex.internalPointer()); + if(element) + { + switch(role) + { + case PresentationModel::Roles::X : + { + rData = QString::number(element->x()); + break; + } + case PresentationModel::Roles::Y : + { + rData = QString::number(element->y()); + break; + } + case PresentationModel::Roles::Width : + { + rData = QString::number(element->width()); + break; + } + case PresentationModel::Roles::Height : + { + rData = QString::number(element->height()); + break; + } + case PresentationModel::Roles::Html : + { + rData = element->toHtml(); + break; + } + } + } + } + return rData; +} + +QHash SlideProxyModel::roleNames() const +{ + QHash rHash; + rHash.insert(PresentationModel::Roles::X, "X"); + rHash.insert(PresentationModel::Roles::Y, "Y"); + rHash.insert(PresentationModel::Roles::Width, "Width"); + rHash.insert(PresentationModel::Roles::Height, "Height"); + rHash.insert(PresentationModel::Roles::Html, "Html"); + return rHash; +} + +void SlideProxyModel::firstSlide() +{ + setSlideNumber(0); +} + +void SlideProxyModel::lastSlide() +{ + setSlideNumber(sourceModel()->rowCount() - 1); +} + +void SlideProxyModel::previousSlide() +{ + setSlideNumber(slideNumber() - 1); +} + +void SlideProxyModel::nextSlide() +{ + setSlideNumber(slideNumber() + 1); +} diff --git a/pllug-presentation-system/presentation-data/slideproxymodel.h b/pllug-presentation-system/presentation-data/slideproxymodel.h new file mode 100644 index 0000000..a77678a --- /dev/null +++ b/pllug-presentation-system/presentation-data/slideproxymodel.h @@ -0,0 +1,41 @@ +#ifndef SLIDEPROXYMODEL_H +#define SLIDEPROXYMODEL_H + +#include +#include +#include +#include + +class Slide; + +class SlideProxyModel : public QAbstractProxyModel +{ + Q_OBJECT +public: + explicit SlideProxyModel(QObject *parent = Q_NULLPTR); + + Q_INVOKABLE int slideNumber() const; + Q_INVOKABLE void setSlideNumber(int slideNumber); + + Q_INVOKABLE int slideCount() const; + + Q_INVOKABLE virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + Q_INVOKABLE virtual QModelIndex parent(const QModelIndex &child) const override; + Q_INVOKABLE virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + Q_INVOKABLE virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + Q_INVOKABLE virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; + Q_INVOKABLE virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; + virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override; + virtual QHash roleNames() const override; + + Q_INVOKABLE void firstSlide(); + Q_INVOKABLE void lastSlide(); + Q_INVOKABLE void previousSlide(); + Q_INVOKABLE void nextSlide(); + +private: + int mSlideNumber; +}; + +#endif // SLIDEPROXYMODEL_H diff --git a/pllug-presentation-system/resources/qml.qrc b/pllug-presentation-system/resources/qml.qrc index 4d6d66d..0b38384 100644 --- a/pllug-presentation-system/resources/qml.qrc +++ b/pllug-presentation-system/resources/qml.qrc @@ -2,8 +2,11 @@ qml/main.qml - - qml/TestScreen/Page1.qml - qml/TestScreen/Page1Form.ui.qml + + qml/View/SlideView.qml + qml/View/MainWindow.qml + qml/View/PageIndicator.qml + qml/View/ToolBarPPS.qml + qml/View/SideBar.qml diff --git a/pllug-presentation-system/resources/qml/TestScreen/Page1.qml b/pllug-presentation-system/resources/qml/TestScreen/Page1.qml deleted file mode 100644 index d5b808f..0000000 --- a/pllug-presentation-system/resources/qml/TestScreen/Page1.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.7 - -Page1Form { - button1.onClicked: { - console.log("Button 1 clicked."); - } - button2.onClicked: { - console.log("Button 2 clicked."); - } -} diff --git a/pllug-presentation-system/resources/qml/TestScreen/Page1Form.ui.qml b/pllug-presentation-system/resources/qml/TestScreen/Page1Form.ui.qml deleted file mode 100644 index 963f9ab..0000000 --- a/pllug-presentation-system/resources/qml/TestScreen/Page1Form.ui.qml +++ /dev/null @@ -1,23 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.0 - - -Item { - property alias button1: button1 - property alias button2: button2 - - RowLayout { - anchors.centerIn: parent - - Button { - id: button1 - text: qsTr("Press Me 1") - } - - Button { - id: button2 - text: qsTr("Press Me 2") - } - } -} diff --git a/pllug-presentation-system/resources/qml/View/MainWindow.qml b/pllug-presentation-system/resources/qml/View/MainWindow.qml new file mode 100644 index 0000000..8f7be13 --- /dev/null +++ b/pllug-presentation-system/resources/qml/View/MainWindow.qml @@ -0,0 +1,45 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.0 + +Item { + id: mainWindow + + SideBar{ + id: sideBar + + Component.onCompleted: { + toolBar.sideBarButtonClicked.connect(sideBar.open) + sideBar.currentSlideChanged.connect(sideBar.close) + } + + height: parent.height + width: 250 + } + + ToolBarPPS{ + id: toolBar + } + + SlideView { + id: slideView + + anchors { + topMargin: toolBar.height + fill: parent + } + + PageIndicator{ + id: pageIndicator + + Component.onCompleted: { + sideBar.currentSlideChanged.connect(pageIndicator.updateSlideIndexText) + } + + anchors{ + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + bottomMargin: 10 + } + } + } +} diff --git a/pllug-presentation-system/resources/qml/View/PageIndicator.qml b/pllug-presentation-system/resources/qml/View/PageIndicator.qml new file mode 100644 index 0000000..06fa3b3 --- /dev/null +++ b/pllug-presentation-system/resources/qml/View/PageIndicator.qml @@ -0,0 +1,152 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import com.cutehacks.fontawesome 1.0 + +RowLayout{ + id: pageIndicator + + QtObject { + id: internal + property int buttonWidth: 50 + property int buttonHeight: 20 + property int buttonRadius: 7 + property int fontPixelSize: 15 + property color buttonColor: "grey" + property color buttonPressedColor: "black" + property color buttonTextColor: "white" + } + + function updateSlideIndexText() { + slideIndexText.text = slideModel.slideNumber() + 1 + " of " + slideModel.slideCount() + } + + Button{ + id: startButton + + onClicked: { + slideModel.firstSlide() + updateSlideIndexText() + } + + width: internal.buttonWidth + height: internal.buttonHeight + style: ButtonStyle{ + background: Rectangle{ + radius: internal.buttonRadius + anchors.margins: 5 + color: control.pressed ? internal.buttonPressedColor : internal.buttonColor + } + + label: FAText{ + text: icons.angle_double_left + font.pixelSize: internal.fontPixelSize + anchors.centerIn: parent + color: internal.buttonTextColor + } + } + + } + + Button{ + id: previousSlideButton + + onClicked: { + slideModel.previousSlide() + updateSlideIndexText() + } + + width: internal.buttonWidth + height: internal.buttonHeight + style: ButtonStyle{ + background: Rectangle{ + id: previousSlideRect + + radius: internal.buttonRadius + color: control.pressed ? internal.buttonPressedColor : internal.buttonColor + } + + label: FAText{ + text: icons.angle_left + font.pixelSize: internal.fontPixelSize + anchors.centerIn: parent + color: internal.buttonTextColor + } + } + } + + Rectangle{ + id: slideIndexLabel + + width: 80 + height: 30 + color: "lightgrey" + radius: internal.buttonRadius + + Text{ + id: slideIndexText + + text: slideModel.slideNumber() + 1 + " of " + slideModel.slideCount() + font.pixelSize: internal.fontPixelSize + 8 + anchors.centerIn: parent + color: "grey" + } + } + + Button{ + id: nextSlideButton + + onClicked: { + slideModel.nextSlide() + updateSlideIndexText() + } + + width: internal.buttonWidth + height: internal.buttonHeight + style: ButtonStyle{ + background: Rectangle{ + id: nextSlideRect + + radius: internal.buttonRadius + color: control.pressed ? internal.buttonPressedColor : internal.buttonColor + } + + label: FAText{ + text: icons.angle_right + font.pixelSize: internal.fontPixelSize + anchors.centerIn: parent + color: internal.buttonTextColor + } + } + + + } + + Button{ + id: endButton + + onClicked: { + slideModel.lastSlide() + updateSlideIndexText() + } + + width: internal.buttonWidth + height: internal.buttonHeight + style: ButtonStyle{ + background: Rectangle{ + id: endRect + + radius: internal.buttonRadius + color: control.pressed ? internal.buttonPressedColor : internal.buttonColor + } + + label: FAText{ + text: icons.angle_double_right + font.pixelSize: internal.fontPixelSize + anchors.centerIn: parent + color: internal.buttonTextColor + } + } + } +} diff --git a/pllug-presentation-system/resources/qml/View/SideBar.qml b/pllug-presentation-system/resources/qml/View/SideBar.qml new file mode 100644 index 0000000..43a2f6b --- /dev/null +++ b/pllug-presentation-system/resources/qml/View/SideBar.qml @@ -0,0 +1,96 @@ +import QtQuick 2.0 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 1.4 + +Drawer { + id: drawer + + signal currentSlideChanged + + contentItem: ListView { + id: listView + + spacing: 25 + width: 250 + clip: true + ScrollIndicator.vertical: ScrollIndicator { } + + model: presentationModel + + delegate: Rectangle{ + id: slide + + anchors{ + left: parent.left + right: parent.right + leftMargin: 20 + rightMargin: 20 + } + width: parent.width + height: text.height + 8 + radius: 5 + border.color: area.pressed ? "white" : "black" + color: area.pressed ? "blue" : "white" + + Text{ + id: text + + text: Html + width: parent.width + wrapMode: Text.WordWrap + anchors{ + left: parent.left + leftMargin: 5 + right: parent.right + rightMargin: 5 + top: parent.top + topMargin: 5 + bottom: parent.bottom + bottomMargin: 5 + } + color: area.pressed ? "white" : "black" + } + + Rectangle{ + id: slideNumber + + width: 20 + height: 20 + radius: 5 + opacity: 0.5 + color: area.pressed ? "white" : "grey" + anchors{ + rightMargin: 3 + right: parent.right + top: parent.top + topMargin: 3 + } + + Text{ + text: (index + 1) + font{ + bold: true + pixelSize: 15 + family: "Lucida Sans Unicode" + } + color: area.pressed ? "white" : "black" + anchors.centerIn: parent + } + } + + MouseArea{ + id: area + + onClicked: { + slideModel.setSlideNumber(index) + drawer.currentSlideChanged() + + } + + anchors.fill: parent + } + } + } +} + diff --git a/pllug-presentation-system/resources/qml/View/SlideView.qml b/pllug-presentation-system/resources/qml/View/SlideView.qml new file mode 100644 index 0000000..a602797 --- /dev/null +++ b/pllug-presentation-system/resources/qml/View/SlideView.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.1 + +ColumnLayout { + anchors.fill: parent + Layout.fillHeight: false + + ListView { + anchors.fill: parent + + model: slideModel + + delegate: Rectangle{ + anchors{ + top: parent.top + topMargin: 15 + left: parent.left + leftMargin: 15 + right: parent.right + rightMargin: 15 + } + width: parent.width + + Text{ + text: Html + font.pixelSize: 25 + width: parent.width + wrapMode: Text.WordWrap + } + } + } +} diff --git a/pllug-presentation-system/resources/qml/View/ToolBarPPS.qml b/pllug-presentation-system/resources/qml/View/ToolBarPPS.qml new file mode 100644 index 0000000..eb24f58 --- /dev/null +++ b/pllug-presentation-system/resources/qml/View/ToolBarPPS.qml @@ -0,0 +1,47 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import com.cutehacks.fontawesome 1.0 + +ToolBar { + signal sideBarButtonClicked + + RowLayout { + anchors.fill: parent + + ToolButton { + id: menuButton + + anchors{ + left: parent.left + margins: 10 + } + + FAText{ + font.pixelSize: 20 + text: icons.ellipsis_v + anchors.centerIn: parent + } + } + + ToolButton { + id: sidebarButton + + onClicked: { + sideBarButtonClicked() + } + + anchors{ + left: menuButton.right + margins: 10 + } + + FAText{ + font.pixelSize: 20 + text: icons.bars + anchors.centerIn: parent + } + } + } +} diff --git a/pllug-presentation-system/resources/qml/main.qml b/pllug-presentation-system/resources/qml/main.qml index 373fc16..b6d8542 100644 --- a/pllug-presentation-system/resources/qml/main.qml +++ b/pllug-presentation-system/resources/qml/main.qml @@ -1,44 +1,17 @@ -import QtQuick 2.7 +import QtQuick.Controls 1.4 import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.0 -import com.cutehacks.fontawesome 1.0 - -import "./TestScreen" +import "./View" ApplicationWindow { visible: true width: 640 height: 480 - title: qsTr("Hello World") + title: qsTr("PLLUG-Presentation-System-2.0") + MainWindow{ + id: mainWindow - SwipeView { - id: swipeView anchors.fill: parent - currentIndex: tabBar.currentIndex - - Page1 { - } - - Page { - FAText { - text: icons.ambulance - anchors.centerIn: parent - font.pixelSize: 72 - color: "steelblue" - } - } - } - - footer: TabBar { - id: tabBar - currentIndex: swipeView.currentIndex - TabButton { - text: qsTr("First") - } - TabButton { - text: qsTr("Second") - } } } diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 8ab0e3e..fb0aefd 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -4,7 +4,8 @@ SUBDIRS += presentation \ slide \ domdocumentdivider \ htmlimport \ - importdivision + importdivision \ + presentationmodel # Curtom make target for all running available tests tests.CONFIG = recursive diff --git a/tests/auto/presentationmodel/modeltest.cpp b/tests/auto/presentationmodel/modeltest.cpp new file mode 100644 index 0000000..c6b79db --- /dev/null +++ b/tests/auto/presentationmodel/modeltest.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "modeltest.h" + +#include +#include +#include + +/*! + Connect to all of the models signals. Whenever anything happens + recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); + + connect(model, &QAbstractItemModel::columnsAboutToBeInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsAboutToBeRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::dataChanged, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::headerDataChanged, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::modelReset, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsAboutToBeInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsRemoved, + this, &ModelTest::runAllTests); + + // Special checks for inserting/removing + connect(model, &QAbstractItemModel::layoutAboutToBeChanged, + this, &ModelTest::layoutAboutToBeChanged); + connect(model, &QAbstractItemModel::layoutChanged, + this, &ModelTest::layoutChanged); + + connect(model, &QAbstractItemModel::rowsAboutToBeInserted, + this, &ModelTest::rowsAboutToBeInserted); + connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ModelTest::rowsAboutToBeRemoved); + connect(model, &QAbstractItemModel::rowsInserted, + this, &ModelTest::rowsInserted); + connect(model, &QAbstractItemModel::rowsRemoved, + this, &ModelTest::rowsRemoved); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + QModelIndex m1 = model->parent(QModelIndex()); + QModelIndex m2 = QModelIndex(); + Q_ASSERT(m1 == m2); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + QModelIndex tmp; + tmp = model->index(rows, columns); + Q_ASSERT(tmp == QModelIndex()); + tmp = model->index(0, 0); + Q_ASSERT(tmp.isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + QModelIndex tmp; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + tmp = model->parent(topIndex); + Q_ASSERT(tmp == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + tmp = model->parent(childIndex); + Q_ASSERT(tmp == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + A model that returns an index of parent X should also return X when asking + for the parent of the index. + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + QModelIndex tmp; + + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + //qDebug() << "TTT 1: " << model->parent(index); + //qDebug() << "TTT 2: " << parent; + //qDebug() << "TTT 3: " << index; + tmp = model->parent(index); + Q_ASSERT(tmp == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::Size)); + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) + Q_ASSERT(fontVariant.canConvert(QVariant::Font)); + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask))); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) + Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) + Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} diff --git a/tests/auto/presentationmodel/modeltest.h b/tests/auto/presentationmodel/modeltest.h new file mode 100644 index 0000000..29accd0 --- /dev/null +++ b/tests/auto/presentationmodel/modeltest.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; \ No newline at end of file diff --git a/tests/auto/presentationmodel/modeltest.pri b/tests/auto/presentationmodel/modeltest.pri new file mode 100644 index 0000000..65b955a --- /dev/null +++ b/tests/auto/presentationmodel/modeltest.pri @@ -0,0 +1,3 @@ +HEADERS += $$PWD/modeltest.h +SOURCES += $$PWD/modeltest.cpp +INCLUDEPATH += $$PWD \ No newline at end of file diff --git a/tests/auto/presentationmodel/presentationmodel.pro b/tests/auto/presentationmodel/presentationmodel.pro new file mode 100644 index 0000000..74f9380 --- /dev/null +++ b/tests/auto/presentationmodel/presentationmodel.pro @@ -0,0 +1,25 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-07-26T14:17:09 +# +#------------------------------------------------- + +QT += xml testlib + +QT -= gui + +TARGET = tst_presentationmodeltest + +include(../includes/default.pri) +include(../../../pllug-presentation-system/presentation-data/presentation-data.pri) + +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += tst_presentationmodeltest.cpp + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +include(./modeltest.pri) \ No newline at end of file diff --git a/tests/auto/presentationmodel/tst_presentationmodeltest.cpp b/tests/auto/presentationmodel/tst_presentationmodeltest.cpp new file mode 100644 index 0000000..17273f4 --- /dev/null +++ b/tests/auto/presentationmodel/tst_presentationmodeltest.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include "presentationmodel.h" +#include "modeltest.h" +#include "presentation.h" +#include "presentationelement.h" +#include "domdocumentdivider.h" +#include "presentationelementfactory.h" +#include "htmlimport.h" +#include "slide.h" +#include "testutility.h" + +#include "slideproxymodel.h" + +class PresentationModelTest : public QObject +{ + Q_OBJECT + +public: + PresentationModelTest(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void test_oneSlide_ModelWithOneRow(); + void test_2(); +}; + +PresentationModelTest::PresentationModelTest() +{ +} + +void PresentationModelTest::initTestCase() +{ +} + +void PresentationModelTest::cleanupTestCase() +{ +} + +void PresentationModelTest::test_oneSlide_ModelWithOneRow() +{ + QFile htmlFile(TestUtility::getResourcePath("separator_headers.html")); + htmlFile.open(QFile::ReadOnly); + QByteArray input = htmlFile.readAll(); + htmlFile.close(); + std::shared_ptr factoryPtr(new PresentationElementFactory()); + HtmlImport importObject(factoryPtr); + QList elements = importObject.import(input); + DomDocumentDivider divider; + std::unique_ptr presentation = divider.import(elements); + + PresentationModel *model = new PresentationModel(); + model->setPresentation(presentation.release()); + + for(int i = 0; i < model->rowCount(model->index(0,0).parent()); ++i) + { + QModelIndex slideIndex = model->index(i,0); + for(int j = 0; j < model->rowCount(slideIndex); ++j) + { + QModelIndex elementIndex = model->index(j,0,slideIndex); + PresentationElement *element = static_cast(elementIndex.internalPointer()); + qDebug() << "Slide" << i << "Element" << j << element->toHtml(); + } + } +} + +void PresentationModelTest::test_2() +{ + QFile htmlFile(TestUtility::getResourcePath("separator_headers.html")); + htmlFile.open(QFile::ReadOnly); + QByteArray input = htmlFile.readAll(); + htmlFile.close(); + std::shared_ptr factoryPtr(new PresentationElementFactory()); + HtmlImport importObject(factoryPtr); + QList elements = importObject.import(input); + DomDocumentDivider divider; + std::unique_ptr presentation = divider.import(elements); + + PresentationModel *model = new PresentationModel(); + Presentation *pres = presentation.release(); + model->setPresentation(pres); + SlideProxyModel slideModel; + slideModel.setSourceModel(model); + + for(int i = 0; i < pres->slideCount(); ++i) + { + slideModel.setSlideNumber(i); + for(int j = 0; j < slideModel.rowCount(); ++j) + { + QModelIndex elementIndex = slideModel.index(j,0); + PresentationElement *element = static_cast(elementIndex.internalPointer()); + qDebug() << "Slide" << i << "Element" << j << element->toHtml(); + } + } +} + +QTEST_APPLESS_MAIN(PresentationModelTest) + +#include "tst_presentationmodeltest.moc" diff --git a/tests/auto/resources/multiple_slides.html b/tests/auto/resources/multiple_slides.html new file mode 100644 index 0000000..3bef0d5 --- /dev/null +++ b/tests/auto/resources/multiple_slides.html @@ -0,0 +1,16 @@ + +

CHAPTER I. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+
+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+

CHAPTER III. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+

CHAPTER IV. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+

CHAPTER V. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+

CHAPTER VI. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+

CHAPTER VII. Down the Rabbit-Hole

+

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

+
\ No newline at end of file diff --git a/tests/auto/resources/separator_headers.html b/tests/auto/resources/separator_headers.html index d1014d5..9b9bae9 100644 --- a/tests/auto/resources/separator_headers.html +++ b/tests/auto/resources/separator_headers.html @@ -3,6 +3,6 @@

CHAPTER I. Down the Rabbit-Hole

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'


Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

-

CHAPTER I. Down the Rabbit-Hole

+

CHAPTER II. Down the Rabbit-Hole

Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?'

\ No newline at end of file