1 //  Copyright (C) 2007 Ole Laursen
2 //  Copyright (C) 2007-2009, 2012, 2014, 2015, 2017, 2020 Ben Asselstine
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 3 of the License, or
7 //  (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 Library 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
17 //  02110-1301, USA.
18 
19 #include <config.h>
20 
21 #include <gtkmm.h>
22 #include <sigc++/functors/mem_fun.h>
23 
24 #include "stack-editor-dialog.h"
25 
26 #include "ucompose.hpp"
27 #include "defs.h"
28 #include "stack.h"
29 #include "army.h"
30 #include "armyproto.h"
31 #include "hero.h"
32 #include "heroproto.h"
33 #include "ImageCache.h"
34 #include "playerlist.h"
35 #include "stacklist.h"
36 #include "stacktile.h"
37 #include "hero-editor-dialog.h"
38 #include "GameMap.h"
39 #include "font-size.h"
40 
41 #include "select-army-dialog.h"
42 
43 #define method(x) sigc::mem_fun(*this, &StackEditorDialog::x)
44 
45 namespace
46 {
47   //FIXME this should be MAX_STACK_SIZE from defs.h
48     int const max_stack_size = 8;
49 }
50 
StackEditorDialog(Gtk::Window & parent,Stack * s,int m)51 StackEditorDialog::StackEditorDialog(Gtk::Window &parent, Stack *s, int m)
52  : LwEditorDialog(parent, "stack-editor-dialog.ui"),
53     strength_column(_("Strength"), strength_renderer),
54 	moves_column(_("Max Moves"), moves_renderer),
55 	upkeep_column(_("Upkeep"), upkeep_renderer)
56 {
57   stack = s;
58   min_size = m;
59   player_combobox = 0;
60 
61   if (stack->getOwner())
62     {
63       // setup the player combo
64       player_combobox = manage(new Gtk::ComboBoxText);
65 
66       int c = 0, player_no = 0;
67       for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
68            end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
69         {
70           Player *player = *i;
71           player_combobox->append(player->getName());
72           if (player == stack->getOwner())
73             player_no = c;
74         }
75 
76       player_combobox->set_active(player_no);
77       player_combobox->signal_changed().connect (method(on_player_changed));
78 
79       Gtk::Box *box;
80       xml->get_widget("player_hbox", box);
81       box->pack_start(*player_combobox, Gtk::PACK_SHRINK);
82     }
83 
84   // setup the army list
85   army_list = Gtk::ListStore::create(army_columns);
86 
87   xml->get_widget("army_treeview", army_treeview);
88   army_treeview->set_model(army_list);
89 
90   army_treeview->append_column("", army_columns.image);
91 
92   strength_renderer.property_editable() = true;
93   strength_renderer.signal_edited().connect(method(on_strength_edited));
94   strength_column.set_cell_data_func(strength_renderer, method(cell_data_strength));
95   army_treeview->append_column(strength_column);
96 
97   moves_renderer.property_editable() = true;
98   moves_renderer.signal_edited().connect(method(on_moves_edited));
99   moves_column.set_cell_data_func(moves_renderer, method(cell_data_moves));
100   army_treeview->append_column(moves_column);
101 
102   upkeep_renderer.property_editable() = true;
103   upkeep_renderer.signal_edited().connect(method(on_upkeep_edited));
104   upkeep_column.set_cell_data_func(upkeep_renderer, method(cell_data_upkeep));
105   army_treeview->append_column(upkeep_column);
106 
107   army_treeview->append_column(_("Name"), army_columns.name);
108 
109   xml->get_widget("fortified_switch", fortified_switch);
110   fortified_switch->set_active(stack->getFortified());
111   fortified_switch->property_active().signal_changed().connect(method(on_fortified_toggled));
112 
113   xml->get_widget("add_button", add_button);
114   xml->get_widget("remove_button", remove_button);
115   xml->get_widget("copy_button", copy_button);
116   xml->get_widget("edit_hero_button", edit_hero_button);
117 
118   add_button->signal_clicked().connect(method(on_add_clicked));
119   remove_button->signal_clicked().connect(method(on_remove_clicked));
120   copy_button->signal_clicked().connect(method(on_copy_clicked));
121   edit_hero_button->signal_clicked().connect(method(on_edit_hero_clicked));
122 
123   army_treeview->get_selection()->signal_changed().connect(method(on_selection_changed));
124 
125   for (Stack::iterator i = stack->begin(), end = stack->end(); i != end; ++i)
126     add_army(*i);
127   set_button_sensitivity();
128 }
129 
run()130 int StackEditorDialog::run()
131 {
132   dialog->show_all();
133   return dialog->run();
134 }
135 
update_armies()136 void StackEditorDialog::update_armies ()
137 {
138   // remove removed armies from stack
139   for (Stack::iterator i = stack->begin(), end = stack->end(); i != end;)
140     {
141       Army *a = *i;
142       ++i;
143 
144       bool found = false;
145       for (Gtk::TreeIter j = army_list->children().begin(),
146            jend = army_list->children().end(); j != jend; ++j)
147         if ((*j)[army_columns.army] == a)
148           {
149             found = true;
150             break;
151           }
152 
153       if (!found)
154         {
155           stack->remove(a);
156           delete a;
157         }
158     }
159 
160   //set the stats for all of the armies in our list
161   for (Gtk::TreeIter j = army_list->children().begin(),
162        jend = army_list->children().end(); j != jend; ++j)
163     {
164       Army *a = (*j)[army_columns.army];
165       a->setStat(Army::STRENGTH, (*j)[army_columns.strength]);
166       a->setMaxMoves((*j)[army_columns.moves]);
167       a->setStat(Army::MOVES, (*j)[army_columns.moves]);
168       a->setUpkeep((*j)[army_columns.upkeep]);
169     }
170   bool ship = stack->hasShip();
171   // add added armies to stack
172   for (Gtk::TreeIter j = army_list->children().begin(),
173        jend = army_list->children().end(); j != jend; ++j)
174     {
175       Army *a = (*j)[army_columns.army];
176 
177       a->setInShip(ship);
178       if (std::find(stack->begin(), stack->end(), a) == stack->end())
179         stack->push_back(a);
180     }
181 
182   // now set allegiance, it's important to do it after possibly new stack
183   // armies have been added
184   // this also helps the stack ship icon show up when it's needed.
185 
186   if (get_selected_player()->getId() != stack->getOwner()->getId())
187     {
188       Player *player = get_selected_player();
189       Player *old_active = Playerlist::getActiveplayer();
190       Playerlist::getInstance()->setActiveplayer(player);
191       Stack *new_stack = new Stack(*stack);
192       GameMap::getStacks(new_stack->getPos())->remove(stack);
193       stack->sdying.emit(stack);
194       stack->getOwner()->deleteStack(stack);
195       new_stack->setPlayer(player);
196       GameMap::getInstance()->putStack(new_stack);
197       Playerlist::getInstance()->setActiveplayer(old_active);
198       stack = new_stack;
199 
200       Stack::iterator i = stack->begin ();
201       for (Gtk::TreeIter j = army_list->children().begin(),
202            jend = army_list->children().end(); j != jend; ++j, ++i)
203       (*j)[army_columns.army] = *i;
204     }
205 }
206 
get_selected_player()207 Player *StackEditorDialog::get_selected_player()
208 {
209   int c = 0, row = player_combobox->get_active_row_number();
210   Player *player = Playerlist::getInstance()->getNeutral();
211   for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
212        end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
213     if (c == row)
214       {
215 	player = *i;
216 	break;
217       }
218   return player;
219 }
220 
on_copy_clicked()221 void StackEditorDialog::on_copy_clicked()
222 {
223   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
224   if (i)
225     {
226       Player *player = get_selected_player();
227       Army *army = (*i)[army_columns.army];
228       Army *new_army = new Army(*army, player);
229       new_army->assignNewId();
230       add_army(new_army);
231       update_armies ();
232     }
233 
234   set_button_sensitivity();
235 }
236 
on_add_clicked()237 void StackEditorDialog::on_add_clicked()
238 {
239   SelectArmyDialog d(*dialog, SelectArmyDialog::SELECT_NORMAL_WITH_HERO,
240                      stack->getOwner(), -1);
241   d.run();
242 
243   Player *player = get_selected_player();
244   const ArmyProto *army = d.get_selected_army();
245   if (army)
246     {
247       if (army->isHero() == true)
248         {
249           HeroProto *hp = new HeroProto(*army);
250           hp->setOwnerId(player->getId());
251           add_army(new Hero(*hp));
252           delete hp;
253         }
254       else
255         add_army(new Army(*army, player));
256       update_armies ();
257     }
258 }
259 
on_edit_hero_clicked()260 void StackEditorDialog::on_edit_hero_clicked()
261 {
262   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
263   if (i)
264     {
265       Army *army = (*i)[army_columns.army];
266       Hero *hero = dynamic_cast<Hero*>(army);
267       HeroEditorDialog d(*dialog, hero);
268       d.run();
269       (*i)[army_columns.name] = hero->getName();
270       update_armies ();
271     }
272 
273 }
274 
on_remove_clicked()275 void StackEditorDialog::on_remove_clicked()
276 {
277   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
278   if (i)
279     {
280       Army *army = (*i)[army_columns.army];
281       army_list->erase(i);
282       if (std::find(stack->begin(), stack->end(), army) == stack->end())
283         delete army;
284       update_armies ();
285     }
286 
287   set_button_sensitivity();
288 }
289 
add_army(Army * a)290 void StackEditorDialog::add_army(Army *a)
291 {
292   ImageCache *gc = ImageCache::getInstance();
293   Gtk::TreeIter i = army_list->append();
294   (*i)[army_columns.army] = a;
295   guint32 fs = FontSize::getInstance ()->get_height ();
296   (*i)[army_columns.image] = gc->getArmyPic(a->getOwner()->getArmyset(),
297                                             a->getTypeId(), a->getOwner(),
298                                             NULL, false, fs)->to_pixbuf();
299   (*i)[army_columns.strength] = a->getStat(Army::STRENGTH, false);
300   (*i)[army_columns.moves] = a->getStat(Army::MOVES, false);
301   (*i)[army_columns.upkeep] = a->getUpkeep();
302   (*i)[army_columns.name] = a->getName();
303 
304   army_treeview->get_selection()->select(i);
305 
306   set_button_sensitivity();
307 }
308 
on_selection_changed()309 void StackEditorDialog::on_selection_changed()
310 {
311   set_button_sensitivity();
312 }
313 
set_button_sensitivity()314 void StackEditorDialog::set_button_sensitivity()
315 {
316   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
317   int armies = army_list->children().size();
318   add_button->set_sensitive(armies < max_stack_size);
319   copy_button->set_sensitive(armies < max_stack_size);
320   remove_button->set_sensitive(armies > min_size && i);
321   if (i)
322     {
323       Army *army = (*i)[army_columns.army];
324       if (army->isHero())
325         {
326           edit_hero_button->set_sensitive(true);
327           copy_button->set_sensitive(false);
328         }
329       else
330         edit_hero_button->set_sensitive(false);
331     }
332   Player *player = get_selected_player();
333   bool neutral = player == Playerlist::getInstance()->getNeutral();
334   bool can_defend = GameMap::getInstance()->can_defend (stack);
335   if (neutral || !can_defend)
336     fortified_switch->set_sensitive(false);
337   else
338     fortified_switch->set_sensitive(true);
339 }
340 
on_fortified_toggled()341 void StackEditorDialog::on_fortified_toggled()
342 {
343   stack->setFortified(fortified_switch->get_active());
344 }
345 
on_player_changed()346 void StackEditorDialog::on_player_changed()
347 {
348   ImageCache *gc = ImageCache::getInstance();
349   Player *player = get_selected_player();
350   if (player == Playerlist::getInstance()->getNeutral())
351     fortified_switch->set_active(false);
352   set_button_sensitivity();
353 
354   guint32 fs = FontSize::getInstance ()->get_height ();
355   for (Gtk::TreeIter j = army_list->children().begin(),
356        jend = army_list->children().end(); j != jend; ++j)
357     {
358       Army *a = (*j)[army_columns.army];
359       (*j)[army_columns.image] = gc->getArmyPic(player->getArmyset(),
360                                                 a->getTypeId(),
361                                                 player, NULL, false,
362                                                 fs)->to_pixbuf();
363     }
364 
365   update_armies ();
366 }
367 
cell_data_strength(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)368 void StackEditorDialog::cell_data_strength(Gtk::CellRenderer *renderer,
369 				     const Gtk::TreeIter& i)
370 {
371   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
372     = Gtk::Adjustment::create((*i)[army_columns.strength],
373                               MIN_STRENGTH_FOR_ARMY_UNITS,
374                               MAX_STRENGTH_FOR_ARMY_UNITS, 1);
375   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
376     String::ucompose("%1", (*i)[army_columns.strength]);
377 }
378 
on_strength_edited(const Glib::ustring & path,const Glib::ustring & new_text)379 void StackEditorDialog::on_strength_edited(const Glib::ustring &path,
380 				   const Glib::ustring &new_text)
381 {
382   int str = atoi(new_text.c_str());
383   if (str < (int)MIN_STRENGTH_FOR_ARMY_UNITS || str >
384       (int)MAX_STRENGTH_FOR_ARMY_UNITS)
385     return;
386   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.strength] = str;
387   update_armies ();
388 }
389 
cell_data_moves(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)390 void StackEditorDialog::cell_data_moves(Gtk::CellRenderer *renderer,
391 				  const Gtk::TreeIter& i)
392 {
393   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
394     = Gtk::Adjustment::create((*i)[army_columns.moves],
395                               MIN_MOVES_FOR_ARMY_UNITS,
396                               MAX_MOVES_FOR_ARMY_UNITS, 1);
397   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
398     String::ucompose("%1", (*i)[army_columns.moves]);
399 }
400 
on_moves_edited(const Glib::ustring & path,const Glib::ustring & new_text)401 void StackEditorDialog::on_moves_edited(const Glib::ustring &path,
402 				   const Glib::ustring &new_text)
403 {
404   int moves = atoi(new_text.c_str());
405   if (moves < (int)MIN_MOVES_FOR_ARMY_UNITS || moves >
406       (int)MAX_MOVES_FOR_ARMY_UNITS)
407     return;
408   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.moves] = moves;
409   update_armies ();
410 }
411 
cell_data_upkeep(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)412 void StackEditorDialog::cell_data_upkeep(Gtk::CellRenderer *renderer,
413 				   const Gtk::TreeIter& i)
414 {
415   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
416     = Gtk::Adjustment::create((*i)[army_columns.upkeep],
417                               MIN_UPKEEP_FOR_ARMY_UNITS,
418                               MAX_UPKEEP_FOR_ARMY_UNITS, 1);
419   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
420     String::ucompose("%1", (*i)[army_columns.upkeep]);
421 }
422 
on_upkeep_edited(const Glib::ustring & path,const Glib::ustring & new_text)423 void StackEditorDialog::on_upkeep_edited(const Glib::ustring &path,
424 				   const Glib::ustring &new_text)
425 {
426   int upkeep = atoi(new_text.c_str());
427   if (upkeep < (int)MIN_UPKEEP_FOR_ARMY_UNITS ||
428       upkeep > (int)MAX_UPKEEP_FOR_ARMY_UNITS)
429     return;
430   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.upkeep] = upkeep;
431   update_armies ();
432 }
433