1 // Copyright (C) 2007 Ole Laursen
2 // Copyright (C) 2007, 2008, 2009 Ben Asselstine
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.
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.
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
24 #include <sigc++/functors/mem_fun.h>
26 #include "city-window.h"
28 #include "glade-helpers.h"
29 #include "image-helpers.h"
30 #include "input-helpers.h"
31 #include "ucompose.hpp"
34 #include "armyprodbase.h"
37 #include "GraphicsCache.h"
38 #include "armysetlist.h"
39 #include "buy-production-dialog.h"
40 #include "destination-dialog.h"
43 #include "playerlist.h"
46 CityWindow::CityWindow(City *c, bool razing_possible,
47 bool see_opponents_production)
52 Glib::RefPtr<Gtk::Builder> xml
53 = Gtk::Builder::create_from_file(get_glade_path() + "/city-window.ui");
55 xml->get_widget("dialog", dialog);
57 window_closed.connect(sigc::mem_fun(dialog, &Gtk::Dialog::hide));
58 set_title(c->getName());
60 xml->get_widget("map_image", map_image);
62 prodmap = new VectorMap(c, VectorMap::SHOW_ORIGIN_CITY_VECTORING,
63 see_opponents_production);
64 prodmap->map_changed.connect(
65 sigc::mem_fun(this, &CityWindow::on_map_changed));
67 Gtk::EventBox *map_eventbox;
68 xml->get_widget("map_eventbox", map_eventbox);
69 map_eventbox->add_events(Gdk::BUTTON_PRESS_MASK);
70 map_eventbox->signal_button_press_event().connect(
71 sigc::mem_fun(*this, &CityWindow::on_map_mouse_button_event));
72 xml->get_widget("status_label", status_label);
73 xml->get_widget("turns_left_label", turns_left_label);
74 xml->get_widget("current_label", current_label);
75 xml->get_widget("current_image", current_image);
76 xml->get_widget("production_info_label1", production_info_label1);
77 xml->get_widget("production_info_label2", production_info_label2);
78 xml->get_widget("buy_button", buy_button);
79 xml->get_widget("on_hold_button", on_hold_button);
80 on_hold_button->signal_clicked().connect(
81 sigc::mem_fun(this, &CityWindow::on_on_hold_clicked));
82 buy_button->signal_clicked().connect(
83 sigc::mem_fun(this, &CityWindow::on_buy_clicked));
84 xml->get_widget("destination_button", destination_button);
85 destination_button->signal_clicked().connect(
86 sigc::mem_fun(this, &CityWindow::on_destination_clicked));
87 xml->get_widget("rename_button", rename_button);
88 rename_button->signal_clicked().connect(
89 sigc::mem_fun(this, &CityWindow::on_rename_clicked));
90 xml->get_widget("raze_button", raze_button);
91 raze_button->signal_clicked().connect(
92 sigc::mem_fun(this, &CityWindow::on_raze_clicked));
94 xml->get_widget("production_toggles_hbox", production_toggles_hbox);
95 for (unsigned int i = 1; i <= city->getMaxNoOfProductionBases(); ++i) {
96 Gtk::ToggleButton *toggle = new Gtk::ToggleButton();
97 production_toggles_hbox->pack_start(*manage(toggle), false, false, 0);
98 production_toggles.push_back(toggle);
99 toggle->signal_toggled().connect(
100 sigc::bind(sigc::mem_fun(this, &CityWindow::on_production_toggled),
102 toggle->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
103 toggle->signal_button_press_event().connect(
104 sigc::bind(sigc::mem_fun(*this, &CityWindow::on_production_button_event),
107 toggle->signal_button_release_event().connect(
108 sigc::bind(sigc::mem_fun(*this, &CityWindow::on_production_button_event),
113 d_razing_possible = razing_possible;
115 fill_in_production_toggles();
117 ignore_toggles = false;
120 CityWindow::~CityWindow()
124 if (army_info_tip != NULL)
125 delete army_info_tip;
128 bool CityWindow::on_map_mouse_button_event(GdkEventButton *e)
130 if (e->type != GDK_BUTTON_PRESS)
131 return true; // useless event
133 prodmap->mouse_button_event(to_input_event(e));
135 city = prodmap->getCity();
137 fill_in_production_toggles();
138 fill_in_production_info();
142 void CityWindow::set_parent_window(Gtk::Window &parent)
144 dialog->set_transient_for(parent);
145 //dialog->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
148 void CityWindow::hide()
153 void CityWindow::run()
156 prodmap->draw(Playerlist::getActiveplayer());
161 void CityWindow::fill_in_city_info()
164 set_title(city->getName());
166 // fill in status label
168 if (city->isCapital())
170 s += String::ucompose(_("Capital city of %1"),
171 city->getCapitalOwner()->getName());
175 s += String::ucompose(_("Defence: %1"), city->getDefenseLevel());
177 s += String::ucompose(_("Income: %1"), city->getGold());
178 status_label->set_text(s);
181 void CityWindow::fill_in_production_toggles()
183 Player *player = city->getOwner();
184 unsigned int as = player->getArmyset();
185 int production_index = city->getActiveProductionSlot();
187 Glib::RefPtr<Gdk::Pixbuf> pic;
188 GraphicsCache *gc = GraphicsCache::getInstance();
190 Glib::RefPtr<Gdk::Pixbuf> s
191 = GraphicsCache::getInstance()->getArmyPic(as, 0, player, NULL)->to_pixbuf();
192 Glib::RefPtr<Gdk::Pixbuf> empty_pic
193 = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, s->get_width(), s->get_height());
194 empty_pic->fill(0x00000000);
196 ignore_toggles = true;
197 for (unsigned int i = 0; i < city->getMaxNoOfProductionBases(); i++)
199 Gtk::ToggleButton *toggle = production_toggles[i];
200 toggle->foreach(sigc::mem_fun(toggle, &Gtk::Container::remove));
202 type = city->getArmytype(i);
204 // use GraphicsCache to load army pics because of player-specific
206 pic = gc->getArmyPic(as, type, player, NULL)->to_pixbuf();
210 Gtk::Image *image = new Gtk::Image();
211 image->property_pixbuf() = pic;
212 toggle->add(*manage(image));
214 toggle->set_active((int)i == production_index);
217 ignore_toggles = false;
219 on_hold_button->set_sensitive(production_index != -1);
220 fill_in_production_info();
224 void CityWindow::on_production_toggled(Gtk::ToggleButton *toggle)
226 if (city->getOwner() != Playerlist::getActiveplayer())
228 toggle->set_active(false);
235 ignore_toggles = true;
236 for (unsigned int i = 0; i < production_toggles.size(); ++i) {
237 if (toggle == production_toggles[i])
240 production_toggles[i]->set_active(toggle == production_toggles[i]);
242 ignore_toggles = false;
244 bool is_empty = city->getArmytype(slot) == -1;
247 city->getOwner()->cityChangeProduction(city, -1);
249 city->getOwner()->cityChangeProduction(city, slot);
251 on_hold_button->set_sensitive(!is_empty);
253 fill_in_production_info();
256 void CityWindow::fill_in_production_info()
258 Player *player = city->getOwner();
259 unsigned int as = player->getArmyset();
260 Glib::RefPtr<Gdk::Pixbuf> pic;
261 GraphicsCache *gc = GraphicsCache::getInstance();
262 int slot = city->getActiveProductionSlot();
263 Glib::RefPtr<Gdk::Pixbuf> s
264 = GraphicsCache::getInstance()->getArmyPic(as, 0, player, NULL)->to_pixbuf();
265 Glib::RefPtr<Gdk::Pixbuf> empty_pic
266 = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, s->get_width(), s->get_height());
267 empty_pic->fill(0x00000000);
269 Glib::ustring s1, s2, s3;
270 Glib::ustring s4 = _("Current:");
275 s1 = _("No production");
282 const ArmyProdBase * a = city->getProductionBase(slot);
284 // fill in first column
287 // note to translators: %1/%2 is the no. of steps completed out of the
288 // total no. of steps in the production
289 s1 += String::ucompose(_("Time: %1"), a->getProduction());
291 s1 += String::ucompose(_("Strength: %1"),
294 // fill in second column
296 s2 += String::ucompose(_("Moves: %1"), a->getMaxMoves());
298 s2 += String::ucompose(_("Cost: %1"), a->getUpkeep());
300 s3 = String::ucompose(_("%1t"), city->getDuration());
301 if (city->getVectoring() != Vector<int>(-1, -1))
303 Citylist *cl = Citylist::getInstance();
304 City *dest = cl->getNearestFriendlyCity(city->getVectoring(), 4);
305 s3 += String::ucompose(_(", then to %1"),
306 dest ? dest->getName() : "Standard");
308 pic = gc->getArmyPic(as, a->getTypeId(), player, NULL)->to_pixbuf();
311 current_image->property_pixbuf() = pic;
312 production_info_label1->set_markup(s1);
313 production_info_label2->set_markup(s2);
314 turns_left_label->set_markup("<i>" + s3 + "</i>");
315 current_label->set_markup("<i>" + s4 + "</i>");
317 if (city->getOwner () != Playerlist::getActiveplayer())
319 turns_left_label->hide();
320 current_label->hide();
321 current_image->hide();
322 buy_button->set_sensitive(false);
323 raze_button->set_sensitive(false);
324 rename_button->set_sensitive(false);
325 destination_button->set_sensitive(false);
326 on_hold_button->set_sensitive(false);
327 for (unsigned int i = 0; i < production_toggles.size(); ++i)
329 // production_toggles[i]->set_sensitive(false);
330 production_toggles[i]->set_active(false);
332 production_info_label1->hide();
333 production_info_label2->hide();
337 turns_left_label->show();
338 current_label->show();
339 current_image->show();
340 buy_button->set_sensitive(true);
341 raze_button->set_sensitive (d_razing_possible);
342 rename_button->set_sensitive(true);
343 destination_button->set_sensitive(true);
344 on_hold_button->set_sensitive(true);
345 //for (unsigned int i = 0; i < production_toggles.size(); ++i)
346 //production_toggles[i]->set_sensitive(true);
347 production_info_label1->show();
348 production_info_label2->show();
352 bool CityWindow::on_production_button_event(GdkEventButton *e, Gtk::ToggleButton *toggle)
354 MouseButtonEvent event = to_input_event(e);
355 if (event.button == MouseButtonEvent::RIGHT_BUTTON
356 && event.state == MouseButtonEvent::PRESSED) {
358 for (unsigned int i = 0; i < production_toggles.size(); ++i) {
359 if (toggle == production_toggles[i])
364 const ArmyProdBase *prodbase = city->getProductionBase(slot);
368 if (army_info_tip != NULL)
369 delete army_info_tip;
370 army_info_tip = new ArmyInfoTip(toggle, prodbase, city);
374 else if (event.button == MouseButtonEvent::RIGHT_BUTTON
375 && event.state == MouseButtonEvent::RELEASED) {
377 if (army_info_tip != NULL)
379 delete army_info_tip;
380 army_info_tip = NULL;
389 void CityWindow::on_on_hold_clicked() //stop button
391 city->setVectoring(Vector<int>(-1,-1));
392 city->getOwner()->cityChangeProduction(city, -1);
393 on_hold_button->set_sensitive(false);
394 ignore_toggles = true;
395 for (unsigned int i = 0; i < production_toggles.size(); ++i)
396 production_toggles[i]->set_active(false);
397 ignore_toggles = false;
398 fill_in_production_info();
399 prodmap->draw(Playerlist::getActiveplayer());
402 void CityWindow::on_buy_clicked()
404 BuyProductionDialog d(city);
405 d.set_parent_window(*dialog);
408 int army = d.get_selected_army();
410 if (army != BuyProductionDialog::NO_ARMY_SELECTED)
413 slot = city->getFreeSlot();
417 //no free slots available. change the one we're on.
418 slot = city->getActiveProductionSlot();
422 city->getOwner()->cityBuyProduction(city, slot, army);
423 city->getOwner()->cityChangeProduction(city, slot);
425 fill_in_production_toggles();
426 fill_in_production_info();
431 void CityWindow::on_destination_clicked()
433 DestinationDialog d(city, &d_see_all);
434 d.set_parent_window(*dialog);
436 fill_in_production_info();
437 prodmap->draw(Playerlist::getActiveplayer());
440 void CityWindow::on_map_changed(Glib::RefPtr<Gdk::Pixmap> map)
442 map_image->property_pixmap() = map;
445 void CityWindow::on_rename_clicked ()
447 Gtk::Dialog* subdialog;
449 Glib::RefPtr<Gtk::Builder> renamexml
450 = Gtk::Builder::create_from_file(get_glade_path() + "/city-rename-dialog.ui");
452 renamexml->get_widget("dialog", subdialog);
454 decorator.decorate(subdialog);
455 decorator.window_closed.connect(sigc::mem_fun(subdialog, &Gtk::Dialog::hide));
456 subdialog->set_transient_for(*dialog);
458 Glib::ustring s = _("Rename City");
461 renamexml->get_widget("label", l);
463 renamexml->get_widget("name_entry", e);
465 decorator.set_title(s);
466 s = _("Type the new name for this city:");
469 e->set_text(city->getName());
470 subdialog->show_all();
471 int response = subdialog->run();
473 if (response == Gtk::RESPONSE_ACCEPT) // changed city name
475 Playerlist::getActiveplayer()->cityRename(city, e->get_text());
483 void CityWindow::on_raze_clicked ()
485 on_raze_clicked (city, dialog);
488 bool CityWindow::on_raze_clicked (City *city, Gtk::Dialog *parent)
490 Gtk::Dialog* subdialog;
492 Glib::RefPtr<Gtk::Builder> razexml
493 = Gtk::Builder::create_from_file(get_glade_path() + "/city-raze-dialog.ui");
495 razexml->get_widget("dialog", subdialog);
497 decorator.decorate(subdialog);
498 decorator.window_closed.connect(sigc::mem_fun(subdialog, &Gtk::Dialog::hide));
499 subdialog->set_transient_for(*parent);
500 subdialog->set_icon_from_file(File::getMiscFile("various/castle_icon.png"));
502 Glib::ustring s = _("Raze City");
505 razexml->get_widget("label", l);
507 decorator.set_title(s);
508 s = String::ucompose(_("Are you sure that you want to raze %1?"),
511 s += _("You won't be popular!");
514 subdialog->show_all();
515 int response = subdialog->run();
519 if (response == Gtk::RESPONSE_ACCEPT) // burn it to the ground ralphie boy!
521 Playerlist::getActiveplayer()->cityRaze(city);