1 /***************************************************************************
2 **                                                                        **
3 **  Polyphone, a soundfont editor                                         **
4 **  Copyright (C) 2013-2019 Davy Triponney                                **
5 **                                                                        **
6 **  This program is free software: you can redistribute it and/or modify  **
7 **  it under the terms of the GNU General Public License as published by  **
8 **  the Free Software Foundation, either version 3 of the License, or     **
9 **  (at your option) any later version.                                   **
10 **                                                                        **
11 **  This program is distributed in the hope that it will be useful,       **
12 **  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
13 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          **
14 **  GNU General Public License for more details.                          **
15 **                                                                        **
16 **  You should have received a copy of the GNU General Public License     **
17 **  along with this program. If not, see http://www.gnu.org/licenses/.    **
18 **                                                                        **
19 ****************************************************************************
20 **           Author: Davy Triponney                                       **
21 **  Website/Contact: https://www.polyphone-soundfonts.com                 **
22 **             Date: 01.01.2013                                           **
23 ***************************************************************************/
24 
25 #include "soundfontviewer.h"
26 #include "ui_soundfontviewer.h"
27 #include "contextmanager.h"
28 #include "repositorymanager.h"
29 #include "detailsmanager.h"
30 #include "soundfontfilter.h"
31 #include "soundfontdetails.h"
32 #include "soundfontdescriptiondata.h"
33 #include "usermanager.h"
34 #include "urlreader.h"
35 #include "uploadingdialog.h"
36 #include <QMessageBox>
37 
38 SoundfontViewer::SoundfontViewer(QWidget *parent) : QWidget(parent),
39     ui(new Ui::SoundfontViewer),
40     _urlReader(new UrlReader(RepositoryManager::BASE_URL + "upload", false)),
41     _waitingDialog(nullptr)
42 {
43     ui->setupUi(this);
44 
45     // Colors
46     QColor highlighted = ContextManager::theme()->getColor(ThemeManager::HIGHLIGHTED_BACKGROUND);
47     QColor highlightedText = ContextManager::theme()->getColor(ThemeManager::HIGHLIGHTED_TEXT);
48     QColor border = this->palette().dark().color();
49     QColor highlightedHover = ContextManager::theme()->getColor(ThemeManager::HIGHLIGHTED_BACKGROUND, ThemeManager::HOVERED);
50     QColor buttonBackgroundHover = ContextManager::theme()->getColor(ThemeManager::HIGHLIGHTED_BACKGROUND, ThemeManager::HOVERED);
51 
52     // Style - top
53     ui->frameTitle->setStyleSheet("QFrame{background-color:" + highlighted.name() + ";color:" + highlightedText.name() + "}");
54     ui->frameTitle_2->setStyleSheet("QFrame{background-color:" + highlighted.name() + ";color:" + highlightedText.name() + "}");
55     ui->frameTitle_2->setMinimumHeight(ui->frameTitle->height());
56 
57     ui->pushEdit->setIcon(ContextManager::theme()->getColoredSvg(":/icons/pen.svg", QSize(22, 22), ThemeManager::ColorType::HIGHLIGHTED_TEXT));
58     ui->pushEdit->setStyleSheet("QPushButton{border: 0; border-left: 1px solid " + border.name() + "; padding:0 5px 0 5px;" +
59                                 "color:" + highlightedText.name() + "}" +
60                                 "QPushButton:hover{background-color:" + highlightedHover.name() + "}");
61     ui->pushSave->setIcon(ContextManager::theme()->getColoredSvg(":/icons/check.svg", QSize(16, 16), ThemeManager::ColorType::HIGHLIGHTED_TEXT));
62     ui->pushSave->setStyleSheet("QPushButton{border: 0; border-left: 1px solid " + border.name() + "; padding:0 5px 0 5px;" +
63                                 "color:" + highlightedText.name() + "}" +
64                                 "QPushButton:hover{background-color:" + highlightedHover.name() + "}");
65     ui->pushCancel->setIcon(ContextManager::theme()->getColoredSvg(":/icons/close.svg", QSize(16, 16), ThemeManager::ColorType::HIGHLIGHTED_TEXT));
66     ui->pushCancel->setStyleSheet("QPushButton{border: 0; border-left: 1px solid " + border.name() + "; padding:0 5px 0 5px;" +
67                                   "color:" + highlightedText.name() + "}" +
68                                   "QPushButton:hover{background-color:" + highlightedHover.name() + "}");
69 
70     // Hide the edit button
71     ui->pushEdit->hide();
72 
73     // Style - left
74     QString tmp = QString("QPushButton{background-color:%1; color:%2;border:0px;padding:5px;border-radius:4px;}") +
75             "QPushButton:hover{ background-color:%3;}";
76     ui->scrollArea->setStyleSheet(tmp.arg(highlighted.name()).arg(highlightedText.name()).arg(buttonBackgroundHover.name()));
77     ui->scrollArea_3->setStyleSheet(tmp.arg(highlighted.name()).arg(highlightedText.name()).arg(buttonBackgroundHover.name()));
78 
79     // Style - center
80     ui->pushRetry->setIcon(ContextManager::theme()->getColoredSvg(":/icons/reload.svg", QSize(16, 16), ThemeManager::HIGHLIGHTED_TEXT));
81     ui->pushRetry->setStyleSheet("QPushButton{background-color:" + highlighted.name() + ";border-radius:5px;padding:5px}");
82     ui->stackedWidget->setStyleSheet("QStackedWidget#stackedWidget{border:1px solid " + border.name() + ";border-top:0;border-right:0;border-bottom:0}");
83     ui->stackedWidget_2->setStyleSheet("QStackedWidget#stackedWidget_2{border:1px solid " + border.name() + ";border-top:0;border-right:0;border-bottom:0}");
84     ui->scrollMainArea->setStyleSheet("QWidget#scrollMainArea{ background: " + ContextManager::theme()->getColor(ThemeManager::LIST_BACKGROUND).name() +
85                                       ";color:" + ContextManager::theme()->getColor(ThemeManager::LIST_TEXT).name() + "; }");
86     ui->scrollMainArea_2->setStyleSheet("QWidget#scrollMainArea_2{ background: " + ContextManager::theme()->getColor(ThemeManager::LIST_BACKGROUND).name() +
87                                         ";color:" + ContextManager::theme()->getColor(ThemeManager::LIST_TEXT).name() + "; }");
88     ui->pageFailed->setStyleSheet("QWidget#pageFailed{ background: " + ContextManager::theme()->getColor(ThemeManager::LIST_BACKGROUND).name() +
89                                   ";color:" + ContextManager::theme()->getColor(ThemeManager::LIST_TEXT).name() + "; }");
90     ui->pageWait->setStyleSheet("QWidget#pageWait{ background: " + ContextManager::theme()->getColor(ThemeManager::LIST_BACKGROUND).name() +
91                                 ";color:" + ContextManager::theme()->getColor(ThemeManager::LIST_TEXT).name() + "; }");
92 
93     // Connections
94     connect(ui->viewerLeft, SIGNAL(itemClicked(SoundfontFilter*)), this, SIGNAL(itemClicked(SoundfontFilter*)));
95     connect(_urlReader, SIGNAL(downloadCompleted(QString)), this, SLOT(postCompleted(QString)), Qt::QueuedConnection);
96     connect(_urlReader, SIGNAL(progressChanged(int)), this, SLOT(postProgressChanged(int)));
97     connect(RepositoryManager::getInstance(), SIGNAL(ready(QString)), this, SLOT(onRefreshReady(QString)));
98     connect(DetailsManager::getInstance(), SIGNAL(detailsReady(int)), this, SLOT(onDetailsReady(int)));
99     connect(DetailsManager::getInstance(), SIGNAL(detailsFailed(int,QString)), this, SLOT(onDetailsFailed(int,QString)));
100 }
101 
102 SoundfontViewer::~SoundfontViewer()
103 {
104     delete _waitingDialog;
105     delete _urlReader;
106     delete ui;
107 }
108 
109 void SoundfontViewer::initialize(int soundfontId, bool forceReload)
110 {
111     ui->spinner->startAnimation();
112     ui->stackedWidget->setCurrentIndex(0);
113     ui->stackWidgetMain->setCurrentIndex(0);
114     ui->pushEdit->hide();
115     _soundfontId = soundfontId;
116 
117     // Fill basic information
118     SoundfontInformation * soundfontInfo = RepositoryManager::getInstance()->getSoundfontInformation(soundfontId);
119     if (soundfontInfo == nullptr) // Invalid id
120     {
121         delete _waitingDialog;
122         _waitingDialog = nullptr;
123         ui->stackedWidget->setCurrentIndex(2);
124         return;
125     }
126     ui->viewerTop->initialize(soundfontInfo);
127     ui->viewerLeft->initialize(soundfontInfo);
128 
129     // Ask for the details
130     DetailsManager::getInstance()->askForSoundfontDetails(_soundfontId, forceReload);
131 }
132 
133 void SoundfontViewer::onDetailsReady(int soundfontId)
134 {
135     if (soundfontId != _soundfontId)
136         return;
137 
138     // A dialog can be open
139     delete _waitingDialog;
140     _waitingDialog = nullptr;
141 
142     // Get the details
143     SoundfontDetails * sd = DetailsManager::getInstance()->getDetails(_soundfontId);
144     if (sd != nullptr)
145     {
146         ui->viewerCenter->initialize(sd);
147         ui->stackedWidget->setCurrentIndex(1);
148 
149         // Check if it's possible for the user to edit the soundfont
150         ui->pushEdit->setVisible(UserManager::getInstance()->getConnectionState() == UserManager::CONNECTED_ADMIN ||
151                                  (sd->getDescription()->getCreatedBy() != -1 &&
152                 sd->getDescription()->getCreatedBy() == UserManager::getInstance()->getUserId()));
153     }
154     else
155         ui->stackedWidget->setCurrentIndex(2);
156 }
157 
158 void SoundfontViewer::onDetailsFailed(int soundfontId, QString error)
159 {
160     Q_UNUSED(error)
161 
162     if (soundfontId != _soundfontId)
163         return;
164 
165     ui->stackedWidget->setCurrentIndex(2);
166 }
167 
168 void SoundfontViewer::on_pushRetry_clicked()
169 {
170     ui->stackedWidget->setCurrentIndex(0);
171     DetailsManager::getInstance()->askForSoundfontDetails(_soundfontId, false);
172 }
173 
174 void SoundfontViewer::on_pushEdit_clicked()
175 {
176     // Initialize the editor
177     SoundfontInformation * soundfontInfo = RepositoryManager::getInstance()->getSoundfontInformation(_soundfontId);
178     SoundfontDetails * sd = DetailsManager::getInstance()->getDetails(_soundfontId);
179     if (soundfontInfo == nullptr || sd == nullptr)
180         return; // Should not be possible
181     ui->editorTop->initialize(soundfontInfo);
182     ui->editorLeft->initialize(soundfontInfo);
183     ui->editorCenter->initialize(sd);
184 
185     // Switch the view
186     ui->stackWidgetMain->setCurrentIndex(1);
187 }
188 
189 void SoundfontViewer::on_pushCancel_clicked()
190 {
191     // Nothing special
192     ui->stackWidgetMain->setCurrentIndex(0);
193 }
194 
195 void SoundfontViewer::on_pushSave_clicked()
196 {
197     // Check the editing is valid
198     QString error = ui->editorTop->getEditingError();
199     if (!error.isEmpty())
200     {
201         QMessageBox::warning(this, tr("Warning"), error);
202         return;
203     }
204     error = ui->editorLeft->getEditingError();
205     if (!error.isEmpty())
206     {
207         QMessageBox::warning(this, tr("Warning"), error);
208         return;
209     }
210     error = ui->editorCenter->getEditingError();
211     if (!error.isEmpty())
212     {
213         QMessageBox::warning(this, tr("Warning"), error);
214         return;
215     }
216 
217     // Get a full description of the editing
218     QMap<QString, QString> arguments;
219     ui->editorTop->fillArguments(arguments);
220     ui->editorLeft->fillArguments(arguments);
221     ui->editorCenter->fillArguments(arguments);
222     QMap<QString, QString> files = ui->editorCenter->getFileArguments();
223 
224     // Save the changes on the website
225     _urlReader->clearArguments();
226     _urlReader->addArgument("id", QString::number(_soundfontId));
227     _urlReader->addArgument("user", ContextManager::configuration()->getValue(ConfManager::SECTION_REPOSITORY, "username", "").toString());
228     _urlReader->addArgument("pass", ContextManager::configuration()->getValue(ConfManager::SECTION_REPOSITORY, "password", "").toString());
229     foreach (QString key, arguments.keys())
230         _urlReader->addArgument(key, arguments[key]);
231     foreach (QString key, files.keys())
232         if (!files[key].isEmpty())
233             _urlReader->addFileAsArgument(key, files[key]);
234     _urlReader->download();
235 
236     // Open a progress dialog
237     if (_waitingDialog != nullptr)
238         delete _waitingDialog;
239     _waitingDialog = new UploadingDialog(100, this);
240     _waitingDialog->show();
241     connect(_waitingDialog, SIGNAL(canceled()), this, SLOT(onUploadCancel()));
242 }
243 
244 void SoundfontViewer::onUploadCancel()
245 {
246     // Stop the query
247     _urlReader->stop();
248 
249     // Delete the waiting dialog
250     delete _waitingDialog;
251     _waitingDialog = nullptr;
252 }
253 
254 void SoundfontViewer::postProgressChanged(int progress)
255 {
256     _waitingDialog->setValue(progress);
257 }
258 
259 void SoundfontViewer::postCompleted(QString error)
260 {
261     // Analyze the result
262     int currentId = -1;
263     if (error.isEmpty())
264     {
265         // The answer is either the soundfont id that has been updated / added or an error
266         QString answer = _urlReader->getRawData();
267         bool ok = false;
268         currentId = answer.toInt(&ok);
269         if (!ok)
270         {
271             currentId = -1;
272             error = answer;
273         }
274     }
275 
276     if (error.isEmpty() && currentId >= 0)
277     {
278         // Successfully upload the soundfont: reload the soundfont list and the page
279         _soundfontId = currentId;
280         RepositoryManager::getInstance()->initialize();
281     }
282     else
283     {
284         // Delete the dialog and notify that the upload failed
285         delete _waitingDialog;
286         _waitingDialog = nullptr;
287         QMessageBox::warning(this, tr("Warning"), tr("The upload failed: %1").arg(error));
288     }
289 }
290 
291 void SoundfontViewer::onRefreshReady(QString error)
292 {
293     if (error.isEmpty())
294     {
295         // Refresh the page
296         this->initialize(_soundfontId, true);
297     }
298     else
299     {
300         // The page cannot be refreshed
301         delete _waitingDialog;
302         _waitingDialog = nullptr;
303         onDetailsFailed(_soundfontId, error);
304     }
305 }
306