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