1// Aseprite
2// Copyright (C) 2019-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/ui/pref_widget.h"
13
14#include "app/widget_loader.h"
15
16#include "app/app.h"
17#include "app/i18n/strings.h"
18#include "app/modules/gui.h"
19#include "app/resource_finder.h"
20#include "app/ui/button_set.h"
21#include "app/ui/color_button.h"
22#include "app/ui/drop_down_button.h"
23#include "app/ui/expr_entry.h"
24#include "app/ui/icon_button.h"
25#include "app/ui/search_entry.h"
26#include "app/ui/skin/skin_theme.h"
27#include "app/widget_not_found.h"
28#include "app/xml_document.h"
29#include "app/xml_exception.h"
30#include "base/exception.h"
31#include "base/fs.h"
32#include "base/memory.h"
33#include "os/system.h"
34#include "ui/ui.h"
35
36#include "tinyxml.h"
37
38#include <cstdio>
39#include <cstdlib>
40#include <cstring>
41#include <limits>
42#include <memory>
43
44namespace app {
45
46using namespace ui;
47using namespace app::skin;
48
49static int convert_align_value_to_flags(const char *value);
50static int int_attr(const TiXmlElement* elem, const char* attribute_name, int default_value);
51
52WidgetLoader::WidgetLoader()
53 : m_tooltipManager(NULL)
54{
55}
56
57WidgetLoader::~WidgetLoader()
58{
59 for (TypeCreatorsMap::iterator
60 it=m_typeCreators.begin(), end=m_typeCreators.end(); it != end; ++it)
61 it->second->dispose();
62}
63
64void WidgetLoader::addWidgetType(const char* tagName, IWidgetTypeCreator* creator)
65{
66 m_typeCreators[tagName] = creator;
67}
68
69Widget* WidgetLoader::loadWidget(const char* fileName, const char* widgetId, ui::Widget* widget)
70{
71 std::string buf;
72
73 ResourceFinder rf;
74 rf.addPath(fileName);
75
76 buf = "widgets/";
77 buf += fileName;
78 rf.includeDataDir(buf.c_str());
79
80 if (!rf.findFirst())
81 throw WidgetNotFound(widgetId);
82
83 widget = loadWidgetFromXmlFile(rf.filename(), widgetId, widget);
84 if (!widget)
85 throw WidgetNotFound(widgetId);
86
87 return widget;
88}
89
90Widget* WidgetLoader::loadWidgetFromXmlFile(
91 const std::string& xmlFilename,
92 const std::string& widgetId,
93 ui::Widget* widget)
94{
95 m_tooltipManager = NULL;
96 m_xmlTranslator.setStringIdPrefix(widgetId.c_str());
97
98 XmlDocumentRef doc(open_xml(xmlFilename));
99 TiXmlHandle handle(doc.get());
100
101 // Search the requested widget.
102 TiXmlElement* xmlElement = handle
103 .FirstChild("gui")
104 .FirstChildElement().ToElement();
105
106 while (xmlElement) {
107 const char* nodename = xmlElement->Attribute("id");
108
109 if (nodename && nodename == widgetId) {
110 widget = convertXmlElementToWidget(xmlElement, NULL, NULL, widget);
111 break;
112 }
113
114 xmlElement = xmlElement->NextSiblingElement();
115 }
116
117 return widget;
118}
119
120Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget* root, Widget* parent, Widget* widget)
121{
122 const std::string elem_name = elem->Value();
123
124 // TODO error handling: add a message if the widget is bad specified
125
126 // Try to use one of the creators.
127 TypeCreatorsMap::iterator it = m_typeCreators.find(elem_name);
128
129 if (it != m_typeCreators.end()) {
130 if (!widget)
131 widget = it->second->createWidgetFromXml(elem);
132 }
133 else if (elem_name == "panel") {
134 if (!widget)
135 widget = new Panel();
136 }
137 else if (elem_name == "box") {
138 bool horizontal = bool_attr(elem, "horizontal", false);
139 bool vertical = bool_attr(elem, "vertical", false);
140 int align = (horizontal ? HORIZONTAL: vertical ? VERTICAL: 0);
141
142 if (!widget)
143 widget = new Box(align);
144 else
145 widget->setAlign(widget->align() | align);
146 }
147 else if (elem_name == "vbox") {
148 if (!widget)
149 widget = new VBox();
150 }
151 else if (elem_name == "hbox") {
152 if (!widget)
153 widget = new HBox();
154 }
155 else if (elem_name == "boxfiller") {
156 if (!widget)
157 widget = new BoxFiller();
158 }
159 else if (elem_name == "button") {
160 const char* icon_name = elem->Attribute("icon");
161
162 if (!widget) {
163 if (icon_name) {
164 SkinPartPtr part = SkinTheme::instance()->getPartById(icon_name);
165 if (!part)
166 throw base::Exception("<button> element found with invalid 'icon' attribute '%s'",
167 icon_name);
168
169 widget = new IconButton(part);
170 }
171 else {
172 widget = new Button("");
173 }
174 }
175
176 bool left = bool_attr(elem, "left", false);
177 bool right = bool_attr(elem, "right", false);
178 bool top = bool_attr(elem, "top", false);
179 bool bottom = bool_attr(elem, "bottom", false);
180 bool closewindow = bool_attr(elem, "closewindow", false);
181
182 widget->setAlign((left ? LEFT: (right ? RIGHT: CENTER)) |
183 (top ? TOP: (bottom ? BOTTOM: MIDDLE)));
184
185 if (closewindow) {
186 static_cast<Button*>(widget)
187 ->Click.connect([widget]{ widget->closeWindow(); });
188 }
189 }
190 else if (elem_name == "check") {
191 const char* looklike = elem->Attribute("looklike");
192 const char* pref = elem->Attribute("pref");
193
194 ASSERT(!widget || !pref); // widget && pref is not supported
195
196 if (looklike != NULL && strcmp(looklike, "button") == 0) {
197 ASSERT(!pref); // not supported yet
198
199 if (!widget)
200 widget = new CheckBox("", kButtonWidget);
201 }
202 else {
203 if (!widget) {
204 // Automatic bind <check> widget with bool preference option
205 if (pref) {
206 std::unique_ptr<BoolPrefWidget<CheckBox>> prefWidget(
207 new BoolPrefWidget<CheckBox>(""));
208 prefWidget->setPref(pref);
209 widget = prefWidget.release();
210 }
211 else {
212 widget = new CheckBox("");
213 }
214 }
215 }
216
217 bool center = bool_attr(elem, "center", false);
218 bool right = bool_attr(elem, "right", false);
219 bool top = bool_attr(elem, "top", false);
220 bool bottom = bool_attr(elem, "bottom", false);
221
222 widget->setAlign((center ? CENTER:
223 (right ? RIGHT: LEFT)) |
224 (top ? TOP:
225 (bottom ? BOTTOM: MIDDLE)));
226 }
227 else if (elem_name == "combobox") {
228 if (!widget)
229 widget = new ComboBox();
230
231 bool editable = bool_attr(elem, "editable", false);
232 if (editable)
233 ((ComboBox*)widget)->setEditable(true);
234
235 const char* suffix = elem->Attribute("suffix");
236 if (suffix)
237 ((ComboBox*)widget)->getEntryWidget()->setSuffix(suffix);
238 }
239 else if (elem_name == "entry" ||
240 elem_name == "expr") {
241 const char* maxsize = elem->Attribute("maxsize");
242 if (elem_name == "entry" && !maxsize)
243 throw std::runtime_error("<entry> element found without 'maxsize' attribute");
244
245 const char* suffix = elem->Attribute("suffix");
246 const char* decimals = elem->Attribute("decimals");
247 const bool readonly = bool_attr(elem, "readonly", false);
248
249 widget = (elem_name == "expr" ?
250 new ExprEntry:
251 new Entry(strtol(maxsize, nullptr, 10), ""));
252
253 if (readonly)
254 ((Entry*)widget)->setReadOnly(true);
255
256 if (suffix)
257 ((Entry*)widget)->setSuffix(suffix);
258
259 if (elem_name == "expr" && decimals)
260 ((ExprEntry*)widget)->setDecimals(strtol(decimals, nullptr, 10));
261 }
262 else if (elem_name == "grid") {
263 const char *columns = elem->Attribute("columns");
264 bool same_width_columns = bool_attr(elem, "same_width_columns", false);
265
266 if (columns != NULL) {
267 widget = new ui::Grid(strtol(columns, NULL, 10),
268 same_width_columns);
269 }
270 }
271 else if (elem_name == "label") {
272 if (!widget)
273 widget = new Label("");
274
275 bool center = bool_attr(elem, "center", false);
276 bool right = bool_attr(elem, "right", false);
277 bool top = bool_attr(elem, "top", false);
278 bool bottom = bool_attr(elem, "bottom", false);
279
280 widget->setAlign((center ? CENTER:
281 (right ? RIGHT: LEFT)) |
282 (top ? TOP:
283 (bottom ? BOTTOM: MIDDLE)));
284 }
285 else if (elem_name == "link") {
286 const char* url = elem->Attribute("url");
287
288 if (!widget)
289 widget = new LinkLabel(url ? url: "", "");
290 else {
291 LinkLabel* link = dynamic_cast<LinkLabel*>(widget);
292 ASSERT(link != NULL);
293 if (link)
294 link->setUrl(url);
295 }
296
297 bool center = bool_attr(elem, "center", false);
298 bool right = bool_attr(elem, "right", false);
299 bool top = bool_attr(elem, "top", false);
300 bool bottom = bool_attr(elem, "bottom", false);
301
302 widget->setAlign(
303 (center ? CENTER: (right ? RIGHT: LEFT)) |
304 (top ? TOP: (bottom ? BOTTOM: MIDDLE)));
305 }
306 else if (elem_name == "listbox") {
307 if (!widget)
308 widget = new ListBox();
309
310 bool multiselect = bool_attr(elem, "multiselect", false);
311 if (multiselect)
312 static_cast<ListBox*>(widget)->setMultiselect(multiselect);
313 }
314 else if (elem_name == "listitem") {
315 ListItem* listitem;
316 if (!widget) {
317 listitem = new ListItem("");
318 widget = listitem;
319 }
320 else {
321 listitem = dynamic_cast<ListItem*>(widget);
322 ASSERT(listitem != NULL);
323 }
324
325 const char* value = elem->Attribute("value");
326 if (value)
327 listitem->setValue(value);
328 }
329 else if (elem_name == "splitter") {
330 bool horizontal = bool_attr(elem, "horizontal", false);
331 bool vertical = bool_attr(elem, "vertical", false);
332 const char* by = elem->Attribute("by");
333 const char* position = elem->Attribute("position");
334 Splitter::Type type = (by && strcmp(by, "pixel") == 0 ?
335 Splitter::ByPixel:
336 Splitter::ByPercentage);
337
338 Splitter* splitter = new Splitter(type,
339 horizontal ? HORIZONTAL:
340 vertical ? VERTICAL: 0);
341 if (position) {
342 splitter->setPosition(strtod(position, NULL)
343 * (type == Splitter::ByPixel ? guiscale(): 1));
344 }
345 widget = splitter;
346 }
347 else if (elem_name == "radio") {
348 const char* group = elem->Attribute("group");
349 const char* looklike = elem->Attribute("looklike");
350
351 int radio_group = (group ? strtol(group, NULL, 10): 1);
352
353 if (!widget) {
354 if (looklike != NULL && strcmp(looklike, "button") == 0) {
355 widget = new RadioButton("", radio_group, kButtonWidget);
356 }
357 else {
358 widget = new RadioButton("", radio_group);
359 }
360 }
361 else {
362 RadioButton* radio = dynamic_cast<RadioButton*>(widget);
363 ASSERT(radio != NULL);
364 if (radio)
365 radio->setRadioGroup(radio_group);
366 }
367
368 bool center = bool_attr(elem, "center", false);
369 bool right = bool_attr(elem, "right", false);
370 bool top = bool_attr(elem, "top", false);
371 bool bottom = bool_attr(elem, "bottom", false);
372
373 widget->setAlign(
374 (center ? CENTER:
375 (right ? RIGHT: LEFT)) |
376 (top ? TOP:
377 (bottom ? BOTTOM: MIDDLE)));
378 }
379 else if (elem_name == "separator") {
380 bool center = bool_attr(elem, "center", false);
381 bool right = bool_attr(elem, "right", false);
382 bool middle = bool_attr(elem, "middle", false);
383 bool bottom = bool_attr(elem, "bottom", false);
384 bool horizontal = bool_attr(elem, "horizontal", false);
385 bool vertical = bool_attr(elem, "vertical", false);
386 int align =
387 (horizontal ? HORIZONTAL: 0) |
388 (vertical ? VERTICAL: 0) |
389 (center ? CENTER: (right ? RIGHT: LEFT)) |
390 (middle ? MIDDLE: (bottom ? BOTTOM: TOP));
391
392 if (!widget) {
393 widget = new Separator(m_xmlTranslator(elem, "text"), align);
394 }
395 else
396 widget->setAlign(widget->align() | align);
397 }
398 else if (elem_name == "slider") {
399 const char *min = elem->Attribute("min");
400 const char *max = elem->Attribute("max");
401 const bool readonly = bool_attr(elem, "readonly", false);
402 int min_value = (min ? strtol(min, nullptr, 10): 0);
403 int max_value = (max ? strtol(max, nullptr, 10): 0);
404
405 widget = new Slider(min_value, max_value, min_value);
406 static_cast<Slider*>(widget)->setReadOnly(readonly);
407 }
408 else if (elem_name == "textbox") {
409 const char* text = (elem->GetText() ? elem->GetText(): "");
410 bool wordwrap = bool_attr(elem, "wordwrap", false);
411
412 if (!widget)
413 widget = new TextBox(text, 0);
414 else
415 widget->setText(text);
416
417 if (wordwrap)
418 widget->setAlign(widget->align() | WORDWRAP);
419 }
420 else if (elem_name == "view") {
421 if (!widget)
422 widget = new View();
423 }
424 else if (elem_name == "window") {
425 if (!widget) {
426 bool desktop = bool_attr(elem, "desktop", false);
427
428 if (desktop)
429 widget = new Window(Window::DesktopWindow);
430 else if (elem->Attribute("text"))
431 widget = new Window(Window::WithTitleBar, m_xmlTranslator(elem, "text"));
432 else
433 widget = new Window(Window::WithoutTitleBar);
434 }
435 }
436 else if (elem_name == "colorpicker") {
437 const bool rgba = bool_attr(elem, "rgba", false);
438 const bool simple = bool_attr(elem, "simple", false);
439
440 if (!widget) {
441 ColorButtonOptions options;
442 options.canPinSelector = false;
443 options.showSimpleColors = simple;
444 options.showIndexTab = true;
445 widget = new ColorButton(Color::fromMask(),
446 (rgba ? IMAGE_RGB:
447 app_get_current_pixel_format()),
448 options);
449 }
450 }
451 else if (elem_name == "dropdownbutton") {
452 if (!widget) {
453 widget = new DropDownButton(m_xmlTranslator(elem, "text").c_str());
454 }
455 }
456 else if (elem_name == "buttonset") {
457 const char* columns = elem->Attribute("columns");
458
459 if (!widget && columns)
460 widget = new ButtonSet(strtol(columns, NULL, 10));
461
462 if (ButtonSet* buttonset = dynamic_cast<ButtonSet*>(widget)) {
463 if (bool_attr(elem, "multiple", false))
464 buttonset->setMultiMode(ButtonSet::MultiMode::Set);
465 if (bool_attr(elem, "oneormore", false))
466 buttonset->setMultiMode(ButtonSet::MultiMode::OneOrMore);
467 }
468 }
469 else if (elem_name == "item") {
470 if (!parent)
471 throw std::runtime_error("<item> without parent");
472
473 if (ButtonSet* buttonset = dynamic_cast<ButtonSet*>(parent)) {
474 const char* icon = elem->Attribute("icon");
475 const char* text = elem->Attribute("text");
476 int hspan = int_attr(elem, "hspan", 1);
477 int vspan = int_attr(elem, "vspan", 1);
478
479 ButtonSet::Item* item = new ButtonSet::Item();
480
481 if (icon) {
482 SkinPartPtr part = SkinTheme::instance()->getPartById(std::string(icon));
483 if (part)
484 item->setIcon(part);
485 }
486
487 if (text)
488 item->setText(m_xmlTranslator(elem, "text"));
489
490 buttonset->addItem(item, hspan, vspan);
491 fillWidgetWithXmlElementAttributes(elem, root, item);
492 }
493 }
494 else if (elem_name == "image") {
495 if (!widget) {
496 const char* file = elem->Attribute("file");
497 const char* icon = elem->Attribute("icon");
498
499 if (file) {
500 ResourceFinder rf;
501 rf.includeDataDir(file);
502 if (!rf.findFirst())
503 throw base::Exception("File %s not found", file);
504
505 try {
506 os::SurfaceRef sur = os::instance()->loadRgbaSurface(rf.filename().c_str());
507 if (sur) {
508 sur->setImmutable();
509 widget = new ImageView(sur, 0);
510 }
511 }
512 catch (...) {
513 throw base::Exception("Error loading %s file", file);
514 }
515 }
516 else if (icon) {
517 SkinPartPtr part = SkinTheme::instance()->getPartById(std::string(icon));
518 if (part) {
519 widget = new ImageView(part->bitmapRef(0), 0);
520 }
521 }
522 }
523 }
524 else if (elem_name == "search") {
525 if (!widget)
526 widget = new SearchEntry;
527 }
528
529 // Was the widget created?
530 if (widget) {
531 fillWidgetWithXmlElementAttributesWithChildren(elem, root, widget);
532 }
533
534 return widget;
535}
536
537void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, Widget* root, Widget* widget)
538{
539 const char* id = elem->Attribute("id");
540 const char* tooltip_dir = elem->Attribute("tooltip_dir");
541 bool selected = bool_attr(elem, "selected", false);
542 bool disabled = bool_attr(elem, "disabled", false);
543 bool expansive = bool_attr(elem, "expansive", false);
544 bool homogeneous = bool_attr(elem, "homogeneous", false);
545 bool magnet = bool_attr(elem, "magnet", false);
546 bool noborders = bool_attr(elem, "noborders", false);
547 bool visible = bool_attr(elem, "visible", true);
548 const char* width = elem->Attribute("width");
549 const char* height = elem->Attribute("height");
550 const char* minwidth = elem->Attribute("minwidth");
551 const char* minheight = elem->Attribute("minheight");
552 const char* maxwidth = elem->Attribute("maxwidth");
553 const char* maxheight = elem->Attribute("maxheight");
554 const char* border = elem->Attribute("border");
555 const char* styleid = elem->Attribute("style");
556 const char* childspacing = elem->Attribute("childspacing");
557
558 if (width) {
559 if (!minwidth) minwidth = width;
560 if (!maxwidth) maxwidth = width;
561 }
562
563 if (height) {
564 if (!minheight) minheight = height;
565 if (!maxheight) maxheight = height;
566 }
567
568 if (id)
569 widget->setId(id);
570
571 if (elem->Attribute("text"))
572 widget->setText(m_xmlTranslator(elem, "text"));
573
574 if (elem->Attribute("tooltip") && root) {
575 if (!m_tooltipManager) {
576 m_tooltipManager = new ui::TooltipManager();
577 root->addChild(m_tooltipManager);
578 }
579
580 int dir = LEFT;
581 if (tooltip_dir) {
582 if (strcmp(tooltip_dir, "top") == 0) dir = TOP;
583 else if (strcmp(tooltip_dir, "bottom") == 0) dir = BOTTOM;
584 else if (strcmp(tooltip_dir, "left") == 0) dir = LEFT;
585 else if (strcmp(tooltip_dir, "right") == 0) dir = RIGHT;
586 }
587
588 Widget* widgetWithTooltip;
589 if (widget->type() == ui::kComboBoxWidget)
590 widgetWithTooltip = static_cast<ComboBox*>(widget)->getEntryWidget();
591 else
592 widgetWithTooltip = widget;
593 m_tooltipManager->addTooltipFor(widgetWithTooltip, m_xmlTranslator(elem, "tooltip"), dir);
594 }
595
596 if (selected)
597 widget->setSelected(selected);
598
599 if (disabled)
600 widget->setEnabled(false);
601
602 if (expansive)
603 widget->setExpansive(true);
604
605 if (!visible)
606 widget->setVisible(false);
607
608 if (homogeneous)
609 widget->setAlign(widget->align() | HOMOGENEOUS);
610
611 if (magnet)
612 widget->setFocusMagnet(true);
613
614 if (noborders) {
615 widget->InitTheme.connect(
616 [widget]{
617 widget->noBorderNoChildSpacing();
618 });
619 }
620
621 if (border) {
622 int v = strtol(border, nullptr, 10);
623 widget->InitTheme.connect(
624 [widget, v]{
625 widget->setBorder(gfx::Border(v*guiscale()));
626 });
627 }
628
629 if (childspacing) {
630 int v = strtol(childspacing, nullptr, 10);
631 widget->InitTheme.connect(
632 [widget, v]{
633 widget->setChildSpacing(v*guiscale());
634 });
635 }
636
637 if (minwidth || minheight ||
638 maxwidth || maxheight) {
639 const int minw = (minwidth ? strtol(minwidth, NULL, 10): 0);
640 const int minh = (minheight ? strtol(minheight, NULL, 10): 0);
641 const int maxw = (maxwidth ? strtol(maxwidth, NULL, 10): 0);
642 const int maxh = (maxheight ? strtol(maxheight, NULL, 10): 0);
643 widget->InitTheme.connect(
644 [widget, minw, minh, maxw, maxh]{
645 widget->setMinSize(gfx::Size(0, 0));
646 widget->setMaxSize(gfx::Size(std::numeric_limits<int>::max(),
647 std::numeric_limits<int>::max()));
648 const gfx::Size reqSize = widget->sizeHint();
649 widget->setMinSize(
650 gfx::Size((minw > 0 ? guiscale()*minw: reqSize.w),
651 (minh > 0 ? guiscale()*minh: reqSize.h)));
652 widget->setMaxSize(
653 gfx::Size((maxw > 0 ? guiscale()*maxw: std::numeric_limits<int>::max()),
654 (maxh > 0 ? guiscale()*maxh: std::numeric_limits<int>::max())));
655 });
656 }
657
658 if (styleid) {
659 std::string styleIdStr = styleid;
660 widget->InitTheme.connect(
661 [widget, styleIdStr]{
662 auto theme = SkinTheme::get(widget);
663 ui::Style* style = theme->getStyleById(styleIdStr);
664 if (style)
665 widget->setStyle(style);
666 else
667 throw base::Exception("Style %s not found", styleIdStr.c_str());
668 });
669 }
670
671 // Assign widget mnemonic from the character preceded by a '&'
672 widget->processMnemonicFromText();
673 widget->initTheme();
674}
675
676void WidgetLoader::fillWidgetWithXmlElementAttributesWithChildren(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget)
677{
678 fillWidgetWithXmlElementAttributes(elem, root, widget);
679
680 if (!root)
681 root = widget;
682
683 // Children
684 const TiXmlElement* childElem = elem->FirstChildElement();
685 while (childElem) {
686 Widget* child = convertXmlElementToWidget(childElem, root, widget, NULL);
687 if (child) {
688 // Attach the child in the view
689 if (widget->type() == kViewWidget) {
690 static_cast<View*>(widget)->attachToView(child);
691 break;
692 }
693 // Add the child in the grid
694 else if (widget->type() == kGridWidget) {
695 const char* cell_hspan = childElem->Attribute("cell_hspan");
696 const char* cell_vspan = childElem->Attribute("cell_vspan");
697 const char* cell_align = childElem->Attribute("cell_align");
698 int hspan = cell_hspan ? strtol(cell_hspan, NULL, 10): 1;
699 int vspan = cell_vspan ? strtol(cell_vspan, NULL, 10): 1;
700 int align = cell_align ? convert_align_value_to_flags(cell_align): 0;
701 auto grid = dynamic_cast<ui::Grid*>(widget);
702 ASSERT(grid != nullptr);
703
704 grid->addChildInCell(child, hspan, vspan, align);
705 }
706 // Attach the child in the view
707 else if (widget->type() == kComboBoxWidget &&
708 child->type() == kListItemWidget) {
709 auto combo = dynamic_cast<ComboBox*>(widget);
710 ASSERT(combo != nullptr);
711
712 combo->addItem(dynamic_cast<ListItem*>(child));
713 }
714 // Just add the child in any other kind of widget
715 else
716 widget->addChild(child);
717 }
718 childElem = childElem->NextSiblingElement();
719 }
720
721 if (widget->type() == kViewWidget) {
722 bool maxsize = bool_attr(elem, "maxsize", false);
723 if (maxsize)
724 static_cast<View*>(widget)->makeVisibleAllScrollableArea();
725 }
726}
727
728static int convert_align_value_to_flags(const char *value)
729{
730 char *tok, *ptr = base_strdup(value);
731 int flags = 0;
732
733 for (tok=strtok(ptr, " ");
734 tok != NULL;
735 tok=strtok(NULL, " ")) {
736 if (strcmp(tok, "horizontal") == 0) {
737 flags |= HORIZONTAL;
738 }
739 else if (strcmp(tok, "vertical") == 0) {
740 flags |= VERTICAL;
741 }
742 else if (strcmp(tok, "left") == 0) {
743 flags |= LEFT;
744 }
745 else if (strcmp(tok, "center") == 0) {
746 flags |= CENTER;
747 }
748 else if (strcmp(tok, "right") == 0) {
749 flags |= RIGHT;
750 }
751 else if (strcmp(tok, "top") == 0) {
752 flags |= TOP;
753 }
754 else if (strcmp(tok, "middle") == 0) {
755 flags |= MIDDLE;
756 }
757 else if (strcmp(tok, "bottom") == 0) {
758 flags |= BOTTOM;
759 }
760 else if (strcmp(tok, "homogeneous") == 0) {
761 flags |= HOMOGENEOUS;
762 }
763 }
764
765 base_free(ptr);
766 return flags;
767}
768
769static int int_attr(const TiXmlElement* elem, const char* attribute_name, int default_value)
770{
771 const char* value = elem->Attribute(attribute_name);
772
773 return (value ? strtol(value, NULL, 10): default_value);
774}
775
776} // namespace app
777