1 /*
2 * Copyright (C) 2006-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20 #ifndef WL_EDITOR_UI_MENUS_CATEGORIZED_ITEM_SELECTION_MENU_H
21 #define WL_EDITOR_UI_MENUS_CATEGORIZED_ITEM_SELECTION_MENU_H
22
23 #include "boost/format.hpp"
24
25 #include "base/i18n.h"
26 #include "logic/map_objects/description_maintainer.h"
27 #include "logic/map_objects/world/editor_category.h"
28 #include "ui_basic/box.h"
29 #include "ui_basic/checkbox.h"
30 #include "ui_basic/multilinetextarea.h"
31 #include "ui_basic/panel.h"
32 #include "ui_basic/tabpanel.h"
33
34 template <typename DescriptionType, typename ToolType>
35 class CategorizedItemSelectionMenu : public UI::Box {
36 public:
37 // Creates a box with a tab panel for each category in 'categories' and
38 // populates them with the 'descriptions' ordered by the category by calling
39 // 'create_checkbox' for each of the descriptions. Calls
40 // 'select_correct_tool' whenever a selection has been made, also keeps a
41 // text label updated and updates the 'tool' with current selections. Does
42 // not take ownership.
43 CategorizedItemSelectionMenu(
44 UI::Panel* parent,
45 const Widelands::DescriptionMaintainer<Widelands::EditorCategory>& categories,
46 const Widelands::DescriptionMaintainer<DescriptionType>& descriptions,
47 std::function<UI::Checkbox*(UI::Panel* parent, const DescriptionType& descr)> create_checkbox,
48 const std::function<void()> select_correct_tool,
49 ToolType* const tool);
50
51 private:
52 // Called when an item was selected.
53 void selected(int32_t, bool);
54
55 // Update the label with the currently selected object names.
56 void update_label();
57
58 const Widelands::DescriptionMaintainer<DescriptionType>& descriptions_;
59 std::function<void()> select_correct_tool_;
60 bool protect_against_recursive_select_;
61 UI::TabPanel tab_panel_;
62 UI::MultilineTextarea current_selection_names_;
63 std::map<int, UI::Checkbox*> checkboxes_;
64 ToolType* const tool_; // not owned
65 };
66
67 template <typename DescriptionType, typename ToolType>
CategorizedItemSelectionMenu(UI::Panel * parent,const Widelands::DescriptionMaintainer<Widelands::EditorCategory> & categories,const Widelands::DescriptionMaintainer<DescriptionType> & descriptions,const std::function<UI::Checkbox * (UI::Panel * parent,const DescriptionType & descr)> create_checkbox,const std::function<void ()> select_correct_tool,ToolType * const tool)68 CategorizedItemSelectionMenu<DescriptionType, ToolType>::CategorizedItemSelectionMenu(
69 UI::Panel* parent,
70 const Widelands::DescriptionMaintainer<Widelands::EditorCategory>& categories,
71 const Widelands::DescriptionMaintainer<DescriptionType>& descriptions,
72 const std::function<UI::Checkbox*(UI::Panel* parent, const DescriptionType& descr)>
73 create_checkbox,
74 const std::function<void()> select_correct_tool,
75 ToolType* const tool)
76 : UI::Box(parent, 0, 0, UI::Box::Vertical),
77 descriptions_(descriptions),
78 select_correct_tool_(select_correct_tool),
79 protect_against_recursive_select_(false),
80 tab_panel_(this, UI::TabPanelStyle::kWuiLight),
81 current_selection_names_(this,
82 0,
83 0,
84 20,
85 20,
86 UI::PanelStyle::kWui,
87 "",
88 UI::Align::kCenter,
89 UI::MultilineTextarea::ScrollMode::kNoScrolling),
90 tool_(tool) {
91 add(&tab_panel_);
92
93 for (uint32_t category_index = 0; category_index < categories.size(); ++category_index) {
94 const Widelands::EditorCategory& category = categories.get(category_index);
95
96 std::vector<int> item_indices;
97 for (size_t j = 0; j < descriptions_.size(); ++j) {
98 if (descriptions_.get(j).editor_category()->name() != category.name()) {
99 continue;
100 }
101 item_indices.push_back(j);
102 }
103
104 UI::Box* vertical = new UI::Box(&tab_panel_, 0, 0, UI::Box::Vertical);
105 const int kSpacing = 5;
106 vertical->add_space(kSpacing);
107
108 int nitems_handled = 0;
109 UI::Box* horizontal = nullptr;
110 for (const int i : item_indices) {
111 if (nitems_handled % category.items_per_row() == 0) {
112 horizontal = new UI::Box(vertical, 0, 0, UI::Box::Horizontal);
113 horizontal->add_space(kSpacing);
114
115 vertical->add(horizontal);
116 vertical->add_space(kSpacing);
117 }
118 assert(horizontal != nullptr);
119
120 UI::Checkbox* cb = create_checkbox(horizontal, descriptions_.get(i));
121 cb->set_state(tool_->is_enabled(i));
122 cb->changedto.connect([this, i](bool b) { selected(i, b); });
123 checkboxes_[i] = cb;
124 horizontal->add(cb);
125 horizontal->add_space(kSpacing);
126 ++nitems_handled;
127 }
128 tab_panel_.add(category.name(), category.picture(), vertical, category.descname());
129 }
130 add(¤t_selection_names_, UI::Box::Resizing::kFullSize);
131 tab_panel_.sigclicked.connect([this]() { update_label(); });
132 update_label();
133 }
134
135 template <typename DescriptionType, typename ToolType>
selected(const int32_t n,const bool t)136 void CategorizedItemSelectionMenu<DescriptionType, ToolType>::selected(const int32_t n,
137 const bool t) {
138 if (protect_against_recursive_select_)
139 return;
140
141 // TODO(unknown): This code is erroneous. It checks the current key state. What it
142 // needs is the key state at the time the mouse was clicked. See the
143 // usage comment for get_key_state.
144 const bool multiselect = SDL_GetModState() & KMOD_CTRL;
145 if (!t && (!multiselect || tool_->get_nr_enabled() == 1))
146 checkboxes_[n]->set_state(true);
147 else {
148 if (!multiselect) {
149 for (uint32_t i = 0; tool_->get_nr_enabled(); ++i)
150 tool_->enable(i, false);
151 // disable all checkboxes
152 protect_against_recursive_select_ = true;
153 const int32_t size = checkboxes_.size();
154 for (int32_t i = 0; i < size; ++i) {
155 if (i != n)
156 checkboxes_[i]->set_state(false);
157 }
158 protect_against_recursive_select_ = false;
159 }
160
161 tool_->enable(n, t);
162 select_correct_tool_();
163 update_label();
164 }
165 }
166
167 template <typename DescriptionType, typename ToolType>
update_label()168 void CategorizedItemSelectionMenu<DescriptionType, ToolType>::update_label() {
169 current_selection_names_.set_size(tab_panel_.get_inner_w(), 20);
170 std::string buf;
171 constexpr int max_string_size = 100;
172 int j = tool_->get_nr_enabled();
173 for (int i = 0; j && buf.size() < max_string_size; ++i) {
174 if (tool_->is_enabled(i)) {
175 if (j < tool_->get_nr_enabled()) {
176 buf += " • ";
177 }
178 buf += descriptions_.get(i).descname();
179 --j;
180 }
181 }
182 if (buf.size() > max_string_size) {
183 /** TRANSLATORS: %s are the currently selected items in an editor tool*/
184 buf = (boost::format(_("Current: %s …")) % buf).str();
185 } else if (buf.empty()) {
186 /** TRANSLATORS: Help text in an editor tool*/
187 buf = _("Click to select an item. Use the Ctrl key to select multiple items.");
188 } else {
189 /** TRANSLATORS: %s are the currently selected items in an editor tool*/
190 buf = (boost::format(_("Current: %s")) % buf).str();
191 }
192 current_selection_names_.set_text(buf);
193 }
194
195 #endif // end of include guard: WL_EDITOR_UI_MENUS_CATEGORIZED_ITEM_SELECTION_MENU_H
196