diff --git a/src/gui/folderwizard/folderwizard.cpp b/src/gui/folderwizard/folderwizard.cpp index bcf983accb0..d4a1f60ab76 100644 --- a/src/gui/folderwizard/folderwizard.cpp +++ b/src/gui/folderwizard/folderwizard.cpp @@ -35,6 +35,7 @@ #include "gui/accountstate.h" #include "gui/folderman.h" #include "gui/selectivesyncdialog.h" +#include "gui/spaces/spacesmodel.h" #include #include @@ -103,7 +104,7 @@ QString FolderWizardPrivate::initialLocalPath() const { QString defaultPath = defaultSyncRoot(); if (_account->supportsSpaces()) { - defaultPath += QLatin1Char('/') + _spacesPage->selectedSpace()->title(); + defaultPath += QLatin1Char('/') + _spacesPage->selectedSpace(Spaces::SpacesModel::Columns::Name).toString(); }; return FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath); } @@ -116,7 +117,7 @@ QString FolderWizardPrivate::remotePath() const QUrl FolderWizardPrivate::davUrl() const { if (_account->supportsSpaces()) { - auto url = _spacesPage->selectedSpace()->webDavUrl(); + auto url = _spacesPage->selectedSpace(Spaces::SpacesModel::Columns::WebDavUrl).toUrl(); if (!url.path().endsWith(QLatin1Char('/'))) { url.setPath(url.path() + QLatin1Char('/')); } @@ -128,9 +129,7 @@ QUrl FolderWizardPrivate::davUrl() const QString FolderWizardPrivate::displayName() const { if (_account->supportsSpaces()) { - const auto selectedSpace = _spacesPage->selectedSpace(); - Q_ASSERT(selectedSpace.has_value()); - return selectedSpace->title(); + return _spacesPage->selectedSpace(Spaces::SpacesModel::Columns::Name).toString(); }; return QString(); } diff --git a/src/gui/folderwizard/spacespage.cpp b/src/gui/folderwizard/spacespage.cpp index f15de1fcf19..e9af3dc39db 100644 --- a/src/gui/folderwizard/spacespage.cpp +++ b/src/gui/folderwizard/spacespage.cpp @@ -12,52 +12,34 @@ * for more details. */ #include "spacespage.h" -#include "graphapi/drives.h" #include "ui_spacespage.h" #include -#include using namespace OCC; -SpacesPage::SpacesPage(AccountPtr accountPtr, QWidget *parent) +SpacesPage::SpacesPage(AccountPtr acc, QWidget *parent) : QWizardPage(parent) - , _ui(new Ui::SpacesPage) + , ui(new Ui::SpacesPage) { - _ui->setupUi(this); + ui->setupUi(this); - connect(_ui->widget, &Spaces::SpacesBrowser::selectionChanged, this, &QWizardPage::completeChanged); + ui->widget->setAccount(acc); - QTimer::singleShot(0, this, [this, accountPtr] { - auto drive = new OCC::GraphApi::DrivesJob(accountPtr); - - connect(drive, &OCC::GraphApi::DrivesJob::finishedSignal, [drive, accountPtr, this] { - QList spaces; - - for (const auto &d : drive->drives()) { - spaces.append(Spaces::Space::fromDrive(d)); - } - - _ui->widget->setItems(accountPtr, spaces); - }); - - drive->start(); - }); + connect(ui->widget, &Spaces::SpacesBrowser::selectionChanged, this, &QWizardPage::completeChanged); } SpacesPage::~SpacesPage() { - delete _ui; + delete ui; } bool OCC::SpacesPage::isComplete() const { - return _ui->widget->selectedSpace().has_value(); + return ui->widget->currentSpace().isValid(); } -std::optional OCC::SpacesPage::selectedSpace() const +QVariant OCC::SpacesPage::selectedSpace(Spaces::SpacesModel::Columns column) const { - const auto &selectedSpace = _ui->widget->selectedSpace(); - Q_ASSERT(selectedSpace.has_value()); - return selectedSpace; + return ui->widget->currentSpace().siblingAtColumn(static_cast(column)).data(); } diff --git a/src/gui/folderwizard/spacespage.h b/src/gui/folderwizard/spacespage.h index 06ce5a100e7..850c86608f9 100644 --- a/src/gui/folderwizard/spacespage.h +++ b/src/gui/folderwizard/spacespage.h @@ -13,13 +13,12 @@ */ #pragma once +#include "gui/spaces/spacesmodel.h" + #include "accountfwd.h" -#include "gui/spaces/space.h" #include -#include - namespace Ui { class SpacesPage; @@ -31,15 +30,16 @@ class SpacesPage : public QWizardPage Q_OBJECT public: - explicit SpacesPage(AccountPtr accountPtr, QWidget *parent = nullptr); + explicit SpacesPage(AccountPtr acc, QWidget *parent = nullptr); ~SpacesPage(); bool isComplete() const override; - std::optional selectedSpace() const; + + QVariant selectedSpace(Spaces::SpacesModel::Columns column) const; private: - Ui::SpacesPage *_ui; + Ui::SpacesPage *ui; }; -} +} \ No newline at end of file diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index fe10c8471ad..5d909491924 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -25,7 +25,6 @@ #include "folderwizard/folderwizard.h" #include "graphapi/drives.h" #include "gui/accountsettings.h" -#include "gui/spaces/space.h" #include "guiutility.h" #include "logbrowser.h" #include "logger.h" @@ -70,26 +69,26 @@ void setUpInitialSyncFolder(AccountStatePtr accountStatePtr, bool useVfs) }; if (accountStatePtr->supportsSpaces()) { - auto *drivesJob = new GraphApi::DrivesJob(accountStatePtr->account()); + auto *drive = new GraphApi::Drives(accountStatePtr->account()); - QObject::connect(drivesJob, &GraphApi::DrivesJob::finishedSignal, [accountStatePtr, drivesJob, addFolder, finalize] { - if (drivesJob->parseError().error == QJsonParseError::NoError) { - const auto &drives = drivesJob->drives(); + QObject::connect(drive, &GraphApi::Drives::finishedSignal, [accountStatePtr, drive, addFolder, finalize] { + if (drive->parseError().error == QJsonParseError::NoError) { + const auto &drives = drive->drives(); if (!drives.isEmpty()) { const QDir localDir(accountStatePtr->account()->defaultSyncRoot()); FileSystem::setFolderMinimumPermissions(localDir.path()); Utility::setupFavLink(localDir.path()); for (const auto &d : drives) { - const auto space = Spaces::Space::fromDrive(d); - const QString folderName = FolderMan::instance()->findGoodPathForNewSyncFolder(localDir.filePath(space.title())); - addFolder(folderName, {}, QUrl::fromEncoded(d.getRoot().getWebDavUrl().toUtf8()), space.title()); + const QString name = GraphApi::Drives::getDriveDisplayName(d); + const QString folderName = FolderMan::instance()->findGoodPathForNewSyncFolder(localDir.filePath(name)); + addFolder(folderName, {}, QUrl::fromEncoded(d.getRoot().getWebDavUrl().toUtf8()), name); } finalize(); } } }); - drivesJob->start(); + drive->start(); return; } else { diff --git a/src/gui/spaces/CMakeLists.txt b/src/gui/spaces/CMakeLists.txt index f4896531eaa..6f29a71a12b 100644 --- a/src/gui/spaces/CMakeLists.txt +++ b/src/gui/spaces/CMakeLists.txt @@ -1,9 +1,5 @@ add_library(spaces STATIC - spacesbrowser.cpp - spacesbrowser.ui - spaceitemwidget.cpp - space.cpp -) + spacesmodel.cpp spacesbrowser.cpp spacesbrowser.ui) target_link_libraries(spaces PUBLIC Qt5::Widgets libsync) set_target_properties(spaces PROPERTIES AUTOUIC ON AUTORCC ON) apply_common_target_settings(spaces) diff --git a/src/gui/spaces/space.cpp b/src/gui/spaces/space.cpp deleted file mode 100644 index 9715b40e84b..00000000000 --- a/src/gui/spaces/space.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) by Fabian Müller - * - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#include "space.h" -#include "gui/guiutility.h" - -#include -#include - -namespace { -static const char contextC[] = "space"; -const auto personalC = QLatin1String("personal"); -const auto shareC = QLatin1String("virtual"); -} - -namespace OCC::Spaces { - -Space::Space(const QString &name, const QString &subtitle, const QUrl &webUrl, const QUrl &webDavUrl, const QUrl &imageUrl) - : _name(name) - , _subtitle(subtitle) - , _webUrl(webUrl) - , _webDavUrl(webDavUrl) - , _imageUrl(imageUrl) -{ -} - -const QString &Space::title() const -{ - return _name; -} - -const QString &Space::subtitle() const -{ - return _subtitle; -} - -const QUrl &Space::webUrl() const -{ - return _webDavUrl; -} - -const QUrl &Space::webDavUrl() const -{ - return _webDavUrl; -} - -const QUrl &Space::imageUrl() const -{ - return _imageUrl; -} - -Space Space::fromDrive(const OpenAPI::OAIDrive &drive) -{ - QUrl imageUrl = [special = drive.getSpecial()] { - const auto imageFolder = std::find_if(special.begin(), special.end(), [](const OpenAPI::OAIDriveItem &item) { - return item.getSpecialFolder().getName() == QStringLiteral("image"); - }); - - if (imageFolder == special.end()) { - return QUrl(); - } - - return QUrl(imageFolder->getWebDavUrl()); - }(); - - const QString driveTitle = [&drive]() { - if (drive.getDriveType() == personalC) { - return QCoreApplication::translate(contextC, "Personal"); - } else if (drive.getDriveType() == shareC) { - // don't call it ShareJail - return QCoreApplication::translate(contextC, "Shares"); - } - return drive.getName(); - }(); - - const QString driveSubtitle = [&drive]() { - QString description = drive.getDescription(); - - if (description.isEmpty() && drive.getDriveType() == personalC) { - description = QCoreApplication::translate(contextC, "This is your personal space."); - } - - return description; - }(); - - return { - driveTitle, - driveSubtitle, - QUrl(drive.getWebUrl()), - QUrl(drive.getRoot().getWebDavUrl()), - imageUrl - }; -} - -} // OCC::Spaces diff --git a/src/gui/spaces/space.h b/src/gui/spaces/space.h deleted file mode 100644 index d293f4ba20d..00000000000 --- a/src/gui/spaces/space.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) by Fabian Müller - * - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#pragma once - -#include "OAIDrive.h" - -#include -#include -#include - -namespace OCC::Spaces { - -class Space -{ -public: - Space(const QString &name, const QString &subtitle, const QUrl &webUrl, const QUrl &webDavUrl, const QUrl &imageUrl); - - static Space fromDrive(const OpenAPI::OAIDrive &drive); - - [[nodiscard]] const QString &title() const; - [[nodiscard]] const QString &subtitle() const; - [[nodiscard]] const QUrl &webUrl() const; - [[nodiscard]] const QUrl &webDavUrl() const; - [[nodiscard]] const QUrl &imageUrl() const; - -private: - QString _name; - QString _subtitle; - QUrl _webUrl; - QUrl _webDavUrl; - QUrl _imageUrl; -}; - -} // OCC::Spaces diff --git a/src/gui/spaces/spaceitemwidget.cpp b/src/gui/spaces/spaceitemwidget.cpp deleted file mode 100644 index 5502f89c161..00000000000 --- a/src/gui/spaces/spaceitemwidget.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) by Fabian Müller - * - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#include "spaceitemwidget.h" -#include "ui_spaceitemwidget.h" - -#include "gui/guiutility.h" -#include "networkjobs.h" - -namespace OCC::Spaces { - -SpaceItemWidget::SpaceItemWidget(const AccountPtr &accountPtr, const Space &space, QWidget *parent) - : QWidget(parent) - , _ui(new Ui::SpaceItemWidget) - , _space(space) -{ - _ui->setupUi(this); - - static const QSize ImageSizeC(128, 128); - static const auto PlaceholderImageC = OCC::Utility::getCoreIcon(QStringLiteral("th-large")).pixmap(ImageSizeC); - - // clear the placeholder - _ui->imageLabel->setText(QString()); - - // we want to make sure all entries in the browser have the same height - _ui->imageLabel->setMinimumSize(ImageSizeC); - - // showing the placeholder until the real image has been loaded (if available) should improve the UX - _ui->imageLabel->setPixmap(PlaceholderImageC); - - if (!space.imageUrl().isEmpty()) { - auto job = new OCC::SimpleNetworkJob(accountPtr, space.imageUrl(), {}, "GET", {}, {}, nullptr); - - connect(job, &OCC::SimpleNetworkJob::finishedSignal, this, [job, this] { - QPixmap pixmap; - qDebug() << "loading pixmap from space image:" << pixmap.loadFromData(job->reply()->readAll()); - const auto scaledPixmap = pixmap.scaled(ImageSizeC, Qt::KeepAspectRatio); - _ui->imageLabel->setPixmap(scaledPixmap); - }); - - job->start(); - } - - _ui->titleLabel->setText(space.title()); - - if (space.subtitle().isEmpty()) { - _ui->subtitleLabel->hide(); - } else { - _ui->subtitleLabel->setText(space.subtitle()); - } - - if (space.webUrl().isEmpty()) { - _ui->openBrowserButton->setEnabled(false); - } - - connect(_ui->openBrowserButton, &QPushButton::clicked, this, [this]() { - Utility::openBrowser(_space.webUrl(), this); - }); - - connect(_ui->radioButton, &QRadioButton::clicked, this, &SpaceItemWidget::radioButtonClicked); -} - -SpaceItemWidget::~SpaceItemWidget() -{ - delete _ui; -} - -const Space &SpaceItemWidget::space() const -{ - return _space; -} - -void SpaceItemWidget::setRadioButtonChecked(bool checked) -{ - _ui->radioButton->setChecked(checked); -} - -} // OCC::Spaces diff --git a/src/gui/spaces/spaceitemwidget.h b/src/gui/spaces/spaceitemwidget.h deleted file mode 100644 index c68d4c37bf6..00000000000 --- a/src/gui/spaces/spaceitemwidget.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) by Fabian Müller - * - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. - */ - -#pragma once - -#include "account.h" -#include "space.h" - -#include - - -QT_BEGIN_NAMESPACE -namespace Ui { -class SpaceItemWidget; -} -QT_END_NAMESPACE - -namespace OCC::Spaces { - -class SpaceItemWidget : public QWidget -{ - Q_OBJECT - -public: - explicit SpaceItemWidget(const AccountPtr &accountPtr, const Space &space, QWidget *parent = nullptr); - ~SpaceItemWidget() override; - - [[nodiscard]] const Space &space() const; - - void setRadioButtonChecked(bool checked); - -Q_SIGNALS: - void radioButtonClicked(); - -private: - Ui::SpaceItemWidget *_ui; - Space _space; -}; - -} // OCC::Spaces diff --git a/src/gui/spaces/spaceitemwidget.ui b/src/gui/spaces/spaceitemwidget.ui deleted file mode 100644 index c70c3dc4fcb..00000000000 --- a/src/gui/spaces/spaceitemwidget.ui +++ /dev/null @@ -1,118 +0,0 @@ - - - SpaceItemWidget - - - - 0 - 0 - 832 - 269 - - - - spaceslistwidget - - - - - - - - - - - - - logo [placeholder] - - - Qt::AlignCenter - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 150 - 0 - - - - - 12 - - - - space title [placeholder] - - - true - - - - - - - - 150 - 0 - - - - space subtitle [placeholder] - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Open in Web - - - - :/client/resources/light/arrow-up-right-from-square.svg:/client/resources/light/arrow-up-right-from-square.svg - - - - - - - - - - diff --git a/src/gui/spaces/spacesbrowser.cpp b/src/gui/spaces/spacesbrowser.cpp index fda2876d445..a237bed684a 100644 --- a/src/gui/spaces/spacesbrowser.cpp +++ b/src/gui/spaces/spacesbrowser.cpp @@ -11,14 +11,14 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ - #include "spacesbrowser.h" #include "ui_spacesbrowser.h" +#include "spacesmodel.h" + #include "graphapi/drives.h" #include "gui/models/expandingheaderview.h" -#include "spaceitemwidget.h" #include #include @@ -27,63 +27,52 @@ using namespace OCC::Spaces; SpacesBrowser::SpacesBrowser(QWidget *parent) : QWidget(parent) - , _ui(new Ui::SpacesBrowser) + , ui(new Ui::SpacesBrowser) { - _ui->setupUi(this); - - connect(_ui->listWidget, &QListWidget::itemSelectionChanged, this, [this]() { - Q_EMIT selectionChanged(); - }); - - // this way, we just need to emit selectionChanged, and have this code be called anyway - connect(this, &SpacesBrowser::selectionChanged, this, [this]() { - for (int i = 0; i < _ui->listWidget->count(); ++i) { - qobject_cast(_ui->listWidget->itemWidget(_ui->listWidget->item(i)))->setRadioButtonChecked(false); - } - - const auto selectedItems = _ui->listWidget->selectedItems(); - - Q_ASSERT(selectedItems.size() == 1); - const auto itemWidget = qobject_cast(_ui->listWidget->itemWidget(selectedItems.front())); - - itemWidget->setRadioButtonChecked(true); + ui->setupUi(this); + _model = new SpacesModel(this); + ui->tableView->setModel(_model); + + connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SpacesBrowser::selectionChanged); + + ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + auto header = new OCC::ExpandingHeaderView(QStringLiteral("SpacesBrowserHeader"), ui->tableView); + ui->tableView->setHorizontalHeader(header); + header->setExpandingColumn(static_cast(SpacesModel::Columns::Name)); + header->hideSection(static_cast(SpacesModel::Columns::WebDavUrl)); + // not used yet + header->hideSection(static_cast(SpacesModel::Columns::WebUrl)); + header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header, &QHeaderView::customContextMenuRequested, header, [header, this] { + auto menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + header->addResetActionToMenu(menu); + menu->popup(QCursor::pos()); }); } -void SpacesBrowser::setItems(const AccountPtr &accountPtr, const QList &spaces) -{ - for (const auto &space : spaces) { - auto listWidgetItem = new QListWidgetItem(_ui->listWidget); - - _ui->listWidget->addItem(listWidgetItem); - - auto itemWidget = new SpaceItemWidget(accountPtr, space, _ui->listWidget); - _ui->listWidget->setItemWidget(listWidgetItem, itemWidget); - - // otherwise, the widget will collapse to its minimum size - // note that this expands the frame the list widget draws around the frame, which can only be done here - listWidgetItem->setSizeHint(itemWidget->sizeHint()); - - connect(itemWidget, &SpaceItemWidget::radioButtonClicked, this, [this, listWidgetItem]() { - listWidgetItem->setSelected(true); - Q_EMIT selectionChanged(); - }); - } -} - SpacesBrowser::~SpacesBrowser() { - delete _ui; + delete ui; } -std::optional SpacesBrowser::selectedSpace() const +void SpacesBrowser::setAccount(OCC::AccountPtr acc) { - const auto selectedItems = _ui->listWidget->selectedItems(); - - if (selectedItems.empty()) { - return std::nullopt; + _acc = acc; + if (acc) { + QTimer::singleShot(0, this, [this] { + auto drive = new OCC::GraphApi::Drives(_acc); + connect(drive, &OCC::GraphApi::Drives::finishedSignal, [drive, this] { + _model->setData(_acc, drive->drives()); + show(); + }); + drive->start(); + }); } +} - Q_ASSERT(selectedItems.size() == 1); - return qobject_cast(_ui->listWidget->itemWidget(selectedItems.front()))->space(); +QModelIndex SpacesBrowser::currentSpace() +{ + const auto spaces = ui->tableView->selectionModel()->selectedRows(); + return spaces.isEmpty() ? QModelIndex {} : spaces.first(); } diff --git a/src/gui/spaces/spacesbrowser.h b/src/gui/spaces/spacesbrowser.h index 79c9f49c415..12c9503396d 100644 --- a/src/gui/spaces/spacesbrowser.h +++ b/src/gui/spaces/spacesbrowser.h @@ -11,16 +11,12 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ - #pragma once #include "account.h" -#include "space.h" #include -#include - namespace Ui { class SpacesBrowser; } @@ -36,15 +32,18 @@ class SpacesBrowser : public QWidget explicit SpacesBrowser(QWidget *parent = nullptr); ~SpacesBrowser(); - void setItems(const AccountPtr &accountPtr, const QList &spaces); + void setAccount(OCC::AccountPtr acc); - std::optional selectedSpace() const; + QModelIndex currentSpace(); Q_SIGNALS: void selectionChanged(); private: - Ui::SpacesBrowser *_ui; + Ui::SpacesBrowser *ui; + + OCC::AccountPtr _acc; + SpacesModel *_model; }; -} +} \ No newline at end of file diff --git a/src/gui/spaces/spacesbrowser.ui b/src/gui/spaces/spacesbrowser.ui index 9eb8b924e4f..b4c6fe2240e 100644 --- a/src/gui/spaces/spacesbrowser.ui +++ b/src/gui/spaces/spacesbrowser.ui @@ -27,20 +27,22 @@ 0 - - - Please select the space you want to synchronize: + + + QAbstractItemView::SingleSelection - - - - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel + + QAbstractItemView::SelectRows + + true + + + false + + + false + diff --git a/src/gui/spaces/spacesmodel.cpp b/src/gui/spaces/spacesmodel.cpp new file mode 100644 index 00000000000..ff8e8ff3043 --- /dev/null +++ b/src/gui/spaces/spacesmodel.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) by Hannah von Reth + * + * This program 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ +#include "spacesmodel.h" + +#include "common/utility.h" +// TODO: move models out from core +#include "gui/models/models.h" +#include "networkjobs.h" + +#include + +namespace { +constexpr QSize ImageSizeC(128, 128); +} + +using namespace OCC::Spaces; + +SpacesModel::SpacesModel(QObject *parent) + : QAbstractTableModel(parent) +{ +} + +QVariant SpacesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + const auto actionRole = static_cast(section); + switch (role) { + case Qt::DisplayRole: + switch (actionRole) { + case Columns::Name: + return tr("Name"); + case Columns::Description: + return tr("Description"); + case Columns::WebUrl: + return tr("Web URL"); + case Columns::WebDavUrl: + return tr("Web Dav URL"); + case Columns::Image: + return tr("Image"); + case Columns::ColumnCount: + Q_UNREACHABLE(); + break; + } + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + +int SpacesModel::rowCount(const QModelIndex &parent) const +{ + Q_ASSERT(checkIndex(parent)); + if (parent.isValid()) + return 0; + return static_cast(_data.size()); +} + +int SpacesModel::columnCount(const QModelIndex &parent) const +{ + Q_ASSERT(checkIndex(parent)); + if (parent.isValid()) { + return 0; + } + return static_cast(Columns::ColumnCount); +} + +QVariant SpacesModel::data(const QModelIndex &index, int role) const +{ + Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); + + const auto column = static_cast(index.column()); + const auto &item = _data.at(index.row()); + switch (role) { + case Qt::DisplayRole: + switch (column) { + case Columns::Name: + return GraphApi::Drives::getDriveDisplayName(item); + case Columns::Description: + return item.getDescription(); + case Columns::WebUrl: + return item.getWebUrl(); + case Columns::WebDavUrl: + return item.getRoot().getWebDavUrl(); + case Columns::ColumnCount: + Q_UNREACHABLE(); + break; + } + break; + case Qt::DecorationRole: + switch (column) { + case Columns::Image: { + if (auto it = Utility::optionalFind(_images, item.getId())) { + return QVariant::fromValue(it->value()); + } + const auto imgUrl = data(index, Models::UnderlyingDataRole).toUrl(); + if (imgUrl.isEmpty()) { + return {}; + } + // TODO: placeholder + _images[item.getId()] = QPixmap(); + auto job = new OCC::SimpleNetworkJob(_acc, imgUrl, {}, "GET", {}, {}, nullptr); + connect(job, &OCC::SimpleNetworkJob::finishedSignal, this, [job, id = item.getId(), index, this] { + QPixmap img; + qDebug() << img.loadFromData(job->reply()->readAll()); + img = img.scaled(ImageSizeC, Qt::KeepAspectRatio); + _images[id] = img; + Q_EMIT const_cast(this)->dataChanged(index, index, { Qt::DecorationRole }); + }); + job->start(); + return _images[item.getId()]; + } + default: + return {}; + } + case Qt::SizeHintRole: { + switch (column) { + case Columns::Image: + return ImageSizeC; + default: + return {}; + } + } + case Models::UnderlyingDataRole: + switch (column) { + case Columns::Image: { + const auto &special = item.getSpecial(); + const auto img = std::find_if(special.cbegin(), special.cend(), [](const OpenAPI::OAIDriveItem &it) { + return it.getSpecialFolder().getName() == QLatin1String("image"); + }); + return img == special.cend() ? QString() : img->getWebDavUrl(); + } + default: + return data(index, Qt::DisplayRole); + } + } + return {}; +} + +void SpacesModel::setData(OCC::AccountPtr acc, const QList &data) +{ + beginResetModel(); + _acc = acc; + _data = data; + endResetModel(); +} diff --git a/src/gui/spaces/spacesmodel.h b/src/gui/spaces/spacesmodel.h new file mode 100644 index 00000000000..0d0f454bf97 --- /dev/null +++ b/src/gui/spaces/spacesmodel.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) by Hannah von Reth + * + * This program 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ +#pragma once + +#include "accountfwd.h" + +#include + +#include + +namespace OCC::Spaces { +class SpacesModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum class Columns { + Image, + Name, + Description, + WebUrl, + WebDavUrl, + + ColumnCount + }; + Q_ENUM(Columns) + explicit SpacesModel(QObject *parent = nullptr); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + void setData(OCC::AccountPtr acc, const QList &data); + +private: + QList _data; + + mutable QHash _images; + OCC::AccountPtr _acc; +}; +} \ No newline at end of file diff --git a/src/libsync/graphapi/drives.cpp b/src/libsync/graphapi/drives.cpp index f68a7f43a7f..cf842a3db5c 100644 --- a/src/libsync/graphapi/drives.cpp +++ b/src/libsync/graphapi/drives.cpp @@ -28,18 +28,21 @@ using namespace GraphApi; namespace { const auto mountpointC = QLatin1String("mountpoint"); +const auto personalC = QLatin1String("personal"); +const auto shareC = QLatin1String("virtual"); + } -DrivesJob::DrivesJob(const AccountPtr &account, QObject *parent) +Drives::Drives(const AccountPtr &account, QObject *parent) : JsonJob(account, account->url(), QStringLiteral("/graph/v1.0/me/drives"), "GET", {}, {}, parent) { } -DrivesJob::~DrivesJob() +Drives::~Drives() { } -const QList &DrivesJob::drives() const +const QList &Drives::drives() const { if (_drives.isEmpty() && parseError().error == QJsonParseError::NoError) { OpenAPI::OAICollection_of_drives drives; @@ -53,3 +56,14 @@ const QList &DrivesJob::drives() const } return _drives; } + +QString Drives::getDriveDisplayName(const OpenAPI::OAIDrive &drive) +{ + if (drive.getDriveType() == personalC) { + return tr("Personal"); + } else if (drive.getDriveType() == shareC) { + // don't call it ShareJail + return tr("Shares"); + } + return drive.getName(); +} \ No newline at end of file diff --git a/src/libsync/graphapi/drives.h b/src/libsync/graphapi/drives.h index 35cac1a9c98..b0415492c99 100644 --- a/src/libsync/graphapi/drives.h +++ b/src/libsync/graphapi/drives.h @@ -18,17 +18,26 @@ #include "owncloudlib.h" #include -namespace OCC::GraphApi { -class OWNCLOUDSYNC_EXPORT DrivesJob : public JsonJob -{ - Q_OBJECT -public: - DrivesJob(const AccountPtr &account, QObject *parent = nullptr); - ~DrivesJob(); +namespace OCC { +namespace GraphApi { + class OWNCLOUDSYNC_EXPORT Drives : public JsonJob + { + Q_OBJECT + public: + Drives(const AccountPtr &account, QObject *parent = nullptr); + ~Drives(); - const QList &drives() const; + /*** + * Returns the display name of the drive. + * This is identical to drive.getName() for most drives. + * Exceptions: Personal spaces + */ + static QString getDriveDisplayName(const OpenAPI::OAIDrive &drive); -private: - mutable QList _drives; -}; + const QList &drives() const; + + private: + mutable QList _drives; + }; +} } diff --git a/src/resources/client.qrc b/src/resources/client.qrc index 34fd432ba01..b09dc54d1a9 100644 --- a/src/resources/client.qrc +++ b/src/resources/client.qrc @@ -17,8 +17,6 @@ font-awesome/light/step-forward-solid.svg font-awesome/light/clipboard-solid.svg font-awesome/light/copy-solid.svg - font-awesome/light/arrow-up-right-from-square-solid.svg - font-awesome/light/th-large-solid.svg font-awesome/dark/folder-solid.svg font-awesome/dark/cog-solid.svg font-awesome/dark/bolt-solid.svg @@ -36,8 +34,6 @@ font-awesome/dark/step-forward-solid.svg font-awesome/dark/clipboard-solid.svg font-awesome/dark/copy-solid.svg - font-awesome/dark/arrow-up-right-from-square-solid.svg - font-awesome/dark/th-large-solid.svg wizard/style.qss oauth/oauth.html.in diff --git a/src/resources/font-awesome/dark/arrow-up-right-from-square-solid.svg b/src/resources/font-awesome/dark/arrow-up-right-from-square-solid.svg deleted file mode 100644 index 90910aac47e..00000000000 --- a/src/resources/font-awesome/dark/arrow-up-right-from-square-solid.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/resources/font-awesome/dark/th-large-solid.svg b/src/resources/font-awesome/dark/th-large-solid.svg deleted file mode 100644 index 4f45fb2f8df..00000000000 --- a/src/resources/font-awesome/dark/th-large-solid.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/resources/font-awesome/light/arrow-up-right-from-square-solid.svg b/src/resources/font-awesome/light/arrow-up-right-from-square-solid.svg deleted file mode 100644 index 52de12259bb..00000000000 --- a/src/resources/font-awesome/light/arrow-up-right-from-square-solid.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/resources/font-awesome/light/th-large-solid.svg b/src/resources/font-awesome/light/th-large-solid.svg deleted file mode 100644 index 6097b18b196..00000000000 --- a/src/resources/font-awesome/light/th-large-solid.svg +++ /dev/null @@ -1 +0,0 @@ -