initial commit, lordsawar source, slightly modified
[lordsawar] / src / editor / cityset-window.cpp
1 //  Copyright (C) 2009 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 <iostream>
21 #include <iomanip>
22 #include <assert.h>
23 #include <string.h>
24
25 #include <sigc++/functors/mem_fun.h>
26 #include <sigc++/functors/ptr_fun.h>
27
28 #include <gtkmm.h>
29 #include "cityset-window.h"
30 #include "cityset-info-dialog.h"
31 #include "masked-image-editor-dialog.h"
32
33 #include "image-helpers.h"
34 #include "input-helpers.h"
35 #include "error-utils.h"
36
37 #include "defs.h"
38 #include "File.h"
39
40 #include "ucompose.hpp"
41
42 #include "glade-helpers.h"
43 #include "image-editor-dialog.h"
44 #include "GraphicsCache.h"
45 #include "citysetlist.h"
46
47
48 CitySetWindow::CitySetWindow(std::string load_filename)
49 {
50   needs_saving = false;
51   d_cityset = NULL;
52     Glib::RefPtr<Gtk::Builder> xml
53         = Gtk::Builder::create_from_file(get_glade_path() + "/cityset-window.ui");
54
55     xml->get_widget("window", window);
56     window->set_icon_from_file(File::getMiscFile("various/castle_icon.png"));
57     window->signal_delete_event().connect
58       (sigc::mem_fun(*this, &CitySetWindow::on_window_closed));
59
60     xml->get_widget("cityset_frame", cityset_frame);
61     xml->get_widget("new_cityset_menuitem", new_cityset_menuitem);
62     new_cityset_menuitem->signal_activate().connect
63       (sigc::mem_fun(this, &CitySetWindow::on_new_cityset_activated));
64     xml->get_widget("load_cityset_menuitem", load_cityset_menuitem);
65     load_cityset_menuitem->signal_activate().connect
66       (sigc::mem_fun(this, &CitySetWindow::on_load_cityset_activated));
67     xml->get_widget("save_cityset_menuitem", save_cityset_menuitem);
68     save_cityset_menuitem->signal_activate().connect
69       (sigc::mem_fun(this, &CitySetWindow::on_save_cityset_activated));
70     xml->get_widget("validate_cityset_menuitem", validate_cityset_menuitem);
71     validate_cityset_menuitem->signal_activate().connect
72       (sigc::mem_fun(this, &CitySetWindow::on_validate_cityset_activated));
73     xml->get_widget("quit_menuitem", quit_menuitem);
74     quit_menuitem->signal_activate().connect
75        (sigc::mem_fun(this, &CitySetWindow::on_quit_activated));
76     xml->get_widget("edit_cityset_info_menuitem", edit_cityset_info_menuitem);
77     edit_cityset_info_menuitem->signal_activate().connect
78       (sigc::mem_fun(this, &CitySetWindow::on_edit_cityset_info_activated));
79     xml->get_widget ("help_about_menuitem", help_about_menuitem);
80     help_about_menuitem->signal_activate().connect
81        (sigc::mem_fun(this, &CitySetWindow::on_help_about_activated));
82     xml->get_widget("city_tile_width_spinbutton", city_tile_width_spinbutton);
83     city_tile_width_spinbutton->set_range (1, 4);
84     city_tile_width_spinbutton->signal_changed().connect
85       (sigc::mem_fun(this, &CitySetWindow::on_city_tile_width_changed));
86     city_tile_width_spinbutton->signal_insert_text().connect
87       (sigc::mem_fun(this, &CitySetWindow::on_city_tile_width_text_changed));
88     xml->get_widget("ruin_tile_width_spinbutton", ruin_tile_width_spinbutton);
89     ruin_tile_width_spinbutton->set_range (1, 4);
90     ruin_tile_width_spinbutton->signal_changed().connect
91       (sigc::mem_fun(this, &CitySetWindow::on_ruin_tile_width_changed));
92     ruin_tile_width_spinbutton->signal_insert_text().connect
93       (sigc::mem_fun(this, &CitySetWindow::on_ruin_tile_width_text_changed));
94     xml->get_widget("temple_tile_width_spinbutton", 
95                     temple_tile_width_spinbutton);
96     temple_tile_width_spinbutton->set_range (1, 4);
97     temple_tile_width_spinbutton->signal_changed().connect
98       (sigc::mem_fun(this, &CitySetWindow::on_temple_tile_width_changed));
99     temple_tile_width_spinbutton->signal_insert_text().connect
100       (sigc::mem_fun(this, &CitySetWindow::on_temple_tile_width_text_changed));
101
102     window->signal_delete_event().connect
103       (sigc::mem_fun(*this, &CitySetWindow::on_delete_event));
104     xml->get_widget("change_citypics_button", change_citypics_button);
105     change_citypics_button->signal_clicked().connect(
106         sigc::mem_fun(this, &CitySetWindow::on_change_citypics_clicked));
107     xml->get_widget("change_razedcitypics_button", change_razedcitypics_button);
108     change_razedcitypics_button->signal_clicked().connect(
109         sigc::mem_fun(this, &CitySetWindow::on_change_razedcitypics_clicked));
110     xml->get_widget("change_portpic_button", change_portpic_button);
111     change_portpic_button->signal_clicked().connect(
112         sigc::mem_fun(this, &CitySetWindow::on_change_portpic_clicked));
113     xml->get_widget("change_signpostpic_button", change_signpostpic_button);
114     change_signpostpic_button->signal_clicked().connect(
115         sigc::mem_fun(this, &CitySetWindow::on_change_signpostpic_clicked));
116     xml->get_widget("change_ruinpics_button", change_ruinpics_button);
117     change_ruinpics_button->signal_clicked().connect(
118         sigc::mem_fun(this, &CitySetWindow::on_change_ruinpics_clicked));
119     xml->get_widget("change_templepic_button", change_templepic_button);
120     change_templepic_button->signal_clicked().connect(
121         sigc::mem_fun(this, &CitySetWindow::on_change_templepic_clicked));
122     xml->get_widget("change_towerpics_button", change_towerpics_button);
123     change_towerpics_button->signal_clicked().connect(
124         sigc::mem_fun(this, &CitySetWindow::on_change_towerpics_clicked));
125
126     update_cityset_panel();
127
128     if (load_filename.empty() == false)
129       {
130         load_cityset (load_filename);
131         update_cityset_panel();
132       }
133     update_cityset_menuitems();
134 }
135
136 void
137 CitySetWindow::update_cityset_menuitems()
138 {
139   if (d_cityset == NULL)
140     {
141       save_cityset_menuitem->set_sensitive(false);
142       validate_cityset_menuitem->set_sensitive(false);
143       edit_cityset_info_menuitem->set_sensitive(false);
144     }
145   else
146     {
147       std::string file = d_cityset->getConfigurationFile();
148       if (File::exists(file) == false)
149         save_cityset_menuitem->set_sensitive(true);
150       else if (File::is_writable(file) == false)
151         save_cityset_menuitem->set_sensitive(false);
152       else
153         save_cityset_menuitem->set_sensitive(true);
154       edit_cityset_info_menuitem->set_sensitive(true);
155       validate_cityset_menuitem->set_sensitive(true);
156     }
157 }
158
159 void
160 CitySetWindow::update_cityset_panel()
161 {
162   cityset_frame->set_sensitive(d_cityset != NULL);
163   std::string no_image = _("no image set");
164   std::string s;
165   if (d_cityset && d_cityset->getCitiesFilename().empty() == false)
166     s = d_cityset->getCitiesFilename() + ".png";
167   else
168     s = no_image;
169   change_citypics_button->set_label(s);
170   if (d_cityset && d_cityset->getRazedCitiesFilename().empty() == false)
171     s = d_cityset->getRazedCitiesFilename() + ".png";
172   else
173     s = no_image;
174   change_razedcitypics_button->set_label(s);
175   if (d_cityset && d_cityset->getPortFilename().empty() == false)
176     s = d_cityset->getPortFilename() + ".png";
177   else
178     s = no_image;
179   change_portpic_button->set_label(s);
180   if (d_cityset && d_cityset->getSignpostFilename().empty() == false)
181     s = d_cityset->getSignpostFilename() + ".png";
182   else
183     s = no_image;
184   change_signpostpic_button->set_label(s);
185   if (d_cityset && d_cityset->getRuinsFilename().empty() == false)
186     s = d_cityset->getRuinsFilename() + ".png";
187   else
188     s = no_image;
189   change_ruinpics_button->set_label(s);
190   if (d_cityset && d_cityset->getTemplesFilename().empty() == false)
191     s = d_cityset->getTemplesFilename() + ".png";
192   else
193     s = no_image;
194   change_templepic_button->set_label(s);
195   if (d_cityset && d_cityset->getTowersFilename().empty() == false)
196     s = d_cityset->getTowersFilename() + ".png";
197   else
198     s = no_image;
199   change_towerpics_button->set_label(s);
200   if (d_cityset)
201     city_tile_width_spinbutton->set_value(d_cityset->getCityTileWidth());
202   else
203     city_tile_width_spinbutton->set_value(2);
204   if (d_cityset)
205     ruin_tile_width_spinbutton->set_value(d_cityset->getRuinTileWidth());
206   else
207     ruin_tile_width_spinbutton->set_value(1);
208   if (d_cityset)
209     temple_tile_width_spinbutton->set_value(d_cityset->getTempleTileWidth());
210   else
211     temple_tile_width_spinbutton->set_value(1);
212 }
213
214 CitySetWindow::~CitySetWindow()
215 {
216   delete window;
217 }
218
219 void CitySetWindow::show()
220 {
221   window->show();
222 }
223
224 void CitySetWindow::hide()
225 {
226   window->hide();
227 }
228
229 bool CitySetWindow::on_delete_event(GdkEventAny *e)
230 {
231   hide();
232
233   return true;
234 }
235
236 void CitySetWindow::on_new_cityset_activated()
237 {
238   std::string name = "";
239   int id = Citysetlist::getNextAvailableId();
240   Cityset *cityset = new Cityset(id, name);
241   CitySetInfoDialog d(cityset, false);
242   d.set_parent_window(*window);
243   int response = d.run();
244   if (response != Gtk::RESPONSE_ACCEPT)
245     {
246       delete cityset;
247       return;
248     }
249   if (d_cityset)
250     delete d_cityset;
251   d_cityset = cityset;
252   std::string dir = File::getUserCitysetDir() + "/" + d_cityset->getSubDir();
253   d_cityset->setDirectory(dir);
254   File::create_dir(dir);
255   current_save_filename = d_cityset->getConfigurationFile();
256
257   update_cityset_panel();
258   update_cityset_menuitems();
259
260   XML_Helper helper(current_save_filename, std::ios::out, false);
261   d_cityset->save(&helper);
262   helper.close();
263   needs_saving = true;
264 }
265
266 void CitySetWindow::on_load_cityset_activated()
267 {
268   Gtk::FileChooserDialog chooser(*window, 
269                                  _("Choose a Cityset to Load"));
270   Gtk::FileFilter sav_filter;
271   sav_filter.add_pattern("*" + CITYSET_EXT);
272   chooser.set_filter(sav_filter);
273   chooser.set_current_folder(File::getUserCitysetDir());
274
275   chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
276   chooser.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
277   chooser.set_default_response(Gtk::RESPONSE_ACCEPT);
278
279   chooser.show_all();
280   int res = chooser.run();
281
282   if (res == Gtk::RESPONSE_ACCEPT)
283     {
284       load_cityset(chooser.get_filename());
285       chooser.hide();
286     }
287
288   update_cityset_menuitems();
289   update_cityset_panel();
290 }
291
292 void CitySetWindow::on_validate_cityset_activated()
293 {
294   std::list<std::string> msgs;
295   if (d_cityset == NULL)
296     return;
297   if (d_cityset->validateCitiesFilename() == false)
298     msgs.push_back(_("The cities picture is not set."));
299   if (d_cityset->validateRazedCitiesFilename() == false)
300     msgs.push_back(_("The razed cities picture is not set."));
301   if (d_cityset->validatePortFilename() == false)
302     msgs.push_back(_("The port picture is not set."));
303   if (d_cityset->validateSignpostFilename() == false)
304     msgs.push_back(_("The signpost picture is not set."));
305   if (d_cityset->validateRuinsFilename() == false)
306     msgs.push_back(_("The ruins picture is not set."));
307   if (d_cityset->validateTemplesFilename() == false)
308     msgs.push_back(_("The temple picture is not set."));
309   if (d_cityset->validateTowersFilename() == false)
310     msgs.push_back(_("The towers picture is not set."));
311   if (d_cityset->validateCityTileWidth() == false)
312     msgs.push_back(_("The tile width for temples must be over zero."));
313   if (d_cityset->validateRuinTileWidth() == false)
314     msgs.push_back(_("The tile width for ruins must be over zero."));
315   if (d_cityset->validateTempleTileWidth() == false)
316     msgs.push_back(_("The tile width for temples must be over zero."));
317
318   std::string msg = "";
319   for (std::list<std::string>::iterator it = msgs.begin(); it != msgs.end();
320        it++)
321     msg += (*it) + "\n";
322
323   if (msg != "")
324     {
325       Gtk::MessageDialog dialog(*window, msg);
326       dialog.run();
327       dialog.hide();
328     }
329
330   return;
331 }
332
333 void CitySetWindow::on_save_cityset_activated()
334 {
335   if (current_save_filename.empty())
336     current_save_filename = d_cityset->getConfigurationFile();
337
338   XML_Helper helper(current_save_filename, std::ios::out, false);
339   d_cityset->save(&helper);
340   helper.close();
341   needs_saving = false;
342 }
343
344 void CitySetWindow::on_edit_cityset_info_activated()
345 {
346   CitySetInfoDialog d(d_cityset, true);
347   d.set_parent_window(*window);
348   int response = d.run();
349   if (response == Gtk::RESPONSE_ACCEPT)
350     needs_saving = true;
351 }
352
353 void CitySetWindow::on_help_about_activated()
354 {
355   Gtk::AboutDialog* dialog;
356
357   Glib::RefPtr<Gtk::Builder> xml
358     = Gtk::Builder::create_from_file(get_glade_path() + "/../about-dialog.ui");
359
360   xml->get_widget("dialog", dialog);
361   dialog->set_transient_for(*window);
362   dialog->set_icon_from_file(File::getMiscFile("various/castle_icon.png"));
363
364   dialog->set_version(PACKAGE_VERSION);
365   dialog->set_logo(GraphicsCache::getMiscPicture("castle_icon.png")->to_pixbuf());
366   dialog->show_all();
367   dialog->run();
368   delete dialog;
369
370   return;
371 }
372
373 void CitySetWindow::load_cityset(std::string filename)
374 {
375   current_save_filename = filename;
376
377   std::string name = File::get_basename(filename);
378
379   Cityset *cityset = Cityset::create(filename);
380   if (cityset == NULL)
381     {
382       std::string msg;
383       msg = "Error!  Cityset could not be loaded.";
384       Gtk::MessageDialog dialog(*window, msg);
385       dialog.run();
386       dialog.hide();
387       return;
388     }
389   if (d_cityset)
390     delete d_cityset;
391   d_cityset = cityset;
392
393   d_cityset->setSubDir(name);
394   d_cityset->instantiateImages();
395 }
396
397 bool CitySetWindow::quit()
398 {
399   if (needs_saving == true)
400     {
401       Gtk::Dialog* dialog;
402       Glib::RefPtr<Gtk::Builder> xml
403         = Gtk::Builder::create_from_file(get_glade_path() + 
404                                          "/editor-quit-dialog.ui");
405       xml->get_widget("dialog", dialog);
406       dialog->set_transient_for(*window);
407       int response = dialog->run();
408       dialog->hide();
409       delete dialog;
410
411       if (response == Gtk::RESPONSE_CANCEL) //we don't want to quit
412         return false;
413
414       else if (response == Gtk::RESPONSE_ACCEPT) // save and quit
415         on_save_cityset_activated();
416       //else if (Response == Gtk::CLOSE) // don't save just quit
417       window->hide();
418     }
419   else
420     window->hide();
421   return true;
422 }
423
424 bool CitySetWindow::on_window_closed(GdkEventAny*)
425 {
426   return !quit();
427 }
428
429 void CitySetWindow::on_quit_activated()
430 {
431   quit();
432 }
433
434 void CitySetWindow::on_city_tile_width_text_changed(const Glib::ustring &s, int* p)
435 {
436   city_tile_width_spinbutton->set_value(atoi(city_tile_width_spinbutton->get_text().c_str()));
437   on_city_tile_width_changed();
438 }
439
440 void CitySetWindow::on_city_tile_width_changed()
441 {
442   if (!d_cityset)
443     return;
444   d_cityset->setCityTileWidth(city_tile_width_spinbutton->get_value());
445   needs_saving = true;
446 }
447
448 void CitySetWindow::on_ruin_tile_width_text_changed(const Glib::ustring &s, int* p)
449 {
450   ruin_tile_width_spinbutton->set_value(atoi(ruin_tile_width_spinbutton->get_text().c_str()));
451   on_ruin_tile_width_changed();
452 }
453
454 void CitySetWindow::on_ruin_tile_width_changed()
455 {
456   if (!d_cityset)
457     return;
458   d_cityset->setRuinTileWidth(ruin_tile_width_spinbutton->get_value());
459   needs_saving = true;
460 }
461
462 void CitySetWindow::on_temple_tile_width_text_changed(const Glib::ustring &s, int* p)
463 {
464   temple_tile_width_spinbutton->set_value(atoi(temple_tile_width_spinbutton->get_text().c_str()));
465   on_temple_tile_width_changed();
466 }
467
468 void CitySetWindow::on_temple_tile_width_changed()
469 {
470   if (!d_cityset)
471     return;
472   d_cityset->setTempleTileWidth(temple_tile_width_spinbutton->get_value());
473   needs_saving = true;
474 }
475
476 void CitySetWindow::on_change_citypics_clicked()
477 {
478   std::string filename = "";
479   if (d_cityset->getCitiesFilename().empty() == false)
480     filename = d_cityset->getFile(d_cityset->getCitiesFilename());
481   ImageEditorDialog d(filename, MAX_PLAYERS + 1);
482   int response = d.run();
483   if (response == Gtk::RESPONSE_ACCEPT)
484     {
485       if (d_cityset->getFile(d_cityset->getCitiesFilename()) != 
486           d.get_selected_filename())
487         {
488           File::copy(d.get_selected_filename(), 
489                      d_cityset->getFile(d_cityset->getCitiesFilename()));
490         }
491
492       d_cityset->setCitiesFilename
493         (File::get_basename(d.get_selected_filename()));
494       needs_saving = true;
495       update_cityset_panel();
496     }
497 }
498 void CitySetWindow::on_change_razedcitypics_clicked()
499 {
500   std::string filename = "";
501   if (d_cityset->getRazedCitiesFilename().empty() == false)
502     filename = d_cityset->getFile(d_cityset->getRazedCitiesFilename());
503   ImageEditorDialog d(filename, MAX_PLAYERS);
504   int response = d.run();
505   if (response == Gtk::RESPONSE_ACCEPT)
506     {
507       if (d_cityset->getFile(d_cityset->getRazedCitiesFilename()) != 
508           d.get_selected_filename())
509         {
510           File::copy(d.get_selected_filename(), 
511                      d_cityset->getFile(d_cityset->getRazedCitiesFilename()));
512         }
513
514       d_cityset->setRazedCitiesFilename
515         (File::get_basename(d.get_selected_filename()));
516       needs_saving = true;
517       update_cityset_panel();
518     }
519 }
520 void CitySetWindow::on_change_portpic_clicked()
521 {
522   std::string filename = "";
523   if (d_cityset->getPortFilename().empty() == false)
524     filename = d_cityset->getFile(d_cityset->getPortFilename());
525   ImageEditorDialog d(filename, 1);
526   int response = d.run();
527   if (response == Gtk::RESPONSE_ACCEPT)
528     {
529       if (d_cityset->getFile(d_cityset->getPortFilename()) != 
530           d.get_selected_filename())
531         {
532           File::copy(d.get_selected_filename(), 
533                      d_cityset->getFile(d_cityset->getPortFilename()));
534         }
535
536       d_cityset->setPortFilename(File::get_basename(d.get_selected_filename()));
537       needs_saving = true;
538       update_cityset_panel();
539     }
540 }
541 void CitySetWindow::on_change_signpostpic_clicked()
542 {
543   std::string filename = "";
544   if (d_cityset->getSignpostFilename().empty() == false)
545     filename = d_cityset->getFile(d_cityset->getSignpostFilename());
546   ImageEditorDialog d(filename, 1);
547   int response = d.run();
548   if (response == Gtk::RESPONSE_ACCEPT)
549     {
550       if (d_cityset->getFile(d_cityset->getSignpostFilename()) != 
551           d.get_selected_filename())
552         {
553           File::copy(d.get_selected_filename(), 
554                      d_cityset->getFile(d_cityset->getSignpostFilename()));
555         }
556
557       d_cityset->setSignpostFilename
558         (File::get_basename(d.get_selected_filename()));
559       needs_saving = true;
560       update_cityset_panel();
561     }
562 }
563 void CitySetWindow::on_change_ruinpics_clicked()
564 {
565   std::string filename = "";
566   if (d_cityset->getRuinsFilename().empty() == false)
567     filename = d_cityset->getFile(d_cityset->getRuinsFilename());
568   ImageEditorDialog d(filename, RUIN_TYPES);
569   int response = d.run();
570   if (response == Gtk::RESPONSE_ACCEPT)
571     {
572       if (d_cityset->getFile(d_cityset->getRuinsFilename()) != 
573           d.get_selected_filename())
574         {
575           File::copy(d.get_selected_filename(), 
576                      d_cityset->getFile(d_cityset->getRuinsFilename()));
577         }
578
579       d_cityset->setRuinsFilename
580         (File::get_basename(d.get_selected_filename()));
581       needs_saving = true;
582       update_cityset_panel();
583     }
584 }
585 void CitySetWindow::on_change_templepic_clicked()
586 {
587   std::string filename = "";
588   if (d_cityset->getTemplesFilename().empty() == false)
589     filename = d_cityset->getFile(d_cityset->getTemplesFilename());
590   ImageEditorDialog d(filename, TEMPLE_TYPES);
591   int response = d.run();
592   if (response == Gtk::RESPONSE_ACCEPT)
593     {
594       if (d_cityset->getFile(d_cityset->getTemplesFilename()) != 
595           d.get_selected_filename())
596         {
597           File::copy(d.get_selected_filename(), 
598                      d_cityset->getFile(d_cityset->getTemplesFilename()));
599         }
600
601       d_cityset->setTemplesFilename
602         (File::get_basename(d.get_selected_filename()));
603       needs_saving = true;
604       update_cityset_panel();
605     }
606 }
607 void CitySetWindow::on_change_towerpics_clicked()
608 {
609   std::string filename = "";
610   if (d_cityset->getTowersFilename().empty() == false)
611     filename = d_cityset->getFile(d_cityset->getTowersFilename());
612   ImageEditorDialog d(filename, MAX_PLAYERS);
613   int response = d.run();
614   if (response == Gtk::RESPONSE_ACCEPT)
615     {
616       if (d_cityset->getFile(d_cityset->getTowersFilename()) != 
617           d.get_selected_filename())
618         {
619           File::copy(d.get_selected_filename(), 
620                      d_cityset->getFile(d_cityset->getTowersFilename()));
621         }
622
623       d_cityset->setTowersFilename
624         (File::get_basename(d.get_selected_filename()));
625       needs_saving = true;
626       update_cityset_panel();
627     }
628 }