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