1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2010 Robert Bieber
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "tag_table.h"
25 #include "parsetreenode.h"
26 #include "parsetreemodel.h"
29 #include "rbprogressbar.h"
30 #include "rbtoucharea.h"
35 int ParseTreeNode::openConditionals
= 0;
36 bool ParseTreeNode::breakFlag
= false;
38 /* Root element constructor */
39 ParseTreeNode::ParseTreeNode(struct skin_element
* data
)
40 : parent(0), element(0), param(0), children()
44 children
.append(new ParseTreeNode(data
, this));
49 /* Normal element constructor */
50 ParseTreeNode::ParseTreeNode(struct skin_element
* data
, ParseTreeNode
* parent
)
51 : parent(parent
), element(data
), param(0), children()
57 for(int i
= 0; i
< element
->params_count
; i
++)
59 if(element
->params
[i
].type
== skin_tag_parameter::CODE
)
60 children
.append(new ParseTreeNode(element
->params
[i
].data
.code
,
63 children
.append(new ParseTreeNode(&element
->params
[i
], this));
68 for(int i
= 0; i
< element
->params_count
; i
++)
69 children
.append(new ParseTreeNode(&data
->params
[i
], this));
70 for(int i
= 0; i
< element
->children_count
; i
++)
71 children
.append(new ParseTreeNode(data
->children
[i
], this));
75 for(int i
= 0; i
< element
->children_count
; i
++)
77 children
.append(new ParseTreeNode(data
->children
[i
], this));
82 for(int i
= 0; i
< element
->params_count
; i
++)
83 children
.append(new ParseTreeNode(&data
->params
[i
], this));
84 /* Deliberate fall-through here */
87 for(int i
= 0; i
< data
->children_count
; i
++)
89 for(struct skin_element
* current
= data
->children
[i
]; current
;
90 current
= current
->next
)
92 children
.append(new ParseTreeNode(current
, this));
102 /* Parameter constructor */
103 ParseTreeNode::ParseTreeNode(skin_tag_parameter
*data
, ParseTreeNode
*parent
)
104 : parent(parent
), element(0), param(data
), children()
109 ParseTreeNode::~ParseTreeNode()
111 for(int i
= 0; i
< children
.count(); i
++)
115 QString
ParseTreeNode::genCode() const
121 switch(element
->type
)
126 /* Generating the Viewport tag, if necessary */
129 buffer
.append(TAGSYM
);
130 buffer
.append(element
->tag
->name
);
131 buffer
.append(ARGLISTOPENSYM
);
132 for(int i
= 0; i
< element
->params_count
; i
++)
134 buffer
.append(children
[i
]->genCode());
135 if(i
!= element
->params_count
- 1)
136 buffer
.append(ARGLISTSEPERATESYM
);
138 buffer
.append(ARGLISTCLOSESYM
);
142 for(int i
= element
->params_count
; i
< children
.count(); i
++)
143 buffer
.append(children
[i
]->genCode());
147 for(int i
= 0; i
< children
.count(); i
++)
149 buffer
.append(children
[i
]->genCode());
151 if(openConditionals
== 0
152 && !(parent
&& parent
->element
->type
== LINE_ALTERNATOR
))
158 case LINE_ALTERNATOR
:
159 for(int i
= 0; i
< children
.count(); i
++)
161 buffer
.append(children
[i
]->genCode());
162 if(i
!= children
.count() - 1)
163 buffer
.append(MULTILINESYM
);
165 if(openConditionals
== 0)
172 /* Inserting the tag part */
173 buffer
.append(TAGSYM
);
174 buffer
.append(CONDITIONSYM
);
175 buffer
.append(element
->tag
->name
);
176 if(element
->params_count
> 0)
178 buffer
.append(ARGLISTOPENSYM
);
179 for(int i
= 0; i
< element
->params_count
; i
++)
181 buffer
.append(children
[i
]->genCode());
182 if( i
!= element
->params_count
- 1)
183 buffer
.append(ARGLISTSEPERATESYM
);
184 buffer
.append(ARGLISTCLOSESYM
);
188 /* Inserting the sublines */
189 buffer
.append(ENUMLISTOPENSYM
);
190 for(int i
= element
->params_count
; i
< children
.count(); i
++)
192 buffer
.append(children
[i
]->genCode());
193 if(i
!= children
.count() - 1)
194 buffer
.append(ENUMLISTSEPERATESYM
);
196 buffer
.append(ENUMLISTCLOSESYM
);
201 buffer
.append(TAGSYM
);
202 buffer
.append(element
->tag
->name
);
204 if(element
->params_count
> 0)
206 /* Rendering parameters if there are any */
207 buffer
.append(ARGLISTOPENSYM
);
208 for(int i
= 0; i
< children
.count(); i
++)
210 buffer
.append(children
[i
]->genCode());
211 if(i
!= children
.count() - 1)
212 buffer
.append(ARGLISTSEPERATESYM
);
214 buffer
.append(ARGLISTCLOSESYM
);
216 if(element
->tag
->params
[strlen(element
->tag
->params
) - 1] == '\n')
221 for(char* cursor
= (char*)element
->data
; *cursor
; cursor
++)
223 if(find_escape_character(*cursor
))
224 buffer
.append(TAGSYM
);
225 buffer
.append(*cursor
);
230 buffer
.append(COMMENTSYM
);
231 buffer
.append((char*)element
->data
);
240 case skin_tag_parameter::STRING
:
241 for(char* cursor
= param
->data
.text
; *cursor
; cursor
++)
243 if(find_escape_character(*cursor
))
244 buffer
.append(TAGSYM
);
245 buffer
.append(*cursor
);
249 case skin_tag_parameter::INTEGER
:
250 buffer
.append(QString::number(param
->data
.number
, 10));
253 case skin_tag_parameter::DECIMAL
:
254 buffer
.append(QString::number(param
->data
.number
/ 10., 'f', 1));
257 case skin_tag_parameter::DEFAULT
:
258 buffer
.append(DEFAULTSYM
);
261 case skin_tag_parameter::CODE
:
262 buffer
.append(QObject::tr("This doesn't belong here"));
269 for(int i
= 0; i
< children
.count(); i
++)
270 buffer
.append(children
[i
]->genCode());
276 /* A more or less random hashing algorithm */
277 int ParseTreeNode::genHash() const
284 hash
+= element
->type
;
285 switch(element
->type
)
291 case LINE_ALTERNATOR
:
293 hash
+= element
->children_count
;
297 for(unsigned int i
= 0; i
< strlen(element
->tag
->name
); i
++)
298 hash
+= element
->tag
->name
[i
];
303 text
= (char*)element
->data
;
304 for(unsigned int i
= 0; i
< strlen(text
); i
++)
307 hash
+= text
[i
] % element
->type
;
309 hash
+= text
[i
] % element
->type
* 2;
321 case skin_tag_parameter::DEFAULT
:
322 case skin_tag_parameter::CODE
:
325 case skin_tag_parameter::INTEGER
:
326 hash
+= param
->data
.number
* (param
->data
.number
/ 4);
329 case skin_tag_parameter::STRING
:
330 for(unsigned int i
= 0; i
< strlen(param
->data
.text
); i
++)
333 hash
+= param
->data
.text
[i
] * 2;
335 hash
+= param
->data
.text
[i
];
339 case skin_tag_parameter::DECIMAL
:
340 hash
+= param
->data
.number
;
345 for(int i
= 0; i
< children
.count(); i
++)
347 hash
+= children
[i
]->genHash();
353 ParseTreeNode
* ParseTreeNode::child(int row
)
355 if(row
< 0 || row
>= children
.count())
358 return children
[row
];
361 int ParseTreeNode::numChildren() const
363 return children
.count();
367 QVariant
ParseTreeNode::data(int column
) const
371 case ParseTreeModel::typeColumn
:
374 switch(element
->type
)
377 return QObject::tr("Unknown");
379 return QObject::tr("Viewport");
382 return QObject::tr("Logical Line");
384 case LINE_ALTERNATOR
:
385 return QObject::tr("Alternator");
388 return QObject::tr("Comment");
391 return QObject::tr("Conditional Tag");
394 return QObject::tr("Tag");
397 return QObject::tr("Plaintext");
404 case skin_tag_parameter::STRING
:
405 return QObject::tr("String");
407 case skin_tag_parameter::INTEGER
:
408 return QObject::tr("Integer");
410 case skin_tag_parameter::DECIMAL
:
411 return QObject::tr("Decimal");
413 case skin_tag_parameter::DEFAULT
:
414 return QObject::tr("Default Argument");
416 case skin_tag_parameter::CODE
:
417 return QObject::tr("This doesn't belong here");
422 return QObject::tr("Root");
427 case ParseTreeModel::valueColumn
:
430 switch(element
->type
)
435 case LINE_ALTERNATOR
:
439 return QString(element
->tag
->name
);
443 return QString((char*)element
->data
);
446 return QString(element
->tag
->name
);
453 case skin_tag_parameter::DEFAULT
:
454 return QObject::tr("-");
456 case skin_tag_parameter::STRING
:
457 return QString(param
->data
.text
);
459 case skin_tag_parameter::INTEGER
:
460 return QString::number(param
->data
.number
, 10);
462 case skin_tag_parameter::DECIMAL
:
463 return QString::number(param
->data
.number
/ 10., 'f', 1);
465 case skin_tag_parameter::CODE
:
466 return QObject::tr("Seriously, something's wrong here");
476 case ParseTreeModel::lineColumn
:
478 return QString::number(element
->line
, 10);
488 int ParseTreeNode::getRow() const
493 return parent
->children
.indexOf(const_cast<ParseTreeNode
*>(this));
496 ParseTreeNode
* ParseTreeNode::getParent() const
501 /* This version is called for the root node and for viewports */
502 void ParseTreeNode::render(const RBRenderInfo
& info
)
504 /* Parameters don't get rendered */
505 if(!element
&& param
)
508 /* If we're at the root, we need to render each viewport */
509 if(!element
&& !param
)
511 for(int i
= 0; i
< children
.count(); i
++)
513 children
[i
]->render(info
);
519 if(element
->type
!= VIEWPORT
)
521 std::cerr
<< QObject::tr("Error in parse tree").toStdString()
526 rendered
= new RBViewport(element
, info
);
528 for(int i
= element
->params_count
; i
< children
.count(); i
++)
529 children
[i
]->render(info
, dynamic_cast<RBViewport
*>(rendered
));
533 /* This version is called for logical lines, tags, conditionals and such */
534 void ParseTreeNode::render(const RBRenderInfo
&info
, RBViewport
* viewport
,
537 if(element
->type
== LINE
)
539 for(int i
= 0; i
< children
.count(); i
++)
540 children
[i
]->render(info
, viewport
);
541 if(!noBreak
&& !breakFlag
)
544 viewport
->flushText();
549 else if(element
->type
== TEXT
)
551 viewport
->write(QString(static_cast<char*>(element
->data
)));
553 else if(element
->type
== TAG
)
555 if(!execTag(info
, viewport
))
556 viewport
->write(evalTag(info
).toString());
557 if(element
->tag
->flags
& NOBREAK
)
560 else if(element
->type
== CONDITIONAL
)
562 int child
= evalTag(info
, true, element
->children_count
).toInt();
563 children
[element
->params_count
+ child
]->render(info
, viewport
, true);
565 else if(element
->type
== LINE_ALTERNATOR
)
567 /* First we build a list of the times for each branch */
569 for(int i
= 0; i
< children
.count() ; i
++)
570 times
.append(findBranchTime(children
[i
], info
));
572 double totalTime
= 0;
573 for(int i
= 0; i
< children
.count(); i
++)
574 totalTime
+= times
[i
];
576 /* Now we figure out which branch to select */
577 double timeLeft
= info
.device()->data(QString("simtime")).toDouble();
579 /* Skipping any full cycles */
580 timeLeft
-= totalTime
* std::floor(timeLeft
/ totalTime
);
585 timeLeft
-= times
[branch
];
590 if(branch
>= times
.count())
594 /* In case we end up on a disabled branch, skip ahead. If we find that
595 * all the branches are disabled, don't render anything
597 int originalBranch
= branch
;
598 while(times
[branch
] == 0)
601 if(branch
== originalBranch
)
606 if(branch
>= times
.count())
610 /* ...and finally render the selected branch */
612 children
[branch
]->render(info
, viewport
, true);
616 bool ParseTreeNode::execTag(const RBRenderInfo
& info
, RBViewport
* viewport
)
621 int x
, y
, tiles
, tile
, maxWidth
, maxHeight
, width
, height
;
622 char c
, hAlign
, vAlign
;
625 /* Two switch statements to narrow down the tag name */
626 switch(element
->tag
->name
[0])
630 switch(element
->tag
->name
[1])
634 viewport
->alignText(RBViewport::Center
);
639 viewport
->alignText(RBViewport::Left
);
644 viewport
->alignText(RBViewport::Right
);
653 if(info
.device()->data("rtl").toBool())
654 viewport
->alignText(RBViewport::Right
);
656 viewport
->alignText(RBViewport::Left
);
661 if(info
.device()->data("rtl").toBool())
662 viewport
->alignText(RBViewport::Left
);
664 viewport
->alignText(RBViewport::Right
);
671 switch(element
->tag
->name
[1])
675 new RBProgressBar(viewport
, info
, element
->params_count
,
681 if(element
->params_count
> 0)
683 new RBProgressBar(viewport
, info
, element
->params_count
,
684 element
->params
, true);
694 switch(element
->tag
->name
[1])
698 viewport
->scrollText(info
.device()->data("simtime").toDouble());
705 switch(element
->tag
->name
[1])
709 info
.screen()->breakSBS();
714 /* Totally extraneous */
719 viewport
->enableStatusBar();
726 switch(element
->tag
->name
[1])
731 id
.append(element
->params
[0].data
.text
[0]);
732 c
= element
->params
[0].data
.text
[1];
746 if(info
.screen()->getImage(id
))
748 image
= new RBImage(*(info
.screen()->getImage(id
)), viewport
);
749 image
->setTile(tile
);
757 id
= element
->params
[0].data
.text
;
758 filename
= info
.settings()->value("imagepath", "") + "/" +
759 element
->params
[1].data
.text
;
760 x
= element
->params
[2].data
.number
;
761 y
= element
->params
[3].data
.number
;
762 if(element
->params_count
> 4)
763 tiles
= element
->params
[4].data
.number
;
767 info
.screen()->loadImage(id
, new RBImage(filename
, tiles
, x
, y
,
773 id
= element
->params
[0].data
.text
;
774 filename
= info
.settings()->value("imagepath", "") + "/" +
775 element
->params
[1].data
.text
;
776 x
= element
->params
[2].data
.number
;
777 y
= element
->params
[3].data
.number
;
778 image
= new RBImage(filename
, 1, x
, y
, viewport
);
779 info
.screen()->loadImage(id
, new RBImage(filename
, 1, x
, y
,
781 info
.screen()->getImage(id
)->show();
789 switch(element
->tag
->name
[1])
793 info
.screen()->showAlbumArt(viewport
);
798 x
= element
->params
[0].data
.number
;
799 y
= element
->params
[1].data
.number
;
800 maxWidth
= element
->params
[2].data
.number
;
801 maxHeight
= element
->params
[3].data
.number
;
802 hAlign
= element
->params_count
> 4
803 ? element
->params
[4].data
.text
[0] : 'c';
804 vAlign
= element
->params_count
> 5
805 ? element
->params
[5].data
.text
[0] : 'c';
806 width
= info
.device()->data("artwidth").toInt();
807 height
= info
.device()->data("artheight").toInt();
808 info
.screen()->setAlbumArt(new RBAlbumArt(viewport
, x
, y
, maxWidth
,
809 maxHeight
, width
, height
,
818 switch(element
->tag
->name
[1])
823 x
= element
->params
[0].data
.number
;
824 filename
= info
.settings()->value("themebase", "") + "/fonts/" +
825 element
->params
[1].data
.text
;
826 info
.screen()->loadFont(x
, new RBFont(filename
));
834 switch(element
->tag
->name
[1])
838 if(element
->params_count
< 5)
840 int x
= element
->params
[0].data
.number
;
841 int y
= element
->params
[1].data
.number
;
842 int width
= element
->params
[2].data
.number
;
843 int height
= element
->params
[3].data
.number
;
844 QString
action(element
->params
[4].data
.text
);
845 RBTouchArea
* temp
= new RBTouchArea(width
, height
, action
, info
);
856 switch(element
->tag
->name
[1])
861 viewport
->setBGColor(RBScreen::
862 stringToColor(QString(element
->params
[0].
869 id
= element
->params
[0].data
.text
;
870 info
.screen()->showViewport(id
);
875 viewport
->setFGColor(RBScreen::
876 stringToColor(QString(element
->params
[0].
883 viewport
->showPlaylist(info
, element
->params
[0].data
.number
,
884 element
->params
[1].data
.code
,
885 element
->params
[2].data
.code
);
890 info
.screen()->makeCustomUI(element
->params
[0].data
.text
);
899 switch(element
->tag
->name
[1])
903 filename
= QString(element
->params
[0].data
.text
);
904 if(info
.sbsScreen() && info
.screen()->parentItem())
905 info
.sbsScreen()->setBackdrop(filename
);
907 info
.screen()->setBackdrop(filename
);
919 QVariant
ParseTreeNode::evalTag(const RBRenderInfo
& info
, bool conditional
,
924 return info
.device()->data(QString(element
->tag
->name
),
925 element
->params_count
, element
->params
);
929 /* If we're evaluating for a conditional, we return the child branch
930 * index that should be selected. For true/false values, this is
931 * 0 for true, 1 for false, and we also have to make sure not to
932 * ever exceed the number of available children
936 QVariant val
= info
.device()->data("?" + QString(element
->tag
->name
));
938 val
= info
.device()->data(QString(element
->tag
->name
),
939 element
->params_count
, element
->params
);
945 else if(QString(element
->tag
->name
) == "bl")
947 /* bl has to be scaled to the number of available children, but it
948 * also has an initial -1 value for an unknown state */
956 child
= ((branches
- 1) * child
/ 100) + 1;
959 else if(QString(element
->tag
->name
) == "pv")
961 /* ?pv gets scaled to the number of available children, sandwiched
962 * in between mute and 0/>0dB. I assume a floor of -50dB for the
965 int dB
= val
.toInt();
970 child
= branches
- 2;
972 child
= branches
- 1;
975 int options
= branches
- 3;
976 child
= (options
* (dB
+ 50)) / 50;
979 else if(QString(element
->tag
->name
) == "px")
982 child
= branches
* child
/ 100;
984 else if(val
.type() == QVariant::Bool
)
986 /* Boolean values have to be reversed, because conditionals are
987 * always of the form %?tag<true|false>
994 else if(val
.type() == QVariant::String
)
996 if(val
.toString().length() > 0)
1001 else if(element
->tag
->name
[0] == 'i' || element
->tag
->name
[0] == 'I'
1002 || element
->tag
->name
[0] == 'f' || element
->tag
->name
[0] == 'F')
1004 if(info
.device()->data("id3available").toBool())
1011 child
= val
.toInt();
1017 if(child
< branches
)
1020 return branches
- 1;
1024 double ParseTreeNode::findBranchTime(ParseTreeNode
*branch
,
1025 const RBRenderInfo
& info
)
1028 for(int i
= 0; i
< branch
->children
.count(); i
++)
1030 ParseTreeNode
* current
= branch
->children
[i
];
1031 if(current
->element
->type
== TAG
)
1033 if(current
->element
->tag
->name
[0] == 't'
1034 && current
->element
->tag
->name
[1] == '\0')
1036 retval
= current
->element
->params
[0].data
.number
/ 10.;
1039 else if(current
->element
->type
== CONDITIONAL
)
1041 retval
= findConditionalTime(current
, info
);
1047 double ParseTreeNode::findConditionalTime(ParseTreeNode
*conditional
,
1048 const RBRenderInfo
& info
)
1050 int child
= conditional
->evalTag(info
, true,
1051 conditional
->children
.count()).toInt();
1052 return findBranchTime(conditional
->children
[child
], info
);