1 /*
2  * Copyright (C) 2011-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/portdockwaresdisplay.h"
21 
22 #include "economy/expedition_bootstrap.h"
23 #include "economy/portdock.h"
24 #include "logic/player.h"
25 #include "ui_basic/icon.h"
26 #include "wui/inputqueuedisplay.h"
27 
28 using Widelands::InputQueue;
29 using Widelands::PortDock;
30 using Widelands::Warehouse;
31 
32 namespace {
33 
34 /**
35  * Display wares or workers that are waiting to be shipped from a port.
36  */
37 struct PortDockWaresDisplay : AbstractWaresDisplay {
38 	PortDockWaresDisplay(Panel* parent,
39 	                     uint32_t width,
40 	                     const PortDock& pd,
41 	                     Widelands::WareWorker type);
42 
43 	std::string info_for_ware(Widelands::DescriptionIndex ware) override;
44 
45 private:
46 	const PortDock& portdock_;
47 };
48 
PortDockWaresDisplay(Panel * parent,uint32_t width,const PortDock & pd,Widelands::WareWorker type)49 PortDockWaresDisplay::PortDockWaresDisplay(Panel* parent,
50                                            uint32_t width,
51                                            const PortDock& pd,
52                                            Widelands::WareWorker type)
53    : AbstractWaresDisplay(parent, 0, 0, pd.owner().tribe(), type, false), portdock_(pd) {
54 	set_inner_size(width, 0);
55 }
56 
info_for_ware(Widelands::DescriptionIndex ware)57 std::string PortDockWaresDisplay::info_for_ware(Widelands::DescriptionIndex ware) {
58 	const uint32_t count = portdock_.count_waiting(get_type(), ware);
59 	return boost::lexical_cast<std::string>(count);
60 }
61 
62 }  // anonymous namespace
63 
64 /**
65  * Create a panel that displays the wares or workers that are waiting to be shipped from a port.
66  */
create_portdock_wares_display(UI::Panel * parent,uint32_t width,const PortDock & pd,Widelands::WareWorker type)67 AbstractWaresDisplay* create_portdock_wares_display(UI::Panel* parent,
68                                                     uint32_t width,
69                                                     const PortDock& pd,
70                                                     Widelands::WareWorker type) {
71 	return new PortDockWaresDisplay(parent, width, pd, type);
72 }
73 
74 static char const* kPicWarePresent = "images/wui/buildings/high_priority_button.png";
75 static char const* kPicWareComing = "images/wui/buildings/normal_priority_button.png";
76 static char const* kPicWareMissing = "images/wui/buildings/low_priority_button.png";
77 static char const* kNoWare = "images/wui/editor/no_ware.png";
78 static const auto kEmptySlot = std::make_pair(Widelands::wwWARE, Widelands::INVALID_INDEX);
79 
80 struct PortDockAdditionalItemsDisplay : UI::Box {
81 public:
PortDockAdditionalItemsDisplayPortDockAdditionalItemsDisplay82 	PortDockAdditionalItemsDisplay(
83 	   Widelands::Game& g, Panel* parent, bool can_act, PortDock& pd, const uint32_t capacity)
84 	   : UI::Box(parent, 0, 0, UI::Box::Horizontal), game_(g), portdock_(pd), capacity_(capacity) {
85 		assert(capacity_ > 0);
86 		assert(portdock_.expedition_bootstrap());
87 		assert(portdock_.expedition_bootstrap()->count_additional_queues() <= capacity_);
88 		for (uint32_t c = 0; c < capacity_; ++c) {
89 			UI::Box* box = new UI::Box(this, 0, 0, UI::Box::Vertical);
90 
91 			UI::Dropdown<std::pair<Widelands::WareWorker, Widelands::DescriptionIndex>>& d =
92 			   *new UI::Dropdown<std::pair<Widelands::WareWorker, Widelands::DescriptionIndex>>(
93 			      box, (boost::format("additional_%u") % c).str(), 0, 0, kWareMenuPicWidth, 8,
94 			      kWareMenuPicHeight, _("Additional item"), UI::DropdownType::kPictorial,
95 			      UI::PanelStyle::kWui, UI::ButtonStyle::kWuiSecondary);
96 			d.add(_("(Empty)"), kEmptySlot, g_gr->images().get(kNoWare), true, _("(Empty)"));
97 			for (Widelands::DescriptionIndex i : pd.owner().tribe().wares()) {
98 				const Widelands::WareDescr& w = *pd.owner().tribe().get_ware_descr(i);
99 				d.add(
100 				   w.descname(), std::make_pair(Widelands::wwWARE, i), w.icon(), false, w.descname());
101 			}
102 			for (Widelands::DescriptionIndex i : pd.owner().tribe().workers()) {
103 				const Widelands::WorkerDescr& w = *pd.owner().tribe().get_worker_descr(i);
104 				d.add(
105 				   w.descname(), std::make_pair(Widelands::wwWORKER, i), w.icon(), false, w.descname());
106 			}
107 			d.set_enabled(can_act);
108 			d.selected.connect([this, c]() { select(c); });
109 
110 			UI::Icon* icon = new UI::Icon(box, g_gr->images().get(kNoWare));
111 			icon->set_handle_mouse(true);
112 			boxes_.push_back(box);
113 			icons_.push_back(icon);
114 			dropdowns_.push_back(&d);
115 			box->add(&d);
116 			box->add(icon);
117 			add(box);
118 		}
119 		update_selection();
120 	}
121 
thinkPortDockAdditionalItemsDisplay122 	void think() override {
123 		UI::Box::think();
124 		if (!portdock_.expedition_bootstrap()) {
125 			return die();
126 		}
127 		update_selection();
128 
129 		for (uint32_t c = 0; c < capacity_; ++c) {
130 			const InputQueue* iq = portdock_.expedition_bootstrap()->inputqueue(c);
131 			assert(!iq || (iq->get_max_size() == 1 && iq->get_max_fill() == 1));
132 			icons_[c]->set_icon(g_gr->images().get(
133 			   iq ?
134 			      iq->get_filled() ? kPicWarePresent : iq->get_missing() ? kPicWareMissing :
135 			                                                               kPicWareComing :
136 			      kNoWare));
137 			icons_[c]->set_tooltip(
138 			   iq ?
139 			      iq->get_filled() ?
140 			      /** TRANSLATORS: Tooltip for a ware that is present in the building */
141 			         _("Present") :
142 			         iq->get_missing() ?
143 			         /** TRANSLATORS: Tooltip for a ware that is neither present in the
144 			            building nor being transported there */
145 			            _("Missing") :
146 			            /** TRANSLATORS: Tooltip for a ware that is not present in the
147 			               building, but already being transported there */
148 			            _("Coming") :
149 			      "");
150 		}
151 	}
152 
update_selectionPortDockAdditionalItemsDisplay153 	void update_selection() {
154 		for (uint32_t c = 0; c < capacity_; ++c) {
155 			if (dropdowns_[c]->is_expanded()) {
156 				continue;
157 			}
158 			const InputQueue* iq = portdock_.expedition_bootstrap()->inputqueue(c);
159 			if (!iq) {
160 				dropdowns_[c]->select(kEmptySlot);
161 			} else {
162 				dropdowns_[c]->select(std::make_pair(iq->get_type(), iq->get_index()));
163 			}
164 			assert(dropdowns_[c]->has_selection());
165 		}
166 	}
167 
selectPortDockAdditionalItemsDisplay168 	void select(uint32_t index) {
169 		assert(index < dropdowns_.size());
170 		const auto& new_sel = dropdowns_[index]->get_selected();
171 		const InputQueue* iq = portdock_.expedition_bootstrap()->inputqueue(index);
172 		if (new_sel.second == Widelands::INVALID_INDEX && !iq) {
173 			return;
174 		}
175 		if (iq && iq->get_type() == new_sel.first && iq->get_index() == new_sel.second) {
176 			return;
177 		}
178 		if (iq) {
179 			game_.send_player_expedition_config(portdock_, iq->get_type(), iq->get_index(), false);
180 		}
181 		if (new_sel.second != Widelands::INVALID_INDEX) {
182 			game_.send_player_expedition_config(portdock_, new_sel.first, new_sel.second, true);
183 		}
184 	}
185 
186 private:
187 	Widelands::Game& game_;
188 	PortDock& portdock_;
189 	uint32_t capacity_;
190 	std::vector<UI::Box*> boxes_;
191 	std::vector<UI::Icon*> icons_;
192 	std::vector<UI::Dropdown<std::pair<Widelands::WareWorker, Widelands::DescriptionIndex>>*>
193 	   dropdowns_;
194 };
195 
196 /// Create a panel that displays the wares and the builder waiting for the expedition to start.
197 UI::Box*
create_portdock_expedition_display(UI::Panel * parent,Warehouse & wh,InteractiveGameBase & igb)198 create_portdock_expedition_display(UI::Panel* parent, Warehouse& wh, InteractiveGameBase& igb) {
199 	UI::Box& box = *new UI::Box(parent, 0, 0, UI::Box::Vertical);
200 
201 	// Add the input queues.
202 	int32_t capacity =
203 	   igb.egbase().tribes().get_ship_descr(wh.get_owner()->tribe().ship())->get_default_capacity();
204 	for (const InputQueue* wq : wh.get_portdock()->expedition_bootstrap()->queues(false)) {
205 		InputQueueDisplay* iqd = new InputQueueDisplay(&box, 0, 0, igb, wh, *wq, true);
206 		box.add(iqd);
207 		capacity -= wq->get_max_size();
208 	}
209 	assert(capacity >= 0);
210 
211 	if (capacity > 0) {
212 		const bool can_act = igb.can_act(wh.get_owner()->player_number());
213 		box.add(new PortDockAdditionalItemsDisplay(
214 		           igb.game(), &box, can_act, *wh.get_portdock(), capacity),
215 		        UI::Box::Resizing::kAlign, UI::Align::kCenter);
216 	}
217 
218 	return &box;
219 }
220