set(client_UI_SRCS
accountsettings.ui
+ conflictdialog.ui
folderwizardsourcepage.ui
folderwizardtargetpage.ui
generalsettings.ui
accountmanager.cpp
accountsettings.cpp
application.cpp
+ conflictdialog.cpp
conflictsolver.cpp
connectionvalidator.cpp
folder.cpp
--- /dev/null
+/*
+ * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
+ *
+ * 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 "conflictdialog.h"
+#include "ui_conflictdialog.h"
+
+#include "conflictsolver.h"
+
+#include <QDateTime>
+#include <QDebug>
+#include <QDesktopServices>
+#include <QFileInfo>
+#include <QMimeDatabase>
+#include <QPushButton>
+#include <QUrl>
+
+namespace {
+void forceHeaderFont(QWidget *widget)
+{
+ auto font = widget->font();
+ font.setPointSizeF(font.pointSizeF() * 1.5);
+ widget->setFont(font);
+}
+
+void setBoldFont(QWidget *widget, bool bold)
+{
+ auto font = widget->font();
+ font.setBold(bold);
+ widget->setFont(font);
+}
+}
+
+namespace OCC {
+
+ConflictDialog::ConflictDialog(QWidget *parent)
+ : QDialog(parent)
+ , _ui(new Ui::ConflictDialog)
+ , _solver(new ConflictSolver(this))
+{
+ _ui->setupUi(this);
+ forceHeaderFont(_ui->conflictMessage);
+ _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ _ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Keep selected version"));
+
+ connect(_ui->localVersionRadio, &QCheckBox::toggled, this, &ConflictDialog::updateButtonStates);
+ connect(_ui->localVersionButton, &QToolButton::clicked, this, [=] {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(_solver->localVersionFilename()));
+ });
+
+ connect(_ui->remoteVersionRadio, &QCheckBox::toggled, this, &ConflictDialog::updateButtonStates);
+ connect(_ui->remoteVersionButton, &QToolButton::clicked, this, [=] {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(_solver->remoteVersionFilename()));
+ });
+
+ connect(_solver, &ConflictSolver::localVersionFilenameChanged, this, &ConflictDialog::updateWidgets);
+ connect(_solver, &ConflictSolver::remoteVersionFilenameChanged, this, &ConflictDialog::updateWidgets);
+}
+
+QString ConflictDialog::baseFilename() const
+{
+ return _baseFilename;
+}
+
+ConflictDialog::~ConflictDialog() = default;
+
+QString ConflictDialog::localVersionFilename() const
+{
+ return _solver->localVersionFilename();
+}
+
+QString ConflictDialog::remoteVersionFilename() const
+{
+ return _solver->remoteVersionFilename();
+}
+
+void ConflictDialog::setBaseFilename(const QString &baseFilename)
+{
+ if (_baseFilename == baseFilename) {
+ return;
+ }
+
+ _baseFilename = baseFilename;
+ _ui->conflictMessage->setText(tr("Conflicting versions of %1.").arg(_baseFilename));
+}
+
+void ConflictDialog::setLocalVersionFilename(const QString &localVersionFilename)
+{
+ _solver->setLocalVersionFilename(localVersionFilename);
+}
+
+void ConflictDialog::setRemoteVersionFilename(const QString &remoteVersionFilename)
+{
+ _solver->setRemoteVersionFilename(remoteVersionFilename);
+}
+
+void ConflictDialog::accept()
+{
+ const auto isLocalPicked = _ui->localVersionRadio->isChecked();
+ const auto isRemotePicked = _ui->remoteVersionRadio->isChecked();
+
+ Q_ASSERT(isLocalPicked || isRemotePicked);
+ if (!isLocalPicked && !isRemotePicked) {
+ return;
+ }
+
+ const auto solution = isLocalPicked && isRemotePicked ? ConflictSolver::KeepBothVersions
+ : isLocalPicked ? ConflictSolver::KeepLocalVersion
+ : ConflictSolver::KeepRemoteVersion;
+ if (_solver->exec(solution)) {
+ QDialog::accept();
+ }
+}
+
+void ConflictDialog::updateWidgets()
+{
+ QMimeDatabase mimeDb;
+
+ const auto updateGroup = [this, &mimeDb](const QString &filename, QLabel *linkLabel, const QString &linkText, QLabel *mtimeLabel, QLabel *sizeLabel, QToolButton *button) {
+ const auto fileUrl = QUrl::fromLocalFile(filename).toString();
+ linkLabel->setText(QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl).arg(linkText));
+
+ const auto info = QFileInfo(filename);
+ mtimeLabel->setText(info.lastModified().toString());
+ sizeLabel->setText(locale().formattedDataSize(info.size()));
+
+ const auto mime = mimeDb.mimeTypeForFile(filename);
+ if (QIcon::hasThemeIcon(mime.iconName())) {
+ button->setIcon(QIcon::fromTheme(mime.iconName()));
+ } else {
+ button->setIcon(QIcon(":/qt-project.org/styles/commonstyle/images/file-128.png"));
+ }
+ };
+
+ const auto localVersion = _solver->localVersionFilename();
+ updateGroup(localVersion,
+ _ui->localVersionLink,
+ tr("Open local version"),
+ _ui->localVersionMtime,
+ _ui->localVersionSize,
+ _ui->localVersionButton);
+
+ const auto remoteVersion = _solver->remoteVersionFilename();
+ updateGroup(remoteVersion,
+ _ui->remoteVersionLink,
+ tr("Open remote version"),
+ _ui->remoteVersionMtime,
+ _ui->remoteVersionSize,
+ _ui->remoteVersionButton);
+
+ const auto localMtime = QFileInfo(localVersion).lastModified();
+ const auto remoteMtime = QFileInfo(remoteVersion).lastModified();
+
+ setBoldFont(_ui->localVersionMtime, localMtime > remoteMtime);
+ setBoldFont(_ui->remoteVersionMtime, remoteMtime > localMtime);
+}
+
+void ConflictDialog::updateButtonStates()
+{
+ const auto isLocalPicked = _ui->localVersionRadio->isChecked();
+ const auto isRemotePicked = _ui->remoteVersionRadio->isChecked();
+ _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isLocalPicked || isRemotePicked);
+
+ const auto text = isLocalPicked && isRemotePicked ? tr("Keep both versions")
+ : isLocalPicked ? tr("Keep local version")
+ : isRemotePicked ? tr("Keep server version")
+ : tr("Keep selected version");
+ _ui->buttonBox->button(QDialogButtonBox::Ok)->setText(text);
+}
+
+} // namespace OCC
--- /dev/null
+/*
+ * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
+ *
+ * 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.
+ */
+
+#ifndef CONFLICTDIALOG_H
+#define CONFLICTDIALOG_H
+
+#include <QDialog>
+
+namespace OCC {
+
+class ConflictSolver;
+
+namespace Ui {
+ class ConflictDialog;
+}
+
+class ConflictDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit ConflictDialog(QWidget *parent = nullptr);
+ ~ConflictDialog() override;
+
+ QString baseFilename() const;
+ QString localVersionFilename() const;
+ QString remoteVersionFilename() const;
+
+public slots:
+ void setBaseFilename(const QString &baseFilename);
+ void setLocalVersionFilename(const QString &localVersionFilename);
+ void setRemoteVersionFilename(const QString &remoteVersionFilename);
+
+ void accept() override;
+
+private:
+ void updateWidgets();
+ void updateButtonStates();
+
+ QString _baseFilename;
+ QScopedPointer<Ui::ConflictDialog> _ui;
+ ConflictSolver *_solver;
+};
+
+} // namespace OCC
+
+#endif // CONFLICTDIALOG_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OCC::ConflictDialog</class>
+ <widget class="QDialog" name="OCC::ConflictDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>504</width>
+ <height>407</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Sync Conflict</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QLabel" name="conflictMessage">
+ <property name="text">
+ <string>Conflicting versions of %1.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <property name="spacing">
+ <number>16</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Which version of the file do you want to keep?<br/>If you select both versions, the local file will have a number added to its name.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>16</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QCheckBox" name="localVersionRadio">
+ <property name="text">
+ <string>Local version</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QToolButton" name="localVersionButton">
+ <property name="toolTip">
+ <string>Click to open the file</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="localVersionMtime">
+ <property name="text">
+ <string>today</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="localVersionSize">
+ <property name="text">
+ <string>0 byte</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="localVersionLink">
+ <property name="text">
+ <string><a href="%1">Open local version</a></string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QCheckBox" name="remoteVersionRadio">
+ <property name="text">
+ <string>Server version</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QToolButton" name="remoteVersionButton">
+ <property name="toolTip">
+ <string>Click to open the file</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="remoteVersionMtime">
+ <property name="text">
+ <string>today</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="remoteVersionSize">
+ <property name="text">
+ <string>0 byte</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="remoteVersionLink">
+ <property name="text">
+ <string><a href="%1">Open server version</a></string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>OCC::ConflictDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>123</x>
+ <y>218</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>148</x>
+ <y>248</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>OCC::ConflictDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>181</x>
+ <y>215</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>240</x>
+ <y>254</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>