A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Robert Bieber
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "projectmodel.h"
23#include "configdocument.h"
24#include "ui_configdocument.h"
25#include "preferencesdialog.h"
26
27#include <QMessageBox>
28#include <QFile>
29#include <QSettings>
30#include <QFileDialog>
31#include <QPair>
32
33ConfigDocument::ConfigDocument(QMap<QString, QString>& settings, QString file,
34 QWidget *parent)
35 : TabContent(parent),
36 ui(new Ui::ConfigDocument),
37 filePath(file), block(false)
38{
39 ui->setupUi(this);
40 editor = new CodeEditor(this);
41 editor->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
42 editor->setLineWrapMode(CodeEditor::NoWrap);
43 ui->splitter->addWidget(editor);
44
45 QObject::connect(editor, SIGNAL(textChanged()),
46 this, SLOT(textChanged()));
47
48 /* Loading the splitter status */
49 QSettings appSettings;
50 appSettings.beginGroup("ConfigDocument");
51
52 if(!appSettings.value("textVisible", true).toBool())
53 {
54 editor->setVisible(false);
55 ui->textButton->setChecked(false);
56 }
57 else
58 {
59 ui->textButton->setChecked(true);
60 }
61 if(!appSettings.value("linesVisible", false).toBool())
62 {
63 ui->scrollArea->setVisible(false);
64 ui->linesButton->setChecked(false);
65 }
66 else
67 {
68 ui->linesButton->setChecked(true);
69 }
70
71 if(!appSettings.value("splitter", QByteArray()).toByteArray().isNull())
72 ui->splitter->restoreState(appSettings.value("splitter").toByteArray());
73
74 appSettings.endGroup();
75
76 /* Populating the known keys list */
77 QFile fin(":/resources/configkeys");
78 fin.open(QFile::ReadOnly);
79
80 QStringList* container = &primaryKeys;
81 while(!fin.atEnd())
82 {
83 QString current = QString(fin.readLine());
84 if(current == "-\n")
85 container = &secondaryKeys;
86 else if(current != "\n")
87 container->append(current.trimmed());
88 }
89
90 /* Loading the text file */
91 QFile finT(settings.value("configfile", ""));
92 if(finT.open(QFile::Text | QFile::ReadOnly))
93 {
94 editor->setPlainText(QString(finT.readAll()));
95 finT.close();
96 }
97
98 saved = toPlainText();
99
100 QObject::connect(ui->addKeyButton, SIGNAL(pressed()),
101 this, SLOT(addClicked()));
102
103 QObject::connect(ui->linesButton, SIGNAL(toggled(bool)),
104 this, SLOT(buttonChecked()));
105 QObject::connect(ui->textButton, SIGNAL(toggled(bool)),
106 this, SLOT(buttonChecked()));
107
108 settingsChanged();
109}
110
111ConfigDocument::~ConfigDocument()
112{
113 delete ui;
114 editor->deleteLater();
115}
116
117void ConfigDocument::changeEvent(QEvent *e)
118{
119 QWidget::changeEvent(e);
120 switch (e->type()) {
121 case QEvent::LanguageChange:
122 ui->retranslateUi(this);
123 break;
124 default:
125 break;
126 }
127}
128
129QString ConfigDocument::title() const
130{
131 QStringList decompose = filePath.split("/");
132 return decompose.last();
133}
134
135void ConfigDocument::save()
136{
137 QFile fout(filePath);
138
139 if(!fout.exists())
140 {
141 saveAs();
142 return;
143 }
144
145 fout.open(QFile::WriteOnly);
146 fout.write(toPlainText().toLatin1());
147 fout.close();
148
149 saved = toPlainText();
150 emit titleChanged(title());
151 emit configFileChanged(file());
152}
153
154void ConfigDocument::saveAs()
155{
156 /* Determining the directory to open */
157 QString directory = filePath;
158
159 QSettings settings;
160 settings.beginGroup("ProjectModel");
161 if(directory == "")
162 directory = settings.value("defaultDirectory", "").toString();
163
164 filePath = QFileDialog::getSaveFileName(this, tr("Save Document"),
165 directory,
166 ProjectModel::fileFilter());
167 directory = filePath;
168 if(filePath == "")
169 return;
170
171 directory.chop(filePath.length() - filePath.lastIndexOf('/') - 1);
172 settings.setValue("defaultDirectory", directory);
173 settings.endGroup();
174
175 QFile fout(filePath);
176 fout.open(QFile::WriteOnly);
177 fout.write(toPlainText().toLatin1());
178 fout.close();
179
180 saved = toPlainText();
181 emit titleChanged(title());
182 emit configFileChanged(file());
183
184}
185
186bool ConfigDocument::requestClose()
187{
188 if(toPlainText() != saved)
189 {
190 /* Spawning the "Are you sure?" dialog */
191 QMessageBox confirm(this);
192 confirm.setWindowTitle(tr("Confirm Close"));
193 confirm.setText(title() + tr(" has been modified."));
194 confirm.setInformativeText(tr("Do you want to save your changes?"));
195 confirm.setStandardButtons(QMessageBox::Save | QMessageBox::Discard
196 | QMessageBox::Cancel);
197 confirm.setDefaultButton(QMessageBox::Save);
198 int confirmation = confirm.exec();
199
200 switch(confirmation)
201 {
202 case QMessageBox::Save:
203 save();
204 /* After calling save, make sure the user actually went through */
205 if(toPlainText() != saved)
206 return false;
207 else
208 return true;
209
210 case QMessageBox::Discard:
211 return true;
212
213 case QMessageBox::Cancel:
214 return false;
215 }
216 }
217 return true;
218}
219
220QString ConfigDocument::toPlainText() const
221{
222 return editor->toPlainText();
223}
224
225void ConfigDocument::syncFromBoxes()
226{
227 if(block)
228 return;
229 blockUpdates();
230
231 QString buffer = "";
232
233 for(int i = 0; i < keys.count(); i++)
234 {
235 buffer += keys[i]->currentText();
236 buffer += ":";
237 buffer += values[i]->text();
238 buffer += "\n";
239 }
240
241 editor->setPlainText(buffer);
242}
243
244void ConfigDocument::syncFromText()
245{
246 if(block)
247 return;
248
249 blockUpdates();
250
251 QStringList lines = editor->toPlainText().split("\n");
252 QList<QPair<QString, QString> > splits;
253 for(int i = 0; i < lines.count(); i++)
254 {
255 QString line = lines[i];
256 QStringList split = line.split(":");
257 if(split.count() != 2)
258 continue;
259
260 splits.append(QPair<QString, QString>(split[0].trimmed(),
261 split[1].trimmed()));
262 }
263
264 while(deleteButtons.count() > splits.count())
265 {
266 deleteButtons[0]->deleteLater();
267 keys[0]->deleteLater();
268 values[0]->deleteLater();
269 containers[0]->deleteLater();
270 labels[0]->deleteLater();
271
272 deleteButtons.removeAt(0);
273 keys.removeAt(0);
274 values.removeAt(0);
275 containers.removeAt(0);
276 labels.removeAt(0);
277 }
278
279 int initialCount = deleteButtons.count();
280 for(int i = 0; i < splits.count(); i++)
281 {
282 if(i >= initialCount)
283 {
284 addRow(splits[i].first, splits[i].second);
285 }
286 else
287 {
288 int index = keys[i]->findText(splits[i].first);
289 if(index != -1)
290 keys[i]->setCurrentIndex(index);
291 else
292 keys[i]->setEditText(splits[i].first);
293 values[i]->setText(splits[i].second);
294 }
295 }
296}
297
298void ConfigDocument::addRow(QString key, QString value)
299{
300 QHBoxLayout* layout = new QHBoxLayout();
301 NoScrollCombo* keyEdit = new NoScrollCombo(this);
302 QLineEdit* valueEdit = new QLineEdit(value, this);
303 QPushButton* delButton = new QPushButton(tr("-"), this);
304 QLabel* label = new QLabel(":");
305
306 /* Loading the combo box options */
307 keyEdit->setInsertPolicy(QComboBox::InsertAlphabetically);
308 keyEdit->setEditable(true);
309 keyEdit->addItems(primaryKeys);
310 keyEdit->insertSeparator(keyEdit->count());
311 keyEdit->addItems(secondaryKeys);
312 if(keyEdit->findText(key) != -1)
313 keyEdit->setCurrentIndex(keyEdit->findText(key));
314 else
315 keyEdit->setEditText(key);
316
317
318 layout->addWidget(keyEdit);
319 layout->addWidget(label);
320 layout->addWidget(valueEdit);
321 layout->addWidget(delButton);
322
323 delButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
324 delButton->setMaximumWidth(35);
325
326 QObject::connect(delButton, SIGNAL(clicked()),
327 this, SLOT(deleteClicked()));
328 QObject::connect(keyEdit, SIGNAL(currentIndexChanged(QString)),
329 this, SLOT(boxesChanged()));
330 QObject::connect(keyEdit, SIGNAL(textChanged(QString)),
331 this, SLOT(boxesChanged()));
332 QObject::connect(valueEdit, SIGNAL(textChanged(QString)),
333 this, SLOT(boxesChanged()));
334
335 ui->configBoxes->addLayout(layout);
336
337 containers.append(layout);
338 keys.append(keyEdit);
339 values.append(valueEdit);
340 deleteButtons.append(delButton);
341 labels.append(label);
342
343}
344
345void ConfigDocument::deleteClicked()
346{
347 QPushButton* button = dynamic_cast<QPushButton*>(sender());
348 int row = deleteButtons.indexOf(button);
349
350 deleteButtons[row]->deleteLater();
351 keys[row]->deleteLater();
352 values[row]->deleteLater();
353 containers[row]->deleteLater();
354 labels[row]->deleteLater();
355
356 deleteButtons.removeAt(row);
357 keys.removeAt(row);
358 values.removeAt(row);
359 containers.removeAt(row);
360 labels.removeAt(row);
361
362 if(saved != toPlainText())
363 emit titleChanged(title() + "*");
364 else
365 emit titleChanged(title());
366}
367
368void ConfigDocument::addClicked()
369{
370 addRow(tr("Key"), tr("Value"));
371}
372
373void ConfigDocument::boxesChanged()
374{
375 syncFromBoxes();
376 contentsChanged();
377}
378
379void ConfigDocument::textChanged()
380{
381 syncFromText();
382 contentsChanged();
383}
384
385void ConfigDocument::contentsChanged()
386{
387 if(toPlainText() != saved)
388 emit titleChanged(title() + "*");
389 else
390 emit titleChanged(title());
391}
392
393void ConfigDocument::buttonChecked()
394{
395 editor->setVisible(ui->textButton->isChecked());
396 ui->scrollArea->setVisible(ui->linesButton->isChecked());
397
398 QSettings settings;
399 settings.beginGroup("ConfigDocument");
400 settings.setValue("textVisible", ui->textButton->isChecked());
401 settings.setValue("linesVisible", ui->linesButton->isChecked());
402 settings.endGroup();
403}
404
405void ConfigDocument::connectPrefs(PreferencesDialog *prefs)
406{
407 QObject::connect(prefs, SIGNAL(accepted()),
408 this, SLOT(settingsChanged()));
409}
410
411
412void ConfigDocument::settingsChanged()
413{
414 /* Setting the editor colors */
415 QSettings settings;
416 settings.beginGroup("SkinDocument");
417
418 QColor fg = settings.value("fgColor", QColor(Qt::black)).value<QColor>();
419 QColor bg = settings.value("bgColor", QColor(Qt::white)).value<QColor>();
420 QPalette palette;
421 palette.setColor(QPalette::All, QPalette::Base, bg);
422 palette.setColor(QPalette::All, QPalette::Text, fg);
423 editor->setPalette(palette);
424
425 QColor highlight = settings.value("errorColor", QColor(Qt::red)).value<QColor>();
426 editor->setErrorColor(highlight);
427
428 /* Setting the font */
429 QFont def("Monospace");
430 def.setStyleHint(QFont::TypeWriter);
431 QFont family = settings.value("fontFamily", def).value<QFont>();
432 family.setPointSize(settings.value("fontSize", 12).toInt());
433 editor->setFont(family);
434
435 editor->repaint();
436
437 settings.endGroup();
438
439}