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