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"
38 int ParseTreeNode::openConditionals
= 0;
39 bool ParseTreeNode::breakFlag
= false;
41 /* Root element constructor */
42 ParseTreeNode::ParseTreeNode(struct skin_element
* data
, ParseTreeModel
* model
)
43 : parent(0), element(0), param(0), children(), model(model
)
47 children
.append(new ParseTreeNode(data
, this, model
));
52 /* Normal element constructor */
53 ParseTreeNode::ParseTreeNode(struct skin_element
* data
, ParseTreeNode
* parent
,
54 ParseTreeModel
* model
)
55 : parent(parent
), element(data
), param(0),
56 children(), model(model
)
62 for(int i
= 0; i
< element
->params_count
; i
++)
64 if(element
->params
[i
].type
== skin_tag_parameter::CODE
)
65 children
.append(new ParseTreeNode(element
->params
[i
].data
.code
,
68 children
.append(new ParseTreeNode(&element
->params
[i
], this,
74 for(int i
= 0; i
< element
->params_count
; i
++)
75 children
.append(new ParseTreeNode(&data
->params
[i
], this, model
));
76 for(int i
= 0; i
< element
->children_count
; i
++)
77 children
.append(new ParseTreeNode(data
->children
[i
], this, model
));
81 for(int i
= 0; i
< element
->children_count
; i
++)
83 children
.append(new ParseTreeNode(data
->children
[i
], this, model
));
88 for(int i
= 0; i
< element
->params_count
; i
++)
89 children
.append(new ParseTreeNode(&data
->params
[i
], this, model
));
90 /* Deliberate fall-through here */
93 for(int i
= 0; i
< data
->children_count
; i
++)
95 for(struct skin_element
* current
= data
->children
[i
]; current
;
96 current
= current
->next
)
98 children
.append(new ParseTreeNode(current
, this, model
));
108 /* Parameter constructor */
109 ParseTreeNode::ParseTreeNode(skin_tag_parameter
*data
, ParseTreeNode
*parent
,
110 ParseTreeModel
*model
)
111 : parent(parent
), element(0), param(data
),
112 children(), model(model
)
117 ParseTreeNode::~ParseTreeNode()
119 for(int i
= 0; i
< children
.count(); i
++)
123 QString
ParseTreeNode::genCode() const
129 switch(element
->type
)
134 /* Generating the Viewport tag, if necessary */
137 buffer
.append(TAGSYM
);
138 buffer
.append(element
->tag
->name
);
139 buffer
.append(ARGLISTOPENSYM
);
140 for(int i
= 0; i
< element
->params_count
; i
++)
142 buffer
.append(children
[i
]->genCode());
143 if(i
!= element
->params_count
- 1)
144 buffer
.append(ARGLISTSEPARATESYM
);
146 buffer
.append(ARGLISTCLOSESYM
);
150 for(int i
= element
->params_count
; i
< children
.count(); i
++)
151 buffer
.append(children
[i
]->genCode());
155 for(int i
= 0; i
< children
.count(); i
++)
157 buffer
.append(children
[i
]->genCode());
159 if(openConditionals
== 0
160 && !(parent
&& parent
->element
->type
== LINE_ALTERNATOR
)
161 && !(children
.count() > 0 &&
162 children
[children
.count() - 1]->getElement()->type
169 case LINE_ALTERNATOR
:
170 for(int i
= 0; i
< children
.count(); i
++)
172 buffer
.append(children
[i
]->genCode());
173 if(i
!= children
.count() - 1)
174 buffer
.append(MULTILINESYM
);
176 if(openConditionals
== 0)
183 /* Inserting the tag part */
184 buffer
.append(TAGSYM
);
185 buffer
.append(CONDITIONSYM
);
186 buffer
.append(element
->tag
->name
);
187 if(element
->params_count
> 0)
189 buffer
.append(ARGLISTOPENSYM
);
190 for(int i
= 0; i
< element
->params_count
; i
++)
192 buffer
.append(children
[i
]->genCode());
193 if( i
!= element
->params_count
- 1)
194 buffer
.append(ARGLISTSEPARATESYM
);
195 buffer
.append(ARGLISTCLOSESYM
);
199 /* Inserting the sublines */
200 buffer
.append(ENUMLISTOPENSYM
);
201 for(int i
= element
->params_count
; i
< children
.count(); i
++)
203 buffer
.append(children
[i
]->genCode());
204 if(i
!= children
.count() - 1)
205 buffer
.append(ENUMLISTSEPARATESYM
);
207 buffer
.append(ENUMLISTCLOSESYM
);
212 buffer
.append(TAGSYM
);
213 buffer
.append(element
->tag
->name
);
215 if(element
->params_count
> 0)
217 /* Rendering parameters if there are any */
218 buffer
.append(ARGLISTOPENSYM
);
219 for(int i
= 0; i
< children
.count(); i
++)
221 buffer
.append(children
[i
]->genCode());
222 if(i
!= children
.count() - 1)
223 buffer
.append(ARGLISTSEPARATESYM
);
225 buffer
.append(ARGLISTCLOSESYM
);
227 if(element
->tag
->params
[strlen(element
->tag
->params
) - 1] == '\n')
232 for(char* cursor
= (char*)element
->data
; *cursor
; cursor
++)
234 if(find_escape_character(*cursor
))
235 buffer
.append(TAGSYM
);
236 buffer
.append(*cursor
);
241 buffer
.append(COMMENTSYM
);
242 buffer
.append((char*)element
->data
);
251 case skin_tag_parameter::STRING
:
252 for(char* cursor
= param
->data
.text
; *cursor
; cursor
++)
254 if(find_escape_character(*cursor
))
255 buffer
.append(TAGSYM
);
256 buffer
.append(*cursor
);
260 case skin_tag_parameter::INTEGER
:
261 buffer
.append(QString::number(param
->data
.number
, 10));
264 case skin_tag_parameter::DECIMAL
:
265 buffer
.append(QString::number(param
->data
.number
/ 10., 'f', 1));
268 case skin_tag_parameter::DEFAULT
:
269 buffer
.append(DEFAULTSYM
);
272 case skin_tag_parameter::CODE
:
273 buffer
.append(QObject::tr("This doesn't belong here"));
280 for(int i
= 0; i
< children
.count(); i
++)
281 buffer
.append(children
[i
]->genCode());
287 /* A more or less random hashing algorithm */
288 int ParseTreeNode::genHash() const
295 hash
+= element
->type
;
296 switch(element
->type
)
302 case LINE_ALTERNATOR
:
304 hash
+= element
->children_count
;
308 for(unsigned int i
= 0; i
< strlen(element
->tag
->name
); i
++)
309 hash
+= element
->tag
->name
[i
];
314 text
= (char*)element
->data
;
315 for(unsigned int i
= 0; i
< strlen(text
); i
++)
318 hash
+= text
[i
] % element
->type
;
320 hash
+= text
[i
] % element
->type
* 2;
332 case skin_tag_parameter::DEFAULT
:
333 case skin_tag_parameter::CODE
:
336 case skin_tag_parameter::INTEGER
:
337 hash
+= param
->data
.number
* (param
->data
.number
/ 4);
340 case skin_tag_parameter::STRING
:
341 for(unsigned int i
= 0; i
< strlen(param
->data
.text
); i
++)
344 hash
+= param
->data
.text
[i
] * 2;
346 hash
+= param
->data
.text
[i
];
350 case skin_tag_parameter::DECIMAL
:
351 hash
+= param
->data
.number
;
356 for(int i
= 0; i
< children
.count(); i
++)
358 hash
+= children
[i
]->genHash();
364 ParseTreeNode
* ParseTreeNode::child(int row
)
366 if(row
< 0 || row
>= children
.count())
369 return children
[row
];
372 int ParseTreeNode::numChildren() const
374 return children
.count();
378 QVariant
ParseTreeNode::data(int column
) const
382 case ParseTreeModel::typeColumn
:
385 switch(element
->type
)
388 return QObject::tr("Unknown");
390 return QObject::tr("Viewport");
393 return QObject::tr("Logical Line");
395 case LINE_ALTERNATOR
:
396 return QObject::tr("Alternator");
399 return QObject::tr("Comment");
402 return QObject::tr("Conditional Tag");
405 return QObject::tr("Tag");
408 return QObject::tr("Plaintext");
415 case skin_tag_parameter::STRING
:
416 return QObject::tr("String");
418 case skin_tag_parameter::INTEGER
:
419 return QObject::tr("Integer");
421 case skin_tag_parameter::DECIMAL
:
422 return QObject::tr("Decimal");
424 case skin_tag_parameter::DEFAULT
:
425 return QObject::tr("Default Argument");
427 case skin_tag_parameter::CODE
:
428 return QObject::tr("This doesn't belong here");
433 return QObject::tr("Root");
438 case ParseTreeModel::valueColumn
:
441 switch(element
->type
)
446 case LINE_ALTERNATOR
:
450 return QString(element
->tag
->name
);
454 return QString((char*)element
->data
);
457 return QString(element
->tag
->name
);
464 case skin_tag_parameter::DEFAULT
:
465 return QObject::tr("-");
467 case skin_tag_parameter::STRING
:
468 return QString(param
->data
.text
);
470 case skin_tag_parameter::INTEGER
:
471 return QString::number(param
->data
.number
, 10);
473 case skin_tag_parameter::DECIMAL
:
474 return QString::number(param
->data
.number
/ 10., 'f', 1);
476 case skin_tag_parameter::CODE
:
477 return QObject::tr("Seriously, something's wrong here");
487 case ParseTreeModel::lineColumn
:
489 return QString::number(element
->line
, 10);
499 int ParseTreeNode::getRow() const
504 return parent
->children
.indexOf(const_cast<ParseTreeNode
*>(this));
507 ParseTreeNode
* ParseTreeNode::getParent() const
512 /* This version is called for the root node and for viewports */
513 void ParseTreeNode::render(const RBRenderInfo
& info
)
515 /* Parameters don't get rendered */
516 if(!element
&& param
)
519 /* If we're at the root, we need to render each viewport */
520 if(!element
&& !param
)
522 for(int i
= 0; i
< children
.count(); i
++)
524 children
[i
]->render(info
);
530 if(element
->type
!= VIEWPORT
)
532 std::cerr
<< QObject::tr("Error in parse tree").toStdString()
537 rendered
= new RBViewport(element
, info
, this);
539 for(int i
= element
->params_count
; i
< children
.count(); i
++)
540 children
[i
]->render(info
, dynamic_cast<RBViewport
*>(rendered
));
544 /* This version is called for logical lines, tags, conditionals and such */
545 void ParseTreeNode::render(const RBRenderInfo
&info
, RBViewport
* viewport
,
551 if(element
->type
== LINE
)
553 for(int i
= 0; i
< children
.count(); i
++)
554 children
[i
]->render(info
, viewport
);
555 if(!noBreak
&& !breakFlag
)
558 viewport
->flushText();
563 else if(element
->type
== TEXT
)
565 viewport
->write(QString(static_cast<char*>(element
->data
)));
567 else if(element
->type
== TAG
)
569 if(!execTag(info
, viewport
))
570 viewport
->write(evalTag(info
).toString());
571 if(element
->tag
->flags
& NOBREAK
)
574 else if(element
->type
== CONDITIONAL
)
576 int child
= evalTag(info
, true, element
->children_count
).toInt();
577 int max
= children
.count() - element
->params_count
;
580 children
[element
->params_count
+ child
]
581 ->render(info
, viewport
, true);
584 else if(element
->type
== LINE_ALTERNATOR
)
586 /* First we build a list of the times for each branch */
588 for(int i
= 0; i
< children
.count() ; i
++)
589 times
.append(findBranchTime(children
[i
], info
));
591 double totalTime
= 0;
592 for(int i
= 0; i
< children
.count(); i
++)
593 totalTime
+= times
[i
];
595 /* Now we figure out which branch to select */
596 double timeLeft
= info
.device()->data(QString("simtime")).toDouble();
598 /* Skipping any full cycles */
599 timeLeft
-= totalTime
* std::floor(timeLeft
/ totalTime
);
604 timeLeft
-= times
[branch
];
609 if(branch
>= times
.count())
613 /* In case we end up on a disabled branch, skip ahead. If we find that
614 * all the branches are disabled, don't render anything
616 int originalBranch
= branch
;
617 while(times
[branch
] == 0)
620 if(branch
== originalBranch
)
625 if(branch
>= times
.count())
629 /* ...and finally render the selected branch */
631 children
[branch
]->render(info
, viewport
, true);
635 bool ParseTreeNode::execTag(const RBRenderInfo
& info
, RBViewport
* viewport
)
640 int x
, y
, tiles
, tile
, maxWidth
, maxHeight
, width
, height
;
641 char c
, hAlign
, vAlign
;
646 /* Two switch statements to narrow down the tag name */
647 switch(element
->tag
->name
[0])
651 switch(element
->tag
->name
[1])
655 viewport
->alignText(RBViewport::Center
);
660 viewport
->alignText(RBViewport::Left
);
665 viewport
->alignText(RBViewport::Right
);
670 info
.screen()->RtlMirror();
675 if(info
.device()->data("rtl").toBool())
676 viewport
->alignText(RBViewport::Right
);
678 viewport
->alignText(RBViewport::Left
);
683 if(info
.device()->data("rtl").toBool())
684 viewport
->alignText(RBViewport::Left
);
686 viewport
->alignText(RBViewport::Right
);
693 switch(element
->tag
->name
[1])
697 new RBProgressBar(viewport
, info
, this);
702 if(element
->params_count
> 0)
704 new RBProgressBar(viewport
, info
, this, true);
714 switch(element
->tag
->name
[1])
718 viewport
->scrollText(info
.device()->data("simtime").toDouble());
725 switch(element
->tag
->name
[1])
729 info
.screen()->breakSBS();
734 /* Totally extraneous */
739 viewport
->enableStatusBar();
746 switch(element
->tag
->name
[1])
751 /* Breaking up into cases, getting the id first */
752 if(element
->params_count
== 1)
754 /* The old fashioned style */
756 id
.append(element
->params
[0].data
.text
[0]);
760 id
= QString(element
->params
[0].data
.text
);
764 if(info
.screen()->getImage(id
))
766 /* Fetching the image if available */
767 image
= new RBImage(*(info
.screen()->getImage(id
)), viewport
);
774 /* Now determining the particular tile to load */
775 if(element
->params_count
== 1)
777 c
= element
->params
[0].data
.text
[1];
792 /* If the next param is just an int, use it as the tile */
793 if(element
->params
[1].type
== skin_tag_parameter::INTEGER
)
795 tile
= element
->params
[1].data
.number
- 1;
799 tile
= children
[1]->evalTag(info
, true,
800 image
->numTiles()).toInt();
802 /* Adding the offset if there is one */
803 if(element
->params_count
== 3)
804 tile
+= element
->params
[2].data
.number
;
807 /* If there is no image for the current status, then
808 * just refrain from showing anything
818 image
->setTile(tile
);
820 image
->enableMovement();
827 id
= element
->params
[0].data
.text
;
828 if(element
->params
[1].data
.text
== QString("__list_icons__"))
830 filename
= info
.settings()->value("iconset", "");
831 filename
.replace(".rockbox",
832 info
.settings()->value("themebase"));
836 tiles
= temp
.height() / temp
.width();
841 filename
= info
.settings()->value("imagepath", "") + "/" +
842 element
->params
[1].data
.text
;
845 x
= element
->params
[2].data
.number
;
846 y
= element
->params
[3].data
.number
;
847 if(element
->params_count
> 4)
848 tiles
= element
->params
[4].data
.number
;
850 info
.screen()->loadImage(id
, new RBImage(filename
, tiles
, x
, y
,
856 id
= element
->params
[0].data
.text
;
857 filename
= info
.settings()->value("imagepath", "") + "/" +
858 element
->params
[1].data
.text
;
859 x
= element
->params
[2].data
.number
;
860 y
= element
->params
[3].data
.number
;
861 image
= new RBImage(filename
, 1, x
, y
, this, viewport
);
862 info
.screen()->loadImage(id
, image
);
864 image
->enableMovement();
873 switch(element
->tag
->name
[1])
877 info
.screen()->showAlbumArt(viewport
);
882 x
= element
->params
[0].data
.number
;
883 y
= element
->params
[1].data
.number
;
884 maxWidth
= element
->params
[2].data
.number
;
885 maxHeight
= element
->params
[3].data
.number
;
886 hAlign
= element
->params_count
> 4
887 ? element
->params
[4].data
.text
[0] : 'c';
888 vAlign
= element
->params_count
> 5
889 ? element
->params
[5].data
.text
[0] : 'c';
890 width
= info
.device()->data("artwidth").toInt();
891 height
= info
.device()->data("artheight").toInt();
892 info
.screen()->setAlbumArt(new RBAlbumArt(viewport
, x
, y
, maxWidth
,
893 maxHeight
, width
, height
,
894 this, hAlign
, vAlign
));
902 switch(element
->tag
->name
[1])
907 x
= element
->params
[0].data
.number
;
908 filename
= info
.settings()->value("themebase", "") + "/fonts/" +
909 element
->params
[1].data
.text
;
910 fLoad
= new RBFont(filename
);
911 if(!fLoad
->isValid())
912 dynamic_cast<RBScene
*>(info
.screen()->scene())
913 ->addWarning(QObject::tr("Missing font file: ") + filename
);
914 info
.screen()->loadFont(x
, fLoad
);
922 switch(element
->tag
->name
[1])
926 if(element
->params_count
< 5)
928 int x
= element
->params
[0].data
.number
;
929 int y
= element
->params
[1].data
.number
;
930 int width
= element
->params
[2].data
.number
;
931 int height
= element
->params
[3].data
.number
;
932 QString
action(element
->params
[4].data
.text
);
933 RBTouchArea
* temp
= new RBTouchArea(width
, height
, action
, info
,
943 switch(element
->tag
->name
[1])
948 viewport
->setBGColor(RBScreen::
949 stringToColor(QString(element
->params
[0].
956 id
= element
->params
[0].data
.text
;
957 info
.screen()->showViewport(id
);
962 viewport
->setFGColor(RBScreen::
963 stringToColor(QString(element
->params
[0].
970 viewport
->showPlaylist(info
, element
->params
[0].data
.number
,
976 info
.screen()->makeCustomUI(element
->params
[0].data
.text
);
985 switch(element
->tag
->name
[1])
989 filename
= QString(element
->params
[0].data
.text
);
990 info
.screen()->setBackdrop(filename
);
1002 QVariant
ParseTreeNode::evalTag(const RBRenderInfo
& info
, bool conditional
,
1007 if(element
->tag
->name
[0] == 'c' && !info
.device()->data("cc").toBool())
1010 if(QString(element
->tag
->name
) == "Sx")
1011 return element
->params
[0].data
.text
;
1012 return info
.device()->data(QString(element
->tag
->name
),
1013 element
->params_count
, element
->params
);
1017 /* If we're evaluating for a conditional, we return the child branch
1018 * index that should be selected. For true/false values, this is
1019 * 0 for true, 1 for false, and we also have to make sure not to
1020 * ever exceed the number of available children
1024 QVariant val
= info
.device()->data("?" + QString(element
->tag
->name
));
1026 val
= info
.device()->data(QString(element
->tag
->name
),
1027 element
->params_count
, element
->params
);
1033 else if(QString(element
->tag
->name
) == "bl")
1035 /* bl has to be scaled to the number of available children, but it
1036 * also has an initial -1 value for an unknown state */
1037 child
= val
.toInt();
1044 child
= ((branches
- 1) * child
/ 100) + 1;
1047 else if(QString(element
->tag
->name
) == "pv")
1049 /* ?pv gets scaled to the number of available children, sandwiched
1050 * in between mute and 0/>0dB. I assume a floor of -50dB for the
1053 int dB
= val
.toInt();
1058 child
= branches
- 2;
1060 child
= branches
- 1;
1063 int options
= branches
- 3;
1064 child
= (options
* (dB
+ 50)) / 50;
1067 else if(QString(element
->tag
->name
) == "px")
1069 child
= val
.toInt();
1070 child
= branches
* child
/ 100;
1072 else if(val
.type() == QVariant::Bool
)
1074 /* Boolean values have to be reversed, because conditionals are
1075 * always of the form %?tag<true|false>
1082 else if(element
->tag
->name
[0] == 'i' || element
->tag
->name
[0] == 'I'
1083 || element
->tag
->name
[0] == 'f' || element
->tag
->name
[0] == 'F')
1085 if(info
.device()->data("id3available").toBool())
1090 else if(val
.type() == QVariant::String
)
1092 if(val
.toString().length() > 0)
1099 child
= val
.toInt();
1105 if(child
< branches
)
1107 else if(branches
== 1)
1110 return branches
- 1;
1114 double ParseTreeNode::findBranchTime(ParseTreeNode
*branch
,
1115 const RBRenderInfo
& info
)
1118 for(int i
= 0; i
< branch
->children
.count(); i
++)
1120 ParseTreeNode
* current
= branch
->children
[i
];
1121 if(current
->element
->type
== TAG
)
1123 if(current
->element
->tag
->name
[0] == 't'
1124 && current
->element
->tag
->name
[1] == '\0')
1126 retval
= current
->element
->params
[0].data
.number
/ 10.;
1129 else if(current
->element
->type
== CONDITIONAL
)
1131 retval
= findConditionalTime(current
, info
);
1137 double ParseTreeNode::findConditionalTime(ParseTreeNode
*conditional
,
1138 const RBRenderInfo
& info
)
1140 int child
= conditional
->evalTag(info
, true,
1141 conditional
->children
.count()).toInt();
1142 if(child
>= conditional
->children
.count())
1143 child
= conditional
->children
.count() - 1;
1145 return findBranchTime(conditional
->children
[child
], info
);
1148 void ParseTreeNode::modParam(QVariant value
, int index
)
1154 while(index
>= children
.count())
1156 /* Padding children with defaults until we make the necessary
1157 * parameter available
1159 skin_tag_parameter
* newParam
= new skin_tag_parameter
;
1160 newParam
->type
= skin_tag_parameter::DEFAULT
;
1161 /* We'll need to manually delete the extra parameters in the
1164 extraParams
.append(children
.count());
1166 children
.append(new ParseTreeNode(newParam
, this, model
));
1167 element
->params_count
++;
1170 children
[index
]->modParam(value
);
1174 if(value
.type() == QVariant::Double
)
1176 param
->type
= skin_tag_parameter::DECIMAL
;
1177 param
->data
.number
= static_cast<int>(value
.toDouble() * 10);
1179 else if(value
.type() == QVariant::String
)
1181 param
->type
= skin_tag_parameter::STRING
;
1182 free(param
->data
.text
);
1183 param
->data
.text
= strdup(value
.toString().toStdString().c_str());
1185 else if(value
.type() == QVariant::Int
)
1187 param
->type
= skin_tag_parameter::INTEGER
;
1188 param
->data
.number
= value
.toInt();
1191 model
->paramChanged(this);