1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2008  Douglas Boffey <dougaboffey@netscape.net>
4  *  Copyright (C) 2011-2019  The ManaPlus Developers
5  *  Copyright (C) 2019-2021  Andrei Karas
6  *
7  *  This file is part of The ManaPlus Client.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "gui/widgets/tabs/setup_colors.h"
24 
25 #include "gui/gui.h"
26 #include "gui/userpalette.h"
27 
28 #include "gui/fonts/font.h"
29 
30 #include "gui/widgets/browserbox.h"
31 #include "gui/widgets/containerplacer.h"
32 #include "gui/widgets/createwidget.h"
33 #include "gui/widgets/label.h"
34 #include "gui/widgets/layouthelper.h"
35 #include "gui/widgets/listbox.h"
36 #include "gui/widgets/scrollarea.h"
37 #include "gui/widgets/slider.h"
38 #include "gui/widgets/textfield.h"
39 #include "gui/widgets/textpreview.h"
40 
41 #include "utils/delete2.h"
42 #include "utils/gettext.h"
43 #include "utils/stringutils.h"
44 
45 #include <cmath>
46 
47 #include "debug.h"
48 
49 const char *const Setup_Colors::rawmsg =
50     // TRANSLATORS: color selection preview message
51     N_("This is what the color looks like");
52 
Setup_Colors(const Widget2 * const widget)53 Setup_Colors::Setup_Colors(const Widget2 *const widget) :
54     SetupTab(widget),
55     SelectionListener(),
56     mColorBox(CREATEWIDGETR(ListBox, this, userPalette, "")),
57     mScroll(new ScrollArea(this, mColorBox,
58         Opaque_true, "setup_colors_background.xml")),
59     mPreview(new BrowserBox(this, Opaque_true,
60         "browserbox.xml")),
61     mTextPreview(new TextPreview(this, gettext(rawmsg))),
62     mPreviewBox(new ScrollArea(this, mPreview, Opaque_true,
63         "setup_colors_preview_background.xml")),
64     mSelected(-1),
65     // TRANSLATORS: colors tab. label.
66     mGradTypeLabel(new Label(this, _("Type:"))),
67     mGradTypeSlider(new Slider(this, 0.0, 3.0, 1.0)),
68     mGradTypeText(new Label(this)),
69     // TRANSLATORS: colors tab. label.
70     mGradDelayLabel(new Label(this, _("Delay:"))),
71     mGradDelaySlider(new Slider(this, 20.0, 100.0, 1.0)),
72     mGradDelayText(new TextField(this, std::string(), LoseFocusOnTab_true,
73         nullptr, std::string(), false)),
74     // TRANSLATORS: colors tab. label.
75     mRedLabel(new Label(this, _("Red:"))),
76     mRedSlider(new Slider(this, 0.0, 255.0, 1.0)),
77     mRedText(new TextField(this, std::string(), LoseFocusOnTab_true,
78         nullptr, std::string(), false)),
79     // TRANSLATORS: colors tab. label.
80     mGreenLabel(new Label(this, _("Green:"))),
81     mGreenSlider(new Slider(this, 0.0, 255.0, 1.0)),
82     mGreenText(new TextField(this, std::string(), LoseFocusOnTab_true,
83         nullptr, std::string(), false)),
84     // TRANSLATORS: colors tab. label.
85     mBlueLabel(new Label(this, _("Blue:"))),
86     mBlueSlider(new Slider(this, 0.0, 255.0, 1.0)),
87     mBlueText(new TextField(this, std::string(), LoseFocusOnTab_true,
88         nullptr, std::string(), false))
89 {
90     // TRANSLATORS: settings colors tab name
91     setName(_("Colors"));
92     mColorBox->addSelectionListener(this);
93     mScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
94     mPreview->setOpaque(Opaque_false);
95 
96     // don't do anything with links
97     mPreview->setLinkHandler(nullptr);
98 
99     mPreviewBox->setHeight(20);
100     mPreviewBox->setScrollPolicy(ScrollArea::SHOW_NEVER,
101         ScrollArea::SHOW_NEVER);
102 
103     mGradTypeSlider->setWidth(180);
104     mGradTypeSlider->setActionEventId("slider_grad");
105     mGradTypeSlider->setValue(0);
106     mGradTypeSlider->addActionListener(this);
107     mGradTypeSlider->setEnabled(false);
108 
109     // TRANSLATORS: color type
110     std::string longText = _("Static");
111 
112     const Font *const font = getFont();
113     // TRANSLATORS: color type
114     if (getFont()->getWidth(_("Pulse")) > font->getWidth(longText))
115     {
116         // TRANSLATORS: color type
117         longText = _("Pulse");
118     }
119     // TRANSLATORS: color type
120     if (getFont()->getWidth(_("Rainbow")) > font->getWidth(longText))
121     {
122         // TRANSLATORS: color type
123         longText = _("Rainbow");
124     }
125     // TRANSLATORS: color type
126     if (getFont()->getWidth(_("Spectrum")) > font->getWidth(longText))
127     {
128         // TRANSLATORS: color type
129         longText = _("Spectrum");
130     }
131 
132     mGradTypeText->setCaption(longText);
133 
134     mGradDelayText->setWidth(40);
135     mGradDelayText->setRange(20, 100);
136     mGradDelayText->setNumeric(true);
137     mGradDelayText->setEnabled(false);
138 
139     mGradDelaySlider->setWidth(180);
140     mGradDelaySlider->setValue(mGradDelayText->getValue());
141     mGradDelaySlider->setActionEventId("slider_graddelay");
142     mGradDelaySlider->addActionListener(this);
143     mGradDelaySlider->setEnabled(false);
144 
145     mRedText->setWidth(40);
146     mRedText->setRange(0, 255);
147     mRedText->setNumeric(true);
148     mRedText->setEnabled(false);
149 
150     mRedSlider->setWidth(180);
151     mRedSlider->setValue(mRedText->getValue());
152     mRedSlider->setActionEventId("slider_red");
153     mRedSlider->addActionListener(this);
154     mRedSlider->setEnabled(false);
155 
156     mGreenText->setWidth(40);
157     mGreenText->setRange(0, 255);
158     mGreenText->setNumeric(true);
159     mGreenText->setEnabled(false);
160 
161     mGreenSlider->setWidth(180);
162     mGreenSlider->setValue(mGreenText->getValue());
163     mGreenSlider->setActionEventId("slider_green");
164     mGreenSlider->addActionListener(this);
165     mGreenSlider->setEnabled(false);
166 
167     mBlueText->setWidth(40);
168     mBlueText->setRange(0, 255);
169     mBlueText->setNumeric(true);
170     mBlueText->setEnabled(false);
171 
172     mBlueSlider->setWidth(180);
173     mBlueSlider->setValue(mBlueText->getValue());
174     mBlueSlider->setActionEventId("slider_blue");
175     mBlueSlider->addActionListener(this);
176     mBlueSlider->setEnabled(false);
177 
178     setOpaque(Opaque_false);
179 
180     // Do the layout
181     LayoutHelper h(this);
182     ContainerPlacer place = h.getPlacer(0, 0);
183 
184     place(0, 0, mScroll, 6, 6).setPadding(2);
185     place(0, 6, mPreviewBox, 6, 1).setPadding(2);
186     place(0, 7, mGradTypeLabel, 3, 1);
187     place(3, 7, mGradTypeSlider, 1, 1);
188     place(4, 7, mGradTypeText, 2, 1).setPadding(1);
189     place(0, 8, mRedLabel, 3, 1);
190     place(3, 8, mRedSlider, 1, 1);
191     place(5, 8, mRedText, 1, 1).setPadding(1);
192     place(0, 9, mGreenLabel, 3, 1);
193     place(3, 9, mGreenSlider, 1, 1);
194     place(5, 9, mGreenText, 1, 1).setPadding(1);
195     place(0, 10, mBlueLabel, 3, 1);
196     place(3, 10, mBlueSlider, 1, 1);
197     place(5, 10, mBlueText, 1, 1).setPadding(1);
198     place(0, 11, mGradDelayLabel, 3, 1);
199     place(3, 11, mGradDelaySlider, 1, 1);
200     place(5, 11, mGradDelayText, 1, 1).setPadding(1);
201 
202     mGradTypeText->setCaption("");
203 
204     setDimension(Rect(0, 0, 365, 350));
205 }
206 
~Setup_Colors()207 Setup_Colors::~Setup_Colors()
208 {
209     if ((mPreviewBox != nullptr) && mPreviewBox->getContent() == mPreview)
210         delete2(mTextPreview)
211     else
212         delete2(mPreview)
213 }
214 
action(const ActionEvent & event)215 void Setup_Colors::action(const ActionEvent &event)
216 {
217     const std::string &eventId = event.getId();
218     if (eventId == "slider_grad")
219     {
220         updateGradType();
221         updateColor();
222         return;
223     }
224 
225     if (eventId == "slider_graddelay")
226     {
227         mGradDelayText->setText(toString(
228             std::floor(mGradDelaySlider->getValue())));
229         updateColor();
230         return;
231     }
232     if (eventId == "slider_red")
233     {
234         mRedText->setText(toString(std::floor(mRedSlider->getValue())));
235         updateColor();
236         return;
237     }
238     if (eventId == "slider_green")
239     {
240         mGreenText->setText(toString(std::floor(mGreenSlider->getValue())));
241         updateColor();
242         return;
243     }
244     if (eventId == "slider_blue")
245     {
246         mBlueText->setText(toString(std::floor(mBlueSlider->getValue())));
247         updateColor();
248         return;
249     }
250 }
251 
valueChanged(const SelectionEvent & event A_UNUSED)252 void Setup_Colors::valueChanged(const SelectionEvent &event A_UNUSED)
253 {
254     if (userPalette == nullptr)
255         return;
256 
257     mSelected = mColorBox->getSelected();
258     const UserColorIdT type = static_cast<UserColorIdT>(
259         userPalette->getColorTypeAt(mSelected));
260     const Color *col = &userPalette->getColor(type, 255U);
261     const GradientTypeT grad = userPalette->getGradientType(type);
262     const int delay = userPalette->getGradientDelay(type);
263     const Visible showControls = fromBool(grad != GradientType::LABEL,
264         Visible);
265     mPreview->setVisible(showControls);
266     mPreviewBox->setVisible(showControls);
267     mTextPreview->setVisible(showControls);
268     mGradTypeLabel->setVisible(showControls);
269     mGradTypeSlider->setVisible(showControls);
270     mGradTypeText->setVisible(showControls);
271     mGradDelayLabel->setVisible(showControls);
272     mGradDelaySlider->setVisible(showControls);
273     mGradDelayText->setVisible(showControls);
274     mRedLabel->setVisible(showControls);
275     mRedSlider->setVisible(showControls);
276     mRedText->setVisible(showControls);
277     mGreenLabel->setVisible(showControls);
278     mGreenSlider->setVisible(showControls);
279     mGreenText->setVisible(showControls);
280     mBlueLabel->setVisible(showControls);
281     mBlueSlider->setVisible(showControls);
282     mBlueText->setVisible(showControls);
283 
284     mPreview->clearRows();
285     mPreviewBox->setContent(mTextPreview);
286     mTextPreview->setFont(boldFont);
287     mTextPreview->setTextColor(col);
288     mTextPreview->setTextBGColor(nullptr);
289     mTextPreview->setOpaque(Opaque_false);
290     mTextPreview->setShadow(true);
291     mTextPreview->setOutline(true);
292     mTextPreview->useTextAlpha(false);
293 
294 // probably need combite both switches and add all mssing color ids.
295 
296     PRAGMA45(GCC diagnostic push)
297     PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
298     switch (type)
299     {
300         case UserColorId::AIR_COLLISION_HIGHLIGHT:
301         case UserColorId::WATER_COLLISION_HIGHLIGHT:
302         case UserColorId::MONSTER_COLLISION_HIGHLIGHT:
303         case UserColorId::GROUNDTOP_COLLISION_HIGHLIGHT:
304         case UserColorId::COLLISION_HIGHLIGHT:
305         case UserColorId::PORTAL_HIGHLIGHT:
306         case UserColorId::HOME_PLACE:
307         case UserColorId::ROAD_POINT:
308         case UserColorId::NET:
309             mTextPreview->setBGColor(col);
310             mTextPreview->setOpaque(Opaque_true);
311             mTextPreview->setOutline(false);
312             mTextPreview->setShadow(false);
313             break;
314         case UserColorId::ATTACK_RANGE_BORDER:
315         case UserColorId::HOME_PLACE_BORDER:
316             if (gui != nullptr)
317                 mTextPreview->setFont(gui->getFont());
318             mTextPreview->setTextColor(col);
319             mTextPreview->setOutline(false);
320             mTextPreview->setShadow(false);
321             break;
322         case UserColorId::PARTICLE:
323         case UserColorId::EXP_INFO:
324         case UserColorId::PICKUP_INFO:
325         case UserColorId::HIT_PLAYER_MONSTER:
326         case UserColorId::HIT_MONSTER_PLAYER:
327         case UserColorId::HIT_CRITICAL:
328         case UserColorId::MISS:
329         case UserColorId::HIT_LOCAL_PLAYER_MONSTER:
330         case UserColorId::HIT_LOCAL_PLAYER_CRITICAL:
331         case UserColorId::HIT_LOCAL_PLAYER_MISS:
332         case UserColorId::ATTACK_RANGE:
333         case UserColorId::MONSTER_ATTACK_RANGE:
334         case UserColorId::FLOOR_ITEM_TEXT:
335         case UserColorId::SKILL_ATTACK_RANGE:
336             mTextPreview->setShadow(false);
337             break;
338         default:
339             break;
340     }
341 
342     switch (type)
343     {
344         case UserColorId::PORTAL_HIGHLIGHT:
345         case UserColorId::ATTACK_RANGE:
346         case UserColorId::ATTACK_RANGE_BORDER:
347         case UserColorId::MONSTER_ATTACK_RANGE:
348         case UserColorId::SKILL_ATTACK_RANGE:
349         case UserColorId::HOME_PLACE:
350         case UserColorId::HOME_PLACE_BORDER:
351         case UserColorId::AIR_COLLISION_HIGHLIGHT:
352         case UserColorId::WATER_COLLISION_HIGHLIGHT:
353         case UserColorId::MONSTER_COLLISION_HIGHLIGHT:
354         case UserColorId::GROUNDTOP_COLLISION_HIGHLIGHT:
355         case UserColorId::COLLISION_HIGHLIGHT:
356         case UserColorId::WALKABLE_HIGHLIGHT:
357         case UserColorId::ROAD_POINT:
358         case UserColorId::MONSTER_HP:
359         case UserColorId::MONSTER_HP2:
360         case UserColorId::HOMUN_HP:
361         case UserColorId::HOMUN_HP2:
362         case UserColorId::MERC_HP:
363         case UserColorId::MERC_HP2:
364         case UserColorId::ELEMENTAL_HP:
365         case UserColorId::ELEMENTAL_HP2:
366         case UserColorId::PLAYER_HP:
367         case UserColorId::PLAYER_HP2:
368         case UserColorId::FLOOR_ITEM_TEXT:
369         case UserColorId::NET:
370             // TRANSLATORS: colors tab. label.
371             mGradDelayLabel->setCaption(_("Alpha:"));
372             mGradDelayText->setRange(0, 255);
373             mGradDelaySlider->setScale(0, 255);
374             break;
375         default:
376             // TRANSLATORS: colors tab. label.
377             mGradDelayLabel->setCaption(_("Delay:"));
378             mGradDelayText->setRange(20, 100);
379             mGradDelaySlider->setScale(20, 100);
380             break;
381     }
382     PRAGMA45(GCC diagnostic pop)
383     if (grad != GradientType::STATIC && grad != GradientType::PULSE)
384     { // If nonstatic color, don't display the current, but the committed
385       // color at the sliders
386         col = &userPalette->getCommittedColor(type);
387     }
388     else if (grad == GradientType::PULSE)
389     {
390         col = &userPalette->getTestColor(type);
391     }
392 
393     setEntry(mGradDelaySlider, mGradDelayText, delay);
394     setEntry(mRedSlider, mRedText, col->r);
395     setEntry(mGreenSlider, mGreenText, col->g);
396     setEntry(mBlueSlider, mBlueText, col->b);
397 
398     mGradTypeSlider->setValue(CAST_S32(grad));
399     updateGradType();
400     mGradTypeSlider->setEnabled(true);
401 }
402 
setEntry(Slider * const s,TextField * const t,const int value)403 void Setup_Colors::setEntry(Slider *const s, TextField *const t,
404                             const int value)
405 {
406     if (s != nullptr)
407         s->setValue(value);
408     if (t != nullptr)
409         t->setText(toString(value));
410 }
411 
apply()412 void Setup_Colors::apply()
413 {
414     if (userPalette != nullptr)
415         userPalette->commit();
416 }
417 
cancel()418 void Setup_Colors::cancel()
419 {
420     if (userPalette == nullptr)
421         return;
422 
423     userPalette->rollback();
424     const UserColorIdT type = static_cast<UserColorIdT>(
425         userPalette->getColorTypeAt(mSelected));
426     const Color *const col = &userPalette->getColor(type, 255U);
427     mGradTypeSlider->setValue(CAST_S32(
428         userPalette->getGradientType(type)));
429     const int delay = userPalette->getGradientDelay(type);
430     setEntry(mGradDelaySlider, mGradDelayText, delay);
431     setEntry(mRedSlider, mRedText, col->r);
432     setEntry(mGreenSlider, mGreenText, col->g);
433     setEntry(mBlueSlider, mBlueText, col->b);
434 }
435 
updateGradType()436 void Setup_Colors::updateGradType()
437 {
438     if (mSelected == -1 || (userPalette == nullptr))
439         return;
440 
441     mSelected = mColorBox->getSelected();
442     const UserColorIdT type = static_cast<UserColorIdT>(
443         userPalette->getColorTypeAt(mSelected));
444     const GradientTypeT grad = userPalette->getGradientType(type);
445 
446     mGradTypeText->setCaption(
447         // TRANSLATORS: color type
448         (grad == GradientType::STATIC) ? _("Static") :
449         // TRANSLATORS: color type
450         (grad == GradientType::PULSE) ? _("Pulse") :
451         // TRANSLATORS: color type
452         (grad == GradientType::RAINBOW) ? _("Rainbow") : _("Spectrum"));
453 
454     const bool enable = (grad == GradientType::STATIC ||
455         grad == GradientType::PULSE);
456     const bool delayEnable = true;
457 
458     mGradDelayText->setEnabled(delayEnable);
459     mGradDelaySlider->setEnabled(delayEnable);
460 
461     mRedText->setEnabled(enable);
462     mRedSlider->setEnabled(enable);
463     mGreenText->setEnabled(enable);
464     mGreenSlider->setEnabled(enable);
465     mBlueText->setEnabled(enable);
466     mBlueSlider->setEnabled(enable);
467 }
468 
updateColor() const469 void Setup_Colors::updateColor() const
470 {
471     if (mSelected == -1 || (userPalette == nullptr))
472         return;
473 
474     const UserColorIdT type = static_cast<UserColorIdT>(
475         userPalette->getColorTypeAt(mSelected));
476     const GradientTypeT grad = static_cast<GradientTypeT>(
477         CAST_S32(mGradTypeSlider->getValue()));
478     const int delay = CAST_S32(mGradDelaySlider->getValue());
479     userPalette->setGradient(type, grad);
480     userPalette->setGradientDelay(type, delay);
481 
482     if (grad == GradientType::STATIC)
483     {
484         userPalette->setColor(type,
485             CAST_S32(mRedSlider->getValue()),
486             CAST_S32(mGreenSlider->getValue()),
487             CAST_S32(mBlueSlider->getValue()));
488     }
489     else if (grad == GradientType::PULSE)
490     {
491         userPalette->setTestColor(type, Color(
492             CAST_S32(mRedSlider->getValue()),
493             CAST_S32(mGreenSlider->getValue()),
494             CAST_S32(mBlueSlider->getValue()),
495             255U));
496     }
497 }
498