Merge branch 'master' into nikitakit_restructure
[synfig.git] / synfig-studio / src / gui / modules / mod_palette / dock_paledit.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file dock_paledit.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "dock_paledit.h"
34 #include "../../widgets/widget_color.h"
35 #include <gtkmm/frame.h>
36 #include <gtkmm/table.h>
37 #include <gtkmm/label.h>
38 #include <synfig/general.h>
39 #include <synfigapp/canvasinterface.h>
40 #include <synfigapp/value_desc.h>
41 #include "../../widgets/widget_color.h"
42 #include <gtkmm/spinbutton.h>
43 #include <gtkmm/menu.h>
44 #include <synfigapp/main.h>
45 #include "../../app.h"
46 #include "../../dialogs/dialog_color.h"
47 #include <errno.h>
48
49 #include "../../general.h"
50
51 #endif
52
53 /* === U S I N G =========================================================== */
54
55 using namespace std;
56 using namespace etl;
57 using namespace synfig;
58 using namespace studio;
59
60 /* === M A C R O S ========================================================= */
61
62 /* === G L O B A L S ======================================================= */
63 /*
64 class studio::PaletteSettings : public synfigapp::Settings
65 {
66         Dock_PalEdit* dialog_palette;
67         synfig::String name;
68 public:
69         PaletteSettings(Dock_PalEdit* window,const synfig::String& name):
70                 dialog_palette(window),
71                 name(name)
72         {
73                 dialog_palette->dialog_settings.add_domain(this,name);
74         }
75
76         virtual ~PaletteSettings()
77         {
78                 dialog_palette->dialog_settings.remove_domain(name);
79         }
80
81         virtual bool get_value(const synfig::String& key, synfig::String& value)const
82         {
83                 int i(atoi(key.c_str()));
84                 if(i<0 || i>=dialog_palette->size())
85                         return false;
86                 Color c(dialog_palette->get_color(i));
87                 value=strprintf("%f %f %f %f",c.get_r(),c.get_g(),c.get_b(),c.get_a());
88                 return true;
89         }
90
91         virtual bool set_value(const synfig::String& key,const synfig::String& value)
92         {
93                 int i(atoi(key.c_str()));
94                 if(i<0)
95                         return false;
96                 if(i>=dialog_palette->size())
97                         dialog_palette->palette_.resize(i+1);
98                 float r,g,b,a;
99                 if(!strscanf(value,"%f %f %f %f",&r,&g,&b,&a))
100                         return false;
101                 dialog_palette->set_color(Color(r,g,b,a),i);
102                 return true;
103         }
104
105         virtual KeyList get_key_list()const
106         {
107                 synfigapp::Settings::KeyList ret(synfigapp::Settings::get_key_list());
108
109                 int i;
110                 for(i=0;i<dialog_palette->size();i++)
111                         ret.push_back(strprintf("%03d",i));
112                 return ret;
113         }
114 };
115 */
116 /* === P R O C E D U R E S ================================================= */
117
118 /* === M E T H O D S ======================================================= */
119
120 Dock_PalEdit::Dock_PalEdit():
121         Dockable("pal_edit",_("Palette Editor"),Gtk::StockID("gtk-select-color")),
122         //palette_settings(new PaletteSettings(this,"colors")),
123         table(2,2,false)
124 {
125         action_group=Gtk::ActionGroup::create("action_group_pal_edit");
126         action_group->add(Gtk::Action::create(
127                 "palette-add-color",
128                 Gtk::StockID("gtk-add"),
129                 _("Add Color"),
130                 _("Add current outline color\nto the palette")
131         ),
132                 sigc::mem_fun(
133                         *this,
134                         &Dock_PalEdit::on_add_pressed
135                 )
136         );
137         action_group->add(Gtk::Action::create(
138                 "palette-save",
139                 Gtk::StockID("gtk-save"),
140                 _("Save palette"),
141                 _("Save the current palette")
142         ),
143                 sigc::mem_fun(
144                         *this,
145                         &Dock_PalEdit::on_save_pressed
146                 )
147         );
148         action_group->add(Gtk::Action::create(
149                 "palette-load",
150                 Gtk::StockID("gtk-open"),
151                 _("Load a palette"),
152                 _("Load a saved palette")
153         ),
154                 sigc::mem_fun(
155                         *this,
156                         &Dock_PalEdit::on_load_pressed
157                 )
158         );
159         action_group->add(Gtk::Action::create(
160                 "palette-set-default",
161                 Gtk::StockID("gtk-clear"),
162                 _("Load default"),
163                 _("Load default palette")
164         ),
165                 sigc::mem_fun(
166                         *this,
167                         &Dock_PalEdit::set_default_palette
168                 )
169         );
170
171
172         App::ui_manager()->insert_action_group(action_group);
173
174     Glib::ustring ui_info =
175         "<ui>"
176         "       <toolbar action='toolbar-palette'>"
177         "       <toolitem action='palette-add-color' />"
178         "       <toolitem action='palette-save' />"
179         "       <toolitem action='palette-load' />"
180         "       <toolitem action='palette-set-default' />"
181         "       </toolbar>"
182         "</ui>"
183         ;
184
185         App::ui_manager()->add_ui_from_string(ui_info);
186
187         set_toolbar(*dynamic_cast<Gtk::Toolbar*>(App::ui_manager()->get_widget("/toolbar-palette")));
188
189         /*
190         add_button(
191                 Gtk::StockID("gtk-add"),
192                 _("Add current outline color\nto the palette")
193         )->signal_clicked().connect(
194                 sigc::mem_fun(
195                         *this,
196                         &Dock_PalEdit::on_add_pressed
197                 )
198         );
199         */
200
201         add(table);
202         table.set_homogeneous(true);
203
204         set_default_palette();
205
206         show_all_children();
207 }
208
209 Dock_PalEdit::~Dock_PalEdit()
210 {
211         //delete palette_settings;
212 }
213
214 void
215 Dock_PalEdit::set_palette(const synfig::Palette& x)
216 {
217         palette_=x;
218         refresh();
219 }
220
221 void
222 Dock_PalEdit::on_add_pressed()
223 {
224         add_color(synfigapp::Main::get_outline_color());
225 }
226
227 void
228 Dock_PalEdit::on_save_pressed()
229 {
230         synfig::String filename = "";
231         while (App::dialog_save_file(_("Choose a Filename to Save As"),
232                                                                  filename, ANIMATION_DIR_PREFERENCE))
233         {
234                 // If the filename still has wildcards, then we should
235                 // continue looking for the file we want
236                 string base_filename = basename(filename);
237                 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
238                         continue;
239
240                 if (filename_extension(filename) == "")
241                         filename+=".spal";
242
243                 try
244                 {
245                         String ext(filename_extension(filename));
246                         if(ext!=".spal" && !App::dialog_yes_no(_("Unknown extension"),
247                                 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
248                                 continue;
249                 }
250                 catch(...)
251                 {
252                         continue;
253                 }
254
255                 {
256                         struct stat     s;
257                         int stat_return = stat(filename.c_str(), &s);
258
259                         // if stat() fails with something other than 'file doesn't exist', there's been a real
260                         // error of some kind.  let's give up now and ask for a new path.
261                         if (stat_return == -1 && errno != ENOENT)
262                         {
263                                 perror(filename.c_str());
264                                 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
265                                 App::dialog_error_blocking(_("Save Palette - Error"),msg.c_str());
266                                 continue;
267                         }
268
269                         // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
270                         string msg(strprintf(_("A file named '%s' already exists.\n\n"
271                                                                         "Do you want to replace it with the file you are saving?"), filename.c_str()));
272                         if ((stat_return == 0) &&
273                                 !App::dialog_yes_no(_("File exists"),msg.c_str()))
274                                 continue;
275                 }
276                 palette_.save_to_file(filename);
277                 return;
278         }
279 }
280
281 void
282 Dock_PalEdit::on_load_pressed()
283 {
284         synfig::String filename = "*.spal";
285         while(App::dialog_open_file(_("Choose a Palette to load"), filename, ANIMATION_DIR_PREFERENCE))
286         {
287                 // If the filename still has wildcards, then we should
288                 // continue looking for the file we want
289                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
290                         continue;
291
292                 try
293                 {
294                         palette_=synfig::Palette::load_from_file(filename);
295                 }
296                 catch (...)
297                 {
298                         App::get_ui_interface()->error(_("Unable to open file"));
299                         continue;
300                 }
301                 break;
302         }
303         refresh();
304 }
305
306 void
307 Dock_PalEdit::show_menu(int i)
308 {
309         Gtk::Menu* menu(manage(new Gtk::Menu()));
310         menu->signal_hide().connect(sigc::bind(sigc::ptr_fun(&delete_widget), menu));
311
312         menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-select-color"),
313                 sigc::bind(
314                         sigc::mem_fun(*this,&studio::Dock_PalEdit::edit_color),
315                         i
316                 )
317         ));
318
319         menu->items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::StockID("gtk-delete"),
320                 sigc::bind(
321                         sigc::mem_fun(*this,&studio::Dock_PalEdit::erase_color),
322                         i
323                 )
324         ));
325
326         //menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem());
327
328         menu->popup(3,gtk_get_current_event_time());
329 }
330
331 int
332 Dock_PalEdit::add_color(const synfig::Color& x)
333 {
334         palette_.push_back(x);
335         signal_changed()();
336         refresh();
337         return size()-1;
338 }
339
340 void
341 Dock_PalEdit::set_color(synfig::Color x, int i)
342 {
343         palette_[i].color=x;
344         signal_changed()();
345         refresh();
346 }
347
348 Color
349 Dock_PalEdit::get_color(int i)const
350 {
351         return palette_[i].color;
352 }
353
354 void
355 Dock_PalEdit::erase_color(int i)
356 {
357         palette_.erase(palette_.begin()+i);
358         signal_changed()();
359         refresh();
360 }
361
362 void
363 Dock_PalEdit::refresh()
364 {
365         const int width(12);
366
367         // Clear the table
368         table.foreach(sigc::mem_fun(table,&Gtk::Table::remove));
369
370         for(int i=0;i<size();i++)
371         {
372                 Widget_Color* widget_color(manage(new Widget_Color()));
373                 widget_color->set_value(get_color(i));
374                 widget_color->set_size_request(12,12);
375                 widget_color->signal_activate().connect(
376                         sigc::bind(
377                                 sigc::mem_fun(*this,&studio::Dock_PalEdit::select_fill_color),
378                                 i
379                         )
380                 );
381                 widget_color->signal_middle_click().connect(
382                         sigc::bind(
383                                 sigc::mem_fun(*this,&studio::Dock_PalEdit::select_outline_color),
384                                 i
385                         )
386                 );
387                 widget_color->signal_right_click().connect(
388                         sigc::bind(
389                                 sigc::mem_fun(*this,&studio::Dock_PalEdit::show_menu),
390                                 i
391                         )
392                 );
393                 int c(i%width),r(i/width);
394                 table.attach(*widget_color, c, c+1, r, r+1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
395         }
396         table.show_all();
397         queue_draw();
398 }
399
400
401 void
402 Dock_PalEdit::edit_color(int i)
403 {
404         App::dialog_color->reset();
405         App::dialog_color->set_color(get_color(i));
406         App::dialog_color->signal_edited().connect(
407                 sigc::bind(
408                         sigc::mem_fun(*this,&studio::Dock_PalEdit::set_color),
409                         i
410                 )
411         );
412         App::dialog_color->present();
413 }
414
415 void
416 Dock_PalEdit::select_fill_color(int i)
417 {
418         synfigapp::Main::set_fill_color(get_color(i));
419 }
420
421 void
422 Dock_PalEdit::select_outline_color(int i)
423 {
424         synfigapp::Main::set_outline_color(get_color(i));
425 }
426
427 void
428 Dock_PalEdit::set_default_palette()
429 {
430         int width=12;
431
432         palette_.clear();
433
434         // Greys
435         palette_.push_back(Color::alpha());
436         for(int i=0;i<width-1;i++)
437         {
438                 Color c(
439                         float(i)/(float)(width-2),
440                         float(i)/(float)(width-2),
441                         float(i)/(float)(width-2)
442                 );
443                 palette_.push_back(c);
444         }
445
446         // Tans
447         for(int i=0;i<width;i++)
448         {
449                 float x(float(i)/(float)(width-1));
450                 const Color tan1(0.2,0.05,0);
451                 const Color tan2(0.85,0.64,0.20);
452
453                 palette_.push_back(Color::blend(tan2,tan1,x));
454         }
455
456         // Solids
457         palette_.push_back(Color::red());
458         palette_.push_back(Color(1.0f,0.25f,0.0f));     // Orange
459         palette_.push_back(Color::yellow());
460         palette_.push_back(Color(0.25f,1.00f,0.0f));    // yellow-green
461         palette_.push_back(Color::green());
462         palette_.push_back(Color(0.0f,1.00f,0.25f));    // green-blue
463         palette_.push_back(Color::cyan());
464         palette_.push_back(Color(0.0f,0.25f,1.0f));     // Sea Blue
465         palette_.push_back(Color::blue());
466         palette_.push_back(Color(0.25f,0.0f,1.0f));
467         palette_.push_back(Color::magenta());
468         palette_.push_back(Color(1.0f,0.0f,0.25f));
469
470
471         const int levels(3);
472
473         // Colors
474         for(int j=0;j<levels;j++)
475         for(int i=0;i<width;i++)
476         {
477                 Color c(Color::red());
478                 c.set_hue(c.get_hue()-Angle::rot(float(i)/(float)(width)));
479                 c=c.clamped();
480                 float s(float(levels-j)/float(levels));
481                 s*=s;
482                 c.set_r(c.get_r()*s);
483                 c.set_g(c.get_g()*s);
484                 c.set_b(c.get_b()*s);
485                 palette_.push_back(c);
486         }
487
488
489         /*
490         const int levels(3);
491
492         for(int i=0;i<levels*levels*levels;i++)
493         {
494                 Color c(
495                         float(i%levels)/(float)(levels-1),
496                         float(i/levels%levels)/(float)(levels-1),
497                         float(i/(levels*levels))/(float)(levels-1)
498                 );
499                 palette_.push_back(c);
500         }
501         */
502         refresh();
503 }
504
505 int
506 Dock_PalEdit::size()const
507 {
508         return palette_.size();
509 }