1 /*
2  * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  * for more details.
13  */
14 
15 #include "conflictdialog.h"
16 #include "ui_conflictdialog.h"
17 
18 #include "conflictsolver.h"
19 
20 #include <QDateTime>
21 #include <QDebug>
22 #include <QDesktopServices>
23 #include <QFileInfo>
24 #include <QMimeDatabase>
25 #include <QPushButton>
26 #include <QUrl>
27 
28 namespace {
forceHeaderFont(QWidget * widget)29 void forceHeaderFont(QWidget *widget)
30 {
31     auto font = widget->font();
32     font.setPointSizeF(font.pointSizeF() * 1.5);
33     widget->setFont(font);
34 }
35 
setBoldFont(QWidget * widget,bool bold)36 void setBoldFont(QWidget *widget, bool bold)
37 {
38     auto font = widget->font();
39     font.setBold(bold);
40     widget->setFont(font);
41 }
42 }
43 
44 namespace OCC {
45 
ConflictDialog(QWidget * parent)46 ConflictDialog::ConflictDialog(QWidget *parent)
47     : QDialog(parent)
48     , _ui(new Ui::ConflictDialog)
49     , _solver(new ConflictSolver(this))
50 {
51     _ui->setupUi(this);
52     forceHeaderFont(_ui->conflictMessage);
53     _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
54     _ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Keep selected version"));
55 
56     connect(_ui->localVersionRadio, &QCheckBox::toggled, this, &ConflictDialog::updateButtonStates);
57     connect(_ui->localVersionButton, &QToolButton::clicked, this, [=] {
58         QDesktopServices::openUrl(QUrl::fromLocalFile(_solver->localVersionFilename()));
59     });
60 
61     connect(_ui->remoteVersionRadio, &QCheckBox::toggled, this, &ConflictDialog::updateButtonStates);
62     connect(_ui->remoteVersionButton, &QToolButton::clicked, this, [=] {
63         QDesktopServices::openUrl(QUrl::fromLocalFile(_solver->remoteVersionFilename()));
64     });
65 
66     connect(_solver, &ConflictSolver::localVersionFilenameChanged, this, &ConflictDialog::updateWidgets);
67     connect(_solver, &ConflictSolver::remoteVersionFilenameChanged, this, &ConflictDialog::updateWidgets);
68 }
69 
baseFilename() const70 QString ConflictDialog::baseFilename() const
71 {
72     return _baseFilename;
73 }
74 
75 ConflictDialog::~ConflictDialog() = default;
76 
localVersionFilename() const77 QString ConflictDialog::localVersionFilename() const
78 {
79     return _solver->localVersionFilename();
80 }
81 
remoteVersionFilename() const82 QString ConflictDialog::remoteVersionFilename() const
83 {
84     return _solver->remoteVersionFilename();
85 }
86 
setBaseFilename(const QString & baseFilename)87 void ConflictDialog::setBaseFilename(const QString &baseFilename)
88 {
89     if (_baseFilename == baseFilename) {
90         return;
91     }
92 
93     _baseFilename = baseFilename;
94     _ui->conflictMessage->setText(tr("Conflicting versions of %1.").arg(_baseFilename));
95 }
96 
setLocalVersionFilename(const QString & localVersionFilename)97 void ConflictDialog::setLocalVersionFilename(const QString &localVersionFilename)
98 {
99     _solver->setLocalVersionFilename(localVersionFilename);
100 }
101 
setRemoteVersionFilename(const QString & remoteVersionFilename)102 void ConflictDialog::setRemoteVersionFilename(const QString &remoteVersionFilename)
103 {
104     _solver->setRemoteVersionFilename(remoteVersionFilename);
105 }
106 
accept()107 void ConflictDialog::accept()
108 {
109     const auto isLocalPicked = _ui->localVersionRadio->isChecked();
110     const auto isRemotePicked = _ui->remoteVersionRadio->isChecked();
111 
112     Q_ASSERT(isLocalPicked || isRemotePicked);
113     if (!isLocalPicked && !isRemotePicked) {
114         return;
115     }
116 
117     const auto solution = isLocalPicked && isRemotePicked ? ConflictSolver::KeepBothVersions
118                         : isLocalPicked ? ConflictSolver::KeepLocalVersion
119                         : ConflictSolver::KeepRemoteVersion;
120     if (_solver->exec(solution)) {
121         QDialog::accept();
122     }
123 }
124 
updateWidgets()125 void ConflictDialog::updateWidgets()
126 {
127     QMimeDatabase mimeDb;
128 
129     const auto updateGroup = [this, &mimeDb](const QString &filename, QLabel *linkLabel, const QString &linkText, QLabel *mtimeLabel, QLabel *sizeLabel, QToolButton *button) {
130         const auto fileUrl = QUrl::fromLocalFile(filename).toString();
131         linkLabel->setText(QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl).arg(linkText));
132 
133         const auto info = QFileInfo(filename);
134         mtimeLabel->setText(info.lastModified().toString());
135         sizeLabel->setText(locale().formattedDataSize(info.size()));
136 
137         const auto mime = mimeDb.mimeTypeForFile(filename);
138         if (QIcon::hasThemeIcon(mime.iconName())) {
139             button->setIcon(QIcon::fromTheme(mime.iconName()));
140         } else {
141             button->setIcon(QIcon(":/qt-project.org/styles/commonstyle/images/file-128.png"));
142         }
143     };
144 
145     const auto localVersion = _solver->localVersionFilename();
146     updateGroup(localVersion,
147                 _ui->localVersionLink,
148                 tr("Open local version"),
149                 _ui->localVersionMtime,
150                 _ui->localVersionSize,
151                 _ui->localVersionButton);
152 
153     const auto remoteVersion = _solver->remoteVersionFilename();
154     updateGroup(remoteVersion,
155                 _ui->remoteVersionLink,
156                 tr("Open server version"),
157                 _ui->remoteVersionMtime,
158                 _ui->remoteVersionSize,
159                 _ui->remoteVersionButton);
160 
161     const auto localMtime = QFileInfo(localVersion).lastModified();
162     const auto remoteMtime = QFileInfo(remoteVersion).lastModified();
163 
164     setBoldFont(_ui->localVersionMtime, localMtime > remoteMtime);
165     setBoldFont(_ui->remoteVersionMtime, remoteMtime > localMtime);
166 }
167 
updateButtonStates()168 void ConflictDialog::updateButtonStates()
169 {
170     const auto isLocalPicked = _ui->localVersionRadio->isChecked();
171     const auto isRemotePicked = _ui->remoteVersionRadio->isChecked();
172     _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isLocalPicked || isRemotePicked);
173 
174     const auto text = isLocalPicked && isRemotePicked ? tr("Keep both versions")
175                     : isLocalPicked ? tr("Keep local version")
176                     : isRemotePicked ? tr("Keep server version")
177                     : tr("Keep selected version");
178     _ui->buttonBox->button(QDialogButtonBox::Ok)->setText(text);
179 }
180 
181 } // namespace OCC
182