1 //  Copyright (C) 2008, 2009, 2010, 2012, 2014, 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 <gtkmm.h>
21 #include <sigc++/functors/mem_fun.h>
22 
23 #include "tileset-selector-editor-dialog.h"
24 
25 #include "ucompose.hpp"
26 #include "defs.h"
27 #include "File.h"
28 #include "shieldsetlist.h"
29 #include "ImageCache.h"
30 #include "tileset-window.h"
31 #include "past-chooser.h"
32 #include "font-size.h"
33 #include "image-file-filter.h"
34 #include "timed-message-dialog.h"
35 
36 #define method(x) sigc::mem_fun(*this, &TilesetSelectorEditorDialog::x)
37 
TilesetSelectorEditorDialog(Gtk::Window & parent,Tileset * tileset)38 TilesetSelectorEditorDialog::TilesetSelectorEditorDialog(Gtk::Window &parent, Tileset *tileset)
39  : LwEditorDialog(parent, "tileset-selector-editor-dialog.ui")
40 {
41   d_changed = false;
42   bool broken = false;
43   d_tileset = tileset;
44   Glib::ustring imgname = d_tileset->getSmallSelectorFilename();
45   if (imgname.empty() == false)
46     {
47       Glib::ustring f = d_tileset->getFileFromConfigurationFile(imgname);
48       small_selector = PixMask::create (f, broken);
49     }
50   else
51     small_selector = NULL;
52   imgname = d_tileset->getLargeSelectorFilename();
53   if (imgname.empty() == false)
54     {
55       Glib::ustring f = d_tileset->getFileFromConfigurationFile(imgname);
56       large_selector = PixMask::create (f, broken);
57     }
58   else
59     large_selector = NULL;
60 
61   Gtk::Box *box;
62   xml->get_widget("shieldset_box", box);
63   setup_shield_theme_combobox(box);
64   xml->get_widget("preview_table", preview_table);
65 
66   xml->get_widget("large_selector_radiobutton", large_selector_radiobutton);
67   large_selector_radiobutton->signal_toggled().connect (method(on_button_toggle));
68   xml->get_widget("small_selector_radiobutton", small_selector_radiobutton);
69   small_selector_radiobutton->signal_toggled().connect (method(on_button_toggle));
70   xml->get_widget("selector_imagebutton", selector_imagebutton);
71   selector_imagebutton->signal_clicked().connect (method(on_selector_imagebutton_clicked));
72 
73   show_preview_selectors();
74   update_selector_panel();
75 }
76 
on_button_toggle()77 void TilesetSelectorEditorDialog::on_button_toggle ()
78 {
79   show_preview_selectors();
80   update_selector_panel();
81 }
82 
run()83 bool TilesetSelectorEditorDialog::run()
84 {
85   dialog->show_all();
86   dialog->run();
87   dialog->hide ();
88   return d_changed;
89 }
90 
setup_shield_theme_combobox(Gtk::Box * box)91 void TilesetSelectorEditorDialog::setup_shield_theme_combobox(Gtk::Box *box)
92 {
93   // fill in shield themes combobox
94   shield_theme_combobox = manage(new Gtk::ComboBoxText);
95 
96   Shieldsetlist *sl = Shieldsetlist::getInstance();
97   std::list<Glib::ustring> shield_themes = sl->getValidNames();
98   int counter = 0;
99   int default_id = 0;
100   for (std::list<Glib::ustring>::iterator i = shield_themes.begin(),
101        end = shield_themes.end(); i != end; ++i)
102     {
103       if (*i == _("Default"))
104 	default_id = counter;
105       shield_theme_combobox->append(Glib::filename_to_utf8(*i));
106       counter++;
107     }
108 
109   shield_theme_combobox->set_active(default_id);
110   shield_theme_combobox->signal_changed().connect (method(on_shieldset_changed));
111 
112   box->set_center_widget (*shield_theme_combobox);
113 }
114 
on_shieldset_changed()115 void TilesetSelectorEditorDialog::on_shieldset_changed()
116 {
117   show_preview_selectors();
118 }
119 
load_selector_image(Glib::ustring filename)120 bool TilesetSelectorEditorDialog::load_selector_image (Glib::ustring filename)
121 {
122   bool broken = false;
123   if (large_selector_radiobutton->get_active() == true)
124     {
125       if (large_selector)
126         delete large_selector;
127       large_selector = PixMask::create (filename, broken);
128     }
129   else if (small_selector_radiobutton->get_active() == true)
130     {
131       if (small_selector)
132         delete small_selector;
133       small_selector = PixMask::create (filename, broken);
134     }
135   return false;
136 }
137 
on_image_chosen(Gtk::FileChooserDialog * d)138 bool TilesetSelectorEditorDialog::on_image_chosen (Gtk::FileChooserDialog *d)
139 {
140   bool broken = load_selector_image (d->get_filename ());
141   if (!broken)
142     {
143       Glib::ustring imgname = get_selector_filename ();
144       Glib::ustring newname = "";
145       bool success = false;
146       if (imgname.empty() == true)
147         success =
148           d_tileset->addFileInCfgFile(d->get_filename(), newname);
149       else
150         success =
151           d_tileset->replaceFileInCfgFile(imgname, d->get_filename(), newname);
152       if (success)
153         {
154           set_selector_filename (newname);
155           d_changed = true;
156           show_preview_selectors ();
157           update_selector_panel();
158         }
159       else
160         {
161           Glib::ustring errmsg = Glib::strerror(errno);
162           TimedMessageDialog
163             td(*d, String::ucompose(_("Couldn't add %1 to :\n%2\n%3"),
164                                     d->get_filename (),
165                                     d_tileset->getConfigurationFile(),
166                                     errmsg), 0);
167           td.run_and_hide ();
168           broken = true;
169         }
170     }
171   else
172     {
173       TimedMessageDialog
174         td(*d, String::ucompose(_("Couldn't make sense of the image:\n%1"),
175                                 d->get_filename ()), 0);
176       td.run_and_hide ();
177       broken = true;
178     }
179   return broken;
180 }
181 
show_preview_selectors()182 void TilesetSelectorEditorDialog::show_preview_selectors()
183 {
184   //load it up and show in the colours of the selected shield theme
185   clearSelector();
186   if (loadSelector () == true)
187     {
188       on_heartbeat ();
189       heartbeat = Glib::signal_timeout().connect
190         (sigc::bind_return (method (on_heartbeat), true),
191          TIMER_BIGMAP_SELECTOR);
192     }
193 }
194 
clearSelector()195 void TilesetSelectorEditorDialog::clearSelector()
196 {
197   if (heartbeat.connected())
198     heartbeat.disconnect();
199 
200   for (std::map< guint32, std::list<Glib::RefPtr<Gdk::Pixbuf> >* >::iterator it = selectors.begin();
201        it != selectors.end(); it++)
202     {
203       for (std::list<Glib::RefPtr<Gdk::Pixbuf> >::iterator lit =
204            (*it).second->begin(); lit != (*it).second->end(); lit++)
205 	{
206 	  (*lit).clear();
207 	}
208       (*it).second->clear();
209       delete ((*it).second);
210     }
211   selectors.clear();
212   preview_table->foreach(sigc::mem_fun(preview_table, &Gtk::Container::remove));
213 }
214 
loadSelector()215 bool TilesetSelectorEditorDialog::loadSelector()
216 {
217   std::vector<PixMask *> images;
218   std::vector<PixMask *> masks;
219   PixMask *p = NULL;
220   if (large_selector_radiobutton->get_active() == true)
221     p = large_selector;
222   else if (small_selector_radiobutton->get_active() == true)
223     p = small_selector;
224   if (!p)
225     return false;
226   if (p->get_unscaled_height () == 0)
227     return false;
228   bool success =
229     SelectorPixMaskCacheItem::loadSelectors(p, d_tileset->getTileSize(),
230                                             images, masks, false);
231   if (success)
232     {
233       Glib::ustring n = shield_theme_combobox->get_active_text();
234       Shieldset *shieldset = Shieldsetlist::getInstance()->get(n, 0);
235 
236       for (unsigned int i = 0; i < MAX_PLAYERS; i++)
237 	{
238 	  std::list<Glib::RefPtr<Gdk::Pixbuf> > *mylist =
239             new std::list<Glib::RefPtr<Gdk::Pixbuf> >();
240 	  selectors[i] = mylist;
241 	}
242 
243       for (std::vector<PixMask*>::iterator it = images.begin(),
244            mit = masks.begin(); it != images.end(); it++, mit++)
245 	{
246 	  for (Shieldset::iterator sit = shieldset->begin();
247                sit != shieldset->end(); sit++)
248             {
249               if ((*sit)->getOwner() == 8) //ignore neutral
250                 continue;
251               PixMask *q =
252                 ImageCache::applyMask(*it, *mit, (*sit)->getColor());
253               double ratio = EDITOR_DIALOG_TILE_PIC_FONTSIZE_MULTIPLE;
254               int font_size = FontSize::getInstance()->get_height ();
255               double new_height = font_size * ratio;
256               int new_width =
257                 ImageCache::calculate_width_from_adjusted_height
258                 (q, new_height);
259               PixMask::scale (q, new_width, new_height);
260               selectors[(*sit)->getOwner()]->push_back (q->to_pixbuf ());
261 
262               frame[(*sit)->getOwner()] =
263                 selectors[(*sit)->getOwner()]->begin();
264             }
265 	}
266 
267       for (std::vector<PixMask*>::iterator it = images.begin();
268            it != images.end(); it++)
269 	delete *it;
270       for (std::vector<PixMask*>::iterator it = masks.begin();
271            it != masks.end(); it++)
272 	delete *it;
273     }
274 
275   return success;
276 }
277 
on_heartbeat()278 void TilesetSelectorEditorDialog::on_heartbeat()
279 {
280   preview_table->foreach(sigc::mem_fun(preview_table, &Gtk::Container::remove));
281   for (int i = 0; i < 4; i++)
282     preview_table->insert_row (i);
283   for (int i = 0; i < 2; i++)
284     preview_table->insert_column (i);
285 
286   int x = 0;
287   int y = 0;
288   int count = 0;
289   for (std::map< guint32, std::list<Glib::RefPtr<Gdk::Pixbuf> >* >::iterator it = selectors.begin();
290        it != selectors.end(); it++)
291     {
292       //make a pixbuf and attach it
293       switch (count)
294 	{
295 	case 0: x = 0; y = 0; break;
296 	case 1: x = 0; y = 1; break;
297 	case 2: x = 0; y = 2; break;
298 	case 3: x = 0; y = 3; break;
299 	case 4: x = 1; y = 0; break;
300 	case 5: x = 1; y = 1; break;
301 	case 6: x = 1; y = 2; break;
302 	case 7: x = 1; y = 3; break;
303 	}
304       preview_table->attach(*manage(new Gtk::Image(*frame[count])), y, x, 1, 1);
305 
306       frame[count]++;
307       if (frame[count] == selectors[count]->end())
308 	frame[count] = selectors[count]->begin();
309       count++;
310     }
311   preview_table->show_all();
312 }
313 
update_selector_panel()314 void TilesetSelectorEditorDialog::update_selector_panel()
315 {
316   Glib::ustring f = get_selector_filename ();
317   if (f.empty () == false)
318     selector_imagebutton->set_label (f);
319   else
320     {
321       selector_imagebutton->set_label (_("no image set"));
322       clearSelector();
323     }
324 }
325 
image_filechooser(bool clear)326 Gtk::FileChooserDialog* TilesetSelectorEditorDialog::image_filechooser(bool clear)
327 {
328   Glib::ustring filename = "";
329   Glib::ustring title = "";
330   if (large_selector_radiobutton->get_active() == true)
331     title = _("Choose a large selector image");
332   else if (small_selector_radiobutton->get_active() == true)
333     title = _("Choose a small selector image");
334   Gtk::FileChooserDialog *d = new Gtk::FileChooserDialog(*dialog, title);
335   ImageFileFilter::getInstance ()->add (d);
336   d->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
337   d->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
338   if (clear)
339     d->add_button(Gtk::Stock::CLEAR, Gtk::RESPONSE_REJECT);
340   d->set_default_response(Gtk::RESPONSE_ACCEPT);
341   d->set_current_folder(PastChooser::getInstance()->get_dir(d));
342   return d;
343 }
344 
get_selector_filename()345 Glib::ustring TilesetSelectorEditorDialog::get_selector_filename ()
346 {
347   if (large_selector_radiobutton->get_active() == true)
348     return d_tileset->getLargeSelectorFilename ();
349   else if (small_selector_radiobutton->get_active() == true)
350     return d_tileset->getSmallSelectorFilename ();
351   return "";
352 }
353 
set_selector_filename(Glib::ustring f)354 void TilesetSelectorEditorDialog::set_selector_filename (Glib::ustring f)
355 {
356   if (large_selector_radiobutton->get_active() == true)
357     {
358       d_tileset->setLargeSelectorFilename (f);
359       if (f.empty () == false)
360         d_tileset->instantiateLargeSelectorImages();
361     }
362   else if (small_selector_radiobutton->get_active() == true)
363     {
364       d_tileset->setSmallSelectorFilename (f);
365       if (f.empty () == false)
366         d_tileset->instantiateSmallSelectorImages();
367     }
368   return ;
369 }
370 
clear_selector_image()371 void TilesetSelectorEditorDialog::clear_selector_image ()
372 {
373   if (large_selector_radiobutton->get_active() == true)
374     {
375       if (large_selector)
376         delete large_selector;
377       large_selector = NULL;
378       d_tileset->clearLargeSelectorImage();
379     }
380   else if (small_selector_radiobutton->get_active() == true)
381     {
382       if (small_selector)
383         delete small_selector;
384       small_selector = NULL;
385       d_tileset->clearSmallSelectorImage();
386     }
387   return;
388 }
389 
on_selector_imagebutton_clicked()390 void TilesetSelectorEditorDialog::on_selector_imagebutton_clicked ()
391 {
392   Glib::ustring f = get_selector_filename ();
393   Glib::ustring filename = "";
394   Gtk::FileChooserDialog *d = image_filechooser(f != "");
395   if (f != "")
396     filename = d_tileset->getFileFromConfigurationFile(f);
397   int response = d->run();
398   if (filename != "")
399     File::erase(filename);
400   if (response == Gtk::RESPONSE_ACCEPT && d->get_filename() != "")
401     {
402       if (ImageFileFilter::getInstance ()->hasInvalidExt (d->get_filename ()))
403         ImageFileFilter::getInstance ()->showErrorDialog (d);
404       else
405         {
406           if (d->get_filename() != filename)
407             {
408               PastChooser::getInstance()->set_dir(d);
409               on_image_chosen (d);
410             }
411         }
412     }
413   else if (response == Gtk::RESPONSE_REJECT && f != "")
414     {
415       if (d_tileset->removeFileInCfgFile(f))
416         {
417           d_changed = true;
418           d_tileset->uninstantiateSameNamedImages (f);
419           update_selector_panel ();
420         }
421       else
422         {
423           Glib::ustring errmsg = Glib::strerror(errno);
424           TimedMessageDialog
425             td(*d, String::ucompose(_("Couldn't remove %1 from:\n%2\n%3"),
426                                     f, d_tileset->getConfigurationFile(),
427                                     errmsg), 0);
428           td.run_and_hide ();
429         }
430     }
431   d->hide();
432   delete d;
433 }
434 
~TilesetSelectorEditorDialog()435 TilesetSelectorEditorDialog::~TilesetSelectorEditorDialog()
436 {
437   if (small_selector)
438     delete small_selector;
439   if (large_selector)
440     delete large_selector;
441 }
442