1 // Copyright (C) 2009, 2010, 2011, 2012, 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 <iostream>
21 #include <iomanip>
22 #include <assert.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include <sigc++/functors/mem_fun.h>
27 #include <sigc++/functors/ptr_fun.h>
28
29 #include <gtkmm.h>
30 #include "cityset-window.h"
31 #include "builder-cache.h"
32 #include "cityset-info-dialog.h"
33
34 #include "defs.h"
35 #include "File.h"
36
37 #include "ucompose.hpp"
38
39 #include "image-editor-dialog.h"
40 #include "ImageCache.h"
41 #include "citysetlist.h"
42 #include "editor-quit-dialog.h"
43 #include "GameMap.h"
44 #include "editor-save-changes-dialog.h"
45 #include "timed-message-dialog.h"
46
47 #define method(x) sigc::mem_fun(*this, &CitySetWindow::x)
48
CitySetWindow(Glib::ustring load_filename)49 CitySetWindow::CitySetWindow(Glib::ustring load_filename)
50 {
51 needs_saving = false;
52 d_cityset = NULL;
53 Glib::RefPtr<Gtk::Builder> xml =
54 BuilderCache::editor_get("cityset-window.ui");
55
56 xml->get_widget("window", window);
57 window->set_icon_from_file(File::getVariousFile("castle_icon.png"));
58 window->signal_delete_event().connect (method(on_window_closed));
59
60 xml->get_widget("cityset_alignment", cityset_alignment);
61 xml->get_widget("new_cityset_menuitem", new_cityset_menuitem);
62 new_cityset_menuitem->signal_activate().connect (method(on_new_cityset_activated));
63 xml->get_widget("load_cityset_menuitem", load_cityset_menuitem);
64 load_cityset_menuitem->signal_activate().connect (method(on_load_cityset_activated));
65 xml->get_widget("save_cityset_menuitem", save_cityset_menuitem);
66 save_cityset_menuitem->signal_activate().connect (method(on_save_cityset_activated));
67 xml->get_widget("save_as_menuitem", save_as_menuitem);
68 save_as_menuitem->signal_activate().connect (method(on_save_as_activated));
69 xml->get_widget("validate_cityset_menuitem", validate_cityset_menuitem);
70 validate_cityset_menuitem->signal_activate().connect
71 (method(on_validate_cityset_activated));
72 xml->get_widget("quit_menuitem", quit_menuitem);
73 quit_menuitem->signal_activate().connect (method(on_quit_activated));
74 xml->get_widget("edit_cityset_info_menuitem", edit_cityset_info_menuitem);
75 edit_cityset_info_menuitem->signal_activate().connect
76 (method(on_edit_cityset_info_activated));
77 xml->get_widget ("help_about_menuitem", help_about_menuitem);
78 help_about_menuitem->signal_activate().connect
79 (method(on_help_about_activated));
80 xml->get_widget ("tutorial_menuitem", tutorial_menuitem);
81 tutorial_menuitem->signal_activate().connect
82 (method(on_tutorial_video_activated));
83 xml->get_widget("city_tile_width_spinbutton", city_tile_width_spinbutton);
84 city_tile_width_spinbutton->set_range (1, 4);
85 city_tile_width_spinbutton->signal_changed().connect
86 (method(on_city_tile_width_changed));
87 city_tile_width_spinbutton->signal_insert_text().connect
88 (sigc::hide(sigc::hide(method(on_city_tile_width_text_changed))));
89 xml->get_widget("ruin_tile_width_spinbutton", ruin_tile_width_spinbutton);
90 ruin_tile_width_spinbutton->set_range (1, 4);
91 ruin_tile_width_spinbutton->signal_changed().connect
92 (method(on_ruin_tile_width_changed));
93 ruin_tile_width_spinbutton->signal_insert_text().connect
94 (sigc::hide(sigc::hide(method(on_ruin_tile_width_text_changed))));
95 xml->get_widget("temple_tile_width_spinbutton",
96 temple_tile_width_spinbutton);
97 temple_tile_width_spinbutton->set_range (1, 4);
98 temple_tile_width_spinbutton->signal_changed().connect
99 (method(on_temple_tile_width_changed));
100 temple_tile_width_spinbutton->signal_insert_text().connect
101 (sigc::hide(sigc::hide(method(on_temple_tile_width_text_changed))));
102
103 xml->get_widget("change_citypics_button", change_citypics_button);
104 change_citypics_button->signal_clicked().connect
105 (method(on_change_citypics_clicked));
106 xml->get_widget("change_razedcitypics_button", change_razedcitypics_button);
107 change_razedcitypics_button->signal_clicked().connect
108 (method(on_change_razedcitypics_clicked));
109 xml->get_widget("change_portpic_button", change_portpic_button);
110 change_portpic_button->signal_clicked().connect(method(on_change_portpic_clicked));
111 xml->get_widget("change_signpostpic_button", change_signpostpic_button);
112 change_signpostpic_button->signal_clicked().connect
113 (method(on_change_signpostpic_clicked));
114 xml->get_widget("change_ruinpics_button", change_ruinpics_button);
115 change_ruinpics_button->signal_clicked().connect(method(on_change_ruinpics_clicked));
116 xml->get_widget("change_templepic_button", change_templepic_button);
117 change_templepic_button->signal_clicked().connect
118 (method(on_change_templepic_clicked));
119 xml->get_widget("change_towerpics_button", change_towerpics_button);
120 change_towerpics_button->signal_clicked().connect
121 (method(on_change_towerpics_clicked));
122 xml->get_widget ("notebook", notebook);
123
124 if (load_filename != "")
125 current_save_filename = load_filename;
126 update_cityset_panel();
127
128 if (load_filename.empty() == false)
129 {
130 load_cityset (load_filename);
131 update_cityset_panel();
132 update_window_title();
133 }
134 }
135
136 void
update_cityset_panel()137 CitySetWindow::update_cityset_panel()
138 {
139 cityset_alignment->set_sensitive(d_cityset != NULL);
140 Glib::ustring no_image = _("No image set");
141 Glib::ustring s;
142 if (d_cityset && d_cityset->getCitiesFilename().empty() == false)
143 s = d_cityset->getCitiesFilename();
144 else
145 s = no_image;
146 change_citypics_button->set_label(s);
147 if (d_cityset && d_cityset->getRazedCitiesFilename().empty() == false)
148 s = d_cityset->getRazedCitiesFilename();
149 else
150 s = no_image;
151 change_razedcitypics_button->set_label(s);
152 if (d_cityset && d_cityset->getPortFilename().empty() == false)
153 s = d_cityset->getPortFilename();
154 else
155 s = no_image;
156 change_portpic_button->set_label(s);
157 if (d_cityset && d_cityset->getSignpostFilename().empty() == false)
158 s = d_cityset->getSignpostFilename();
159 else
160 s = no_image;
161 change_signpostpic_button->set_label(s);
162 if (d_cityset && d_cityset->getRuinsFilename().empty() == false)
163 s = d_cityset->getRuinsFilename();
164 else
165 s = no_image;
166 change_ruinpics_button->set_label(s);
167 if (d_cityset && d_cityset->getTemplesFilename().empty() == false)
168 s = d_cityset->getTemplesFilename();
169 else
170 s = no_image;
171 change_templepic_button->set_label(s);
172 if (d_cityset && d_cityset->getTowersFilename().empty() == false)
173 s = d_cityset->getTowersFilename();
174 else
175 s = no_image;
176 change_towerpics_button->set_label(s);
177 if (d_cityset)
178 city_tile_width_spinbutton->set_value(d_cityset->getCityTileWidth());
179 else
180 city_tile_width_spinbutton->set_value(2);
181 if (d_cityset)
182 ruin_tile_width_spinbutton->set_value(d_cityset->getRuinTileWidth());
183 else
184 ruin_tile_width_spinbutton->set_value(1);
185 if (d_cityset)
186 temple_tile_width_spinbutton->set_value(d_cityset->getTempleTileWidth());
187 else
188 temple_tile_width_spinbutton->set_value(1);
189 }
190
make_new_cityset()191 bool CitySetWindow::make_new_cityset ()
192 {
193 Glib::ustring msg = _("Save these changes before making a new City Set?");
194 if (check_discard (msg) == false)
195 return false;
196 save_cityset_menuitem->set_sensitive (false);
197 current_save_filename = "";
198 if (d_cityset)
199 delete d_cityset;
200
201 guint32 num = 0;
202 Glib::ustring name =
203 Citysetlist::getInstance()->findFreeName(_("Untitled"), 100, num,
204 Cityset::get_default_tile_size ());
205
206 d_cityset = new Cityset (Citysetlist::getNextAvailableId (1), name);
207 d_cityset->setNewTemporaryFile ();
208
209 update_cityset_panel();
210 needs_saving = true;
211 update_window_title();
212 return true;
213 }
214
on_new_cityset_activated()215 void CitySetWindow::on_new_cityset_activated()
216 {
217 make_new_cityset ();
218 }
219
on_load_cityset_activated()220 void CitySetWindow::on_load_cityset_activated()
221 {
222 load_cityset ();
223 }
224
on_validate_cityset_activated()225 void CitySetWindow::on_validate_cityset_activated()
226 {
227 std::list<Glib::ustring> msgs;
228 if (d_cityset == NULL)
229 return;
230 if (msgs.empty () == true)
231 {
232 bool valid = String::utrim (d_cityset->getName ()) != "";
233 if (!valid)
234 {
235 Glib::ustring s = _("The name of the City Set is invalid.");
236 msgs.push_back(s);
237 }
238 }
239 if (d_cityset->validateCitiesFilename() == false)
240 msgs.push_back(_("The cities picture is not set."));
241 if (d_cityset->validateRazedCitiesFilename() == false)
242 msgs.push_back(_("The razed cities picture is not set."));
243 if (d_cityset->validatePortFilename() == false)
244 msgs.push_back(_("The port picture is not set."));
245 if (d_cityset->validateSignpostFilename() == false)
246 msgs.push_back(_("The signpost picture is not set."));
247 if (d_cityset->validateRuinsFilename() == false)
248 msgs.push_back(_("The ruins picture is not set."));
249 if (d_cityset->validateTemplesFilename() == false)
250 msgs.push_back(_("The temple picture is not set."));
251 if (d_cityset->validateTowersFilename() == false)
252 msgs.push_back(_("The towers picture is not set."));
253 if (d_cityset->validateCityTileWidth() == false)
254 msgs.push_back(_("The tile width for temples must be over zero."));
255 if (d_cityset->validateRuinTileWidth() == false)
256 msgs.push_back(_("The tile width for ruins must be over zero."));
257 if (d_cityset->validateTempleTileWidth() == false)
258 msgs.push_back(_("The tile width for temples must be over zero."));
259 if (msgs.empty() == true && isValidName () == false)
260 msgs.push_back(_("The name of the City Set is not unique."));
261
262 Glib::ustring msg = "";
263 for (std::list<Glib::ustring>::iterator it = msgs.begin(); it != msgs.end();
264 it++)
265 {
266 msg += (*it) + "\n";
267 break;
268 }
269
270 if (msg == "")
271 msg = _("The City Set is valid.");
272
273 TimedMessageDialog dialog(*window, msg, 0);
274 dialog.run_and_hide();
275
276 return;
277 }
278
on_save_as_activated()279 void CitySetWindow::on_save_as_activated()
280 {
281 if (check_save_valid (false))
282 save_current_cityset_file_as ();
283 }
284
save_current_cityset_file_as()285 bool CitySetWindow::save_current_cityset_file_as ()
286 {
287 bool ret = false;
288 while (1)
289 {
290 Gtk::FileChooserDialog chooser(*window, _("Choose a Name"),
291 Gtk::FILE_CHOOSER_ACTION_SAVE);
292 Glib::RefPtr<Gtk::FileFilter> lwc_filter = Gtk::FileFilter::create();
293 lwc_filter->set_name(_("LordsAWar City Sets (*.lwc)"));
294 lwc_filter->add_pattern("*" + CITYSET_EXT);
295 chooser.add_filter(lwc_filter);
296 chooser.set_current_folder(File::getSetDir(CITYSET_EXT, false));
297
298 chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
299 chooser.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
300 chooser.set_default_response(Gtk::RESPONSE_ACCEPT);
301 chooser.set_do_overwrite_confirmation();
302 chooser.set_current_name (File::sanify(d_cityset->getName ()) +
303 CITYSET_EXT);
304
305 chooser.show_all();
306 int res = chooser.run();
307
308 if (res == Gtk::RESPONSE_ACCEPT)
309 {
310 Glib::ustring filename = chooser.get_filename();
311 Glib::ustring old_filename = current_save_filename;
312 guint32 old_id = d_cityset->getId ();
313 d_cityset->setId(Citysetlist::getNextAvailableId(old_id));
314
315 ret = save_current_cityset_file(filename);
316 if (ret == false)
317 {
318 current_save_filename = old_filename;
319 d_cityset->setId(old_id);
320 }
321 else
322 {
323 save_cityset_menuitem->set_sensitive (true);
324 needs_saving = false;
325 d_cityset->created (filename);
326 Glib::ustring dir =
327 File::add_slash_if_necessary (File::get_dirname (filename));
328 if (dir == File::getSetDir(CITYSET_EXT, false) ||
329 dir == File::getSetDir(CITYSET_EXT, true))
330 {
331 //if we saved it to a standard place, update the list
332 Citysetlist::getInstance()->add (Cityset::copy (d_cityset),
333 filename);
334 cityset_saved.emit(d_cityset->getId());
335 }
336 update_cityset_panel();
337 update_window_title();
338 }
339 }
340 chooser.hide ();
341 if (res == Gtk::RESPONSE_CANCEL)
342 break;
343 if (ret == true)
344 break;
345 }
346 return ret;
347 }
348
save_current_cityset_file(Glib::ustring filename)349 bool CitySetWindow::save_current_cityset_file (Glib::ustring filename)
350 {
351
352 current_save_filename = filename;
353 if (current_save_filename.empty())
354 current_save_filename = d_cityset->getConfigurationFile(true);
355
356 bool ok = d_cityset->save(current_save_filename, Cityset::file_extension);
357 if (ok)
358 {
359 if (Citysetlist::getInstance()->reload(d_cityset->getId()))
360 update_cityset_panel();
361 needs_saving = false;
362 update_window_title();
363 cityset_saved.emit(d_cityset->getId());
364 }
365 else
366 {
367 Glib::ustring errmsg = Glib::strerror(errno);
368 Glib::ustring msg = _("Error! City Set could not be saved.");
369 msg += "\n" + current_save_filename + "\n" + errmsg;
370 TimedMessageDialog dialog(*window, msg, 0);
371 dialog.run_and_hide();
372 }
373 return ok;
374 }
375
on_save_cityset_activated()376 void CitySetWindow::on_save_cityset_activated()
377 {
378 if (check_save_valid (true))
379 save_current_cityset_file();
380 }
381
on_edit_cityset_info_activated()382 void CitySetWindow::on_edit_cityset_info_activated()
383 {
384 CitySetInfoDialog d(*window, d_cityset);
385 bool changed = d.run();
386 if (changed)
387 {
388 needs_saving = true;
389 update_window_title();
390 }
391 }
392
on_help_about_activated()393 void CitySetWindow::on_help_about_activated()
394 {
395 Gtk::AboutDialog* dialog;
396
397 Glib::RefPtr<Gtk::Builder> xml
398 = Gtk::Builder::create_from_file(File::getGladeFile("about-dialog.ui"));
399
400 xml->get_widget("dialog", dialog);
401 dialog->set_transient_for(*window);
402 dialog->set_icon_from_file(File::getVariousFile("castle_icon.png"));
403
404 dialog->set_version(PACKAGE_VERSION);
405 dialog->set_logo(ImageCache::loadMiscImage("castle_icon.png")->to_pixbuf());
406 dialog->show_all();
407 dialog->run();
408 delete dialog;
409
410 return;
411 }
412
load_cityset()413 bool CitySetWindow::load_cityset ()
414 {
415 bool ret = false;
416 Glib::ustring msg = _("Save these changes before opening a new City Set?");
417 if (check_discard (msg) == false)
418 return ret;
419 Gtk::FileChooserDialog chooser(*window,
420 _("Choose a City Set to Open"));
421 Glib::RefPtr<Gtk::FileFilter> lwc_filter = Gtk::FileFilter::create();
422 lwc_filter->set_name(_("LordsAWar City Sets (*.lwc)"));
423 lwc_filter->add_pattern("*" + CITYSET_EXT);
424 chooser.add_filter(lwc_filter);
425 chooser.set_current_folder(File::getSetDir(Cityset::file_extension, false));
426
427 chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
428 chooser.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
429 chooser.set_default_response(Gtk::RESPONSE_ACCEPT);
430
431 chooser.show_all();
432 int res = chooser.run();
433
434 if (res == Gtk::RESPONSE_ACCEPT)
435 {
436 bool ok = load_cityset(chooser.get_filename());
437 chooser.hide();
438 if (ok)
439 {
440 needs_saving = false;
441 update_window_title();
442 ret = true;
443 }
444 }
445
446 update_cityset_panel();
447 return ret;
448 }
449
load_cityset(Glib::ustring filename)450 bool CitySetWindow::load_cityset(Glib::ustring filename)
451 {
452 Glib::ustring old_current_save_filename = current_save_filename;
453 current_save_filename = filename;
454
455 bool unsupported_version = false;
456 Cityset *cityset = Cityset::create(filename, unsupported_version);
457 if (cityset == NULL)
458 {
459 Glib::ustring msg;
460 if (unsupported_version)
461 msg = _("Error! The version of City Set is unsupported.");
462 else
463 msg = _("Error! City Set could not be loaded.");
464 TimedMessageDialog dialog(*window, msg, 0);
465 current_save_filename = old_current_save_filename;
466 dialog.run_and_hide();
467 return false;
468 }
469 if (d_cityset)
470 delete d_cityset;
471 d_cityset = cityset;
472 d_cityset->setLoadTemporaryFile ();
473
474 bool broken = false;
475 d_cityset->instantiateImages(false, broken);
476 if (broken)
477 {
478 delete d_cityset;
479 d_cityset = NULL;
480 TimedMessageDialog td(*window, _("Couldn't load City Set images."), 0);
481 td.run_and_hide();
482 return false;
483 }
484 save_cityset_menuitem->set_sensitive (true);
485 update_window_title();
486 return true;
487 }
488
quit()489 bool CitySetWindow::quit()
490 {
491 if (needs_saving)
492 {
493 EditorQuitDialog d (*window);
494 int response = d.run_and_hide();
495
496 if (response == Gtk::RESPONSE_CANCEL) // we don't want to new
497 return false;
498
499 else if (response == Gtk::RESPONSE_ACCEPT) // save it
500 {
501 bool saved = false;
502 bool existing = d_cityset->getDirectory().empty () == false;
503 if (existing)
504 {
505 if (check_save_valid (true))
506 {
507 if (save_current_cityset_file ())
508 saved = true;
509 }
510 else
511 return false;
512 }
513 else
514 {
515 if (check_save_valid (false))
516 saved = save_current_cityset_file_as ();
517 else
518 return false;
519 }
520 if (!saved)
521 return false;
522 }
523 }
524 window->hide ();
525 if (d_cityset)
526 delete d_cityset;
527 return true;
528 }
529
on_window_closed(GdkEventAny *)530 bool CitySetWindow::on_window_closed(GdkEventAny*)
531 {
532 return !quit();
533 }
534
on_quit_activated()535 void CitySetWindow::on_quit_activated()
536 {
537 quit();
538 }
539
on_city_tile_width_text_changed()540 void CitySetWindow::on_city_tile_width_text_changed()
541 {
542 city_tile_width_spinbutton->set_value(atoi(city_tile_width_spinbutton->get_text().c_str()));
543 on_city_tile_width_changed();
544 }
545
on_city_tile_width_changed()546 void CitySetWindow::on_city_tile_width_changed()
547 {
548 if (!d_cityset)
549 return;
550 d_cityset->setCityTileWidth(city_tile_width_spinbutton->get_value());
551 needs_saving = true;
552 update_window_title();
553 }
554
on_ruin_tile_width_text_changed()555 void CitySetWindow::on_ruin_tile_width_text_changed()
556 {
557 ruin_tile_width_spinbutton->set_value(atoi(ruin_tile_width_spinbutton->get_text().c_str()));
558 on_ruin_tile_width_changed();
559 }
560
on_ruin_tile_width_changed()561 void CitySetWindow::on_ruin_tile_width_changed()
562 {
563 if (!d_cityset)
564 return;
565 d_cityset->setRuinTileWidth(ruin_tile_width_spinbutton->get_value());
566 needs_saving = true;
567 update_window_title();
568 }
569
on_temple_tile_width_text_changed()570 void CitySetWindow::on_temple_tile_width_text_changed()
571 {
572 temple_tile_width_spinbutton->set_value(atoi(temple_tile_width_spinbutton->get_text().c_str()));
573 on_temple_tile_width_changed();
574 }
575
on_temple_tile_width_changed()576 void CitySetWindow::on_temple_tile_width_changed()
577 {
578 if (!d_cityset)
579 return;
580 d_cityset->setTempleTileWidth(temple_tile_width_spinbutton->get_value());
581 needs_saving = true;
582 update_window_title();
583 }
584
on_change_citypics_clicked()585 void CitySetWindow::on_change_citypics_clicked()
586 {
587 bool cleared = false;
588 std::vector<PixMask *> frames;
589 for (guint32 i = 0; i < MAX_PLAYERS + 1; i++)
590 if (d_cityset->getCityImage (i))
591 frames.push_back (d_cityset->getCityImage (i));
592 Glib::ustring imgname = d_cityset->getCitiesFilename();
593 Glib::ustring f =
594 change_image(_("Select a Cities image"), imgname, MAX_PLAYERS + 1,
595 frames, cleared, d_cityset->getCityTileWidth ());
596 if (cleared)
597 d_cityset->uninstantiateSameNamedImages (imgname);
598 else
599 {
600 if (f != "")
601 {
602 d_cityset->setCitiesFilename (f);
603 d_cityset->instantiateCityImages();
604 }
605 }
606 update_cityset_panel();
607 }
608
on_change_razedcitypics_clicked()609 void CitySetWindow::on_change_razedcitypics_clicked()
610 {
611 bool cleared = false;
612 std::vector<PixMask *> frames;
613 for (guint32 i = 0; i < MAX_PLAYERS; i++)
614 if (d_cityset->getRazedCityImage (i))
615 frames.push_back (d_cityset->getRazedCityImage (i));
616 Glib::ustring imgname = d_cityset->getRazedCitiesFilename();
617 Glib::ustring f =
618 change_image(_("Select a Razed Cities image"), imgname, MAX_PLAYERS,
619 frames, cleared, d_cityset->getCityTileWidth ());
620 if (cleared)
621 d_cityset->uninstantiateSameNamedImages (imgname);
622 else
623 {
624 if (f != "")
625 {
626 d_cityset->setRazedCitiesFilename (f);
627 d_cityset->instantiateRazedCityImages();
628 }
629 }
630 update_cityset_panel();
631 }
632
on_change_portpic_clicked()633 void CitySetWindow::on_change_portpic_clicked()
634 {
635 bool cleared = false;
636 std::vector<PixMask *> frames;
637 if (d_cityset->getPortImage ())
638 frames.push_back (d_cityset->getPortImage ());
639 Glib::ustring imgname = d_cityset->getPortFilename();
640 Glib::ustring f = change_image(_("Select a Port image"), imgname, 1, frames,
641 cleared, 1);
642 if (cleared)
643 d_cityset->uninstantiateSameNamedImages (imgname);
644 else
645 {
646 if (f != "")
647 {
648 d_cityset->setPortFilename (f);
649 d_cityset->instantiatePortImage();
650 }
651 }
652 update_cityset_panel();
653 }
654
on_change_signpostpic_clicked()655 void CitySetWindow::on_change_signpostpic_clicked()
656 {
657 bool cleared = false;
658 std::vector<PixMask *> frames;
659 if (d_cityset->getSignpostImage ())
660 frames.push_back (d_cityset->getSignpostImage ());
661 Glib::ustring imgname = d_cityset->getSignpostFilename();
662 Glib::ustring f =
663 change_image(_("Select a Signpost image"), imgname, 1, frames, cleared, 1);
664 if (cleared)
665 d_cityset->uninstantiateSameNamedImages (imgname);
666 else
667 {
668 if (f != "")
669 {
670 d_cityset->setSignpostFilename (f);
671 d_cityset->instantiateSignpostImage();
672 }
673 }
674 update_cityset_panel();
675 }
676
on_change_ruinpics_clicked()677 void CitySetWindow::on_change_ruinpics_clicked()
678 {
679 bool cleared = false;
680 std::vector<PixMask *> frames;
681 for (guint32 i = 0; i < RUIN_TYPES; i++)
682 if (d_cityset->getRuinImage (i))
683 frames.push_back (d_cityset->getRuinImage (i));
684 Glib::ustring imgname = d_cityset->getRuinsFilename();
685 Glib::ustring f =
686 change_image(_("Select a Ruins image"), imgname, RUIN_TYPES, frames,
687 cleared, d_cityset->getRuinTileWidth ());
688 if (cleared)
689 d_cityset->uninstantiateSameNamedImages (imgname);
690 else
691 {
692 if (f != "")
693 {
694 d_cityset->setRuinsFilename (f);
695 d_cityset->instantiateRuinImages();
696 }
697 }
698 update_cityset_panel();
699 }
700
on_change_templepic_clicked()701 void CitySetWindow::on_change_templepic_clicked()
702 {
703 bool cleared = false;
704 std::vector<PixMask *> frames;
705 for (guint32 i = 0; i < TEMPLE_TYPES; i++)
706 if (d_cityset->getTempleImage (i))
707 frames.push_back (d_cityset->getTempleImage (i));
708 Glib::ustring imgname = d_cityset->getTemplesFilename();
709 Glib::ustring f =
710 change_image(_("Select a Temples image"), imgname, TEMPLE_TYPES, frames,
711 cleared, d_cityset->getTempleTileWidth ());
712 if (cleared)
713 d_cityset->uninstantiateSameNamedImages (imgname);
714 else
715 {
716 if (f != "")
717 {
718 d_cityset->setTemplesFilename (f);
719 d_cityset->instantiateTempleImages();
720 }
721 }
722 update_cityset_panel();
723 }
724
on_change_towerpics_clicked()725 void CitySetWindow::on_change_towerpics_clicked()
726 {
727 bool cleared = false;
728 std::vector<PixMask *> frames;
729 for (guint32 i = 0; i < MAX_PLAYERS; i++)
730 if (d_cityset->getTowerImage (i))
731 frames.push_back (d_cityset->getTowerImage (i));
732 Glib::ustring imgname = d_cityset->getTowersFilename();
733 Glib::ustring f =
734 change_image(_("Select a Towers image"), imgname, MAX_PLAYERS, frames,
735 cleared, 1);
736 if (cleared)
737 d_cityset->uninstantiateSameNamedImages (imgname);
738 else
739 {
740 if (f != "")
741 {
742 d_cityset->setTowersFilename (f);
743 d_cityset->instantiateTowerImages();
744 }
745 }
746 update_cityset_panel();
747 }
748
change_image(Glib::ustring msg,Glib::ustring imgname,int num,std::vector<PixMask * > frames,bool & cleared,int tw)749 Glib::ustring CitySetWindow::change_image(Glib::ustring msg, Glib::ustring imgname, int num, std::vector<PixMask *> frames, bool &cleared, int tw)
750 {
751 Glib::ustring newfile = "";
752
753 ImageEditorDialog d(*window, imgname, num, frames,
754 EDITOR_DIALOG_TILE_PIC_FONTSIZE_MULTIPLE * (double)tw);
755 d.set_title(msg);
756 int response = d.run();
757 if (response == Gtk::RESPONSE_ACCEPT && d.get_filename() != "")
758 {
759 Glib::ustring newname = "";
760 bool success = false;
761 if (imgname.empty () == true)
762 success = d_cityset->addFileInCfgFile(d.get_filename(), newname);
763 else
764 success =
765 d_cityset->replaceFileInCfgFile(imgname, d.get_filename(), newname);
766 if (success)
767 {
768 newfile = newname;
769 needs_saving = true;
770 update_window_title();
771 }
772 else
773 show_add_file_error(*d.get_dialog(), d.get_filename ());
774 }
775 else if (response == Gtk::RESPONSE_REJECT)
776 {
777 if (d_cityset->removeFileInCfgFile(imgname))
778 {
779 needs_saving = true;
780 update_window_title();
781 cleared = true;
782 newfile = "";
783 }
784 else
785 show_remove_file_error(*d.get_dialog(), imgname);
786 }
787 return newfile;
788 }
789
update_window_title()790 void CitySetWindow::update_window_title()
791 {
792 Glib::ustring title = "";
793 if (needs_saving)
794 title += "*";
795 title += d_cityset->getName();
796 title += " - ";
797 title += _("City Set Editor");
798 window->set_title(title);
799 }
800
show_add_file_error(Gtk::Dialog & d,Glib::ustring file)801 void CitySetWindow::show_add_file_error(Gtk::Dialog &d, Glib::ustring file)
802 {
803 Glib::ustring errmsg = Glib::strerror(errno);
804 Glib::ustring m =
805 String::ucompose(_("Couldn't add %1 to:\n%2\n%3"),
806 file, d_cityset->getConfigurationFile(), errmsg);
807 TimedMessageDialog td(d, m, 0);
808 td.run_and_hide();
809 }
810
show_remove_file_error(Gtk::Dialog & d,Glib::ustring file)811 void CitySetWindow::show_remove_file_error(Gtk::Dialog &d, Glib::ustring file)
812 {
813 Glib::ustring errmsg = Glib::strerror(errno);
814 Glib::ustring m =
815 String::ucompose(_("Couldn't remove %1 from:\n%2\n%3"),
816 file, d_cityset->getConfigurationFile(), errmsg);
817 TimedMessageDialog td(d, m, 0);
818 td.run_and_hide();
819 }
820
~CitySetWindow()821 CitySetWindow::~CitySetWindow()
822 {
823 notebook->property_show_tabs () = false;
824 delete window;
825 }
826
on_tutorial_video_activated()827 void CitySetWindow::on_tutorial_video_activated()
828 {
829 GError *errs = NULL;
830 gtk_show_uri(window->get_screen()->gobj(),
831 "http://vimeo.com/406899445", 0, &errs);
832 }
833
check_discard(Glib::ustring msg)834 bool CitySetWindow::check_discard (Glib::ustring msg)
835 {
836 if (needs_saving)
837 {
838 EditorSaveChangesDialog d (*window, msg);
839 int response = d.run_and_hide();
840
841 if (response == Gtk::RESPONSE_CANCEL) // we don't want to new
842 return false;
843
844 else if (response == Gtk::RESPONSE_ACCEPT) // save it
845 {
846 if (check_save_valid (true))
847 {
848 bool saved = false;
849 if (d_cityset->getDirectory ().empty () == false)
850 saved = save_current_cityset_file_as ();
851 else
852 {
853 if (save_current_cityset_file ())
854 saved = true;
855 }
856 if (!saved)
857 return false;
858 }
859 else
860 return false;
861 }
862 }
863 return true;
864 }
865
check_save_valid(bool existing)866 bool CitySetWindow::check_save_valid (bool existing)
867 {
868 if (check_name_valid (existing) == false)
869 return false;
870
871 if (d_cityset->validate () == false)
872 {
873 if (existing &&
874 GameMap::getInstance()->getCitysetId() == d_cityset->getId())
875 {
876 Glib::ustring errmsg =
877 _("City Set is invalid, and is also the current working City Set.");
878 Glib::ustring msg = _("Error! City Set could not be saved.");
879 msg += "\n" + current_save_filename + "\n" + errmsg;
880 TimedMessageDialog dialog(*window, msg, 0);
881 dialog.run_and_hide();
882 return false;
883 }
884 else
885 {
886 TimedMessageDialog
887 dialog(*window,
888 _("The City Set is invalid. Do you want to proceed?"), 0);
889 dialog.add_cancel_button ();
890 dialog.run_and_hide ();
891 if (dialog.get_response () == Gtk::RESPONSE_CANCEL)
892 return false;
893 }
894 }
895 return true;
896 }
897
check_name_valid(bool existing)898 bool CitySetWindow::check_name_valid (bool existing)
899 {
900 Glib::ustring name = d_cityset->getName ();
901 Glib::ustring newname = "";
902 if (existing)
903 {
904 Cityset *oldcityset =
905 Citysetlist::getInstance ()->get(d_cityset->getId());
906 if (oldcityset && oldcityset->getName () != name)
907 newname = oldcityset->getName ();
908 }
909 guint32 num = 0;
910 Glib::ustring n = String::utrim (String::strip_trailing_numbers (name));
911 if (n == "")
912 n = _("Untitled");
913 if (newname.empty () == true)
914 newname =
915 Citysetlist::getInstance()->findFreeName(n, 100, num,
916 d_cityset->getTileSize ());
917 if (name == "")
918 {
919 if (newname.empty() == true)
920 {
921 Glib::ustring msg =
922 _("The City Set has an invalid name.\nChange it and save again.");
923 TimedMessageDialog d(*window, msg, 0);
924 d.run_and_hide();
925 on_edit_cityset_info_activated ();
926 return false;
927 }
928 else
929 {
930 Glib::ustring msg =
931 String::ucompose (_("The City Set has an invalid name.\nChange it to '%1'?"), newname);
932 TimedMessageDialog d(*window, msg, 0);
933 d.add_cancel_button ();
934 d.run_and_hide ();
935 if (d.get_response () == Gtk::RESPONSE_CANCEL)
936 return false;
937 d_cityset->setName (newname);
938 }
939 }
940
941 //okay the question is whether or not the name is already used.
942 bool same_name = false;
943 Glib::ustring file =
944 Citysetlist::getInstance()->lookupConfigurationFileByName(d_cityset);
945 if (file == "")
946 return true;
947
948 Glib::ustring cfgfile = d_cityset->getConfigurationFile(true);
949
950 if (existing) // this means we're doing File->Save
951 {
952 if (file == cfgfile)
953 return true;
954 same_name = true;
955 }
956 else // this means we're doing File->Save As
957 same_name = true;
958
959 if (same_name)
960 {
961 if (newname.empty() == true)
962 {
963 Glib::ustring msg =
964 _("The City Set has the same name as another one.\nChange it and save again.");
965 TimedMessageDialog d(*window, msg, 0);
966 d.run_and_hide();
967 on_edit_cityset_info_activated ();
968 return false;
969 }
970 else
971 {
972 Glib::ustring msg =
973 String::ucompose (_("The City Set has the same name as another one.\nChange it to '%1' instead?."), newname);
974
975 TimedMessageDialog d(*window, msg, 0);
976 d.add_cancel_button ();
977 d.run_and_hide ();
978 if (d.get_response () == Gtk::RESPONSE_CANCEL)
979 return false;
980 d_cityset->setName (newname);
981 }
982 }
983
984 return true;
985 }
986
isValidName()987 bool CitySetWindow::isValidName ()
988 {
989 Glib::ustring file =
990 Citysetlist::getInstance()->lookupConfigurationFileByName(d_cityset);
991 if (file == "")
992 return true;
993 if (file == d_cityset->getConfigurationFile (true))
994 return true;
995 return false;
996 }
997
998 /*
999 some test cases
1000 1. create a new cityset from scratch, save invalid set, close, load it
1001 2. create a new cityset from scratch, save valid set, then switch sets
1002 3. save a copy of the default cityset, and switch sets
1003 4. modify the working cityset so we can see it change in scenario builder
1004 5. modify the working cityset so that it's invalid, try to save
1005 6. try adding an image file that isn't a .png
1006 7. try adding an image file that says it's a .png but is actually a .jpg
1007 8. try adding an image file that says it's a .png but is actually random data
1008 9. try saving a new cityset that has a same name
1009 10. try saving an existing cityset that has a same name
1010 11. validate a cityset without: the port picture
1011 12. validate a cityset without: the towers picture
1012 13. try saving a new cityset that has an empty name
1013 14. validate a cityset with a same name
1014 15. validate a cityset with an empty name
1015 16. make a new invalid cityset and quit save it
1016 17. load a writable cityset, modify and quit save it
1017 18. load a writable cityset, make it invalid, and then quit save it
1018 19. try saving a cityset we don't have permission to save
1019 20. try quit-saving a cityset we don't have permission to save
1020 */
1021
1022
1023