Manual: Small English improvement
[maemo-rb.git] / utils / themeeditor / models / parsetreenode.cpp
blob1c7de7cc259db65a4d8732ebbf112b42ca1c8e82
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include "symbols.h"
23 #include "tag_table.h"
25 #include "parsetreenode.h"
26 #include "parsetreemodel.h"
28 #include "rbimage.h"
29 #include "rbprogressbar.h"
30 #include "rbtoucharea.h"
32 #include <iostream>
33 #include <cmath>
34 #include <cassert>
36 #include <QDebug>
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)
45 while(data)
47 children.append(new ParseTreeNode(data, this, model));
48 data = data->next;
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)
58 switch(element->type)
61 case TAG:
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,
66 this, model));
67 else
68 children.append(new ParseTreeNode(&element->params[i], this,
69 model));
71 break;
73 case CONDITIONAL:
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));
78 break;
80 case LINE_ALTERNATOR:
81 for(int i = 0; i < element->children_count; i++)
83 children.append(new ParseTreeNode(data->children[i], this, model));
85 break;
87 case VIEWPORT:
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 */
92 case LINE:
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));
101 break;
103 default:
104 break;
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++)
120 delete children[i];
123 QString ParseTreeNode::genCode() const
125 QString buffer = "";
127 if(element)
129 switch(element->type)
131 case UNKNOWN:
132 break;
133 case VIEWPORT:
134 /* Generating the Viewport tag, if necessary */
135 if(element->tag)
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);
147 buffer.append('\n');
150 for(int i = element->params_count; i < children.count(); i++)
151 buffer.append(children[i]->genCode());
152 break;
154 case LINE:
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
163 == COMMENT))
165 buffer.append('\n');
167 break;
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)
177 buffer.append('\n');
178 break;
180 case CONDITIONAL:
181 openConditionals++;
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);
208 openConditionals--;
209 break;
211 case TAG:
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')
228 buffer.append('\n');
229 break;
231 case TEXT:
232 for(char* cursor = (char*)element->data; *cursor; cursor++)
234 if(find_escape_character(*cursor))
235 buffer.append(TAGSYM);
236 buffer.append(*cursor);
238 break;
240 case COMMENT:
241 buffer.append(COMMENTSYM);
242 buffer.append((char*)element->data);
243 buffer.append('\n');
244 break;
247 else if(param)
249 switch(param->type)
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);
258 break;
260 case skin_tag_parameter::INTEGER:
261 buffer.append(QString::number(param->data.number, 10));
262 break;
264 case skin_tag_parameter::DECIMAL:
265 buffer.append(QString::number(param->data.number / 10., 'f', 1));
266 break;
268 case skin_tag_parameter::DEFAULT:
269 buffer.append(DEFAULTSYM);
270 break;
272 case skin_tag_parameter::CODE:
273 buffer.append(QObject::tr("This doesn't belong here"));
274 break;
278 else
280 for(int i = 0; i < children.count(); i++)
281 buffer.append(children[i]->genCode());
284 return buffer;
287 /* A more or less random hashing algorithm */
288 int ParseTreeNode::genHash() const
290 int hash = 0;
291 char *text;
293 if(element)
295 hash += element->type;
296 switch(element->type)
298 case UNKNOWN:
299 break;
300 case VIEWPORT:
301 case LINE:
302 case LINE_ALTERNATOR:
303 case CONDITIONAL:
304 hash += element->children_count;
305 break;
307 case TAG:
308 for(unsigned int i = 0; i < strlen(element->tag->name); i++)
309 hash += element->tag->name[i];
310 break;
312 case COMMENT:
313 case TEXT:
314 text = (char*)element->data;
315 for(unsigned int i = 0; i < strlen(text); i++)
317 if(i % 2)
318 hash += text[i] % element->type;
319 else
320 hash += text[i] % element->type * 2;
322 break;
327 if(param)
329 hash += param->type;
330 switch(param->type)
332 case skin_tag_parameter::DEFAULT:
333 case skin_tag_parameter::CODE:
334 break;
336 case skin_tag_parameter::INTEGER:
337 hash += param->data.number * (param->data.number / 4);
338 break;
340 case skin_tag_parameter::STRING:
341 for(unsigned int i = 0; i < strlen(param->data.text); i++)
343 if(i % 2)
344 hash += param->data.text[i] * 2;
345 else
346 hash += param->data.text[i];
348 break;
350 case skin_tag_parameter::DECIMAL:
351 hash += param->data.number;
352 break;
356 for(int i = 0; i < children.count(); i++)
358 hash += children[i]->genHash();
361 return hash;
364 ParseTreeNode* ParseTreeNode::child(int row)
366 if(row < 0 || row >= children.count())
367 return 0;
369 return children[row];
372 int ParseTreeNode::numChildren() const
374 return children.count();
378 QVariant ParseTreeNode::data(int column) const
380 switch(column)
382 case ParseTreeModel::typeColumn:
383 if(element)
385 switch(element->type)
387 case UNKNOWN:
388 return QObject::tr("Unknown");
389 case VIEWPORT:
390 return QObject::tr("Viewport");
392 case LINE:
393 return QObject::tr("Logical Line");
395 case LINE_ALTERNATOR:
396 return QObject::tr("Alternator");
398 case COMMENT:
399 return QObject::tr("Comment");
401 case CONDITIONAL:
402 return QObject::tr("Conditional Tag");
404 case TAG:
405 return QObject::tr("Tag");
407 case TEXT:
408 return QObject::tr("Plaintext");
411 else if(param)
413 switch(param->type)
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");
431 else
433 return QObject::tr("Root");
436 break;
438 case ParseTreeModel::valueColumn:
439 if(element)
441 switch(element->type)
443 case UNKNOWN:
444 case VIEWPORT:
445 case LINE:
446 case LINE_ALTERNATOR:
447 return QString();
449 case CONDITIONAL:
450 return QString(element->tag->name);
452 case TEXT:
453 case COMMENT:
454 return QString((char*)element->data);
456 case TAG:
457 return QString(element->tag->name);
460 else if(param)
462 switch(param->type)
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");
481 else
483 return QString();
485 break;
487 case ParseTreeModel::lineColumn:
488 if(element)
489 return QString::number(element->line, 10);
490 else
491 return QString();
492 break;
495 return QVariant();
499 int ParseTreeNode::getRow() const
501 if(!parent)
502 return -1;
504 return parent->children.indexOf(const_cast<ParseTreeNode*>(this));
507 ParseTreeNode* ParseTreeNode::getParent() const
509 return parent;
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)
517 return;
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);
527 return;
530 if(element->type != VIEWPORT)
532 std::cerr << QObject::tr("Error in parse tree").toStdString()
533 << std::endl;
534 return;
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,
546 bool noBreak)
548 if(!element)
549 return;
551 if(element->type == LINE)
553 for(int i = 0; i < children.count(); i++)
554 children[i]->render(info, viewport);
555 if(!noBreak && !breakFlag)
556 viewport->newLine();
557 else
558 viewport->flushText();
560 if(breakFlag)
561 breakFlag = false;
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)
572 breakFlag = true;
574 else if(element->type == CONDITIONAL)
576 int child = evalTag(info, true, element->children_count).toInt();
577 int max = children.count() - element->params_count;
578 if(child < max)
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 */
587 QList<double> times;
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);
601 int branch = 0;
602 while(timeLeft > 0)
604 timeLeft -= times[branch];
605 if(timeLeft >= 0)
606 branch++;
607 else
608 break;
609 if(branch >= times.count())
610 branch = 0;
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)
619 branch++;
620 if(branch == originalBranch)
622 branch = -1;
623 break;
625 if(branch >= times.count())
626 branch = 0;
629 /* ...and finally render the selected branch */
630 if(branch >= 0)
631 children[branch]->render(info, viewport, true);
635 bool ParseTreeNode::execTag(const RBRenderInfo& info, RBViewport* viewport)
638 QString filename;
639 QString id;
640 int x, y, tiles, tile, maxWidth, maxHeight, width, height;
641 char c, hAlign, vAlign;
642 RBImage* image;
643 QPixmap temp;
644 RBFont* fLoad;
646 /* Two switch statements to narrow down the tag name */
647 switch(element->tag->name[0])
650 case 'a':
651 switch(element->tag->name[1])
653 case 'c':
654 /* %ac */
655 viewport->alignText(RBViewport::Center);
656 return true;
658 case 'l':
659 /* %al */
660 viewport->alignText(RBViewport::Left);
661 return true;
663 case 'r':
664 /* %ar */
665 viewport->alignText(RBViewport::Right);
666 return true;
668 case 'x':
669 /* %ax */
670 info.screen()->RtlMirror();
671 return true;
673 case 'L':
674 /* %aL */
675 if(info.device()->data("rtl").toBool())
676 viewport->alignText(RBViewport::Right);
677 else
678 viewport->alignText(RBViewport::Left);
679 return true;
681 case 'R':
682 /* %aR */
683 if(info.device()->data("rtl").toBool())
684 viewport->alignText(RBViewport::Left);
685 else
686 viewport->alignText(RBViewport::Right);
687 return true;
690 return false;
692 case 'p':
693 switch(element->tag->name[1])
695 case 'b':
696 /* %pb */
697 new RBProgressBar(viewport, info, this);
698 return true;
700 case 'v':
701 /* %pv */
702 if(element->params_count > 0)
704 new RBProgressBar(viewport, info, this, true);
705 return true;
707 else
708 return false;
711 return false;
713 case 's':
714 switch(element->tag->name[1])
716 case '\0':
717 /* %s */
718 viewport->scrollText(info.device()->data("simtime").toDouble());
719 return true;
722 return false;
724 case 'w':
725 switch(element->tag->name[1])
727 case 'd':
728 /* %wd */
729 info.screen()->breakSBS();
730 return true;
732 case 'e':
733 /* %we */
734 /* Totally extraneous */
735 return true;
737 case 'i':
738 /* %wi */
739 viewport->enableStatusBar();
740 return true;
743 return false;
745 case 'x':
746 switch(element->tag->name[1])
748 case 'd':
749 /* %xd */
751 /* Breaking up into cases, getting the id first */
752 if(element->params_count == 1)
754 /* The old fashioned style */
755 id = "";
756 id.append(element->params[0].data.text[0]);
758 else
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);
769 else
771 image = 0;
774 /* Now determining the particular tile to load */
775 if(element->params_count == 1)
777 c = element->params[0].data.text[1];
779 if(c == '\0')
781 tile = 1;
783 else
785 if(isupper(c))
786 tile = c - 'A' + 25;
787 else
788 tile = c - 'a';
791 }else{
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;
797 else
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;
805 if(tile < 0)
807 /* If there is no image for the current status, then
808 * just refrain from showing anything
810 delete image;
811 return true;
816 if(image)
818 image->setTile(tile);
819 image->show();
820 image->enableMovement();
823 return true;
825 case 'l':
826 /* %xl */
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"));
833 temp.load(filename);
834 if(!temp.isNull())
836 tiles = temp.height() / temp.width();
839 else
841 filename = info.settings()->value("imagepath", "") + "/" +
842 element->params[1].data.text;
843 tiles = 1;
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,
851 this, viewport));
852 return true;
854 case '\0':
855 /* %x */
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);
863 image->show();
864 image->enableMovement();
866 return true;
870 return false;
872 case 'C':
873 switch(element->tag->name[1])
875 case 'd':
876 /* %Cd */
877 info.screen()->showAlbumArt(viewport);
878 return true;
880 case 'l':
881 /* %Cl */
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));
895 return true;
898 return false;
900 case 'F':
902 switch(element->tag->name[1])
905 case 'l':
906 /* %Fl */
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);
915 return true;
919 return false;
921 case 'T':
922 switch(element->tag->name[1])
924 case '\0':
925 /* %T */
926 if(element->params_count < 5)
927 return false;
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,
934 viewport);
935 temp->setPos(x, y);
936 return true;
939 return false;
941 case 'V':
943 switch(element->tag->name[1])
946 case 'b':
947 /* %Vb */
948 viewport->setBGColor(RBScreen::
949 stringToColor(QString(element->params[0].
950 data.text),
951 Qt::white));
952 return true;
954 case 'd':
955 /* %Vd */
956 id = element->params[0].data.text;
957 info.screen()->showViewport(id);
958 return true;
960 case 'f':
961 /* %Vf */
962 viewport->setFGColor(RBScreen::
963 stringToColor(QString(element->params[0].
964 data.text),
965 Qt::black));
966 return true;
968 case 'p':
969 /* %Vp */
970 viewport->showPlaylist(info, element->params[0].data.number,
971 children[1]);
972 return true;
974 case 'I':
975 /* %VI */
976 info.screen()->makeCustomUI(element->params[0].data.text);
977 return true;
981 return false;
983 case 'X':
985 switch(element->tag->name[1])
987 case '\0':
988 /* %X */
989 filename = QString(element->params[0].data.text);
990 info.screen()->setBackdrop(filename);
991 return true;
994 return false;
998 return false;
1002 QVariant ParseTreeNode::evalTag(const RBRenderInfo& info, bool conditional,
1003 int branches)
1005 if(!conditional)
1007 if(element->tag->name[0] == 'c' && !info.device()->data("cc").toBool())
1008 return QString();
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);
1015 else
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
1023 int child;
1024 QVariant val = info.device()->data("?" + QString(element->tag->name));
1025 if(val.isNull())
1026 val = info.device()->data(QString(element->tag->name),
1027 element->params_count, element->params);
1029 if(val.isNull())
1031 child = 1;
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();
1038 if(child == -1)
1040 child = 0;
1042 else
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
1051 * time being
1053 int dB = val.toInt();
1055 if(dB < -50)
1056 child = 0;
1057 else if(dB == 0)
1058 child = branches - 2;
1059 else if(dB > 0)
1060 child = branches - 1;
1061 else
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>
1077 if(val.toBool())
1078 child = 0;
1079 else
1080 child = 1;
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())
1086 child = 0;
1087 else
1088 child = 1;
1090 else if(val.type() == QVariant::String)
1092 if(val.toString().length() > 0)
1093 child = 0;
1094 else
1095 child = 1;
1097 else
1099 child = val.toInt();
1102 if(child < 0)
1103 child = 0;
1105 if(child < branches)
1106 return child;
1107 else if(branches == 1)
1108 return 2;
1109 else
1110 return branches - 1;
1114 double ParseTreeNode::findBranchTime(ParseTreeNode *branch,
1115 const RBRenderInfo& info)
1117 double retval = 2;
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);
1134 return retval;
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)
1150 if(element)
1152 if(index < 0)
1153 return;
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
1162 * destructor
1164 extraParams.append(children.count());
1166 children.append(new ParseTreeNode(newParam, this, model));
1167 element->params_count++;
1170 children[index]->modParam(value);
1172 else if(param)
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);