1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2010 The Mana Developers
5 * Copyright (C) 2011-2019 The ManaPlus Developers
6 * Copyright (C) 2019-2021 Andrei Karas
7 *
8 * This file is part of The ManaPlus Client.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "gui/windowmenu.h"
25
26 #include "configuration.h"
27 #include "settings.h"
28
29 #include "input/inputmanager.h"
30
31 #include "gui/buttoninfo.h"
32 #include "gui/buttontext.h"
33 #include "gui/popupmanager.h"
34 #include "gui/skin.h"
35
36 #include "gui/popups/popupmenu.h"
37 #include "gui/popups/textpopup.h"
38
39 #include "gui/windows/skilldialog.h"
40
41 #include "gui/widgets/button.h"
42
43 #include "utils/dtor.h"
44 #include "utils/foreach.h"
45 #include "utils/gettext.h"
46
47 #include "resources/imageset.h"
48
49 #include "debug.h"
50
51 WindowMenu *windowMenu = nullptr;
52
WindowMenu(const Widget2 * const widget)53 WindowMenu::WindowMenu(const Widget2 *const widget) :
54 Container(widget),
55 ConfigListener(),
56 ActionListener(),
57 SelectionListener(),
58 MouseListener(),
59 mSkin(theme != nullptr ? theme->load("windowmenu.xml", "",
60 true, Theme::getThemePath()) : nullptr),
61 mImageSet(nullptr),
62 mPadding(mSkin != nullptr ? mSkin->getPadding() : 1),
63 mSpacing(mSkin != nullptr ? mSkin->getOption("spacing", 3) : 3),
64 mButtons(),
65 mButtonTexts(),
66 mButtonNames(),
67 mHaveMouse(false),
68 mAutoHide(1),
69 mSmallWindow(mainGraphics->getWidth() < 600)
70 {
71 int x = mPadding;
72 int h = 0;
73
74 setFocusable(false);
75
76 if (settings.showButtonIcons)
77 {
78 mImageSet = Theme::getImageSetFromTheme("buttonsicons.png",
79 mSkin->getOption("imageWidth", 20),
80 mSkin->getOption("imageHeight", 20));
81 }
82
83 // TRANSLATORS: short button name for who is online window.
84 addButton(N_("ONL"),
85 13,
86 // TRANSLATORS: long button name for who is online window.
87 _("Who is online"), x, h, InputAction::WINDOW_ONLINE,
88 Visible_true);
89 // TRANSLATORS: short button name for help window.
90 addButton(N_("HLP"),
91 0,
92 // TRANSLATORS: long button name for help window.
93 _("Help"), x, h, InputAction::WINDOW_HELP,
94 Visible_true);
95 // TRANSLATORS: short button name for quests window.
96 addButton(N_("QE"),
97 12,
98 // TRANSLATORS: long button name for quests window.
99 _("Quests"), x, h, InputAction::WINDOW_QUESTS,
100 Visible_true);
101 // TRANSLATORS: short button name for kill stats window.
102 addButton(N_("KS"),
103 16,
104 // TRANSLATORS: long button name for kill stats window.
105 _("Kill stats"), x, h, InputAction::WINDOW_KILLS,
106 Visible_true);
107 addButton(":-)",
108 6,
109 // TRANSLATORS: long button name for emotes window.
110 _("Smilies"), x, h, InputAction::WINDOW_EMOTE_SHORTCUT,
111 Visible_true);
112 // TRANSLATORS: short button name for chat window.
113 addButton(N_("CH"),
114 20,
115 // TRANSLATORS: longt button name for chat window.
116 _("Chat"), x, h, InputAction::WINDOW_CHAT,
117 #ifdef ANDROID
118 Visible_true);
119 #else // ANDROID
120 Visible_false);
121 #endif // ANDROID
122
123 // TRANSLATORS: short button name for status window.
124 addButton(N_("STA"),
125 17,
126 // TRANSLATORS: long button name for status window.
127 _("Status"), x, h, InputAction::WINDOW_STATUS,
128 Visible_true);
129 // TRANSLATORS: short button name for equipment window.
130 addButton(N_("EQU"),
131 7,
132 // TRANSLATORS: long button name for equipment window.
133 _("Equipment"), x, h, InputAction::WINDOW_EQUIPMENT,
134 Visible_true);
135 // TRANSLATORS: short button name for inventory window.
136 addButton(N_("INV"),
137 9,
138 // TRANSLATORS: long button name for inventory window.
139 _("Inventory"), x, h, InputAction::WINDOW_INVENTORY,
140 Visible_true);
141 // TRANSLATORS: short button name for cart window.
142 addButton(N_("CA"),
143 11,
144 // TRANSLATORS: long button name for cart window.
145 _("Cart"), x, h, InputAction::WINDOW_CART,
146 Visible_true);
147 // TRANSLATORS: short button name for map window.
148 addButton(N_("MAP"),
149 19,
150 // TRANSLATORS: long button name for map window.
151 _("Map"), x, h, InputAction::WINDOW_MINIMAP,
152 Visible_false);
153
154 if (skillDialog->hasSkills())
155 {
156 // TRANSLATORS: short button name for skills window.
157 addButton(N_("SKI"),
158 5,
159 // TRANSLATORS: long button name for skills window.
160 _("Skills"), x, h, InputAction::WINDOW_SKILL,
161 Visible_true);
162 }
163
164 // TRANSLATORS: short button name for social window.
165 addButton(N_("SOC"),
166 1,
167 // TRANSLATORS: long button name for social window.
168 _("Social"), x, h, InputAction::WINDOW_SOCIAL,
169 Visible_true);
170 // TRANSLATORS: short button name for shortcuts window.
171 addButton(N_("SH"),
172 14,
173 // TRANSLATORS: long button name for shortcuts window.
174 _("Shortcuts"), x, h, InputAction::WINDOW_SHORTCUT,
175 Visible_true);
176 // TRANSLATORS: short button name for spells window.
177 addButton(N_("SP"),
178 15,
179 // TRANSLATORS: long button name for spells window.
180 _("Spells"), x, h, InputAction::WINDOW_SPELLS,
181 Visible_true);
182 // TRANSLATORS: short button name for drops window.
183 addButton(N_("DR"),
184 26,
185 // TRANSLATORS: long button name for drops window.
186 _("Drop"), x, h, InputAction::WINDOW_DROP,
187 Visible_false);
188 // TRANSLATORS: short button name for did you know window.
189 addButton(N_("YK"),
190 24,
191 // TRANSLATORS: long button name for did you know window.
192 _("Did you know"), x, h, InputAction::WINDOW_DIDYOUKNOW,
193 Visible_false);
194 // TRANSLATORS: short button name for shop window.
195 addButton(N_("SHP"),
196 18,
197 // TRANSLATORS: long button name for shop window.
198 _("Shop"), x, h, InputAction::WINDOW_SHOP,
199 Visible_false);
200 // TRANSLATORS: short button name for outfits window.
201 addButton(N_("OU"),
202 21,
203 // TRANSLATORS: long button name for outfits window.
204 _("Outfits"), x, h, InputAction::WINDOW_OUTFIT,
205 Visible_false);
206 // TRANSLATORS: short button name for updates window.
207 addButton(N_("UP"),
208 25,
209 // TRANSLATORS: long button name for updates window.
210 _("Updates"), x, h, InputAction::WINDOW_UPDATER,
211 Visible_false);
212 // TRANSLATORS: short button name for bank window.
213 addButton(N_("BA"),
214 2,
215 // TRANSLATORS: long button name for bank window.
216 _("Bank"), x, h, InputAction::WINDOW_BANK,
217 Visible_true);
218 // TRANSLATORS: short button name for mail window.
219 addButton(N_("MA"),
220 10,
221 // TRANSLATORS: long button name for mail window.
222 _("Mail"), x, h, InputAction::WINDOW_MAIL,
223 Visible_true);
224 // TRANSLATORS: short button name for clan window.
225 addButton(N_("CL"),
226 4,
227 // TRANSLATORS: long button name for clan window.
228 _("Clan"), x, h, InputAction::WINDOW_CLAN,
229 Visible_true);
230 // TRANSLATORS: short button name for server info window.
231 addButton(N_("SI"),
232 8,
233 // TRANSLATORS: long button name for server info window.
234 _("Server info"), x, h, InputAction::WINDOW_SERVER_INFO,
235 Visible_true);
236 // TRANSLATORS: short button name for debug window.
237 addButton(N_("DBG"),
238 22,
239 // TRANSLATORS: long button name for debug window.
240 _("Debug"), x, h, InputAction::WINDOW_DEBUG,
241 #ifdef ANDROID
242 Visible_true);
243 #else // ANDROID
244 Visible_false);
245 #endif // ANDROID
246
247 // TRANSLATORS: short button name for windows list menu.
248 addButton(N_("WIN"),
249 23,
250 // TRANSLATORS: long button name for windows list menu.
251 _("Windows"), x, h, InputAction::SHOW_WINDOWS,
252 Visible_false);
253 // TRANSLATORS: short button name for setup window.
254 addButton(N_("SET"),
255 3,
256 // TRANSLATORS: long button name for setup window.
257 _("Setup"), x, h, InputAction::WINDOW_SETUP,
258 Visible_true);
259
260 x += mPadding - mSpacing;
261 if (mainGraphics != nullptr)
262 setDimension(Rect(mainGraphics->mWidth - x, 0, x, h));
263
264 loadButtons();
265
266 addMouseListener(this);
267 setVisible(Visible_true);
268
269 config.addListener("autohideButtons", this);
270 mAutoHide = config.getIntValue("autohideButtons");
271 }
272
~WindowMenu()273 WindowMenu::~WindowMenu()
274 {
275 config.removeListener("autohideButtons", this);
276 CHECKLISTENERS
277
278 for (std::map <std::string, ButtonInfo*>::iterator
279 it = mButtonNames.begin(),
280 it_fend = mButtonNames.end(); it != it_fend; ++it)
281 {
282 delete (*it).second;
283 }
284 mButtonNames.clear();
285 FOR_EACH (STD_VECTOR <Button*>::iterator, it, mButtons)
286 {
287 Button *const btn = *it;
288 if (btn == nullptr)
289 continue;
290 if (btn->mVisible == Visible_false)
291 delete btn;
292 }
293 delete_all(mButtonTexts);
294 mButtonTexts.clear();
295 if (mImageSet != nullptr)
296 {
297 mImageSet->decRef();
298 mImageSet = nullptr;
299 }
300 if (mSkin != nullptr)
301 {
302 if (theme != nullptr)
303 theme->unload(mSkin);
304 mSkin = nullptr;
305 }
306 }
307
action(const ActionEvent & event)308 void WindowMenu::action(const ActionEvent &event)
309 {
310 const std::string &eventId = event.getId();
311 const std::map <std::string, ButtonInfo*>::iterator
312 it = mButtonNames.find(eventId);
313 if (it == mButtonNames.end())
314 return;
315
316 const ButtonInfo *const info = (*it).second;
317 if (info == nullptr)
318 return;
319
320 inputManager.executeAction(info->key);
321 }
322
addButton(const char * const text,const int iconNumber,const std::string & description,int & restrict x,int & restrict h,const InputActionT key,const Visible visible)323 void WindowMenu::addButton(const char *const text,
324 const int iconNumber,
325 const std::string &description,
326 int &restrict x,
327 int &restrict h,
328 const InputActionT key,
329 const Visible visible)
330 {
331 Button *btn = nullptr;
332 if (settings.showButtonIcons &&
333 mImageSet)
334 {
335 Image *const image = mImageSet->get(iconNumber);
336 if (image != nullptr)
337 {
338 /*
339 btn = new Button(this,
340 "buttonsicons.png",
341 20,
342 20,
343 text,
344 BUTTON_SKIN,
345 this);
346 */
347 btn = new Button(this,
348 std::string(),
349 text,
350 BUTTON_SKIN,
351 this);
352 btn->setImage(image);
353 btn->adjustSize();
354 }
355 }
356 if (btn == nullptr)
357 {
358 btn = new Button(this,
359 gettext(text),
360 text,
361 BUTTON_SKIN,
362 this);
363 }
364 btn->setPosition(x, mPadding);
365 btn->setDescription(description);
366 btn->setTag(CAST_S32(key));
367 add(btn);
368 btn->setFocusable(false);
369 x += btn->getWidth() + mSpacing;
370 h = btn->getHeight() + 2 * mPadding;
371 mButtons.push_back(btn);
372 mButtonNames[text] = new ButtonInfo(btn, key, visible);
373 if (key != InputAction::SHOW_WINDOWS)
374 mButtonTexts.push_back(new ButtonText(description, key));
375 }
376
mousePressed(MouseEvent & event)377 void WindowMenu::mousePressed(MouseEvent &event)
378 {
379 if (popupManager == nullptr)
380 return;
381
382 if (event.getButton() == MouseButton::RIGHT)
383 {
384 if (mSmallWindow)
385 return;
386
387 event.consume();
388 Button *const btn = dynamic_cast<Button*>(event.getSource());
389 if (btn == nullptr)
390 return;
391 if (popupMenu != nullptr)
392 {
393 popupMenu->showPopup(getX() + event.getX(),
394 getY() + event.getY(), btn);
395 }
396 }
397 }
398
mouseMoved(MouseEvent & event)399 void WindowMenu::mouseMoved(MouseEvent &event)
400 {
401 mHaveMouse = true;
402
403 if (textPopup == nullptr)
404 return;
405
406 if (event.getSource() == this)
407 {
408 textPopup->hide();
409 return;
410 }
411
412 const Button *const btn = dynamic_cast<const Button *>(
413 event.getSource());
414
415 if (btn == nullptr)
416 {
417 textPopup->hide();
418 return;
419 }
420
421 const int x = event.getX();
422 const int y = event.getY();
423 const InputActionT key = static_cast<InputActionT>(btn->getTag());
424 const Rect &rect = mDimension;
425 if (key != InputAction::NO_VALUE)
426 {
427 textPopup->show(x + rect.x, y + rect.y, btn->getDescription(),
428 // TRANSLATORS: short key name
429 strprintf(_("Key: %s"), inputManager.getKeyValueString(
430 key).c_str()));
431 }
432 else
433 {
434 textPopup->show(x + rect.x, y + rect.y, btn->getDescription());
435 }
436 }
437
mouseExited(MouseEvent & event A_UNUSED)438 void WindowMenu::mouseExited(MouseEvent& event A_UNUSED)
439 {
440 mHaveMouse = false;
441 if (textPopup == nullptr)
442 return;
443
444 textPopup->hide();
445 }
446
showButton(const std::string & name,const Visible visible)447 void WindowMenu::showButton(const std::string &name,
448 const Visible visible)
449 {
450 const ButtonInfo *const info = mButtonNames[name];
451 if ((info == nullptr) || (info->button == nullptr))
452 return;
453
454 info->button->setVisible(visible);
455 updateButtons();
456 saveButtons();
457 }
458
updateButtons()459 void WindowMenu::updateButtons()
460 {
461 int x = mPadding;
462 int h = 0;
463 FOR_EACH (STD_VECTOR <Button*>::const_iterator, it, mButtons)
464 safeRemove(*it);
465 const int pad2 = 2 * mPadding;
466 FOR_EACH (STD_VECTOR <Button*>::iterator, it, mButtons)
467 {
468 Button *const btn = *it;
469 if (btn == nullptr)
470 continue;
471 if (btn->mVisible == Visible_true)
472 {
473 btn->setPosition(x, mPadding);
474 add(btn);
475 x += btn->getWidth() + mSpacing;
476 h = btn->getHeight() + pad2;
477 }
478 }
479 x += mPadding - mSpacing;
480 if (mainGraphics != nullptr)
481 setDimension(Rect(mainGraphics->mWidth - x, 0, x, h));
482 }
483
loadButtons()484 void WindowMenu::loadButtons()
485 {
486 if (!mSmallWindow)
487 {
488 if (config.getValue("windowmenu0", "").empty())
489 {
490 for (std::map <std::string, ButtonInfo*>::iterator
491 it = mButtonNames.begin(),
492 it_fend = mButtonNames.end(); it != it_fend; ++it)
493 {
494 const ButtonInfo *const info = (*it).second;
495 if (info == nullptr ||
496 info->button == nullptr ||
497 info->visible == Visible_true)
498 {
499 continue;
500 }
501 info->button->setVisible(Visible_false);
502 }
503 updateButtons();
504 return;
505 }
506
507 for (int f = 0; f < 30; f ++)
508 {
509 const std::string str = config.getValue(
510 "windowmenu" + toString(f), "");
511 if (str.empty() || str == "SET")
512 continue;
513 const ButtonInfo *const info = mButtonNames[str];
514 if ((info == nullptr) || (info->button == nullptr))
515 continue;
516 info->button->setVisible(Visible_false);
517 }
518 }
519 else
520 {
521 for (std::map <std::string, ButtonInfo*>::iterator
522 it = mButtonNames.begin(),
523 it_fend = mButtonNames.end(); it != it_fend; ++it)
524 {
525 const ButtonInfo *const info = (*it).second;
526 if ((info == nullptr) || (info->button == nullptr))
527 continue;
528 Button *const button = info->button;
529 const std::string &str = button->getActionEventId();
530 button->setVisible(fromBool(
531 str == "SET" || str == "WIN", Visible));
532 }
533 }
534 updateButtons();
535 }
536
saveButtons() const537 void WindowMenu::saveButtons() const
538 {
539 int i = 0;
540 FOR_EACH (STD_VECTOR <Button*>::const_iterator, it, mButtons)
541 {
542 const Button *const btn = *it;
543 if ((btn != nullptr) && btn->mVisible == Visible_false)
544 {
545 config.setValue("windowmenu" + toString(i),
546 btn->getActionEventId());
547 i ++;
548 }
549 }
550 for (int f = i; f < 30; f ++)
551 config.deleteKey("windowmenu" + toString(f));
552 }
553
drawChildren(Graphics * const graphics)554 void WindowMenu::drawChildren(Graphics *const graphics)
555 {
556 if (mHaveMouse ||
557 mAutoHide == 0 ||
558 (mAutoHide == 1 &&
559 mainGraphics != nullptr &&
560 (mSmallWindow || mainGraphics->mWidth > 800)))
561 {
562 Container::drawChildren(graphics);
563 }
564 }
565
safeDrawChildren(Graphics * const graphics)566 void WindowMenu::safeDrawChildren(Graphics *const graphics)
567 {
568 if (mHaveMouse ||
569 mAutoHide == 0 ||
570 (mAutoHide == 1 &&
571 mainGraphics != nullptr &&
572 (mSmallWindow || mainGraphics->mWidth > 800)))
573 {
574 Container::safeDrawChildren(graphics);
575 }
576 }
577
optionChanged(const std::string & name)578 void WindowMenu::optionChanged(const std::string &name)
579 {
580 if (name == "autohideButtons")
581 mAutoHide = config.getIntValue("autohideButtons");
582 }
583
584 #ifdef USE_PROFILER
logicChildren()585 void WindowMenu::logicChildren()
586 {
587 BLOCK_START("WindowMenu::logicChildren")
588 BasicContainer::logicChildren();
589 BLOCK_END("WindowMenu::logicChildren")
590 }
591 #endif // USE_PROFILER
592