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