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 "devicestate.h"
23
24#include <QScrollArea>
25#include <QFile>
26#include <QCheckBox>
27#include <QSpinBox>
28#include <QComboBox>
29#include <QVBoxLayout>
30#include <QLabel>
31#include <QLineEdit>
32#include <QFormLayout>
33#include <QTime>
34
35#include <cstdlib>
36
37DeviceState::DeviceState(QWidget *parent) :
38 QWidget(parent), tabs(this)
39{
40 /* UI stuff */
41 resize(500,400);
42 setWindowIcon(QIcon(":/resources/windowicon.png"));
43 setWindowTitle(tr("Device Settings"));
44
45 QFormLayout* layout = new QFormLayout(this);
46 layout->addWidget(&tabs);
47 this->setLayout(layout);
48
49 /* Loading the tabs */
50 QScrollArea* currentArea = 0;
51 QWidget* panel;
52
53 QFile fin(":/resources/deviceoptions");
54 fin.open(QFile::Text | QFile::ReadOnly);
55 while(!fin.atEnd())
56 {
57 QString line = QString(fin.readLine());
58 line = line.trimmed();
59
60 /* Continue on a comment or an empty line */
61 if(line[0] == '#' || line.length() == 0)
62 continue;
63
64 if(line[0] == '[')
65 {
66 QString buffer;
67 for(int i = 1; line[i] != ']'; i++)
68 buffer.append(line[i]);
69 buffer = buffer.trimmed();
70
71 panel = new QWidget();
72 currentArea = new QScrollArea();
73 layout = new QFormLayout(panel);
74 currentArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
75 currentArea->setWidget(panel);
76 currentArea->setWidgetResizable(true);
77
78 tabs.addTab(currentArea, buffer);
79
80 continue;
81 }
82
83 QStringList elements = line.split(";");
84 QString tag = elements[0].trimmed();
85 QString title = elements[1].trimmed();
86 QString type = elements[2].trimmed();
87 QString defVal = elements[3].trimmed();
88
89 elements = type.split("(");
90 if(elements[0].trimmed() == "text")
91 {
92 QLineEdit* temp = new QLineEdit(defVal, currentArea);
93 layout->addRow(title, temp);
94 inputs.insert(tag, QPair<InputType, QWidget*>(Text, temp));
95
96 temp->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,
97 QSizePolicy::Fixed));
98
99 QObject::connect(temp, SIGNAL(textChanged(QString)),
100 this, SLOT(input()));
101 }
102 else if(elements[0].trimmed() == "check")
103 {
104 QCheckBox* temp = new QCheckBox(title, currentArea);
105 layout->addRow(temp);
106 if(defVal.toLower() == "true")
107 temp->setChecked(true);
108 else
109 temp->setChecked(false);
110 inputs.insert(tag, QPair<InputType, QWidget*>(Check, temp));
111
112 QObject::connect(temp, SIGNAL(toggled(bool)),
113 this, SLOT(input()));
114 }
115 else if(elements[0].trimmed() == "slider")
116 {
117 elements = elements[1].trimmed().split(",");
118 int min = elements[0].trimmed().toInt();
119 QString maxS = elements[1].trimmed();
120 maxS.chop(1);
121 int max = maxS.toInt();
122
123 QSlider* temp = new QSlider(Qt::Horizontal, currentArea);
124 temp->setMinimum(min);
125 temp->setMaximum(max);
126 temp->setValue(defVal.toInt());
127 layout->addRow(title, temp);
128 inputs.insert(tag, QPair<InputType, QWidget*>(Slide, temp));
129
130 QObject::connect(temp, SIGNAL(valueChanged(int)),
131 this, SLOT(input()));
132 }
133 else if(elements[0].trimmed() == "spin")
134 {
135 elements = elements[1].trimmed().split(",");
136 int min = elements[0].trimmed().toInt();
137 QString maxS = elements[1].trimmed();
138 maxS.chop(1);
139 int max = maxS.toInt();
140
141 QSpinBox* temp = new QSpinBox(currentArea);
142 temp->setMinimum(min);
143 temp->setMaximum(max);
144 temp->setValue(defVal.toInt());
145 layout->addRow(title, temp);
146 inputs.insert(tag, QPair<InputType, QWidget*>(Spin, temp));
147
148 QObject::connect(temp, SIGNAL(valueChanged(int)),
149 this, SLOT(input()));
150 }
151 else if(elements[0].trimmed() == "fspin")
152 {
153 elements = elements[1].trimmed().split(",");
154 int min = elements[0].trimmed().toDouble();
155 QString maxS = elements[1].trimmed();
156 maxS.chop(1);
157 int max = maxS.toDouble();
158
159 QDoubleSpinBox* temp = new QDoubleSpinBox(currentArea);
160 temp->setMinimum(min);
161 temp->setMaximum(max);
162 temp->setValue(defVal.toDouble());
163 temp->setSingleStep(0.1);
164 layout->addRow(title, temp);
165 inputs.insert(tag, QPair<InputType, QWidget*>(DSpin, temp));
166
167 QObject::connect(temp, SIGNAL(valueChanged(double)),
168 this, SLOT(input()));
169 }
170 else if(elements[0].trimmed() == "combo")
171 {
172 elements = elements[1].trimmed().split(",");
173
174 int defIndex = 0;
175 QComboBox* temp = new QComboBox(currentArea);
176 for(int i = 0; i < elements.count(); i++)
177 {
178 QString current = elements[i].trimmed();
179 if(i == elements.count() - 1)
180 current.chop(1);
181 temp->addItem(current, i);
182 if(current == defVal)
183 defIndex = i;
184 }
185 temp->setCurrentIndex(defIndex);
186 layout->addRow(title, temp);
187 inputs.insert(tag, QPair<InputType, QWidget*>(Combo, temp));
188
189 QObject::connect(temp, SIGNAL(currentIndexChanged(int)),
190 this, SLOT(input()));
191 }
192
193 }
194}
195
196DeviceState::~DeviceState()
197{
198}
199
200QVariant DeviceState::data(QString tag, int paramCount,
201 skin_tag_parameter *params)
202{
203 /* Handling special cases */
204 if(tag.toLower() == "fm")
205 {
206 QString path = tag[0].isLower()
207 ? data("file").toString() : data("nextfile").toString();
208 return fileName(path, true);
209 }
210 else if(tag.toLower() == "fn")
211 {
212 QString path = tag[0].isLower()
213 ? data("file").toString() : data("nextfile").toString();
214 return fileName(path, false);
215 }
216 else if(tag.toLower() == "fp")
217 {
218 if(tag[0].isLower())
219 return data("file").toString();
220 else
221 return data("nextfile").toString();
222 }
223 else if(tag.toLower() == "d")
224 {
225 QString path = tag[0].isLower()
226 ? data("file").toString() : data("nextfile").toString();
227 if(paramCount > 0)
228 return directory(path, params[0].data.number);
229 else
230 return QVariant();
231 }
232 else if(tag == "pc")
233 {
234 int secs = data("?pc").toInt();
235 return secsToString(secs);
236 }
237 else if(tag == "pr")
238 {
239 int secs = data("?pt").toInt() - data("?pc").toInt();
240 if(secs < 0)
241 secs = 0;
242 return secsToString(secs);
243 }
244 else if(tag == "pt")
245 {
246 int secs = data("?pt").toInt();
247 return secsToString(secs);
248 }
249 else if(tag == "px")
250 {
251 int totalTime = data("?pt").toInt();
252 int currentTime = data("?pc").toInt();
253 return currentTime * 100 / totalTime;
254 }
255 else if(tag == "pS")
256 {
257 double threshhold = paramCount > 0
258 ? params[0].data.number / 10. : 10;
259 if(data("?pc").toDouble() <= threshhold)
260 return true;
261 else
262 return false;
263 }
264 else if(tag == "pE")
265 {
266 double threshhold = paramCount > 0
267 ? params[0].data.number / 10. : 10;
268 if(data("?pt").toDouble() - data("?pc").toDouble() <= threshhold)
269 return true;
270 else
271 return false;
272 }
273 else if(tag == "ce")
274 {
275 return data("month");
276 }
277 else if(tag == "cH")
278 {
279 int hour = data("hour").toInt();
280 if(hour < 10)
281 return "0" + QString::number(hour);
282 else
283 return hour;
284 }
285 else if(tag == "cK")
286 {
287 return data("hour");
288 }
289 else if(tag == "cI")
290 {
291 int hour = data("hour").toInt();
292 if(hour > 12)
293 hour -= 12;
294 if(hour == 0)
295 hour = 12;
296
297 if(hour < 10)
298 return "0" + QString::number(hour);
299 else
300 return hour;
301 }
302 else if(tag == "cl")
303 {
304 int hour = data("hour").toInt();
305 if(hour > 12)
306 hour -= 12;
307 if(hour == 0)
308 hour = 12;
309
310 return hour;
311 }
312 else if(tag == "cm")
313 {
314 int month = data("?cm").toInt() + 1;
315 if(month < 10)
316 return "0" + QString::number(month);
317 else
318 return month;
319 }
320 else if(tag == "cd")
321 {
322 int day = data("day").toInt();
323 if(day < 10)
324 return "0" + QString::number(day);
325 else
326 return day;
327 }
328 else if(tag == "cM")
329 {
330 int minute = data("minute").toInt();
331 if(minute < 10)
332 return "0" + QString::number(minute);
333 else
334 return minute;
335 }
336 else if(tag == "cS")
337 {
338 int second = data("second").toInt();
339 if(second < 10)
340 return "0" + QString::number(second);
341 else
342 return second;
343 }
344 else if(tag == "cy")
345 {
346 QString year = data("cY").toString();
347 return year.right(2);
348 }
349 else if(tag == "cP")
350 {
351 if(data("hour").toInt() >= 12)
352 return "PM";
353 else
354 return "AM";
355 }
356 else if(tag == "cp")
357 {
358 if(data("hour").toInt() >= 12)
359 return "pm";
360 else
361 return "am";
362 }
363 else if(tag == "ca")
364 {
365 switch(data("cw").toInt())
366 {
367 case 0: return "Sun";
368 case 1: return "Mon";
369 case 2: return "Tue";
370 case 3: return "Wed";
371 case 4: return "Thu";
372 case 5: return "Fri";
373 case 6: return "Sat";
374 default: return tr("Error, invalid weekday number");
375 }
376 }
377 else if(tag == "cb")
378 {
379 int month = data("cm").toInt();
380 switch(month)
381 {
382 case 1: return "Jan";
383 case 2: return "Feb";
384 case 3: return "Mar";
385 case 4: return "Apr";
386 case 5: return "May";
387 case 6: return "Jun";
388 case 7: return "Jul";
389 case 8: return "Aug";
390 case 9: return "Sep";
391 case 10: return "Oct";
392 case 11: return "Nov";
393 case 12: return "Dec";
394 }
395 }
396 else if(tag == "cu")
397 {
398 int day = data("?cw").toInt();
399 if(day == 0)
400 day = 7;
401 return day;
402 }
403 else if(tag == "?cu")
404 {
405 int day = data("?cw").toInt() - 1;
406 if(day == -1)
407 day = 6;
408 return day;
409 }
410 else if(tag == "cw")
411 {
412 return data("?cw");
413 }
414 else if(tag == "cs")
415 {
416 int seconds = data("seconds").toInt();
417 if(seconds < 10)
418 return "0" + QString::number(seconds);
419 else
420 return seconds;
421 }
422
423 QPair<InputType, QWidget*> found =
424 inputs.value(tag, QPair<InputType, QWidget*>(Slide, 0));
425
426 if(found.second == 0 && tag[0] == '?')
427 found = inputs.value(tag.right(2), QPair<InputType, QWidget*>(Slide,0));
428
429 if(found.second == 0)
430 return QVariant();
431
432 switch(found.first)
433 {
434 case Text:
435 return dynamic_cast<QLineEdit*>(found.second)->text();
436
437 case Slide:
438 return dynamic_cast<QSlider*>(found.second)->value();
439
440 case Spin:
441 return dynamic_cast<QSpinBox*>(found.second)->value();
442
443 case DSpin:
444 return dynamic_cast<QDoubleSpinBox*>(found.second)->value();
445
446 case Combo:
447 if(tag[0] == '?')
448 return dynamic_cast<QComboBox*>(found.second)->currentIndex();
449 else
450 return dynamic_cast<QComboBox*>(found.second)->currentText();
451
452 case Check:
453 return dynamic_cast<QCheckBox*>(found.second)->isChecked();
454 }
455
456 return QVariant();
457}
458
459void DeviceState::setData(QString tag, QVariant data)
460{
461 QPair<InputType, QWidget*> found =
462 inputs.value(tag, QPair<InputType, QWidget*>(Slide, 0));
463
464 if(found.second == 0)
465 return;
466
467 switch(found.first)
468 {
469 case Text:
470 dynamic_cast<QLineEdit*>(found.second)->setText(data.toString());
471 break;
472
473 case Slide:
474 dynamic_cast<QSlider*>(found.second)->setValue(data.toInt());
475 break;
476
477 case Spin:
478 dynamic_cast<QSpinBox*>(found.second)->setValue(data.toInt());
479 break;
480
481 case DSpin:
482 dynamic_cast<QDoubleSpinBox*>(found.second)->setValue(data.toDouble());
483 break;
484
485 case Combo:
486 if(data.type() == QVariant::String)
487 dynamic_cast<QComboBox*>
488 (found.second)->
489 setCurrentIndex(dynamic_cast<QComboBox*>
490 (found.second)->findText(data.toString()));
491 else
492 dynamic_cast<QComboBox*>(found.second)->
493 setCurrentIndex(data.toInt());
494 break;
495
496 case Check:
497 dynamic_cast<QCheckBox*>(found.second)->setChecked(data.toBool());
498 break;
499 }
500
501 emit settingsChanged();
502}
503
504void DeviceState::input()
505{
506 emit settingsChanged();
507}
508
509QString DeviceState::fileName(QString path, bool extension)
510{
511 path = path.split("/").last();
512 if(!extension)
513 {
514 QString sum;
515 QStringList name = path.split(".");
516 for(int i = 0; i < name.count() - 1; i++)
517 sum.append(name[i]);
518 return sum;
519 }
520 else
521 {
522 return path;
523 }
524}
525
526QString DeviceState::directory(QString path, int level)
527{
528 QStringList dirs = path.split("/");
529 int index = dirs.count() - 1 - level;
530 if(index < 0)
531 index = 0;
532 return dirs[index];
533}
534
535QString DeviceState::secsToString(int secs)
536{
537 int hours = 0;
538 int minutes = 0;
539 while(secs >= 60)
540 {
541 minutes++;
542 secs -= 60;
543 }
544
545 while(minutes >= 60)
546 {
547 hours++;
548 minutes -= 60;
549 }
550
551 QString retval;
552
553 if(hours > 0)
554 {
555 retval += QString::number(hours);
556 if(minutes < 10)
557 retval += ":0";
558 else
559 retval += ":";
560 }
561
562 retval += QString::number(minutes);
563 if(secs < 10)
564 retval += ":0";
565 else
566 retval += ":";
567
568 retval += QString::number(secs);
569 return retval;
570}