1 /*
2 * Copyright (C) 2008-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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "wui/economy_options_window.h"
21
22 #include <memory>
23
24 #include "graphic/graphic.h"
25 #include "io/profile.h"
26 #include "logic/editor_game_base.h"
27 #include "logic/filesystem_constants.h"
28 #include "logic/map_objects/tribes/ware_descr.h"
29 #include "logic/map_objects/tribes/worker_descr.h"
30 #include "logic/player.h"
31 #include "logic/playercommand.h"
32 #include "ui_basic/messagebox.h"
33
34 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
35 static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";
36
37 constexpr int kDesiredWidth = 216;
38
EconomyOptionsWindow(UI::Panel * parent,Widelands::Economy * ware_economy,Widelands::Economy * worker_economy,bool can_act)39 EconomyOptionsWindow::EconomyOptionsWindow(UI::Panel* parent,
40 Widelands::Economy* ware_economy,
41 Widelands::Economy* worker_economy,
42 bool can_act)
43 : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")),
44 main_box_(this, 0, 0, UI::Box::Vertical),
45 ware_serial_(ware_economy->serial()),
46 worker_serial_(worker_economy->serial()),
47 player_(&ware_economy->owner()),
48 tabpanel_(this, UI::TabPanelStyle::kWuiDark),
49 ware_panel_(new EconomyOptionsPanel(
50 &tabpanel_, this, ware_serial_, player_, can_act, Widelands::wwWARE, kDesiredWidth)),
51 worker_panel_(new EconomyOptionsPanel(
52 &tabpanel_, this, worker_serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)),
53 dropdown_box_(this, 0, 0, UI::Box::Horizontal),
54 dropdown_(&dropdown_box_,
55 "economy_profiles",
56 0,
57 0,
58 174,
59 10,
60 34,
61 "",
62 UI::DropdownType::kTextual,
63 UI::PanelStyle::kWui,
64 UI::ButtonStyle::kWuiSecondary),
65 time_last_thought_(0),
66 save_profile_dialog_(nullptr) {
67 set_center_panel(&main_box_);
68
69 tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares"));
70 tabpanel_.add("workers", g_gr->images().get(pic_tab_workers), worker_panel_, _("Workers"));
71
72 UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
73 UI::Button* b = new UI::Button(
74 buttons, "decrease_target_fast", 0, 0, 40, 28, UI::ButtonStyle::kWuiSecondary,
75 g_gr->images().get("images/ui_basic/scrollbar_down_fast.png"), _("Decrease target by 10"));
76 b->sigclicked.connect([this] { change_target(-10); });
77 buttons->add(b);
78 b->set_repeating(true);
79 b->set_enabled(can_act);
80 buttons->add_space(6);
81 b = new UI::Button(buttons, "decrease_target", 0, 0, 40, 28, UI::ButtonStyle::kWuiSecondary,
82 g_gr->images().get("images/ui_basic/scrollbar_down.png"),
83 _("Decrease target"));
84 b->sigclicked.connect([this] { change_target(-1); });
85 buttons->add(b);
86 b->set_repeating(true);
87 b->set_enabled(can_act);
88 buttons->add_space(6);
89
90 b = new UI::Button(buttons, "toggle_infinite", 0, 0, 32, 28, UI::ButtonStyle::kWuiSecondary,
91 g_gr->images().get("images/wui/menus/infinity.png"),
92 _("Toggle infinite target"));
93 b->sigclicked.connect([this] { toggle_infinite(); });
94 buttons->add(b);
95 b->set_repeating(false);
96 b->set_enabled(can_act);
97 buttons->add_space(6);
98
99 b = new UI::Button(buttons, "increase_target", 0, 0, 40, 28, UI::ButtonStyle::kWuiSecondary,
100 g_gr->images().get("images/ui_basic/scrollbar_up.png"), _("Increase target"));
101 b->sigclicked.connect([this] { change_target(1); });
102 buttons->add(b);
103 b->set_repeating(true);
104 b->set_enabled(can_act);
105 buttons->add_space(6);
106 b = new UI::Button(buttons, "increase_target_fast", 0, 0, 40, 28, UI::ButtonStyle::kWuiSecondary,
107 g_gr->images().get("images/ui_basic/scrollbar_up_fast.png"),
108 _("Increase target by 10"));
109 b->sigclicked.connect([this] { change_target(10); });
110 buttons->add(b);
111 b->set_repeating(true);
112 b->set_enabled(can_act);
113
114 dropdown_.set_tooltip(_("Profile to apply to the selected items"));
115 dropdown_box_.set_size(40, 20); // Prevent assert failures
116 dropdown_box_.add(&dropdown_, UI::Box::Resizing::kFullSize);
117 if (can_act) {
118 dropdown_.selected.connect([this] { reset_target(); });
119 } else {
120 dropdown_.set_enabled(false);
121 }
122
123 b = new UI::Button(&dropdown_box_, "save_targets", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu,
124 g_gr->images().get("images/wui/menus/save_game.png"),
125 _("Save target settings"));
126 b->sigclicked.connect([this] { create_target(); });
127 dropdown_box_.add_space(8);
128 dropdown_box_.add(b);
129
130 main_box_.add(&tabpanel_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
131 main_box_.add_space(8);
132 main_box_.add(buttons, UI::Box::Resizing::kAlign, UI::Align::kCenter);
133 main_box_.add_space(8);
134 main_box_.add(&dropdown_box_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
135
136 ware_economy->set_options_window(static_cast<void*>(this));
137 worker_economy->set_options_window(static_cast<void*>(this));
138 economynotes_subscriber_ = Notifications::subscribe<Widelands::NoteEconomy>(
139 [this](const Widelands::NoteEconomy& note) { on_economy_note(note); });
140 profilenotes_subscriber_ =
141 Notifications::subscribe<NoteEconomyProfile>([this](const NoteEconomyProfile& n) {
142 if (n.ware_serial == ware_serial_ && n.worker_serial == worker_serial_) {
143 // We already updated ourself before we changed something
144 return;
145 }
146 read_targets();
147 if (save_profile_dialog_) {
148 save_profile_dialog_->update_table();
149 }
150 });
151
152 read_targets();
153 }
154
~EconomyOptionsWindow()155 EconomyOptionsWindow::~EconomyOptionsWindow() {
156 if (Widelands::Economy* e_wa = player_->get_economy(ware_serial_)) {
157 e_wa->set_options_window(nullptr);
158 }
159 if (Widelands::Economy* e_wo = player_->get_economy(worker_serial_)) {
160 e_wo->set_options_window(nullptr);
161 }
162 if (save_profile_dialog_) {
163 save_profile_dialog_->unset_parent();
164 }
165 }
166
localize_profile_name(const std::string & name)167 std::string EconomyOptionsWindow::localize_profile_name(const std::string& name) {
168 // Translation for the default profile is sourced from the widelands textdomain, and for the
169 // other profiles from the tribes.
170 std::string localized_name = _(name);
171 {
172 i18n::Textdomain td("tribes");
173 localized_name = _(localized_name);
174 }
175 return localized_name;
176 }
177
on_economy_note(const Widelands::NoteEconomy & note)178 void EconomyOptionsWindow::on_economy_note(const Widelands::NoteEconomy& note) {
179 Widelands::Serial* serial = note.old_economy == ware_serial_ ?
180 &ware_serial_ :
181 note.old_economy == worker_serial_ ? &worker_serial_ : nullptr;
182 if (serial) {
183 switch (note.action) {
184 case Widelands::NoteEconomy::Action::kMerged: {
185 *serial = note.new_economy;
186 Widelands::Economy* economy = player_->get_economy(*serial);
187 if (economy == nullptr) {
188 die();
189 return;
190 }
191 economy->set_options_window(static_cast<void*>(this));
192 (*serial == ware_serial_ ? ware_panel_ : worker_panel_)->set_economy(note.new_economy);
193 move_to_top();
194 } break;
195 case Widelands::NoteEconomy::Action::kDeleted:
196 // Make sure that the panels stop thinking first.
197 die();
198 break;
199 }
200 }
201 }
202
layout()203 void EconomyOptionsWindow::layout() {
204 int w, h;
205 tabpanel_.get_desired_size(&w, &h);
206 main_box_.set_desired_size(w, h + 78);
207 update_desired_size();
208 UI::Window::layout();
209 }
210
update_desired_size()211 void EconomyOptionsWindow::EconomyOptionsPanel::update_desired_size() {
212 display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, kDesiredWidth));
213 Box::update_desired_size();
214 get_parent()->layout();
215 }
216
TargetWaresDisplay(UI::Panel * const parent,int32_t const x,int32_t const y,Widelands::Serial serial,Widelands::Player * player,Widelands::WareWorker type,bool selectable)217 EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent,
218 int32_t const x,
219 int32_t const y,
220 Widelands::Serial serial,
221 Widelands::Player* player,
222 Widelands::WareWorker type,
223 bool selectable)
224 : AbstractWaresDisplay(parent, x, y, player->tribe(), type, selectable),
225 serial_(serial),
226 player_(player) {
227 const Widelands::TribeDescr& owner_tribe = player->tribe();
228 if (type == Widelands::wwWORKER) {
229 for (const Widelands::DescriptionIndex& worker_index : owner_tribe.workers()) {
230 const Widelands::WorkerDescr* worker_descr = owner_tribe.get_worker_descr(worker_index);
231 if (!worker_descr->has_demand_check()) {
232 hide_ware(worker_index);
233 }
234 }
235 } else {
236 for (const Widelands::DescriptionIndex& ware_index : owner_tribe.wares()) {
237 const Widelands::WareDescr* ware_descr = owner_tribe.get_ware_descr(ware_index);
238 if (!ware_descr->has_demand_check(owner_tribe.name())) {
239 hide_ware(ware_index);
240 }
241 }
242 }
243 }
244
set_economy(Widelands::Serial serial)245 void EconomyOptionsWindow::TargetWaresDisplay::set_economy(Widelands::Serial serial) {
246 serial_ = serial;
247 }
248
249 std::string
info_for_ware(Widelands::DescriptionIndex const ware)250 EconomyOptionsWindow::TargetWaresDisplay::info_for_ware(Widelands::DescriptionIndex const ware) {
251 Widelands::Economy* economy = player_->get_economy(serial_);
252 if (economy == nullptr) {
253 die();
254 return *(new std::string());
255 }
256 const Widelands::Quantity amount = economy->target_quantity(ware).permanent;
257 if (amount == Widelands::kEconomyTargetInfinity) {
258 /** TRANSLATORS: Infinite number of wares or workers */
259 return g_gr->styles().font_style(UI::FontStyle::kLabel).as_font_tag(_("∞"));
260 }
261 return boost::lexical_cast<std::string>(amount);
262 }
263
264 /**
265 * Wraps the wares/workers display together with some buttons
266 */
EconomyOptionsPanel(UI::Panel * parent,EconomyOptionsWindow * eco_window,Widelands::Serial serial,Widelands::Player * player,bool can_act,Widelands::WareWorker type,int32_t min_w)267 EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent,
268 EconomyOptionsWindow* eco_window,
269 Widelands::Serial serial,
270 Widelands::Player* player,
271 bool can_act,
272 Widelands::WareWorker type,
273 int32_t min_w)
274 : UI::Box(parent, 0, 0, UI::Box::Vertical),
275 serial_(serial),
276 player_(player),
277 type_(type),
278 display_(this, 0, 0, serial_, player_, type, can_act),
279 economy_options_window_(eco_window) {
280 add(&display_, UI::Box::Resizing::kFullSize);
281
282 display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, min_w));
283 }
284
set_economy(Widelands::Serial serial)285 void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) {
286 serial_ = serial;
287 display_.set_economy(serial);
288 }
289
toggle_infinite()290 void EconomyOptionsWindow::toggle_infinite() {
291 if (tabpanel_.active() == 0) {
292 ware_panel_->toggle_infinite();
293 } else {
294 worker_panel_->toggle_infinite();
295 }
296 }
297
change_target(int amount)298 void EconomyOptionsWindow::change_target(int amount) {
299 if (tabpanel_.active() == 0) {
300 ware_panel_->change_target(amount);
301 } else {
302 worker_panel_->change_target(amount);
303 }
304 }
305
reset_target()306 void EconomyOptionsWindow::reset_target() {
307 if (dropdown_.get_selected().empty()) {
308 return;
309 }
310 if (tabpanel_.active() == 0) {
311 ware_panel_->reset_target();
312 } else {
313 worker_panel_->reset_target();
314 }
315 }
316
toggle_infinite()317 void EconomyOptionsWindow::EconomyOptionsPanel::toggle_infinite() {
318 Widelands::Economy* economy = player_->get_economy(serial_);
319 if (!economy) {
320 return die();
321 }
322 assert(economy->type() == type_);
323 Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
324 const bool is_wares = type_ == Widelands::wwWARE;
325 const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
326 for (const Widelands::DescriptionIndex& index : items) {
327 if (display_.ware_selected(index)) {
328 const Widelands::Economy::TargetQuantity& tq = economy->target_quantity(index);
329 Widelands::Quantity new_quantity;
330 if (tq.permanent == Widelands::kEconomyTargetInfinity) {
331 auto it = infinity_substitutes_.find(index);
332 if (it == infinity_substitutes_.end()) {
333 // The window was opended with the target set to infinite,
334 // so we just use the default value
335 new_quantity = is_wares ?
336 economy_options_window_->get_predefined_targets()
337 .at(kDefaultEconomyProfile)
338 .wares.at(index) :
339 economy_options_window_->get_predefined_targets()
340 .at(kDefaultEconomyProfile)
341 .workers.at(index);
342 } else {
343 // Restore saved old value
344 new_quantity = it->second;
345 infinity_substitutes_.erase(it);
346 }
347 } else {
348 new_quantity = Widelands::kEconomyTargetInfinity;
349 // Save old target for when the infinity option is disabled again
350 infinity_substitutes_[index] = tq.permanent;
351 }
352 if (is_wares) {
353 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
354 game.get_gametime(), player_->player_number(), serial_, index, new_quantity));
355 } else {
356 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
357 game.get_gametime(), player_->player_number(), serial_, index, new_quantity));
358 }
359 }
360 }
361 }
362
change_target(int delta)363 void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int delta) {
364 if (delta == 0) {
365 return;
366 }
367 Widelands::Economy* economy = player_->get_economy(serial_);
368 if (!economy) {
369 return die();
370 }
371 assert(economy->type() == type_);
372 Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
373 const bool is_wares = type_ == Widelands::wwWARE;
374 const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
375 for (const Widelands::DescriptionIndex& index : items) {
376 if (display_.ware_selected(index)) {
377 const Widelands::Economy::TargetQuantity& tq = economy->target_quantity(index);
378 if (tq.permanent == Widelands::kEconomyTargetInfinity) {
379 // Infinity ± finite value = infinity
380 continue;
381 }
382 // Don't allow negative new amount
383 const int old_amount = static_cast<int>(tq.permanent);
384 const int new_amount = std::max(0, old_amount + delta);
385 assert(old_amount >= 0);
386 assert(new_amount >= 0);
387 if (new_amount == old_amount) {
388 continue;
389 }
390 if (is_wares) {
391 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
392 game.get_gametime(), player_->player_number(), serial_, index, new_amount));
393 } else {
394 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
395 game.get_gametime(), player_->player_number(), serial_, index, new_amount));
396 }
397 }
398 }
399 }
400
reset_target()401 void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {
402 Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
403 const bool is_wares = type_ == Widelands::wwWARE;
404 const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
405 const PredefinedTargets settings = economy_options_window_->get_selected_target();
406
407 bool anything_selected = false;
408 for (const Widelands::DescriptionIndex& index : items) {
409 if (display_.ware_selected(index)) {
410 anything_selected = true;
411 break;
412 }
413 }
414 for (const Widelands::DescriptionIndex& index : items) {
415 if (display_.ware_selected(index) ||
416 (!anything_selected && !display_.is_ware_hidden(index))) {
417 if (is_wares) {
418 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
419 game.get_gametime(), player_->player_number(), serial_, index,
420 settings.wares.at(index)));
421 } else {
422 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
423 game.get_gametime(), player_->player_number(), serial_, index,
424 settings.workers.at(index)));
425 }
426 }
427 }
428 }
429
430 constexpr unsigned kThinkInterval = 200;
431
think()432 void EconomyOptionsWindow::think() {
433 const uint32_t time = player_->egbase().get_gametime();
434 if (time - time_last_thought_ < kThinkInterval || !player_->get_economy(ware_serial_) ||
435 !player_->get_economy(worker_serial_)) {
436 // If our economy has been deleted, die() was already called, no need to do anything
437 return;
438 }
439 time_last_thought_ = time;
440 update_profiles();
441 }
442
applicable_target()443 std::string EconomyOptionsWindow::applicable_target() {
444 const Widelands::Economy* eco_ware = player_->get_economy(ware_serial_);
445 const Widelands::Economy* eco_worker = player_->get_economy(worker_serial_);
446 for (const auto& pair : predefined_targets_) {
447 bool matches = true;
448 if (tabpanel_.active() == 0) {
449 for (const Widelands::DescriptionIndex& index : player_->tribe().wares()) {
450 const auto it = pair.second.wares.find(index);
451 if (it != pair.second.wares.end() &&
452 eco_ware->target_quantity(index).permanent != it->second) {
453 matches = false;
454 break;
455 }
456 }
457 } else {
458 for (const Widelands::DescriptionIndex& index : player_->tribe().workers()) {
459 const auto it = pair.second.workers.find(index);
460 if (it != pair.second.workers.end() &&
461 eco_worker->target_quantity(index).permanent != it->second) {
462 matches = false;
463 break;
464 }
465 }
466 }
467 if (matches) {
468 return pair.first;
469 }
470 }
471 return "";
472 }
473
update_profiles()474 void EconomyOptionsWindow::update_profiles() {
475 const std::string current_profile = applicable_target();
476
477 for (const auto& pair : predefined_targets_) {
478 if (last_added_to_dropdown_.count(pair.first) == 0) {
479 return update_profiles_needed(current_profile);
480 }
481 }
482 for (const auto& string : last_added_to_dropdown_) {
483 if (!string.empty() && predefined_targets_.find(string) == predefined_targets_.end()) {
484 return update_profiles_needed(current_profile);
485 }
486 }
487 if (last_added_to_dropdown_.count("") == (current_profile.empty() ? 0 : 1)) {
488 return update_profiles_needed(current_profile);
489 }
490
491 update_profiles_select(current_profile);
492 }
493
update_profiles_needed(const std::string & current_profile)494 void EconomyOptionsWindow::update_profiles_needed(const std::string& current_profile) {
495 dropdown_.clear();
496 last_added_to_dropdown_.clear();
497 for (const auto& pair : predefined_targets_) {
498 dropdown_.add(EconomyOptionsWindow::localize_profile_name(pair.first), pair.first);
499 last_added_to_dropdown_.insert(pair.first);
500 }
501 if (current_profile.empty()) {
502 // Nothing selected
503 dropdown_.add("–", "");
504 last_added_to_dropdown_.insert("");
505 }
506 update_profiles_select(current_profile);
507 }
508
update_profiles_select(const std::string & current_profile)509 void EconomyOptionsWindow::update_profiles_select(const std::string& current_profile) {
510 if (dropdown_.is_expanded()) {
511 return;
512 }
513 if (!dropdown_.has_selection() || dropdown_.get_selected() != current_profile) {
514 dropdown_.select(current_profile);
515 }
516 assert(dropdown_.has_selection());
517 }
518
update_save_enabled()519 void EconomyOptionsWindow::SaveProfileWindow::update_save_enabled() {
520 const std::string& text = profile_name_.text();
521 if (text.empty() || text == kDefaultEconomyProfile) {
522 save_.set_enabled(false);
523 save_.set_tooltip(text.empty() ? _("The profile name cannot be empty") :
524 _("The default profile cannot be overwritten"));
525 } else {
526 save_.set_enabled(true);
527 save_.set_tooltip(_("Save the profile under this name"));
528 }
529 }
530
table_selection_changed()531 void EconomyOptionsWindow::SaveProfileWindow::table_selection_changed() {
532 if (!table_.has_selection()) {
533 delete_.set_enabled(false);
534 delete_.set_tooltip("");
535 return;
536 }
537 const std::string& sel = table_[table_.selection_index()];
538 if (economy_options_->get_predefined_targets().at(sel).undeletable) {
539 delete_.set_tooltip(_("The predefined profiles cannot be deleted"));
540 delete_.set_enabled(false);
541 } else {
542 delete_.set_tooltip(_("Delete the selected profiles"));
543 delete_.set_enabled(true);
544 }
545 profile_name_.set_text(sel);
546 update_save_enabled();
547 }
548
update_table()549 void EconomyOptionsWindow::SaveProfileWindow::update_table() {
550 table_.clear();
551 for (const auto& pair : economy_options_->get_predefined_targets()) {
552 table_.add(pair.first).set_string(0, EconomyOptionsWindow::localize_profile_name(pair.first));
553 }
554 layout();
555 }
556
save()557 void EconomyOptionsWindow::SaveProfileWindow::save() {
558 const std::string name = profile_name_.text();
559 assert(!name.empty());
560 assert(name != kDefaultEconomyProfile);
561 for (const auto& pair : economy_options_->get_predefined_targets()) {
562 if (pair.first == name) {
563 UI::WLMessageBox m(
564 this, _("Overwrite?"),
565 _("A profile with this name already exists. Do you wish to replace it?"),
566 UI::WLMessageBox::MBoxType::kOkCancel);
567 if (m.run<UI::Panel::Returncodes>() != UI::Panel::Returncodes::kOk) {
568 return;
569 }
570 break;
571 }
572 }
573 economy_options_->do_create_target(name);
574 die();
575 }
576
delete_selected()577 void EconomyOptionsWindow::SaveProfileWindow::delete_selected() {
578 assert(table_.has_selection());
579
580 auto& map = economy_options_->get_predefined_targets();
581 const std::string& name = table_[table_.selection_index()];
582
583 assert(name != kDefaultEconomyProfile);
584 assert(!map.at(name).undeletable);
585 auto it = map.find(name);
586 assert(it != map.end());
587 map.erase(it);
588
589 economy_options_->save_targets();
590 economy_options_->update_profiles();
591 update_table();
592 }
593
unset_parent()594 void EconomyOptionsWindow::SaveProfileWindow::unset_parent() {
595 economy_options_ = nullptr;
596 die();
597 }
598
think()599 void EconomyOptionsWindow::SaveProfileWindow::think() {
600 if (!economy_options_) {
601 die();
602 }
603 UI::Window::think();
604 }
605
SaveProfileWindow(UI::Panel * parent,EconomyOptionsWindow * eco)606 EconomyOptionsWindow::SaveProfileWindow::SaveProfileWindow(UI::Panel* parent,
607 EconomyOptionsWindow* eco)
608 : UI::Window(parent, "save_economy_options_profile", 0, 0, 0, 0, _("Save Profile")),
609 economy_options_(eco),
610 main_box_(this, 0, 0, UI::Box::Vertical),
611 table_box_(&main_box_, 0, 0, UI::Box::Vertical),
612 table_(&table_box_, 0, 0, 460, 120, UI::PanelStyle::kWui),
613 buttons_box_(&main_box_, 0, 0, UI::Box::Horizontal),
614 profile_name_(&buttons_box_, 0, 0, 240, UI::PanelStyle::kWui),
615 save_(&buttons_box_, "save", 0, 0, 80, 34, UI::ButtonStyle::kWuiPrimary, _("Save")),
616 cancel_(&buttons_box_, "cancel", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Cancel")),
617 delete_(&buttons_box_, "delete", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Delete")) {
618 table_.add_column(200, _("Existing Profiles"));
619 update_table();
620
621 table_.selected.connect([this](uint32_t) { table_selection_changed(); });
622 profile_name_.changed.connect([this] { update_save_enabled(); });
623 profile_name_.ok.connect([this] { save(); });
624 profile_name_.cancel.connect([this] { die(); });
625 save_.sigclicked.connect([this] { save(); });
626 cancel_.sigclicked.connect([this] { die(); });
627 delete_.sigclicked.connect([this] { delete_selected(); });
628
629 table_box_.add(&table_, UI::Box::Resizing::kFullSize);
630 buttons_box_.add(&profile_name_, UI::Box::Resizing::kFullSize);
631 buttons_box_.add(&save_);
632 buttons_box_.add(&cancel_);
633 buttons_box_.add(&delete_);
634 main_box_.add(&table_box_, UI::Box::Resizing::kFullSize);
635 main_box_.add(&buttons_box_, UI::Box::Resizing::kFullSize);
636 set_center_panel(&main_box_);
637
638 table_selection_changed();
639 update_save_enabled();
640 }
641
~SaveProfileWindow()642 EconomyOptionsWindow::SaveProfileWindow::~SaveProfileWindow() {
643 if (economy_options_) {
644 economy_options_->close_save_profile_window();
645 }
646 }
647
create_target()648 void EconomyOptionsWindow::create_target() {
649 if (save_profile_dialog_) {
650 // Already open
651 return;
652 }
653 save_profile_dialog_ = new SaveProfileWindow(get_parent(), this);
654 }
655
close_save_profile_window()656 void EconomyOptionsWindow::close_save_profile_window() {
657 assert(save_profile_dialog_);
658 save_profile_dialog_ = nullptr;
659 }
660
do_create_target(const std::string & name)661 void EconomyOptionsWindow::do_create_target(const std::string& name) {
662 assert(!name.empty());
663 assert(name != kDefaultEconomyProfile);
664 const Widelands::Tribes& tribes = player_->egbase().tribes();
665 const Widelands::TribeDescr& tribe = player_->tribe();
666 Widelands::Economy* ware_economy = player_->get_economy(ware_serial_);
667 Widelands::Economy* worker_economy = player_->get_economy(worker_serial_);
668 PredefinedTargets t;
669 for (Widelands::DescriptionIndex di : tribe.wares()) {
670 if (tribes.get_ware_descr(di)->has_demand_check(tribe.name())) {
671 t.wares[di] = ware_economy->target_quantity(di).permanent;
672 }
673 }
674 for (Widelands::DescriptionIndex di : tribe.workers()) {
675 if (tribes.get_worker_descr(di)->has_demand_check()) {
676 t.workers[di] = worker_economy->target_quantity(di).permanent;
677 }
678 }
679 predefined_targets_[name] = t;
680
681 save_targets();
682 update_profiles();
683 }
684
save_targets()685 void EconomyOptionsWindow::save_targets() {
686 const Widelands::Tribes& tribes = player_->egbase().tribes();
687 Profile profile;
688
689 std::map<std::string, uint32_t> serials;
690 for (const auto& pair : predefined_targets_) {
691 if (pair.first != kDefaultEconomyProfile) {
692 serials.insert(std::make_pair(pair.first, serials.size()));
693 }
694 }
695 Section& global_section = profile.create_section(kDefaultEconomyProfile.c_str());
696 for (const auto& pair : serials) {
697 global_section.set_string(std::to_string(pair.second).c_str(), pair.first);
698 }
699
700 for (const auto& pair : predefined_targets_) {
701 if (pair.first == kDefaultEconomyProfile) {
702 continue;
703 }
704 Section& section = profile.create_section(std::to_string(serials.at(pair.first)).c_str());
705 for (const auto& setting : pair.second.wares) {
706 section.set_natural(tribes.get_ware_descr(setting.first)->name().c_str(), setting.second);
707 }
708 for (const auto& setting : pair.second.workers) {
709 section.set_natural(
710 tribes.get_worker_descr(setting.first)->name().c_str(), setting.second);
711 }
712 }
713
714 Section& section = profile.create_section("undeletable");
715 for (const auto& pair : predefined_targets_) {
716 if (pair.first != kDefaultEconomyProfile) {
717 section.set_bool(std::to_string(serials.at(pair.first)).c_str(), pair.second.undeletable);
718 }
719 }
720
721 g_fs->ensure_directory_exists(kEconomyProfilesDir);
722 std::string complete_filename =
723 kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
724 profile.write(complete_filename.c_str(), false);
725
726 // Inform the windows of other economies of new and deleted profiles
727 Notifications::publish(NoteEconomyProfile(ware_serial_, worker_serial_));
728 }
729
read_targets()730 void EconomyOptionsWindow::read_targets() {
731 predefined_targets_.clear();
732 const Widelands::Tribes& tribes = player_->egbase().tribes();
733 const Widelands::TribeDescr& tribe = player_->tribe();
734
735 {
736 PredefinedTargets t;
737 t.undeletable = true;
738 for (Widelands::DescriptionIndex di : tribe.wares()) {
739 const Widelands::WareDescr* descr = tribes.get_ware_descr(di);
740 if (descr->has_demand_check(tribe.name())) {
741 t.wares.insert(std::make_pair(di, descr->default_target_quantity(tribe.name())));
742 }
743 }
744 for (Widelands::DescriptionIndex di : tribe.workers()) {
745 const Widelands::WorkerDescr* descr = tribes.get_worker_descr(di);
746 if (descr->has_demand_check()) {
747 t.workers.insert(std::make_pair(di, descr->default_target_quantity()));
748 }
749 }
750 predefined_targets_.insert(std::make_pair(kDefaultEconomyProfile, t));
751 }
752
753 std::string complete_filename =
754 kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
755 Profile profile;
756 profile.read(complete_filename.c_str());
757
758 Section* global_section = profile.get_section(kDefaultEconomyProfile);
759 if (global_section) {
760 std::map<std::string, std::string> serials;
761 while (Section::Value* v = global_section->get_next_val()) {
762 serials.insert(std::make_pair(v->get_name(), v->get_string()));
763 }
764
765 for (const auto& pair : serials) {
766 Section* section = profile.get_section(pair.first);
767 PredefinedTargets t;
768 while (Section::Value* v = section->get_next_val()) {
769 const std::string name(v->get_name());
770 Widelands::DescriptionIndex di = tribes.ware_index(name);
771 if (di == Widelands::INVALID_INDEX) {
772 di = tribes.worker_index(name);
773 assert(di != Widelands::INVALID_INDEX);
774 t.workers.insert(std::make_pair(di, v->get_natural()));
775 } else {
776 t.wares.insert(std::make_pair(di, v->get_natural()));
777 }
778 }
779 predefined_targets_.insert(std::make_pair(pair.second, t));
780 }
781
782 if (Section* section = profile.get_section("undeletable")) {
783 while (Section::Value* v = section->get_next_val()) {
784 predefined_targets_.at(serials.at(std::string(v->get_name()))).undeletable =
785 v->get_bool();
786 }
787 }
788 }
789
790 update_profiles();
791 }
792