initial commit, lordsawar source, slightly modified
[lordsawar] / src / editor / tileset-window.cpp
1 //  Copyright (C) 2008, 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 <algorithm>
21 #include <iostream>
22 #include <iomanip>
23 #include <assert.h>
24 #include <libgen.h>
25 #include <string.h>
26
27 #include <sigc++/functors/mem_fun.h>
28 #include <sigc++/functors/ptr_fun.h>
29 #include "ucompose.hpp"
30
31 #include <gtkmm.h>
32 #include "tileset-window.h"
33 #include "tileset-info-dialog.h"
34 #include "tile-preview-dialog.h"
35 #include "tileset-selector-editor-dialog.h"
36 #include "tileset-flag-editor-dialog.h"
37 #include "tileset-explosion-picture-editor-dialog.h"
38 #include "image-editor-dialog.h"
39
40 #include "image-helpers.h"
41 #include "input-helpers.h"
42 #include "error-utils.h"
43
44 #include "defs.h"
45 #include "Configuration.h"
46 #include "tilesetlist.h"
47 #include "Tile.h"
48 #include "File.h"
49 #include "overviewmap.h"
50 #include "GraphicsCache.h"
51
52 #include "ucompose.hpp"
53
54 #include "glade-helpers.h"
55
56
57 TileSetWindow::TileSetWindow(std::string load_filename)
58 {
59   needs_saving = false;
60   d_tileset = NULL;
61     Glib::RefPtr<Gtk::Builder> xml
62         = Gtk::Builder::create_from_file(get_glade_path() + "/tileset-window.ui");
63
64     xml->get_widget("window", window);
65     window->set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
66     window->signal_delete_event().connect
67       (sigc::mem_fun(*this, &TileSetWindow::on_window_closed));
68
69     xml->get_widget("tiles_treeview", tiles_treeview);
70     xml->get_widget("tile_name_entry", tile_name_entry);
71     tile_name_entry->signal_changed().connect
72       (sigc::mem_fun(this, &TileSetWindow::on_tile_name_changed));
73
74     Gtk::HBox *type_combo_container;
75     xml->get_widget("type_combo_container", type_combo_container);
76     tile_type_combobox = new Gtk::ComboBoxText();
77     tile_type_combobox->append_text(_("Grass"));
78     tile_type_combobox->append_text(_("Water"));
79     tile_type_combobox->append_text(_("Forest"));
80     tile_type_combobox->append_text(_("Hills"));
81     tile_type_combobox->append_text(_("Mountains"));
82     tile_type_combobox->append_text(_("Swamp"));
83     tile_type_combobox->append_text(_("Void"));
84     type_combo_container->add(*manage(tile_type_combobox));
85     type_combo_container->show_all();
86     tile_type_combobox->signal_changed().connect
87       (sigc::mem_fun(this, &TileSetWindow::on_tile_type_changed));
88
89     Gtk::HBox *tilestyle_combo_container;
90     xml->get_widget("tilestyle_combo_container", tilestyle_combo_container);
91     tilestyle_combobox = new Gtk::ComboBoxText();
92     tilestyle_combobox->append_text(_("Lone"));
93     tilestyle_combobox->append_text(_("Outer Top-Left"));
94     tilestyle_combobox->append_text(_("Outer Top-Center"));
95     tilestyle_combobox->append_text(_("Outer Top-Right"));
96     tilestyle_combobox->append_text(_("Outer Bottom-Left"));
97     tilestyle_combobox->append_text(_("Outer Bottom-Center"));
98     tilestyle_combobox->append_text(_("Outer Bottom-Right"));
99     tilestyle_combobox->append_text(_("Outer Middle-Left"));
100     tilestyle_combobox->append_text(_("Inner Middle-Center"));
101     tilestyle_combobox->append_text(_("Outer Middle-Right"));
102     tilestyle_combobox->append_text(_("Inner Top-Left"));
103     tilestyle_combobox->append_text(_("Inner Top-Right"));
104     tilestyle_combobox->append_text(_("Inner Bottom-Left"));
105     tilestyle_combobox->append_text(_("Inner Bottom-Right"));
106     tilestyle_combobox->append_text(_("Top-Left To Bottom-Right"));
107     tilestyle_combobox->append_text(_("Bottom-Left To Top-Right"));
108     tilestyle_combobox->append_text(_("Other"));
109     tilestyle_combo_container->add(*manage(tilestyle_combobox));
110     tilestyle_combo_container->show_all();
111     tilestyle_combobox->signal_changed().connect
112       (sigc::mem_fun(this, &TileSetWindow::on_tilestyle_changed));
113
114     Gtk::HBox *pattern_container;
115     xml->get_widget("pattern_container", pattern_container);
116     tile_smallmap_pattern_combobox = new Gtk::ComboBoxText();
117     tile_smallmap_pattern_combobox->append_text(_("Solid"));
118     tile_smallmap_pattern_combobox->append_text(_("Stippled"));
119     tile_smallmap_pattern_combobox->append_text(_("Randomized"));
120     tile_smallmap_pattern_combobox->append_text(_("Sunken"));
121     tile_smallmap_pattern_combobox->append_text(_("Tablecloth"));
122     tile_smallmap_pattern_combobox->append_text(_("Diagonal"));
123     tile_smallmap_pattern_combobox->append_text(_("Crosshatched"));
124     tile_smallmap_pattern_combobox->append_text(_("Sunken Striped"));
125     pattern_container->add(*manage(tile_smallmap_pattern_combobox));
126     pattern_container->show_all();
127     tile_smallmap_pattern_combobox->signal_changed().connect
128       (sigc::mem_fun(this, &TileSetWindow::on_tile_pattern_changed));
129
130     xml->get_widget("tile_moves_spinbutton", tile_moves_spinbutton);
131     xml->get_widget("tile_smallmap_first_colorbutton", 
132                     tile_smallmap_first_colorbutton);
133     tile_smallmap_first_colorbutton->signal_color_set().connect
134       (sigc::mem_fun(this, &TileSetWindow::on_tile_first_color_changed));
135     xml->get_widget("tile_smallmap_second_colorbutton", 
136                     tile_smallmap_second_colorbutton);
137     tile_smallmap_second_colorbutton->signal_color_set().connect
138       (sigc::mem_fun(this, &TileSetWindow::on_tile_second_color_changed));
139     xml->get_widget("tile_smallmap_third_colorbutton", 
140                     tile_smallmap_third_colorbutton);
141     tile_smallmap_third_colorbutton->signal_color_set().connect
142       (sigc::mem_fun(this, &TileSetWindow::on_tile_third_color_changed));
143     xml->get_widget("tile_smallmap_image", tile_smallmap_image);
144
145     xml->get_widget("add_tile_button", add_tile_button);
146     add_tile_button->signal_clicked().connect
147       (sigc::mem_fun(this, &TileSetWindow::on_add_tile_clicked));
148     xml->get_widget("remove_tile_button", remove_tile_button);
149     remove_tile_button->signal_clicked().connect
150       (sigc::mem_fun(this, &TileSetWindow::on_remove_tile_clicked));
151     xml->get_widget("tile_vbox", tile_vbox);
152     // connect callbacks for the menu
153     xml->get_widget("new_tileset_menuitem", new_tileset_menuitem);
154     new_tileset_menuitem->signal_activate().connect
155       (sigc::mem_fun(this, &TileSetWindow::on_new_tileset_activated));
156     xml->get_widget("load_tileset_menuitem", load_tileset_menuitem);
157     load_tileset_menuitem->signal_activate().connect
158       (sigc::mem_fun(this, &TileSetWindow::on_load_tileset_activated));
159     xml->get_widget("save_tileset_menuitem", save_tileset_menuitem);
160     save_tileset_menuitem->signal_activate().connect
161       (sigc::mem_fun(this, &TileSetWindow::on_save_tileset_activated));
162     xml->get_widget("quit_menuitem", quit_menuitem);
163     quit_menuitem->signal_activate().connect
164        (sigc::mem_fun(this, &TileSetWindow::on_quit_activated));
165     xml->get_widget("edit_tileset_info_menuitem", edit_tileset_info_menuitem);
166     edit_tileset_info_menuitem->signal_activate().connect
167       (sigc::mem_fun(this, &TileSetWindow::on_edit_tileset_info_activated));
168     xml->get_widget("army_unit_selector_menuitem", army_unit_selector_menuitem);
169     army_unit_selector_menuitem->signal_activate().connect
170       (sigc::mem_fun(this, &TileSetWindow::on_army_unit_selector_activated));
171     xml->get_widget("roads_picture_menuitem", roads_picture_menuitem);
172     roads_picture_menuitem->signal_activate().connect
173       (sigc::mem_fun(this, &TileSetWindow::on_roads_picture_activated));
174     xml->get_widget("bridges_picture_menuitem", bridges_picture_menuitem);
175     bridges_picture_menuitem->signal_activate().connect
176       (sigc::mem_fun(this, &TileSetWindow::on_bridges_picture_activated));
177     xml->get_widget("fog_picture_menuitem", fog_picture_menuitem);
178     fog_picture_menuitem->signal_activate().connect
179       (sigc::mem_fun(this, &TileSetWindow::on_fog_picture_activated));
180     xml->get_widget("flags_picture_menuitem", flags_picture_menuitem);
181     flags_picture_menuitem->signal_activate().connect
182       (sigc::mem_fun(this, &TileSetWindow::on_flags_picture_activated));
183
184     xml->get_widget("explosion_picture_menuitem", explosion_picture_menuitem);
185     explosion_picture_menuitem->signal_activate().connect
186       (sigc::mem_fun(this, &TileSetWindow::on_explosion_picture_activated));
187     xml->get_widget("preview_tile_menuitem", preview_tile_menuitem);
188     preview_tile_menuitem->signal_activate().connect
189       (sigc::mem_fun(this, &TileSetWindow::on_preview_tile_activated));
190     xml->get_widget ("help_about_menuitem", help_about_menuitem);
191     help_about_menuitem->signal_activate().connect
192        (sigc::mem_fun(this, &TileSetWindow::on_help_about_activated));
193     xml->get_widget("tilestyle_image", tilestyle_image);
194
195     window->signal_delete_event().connect
196       (sigc::mem_fun(*this, &TileSetWindow::on_delete_event));
197
198     tiles_list = Gtk::ListStore::create(tiles_columns);
199     tiles_treeview->set_model(tiles_list);
200     tiles_treeview->append_column("", tiles_columns.name);
201     tiles_treeview->set_headers_visible(false);
202     tiles_treeview->get_selection()->signal_changed().connect
203       (sigc::mem_fun(*this, &TileSetWindow::on_tile_selected));
204
205     xml->get_widget("tilestylesets_treeview", tilestylesets_treeview);
206     tilestylesets_list = Gtk::ListStore::create(tilestylesets_columns);
207     tilestylesets_treeview->set_model(tilestylesets_list);
208     tilestylesets_treeview->append_column("", tilestylesets_columns.name);
209     tilestylesets_treeview->set_headers_visible(false);
210     tilestylesets_treeview->get_selection()->signal_changed().connect
211       (sigc::mem_fun(*this, &TileSetWindow::on_tilestyleset_selected));
212
213     xml->get_widget("tilestyles_treeview", tilestyles_treeview);
214     tilestyles_list = Gtk::ListStore::create(tilestyles_columns);
215     tilestyles_treeview->set_model(tilestyles_list);
216     tilestyles_treeview->append_column("", tilestyles_columns.name);
217     tilestyles_treeview->set_headers_visible(false);
218     tilestyles_treeview->get_selection()->signal_changed().connect
219       (sigc::mem_fun(*this, &TileSetWindow::on_tilestyle_selected));
220
221     xml->get_widget("add_tilestyleset_button", add_tilestyleset_button);
222     add_tilestyleset_button->signal_clicked().connect
223       (sigc::mem_fun(this, &TileSetWindow::on_add_tilestyleset_clicked));
224     xml->get_widget("remove_tilestyleset_button", remove_tilestyleset_button);
225     remove_tilestyleset_button->signal_clicked().connect
226       (sigc::mem_fun(this, &TileSetWindow::on_remove_tilestyleset_clicked));
227     xml->get_widget("tilestyleset_frame", tilestyleset_frame);
228     xml->get_widget("tilestyle_frame", tilestyle_frame);
229     xml->get_widget("image_filechooser_button", image_filechooser_button);
230     image_filechooser_button->signal_selection_changed().connect
231       (sigc::mem_fun(*this, &TileSetWindow::on_image_chosen));
232     xml->get_widget("refresh_button", refresh_button);
233     refresh_button->signal_clicked().connect
234       (sigc::mem_fun(this, &TileSetWindow::on_refresh_clicked));
235
236     Gtk::FileFilter sav_filter;
237     sav_filter.add_pattern("*.png");
238     image_filechooser_button->set_filter(sav_filter);
239     image_filechooser_button->set_current_folder (File::getUserTilesetDir());
240
241     xml->get_widget("tilestyle_standard_image", tilestyle_standard_image);
242     tilestyle_standard_images = 
243       disassemble_row(File::getMiscFile("various/editor/tilestyles.png"), 17);
244
245     update_tile_panel();
246     update_tilestyleset_panel();
247     update_tilestyle_panel();
248     update_tileset_buttons();
249     update_tilestyleset_buttons();
250
251     update_tileset_buttons();
252     update_tilestyleset_buttons();
253     update_tileset_menuitems();
254     update_tile_preview_menuitem();
255
256     tile_smallmap_surface = Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(0), 32, 32, 24);
257     tile_smallmap_surface_gc = Gdk::GC::create(tile_smallmap_surface);
258
259     inhibit_image_change = false;
260     if (load_filename.empty() == false)
261       load_tileset(load_filename);
262 }
263
264 void
265 TileSetWindow::update_tileset_menuitems()
266 {
267   if (d_tileset == NULL)
268     {
269       save_tileset_menuitem->set_sensitive(false);
270       army_unit_selector_menuitem->set_sensitive(false);
271       edit_tileset_info_menuitem->set_sensitive(false);
272       explosion_picture_menuitem->set_sensitive(false);
273       roads_picture_menuitem->set_sensitive(false);
274       bridges_picture_menuitem->set_sensitive(false);
275       fog_picture_menuitem->set_sensitive(false);
276       flags_picture_menuitem->set_sensitive(false);
277     }
278   else
279     {
280       save_tileset_menuitem->set_sensitive(true);
281       army_unit_selector_menuitem->set_sensitive(true);
282       edit_tileset_info_menuitem->set_sensitive(true);
283       explosion_picture_menuitem->set_sensitive(true);
284       roads_picture_menuitem->set_sensitive(true);
285       bridges_picture_menuitem->set_sensitive(true);
286       fog_picture_menuitem->set_sensitive(true);
287       flags_picture_menuitem->set_sensitive(true);
288     }
289 }
290
291 void
292 TileSetWindow::update_tileset_buttons()
293 {
294   if (!tiles_treeview->get_selection()->get_selected())
295     remove_tile_button->set_sensitive(false);
296   else
297     remove_tile_button->set_sensitive(true);
298   if (d_tileset == NULL)
299     add_tile_button->set_sensitive(false);
300   else
301     add_tile_button->set_sensitive(true);
302 }
303
304 void
305 TileSetWindow::update_tilestyleset_buttons()
306 {
307   if (!tilestylesets_treeview->get_selection()->get_selected())
308     remove_tilestyleset_button->set_sensitive(false);
309   else
310     remove_tilestyleset_button->set_sensitive(true);
311   if (d_tileset == NULL)
312     add_tilestyleset_button->set_sensitive(false);
313   else
314     add_tilestyleset_button->set_sensitive(true);
315 }
316
317 void
318 TileSetWindow::update_tilestyle_panel()
319 {
320   if (tilestyles_treeview->get_selection()->get_selected() == 0)
321     {
322       //clear all values
323       tilestyle_frame->set_sensitive(false);
324       tilestyle_combobox->set_active(0);
325       tilestyle_image->clear();
326       tilestyle_standard_image->clear();
327       tilestyle_image->show_all();
328           
329       return;
330     }
331   tilestyle_frame->set_sensitive(true);
332   TileStyle *t = get_selected_tilestyle ();
333   if (t)
334     {
335       tilestyle_combobox->set_active(t->getType());
336       Glib::RefPtr<Gdk::Pixbuf> pixbuf= t->getImage()->to_pixbuf();
337       tilestyle_image->clear();
338       tilestyle_image->property_pixbuf() = pixbuf;
339       tilestyle_image->show_all();
340       int idx = t->getType();
341       tilestyle_standard_image->property_pixbuf() = 
342         tilestyle_standard_images[idx]->to_pixbuf();
343     }
344 }
345
346 int get_image_height(std::string filename)
347 {
348   Glib::RefPtr<Gdk::Pixbuf> row = Gdk::Pixbuf::create_from_file(filename);
349   if (row)
350     return row->get_height();
351   else
352     return 0;
353 }
354
355 int get_image_width (std::string filename)
356 {
357   Glib::RefPtr<Gdk::Pixbuf> row = Gdk::Pixbuf::create_from_file(filename);
358   if (row)
359     return row->get_width();
360   else
361     return 0;
362 }
363
364 void TileSetWindow::fill_tilestyleset_info(TileStyleSet *t)
365 {
366   std::string subdir;
367   if (t->getSubDir() == "")
368     subdir = d_tileset->getSubDir();
369   else
370     subdir = t->getSubDir();
371   std::string n = d_tileset->getFile(t->getName());
372   int height = get_image_height (n);
373   if (height)
374     {
375       d_tileset->setTileSize(height);
376       TileStyleSet *set = get_selected_tilestyleset ();
377       set->instantiateImages(d_tileset->getTileSize(), 
378                              d_tileset->getFile(set->getName()));
379     }
380   inhibit_image_change = true;
381   image_filechooser_button->set_filename(n);
382   refresh_button->set_sensitive(true);
383   //add the tilestyles to the tilestyles_treeview
384   tilestyles_list->clear();
385   for (unsigned int i = 0; i < t->size(); i++)
386     {
387       Gtk::TreeIter l = tilestyles_list->append();
388       (*l)[tilestyles_columns.name] = 
389         String::ucompose("0x%1", Glib::ustring::format(std::hex, std::setfill(L'0'), std::setw(2), (*t)[i]->getId()));
390       (*l)[tilestyles_columns.tilestyle] = (*t)[i];
391     }
392 }
393
394 void
395 TileSetWindow::update_tilestyleset_panel()
396 {
397   if (tilestylesets_treeview->get_selection()->get_selected() == 0)
398     {
399       //clear all values
400       tilestyleset_frame->set_sensitive(false);
401       if (d_tileset)
402         image_filechooser_button->set_current_folder(d_tileset->getDirectory());
403       else
404         image_filechooser_button->set_current_folder(File::getUserTilesetDir());
405       tilestyles_list->clear();
406       refresh_button->set_sensitive(false);
407       return;
408     }
409   tilestyleset_frame->set_sensitive(true);
410   TileStyleSet *t = get_selected_tilestyleset ();
411   if (t && t->getName() != "")
412     {
413       fill_tilestyleset_info(t);
414     }
415 }
416
417 void
418 TileSetWindow::update_tile_panel()
419 {
420   Gdk::Color black("black");
421   //if nothing selected in the treeview, then we don't show anything in
422   //the tile panel
423   if (tiles_treeview->get_selection()->get_selected() == 0)
424     {
425       //clear all values
426       tile_smallmap_image->clear();
427       tile_vbox->set_sensitive(false);
428       tile_type_combobox->set_active(0);
429       tile_moves_spinbutton->set_value(0);
430       tile_name_entry->set_text("");
431       tile_smallmap_pattern_combobox->set_active(0);
432       tile_smallmap_first_colorbutton->set_color(black);
433       tile_smallmap_second_colorbutton->set_color(black);
434       tile_smallmap_third_colorbutton->set_color(black);
435       tilestylesets_list->clear();
436       return;
437     }
438   tile_vbox->set_sensitive(true);
439   Tile *t = get_selected_tile ();
440
441   if (t)
442     {
443       tile_smallmap_first_colorbutton->set_color(black);
444       tile_smallmap_second_colorbutton->set_color(black);
445       tile_smallmap_third_colorbutton->set_color(black);
446       fill_tile_info(t);
447     }
448 }
449 TileSetWindow::~TileSetWindow()
450 {
451   tile_smallmap_surface.reset();
452   delete window;
453 }
454
455 void TileSetWindow::show()
456 {
457   window->show();
458 }
459
460 void TileSetWindow::hide()
461 {
462   window->hide();
463 }
464
465 bool TileSetWindow::on_delete_event(GdkEventAny *e)
466 {
467   hide();
468
469   return true;
470 }
471
472
473 void TileSetWindow::on_new_tileset_activated()
474 {
475   std::string name = "";
476   int id = Tilesetlist::getNextAvailableId(0);
477   Tileset *tileset = new Tileset(id, name);
478   TileSetInfoDialog d(tileset, false);
479   d.set_parent_window(*window);
480   int response = d.run();
481   if (response != Gtk::RESPONSE_ACCEPT)
482     {
483       delete tileset;
484       return;
485     }
486   if (d_tileset)
487     delete d_tileset;
488   d_tileset = tileset;
489   tiles_list->clear();
490
491   std::string dir = File::getUserTilesetDir() + d_tileset->getSubDir();
492   d_tileset->setDirectory(dir);
493   File::create_dir(dir);
494   current_save_filename = d_tileset->getConfigurationFile();
495
496   //copy images??
497   update_tileset_buttons();
498   update_tilestyleset_buttons();
499   update_tileset_menuitems();
500   update_tile_preview_menuitem();
501
502   XML_Helper helper(current_save_filename, std::ios::out, false);
503   d_tileset->save(&helper);
504   helper.close();
505   needs_saving = true;
506 }
507
508 void TileSetWindow::on_load_tileset_activated()
509 {
510   Gtk::FileChooserDialog chooser(*window, 
511                                  _("Choose a Tileset to Load"));
512   Gtk::FileFilter sav_filter;
513   sav_filter.add_pattern("*" + TILESET_EXT);
514   chooser.set_filter(sav_filter);
515   chooser.set_current_folder(File::getUserTilesetDir());
516
517   chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
518   chooser.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
519   chooser.set_default_response(Gtk::RESPONSE_ACCEPT);
520       
521   chooser.show_all();
522   int res = chooser.run();
523
524   if (res == Gtk::RESPONSE_ACCEPT)
525     {
526       load_tileset(chooser.get_filename());
527       chooser.hide();
528     }
529 }
530
531 void TileSetWindow::on_save_tileset_activated()
532 {
533   if (current_save_filename.empty())
534     current_save_filename = d_tileset->getConfigurationFile();
535   needs_saving = false;
536   
537   //Reorder the tileset according to the treeview
538   d_tileset->clear();
539   for (Gtk::TreeIter i = tiles_list->children().begin(),
540        end = tiles_list->children().end(); i != end; ++i) 
541     d_tileset->push_back((*i)[tiles_columns.tile]);
542
543   XML_Helper helper(current_save_filename, std::ios::out, false);
544   d_tileset->save(&helper);
545   helper.close();
546 }
547
548 bool TileSetWindow::quit()
549 {
550   if (needs_saving)
551     {
552       Gtk::Dialog* dialog;
553       Glib::RefPtr<Gtk::Builder> xml
554         = Gtk::Builder::create_from_file(get_glade_path() + 
555                                          "/editor-quit-dialog.ui");
556       xml->get_widget("dialog", dialog);
557       dialog->set_transient_for(*window);
558       int response = dialog->run();
559       dialog->hide();
560       delete dialog;
561       
562       if (response == Gtk::RESPONSE_CANCEL) //we don't want to quit
563         return false;
564
565       else if (response == Gtk::RESPONSE_ACCEPT) // save and quit
566         on_save_tileset_activated();
567       //else if (Response == Gtk::CLOSE) // don't save just quit
568       window->hide();
569     }
570   else
571     window->hide();
572   return true;
573 }
574
575 bool TileSetWindow::on_window_closed(GdkEventAny *event)
576 {
577   return !quit();
578 }
579
580 void TileSetWindow::on_quit_activated()
581 {
582   quit();
583 }
584
585 void TileSetWindow::on_edit_tileset_info_activated()
586 {
587   TileSetInfoDialog d(d_tileset, true);
588   d.set_parent_window(*window);
589   int response = d.run();
590   if (response == Gtk::RESPONSE_ACCEPT)
591     needs_saving = true;
592 }
593
594 void TileSetWindow::on_help_about_activated()
595 {
596   Gtk::AboutDialog* dialog;
597
598   Glib::RefPtr<Gtk::Builder> xml
599     = Gtk::Builder::create_from_file(get_glade_path() + "/../about-dialog.ui");
600
601   xml->get_widget("dialog", dialog);
602   dialog->set_transient_for(*window);
603   dialog->set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
604
605   dialog->set_version(PACKAGE_VERSION);
606   dialog->set_logo(GraphicsCache::getMiscPicture("tileset_icon.png")->to_pixbuf());
607   dialog->show_all();
608   dialog->run();
609   dialog->hide();
610   delete dialog;
611
612   return;
613 }
614
615 void TileSetWindow::update_tile_preview_menuitem()
616 {
617   if (get_selected_tile())
618     preview_tile_menuitem->set_sensitive(true);
619   else
620     preview_tile_menuitem->set_sensitive(false);
621 }
622
623 void TileSetWindow::on_tile_selected()
624 {
625   update_tile_panel();
626   update_tilestyleset_panel();
627   update_tilestyle_panel();
628   update_tileset_buttons();
629   update_tilestyleset_buttons();
630   update_tile_preview_menuitem();
631 }
632
633 void TileSetWindow::on_tilestyleset_selected()
634 {
635   update_tilestyleset_panel();
636   update_tilestyle_panel();
637   update_tilestyleset_buttons();
638 }
639
640 void TileSetWindow::on_tilestyle_selected()
641 {
642   update_tilestyle_panel();
643 }
644
645 void TileSetWindow::fill_tilestylesets()
646 {
647   Tile *t = get_selected_tile();
648   if (!t)
649     return;
650   tilestylesets_list->clear();
651   for (std::list<TileStyleSet*>::iterator it = t->begin(); it != t->end(); it++)
652     {
653       Gtk::TreeIter l = tilestylesets_list->append();
654       (*l)[tilestylesets_columns.name] = (*it)->getName();
655       (*l)[tilestylesets_columns.subdir] = (*it)->getSubDir();
656       (*l)[tilestylesets_columns.tilestyleset] = *it;
657     }
658 }
659
660 void TileSetWindow::fill_tile_info(Tile *tile)
661 {
662   tile_name_entry->set_text(tile->getName());
663   tile_type_combobox->set_active(tile->getTypeIndex());
664   tile_moves_spinbutton->set_value(tile->getMoves());
665   tile_smallmap_pattern_combobox->set_active(tile->getSmallTile()->getPattern());
666   tile_smallmap_first_colorbutton->set_sensitive(true);
667   fill_tilestylesets();
668   fill_colours(tile);
669   fill_tile_smallmap(tile);
670 }
671
672 void TileSetWindow::on_add_tile_clicked()
673 {
674   //add a new empty tile to the tileset
675   Tile *t = new Tile();
676   //add it to the treeview
677   Gtk::TreeIter i = tiles_list->append();
678   t->setName("Untitled");
679   (*i)[tiles_columns.name] = t->getName();
680   (*i)[tiles_columns.tile] = t;
681   d_tileset->push_back(t);
682   tiles_treeview->set_cursor (Gtk::TreePath (String::ucompose("%1", d_tileset->size() - 1)));
683   update_tile_preview_menuitem();
684   needs_saving = true;
685 }
686
687 void TileSetWindow::on_remove_tile_clicked()
688 {
689   //erase the selected row from the treeview
690   //remove the tile from the tileset
691   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
692   Gtk::TreeModel::iterator iterrow = selection->get_selected();
693
694   if (iterrow) 
695     {
696       Gtk::TreeModel::Row row = *iterrow;
697       Tile *a = row[tiles_columns.tile];
698       tiles_list->erase(iterrow);
699
700       for (std::vector<Tile*>::iterator it = d_tileset->begin(); it != d_tileset->end(); it++)
701         {
702           if (*it == a)
703             {
704               d_tileset->erase(it);
705               break;
706             }
707         }
708   
709       needs_saving = true;
710     }
711   update_tile_preview_menuitem();
712 }
713
714 void TileSetWindow::on_tile_first_color_changed()
715 {
716   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
717   Gtk::TreeModel::iterator iterrow = selection->get_selected();
718
719   if (iterrow) 
720     {
721       Gtk::TreeModel::Row row = *iterrow;
722       Tile *t = row[tiles_columns.tile];
723       t->getSmallTile()->setColor(tile_smallmap_first_colorbutton->get_color());
724       fill_tile_smallmap(t);
725       needs_saving = true;
726     }
727 }
728
729 void TileSetWindow::on_tile_second_color_changed()
730 {
731   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
732   Gtk::TreeModel::iterator iterrow = selection->get_selected();
733
734   if (iterrow) 
735     {
736       Gtk::TreeModel::Row row = *iterrow;
737       Tile *t = row[tiles_columns.tile];
738       t->getSmallTile()->setSecondColor(tile_smallmap_second_colorbutton->get_color());
739       fill_tile_smallmap(t);
740       needs_saving = true;
741     }
742 }
743
744 void TileSetWindow::on_tile_third_color_changed()
745 {
746   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
747   Gtk::TreeModel::iterator iterrow = selection->get_selected();
748
749   if (iterrow) 
750     {
751       Gtk::TreeModel::Row row = *iterrow;
752       Tile *t = row[tiles_columns.tile];
753       t->getSmallTile()->setThirdColor(tile_smallmap_third_colorbutton->get_color());
754       fill_tile_smallmap(t);
755       needs_saving = true;
756     }
757 }
758
759 void TileSetWindow::fill_colours(Tile *tile)
760 {
761   Gdk::Color c;
762   switch (tile->getSmallTile()->getPattern())
763     {
764     case SmallTile::SOLID:
765       tile_smallmap_second_colorbutton->set_sensitive(false);
766       tile_smallmap_third_colorbutton->set_sensitive(false);
767       tile_smallmap_first_colorbutton->set_color(tile->getSmallTile()->getColor());
768       break;
769     case SmallTile::STIPPLED: case SmallTile::SUNKEN:
770       tile_smallmap_second_colorbutton->set_sensitive(true);
771       tile_smallmap_third_colorbutton->set_sensitive(false);
772       tile_smallmap_first_colorbutton->set_color(tile->getSmallTile()->getColor());
773       tile_smallmap_second_colorbutton->set_color(tile->getSmallTile()->getSecondColor());
774       break;
775     case SmallTile::RANDOMIZED: case SmallTile::TABLECLOTH: case SmallTile::DIAGONAL: case SmallTile::CROSSHATCH: case SmallTile::SUNKEN_STRIPED:
776       tile_smallmap_second_colorbutton->set_sensitive(true);
777       tile_smallmap_third_colorbutton->set_sensitive(true);
778       tile_smallmap_first_colorbutton->set_color(tile->getSmallTile()->getColor());
779       tile_smallmap_second_colorbutton->set_color(tile->getSmallTile()->getSecondColor());
780       tile_smallmap_third_colorbutton->set_color(tile->getSmallTile()->getThirdColor());
781       break;
782     }
783 }
784
785 void TileSetWindow::on_tile_pattern_changed()
786 {
787   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
788   Gtk::TreeModel::iterator iterrow = selection->get_selected();
789
790   if (iterrow) 
791     {
792       Gtk::TreeModel::Row row = *iterrow;
793       Tile *t = row[tiles_columns.tile];
794       int idx = tile_smallmap_pattern_combobox->get_active_row_number();
795       SmallTile::Pattern pattern = SmallTile::Pattern(idx);
796       t->getSmallTile()->setPattern(pattern);
797       fill_colours(t);
798       fill_tile_smallmap(t);
799       needs_saving = true;
800     }
801 }
802
803 void TileSetWindow::on_tile_type_changed()
804 {
805   int idx = tile_type_combobox->get_active_row_number();
806   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
807   Gtk::TreeModel::iterator iterrow = selection->get_selected();
808
809   if (iterrow) 
810     {
811       Gtk::TreeModel::Row row = *iterrow;
812       Tile *t = row[tiles_columns.tile];
813       t->setTypeByIndex(idx);
814       needs_saving = true;
815     }
816 }
817
818 void TileSetWindow::on_tile_name_changed()
819 {
820   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
821   Gtk::TreeModel::iterator iterrow = selection->get_selected();
822
823   if (iterrow) 
824     {
825       Gtk::TreeModel::Row row = *iterrow;
826       row[tiles_columns.name] = tile_name_entry->get_text();
827       Tile *t = row[tiles_columns.tile];
828       t->setName(tile_name_entry->get_text());
829       needs_saving = true;
830     }
831 }
832
833 void TileSetWindow::fill_tile_smallmap(Tile *tile)
834 {
835   for (int i = 0; i < 32; i++)
836     {
837       for (int j = 0; j < 32; j++)
838         {
839           bool shadowed = false;
840           if (i == 32 - 1)
841             shadowed = true;
842           else if (j == 32 - 1)
843             shadowed = true;
844           OverviewMap::draw_terrain_tile (tile_smallmap_surface,
845                                           tile_smallmap_surface_gc,
846                                           tile->getSmallTile()->getPattern(),
847                                           tile->getSmallTile()->getColor(),
848                                           tile->getSmallTile()->getSecondColor(),
849                                           tile->getSmallTile()->getThirdColor(),
850                                           i, j, shadowed);
851         }
852     }
853
854   tile_smallmap_image->property_pixmap() = tile_smallmap_surface;
855 }
856
857 void TileSetWindow::on_add_tilestyleset_clicked()
858 {
859
860   Gtk::FileChooserDialog chooser(*window, _("Choose a Name"),
861                                  Gtk::FILE_CHOOSER_ACTION_OPEN);
862   Gtk::FileFilter sav_filter;
863   sav_filter.add_pattern("*.png");
864   chooser.set_filter(sav_filter);
865   chooser.set_current_folder(Glib::get_home_dir());
866
867   chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
868   chooser.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
869   chooser.set_default_response(Gtk::RESPONSE_ACCEPT);
870
871   chooser.show_all();
872   int res = chooser.run();
873
874   if (res == Gtk::RESPONSE_ACCEPT)
875     {
876       std::string filename = chooser.get_filename();
877       chooser.hide();
878       //add a new empty tile to the tileset
879       TileStyleSet *t = new TileStyleSet();
880       //add it to the treeview
881       Gtk::TreeIter i = tilestylesets_list->append();
882       t->setName("");
883       (*i)[tilestylesets_columns.name] = t->getName();
884       (*i)[tilestylesets_columns.tilestyleset] = t;
885
886       Tile *tile = get_selected_tile ();
887       tile->push_back(t);
888       tilestylesets_treeview->set_cursor (Gtk::TreePath (String::ucompose("%1", tile->size() - 1)));
889       image_filechooser_button->set_filename(filename);
890       needs_saving = true;
891     }
892 }
893
894 TileStyle * TileSetWindow::get_selected_tilestyle ()
895 {
896   Glib::RefPtr<Gtk::TreeSelection> selection = tilestyles_treeview->get_selection();
897   Gtk::TreeModel::iterator iterrow = selection->get_selected();
898
899   if (iterrow) 
900     {
901       Gtk::TreeModel::Row row = *iterrow;
902       return row[tilestyles_columns.tilestyle];
903     }
904   return NULL;
905 }
906
907 TileStyleSet * TileSetWindow::get_selected_tilestyleset ()
908 {
909   Glib::RefPtr<Gtk::TreeSelection> selection = tilestylesets_treeview->get_selection();
910   Gtk::TreeModel::iterator iterrow = selection->get_selected();
911
912   if (iterrow) 
913     {
914       Gtk::TreeModel::Row row = *iterrow;
915       return row[tilestylesets_columns.tilestyleset];
916     }
917   return NULL;
918 }
919
920 Tile * TileSetWindow::get_selected_tile ()
921 {
922   Glib::RefPtr<Gtk::TreeSelection> selection = tiles_treeview->get_selection();
923   Gtk::TreeModel::iterator iterrow = selection->get_selected();
924
925   if (iterrow) 
926     {
927       Gtk::TreeModel::Row row = *iterrow;
928       return row[tiles_columns.tile];
929     }
930   return NULL;
931 }
932
933 void TileSetWindow::on_remove_tilestyleset_clicked()
934 {
935   //erase the selected row from the treeview
936   //remove the tile from the tileset
937   Glib::RefPtr<Gtk::TreeSelection> selection = tilestylesets_treeview->get_selection();
938   Gtk::TreeModel::iterator iterrow = selection->get_selected();
939
940   if (iterrow) 
941     {
942       Tile *t = get_selected_tile();
943       Gtk::TreeModel::Row row = *iterrow;
944       TileStyleSet *s = row[tilestylesets_columns.tilestyleset];
945       tilestylesets_list->erase(iterrow);
946
947       for (std::list<TileStyleSet*>::iterator it = t->begin(); it != t->end(); it++)
948         {
949           if (*it == s)
950             {
951               t->erase(it);
952               break;
953             }
954         }
955       needs_saving = true;
956     }
957 }
958
959 void TileSetWindow::on_tilestyle_changed()
960 {
961   TileStyle *t = get_selected_tilestyle ();
962   if (t)
963     {
964       t->setType(TileStyle::Type(tilestyle_combobox->get_active_row_number()));
965       int idx = t->getType();
966       tilestyle_standard_image->property_pixbuf() = 
967         tilestyle_standard_images[idx]->to_pixbuf();
968     }
969 }
970
971 void TileSetWindow::on_image_chosen()
972 {
973   std::string selected_filename = image_filechooser_button->get_filename();
974   if (selected_filename.empty())
975     return;
976   if (inhibit_image_change == true)
977     {
978       inhibit_image_change = false;
979       return;
980     }
981
982   unsigned int height = get_image_height (selected_filename);
983   unsigned int width = get_image_width (selected_filename);
984   if ((width % height) != 0)
985     return;
986
987   std::string file = File::get_basename(selected_filename);
988       
989   if (selected_filename != d_tileset->getFile(file))
990     File::copy (selected_filename, d_tileset->getFile(file));
991
992   d_tileset->setTileSize(height);
993
994   on_remove_tilestyleset_clicked();
995
996   //now make a new one
997   TileStyleSet *set = new TileStyleSet();
998   Gtk::TreeIter i = tilestylesets_list->append();
999
1000   set->setSubDir(d_tileset->getSubDir());
1001   (*i)[tilestylesets_columns.subdir] = set->getSubDir();
1002
1003
1004   set->setName(File::get_basename(selected_filename));
1005   (*i)[tilestylesets_columns.name] = set->getName();
1006   (*i)[tilestylesets_columns.tilestyleset] = set;
1007   Tile *tile = get_selected_tile ();
1008   tile->push_back(set);
1009
1010   int no = width / height;
1011   for (int j = 0; j < no; j++)
1012     {
1013       int id = d_tileset->getFreeTileStyleId();
1014       if (id >= 0)
1015         {
1016           TileStyle *style = new TileStyle();
1017           style->setId(id);
1018           style->setType(TileStyle::LONE);
1019           set->push_back(style);
1020         }
1021     }
1022       
1023   needs_saving = true;
1024 }
1025
1026 void TileSetWindow::on_refresh_clicked()
1027 {
1028   TileStyleSet *set = get_selected_tilestyleset ();
1029   set->instantiateImages(d_tileset->getTileSize(), d_tileset->getFile(set->getName()));
1030 }
1031
1032 void TileSetWindow::on_preview_tile_activated()
1033 {
1034   Tile *tile = get_selected_tile();
1035   if (tile)
1036     {
1037       TilePreviewDialog d(tile, d_tileset->getTileSize());
1038       d.run();
1039     }
1040 }
1041       
1042 void TileSetWindow::on_roads_picture_activated()
1043 {
1044   ImageEditorDialog d(d_tileset->getFile(d_tileset->getRoadsFilename()),
1045                       ROAD_TYPES);
1046   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1047   d.set_parent_window(*window);
1048   int response = d.run();
1049   if (response == Gtk::RESPONSE_ACCEPT)
1050     {
1051       std::string filename = d.get_selected_filename();
1052       std::string name = File::get_basename(filename);
1053       File::copy(filename, d_tileset->getFile(name));
1054       d_tileset->setRoadsFilename(name);
1055       needs_saving = true;
1056     }
1057 }
1058 void TileSetWindow::on_bridges_picture_activated()
1059 {
1060   ImageEditorDialog d(d_tileset->getFile(d_tileset->getBridgesFilename()),
1061                       BRIDGE_TYPES);
1062   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1063   d.set_parent_window(*window);
1064   int response = d.run();
1065   if (response == Gtk::RESPONSE_ACCEPT)
1066     {
1067       std::string filename = d.get_selected_filename();
1068       std::string name = File::get_basename(filename);
1069       File::copy(filename, d_tileset->getFile(name));
1070       d_tileset->setBridgesFilename(name);
1071       needs_saving = true;
1072     }
1073 }
1074 void TileSetWindow::on_fog_picture_activated()
1075 {
1076   ImageEditorDialog d(d_tileset->getFile(d_tileset->getFogFilename()),
1077                       FOG_TYPES);
1078   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1079   d.set_parent_window(*window);
1080   int response = d.run();
1081   if (response == Gtk::RESPONSE_ACCEPT)
1082     {
1083       std::string filename = d.get_selected_filename();
1084       std::string name = File::get_basename(filename);
1085       File::copy(filename, d_tileset->getFile(name));
1086       d_tileset->setFogFilename(name);
1087       needs_saving = true;
1088     }
1089 }
1090 void TileSetWindow::on_flags_picture_activated()
1091 {
1092   TilesetFlagEditorDialog d(d_tileset);
1093   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1094   d.set_parent_window(*window);
1095   int response = d.run();
1096   if (response == Gtk::RESPONSE_ACCEPT)
1097     {
1098       std::string filename= d.get_selected_filename();
1099
1100       //copy the file, and set the image name
1101       std::string name = File::get_basename(filename);
1102       if (filename != d_tileset->getFile(name))
1103         File::copy(filename, d_tileset->getFile(name));
1104
1105       d_tileset->setFlagsFilename(name);
1106       needs_saving = true;
1107     }
1108 }
1109 void TileSetWindow::on_army_unit_selector_activated()
1110 {
1111   TilesetSelectorEditorDialog d(d_tileset);
1112   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1113   int response = d.run();
1114   if (response == Gtk::RESPONSE_ACCEPT)
1115     {
1116       std::string small = d.get_small_selector_filename();
1117       std::string large = d.get_large_selector_filename();
1118
1119       //copy the files, and set the image names
1120       std::string name = File::get_basename(small);
1121       if (small != d_tileset->getFile(name))
1122         File::copy(small, d_tileset->getFile(name));
1123       d_tileset->setSmallSelectorFilename(name);
1124
1125       name = File::get_basename(large);
1126       if (large != d_tileset->getFile(name))
1127         File::copy(large, d_tileset->getFile(name));
1128       d_tileset->setLargeSelectorFilename(name);
1129
1130       needs_saving = true;
1131     }
1132 }
1133
1134 void TileSetWindow::on_explosion_picture_activated()
1135 {
1136   TilesetExplosionPictureEditorDialog d(d_tileset);
1137   d.set_icon_from_file(File::getMiscFile("various/tileset_icon.png"));
1138   int response = d.run();
1139   if (response == Gtk::RESPONSE_ACCEPT)
1140     {
1141       std::string filename = d.get_selected_filename();
1142       std::string name = File::get_basename(filename);
1143       std::string dest =d_tileset->getFile(name);
1144       if (filename != d_tileset->getFile(name))
1145         {
1146           File::copy (filename, d_tileset->getFile(name));
1147           d_tileset->setExplosionFilename(name);
1148         }
1149       needs_saving = true;
1150     }
1151 }
1152
1153 void TileSetWindow::load_tileset(std::string filename)
1154 {
1155   current_save_filename = filename;
1156
1157   Tileset *tileset = Tileset::create(filename);
1158   if (tileset == NULL)
1159     {
1160       std::string msg;
1161       msg = "The tileset could not be loaded.";
1162       Gtk::MessageDialog dialog(*window, msg);
1163       dialog.run();
1164       dialog.hide();
1165     }
1166   tiles_list->clear();
1167   if (d_tileset)
1168     delete d_tileset;
1169   d_tileset = tileset;
1170
1171   d_tileset->setSubDir(File::get_basename(filename));
1172   d_tileset->instantiateImages();
1173   for (Tileset::iterator i = d_tileset->begin(); i != d_tileset->end(); ++i)
1174     {
1175       Gtk::TreeIter l = tiles_list->append();
1176       (*l)[tiles_columns.name] = (*i)->getName();
1177       (*l)[tiles_columns.tile] = *i;
1178     }
1179   if (d_tileset->size())
1180     tiles_treeview->set_cursor (Gtk::TreePath ("0"));
1181
1182   update_tileset_buttons();
1183   update_tilestyleset_buttons();
1184   update_tileset_menuitems();
1185   update_tile_panel();
1186   update_tilestyleset_panel();
1187   update_tilestyle_panel();
1188   update_tile_preview_menuitem();
1189 }