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 <QPainter>
23#include <QPainterPath>
24#include <QGraphicsSceneMouseEvent>
25#include <QTransform>
26
27#include <QDebug>
28
29#include <cmath>
30
31#include "rbviewport.h"
32#include "rbscreen.h"
33#include "rbrenderinfo.h"
34#include "parsetreemodel.h"
35#include "tag_table.h"
36#include "skin_parser.h"
37#include "skindocument.h"
38
39/* Pixels/second of text scrolling */
40const double RBViewport::scrollRate = 30;
41
42RBViewport::RBViewport(skin_element* node, const RBRenderInfo& info,
43 ParseTreeNode* pNode)
44 : RBMovable(info.screen()), foreground(info.screen()->foreground()),
45 background(info.screen()->background()), textOffset(0,0),
46 screen(info.screen()), textAlign(Left), showStatusBar(false),
47 statusBarTexture(":/render/statusbar.png"),
48 leftGraphic(0), centerGraphic(0), rightGraphic(0), scrollTime(0),
49 node(pNode), doc(info.document())
50{
51
52 if(!node->tag)
53 {
54 /* Default viewport takes up the entire screen */
55 size = QRectF(0, 0, info.screen()->getWidth(),
56 info.screen()->getHeight());
57 customUI = false;
58 font = screen->getFont(1);
59
60 screen->setDefault(this);
61
62 if(screen->getCustomUI())
63 {
64 RBViewport* cui = screen->getCustomUI();
65 size = cui->boundingRect();
66 setPos(cui->pos());
67
68 }
69
70 /* Making sure the default viewport can't be graphically manipulated */
71 setFlag(ItemIsSelectable, false);
72 setFlag(ItemIsMovable, false);
73
74 if(info.model()->rowCount(QModelIndex()) > 1)
75 {
76 /* If there is more than one viewport in the document */
77 textOffset.setX(-1);
78 }
79 else
80 {
81 setVisible(true);
82 }
83 }
84 else
85 {
86 QString ident;
87 int x,y,w,h;
88 /* Rendering one of the other types of viewport */
89 switch(node->tag->name[1])
90 {
91 case '\0':
92 customUI = false;
93 baseParam= 0;
94 break;
95
96 case 'l':
97 /* A preloaded viewport definition */
98 ident = node->params[0].data.text;
99 customUI = false;
100 if(!screen->viewPortDisplayed(ident))
101 hide();
102 info.screen()->loadViewport(ident, this);
103 baseParam= 1;
104 break;
105
106 case 'i':
107 /* Custom UI Viewport */
108 customUI = true;
109 baseParam= 1;
110 if(node->params[0].type == skin_tag_parameter::DEFAULT)
111 {
112 setVisible(true);
113 }
114 else
115 {
116 hide();
117 info.screen()->loadViewport(ident, this);
118 }
119 break;
120 }
121 /* Now we grab the info common to all viewports */
122 int param = baseParam;
123 x = node->params[param++].data.number;
124 if(x < 0)
125 x = info.screen()->boundingRect().right() + x;
126 y = node->params[param++].data.number;
127 if(y < 0)
128 y = info.screen()->boundingRect().bottom() + y;
129
130 if(node->params[param].type == skin_tag_parameter::DEFAULT)
131 w = info.screen()->getWidth() - x;
132 else
133 w = node->params[param].data.number;
134 if(w < 0)
135 w = info.screen()->getWidth() + w - x;
136
137 if(node->params[++param].type == skin_tag_parameter::DEFAULT)
138 h = info.screen()->getHeight() - y;
139 else
140 h = node->params[param].data.number;
141 if(h < 0)
142 h = info.screen()->getHeight() + h - y;
143
144 /* Adjusting to screen coordinates if necessary */
145 if(screen->parentItem() != 0)
146 {
147 x -= screen->parentItem()->pos().x();
148 y -= screen->parentItem()->pos().y();
149 }
150
151 if(node->params[++param].type == skin_tag_parameter::DEFAULT)
152 font = screen->getFont(1);
153 else
154 font = screen->getFont(node->params[param].data.number);
155
156 setPos(x, y);
157 size = QRectF(0, 0, w, h);
158 }
159
160 debug = info.device()->data("showviewports").toBool();
161 lineHeight = font->lineHeight();
162
163 if(info.screen()->isRtlMirrored() && info.device()->data("rtl").toBool())
164 {
165 /* Mirroring the viewport */
166 double x = screen->boundingRect().width() - 2 * pos().x();
167 QTransform t;
168 t.translate(x, 0);
169 t.scale(-1, 1);
170 setTransform(t);
171 }
172
173 if(customUI)
174 screen->setCustomUI(this);
175}
176
177RBViewport::~RBViewport()
178{
179}
180
181QPainterPath RBViewport::shape() const
182{
183 QPainterPath retval;
184 retval.addRect(size);
185 return retval;
186}
187
188void RBViewport::paint(QPainter *painter,
189 const QStyleOptionGraphicsItem *option, QWidget *widget)
190{
191 if(!screen->hasBackdrop() && background != screen->background())
192 {
193 painter->fillRect(size, QBrush(background));
194 }
195
196 painter->setBrush(Qt::NoBrush);
197 painter->setPen(customUI ? Qt::blue : Qt::red);
198 if(debug)
199 painter->drawRect(size);
200
201 if(showStatusBar)
202 painter->fillRect(QRectF(0, 0, size.width(), 8), statusBarTexture);
203
204 RBMovable::paint(painter, option, widget);
205}
206
207void RBViewport::newLine()
208{
209 if(textOffset.x() < 0)
210 return;
211
212 if(leftText != "")
213 alignLeft();
214
215 if(centerText != "")
216 alignCenter();
217
218 if(rightText != "")
219 alignRight();
220
221 textOffset.setY(textOffset.y() + lineHeight);
222 textOffset.setX(0);
223 textAlign = Left;
224
225 leftText.clear();
226 rightText.clear();
227 centerText.clear();
228
229 leftGraphic = 0;
230 centerGraphic = 0;
231 rightGraphic = 0;
232
233 scrollTime = 0;
234}
235
236void RBViewport::write(QString text)
237{
238 if(textOffset.x() < 0)
239 return;
240
241 Alignment align = textAlign;
242
243 if(align == Left)
244 {
245 leftText.append(text);
246 }
247 else if(align == Center)
248 {
249 centerText.append(text);
250 }
251 else if(align == Right)
252 {
253 rightText.append(text);
254 }
255}
256
257void RBViewport::showPlaylist(const RBRenderInfo &info, int start,
258 ParseTreeNode* lines)
259{
260
261 int song = start + info.device()->data("pp").toInt();
262 int numSongs = info.device()->data("pe").toInt();
263
264 while(song <= numSongs && textOffset.y() + lineHeight < size.height())
265 {
266 lines->render(info, this);
267 newLine();
268 song++;
269 }
270}
271
272void RBViewport::makeFullScreen()
273{
274 size = screen->boundingRect();
275 setPos(screen->pos());
276}
277
278void RBViewport::saveGeometry()
279{
280 QRectF bounds = boundingRect();
281 QPointF origin = pos();
282
283 node->modParam(static_cast<int>(origin.x()), baseParam);
284 node->modParam(static_cast<int>(origin.y()), baseParam + 1);
285 node->modParam(static_cast<int>(bounds.width()), baseParam + 2);
286 node->modParam(static_cast<int>(bounds.height()), baseParam + 3);
287}
288
289void RBViewport::alignLeft()
290{
291 int y = textOffset.y();
292
293 if(leftGraphic)
294 delete leftGraphic;
295
296 leftGraphic = font->renderText(leftText, foreground, size.width(), this);
297 leftGraphic->setPos(0, y);
298
299 /* Setting scroll position if necessary */
300 int difference = leftGraphic->realWidth()
301 - leftGraphic->boundingRect().width();
302 if(difference > 0)
303 {
304 /* Subtracting out complete cycles */
305 double totalTime = 2 * difference / scrollRate;
306 scrollTime -= totalTime * std::floor(scrollTime / totalTime);
307
308 /* Calculating the offset */
309 if(scrollTime < static_cast<double>(difference) / scrollRate)
310 {
311 leftGraphic->setOffset(scrollRate * scrollTime);
312 }
313 else
314 {
315 scrollTime -= static_cast<double>(difference) / scrollRate;
316 leftGraphic->setOffset(difference - scrollRate * scrollTime);
317 }
318 }
319}
320
321void RBViewport::alignCenter()
322{
323 int y = textOffset.y();
324 int x = 0;
325
326 if(centerGraphic)
327 delete centerGraphic;
328
329 centerGraphic = font->renderText(centerText, foreground, size.width(),
330 this);
331
332 if(centerGraphic->boundingRect().width() < size.width())
333 {
334 x = size.width() - centerGraphic->boundingRect().width();
335 x /= 2;
336 }
337 else
338 {
339 x = 0;
340 }
341
342 centerGraphic->setPos(x, y);
343
344 /* Setting scroll position if necessary */
345 int difference = centerGraphic->realWidth()
346 - centerGraphic->boundingRect().width();
347 if(difference > 0)
348 {
349 /* Subtracting out complete cycles */
350 double totalTime = 2 * difference / scrollRate;
351 scrollTime -= totalTime * std::floor(scrollTime / totalTime);
352
353 /* Calculating the offset */
354 if(scrollTime < static_cast<double>(difference) / scrollRate)
355 {
356 centerGraphic->setOffset(scrollRate * scrollTime);
357 }
358 else
359 {
360 scrollTime -= static_cast<double>(difference) / scrollRate;
361 centerGraphic->setOffset(difference - scrollRate * scrollTime);
362 }
363 }
364
365}
366
367void RBViewport::alignRight()
368{
369 int y = textOffset.y();
370 int x = 0;
371
372 if(rightGraphic)
373 delete rightGraphic;
374
375 rightGraphic = font->renderText(rightText, foreground, size.width(), this);
376
377 if(rightGraphic->boundingRect().width() < size.width())
378 x = size.width() - rightGraphic->boundingRect().width();
379 else
380 x = 0;
381
382 rightGraphic->setPos(x, y);
383
384 /* Setting scroll position if necessary */
385 int difference = rightGraphic->realWidth()
386 - rightGraphic->boundingRect().width();
387 if(difference > 0)
388 {
389 /* Subtracting out complete cycles */
390 double totalTime = 2 * difference / scrollRate;
391 scrollTime -= totalTime * std::floor(scrollTime / totalTime);
392
393 /* Calculating the offset */
394 if(scrollTime < static_cast<double>(difference) / scrollRate)
395 {
396 rightGraphic->setOffset(scrollRate * scrollTime);
397 }
398 else
399 {
400 scrollTime -= static_cast<double>(difference) / scrollRate;
401 rightGraphic->setOffset(difference - scrollRate * scrollTime);
402 }
403 }
404}
405