1 //  Copyright (C) 2011, 2014, 2015, 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 <gtkmm.h>
21 
22 #include "stack-tile-box.h"
23 #include "stacktile.h"
24 #include "stack.h"
25 #include "stack-army-button.h"
26 #include "army-info-tip.h"
27 #include "builder-cache.h"
28 
29 #include "ucompose.hpp"
30 #include "defs.h"
31 #include "ImageCache.h"
32 #include "File.h"
33 #include "playerlist.h"
34 #include "player.h"
35 #include "GameMap.h"
36 #include "army.h"
37 #include "shield.h"
38 #include "font-size.h"
39 
40 #define method(x) sigc::mem_fun(*this, &StackTileBox::x)
41 
create()42 StackTileBox * StackTileBox::create()
43 {
44   Glib::ustring file = "stack-tile-box-large-screen.ui";
45   Glib::RefPtr<Gtk::Builder> xml = BuilderCache::get(file);
46 
47   StackTileBox *box;
48   xml->get_widget_derived("box", box);
49   return box;
50 }
51 
pad_image(Gtk::Image * image)52 void StackTileBox::pad_image(Gtk::Image *image)
53 {
54   int padding = 3;
55   image->property_xpad() = padding;
56   image->property_ypad() = padding;
57 }
58 
StackTileBox(BaseObjectType * baseObject,const Glib::RefPtr<Gtk::Builder> & xml)59 StackTileBox::StackTileBox(BaseObjectType* baseObject, const Glib::RefPtr<Gtk::Builder> &xml)
60   : Gtk::Box(baseObject)
61 {
62   d_inhibit = false;
63   army_info_tip = NULL;
64   xml->get_widget("stack_info_box", stack_info_box);
65   xml->get_widget("stack_info_container", stack_info_container);
66   xml->get_widget("group_moves_label", group_moves_label);
67   xml->get_widget("group_togglebutton", group_ungroup_toggle);
68   group_ungroup_toggle->signal_toggled().connect
69     (sigc::bind(method(on_group_toggled), group_ungroup_toggle));
70   xml->get_widget("terrain_image", terrain_image);
71 
72   //okay let's make our army buttons.
73   for (unsigned int i = 0; i < MAX_ARMIES_ON_A_SINGLE_TILE; i++)
74     {
75       //we put them in a vbox so that the buttons don't expand horizontally.
76       StackArmyButton *button = StackArmyButton::create();
77       button->get_parent()->remove(*button);
78       Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_VERTICAL);
79       box->pack_start(*Gtk::manage(button), Gtk::PACK_SHRINK);
80       button->reparent(*box);
81       stack_army_buttons.push_back(button);
82       stack_info_box->pack_start(*Gtk::manage(box), Gtk::PACK_SHRINK);
83     }
84   memset (army_conn, 0, sizeof (army_conn));
85   memset (stack_conn, 0, sizeof (stack_conn));
86   d_inhibit_group_toggle = false;
87 }
88 
reset_army_buttons()89 void StackTileBox::reset_army_buttons()
90 {
91   for (unsigned int i = 0; i < stack_army_buttons.size(); i++)
92     stack_army_buttons[i]->reset();
93   for (unsigned int i = 0; i < MAX_ARMIES_ON_A_SINGLE_TILE; i++)
94     {
95       army_conn[i].disconnect();
96       stack_conn[i].disconnect();
97     }
98 }
99 
~StackTileBox()100 StackTileBox::~StackTileBox()
101 {
102   if (army_info_tip)
103     delete army_info_tip;
104 }
105 
on_stack_toggled(Stack * stack)106 void StackTileBox::on_stack_toggled(Stack *stack)
107 {
108   if (d_inhibit || d_inhibit_group_toggle)
109     return;
110   if (stack == currently_selected_stack)
111     return;
112   currently_selected_stack = stack;
113   Playerlist::getActiveplayer()->setActivestack(stack);
114   on_stack_info_changed(stack);
115   stack_composition_modified.emit(stack);
116 }
117 
on_army_toggled(StackArmyButton * toggle,Stack * stack,Army * army)118 void StackTileBox::on_army_toggled(StackArmyButton *toggle, Stack *stack, Army *army)
119 {
120   if (d_inhibit || d_inhibit_group_toggle)
121     return;
122   for (stack_army_buttons_type::iterator i = stack_army_buttons.begin(),
123        end = stack_army_buttons.end(); i != end; ++i)
124     (*i)->update_stack_button(toggle == *i);
125   Player *p = Playerlist::getActiveplayer();
126   Stack *s = p->getActivestack();
127   group_ungroup_toggle->set_sensitive(false);
128   if (toggle->get_active() == true)
129     {
130       if (stack->size() > 1)
131 	{
132 	  Stack *new_stack = p->stackSplitArmy(stack, army);
133 	  if (new_stack)
134 	    p->stackJoin(currently_selected_stack, new_stack);
135 	}
136       else
137 	p->stackJoin(currently_selected_stack, stack);
138       currently_selected_stack->sortForViewing(true);
139     }
140   else
141     {
142       p->stackSplitArmy(stack, army);
143       stack->sortForViewing(true);
144     }
145   on_stack_info_changed(s);
146   group_ungroup_toggle->set_sensitive(true);
147   stack_composition_modified.emit(s);
148 }
149 
on_group_toggled(Gtk::ToggleButton * toggle)150 void StackTileBox::on_group_toggled(Gtk::ToggleButton *toggle)
151 {
152   if (d_inhibit_group_toggle)
153     return;
154   if (toggle->get_sensitive() == false)
155     return;
156   bool active = toggle->get_active();
157 
158   StackTile *s = GameMap::getStacks(currently_selected_stack->getPos());
159   stack_tile_group_toggle.emit(true);
160   if (active)
161     {
162       s->group(Playerlist::getActiveplayer(), currently_selected_stack);
163       currently_selected_stack->sortForViewing(true);
164     }
165   else
166     s->ungroup(Playerlist::getActiveplayer());
167   stack_tile_group_toggle.emit(false);
168 
169   on_stack_info_changed(currently_selected_stack);
170   stack_composition_modified.emit(currently_selected_stack);
171 }
172 
show_stack(StackTile * s)173 void StackTileBox::show_stack(StackTile *s)
174 {
175   reset_army_buttons();
176   Player *p = Playerlist::getActiveplayer();
177   std::vector<Stack *> stks;
178   stks = s->getFriendlyStacks(p);
179   unsigned int count= 0;
180 
181   guint32 colour_id = 0;
182   if (colour_id == p->getId())
183     colour_id = Shield::get_next_shield(colour_id);
184   for (std::vector<Stack *>::iterator j = stks.begin(); j != stks.end(); j++)
185     {
186       bool first = true;
187       for (Stack::iterator i = (*j)->begin(); i != (*j)->end(); ++i)
188         {
189           Stack *stack = NULL;
190           if (first == true)
191             {
192               first = false;
193               stack = *j;
194             }
195           if (count >= MAX_ARMIES_ON_A_SINGLE_TILE)
196             break;
197 
198           StackArmyButton *button = stack_army_buttons[count];
199           button->draw(stack, *i, colour_id, (*j) == currently_selected_stack);
200           army_conn[count].disconnect();
201           army_conn[count] = button->army_toggled.connect
202             (sigc::bind(method(on_army_toggled), button, *j, *i));
203           stack_conn[count].disconnect();
204           stack_conn[count] = button->stack_clicked.connect
205             (sigc::bind(method(on_stack_toggled), *j));
206           count++;
207         }
208 
209       colour_id = Shield::get_next_shield(colour_id);
210       if (colour_id== p->getId())
211         colour_id = Shield::get_next_shield(colour_id);
212     }
213 
214   fill_in_group_info(s, currently_selected_stack);
215 }
216 
toggle_group_ungroup()217 void StackTileBox::toggle_group_ungroup()
218 {
219   group_ungroup_toggle->set_active(!group_ungroup_toggle->get_active());
220 }
221 
fill_in_group_info(StackTile * stile,Stack * s)222 void StackTileBox::fill_in_group_info (StackTile *stile, Stack *s)
223 {
224   guint32 bonus = s->calculateMoveBonus();
225   ImageCache *gc = ImageCache::getInstance();
226   terrain_image->property_pixbuf() =
227     gc->getMoveBonusPic(bonus, s->hasShip(),
228                         FontSize::getInstance ()->get_height ())->to_pixbuf();
229   group_moves_label->set_markup(String::ucompose("<b>%1</b>", s->getMoves()));
230   group_ungroup_toggle->set_sensitive(false);
231   d_inhibit_group_toggle = true;
232   if (stile->getFriendlyStacks(s->getOwner()).size() != 1)
233     group_ungroup_toggle->set_active(false);
234   else
235     group_ungroup_toggle->set_active(true);
236   if (group_ungroup_toggle->get_active() == true)
237     group_ungroup_toggle->set_label(_("UnGrp"));
238   else
239     group_ungroup_toggle->set_label(_("Grp"));
240   group_ungroup_toggle->set_sensitive(true);
241   d_inhibit_group_toggle = false;
242 }
243 
on_stack_info_changed(Stack * s)244 void StackTileBox::on_stack_info_changed(Stack *s)
245 {
246   set_selected_stack(s);
247 
248   if (s->getOwner()->getType() == Player::HUMAN)
249     {
250       StackTile *stile = GameMap::getStacks(s->getPos());
251       stile->setDefending(s->getOwner(), false);
252       stile->setParked(s->getOwner(), false);
253       show_stack(stile);
254     }
255 }
256