1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/scummsys.h"
24 #include "common/system.h"
25 #include "common/rect.h"
26 #include "common/textconsole.h"
27 #include "common/translation.h"
28 #include "graphics/pixelformat.h"
29 #include "graphics/svg.h"
30 #include "gui/widget.h"
31 #include "gui/gui-manager.h"
32
33 #include "gui/ThemeEval.h"
34
35 #include "gui/dialog.h"
36 #include "gui/widgets/popup.h"
37 #include "gui/widgets/scrollcontainer.h"
38
39 namespace GUI {
40
Widget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & tooltip)41 Widget::Widget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &tooltip)
42 : GuiObject(x, y, w, h), _type(0), _boss(boss), _tooltip(tooltip),
43 _flags(0), _hasFocus(false), _state(ThemeEngine::kStateEnabled) {
44 init();
45 }
46
Widget(GuiObject * boss,const Common::String & name,const Common::U32String & tooltip)47 Widget::Widget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip)
48 : GuiObject(name), _type(0), _boss(boss), _tooltip(tooltip),
49 _flags(0), _hasFocus(false), _state(ThemeEngine::kStateDisabled) {
50 init();
51 }
52
init()53 void Widget::init() {
54 // Insert into the widget list of the boss
55 _next = _boss->_firstWidget;
56 _boss->_firstWidget = this;
57 _needsRedraw = true;
58 }
59
~Widget()60 Widget::~Widget() {
61 delete _next;
62 _next = nullptr;
63 }
64
setFlags(int flags)65 void Widget::setFlags(int flags) {
66 updateState(_flags, _flags | flags);
67 _flags |= flags;
68 }
69
clearFlags(int flags)70 void Widget::clearFlags(int flags) {
71 updateState(_flags, _flags & ~flags);
72 _flags &= ~flags;
73 }
74
updateState(int oldFlags,int newFlags)75 void Widget::updateState(int oldFlags, int newFlags) {
76 if (newFlags & WIDGET_ENABLED) {
77 _state = ThemeEngine::kStateEnabled;
78 if (newFlags & WIDGET_HILITED)
79 _state = ThemeEngine::kStateHighlight;
80 if (newFlags & WIDGET_PRESSED)
81 _state = ThemeEngine::kStatePressed;
82 } else {
83 _state = ThemeEngine::kStateDisabled;
84 }
85 }
86
markAsDirty()87 void Widget::markAsDirty() {
88 _needsRedraw = true;
89
90 Widget *w = _firstWidget;
91 while (w) {
92 w->markAsDirty();
93 w = w->next();
94 }
95 }
96
draw()97 void Widget::draw() {
98 if (!isVisible() || !_boss->isVisible())
99 return;
100
101 if (_needsRedraw) {
102 int oldX = _x, oldY = _y;
103
104 // Account for our relative position in the dialog
105 _x = getAbsX();
106 _y = getAbsY();
107
108 Common::Rect oldClip = g_gui.theme()->swapClipRect(_boss->getClipRect());
109
110 if (g_gui.useRTL()) {
111 _x = g_system->getOverlayWidth() - _x - _w;
112
113 if (this->_name.contains("GameOptions") || this->_name.contains("GlobalOptions") || this->_name.contains("Browser") || this->_name.empty()) {
114 /** The dialogs named above are the stacked dialogs for which the left+right paddings need to be adjusted for RTL.
115 The _name is empty for some special widgets - like RemapWidgets, NavBars, ScrollBars and they need to be adjusted too.
116 */
117 _x = _x + g_gui.getOverlayOffset();
118 }
119
120 Common::Rect r = _boss->getClipRect();
121 r.moveTo(_x, r.top);
122
123 g_gui.theme()->swapClipRect(r);
124 }
125
126 // Draw border
127 if (_flags & WIDGET_BORDER) {
128 g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h),
129 ThemeEngine::kWidgetBackgroundBorder);
130 _x += 4;
131 _y += 4;
132 _w -= 8;
133 _h -= 8;
134 }
135
136 // Now perform the actual widget draw
137 drawWidget();
138
139 g_gui.theme()->swapClipRect(oldClip);
140
141 // Restore x/y
142 if (_flags & WIDGET_BORDER) {
143 _x -= 4;
144 _y -= 4;
145 _w += 8;
146 _h += 8;
147 }
148
149 _x = oldX;
150 _y = oldY;
151
152 _needsRedraw = false;
153 }
154
155 // Draw all children
156 Widget *w = _firstWidget;
157 while (w) {
158 w->draw();
159 w = w->_next;
160 }
161 }
162
findWidgetInChain(Widget * w,int x,int y)163 Widget *Widget::findWidgetInChain(Widget *w, int x, int y) {
164 while (w) {
165 // Stop as soon as we find a widget that contains the point (x,y)
166 if (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->getHeight())
167 break;
168 w = w->_next;
169 }
170 if (w)
171 w = w->findWidget(x - w->_x, y - w->_y);
172 return w;
173 }
174
findWidgetInChain(Widget * w,const char * name)175 Widget *Widget::findWidgetInChain(Widget *w, const char *name) {
176 while (w) {
177 if (w->_name == name) {
178 return w;
179 }
180 w = w->_next;
181 }
182 return nullptr;
183 }
184
containsWidgetInChain(Widget * w,Widget * search)185 bool Widget::containsWidgetInChain(Widget *w, Widget *search) {
186 while (w) {
187 if (w == search || w->containsWidget(search))
188 return true;
189 w = w->_next;
190 }
191 return false;
192 }
193
setEnabled(bool e)194 void Widget::setEnabled(bool e) {
195 if ((_flags & WIDGET_ENABLED) != e) {
196 if (e)
197 setFlags(WIDGET_ENABLED);
198 else
199 clearFlags(WIDGET_ENABLED);
200
201 g_gui.scheduleTopDialogRedraw();
202 }
203 }
204
isEnabled() const205 bool Widget::isEnabled() const {
206 return ((_flags & WIDGET_ENABLED) != 0);
207 }
208
setVisible(bool e)209 void Widget::setVisible(bool e) {
210 if (e)
211 clearFlags(WIDGET_INVISIBLE);
212 else
213 setFlags(WIDGET_INVISIBLE);
214 }
215
isVisible() const216 bool Widget::isVisible() const {
217 if (g_gui.xmlEval()->getVar("Dialog." + _name + ".Visible", 1) == 0)
218 return false;
219
220 return !(_flags & WIDGET_INVISIBLE);
221 }
222
useRTL() const223 bool Widget::useRTL() const {
224 return _useRTL;
225 }
226
parseHotkey(const Common::U32String & label)227 uint8 Widget::parseHotkey(const Common::U32String &label) {
228 if (!label.contains('~'))
229 return 0;
230
231 int state = 0;
232 uint8 hotkey = 0;
233
234 for (uint i = 0; i < label.size() && state != 3; i++) {
235 switch (state) {
236 case 0:
237 if (label[i] == '~')
238 state = 1;
239 break;
240 case 1:
241 if (label[i] != '~') {
242 state = 2;
243 hotkey = label[i];
244 } else
245 state = 0;
246 break;
247 case 2:
248 if (label[i] == '~')
249 state = 3;
250 else
251 state = 0;
252 break;
253 default:
254 break;
255 }
256 }
257
258 if (state == 3)
259 return hotkey;
260
261 return 0;
262 }
263
cleanupHotkey(const Common::U32String & label)264 Common::U32String Widget::cleanupHotkey(const Common::U32String &label) {
265 Common::U32String res("");
266
267 for (Common::U32String::const_iterator itr = label.begin(); itr != label.end(); itr++) {
268 if (*itr != '~') {
269 res += *itr;
270 }
271 }
272
273 return res;
274 }
275
read(const Common::U32String & str)276 void Widget::read(const Common::U32String &str) {
277 if (ConfMan.hasKey("tts_enabled", "scummvm") &&
278 ConfMan.getBool("tts_enabled", "scummvm")) {
279 Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
280 if (ttsMan == nullptr)
281 return;
282 ttsMan->say(str);
283 }
284 }
285
286 #pragma mark -
287
StaticTextWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & text,Graphics::TextAlign align,const Common::U32String & tooltip,ThemeEngine::FontStyle font,Common::Language lang)288 StaticTextWidget::StaticTextWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &text, Graphics::TextAlign align, const Common::U32String &tooltip, ThemeEngine::FontStyle font, Common::Language lang)
289 : Widget(boss, x, y, w, h, tooltip) {
290 setFlags(WIDGET_ENABLED);
291 _type = kStaticTextWidget;
292 _label = text;
293 _align = Graphics::convertTextAlignH(align, g_gui.useRTL() && _useRTL);
294 setFont(font, lang);
295 }
296
StaticTextWidget(GuiObject * boss,const Common::String & name,const Common::U32String & text,const Common::U32String & tooltip,ThemeEngine::FontStyle font,Common::Language lang)297 StaticTextWidget::StaticTextWidget(GuiObject *boss, const Common::String &name, const Common::U32String &text, const Common::U32String &tooltip, ThemeEngine::FontStyle font, Common::Language lang)
298 : Widget(boss, name, tooltip) {
299 setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
300 _type = kStaticTextWidget;
301 _label = text;
302 _align = Graphics::convertTextAlignH(g_gui.xmlEval()->getWidgetTextHAlign(name), g_gui.useRTL() && _useRTL);
303 setFont(font, lang);
304 }
305
setValue(int value)306 void StaticTextWidget::setValue(int value) {
307 _label = Common::String::format("%d", value);
308 }
309
setLabel(const Common::U32String & label)310 void StaticTextWidget::setLabel(const Common::U32String &label) {
311 if (_label != label) {
312 _label = label;
313
314 markAsDirty();
315 }
316 }
317
setAlign(Graphics::TextAlign align)318 void StaticTextWidget::setAlign(Graphics::TextAlign align) {
319 align = Graphics::convertTextAlignH(align, g_gui.useRTL() && _useRTL);
320 if (_align != align){
321 _align = align;
322
323 markAsDirty();
324 }
325 }
326
327
drawWidget()328 void StaticTextWidget::drawWidget() {
329 g_gui.theme()->drawText(
330 Common::Rect(_x, _y, _x + _w, _y + _h),
331 _label, _state, _align, ThemeEngine::kTextInversionNone, 0, true, _font
332 );
333 }
334
setFont(ThemeEngine::FontStyle font,Common::Language lang)335 void StaticTextWidget::setFont(ThemeEngine::FontStyle font, Common::Language lang) {
336 _font = font;
337
338 if (lang == Common::UNK_LANG)
339 return;
340
341 if (g_gui.theme()->loadExtraFont(font, lang))
342 _font = GUI::ThemeEngine::kFontStyleLangExtra;
343 }
344
345 #pragma mark -
346
ButtonWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)347 ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
348 : StaticTextWidget(boss, x, y, w, h, cleanupHotkey(label), Graphics::kTextAlignCenter, tooltip), CommandSender(boss),
349 _cmd(cmd), _hotkey(hotkey), _duringPress(false) {
350
351 if (hotkey == 0)
352 _hotkey = parseHotkey(label);
353
354 setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
355 _type = kButtonWidget;
356 }
357
ButtonWidget(GuiObject * boss,const Common::String & name,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)358 ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
359 : StaticTextWidget(boss, name, cleanupHotkey(label), tooltip), CommandSender(boss),
360 _cmd(cmd), _hotkey(hotkey), _duringPress(false) {
361 if (hotkey == 0)
362 _hotkey = parseHotkey(label);
363 setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
364 _type = kButtonWidget;
365 }
366
getMinSize(int & minWidth,int & minHeight)367 void ButtonWidget::getMinSize(int &minWidth, int &minHeight) {
368 const Graphics::Font &font = g_gui.getFont(_font);
369
370 minWidth = font.getStringWidth(_label);
371 minHeight = font.getFontHeight();
372 }
373
handleMouseUp(int x,int y,int button,int clickCount)374 void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) {
375 if (isEnabled() && _duringPress && x >= 0 && x < _w && y >= 0 && y < _h) {
376 setUnpressedState();
377 sendCommand(_cmd, 0);
378 }
379 _duringPress = false;
380 }
381
handleMouseDown(int x,int y,int button,int clickCount)382 void ButtonWidget::handleMouseDown(int x, int y, int button, int clickCount) {
383 _duringPress = true;
384 setPressedState();
385 }
386
drawWidget()387 void ButtonWidget::drawWidget() {
388 g_gui.theme()->drawButton(Common::Rect(_x, _y, _x + _w, _y + _h), _label, _state, getFlags());
389 }
390
setLabel(const Common::U32String & label)391 void ButtonWidget::setLabel(const Common::U32String &label) {
392 StaticTextWidget::setLabel(cleanupHotkey(label));
393 }
394
setLabel(const Common::String & label)395 void ButtonWidget::setLabel(const Common::String &label) {
396 ButtonWidget::setLabel(Common::U32String(label));
397 }
398
addClearButton(GuiObject * boss,const Common::String & name,uint32 cmd,int x,int y,int w,int h)399 ButtonWidget *addClearButton(GuiObject *boss, const Common::String &name, uint32 cmd, int x, int y, int w, int h) {
400 ButtonWidget *button;
401
402 #ifndef DISABLE_FANCY_THEMES
403 if (g_gui.xmlEval()->getVar("Globals.ShowSearchPic") == 1 && g_gui.theme()->supportsImages()) {
404 if (!name.empty())
405 button = new PicButtonWidget(boss, name, _("Clear value"), cmd);
406 else
407 button = new PicButtonWidget(boss, x, y, w, h, _("Clear value"), cmd);
408 ((PicButtonWidget *)button)->useThemeTransparency(true);
409 ((PicButtonWidget *)button)->setGfxFromTheme(ThemeEngine::kImageEraser, kPicButtonStateEnabled, false);
410 } else
411 #endif
412 if (!name.empty())
413 button = new ButtonWidget(boss, name, Common::U32String("C"), _("Clear value"), cmd);
414 else
415 button = new ButtonWidget(boss, x, y, w, h, Common::U32String("C"), _("Clear value"), cmd);
416
417 return button;
418 }
419
setHighLighted(bool enable)420 void ButtonWidget::setHighLighted(bool enable) {
421 (enable) ? setFlags(WIDGET_HILITED) : clearFlags(WIDGET_HILITED);
422 markAsDirty();
423 }
424
setPressedState()425 void ButtonWidget::setPressedState() {
426 setFlags(WIDGET_PRESSED);
427 clearFlags(WIDGET_HILITED);
428 markAsDirty();
429 }
430
setUnpressedState()431 void ButtonWidget::setUnpressedState() {
432 clearFlags(WIDGET_PRESSED);
433 markAsDirty();
434 }
435
436 #pragma mark -
437
DropdownButtonWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)438 DropdownButtonWidget::DropdownButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey) :
439 ButtonWidget(boss, x, y, w, h, label, tooltip, cmd, hotkey) {
440 setFlags(getFlags() | WIDGET_TRACK_MOUSE);
441
442 reset();
443 }
444
DropdownButtonWidget(GuiObject * boss,const Common::String & name,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)445 DropdownButtonWidget::DropdownButtonWidget(GuiObject *boss, const Common::String &name, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey) :
446 ButtonWidget(boss, name, label, tooltip, cmd, hotkey) {
447 setFlags(getFlags() | WIDGET_TRACK_MOUSE);
448
449 reset();
450 }
451
reset()452 void DropdownButtonWidget::reset() {
453 _inDropdown = false;
454 _inButton = false;
455 _dropdownWidth = g_gui.xmlEval()->getVar("Globals.DropdownButton.Width", 13);
456 }
457
isInDropDown(int x,int y) const458 bool DropdownButtonWidget::isInDropDown(int x, int y) const {
459 Common::Rect dropdownRect(_w - _dropdownWidth, 0, _w, _h);
460 return dropdownRect.contains(x, y);
461 }
462
handleMouseMoved(int x,int y,int button)463 void DropdownButtonWidget::handleMouseMoved(int x, int y, int button) {
464 if (_entries.empty()) {
465 return;
466 }
467
468 // Detect which part of the button the cursor is over
469 bool inDropdown = isInDropDown(x, y);
470 bool inButton = Common::Rect(_w, _h).contains(x, y) && !inDropdown;
471
472 if (inDropdown != _inDropdown) {
473 _inDropdown = inDropdown;
474 markAsDirty();
475 }
476
477 if (inButton != _inButton) {
478 _inButton = inButton;
479 markAsDirty();
480 }
481 }
482
handleMouseUp(int x,int y,int button,int clickCount)483 void DropdownButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) {
484 if (isEnabled() && !_entries.empty() && _duringPress && isInDropDown(x, y)) {
485
486 PopUpDialog popupDialog(this, "DropdownDialog", x + getAbsX(), y + getAbsY());
487 popupDialog.setPosition(getAbsX(), getAbsY() + _h);
488 popupDialog.setLineHeight(_h);
489 popupDialog.setPadding(_dropdownWidth, _dropdownWidth);
490
491 for (uint i = 0; i < _entries.size(); i++) {
492 popupDialog.appendEntry(_entries[i].label);
493 }
494
495 int newSel = popupDialog.runModal();
496 if (newSel != -1) {
497 sendCommand(_entries[newSel].cmd, 0);
498 }
499
500 setUnpressedState();
501 _duringPress = false;
502 } else {
503 ButtonWidget::handleMouseUp(x, y, button, clickCount);
504 }
505 }
506
reflowLayout()507 void DropdownButtonWidget::reflowLayout() {
508 ButtonWidget::reflowLayout();
509
510 reset();
511 }
512
getMinSize(int & minWidth,int & minHeight)513 void DropdownButtonWidget::getMinSize(int &minWidth, int &minHeight) {
514 ButtonWidget::getMinSize(minWidth, minHeight);
515
516 if (minWidth >= 0) {
517 minWidth += _dropdownWidth * 2;
518 }
519 }
520
appendEntry(const Common::U32String & label,uint32 cmd)521 void DropdownButtonWidget::appendEntry(const Common::U32String &label, uint32 cmd) {
522 Entry e;
523 e.label = label;
524 e.cmd = cmd;
525 _entries.push_back(e);
526 }
527
clearEntries()528 void DropdownButtonWidget::clearEntries() {
529 _entries.clear();
530 }
531
drawWidget()532 void DropdownButtonWidget::drawWidget() {
533 if (_entries.empty()) {
534 // Degrade to a regular button
535 g_gui.theme()->drawButton(Common::Rect(_x, _y, _x + _w, _y + _h), _label, _state);
536 } else {
537 g_gui.theme()->drawDropDownButton(Common::Rect(_x, _y, _x + _w, _y + _h), _dropdownWidth, _label,
538 _state, _inButton, _inDropdown, (g_gui.useRTL() && _useRTL));
539 }
540 }
541
542 #pragma mark -
543
scaleGfx(const Graphics::ManagedSurface * gfx,int w,int h)544 const Graphics::ManagedSurface *scaleGfx(const Graphics::ManagedSurface *gfx, int w, int h) {
545 int nw = w, nh = h;
546
547 // Maintain aspect ratio
548 float xRatio = 1.0f * w / gfx->w;
549 float yRatio = 1.0f * h / gfx->h;
550
551 if (xRatio < yRatio)
552 nh = gfx->h * xRatio;
553 else
554 nw = gfx->w * yRatio;
555
556 if ((nw == w && nh == h) || (nw == gfx->w && nh == gfx->h))
557 return gfx;
558
559 w = nw;
560 h = nh;
561
562 Graphics::ManagedSurface tmp(*gfx);
563
564 const Graphics::ManagedSurface *tmp2 = new Graphics::ManagedSurface(tmp.surfacePtr()->scale(w, h, false));
565 tmp.free();
566
567 return tmp2;
568 }
569
PicButtonWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)570 PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
571 : ButtonWidget(boss, x, y, w, h, Common::U32String(), tooltip, cmd, hotkey),
572 _alpha(255), _transparency(false), _showButton(true) {
573
574 setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
575 _type = kButtonWidget;
576 }
577
PicButtonWidget(GuiObject * boss,const Common::String & name,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)578 PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
579 : ButtonWidget(boss, name, Common::U32String(), tooltip, cmd, hotkey),
580 _alpha(255), _transparency(false), _showButton(true) {
581 setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
582 _type = kButtonWidget;
583 }
584
~PicButtonWidget()585 PicButtonWidget::~PicButtonWidget() {
586 for (int i = 0; i < kPicButtonStateMax + 1; i++)
587 _gfx[i].free();
588 }
589
setGfx(const Graphics::ManagedSurface * gfx,int statenum,bool scale)590 void PicButtonWidget::setGfx(const Graphics::ManagedSurface *gfx, int statenum, bool scale) {
591 _gfx[statenum].free();
592
593 if (!gfx || !gfx->getPixels())
594 return;
595
596 if (gfx->format.bytesPerPixel == 1) {
597 warning("PicButtonWidget::setGfx got paletted surface passed");
598 return;
599 }
600
601 if (!isVisible() || !_boss->isVisible())
602 return;
603
604 float sf = g_gui.getScaleFactor();
605 if (scale && sf != 1.0) {
606 Graphics::Surface *tmp2 = gfx->rawSurface().scale(gfx->w * sf, gfx->h * sf, false);
607 _gfx[statenum].copyFrom(*tmp2);
608 tmp2->free();
609 delete tmp2;
610 } else {
611 _gfx[statenum].copyFrom(*gfx);
612 }
613 }
614
setGfx(const Graphics::Surface * gfx,int statenum,bool scale)615 void PicButtonWidget::setGfx(const Graphics::Surface *gfx, int statenum, bool scale) {
616 const Graphics::ManagedSurface *tmpGfx = new Graphics::ManagedSurface(gfx);
617 setGfx(tmpGfx, statenum, scale);
618 delete tmpGfx;
619 }
620
setGfxFromTheme(const char * name,int statenum,bool scale)621 void PicButtonWidget::setGfxFromTheme(const char *name, int statenum, bool scale) {
622 const Graphics::ManagedSurface *gfx = g_gui.theme()->getImageSurface(name);
623
624 setGfx(gfx, statenum, scale);
625
626 return;
627 }
628
setGfx(int w,int h,int r,int g,int b,int statenum)629 void PicButtonWidget::setGfx(int w, int h, int r, int g, int b, int statenum) {
630 _gfx[statenum].free();
631
632 if (!isVisible() || !_boss->isVisible())
633 return;
634
635 if (w == -1)
636 w = _w;
637 if (h == -1)
638 h = _h;
639
640 const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
641
642 _gfx[statenum].create(w, h, requiredFormat);
643 _gfx[statenum].fillRect(Common::Rect(0, 0, w, h), _gfx[statenum].format.RGBToColor(r, g, b));
644 }
645
drawWidget()646 void PicButtonWidget::drawWidget() {
647 if (_showButton)
648 g_gui.theme()->drawButton(Common::Rect(_x, _y, _x + _w, _y + _h), Common::U32String(), _state, getFlags());
649
650 Graphics::ManagedSurface *gfx;
651
652 if (_state == ThemeEngine::kStateHighlight)
653 gfx = &_gfx[kPicButtonHighlight];
654 else if (_state == ThemeEngine::kStateDisabled)
655 gfx = &_gfx[kPicButtonStateDisabled];
656 else if (_state == ThemeEngine::kStatePressed)
657 gfx = &_gfx[kPicButtonStatePressed];
658 else
659 gfx = &_gfx[kPicButtonStateEnabled];
660
661 if (!gfx->getPixels())
662 gfx = &_gfx[kPicButtonStateEnabled];
663
664 if (gfx->getPixels()) {
665 const int x = _x + (_w - gfx->w) / 2;
666 const int y = _y + (_h - gfx->h) / 2;
667
668 g_gui.theme()->drawSurface(Common::Point(x, y), *gfx, _transparency);
669 }
670 }
671
672 #pragma mark -
673
CheckboxWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)674 CheckboxWidget::CheckboxWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
675 : ButtonWidget(boss, x, y, w, h, label, tooltip, cmd, hotkey), _state(false) {
676 setFlags(WIDGET_ENABLED);
677 _type = kCheckboxWidget;
678 }
679
CheckboxWidget(GuiObject * boss,const Common::String & name,const Common::U32String & label,const Common::U32String & tooltip,uint32 cmd,uint8 hotkey)680 CheckboxWidget::CheckboxWidget(GuiObject *boss, const Common::String &name, const Common::U32String &label, const Common::U32String &tooltip, uint32 cmd, uint8 hotkey)
681 : ButtonWidget(boss, name, label, tooltip, cmd, hotkey), _state(false) {
682 setFlags(WIDGET_ENABLED);
683 _type = kCheckboxWidget;
684 }
685
handleMouseUp(int x,int y,int button,int clickCount)686 void CheckboxWidget::handleMouseUp(int x, int y, int button, int clickCount) {
687 if (isEnabled() && _duringPress && x >= 0 && x < _w && y >= 0 && y < _h) {
688 toggleState();
689 }
690 _duringPress = false;
691 }
692
setState(bool state)693 void CheckboxWidget::setState(bool state) {
694 if (_state != state) {
695 _state = state;
696 //_flags ^= WIDGET_INV_BORDER;
697 markAsDirty();
698 }
699 sendCommand(_cmd, _state);
700 }
701
drawWidget()702 void CheckboxWidget::drawWidget() {
703 g_gui.theme()->drawCheckbox(Common::Rect(_x, _y, _x + _w, _y + _h), _label, _state, Widget::_state, (g_gui.useRTL() && _useRTL));
704 }
705
706 #pragma mark -
RadiobuttonGroup(GuiObject * boss,uint32 cmd)707 RadiobuttonGroup::RadiobuttonGroup(GuiObject *boss, uint32 cmd) : CommandSender(boss) {
708 _value = -1;
709 _cmd = cmd;
710 }
711
setValue(int value)712 void RadiobuttonGroup::setValue(int value) {
713 Common::Array<RadiobuttonWidget *>::iterator button = _buttons.begin();
714 while (button != _buttons.end()) {
715 (*button)->setState((*button)->getValue() == value, false);
716
717 button++;
718 }
719
720 _value = value;
721
722 sendCommand(_cmd, _value);
723 }
724
setEnabled(bool ena)725 void RadiobuttonGroup::setEnabled(bool ena) {
726 Common::Array<RadiobuttonWidget *>::iterator button = _buttons.begin();
727 while (button != _buttons.end()) {
728 (*button)->setEnabled(ena);
729
730 button++;
731 }
732 }
733
734 #pragma mark -
735
RadiobuttonWidget(GuiObject * boss,int x,int y,int w,int h,RadiobuttonGroup * group,int value,const Common::U32String & label,const Common::U32String & tooltip,uint8 hotkey)736 RadiobuttonWidget::RadiobuttonWidget(GuiObject *boss, int x, int y, int w, int h, RadiobuttonGroup *group, int value, const Common::U32String &label, const Common::U32String &tooltip, uint8 hotkey)
737 : ButtonWidget(boss, x, y, w, h, label, tooltip, 0, hotkey), _state(false), _value(value), _group(group) {
738 setFlags(WIDGET_ENABLED);
739 _type = kRadiobuttonWidget;
740 _group->addButton(this);
741 }
742
RadiobuttonWidget(GuiObject * boss,const Common::String & name,RadiobuttonGroup * group,int value,const Common::U32String & label,const Common::U32String & tooltip,uint8 hotkey)743 RadiobuttonWidget::RadiobuttonWidget(GuiObject *boss, const Common::String &name, RadiobuttonGroup *group, int value, const Common::U32String &label, const Common::U32String &tooltip, uint8 hotkey)
744 : ButtonWidget(boss, name, label, tooltip, 0, hotkey), _state(false), _value(value), _group(group) {
745 setFlags(WIDGET_ENABLED);
746 _type = kRadiobuttonWidget;
747 _group->addButton(this);
748 }
749
handleMouseUp(int x,int y,int button,int clickCount)750 void RadiobuttonWidget::handleMouseUp(int x, int y, int button, int clickCount) {
751 if (isEnabled() && _duringPress && x >= 0 && x < _w && y >= 0 && y < _h) {
752 toggleState();
753 }
754 _duringPress = false;
755 }
756
setState(bool state,bool setGroup)757 void RadiobuttonWidget::setState(bool state, bool setGroup) {
758 if (setGroup) {
759 _group->setValue(_value);
760 return;
761 }
762
763 if (_state != state) {
764 _state = state;
765 //_flags ^= WIDGET_INV_BORDER;
766 markAsDirty();
767 }
768 sendCommand(_cmd, _state);
769 }
770
drawWidget()771 void RadiobuttonWidget::drawWidget() {
772 g_gui.theme()->drawRadiobutton(Common::Rect(_x, _y, _x + _w, _y + _h), _label, _state, Widget::_state, (g_gui.useRTL() && _useRTL));
773 }
774
775 #pragma mark -
776
SliderWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & tooltip,uint32 cmd)777 SliderWidget::SliderWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &tooltip, uint32 cmd)
778 : Widget(boss, x, y, w, h, tooltip), CommandSender(boss),
779 _cmd(cmd), _value(0), _oldValue(0), _valueMin(0), _valueMax(100), _isDragging(false), _labelWidth(0) {
780 setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG);
781 _type = kSliderWidget;
782 }
783
SliderWidget(GuiObject * boss,const Common::String & name,const Common::U32String & tooltip,uint32 cmd)784 SliderWidget::SliderWidget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip, uint32 cmd)
785 : Widget(boss, name, tooltip), CommandSender(boss),
786 _cmd(cmd), _value(0), _oldValue(0), _valueMin(0), _valueMax(100), _isDragging(false), _labelWidth(0) {
787 setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG);
788 _type = kSliderWidget;
789 }
790
handleMouseMoved(int x,int y,int button)791 void SliderWidget::handleMouseMoved(int x, int y, int button) {
792 if (g_gui.useRTL() && _useRTL == false) {
793 x = _w - x; // If internal flipping is off, adjust the mouse to behave as if it were LTR.
794 }
795 if (isEnabled() && _isDragging) {
796 int newValue = posToValue(x);
797 if (newValue < _valueMin)
798 newValue = _valueMin;
799 else if (newValue > _valueMax)
800 newValue = _valueMax;
801
802 if (newValue != _value) {
803 _value = newValue;
804 markAsDirty();
805 sendCommand(_cmd, _value); // FIXME - hack to allow for "live update" in sound dialog
806 }
807 }
808 }
809
handleMouseDown(int x,int y,int button,int clickCount)810 void SliderWidget::handleMouseDown(int x, int y, int button, int clickCount) {
811 if (isEnabled()) {
812 _isDragging = true;
813 handleMouseMoved(x, y, button);
814 }
815 }
816
handleMouseUp(int x,int y,int button,int clickCount)817 void SliderWidget::handleMouseUp(int x, int y, int button, int clickCount) {
818 if (isEnabled() && _isDragging) {
819 sendCommand(_cmd, _value);
820 }
821 _isDragging = false;
822 }
823
handleMouseWheel(int x,int y,int direction)824 void SliderWidget::handleMouseWheel(int x, int y, int direction) {
825 if (isEnabled() && !_isDragging) {
826 // Increment or decrement by one
827 int newValue = _value - direction;
828
829 if (newValue < _valueMin)
830 newValue = _valueMin;
831 else if (newValue > _valueMax)
832 newValue = _valueMax;
833
834 if (newValue != _value) {
835 _value = newValue;
836 markAsDirty();
837 sendCommand(_cmd, _value); // FIXME - hack to allow for "live update" in sound dialog
838 }
839 }
840 }
841
drawWidget()842 void SliderWidget::drawWidget() {
843 Common::Rect r1(_x, _y, _x + _w, _y + _h);
844 g_gui.theme()->drawSlider(r1, valueToBarWidth(_value), _state, (g_gui.useRTL() && _useRTL));
845 }
846
valueToBarWidth(int value)847 int SliderWidget::valueToBarWidth(int value) {
848 value = CLIP(value, _valueMin, _valueMax);
849 return (_w * (value - _valueMin) / (_valueMax - _valueMin));
850 }
851
valueToPos(int value)852 int SliderWidget::valueToPos(int value) {
853 value = CLIP(value, _valueMin, _valueMax);
854 return ((_w - 1) * (value - _valueMin + 1) / (_valueMax - _valueMin));
855 }
856
posToValue(int pos)857 int SliderWidget::posToValue(int pos) {
858 return (pos) * (_valueMax - _valueMin) / (_w - 1) + _valueMin;
859 }
860
861 #pragma mark -
862
GraphicsWidget(GuiObject * boss,int x,int y,int w,int h,const Common::U32String & tooltip)863 GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &tooltip)
864 : Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(255), _transparency(false) {
865 setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
866 _type = kGraphicsWidget;
867 }
868
GraphicsWidget(GuiObject * boss,const Common::String & name,const Common::U32String & tooltip)869 GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip)
870 : Widget(boss, name, tooltip), _gfx(), _alpha(255), _transparency(false) {
871 setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
872 _type = kGraphicsWidget;
873 }
874
~GraphicsWidget()875 GraphicsWidget::~GraphicsWidget() {
876 _gfx.free();
877 }
878
setGfx(const Graphics::ManagedSurface * gfx,bool scale)879 void GraphicsWidget::setGfx(const Graphics::ManagedSurface *gfx, bool scale) {
880 _gfx.free();
881
882 if (!gfx || !gfx->getPixels())
883 return;
884
885 if (gfx->format.bytesPerPixel == 1) {
886 warning("GraphicsWidget::setGfx got paletted surface passed");
887 return;
888 }
889
890 if (!isVisible() || !_boss->isVisible())
891 return;
892
893 float sf = g_gui.getScaleFactor();
894 if (scale && sf != 1.0) {
895 _w = gfx->w * sf;
896 _h = gfx->h * sf;
897 } else {
898 _w = gfx->w;
899 _h = gfx->h;
900 }
901
902 if ((_w != gfx->w || _h != gfx->h) && _w && _h) {
903 Graphics::Surface *tmp2 = gfx->rawSurface().scale(_w, _h, false);
904 _gfx.copyFrom(*tmp2);
905 tmp2->free();
906 delete tmp2;
907 } else {
908 _gfx.copyFrom(*gfx);
909 }
910 }
911
setGfx(const Graphics::Surface * gfx,bool scale)912 void GraphicsWidget::setGfx(const Graphics::Surface *gfx, bool scale) {
913 const Graphics::ManagedSurface *tmpGfx = new Graphics::ManagedSurface(gfx);
914 setGfx(tmpGfx, scale);
915 delete tmpGfx;
916 }
917
setGfx(int w,int h,int r,int g,int b)918 void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
919 _gfx.free();
920
921 if (!isVisible() || !_boss->isVisible())
922 return;
923
924 if (w == -1)
925 w = _w;
926 if (h == -1)
927 h = _h;
928
929 const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
930
931 _gfx.create(w, h, requiredFormat);
932 _gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b));
933 }
934
setGfxFromTheme(const char * name)935 void GraphicsWidget::setGfxFromTheme(const char *name) {
936 const Graphics::ManagedSurface *gfx = g_gui.theme()->getImageSurface(name);
937
938 setGfx(gfx, false);
939 }
940
drawWidget()941 void GraphicsWidget::drawWidget() {
942 if (_gfx.getPixels()) {
943 const int x = _x + (_w - _gfx.w) / 2;
944 const int y = _y + (_h - _gfx.h) / 2;
945
946 g_gui.theme()->drawSurface(Common::Point(x, y), _gfx, _transparency);
947 }
948 }
949
950 #pragma mark -
951
ContainerWidget(GuiObject * boss,int x,int y,int w,int h)952 ContainerWidget::ContainerWidget(GuiObject *boss, int x, int y, int w, int h) :
953 Widget(boss, x, y, w, h),
954 _backgroundType(ThemeEngine::kWidgetBackgroundBorder) {
955 setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
956 _type = kContainerWidget;
957 }
958
ContainerWidget(GuiObject * boss,const Common::String & name)959 ContainerWidget::ContainerWidget(GuiObject *boss, const Common::String &name) :
960 Widget(boss, name),
961 _backgroundType(ThemeEngine::kWidgetBackgroundBorder) {
962 setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
963 _type = kContainerWidget;
964 }
965
~ContainerWidget()966 ContainerWidget::~ContainerWidget() {
967 // We also remove the widget from the boss to avoid segfaults, when the
968 // deleted widget is an active widget in the boss.
969 for (Widget *w = _firstWidget; w; w = w->next()) {
970 _boss->removeWidget(w);
971 }
972 }
973
containsWidget(Widget * w) const974 bool ContainerWidget::containsWidget(Widget *w) const {
975 return containsWidgetInChain(_firstWidget, w);
976 }
977
findWidget(int x,int y)978 Widget *ContainerWidget::findWidget(int x, int y) {
979 Widget *w = findWidgetInChain(_firstWidget, x, y);
980 if (w)
981 return w;
982 return this;
983 }
984
removeWidget(Widget * widget)985 void ContainerWidget::removeWidget(Widget *widget) {
986 // We also remove the widget from the boss to avoid a reference to a
987 // widget not in the widget chain anymore.
988 _boss->removeWidget(widget);
989
990 Widget::removeWidget(widget);
991 }
992
setBackgroundType(ThemeEngine::WidgetBackground backgroundType)993 void ContainerWidget::setBackgroundType(ThemeEngine::WidgetBackground backgroundType) {
994 _backgroundType = backgroundType;
995 }
996
drawWidget()997 void ContainerWidget::drawWidget() {
998 g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), _backgroundType);
999 }
1000
1001 #pragma mark -
1002
OptionsContainerWidget(GuiObject * boss,const Common::String & name,const Common::String & dialogLayout,bool scrollable,const Common::String & domain)1003 OptionsContainerWidget::OptionsContainerWidget(GuiObject *boss, const Common::String &name, const Common::String &dialogLayout,
1004 bool scrollable, const Common::String &domain) :
1005 Widget(boss, name),
1006 _domain(domain),
1007 _dialogLayout(dialogLayout),
1008 _parentDialog(nullptr),
1009 _scrollContainer(nullptr) {
1010
1011 if (scrollable) {
1012 _scrollContainer = new ScrollContainerWidget(this, name, _dialogLayout, kReflowCmd);
1013 _scrollContainer->setTarget(this);
1014 _scrollContainer->setBackgroundType(GUI::ThemeEngine::kWidgetBackgroundNo);
1015 }
1016 }
1017
~OptionsContainerWidget()1018 OptionsContainerWidget::~OptionsContainerWidget() {
1019 }
1020
reflowLayout()1021 void OptionsContainerWidget::reflowLayout() {
1022 Widget::reflowLayout();
1023
1024 if (!_dialogLayout.empty()) {
1025 // Since different engines have different number of options,
1026 // we have to create it every time.
1027 defineLayout(*g_gui.xmlEval(), _dialogLayout, _name);
1028
1029 if (!_scrollContainer) {
1030 g_gui.xmlEval()->reflowDialogLayout(_dialogLayout, _firstWidget);
1031 }
1032 }
1033
1034 if (_scrollContainer) {
1035 _scrollContainer->resize(_x, _y, _w, _h, false);
1036 }
1037
1038 Widget *w = _firstWidget;
1039 while (w) {
1040 w->reflowLayout();
1041 w = w->next();
1042 }
1043 }
1044
containsWidget(Widget * widget) const1045 bool OptionsContainerWidget::containsWidget(Widget *widget) const {
1046 return containsWidgetInChain(_firstWidget, widget);
1047 }
1048
findWidget(int x,int y)1049 Widget *OptionsContainerWidget::findWidget(int x, int y) {
1050 // Iterate over all child widgets and find the one which was clicked
1051 return Widget::findWidgetInChain(_firstWidget, x, y);
1052 }
1053
removeWidget(Widget * widget)1054 void OptionsContainerWidget::removeWidget(Widget *widget) {
1055 _boss->removeWidget(widget);
1056 Widget::removeWidget(widget);
1057 }
1058
widgetsBoss()1059 GuiObject *OptionsContainerWidget::widgetsBoss() {
1060 if (_scrollContainer) {
1061 return _scrollContainer;
1062 }
1063
1064 return this;
1065 }
1066
1067 } // End of namespace GUI
1068