1 // Copyright (C) 2008, 2009, 2011, 2014, 2020 Ben Asselstine
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 // 02110-1301, USA.
17
18 #include <config.h>
19
20 #include <sigc++/functors/mem_fun.h>
21
22 #include "stack-info-dialog.h"
23
24 #include <gtkmm.h>
25 #include "input-helpers.h"
26 #include "ucompose.hpp"
27 #include "defs.h"
28 #include "army.h"
29 #include "player.h"
30 #include "armysetlist.h"
31 #include "playerlist.h"
32 #include "stack.h"
33 #include "ImageCache.h"
34 #include "Tile.h"
35 #include <assert.h>
36 #include "stacktile.h"
37 #include "GameMap.h"
38 #include "font-size.h"
39
40 #define method(x) sigc::mem_fun(*this, &StackInfoDialog::x)
41
StackInfoDialog(Gtk::Window & parent,Vector<int> pos)42 StackInfoDialog::StackInfoDialog(Gtk::Window &parent, Vector<int> pos)
43 : LwDialog(parent, "stack-info-dialog.ui")
44 {
45 army_info_tip = NULL;
46 tile = pos;
47 currently_selected_stack = NULL;
48
49 xml->get_widget("stack_table", stack_table);
50 xml->get_widget("group_button", group_button);
51 group_button->signal_clicked().connect (method(on_group_clicked));
52 xml->get_widget("ungroup_button", ungroup_button);
53 ungroup_button->signal_clicked().connect (method(on_ungroup_clicked));
54 fill_stack_info();
55 }
56
~StackInfoDialog()57 StackInfoDialog::~StackInfoDialog()
58 {
59 if (army_info_tip != NULL)
60 delete army_info_tip;
61 }
62
addStack(Stack * s,guint32 & idx)63 void StackInfoDialog::addStack(Stack *s, guint32 &idx)
64 {
65 s->sortForViewing(true);
66 Stack *target = new Stack(Playerlist::getInstance()->getNeutral(), s->getPos());
67 ArmyProto *baseproto = ArmyProto::createScout();
68 Army *army = new Army(*baseproto);
69 delete baseproto;
70 target->add(army);
71 Fight fight(s, target, Fight::FOR_KICKS);
72 delete target;
73
74 bool first = true;
75 guint32 colour_id = 0;
76 if (colour_id == s->getOwner()->getId())
77 colour_id = Shield::get_next_shield(colour_id);
78 for (Stack::iterator it = s->begin(); it != s->end(); it++)
79 {
80 guint32 str = fight.getModifiedStrengthBonus(*it);
81 addArmy(first, s, *it, str, idx, colour_id);
82 if (first == true)
83 first = false;
84 idx++;
85 colour_id = Shield::get_next_shield(colour_id);
86 if (colour_id == s->getOwner()->getId())
87 colour_id = Shield::get_next_shield(colour_id);
88 }
89 }
90
addArmy(bool first,Stack * s,Army * h,guint32 modified_strength,guint32 idx,guint32 colour_id)91 void StackInfoDialog::addArmy (bool first, Stack *s, Army *h, guint32 modified_strength, guint32 idx, guint32 colour_id)
92 {
93 ImageCache *gc = ImageCache::getInstance();
94 Player *player = h->getOwner();
95
96 bool greyed_out = s->getId() != currently_selected_stack->getId();
97 Gtk::ToggleButton *toggle = manage(new Gtk::ToggleButton);
98 Glib::RefPtr<Gdk::Pixbuf> pixbuf=
99 gc->getCircledArmyPic(player->getArmyset(), h->getTypeId(), player, NULL,
100 greyed_out, !greyed_out ? player->getId() : colour_id,
101 true,
102 FontSize::getInstance()->get_height ())->to_pixbuf();
103
104 Gtk::Image *image = NULL;
105 guint32 move_bonus = h->getStat(Army::MOVE_BONUS);
106 bool ship = h->getStat(Army::SHIP);
107 if (ship || move_bonus == (Tile::GRASS | Tile::WATER | Tile::FOREST |
108 Tile::HILLS | Tile::SWAMP | Tile::MOUNTAIN))
109 {
110 image = new Gtk::Image();
111 image->property_pixbuf() =
112 gc->getMoveBonusPic(move_bonus, ship,
113 FontSize::getInstance ()->get_height ())->to_pixbuf();
114 }
115
116 armies.push_back(h);
117 toggle->set_active(s->getId() == currently_selected_stack->getId());
118 toggle->signal_toggled().connect
119 (sigc::bind(method(on_army_toggled), toggle, s, h));
120 toggle->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
121 toggles.push_back(toggle);
122 toggle->signal_button_press_event().connect
123 (sigc::bind(method(on_army_button_event), toggle), false);
124 toggle->signal_button_release_event().connect
125 (sigc::bind(method(on_army_button_event), toggle), false);
126 Gtk::Image *army_image = new Gtk::Image();
127 army_image->property_pixbuf() = pixbuf;
128 toggle->add(*manage(army_image));
129
130 Gtk::Label *name = new Gtk::Label(h->getName());
131 unsigned int strength_value = h->getStat(Army::STRENGTH);
132 Glib::ustring str = String::ucompose("%1", strength_value);
133 if (modified_strength != strength_value)
134 str += String::ucompose(" (%1)", modified_strength);
135
136 Gtk::Label *strength = new Gtk::Label(str);
137 Gtk::Label *bonus = new Gtk::Label(h->getArmyBonusDescription());
138 Gtk::Label *moves = new Gtk::Label(String::ucompose("%1", h->getMoves()));
139
140 if (first)
141 {
142 Gtk::RadioButton *radio;
143 if (radios.size() > 0)
144 {
145 Gtk::RadioButtonGroup b;
146 b = radios.front()->get_group();
147 radio = new Gtk::RadioButton(b);
148 }
149 else
150 radio = new Gtk::RadioButton();
151 radios.push_back(radio);
152 radio->set_active(s->getId() == currently_selected_stack->getId());
153 radio->signal_toggled().connect
154 (sigc::bind(method(on_stack_toggled), radio, s));
155 stack_table->attach(*manage(radio), 0, idx, 1, 1);
156 }
157
158 stack_table->attach(*manage(toggle), 1, idx, 1, 1);
159 stack_table->attach(*manage(name), 2, idx, 1, 1);
160 stack_table->attach(*manage(strength), 3, idx, 1, 1);
161 stack_table->attach(*manage(moves), 4, idx, 1, 1);
162 if (image)
163 stack_table->attach(*manage(image), 5, idx, 1, 1);
164 stack_table->attach(*manage(bonus), 6, idx, 1, 1);
165 }
166
on_group_clicked()167 void StackInfoDialog::on_group_clicked()
168 {
169 StackTile *stile = GameMap::getStacks(tile);
170 Stack *stack = stile->group(Playerlist::getActiveplayer());
171 currently_selected_stack = stack;
172 stack->sortForViewing(true);
173 fill_stack_info();
174 }
175
on_ungroup_clicked()176 void StackInfoDialog::on_ungroup_clicked()
177 {
178 StackTile *stile = GameMap::getStacks(tile);
179 stile->ungroup(Playerlist::getActiveplayer());
180 fill_stack_info();
181 }
182
fill_stack_info()183 void StackInfoDialog::fill_stack_info()
184 {
185 StackTile *stile = GameMap::getStacks(tile);
186 guint32 idx = 1;
187
188 armies.clear();
189 toggles.clear();
190 radios.clear();
191 stack_table->foreach(sigc::mem_fun(stack_table, &Gtk::Container::remove));
192 for (int i = 0; i < 6; i++)
193 stack_table->insert_row (i);
194 for (unsigned int i = 0; i < MAX_ARMIES_ON_A_SINGLE_TILE; i++)
195 stack_table->insert_column (i);
196
197 Pango::AttrList attrs;
198 Pango::Attribute weight = Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD);
199 attrs.insert(weight);
200 Gtk::Label *str = new Gtk::Label(_("Str"));
201 str->set_attributes(attrs);
202 stack_table->attach(*manage(str), 3, 0, 1, 1);
203
204 Gtk::Label *moves = new Gtk::Label(_("Move"));
205 moves->set_attributes(attrs);
206 stack_table->attach(*manage(moves), 4, 0, 1, 1);
207
208 Gtk::Label *bonus = new Gtk::Label(_("Bonus"));
209 bonus->set_attributes(attrs);
210 stack_table->attach(*manage(bonus), 6, 0, 1, 1);
211
212 std::vector<Stack*> stks;
213 stks = stile->getFriendlyStacks(Playerlist::getActiveplayer());
214 if (currently_selected_stack == NULL)
215 currently_selected_stack = stks.front();
216 for (std::vector<Stack *>::iterator i = stks.begin(); i != stks.end(); i++)
217 addStack(*i, idx);
218 stack_table->show_all();
219 }
220
on_army_button_event(GdkEventButton * e,Gtk::ToggleButton * toggle)221 bool StackInfoDialog::on_army_button_event(GdkEventButton *e, Gtk::ToggleButton *toggle)
222 {
223 MouseButtonEvent event = to_input_event(e);
224 if (event.button == MouseButtonEvent::RIGHT_BUTTON &&
225 event.state == MouseButtonEvent::PRESSED)
226 {
227 int slot = -1;
228 for (unsigned int i = 0; i < toggles.size(); ++i)
229 {
230 if (toggle == toggles[i])
231 slot = i;
232 }
233 assert(slot != -1);
234
235 const Army *army = armies[slot];
236
237 if (army)
238 {
239 if (army_info_tip != NULL)
240 delete army_info_tip;
241 army_info_tip = new ArmyInfoTip(toggle, army);
242 }
243 return true;
244 }
245 else if (event.button == MouseButtonEvent::RIGHT_BUTTON &&
246 event.state == MouseButtonEvent::RELEASED)
247 {
248 if (army_info_tip != NULL)
249 {
250 delete army_info_tip;
251 army_info_tip = NULL;
252 }
253 return true;
254 }
255
256 return false;
257 }
258
on_army_toggled(Gtk::ToggleButton * toggle,Stack * stack,Army * army)259 void StackInfoDialog::on_army_toggled(Gtk::ToggleButton *toggle, Stack *stack, Army *army)
260 {
261 Player *p = Playerlist::getActiveplayer();
262 group_button->set_sensitive(false);
263 ungroup_button->set_sensitive(false);
264 if (toggle->get_active() == true)
265 {
266 if (stack->size() > 1)
267 {
268 Stack *new_stack = p->stackSplitArmy(stack, army);
269 if (new_stack)
270 p->stackJoin(currently_selected_stack, new_stack);
271 }
272 else
273 p->stackJoin(currently_selected_stack, stack);
274 currently_selected_stack->sortForViewing(true);
275 }
276 else
277 {
278 p->stackSplitArmy(stack, army);
279 stack->sortForViewing(true);
280 }
281 group_button->set_sensitive(true);
282 ungroup_button->set_sensitive(true);
283 fill_stack_info();
284 }
285
on_stack_toggled(Gtk::RadioButton * radio,Stack * stack)286 void StackInfoDialog::on_stack_toggled(Gtk::RadioButton *radio, Stack *stack)
287 {
288 if (radio->get_active() == true)
289 {
290 if (stack == currently_selected_stack)
291 return;
292 currently_selected_stack = stack;
293 fill_stack_info();
294 }
295 }
296
297