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"
32 int ParseTreeNode::openConditionals
= 0;
34 /* Root element constructor */
35 ParseTreeNode::ParseTreeNode(struct skin_element
* data
)
36 : parent(0), element(0), param(0), children()
40 children
.append(new ParseTreeNode(data
, this));
45 /* Normal element constructor */
46 ParseTreeNode::ParseTreeNode(struct skin_element
* data
, ParseTreeNode
* parent
)
47 : parent(parent
), element(data
), param(0), children()
53 for(int i
= 0; i
< element
->params_count
; i
++)
55 if(element
->params
[i
].type
== skin_tag_parameter::CODE
)
56 children
.append(new ParseTreeNode(element
->params
[i
].data
.code
,
59 children
.append(new ParseTreeNode(&element
->params
[i
], this));
64 for(int i
= 0; i
< element
->params_count
; i
++)
65 children
.append(new ParseTreeNode(&data
->params
[i
], this));
66 for(int i
= 0; i
< element
->children_count
; i
++)
67 children
.append(new ParseTreeNode(data
->children
[i
], this));
71 for(int i
= 0; i
< element
->children_count
; i
++)
73 children
.append(new ParseTreeNode(data
->children
[i
], this));
78 for(int i
= 0; i
< element
->params_count
; i
++)
79 children
.append(new ParseTreeNode(&data
->params
[i
], this));
80 /* Deliberate fall-through here */
83 for(int i
= 0; i
< data
->children_count
; i
++)
85 for(struct skin_element
* current
= data
->children
[i
]; current
;
86 current
= current
->next
)
88 children
.append(new ParseTreeNode(current
, this));
98 /* Parameter constructor */
99 ParseTreeNode::ParseTreeNode(skin_tag_parameter
*data
, ParseTreeNode
*parent
)
100 : parent(parent
), element(0), param(data
), children()
105 ParseTreeNode::~ParseTreeNode()
107 for(int i
= 0; i
< children
.count(); i
++)
111 QString
ParseTreeNode::genCode() const
117 switch(element
->type
)
122 /* Generating the Viewport tag, if necessary */
125 buffer
.append(TAGSYM
);
126 buffer
.append(element
->tag
->name
);
127 buffer
.append(ARGLISTOPENSYM
);
128 for(int i
= 0; i
< element
->params_count
; i
++)
130 buffer
.append(children
[i
]->genCode());
131 if(i
!= element
->params_count
- 1)
132 buffer
.append(ARGLISTSEPERATESYM
);
134 buffer
.append(ARGLISTCLOSESYM
);
138 for(int i
= element
->params_count
; i
< children
.count(); i
++)
139 buffer
.append(children
[i
]->genCode());
143 for(int i
= 0; i
< children
.count(); i
++)
145 buffer
.append(children
[i
]->genCode());
147 if(openConditionals
== 0
148 && !(parent
&& parent
->element
->type
== SUBLINES
))
155 for(int i
= 0; i
< children
.count(); i
++)
157 buffer
.append(children
[i
]->genCode());
158 if(i
!= children
.count() - 1)
159 buffer
.append(MULTILINESYM
);
161 if(openConditionals
== 0)
168 /* Inserting the tag part */
169 buffer
.append(TAGSYM
);
170 buffer
.append(CONDITIONSYM
);
171 buffer
.append(element
->tag
->name
);
172 if(element
->params_count
> 0)
174 buffer
.append(ARGLISTOPENSYM
);
175 for(int i
= 0; i
< element
->params_count
; i
++)
177 buffer
.append(children
[i
]->genCode());
178 if( i
!= element
->params_count
- 1)
179 buffer
.append(ARGLISTSEPERATESYM
);
180 buffer
.append(ARGLISTCLOSESYM
);
184 /* Inserting the sublines */
185 buffer
.append(ENUMLISTOPENSYM
);
186 for(int i
= element
->params_count
; i
< children
.count(); i
++)
188 buffer
.append(children
[i
]->genCode());
189 if(i
!= children
.count() - 1)
190 buffer
.append(ENUMLISTSEPERATESYM
);
192 buffer
.append(ENUMLISTCLOSESYM
);
197 buffer
.append(TAGSYM
);
198 buffer
.append(element
->tag
->name
);
200 if(element
->params_count
> 0)
202 /* Rendering parameters if there are any */
203 buffer
.append(ARGLISTOPENSYM
);
204 for(int i
= 0; i
< children
.count(); i
++)
206 buffer
.append(children
[i
]->genCode());
207 if(i
!= children
.count() - 1)
208 buffer
.append(ARGLISTSEPERATESYM
);
210 buffer
.append(ARGLISTCLOSESYM
);
215 for(char* cursor
= (char*)element
->data
; *cursor
; cursor
++)
217 if(find_escape_character(*cursor
))
218 buffer
.append(TAGSYM
);
219 buffer
.append(*cursor
);
224 buffer
.append(COMMENTSYM
);
225 buffer
.append((char*)element
->data
);
234 case skin_tag_parameter::STRING
:
235 for(char* cursor
= param
->data
.text
; *cursor
; cursor
++)
237 if(find_escape_character(*cursor
))
238 buffer
.append(TAGSYM
);
239 buffer
.append(*cursor
);
243 case skin_tag_parameter::NUMERIC
:
244 buffer
.append(QString::number(param
->data
.numeric
, 10));
247 case skin_tag_parameter::DEFAULT
:
248 buffer
.append(DEFAULTSYM
);
251 case skin_tag_parameter::CODE
:
252 buffer
.append(QObject::tr("This doesn't belong here"));
259 for(int i
= 0; i
< children
.count(); i
++)
260 buffer
.append(children
[i
]->genCode());
266 /* A more or less random hashing algorithm */
267 int ParseTreeNode::genHash() const
274 hash
+= element
->type
;
275 switch(element
->type
)
283 hash
+= element
->children_count
;
287 for(unsigned int i
= 0; i
< strlen(element
->tag
->name
); i
++)
288 hash
+= element
->tag
->name
[i
];
293 text
= (char*)element
->data
;
294 for(unsigned int i
= 0; i
< strlen(text
); i
++)
297 hash
+= text
[i
] % element
->type
;
299 hash
+= text
[i
] % element
->type
* 2;
311 case skin_tag_parameter::DEFAULT
:
312 case skin_tag_parameter::CODE
:
315 case skin_tag_parameter::NUMERIC
:
316 hash
+= param
->data
.numeric
* (param
->data
.numeric
/ 4);
319 case skin_tag_parameter::STRING
:
320 for(unsigned int i
= 0; i
< strlen(param
->data
.text
); i
++)
323 hash
+= param
->data
.text
[i
] * 2;
325 hash
+= param
->data
.text
[i
];
331 for(int i
= 0; i
< children
.count(); i
++)
333 hash
+= children
[i
]->genHash();
339 ParseTreeNode
* ParseTreeNode::child(int row
)
341 if(row
< 0 || row
>= children
.count())
344 return children
[row
];
347 int ParseTreeNode::numChildren() const
349 return children
.count();
353 QVariant
ParseTreeNode::data(int column
) const
357 case ParseTreeModel::typeColumn
:
360 switch(element
->type
)
363 return QObject::tr("Unknown");
365 return QObject::tr("Viewport");
368 return QObject::tr("Logical Line");
371 return QObject::tr("Alternator");
374 return QObject::tr("Comment");
377 return QObject::tr("Conditional Tag");
380 return QObject::tr("Tag");
383 return QObject::tr("Plaintext");
390 case skin_tag_parameter::STRING
:
391 return QObject::tr("String");
393 case skin_tag_parameter::NUMERIC
:
394 return QObject::tr("Number");
396 case skin_tag_parameter::DEFAULT
:
397 return QObject::tr("Default Argument");
399 case skin_tag_parameter::CODE
:
400 return QObject::tr("This doesn't belong here");
405 return QObject::tr("Root");
410 case ParseTreeModel::valueColumn
:
413 switch(element
->type
)
422 return QString(element
->tag
->name
);
426 return QString((char*)element
->data
);
429 return QString(element
->tag
->name
);
436 case skin_tag_parameter::DEFAULT
:
437 return QObject::tr("-");
439 case skin_tag_parameter::STRING
:
440 return QString(param
->data
.text
);
442 case skin_tag_parameter::NUMERIC
:
443 return QString::number(param
->data
.numeric
, 10);
445 case skin_tag_parameter::CODE
:
446 return QObject::tr("Seriously, something's wrong here");
455 case ParseTreeModel::lineColumn
:
457 return QString::number(element
->line
, 10);
467 int ParseTreeNode::getRow() const
472 return parent
->children
.indexOf(const_cast<ParseTreeNode
*>(this));
475 ParseTreeNode
* ParseTreeNode::getParent() const
480 /* This version is called for the root node and for viewports */
481 void ParseTreeNode::render(const RBRenderInfo
& info
)
483 /* Parameters don't get rendered */
484 if(!element
&& param
)
487 /* If we're at the root, we need to render each viewport */
488 if(!element
&& !param
)
490 for(int i
= 0; i
< children
.count(); i
++)
492 children
[i
]->render(info
);
498 if(element
->type
!= VIEWPORT
)
500 std::cerr
<< QObject::tr("Error in parse tree").toStdString()
505 rendered
= new RBViewport(element
, info
);
507 for(int i
= element
->params_count
; i
< children
.count(); i
++)
508 children
[i
]->render(info
, dynamic_cast<RBViewport
*>(rendered
));
512 /* This version is called for logical lines, tags, conditionals and such */
513 void ParseTreeNode::render(const RBRenderInfo
&info
, RBViewport
* viewport
,
516 if(element
->type
== LINE
)
518 for(int i
= 0; i
< children
.count(); i
++)
519 children
[i
]->render(info
, viewport
);
523 else if(element
->type
== TEXT
)
525 viewport
->write(QString(static_cast<char*>(element
->data
)));
527 else if(element
->type
== TAG
)
529 if(!execTag(info
, viewport
))
530 viewport
->write(evalTag(info
).toString());
532 else if(element
->type
== CONDITIONAL
)
534 int child
= evalTag(info
, true, element
->children_count
).toInt();
535 children
[element
->params_count
+ child
]->render(info
, viewport
, true);
537 else if(element
->type
== SUBLINES
)
539 /* First we build a list of the times for each branch */
541 for(int i
= 0; i
< children
.count() ; i
++)
542 times
.append(findBranchTime(children
[i
], info
));
544 /* Now we figure out which branch to select */
545 double timeLeft
= info
.device()->data(QString("?pc")).toDouble();
549 timeLeft
-= times
[branch
];
554 if(branch
>= times
.count())
558 /* In case we end up on a disabled branch, skip ahead. If we find that
559 * all the branches are disabled, don't render anything
561 int originalBranch
= branch
;
562 while(times
[branch
] == 0)
565 if(branch
== originalBranch
)
570 if(branch
>= times
.count())
574 /* ...and finally render the selected branch */
576 children
[branch
]->render(info
, viewport
, true);
580 bool ParseTreeNode::execTag(const RBRenderInfo
& info
, RBViewport
* viewport
)
585 int x
, y
, tiles
, tile
, maxWidth
, maxHeight
, width
, height
;
586 char c
, hAlign
, vAlign
;
589 /* Two switch statements to narrow down the tag name */
590 switch(element
->tag
->name
[0])
594 switch(element
->tag
->name
[1])
598 viewport
->alignText(RBViewport::Center
);
603 viewport
->alignText(RBViewport::Left
);
608 viewport
->alignText(RBViewport::Right
);
615 switch(element
->tag
->name
[1])
618 info
.screen()->disableStatusBar();
622 info
.screen()->enableStatusBar();
626 info
.screen()->disableStatusBar();
627 viewport
->enableStatusBar();
634 switch(element
->tag
->name
[1])
639 id
.append(element
->params
[0].data
.text
[0]);
640 c
= element
->params
[0].data
.text
[1];
654 if(info
.screen()->getImage(id
))
656 image
= new RBImage(*(info
.screen()->getImage(id
)), viewport
);
657 image
->setTile(tile
);
665 id
= element
->params
[0].data
.text
;
666 filename
= info
.settings()->value("imagepath", "") + "/" +
667 element
->params
[1].data
.text
;
668 x
= element
->params
[2].data
.numeric
;
669 y
= element
->params
[3].data
.numeric
;
670 if(element
->params_count
> 4)
671 tiles
= element
->params
[4].data
.numeric
;
675 info
.screen()->loadImage(id
, new RBImage(filename
, tiles
, x
, y
,
681 id
= element
->params
[0].data
.text
;
682 filename
= info
.settings()->value("imagepath", "") + "/" +
683 element
->params
[1].data
.text
;
684 x
= element
->params
[2].data
.numeric
;
685 y
= element
->params
[3].data
.numeric
;
686 image
= new RBImage(filename
, 1, x
, y
, viewport
);
687 info
.screen()->loadImage(id
, new RBImage(filename
, 1, x
, y
,
689 info
.screen()->getImage(id
)->show();
697 switch(element
->tag
->name
[1])
701 info
.screen()->showAlbumArt(viewport
);
706 x
= element
->params
[0].data
.numeric
;
707 y
= element
->params
[1].data
.numeric
;
708 maxWidth
= element
->params
[2].data
.numeric
;
709 maxHeight
= element
->params
[3].data
.numeric
;
710 hAlign
= element
->params_count
> 4
711 ? element
->params
[4].data
.text
[0] : 'c';
712 vAlign
= element
->params_count
> 5
713 ? element
->params
[5].data
.text
[0] : 'c';
714 width
= info
.device()->data("artwidth").toInt();
715 height
= info
.device()->data("artheight").toInt();
716 info
.screen()->setAlbumArt(new RBAlbumArt(viewport
, x
, y
, maxWidth
,
717 maxHeight
, width
, height
,
726 switch(element
->tag
->name
[1])
731 x
= element
->params
[0].data
.numeric
;
732 filename
= info
.settings()->value("themebase", "") + "/fonts/" +
733 element
->params
[1].data
.text
;
734 info
.screen()->loadFont(x
, new RBFont(filename
));
743 switch(element
->tag
->name
[1])
748 viewport
->setBGColor(RBScreen::
749 stringToColor(QString(element
->params
[0].
756 id
= element
->params
[0].data
.text
;
757 info
.screen()->showViewport(id
);
762 viewport
->setFGColor(RBScreen::
763 stringToColor(QString(element
->params
[0].
770 info
.screen()->makeCustomUI(element
->params
[0].data
.text
);
779 switch(element
->tag
->name
[1])
783 filename
= QString(element
->params
[0].data
.text
);
784 info
.screen()->setBackdrop(filename
);
796 QVariant
ParseTreeNode::evalTag(const RBRenderInfo
& info
, bool conditional
,
799 if(strcmp(element
->tag
->name
, "mv") == 0)
805 return info
.device()->data(QString(element
->tag
->name
));
809 /* If we're evaluating for a conditional, we return the child branch
810 * index that should be selected. For true/false values, this is
811 * 0 for true, 1 for false, and we also have to make sure not to
812 * ever exceed the number of available children
816 QVariant val
= info
.device()->data("?" + QString(element
->tag
->name
));
818 val
= info
.device()->data(QString(element
->tag
->name
));
824 else if(QString(element
->tag
->name
) == "bl")
826 /* bl has to be scaled to the number of available children, but it
827 * also has an initial -1 value for an unknown state */
835 child
= ((branches
- 1) * child
/ 100) + 1;
838 else if(QString(element
->tag
->name
) == "px")
841 child
= branches
* child
/ 100;
843 else if(val
.type() == QVariant::Bool
)
845 /* Boolean values have to be reversed, because conditionals are
846 * always of the form %?tag<true|false>
853 else if(val
.type() == QVariant::String
)
855 if(val
.toString().length() > 0)
875 double ParseTreeNode::findBranchTime(ParseTreeNode
*branch
,
876 const RBRenderInfo
& info
)
879 for(int i
= 0; i
< branch
->children
.count(); i
++)
881 ParseTreeNode
* current
= branch
->children
[i
];
882 if(current
->element
->type
== TAG
)
884 if(current
->element
->tag
->name
[0] == 't'
885 && current
->element
->tag
->name
[1] == '\0')
887 retval
= atof(current
->element
->params
[0].data
.text
);
890 else if(current
->element
->type
== CONDITIONAL
)
892 retval
= findConditionalTime(current
, info
);
898 double ParseTreeNode::findConditionalTime(ParseTreeNode
*conditional
,
899 const RBRenderInfo
& info
)
901 int child
= conditional
->evalTag(info
, true,
902 conditional
->children
.count()).toInt();
903 return findBranchTime(conditional
->children
[child
], info
);