From 0bf21870392dfb15ed11613409445c8ec64f3ffa Mon Sep 17 00:00:00 2001
From: Vicent Marti
Date: Wed, 13 Aug 2008 10:42:01 +0000
Subject: Finished GUI layout in g1x.
svn-id: r33818
---
engines/scumm/dialogs.cpp | 4 +-
gui/ThemeEval.cpp | 26 +-
gui/ThemeEval.h | 5 +-
gui/ThemeParser.cpp | 70 +++--
gui/ThemeParser.h | 276 +-----------------
gui/ThemeRenderer.cpp | 23 +-
gui/options.cpp | 2 +-
gui/themes/scummodern.stx | 707 +++++++++++++++++++++++++++++++++++++++-------
gui/themes/scummodern.zip | Bin 111123 -> 137193 bytes
9 files changed, 688 insertions(+), 425 deletions(-)
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 559ad6e69e..1dbbc28f92 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -238,7 +238,7 @@ SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel,
_drawingHints |= GUI::THEME_HINT_SPECIAL_COLOR;
- new StaticTextWidget(this, "ScummSaveload.Title", title);
+ new StaticTextWidget(this, "ScummSaveLoad.Title", title);
// Add choice list
_list = new GUI::ListWidget(this, "ScummSaveLoad.List");
@@ -318,7 +318,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da
}
void SaveLoadChooser::reflowLayout() {
- if (g_gui.xmlEval()->getVar("ScummSaveLoad.ExtInfo.Visible", 1) == 1) {
+ if (g_gui.xmlEval()->getVar("Globals.ScummSaveLoad.ExtInfo.Visible") == 1) {
int16 x, y;
uint16 w, h;
diff --git a/gui/ThemeEval.cpp b/gui/ThemeEval.cpp
index ab9e726b75..f93706cb47 100644
--- a/gui/ThemeEval.cpp
+++ b/gui/ThemeEval.cpp
@@ -85,6 +85,7 @@ void ThemeLayoutMain::reflowLayout() {
void ThemeLayoutVertical::reflowLayout() {
int curX, curY;
int autoWidget = -1;
+ int extraHeight = 0;
curX = _paddingLeft;
curY = _paddingTop;
@@ -127,8 +128,13 @@ void ThemeLayoutVertical::reflowLayout() {
if (_reverse) for (int j = autoWidget - 1; j >= 0; --j)
_children[j]->setY(-(_children[i]->getHeight() + _spacing));
- else
- _children[i]->setY(-2 * (_children[i]->getHeight() + _spacing));
+ else {
+ extraHeight -= (_children[i]->getHeight() + _spacing);
+ _children[i]->setY(extraHeight);
+
+ for (int j = i - 1; j > autoWidget; --j)
+ _children[j]->setY(-(_children[i]->getHeight() + _spacing));
+ }
} else {
_h += _children[i]->getHeight() + _spacing;
}
@@ -138,6 +144,7 @@ void ThemeLayoutVertical::reflowLayout() {
void ThemeLayoutHorizontal::reflowLayout() {
int curX, curY;
int autoWidget = -1;
+ int autoWidth = 0;
curX = _paddingLeft;
curY = _paddingTop;
@@ -178,8 +185,13 @@ void ThemeLayoutHorizontal::reflowLayout() {
if (_reverse) for (int j = autoWidget - 1; j >= 0; --j)
_children[j]->setX(-(_children[i]->getWidth() + _spacing));
- else
- _children[i]->setX(-2 * (_children[i]->getWidth() + _spacing));
+ else {
+ autoWidth -= (_children[i]->getWidth() + _spacing);
+ _children[i]->setX(autoWidth);
+
+ for (int j = i - 1; j > autoWidget; --j)
+ _children[j]->setX(-(_children[i]->getWidth() + _spacing));
+ }
} else {
_w += _children[i]->getWidth() + _spacing;
}
@@ -229,18 +241,18 @@ void ThemeEval::addWidget(const Common::String &name, int w, int h, const Common
setVar(_curDialog + "." + name + ".Enabled", enabled ? 1 : 0);
}
-void ThemeEval::addDialog(const Common::String &name, const Common::String &overlays, bool enabled) {
+void ThemeEval::addDialog(const Common::String &name, const Common::String &overlays, bool enabled, int inset) {
int16 x, y;
uint16 w, h;
ThemeLayout *layout = 0;
if (overlays == "screen") {
- layout = new ThemeLayoutMain(0, 0, g_system->getOverlayWidth(), g_system->getOverlayHeight());
+ layout = new ThemeLayoutMain(inset, inset, g_system->getOverlayWidth() - 2 * inset, g_system->getOverlayHeight() - 2 * inset);
} else if (overlays == "screen_center") {
layout = new ThemeLayoutMain(-1, -1, -1, -1);
} else if (getWidgetData(overlays, x, y, w, h)) {
- layout = new ThemeLayoutMain(x, y, w, h);
+ layout = new ThemeLayoutMain(x + inset, y + inset, w - 2 * inset, h - 2 * inset);
}
if (!layout)
diff --git a/gui/ThemeEval.h b/gui/ThemeEval.h
index aaab4cb2bf..e0c1c5af59 100644
--- a/gui/ThemeEval.h
+++ b/gui/ThemeEval.h
@@ -336,7 +336,7 @@ public:
bool hasVar(const Common::String &name) { return _vars.contains(name) || _builtin.contains(name); }
- void addDialog(const Common::String &name, const Common::String &overlays, bool enabled = true);
+ void addDialog(const Common::String &name, const Common::String &overlays, bool enabled = true, int inset = 0);
void addLayout(ThemeLayout::LayoutType type, int spacing, bool reverse, bool center = false);
void addWidget(const Common::String &name, int w, int h, const Common::String &type, bool enabled = true);
bool addImportedLayout(const Common::String &name);
@@ -383,12 +383,13 @@ public:
void reset() {
_vars.clear();
- _builtin.clear();
_curDialog.clear();
_curLayout.clear();
for (LayoutsMap::iterator i = _layouts.begin(); i != _layouts.end(); ++i)
delete i->_value;
+
+ _layouts.clear();
}
private:
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 572acc0b76..3167f94009 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -130,7 +130,7 @@ bool ThemeParser::parserCallback_defaults(ParserNode *node) {
bool ThemeParser::parserCallback_font(ParserNode *node) {
int red, green, blue;
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -151,7 +151,7 @@ bool ThemeParser::parserCallback_fonts(ParserNode *node) {
}
bool ThemeParser::parserCallback_cursor(ParserNode *node) {
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -171,7 +171,7 @@ bool ThemeParser::parserCallback_cursor(ParserNode *node) {
}
bool ThemeParser::parserCallback_bitmap(ParserNode *node) {
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -209,12 +209,16 @@ bool ThemeParser::parserCallback_text(ParserNode *node) {
}
bool ThemeParser::parserCallback_render_info(ParserNode *node) {
- // TODO: Skip key if it's not for this platform.
+ if (resolutionCheck(node->values["resolution"]) == false)
+ node->ignore = true;
+
return true;
}
bool ThemeParser::parserCallback_layout_info(ParserNode *node) {
- // TODO: skip key
+ if (resolutionCheck(node->values["resolution"]) == false)
+ node->ignore = true;
+
return true;
}
@@ -264,7 +268,7 @@ bool ThemeParser::parserCallback_drawstep(ParserNode *node) {
bool ThemeParser::parserCallback_drawdata(ParserNode *node) {
bool cached = false;
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -479,7 +483,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
}
bool ThemeParser::parserCallback_def(ParserNode *node) {
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -502,7 +506,7 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) {
if (getParentNode(node)->name == "globals") {
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -546,20 +550,12 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) {
return true;
}
-bool ThemeParser::parserCallback_child(ParserNode *node) {
- Common::String var = "Globals." + getParentNode(node)->values["name"] + "." + node->values["name"] + ".";
-
- if (!parseCommonLayoutProps(node, var))
- return parserError("Error when parsing Layout properties of '%s'.", var.c_str());
-
- return true;
-}
-
bool ThemeParser::parserCallback_dialog(ParserNode *node) {
Common::String var = "Dialog." + node->values["name"];
bool enabled = true;
+ int inset = 0;
- if (resolutionCheck(node->values["resolution"])) {
+ if (resolutionCheck(node->values["resolution"]) == false) {
node->ignore = true;
return true;
}
@@ -571,7 +567,12 @@ bool ThemeParser::parserCallback_dialog(ParserNode *node) {
return parserError("Invalid value for Dialog enabling (expecting true/false)");
}
- _theme->themeEval()->addDialog(var, node->values["overlays"], enabled);
+ if (node->values.contains("inset")) {
+ if (!parseIntegerKey(node->values["inset"].c_str(), 1, &inset))
+ return false;
+ }
+
+ _theme->themeEval()->addDialog(var, node->values["overlays"], enabled, inset);
if (node->values.contains("shading")) {
int shading = 0;
@@ -767,14 +768,33 @@ bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String
bool ThemeParser::resolutionCheck(const Common::String &resolution) {
if (resolution.empty())
- return false;
+ return true;
- Common::StringTokenizer tokenizer(resolution, "x");
- Common::String w = tokenizer.nextToken();
- Common::String h = tokenizer.nextToken();
+ Common::StringTokenizer globTokenizer(resolution, ", ");
+ Common::String cur, w, h;
+ bool definedRes = false;
- return ((w == "X" || atoi(w.c_str()) == g_system->getOverlayWidth()) &&
- (h == "Y" || atoi(h.c_str()) == g_system->getOverlayHeight())) == false;
+ while (!globTokenizer.empty()) {
+ bool ignore = false;
+ cur = globTokenizer.nextToken();
+
+ if (cur[0] == '-') {
+ ignore = true;
+ cur.deleteChar(0);
+ } else {
+ definedRes = true;
+ }
+
+ Common::StringTokenizer resTokenizer(cur, "x");
+ w = resTokenizer.nextToken();
+ h = resTokenizer.nextToken();
+
+ if ((w == "X" || atoi(w.c_str()) == g_system->getOverlayWidth()) &&
+ (h == "Y" || atoi(h.c_str()) == g_system->getOverlayHeight()))
+ return !ignore;
+ }
+
+ return !definedRes;
}
}
diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h
index 782ab9a44f..e4821ef00f 100644
--- a/gui/ThemeParser.h
+++ b/gui/ThemeParser.h
@@ -38,272 +38,6 @@
#include "graphics/VectorRenderer.h"
#include "gui/ThemeRenderer.h"
-/**
- *********************************************
- ** Theme Description File format overview. **
- *********************************************
- This document is a work in progress.
- A more complete version will be posted on the wiki soon.
-
-In the new version of the Graphical User Interface for ScummVM, almost
-all properties regarding looks, design and positioning of the UI
-elements are defined in a set of external files.
-
-The chosen syntax for theme description is a basic subset of XML.
-The process of theme description is divided in two main parts: Drawing
-specifications for the vector renderer and theme design/layout
-information for the actual theme engine.
-
-These two core sections of a theme's description may be placed in a
-single file or split for convenience across several files.
-
-_DRAWING SPECIFICATIONS_
-
-The process of rendering a widget on the screen is discretized into
-several phases called "drawing steps". A set of such steps, which
-generate a basic widget shape on screen is called a Draw Data set. The
-GUI Engine loads all the different data sets for a given
-widget and takes care of rendering it into the screen based on its
-current state.
-
-For example, the basic Button widget may be composed of several sets
-of data: Drawing data for the button's idle state, drawing data for
-when the button is hovered and drawing data for when the button is
-pressed.
-
-The functionality of each set of Drawing Data is hard-coded into the
-Graphical User Interface; the most up to date version of all the
-drawing sets may be found extensively commented in the
-"gui/InterfaceManager.h" file, in the DrawData enumeration inside the
-InterfaceManager class.
-
-In order to successfully parse and load a custom theme definition, the
-whole list of Draw Data sets must be specified.
-
-_THEME LAYOUT SPECIFICATIONS_
-
-#######
-
-_SYNTAX OVERVIEW AND PARAMETERS_
-
-As stated before, all the theme description is done through a XML-like
-syntax. The files are parsed left-to-right, ignoring extra whitespaces
-and newlines. Parser data is interpreted during the parsing. As a
-general guideline, theme files are composed of keys which may or not
-contain specific values for the key and which may parent several
-subkeys; independently of this, all keys must be properly closed with
-the '/' operator.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Note how keys which contain no children may be closed by themselves
-or with an external closure.
-
-- Comments
-The parser supports the same comment syntax as the C++ programming
-language. Comment blocks may be specified by surrounding them with the
-'/ *' and '* /' operators, while whole lines may be commented out by
-preceding them with the // operator.
-
-Block comments are parsed in a non-hungry manner, i.e. the first
-comment closure is understood to close the whole commenting block, so
-syntax like
-
- / * hey look this comment finishes here * / or maybe here?? * /
-
-is invalid.
-
-- Section keys.
-The section key is the root level of a theme description file. Each
-file may contain one or more of these keys, which specifies the nature
-of all their children, namely if the children keys specify drawing or
-layout information. Its syntax is as follows:
-
-
- // ...
-
-
-
- // ...
-
-
-The "layout_info" key specifies that all children keys contain
-information regarding the layout of the theme, while the "render_info"
-key specifies that all children keys contain information regarding the
-looks of the theme.
-
-Both keys support the two optional parameters "platform" and
-"resolution", in order to make a certain layout apply to a single
-resolution or to a single platform. To make a key apply for more than
-one specific platform or resolution at the same time, you may separate
-their names with commas.
-
-
-
-
-- Render Info keys:
-The children of a "render_info" key are expected to be one of these
-kind:
-
- -- DrawData key:
- DrawData keys are the core of the rendering engine. They specifiy
- via their own children the looks of all the UI elements. Here's
- their syntax:
-
-
-
-
-
- All drawdata keys must contain an "id" value, specifying which set
- of drawing data they implement. Here's a list of all possible ids.
-
- #########
-
- Remember that all these ids must me implemented in order for the
- parsing to be considered succesful.
-
- DrawData keys may also contain an optional boolean value "cache",
- which states if the set of DrawingSteps may be cached into the
- memory so it can be blit into the Overlay each frame or if the set
- of Drawing Steps should be performed individually each frame. If
- omitted, the "cache" value defaults to false.
-
- Also, just like the key, DrawData keys may also
- contain optional "platform" and "resolution" values, making such
- draw steps specific for a single or several platforms or
- resolutions. In order to specify several platforms or resolutions,
- they must be separated by commas inside the key's value.
-
-
-
-
- When making a set of Drawing Data for a widget specific to a
- single platform or resolution, remember that the set must be also
- implemented later generically for other platforms, or the
- rendering of the theme will fail in such platforms.
-
- Lastly, each DrawData key must contain at least a children
- "drawstep" subkey, with the necessary info for the
- VectorRenderer.
-
- - The DrawStep key
- The DrawStep key is probably the most complex definition of
- a ThemeDescription file. It contains enough information to
- allow the Vector Renderer to draw a basic or complex shape
- into the screen.
-
- DrawStep keys are supposed to have no children, so they must
- be either self-closed or closed externally.
-
- Their basic syntax is as follows:
-
-
-
-
- The only required value is the function "func" which states
- the drawing function that will be used, and it must be
- accompanied by a set of parameters specific to each drawing
- step. Here's a list of such parameters:
-
-Common parameters for all functions:
-
-fill = "none|foreground|background|gradient"
- Specifies the fill mode for the drawn shape.
- Possible values:
-
- "none": Disables filling so only the stroke is shown.
- "foreground" (default): Fills the whole shape with the active foreground
- color.
- "background": Fills the whole shape with the active background
- color.
- "gradient": Fills the whole shape with the active gradient.
-
-gradient_start = "R, G, B" | "color name"
-gradient_end = "R, G, B" | "color name"
-fg_color = "R, G, B" | "color name"
-bg_color = "R, G, B" | "color name"
- Sets the active gradient, foreground or backgroud colors. Colors
- may be specified with their three components (red, green, blue)
- ranging each from 0 to 255, or via a color name which has
- previously been defined in the palette section.
-
- These colours have no default values.
-
-stroke = width (integer)
- Sets the active stroke width; strokes may be disabled by setting
- this value to 0. All shapes are automatically stroked with the
- given width and the active foreground color. Defaults to 1.
-
-shadow = offset (integer)
- Sets the shadow offset. In the rendering engines that support it,
- drawn shapes will have a soft shadow offseted the given amount on
- their bottom-right corner. Defaults to 0 (disabled).
-
-factor = amount (integer)
- The factor value specifies the displacement of the active
- gradient, i.e. its zoom level. It is only taken into account if
- the active fill mode is set to gradient. Defaults to 1.
-
-Standard primitive drawing functions:
-
-func = "circle"
- Draws a primitive circle. Requires the additional parameter
- "radius", with an integer defining the radius of the circle or
- the "auto" value.
-
-func = "square"
- Draws a primitive square/rectangle. Requires no additional parameters.
-
-func = "roundedsq"
- Draws a square/rectangle with rounded corners. Requires the
- additional parameter "radius" defining the radius of the rounded
- corners.
-
-func = "bevelsq"
- Draws a square/rectangle with beveled borders. This square
- ignores the active fill mode, as it is never filled. Requires the
- additional parameter "bevel" with the amount of bevel.
-
-func = "line"
- Draws a line. If the "size" parameter is specified, the line will
- be drawn ranging from the bottom-left corner to the top-right
- corner of the defined box. Optionally, you may define the ending
- point of the line with the "end" parameter.
-
-func = "triangle"
- Draws a triangle. Triangles are always isosceles, meaning they
- are drawn inside the square defined by the position and size
- values, with the given width as the base of the triangle and the
- given height as the height of the triangle.
-
- The optional parameter
-
- orientation = "top|left|right|bottom"
-
- may be specified to define the way in which the triangle is
- pointing. Defaults to top.
-
-func = "fill"
- This call ignores position and size parameters, as it completely
- fills the active drawing surface taken into account the active
- fill mode and colors.
-*/
-
namespace GUI {
using namespace Graphics;
@@ -339,6 +73,7 @@ protected:
CUSTOM_XML_PARSER(ThemeParser) {
XML_KEY(render_info)
+ XML_PROP(resolution, false)
XML_KEY(palette)
XML_KEY(color)
XML_PROP(name, true)
@@ -435,6 +170,7 @@ protected:
KEY_END() // render_info end
XML_KEY(layout_info)
+ XML_PROP(resolution, false)
XML_KEY(globals)
XML_PROP(resolution, false)
XML_KEY(def)
@@ -449,12 +185,6 @@ protected:
XML_PROP(pos, false)
XML_PROP(padding, false)
XML_PROP(resolution, false)
-
- XML_KEY(child)
- XML_PROP(name, true)
- XML_PROP(size, false)
- XML_PROP(padding, false)
- KEY_END()
KEY_END()
KEY_END()
@@ -464,6 +194,7 @@ protected:
XML_PROP(shading, false)
XML_PROP(enabled, false)
XML_PROP(resolution, false)
+ XML_PROP(inset, false)
XML_KEY(layout)
XML_PROP(type, true)
XML_PROP(center, false)
@@ -515,7 +246,6 @@ protected:
bool parserCallback_def(ParserNode *node);
bool parserCallback_widget(ParserNode *node);
bool parserCallback_dialog(ParserNode *node);
- bool parserCallback_child(ParserNode *node);
bool parserCallback_layout(ParserNode *node);
bool parserCallback_space(ParserNode *node);
bool parserCallback_import(ParserNode *node);
diff --git a/gui/ThemeRenderer.cpp b/gui/ThemeRenderer.cpp
index b179ac75c3..9f517abd2d 100644
--- a/gui/ThemeRenderer.cpp
+++ b/gui/ThemeRenderer.cpp
@@ -96,7 +96,7 @@ const ThemeRenderer::TextDataInfo ThemeRenderer::kTextDataDefaults[] = {
ThemeRenderer::ThemeRenderer(Common::String fileName, GraphicsMode mode) :
- _vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled),
+ _vectorRenderer(0), _system(0), _graphicsMode(kGfxDisabled), _font(0),
_screen(0), _backBuffer(0), _bytesPerPixel(0), _initOk(false),
_themeOk(false), _enabled(false), _buffering(false), _cursor(0) {
_system = g_system;
@@ -114,16 +114,8 @@ ThemeRenderer::ThemeRenderer(Common::String fileName, GraphicsMode mode) :
}
_graphicsMode = mode;
- setGraphicsMode(_graphicsMode);
-
- if (_screen->w >= 400 && _screen->h >= 300) {
- _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
- } else {
- _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
- }
-
_themeFileName = fileName;
- _initOk = true;
+ _initOk = false;
}
ThemeRenderer::~ThemeRenderer() {
@@ -149,6 +141,12 @@ bool ThemeRenderer::init() {
clearAll();
resetDrawArea();
}
+
+ if (_screen->w >= 400 && _screen->h >= 300) {
+ _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ } else {
+ _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
+ }
if (isThemeLoadingRequired() || !_themeOk) {
loadTheme(_themeFileName);
@@ -185,9 +183,8 @@ void ThemeRenderer::unloadTheme() {
ImageMan.unregisterSurface(i->_key);
ImageMan.remArchive(_themeFileName + ".zip");
-
-// _themeName.clear();
-// _themeFileName.clear();
+
+ _themeEval->reset();
_themeOk = false;
}
diff --git a/gui/options.cpp b/gui/options.cpp
index 96a8619cf7..1e6e551a57 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -669,7 +669,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
//
// 1) The graphics tab
//
- tab->addTab("Graphics");
+ tab->addTab(g_system->getOverlayWidth() > 320 ? "Graphics" : "GFX");
addGraphicControls(tab, "GlobalOptions_Graphics.");
//
diff --git a/gui/themes/scummodern.stx b/gui/themes/scummodern.stx
index a2a58e3e67..5399ed62f0 100644
--- a/gui/themes/scummodern.stx
+++ b/gui/themes/scummodern.stx
@@ -420,16 +420,6 @@
vertical_align = 'top'
horizontal_align = 'left'
/>
-/* */
@@ -462,55 +452,35 @@
-
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
-
-
+
+
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+