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