Implements PXEGeek's http://wiki.synfig.com/Wish_list entry: "Optionally display...
[synfig.git] / synfig-studio / trunk / src / gtkmm / cellrenderer_value.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file cellrenderer_value.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include <gtkmm/label.h>
33 #include <ETL/stringf>
34 #include <gtkmm/celleditable.h>
35 #include <gtkmm/editable.h>
36 #include <gtkmm/entry.h>
37 #include <gtkmm/eventbox.h>
38 #include <gtk/gtkentry.h> /* see XXX below */
39
40 #include "app.h"
41 #include "widget_value.h"
42 #include "widget_vector.h"
43 #include "widget_filename.h"
44 #include "widget_enum.h"
45 #include "widget_color.h"
46 #include "widget_canvaschooser.h"
47 #include "widget_time.h"
48
49 #include "cellrenderer_gradient.h"
50 #include "cellrenderer_value.h"
51
52 #include "widget_gradient.h"
53 #include "dialog_gradient.h"
54 #include "dialog_color.h"
55 #include <gtkmm/textview.h>
56
57 #endif
58
59 using namespace synfig;
60 using namespace etl;
61 using namespace std;
62 using namespace studio;
63
64 /* === M A C R O S ========================================================= */
65
66 #define DIGITS          15
67
68 #define use_colorspace_gamma()  App::use_colorspace_gamma
69 #define colorspace_gamma()      (2.2f)
70 #define gamma_in(x)             pow((float)x,1.0f/colorspace_gamma())
71 #define gamma_out(x)    pow((float)x,colorspace_gamma())
72
73 /* === G L O B A L S ======================================================= */
74
75 class studio::ValueBase_Entry : public Gtk::EventBox, public Gtk::CellEditable
76 {
77         Glib::ustring path;
78         Widget_ValueBase *valuewidget;
79         bool edit_done_called;
80         Gtk::Widget *parent;
81 public:
82         ValueBase_Entry():
83                 Glib::ObjectBase  (typeid(ValueBase_Entry)),
84                 Gtk::EventBox     (),
85                 Gtk::CellEditable ()
86         {
87                 parent=0;
88                 edit_done_called=false;
89 /*
90                   Gtk::HBox *const hbox = new Gtk::HBox(false, 0);
91                   add(*Gtk::manage(hbox));
92
93                   Gtk::Entry *entry_ = new Gtk::Entry();
94                         entry_->set_text("bleh");
95                   hbox->pack_start(*Gtk::manage(entry_), Gtk::PACK_EXPAND_WIDGET);
96                   entry_->set_has_frame(false);
97                   entry_->gobj()->is_cell_renderer = true; // XXX
98
99 */
100                 valuewidget=manage(new class Widget_ValueBase());
101                 valuewidget->inside_cellrenderer();
102                 add(*valuewidget);
103                 valuewidget->show();
104
105                 //set_flags(Gtk::CAN_FOCUS);
106                 //set_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
107
108                 /*
109                 set_events(//(Gdk::ALL_EVENTS_MASK)
110                 ~(      Gdk::EXPOSURE_MASK
111                         | Gdk::ENTER_NOTIFY_MASK
112                         | Gdk::LEAVE_NOTIFY_MASK
113                         | Gdk::FOCUS_CHANGE_MASK
114                         | Gdk::STRUCTURE_MASK
115                         | Gdk::PROPERTY_CHANGE_MASK
116                         | Gdk::VISIBILITY_NOTIFY_MASK
117                         | Gdk::PROXIMITY_IN_MASK
118                         | Gdk::PROXIMITY_OUT_MASK
119                         | Gdk::SUBSTRUCTURE_MASK
120                 )
121                 );
122                 */
123                 //signal_editing_done().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));
124                 //signal_remove_widget().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));
125
126                 show_all_children();
127
128                 //signal_show().connect(sigc::mem_fun(*this, &ValueBase_Entry::grab_focus));
129         }
130         ~ValueBase_Entry()
131         {
132                 DEBUGPOINT();
133         }
134
135         void on_editing_done()
136         {
137                 hide();
138                 if(parent)parent->grab_focus();
139                 if(!edit_done_called)
140                 {
141                         edit_done_called=true;
142                         Gtk::CellEditable::on_editing_done();
143                 }
144                 else
145                 {
146                         synfig::error("on_editing_done(): Called twice!");
147                 }
148         }
149         void set_parent(Gtk::Widget*x) { parent=x; }
150         void on_remove_widget()
151         {
152                 hide();
153                 edit_done_called=true;
154                 if(parent)parent->grab_focus();
155                 Gtk::CellEditable::on_remove_widget();
156         }
157         void start_editing_vfunc(GdkEvent *event)
158         {
159                 valuewidget->signal_activate().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::editing_done));
160                 show();
161                 //valuewidget->grab_focus();
162                 //get_window()->set_focus(*valuewidget);
163         }
164         bool on_event(GdkEvent *event)
165         {
166                 if(event->any.type==GDK_BUTTON_PRESS ||
167                         event->any.type==GDK_2BUTTON_PRESS ||
168                         event->any.type==GDK_KEY_PRESS ||
169                         event->any.type==GDK_KEY_RELEASE ||
170                         event->any.type==GDK_SCROLL ||
171                         event->any.type==GDK_3BUTTON_PRESS)
172                         return true;
173                 return Gtk::EventBox::on_event(event);
174         }
175         void on_grab_focus()
176         {
177                 Gtk::EventBox::on_grab_focus();
178                 if(valuewidget)
179                         valuewidget->grab_focus();
180         }
181         void set_path(const Glib::ustring &p)
182         {
183                 path=p;
184         }
185         void set_value(const synfig::ValueBase &data)
186         {
187                 if(valuewidget)
188                         valuewidget->set_value(data);
189                 //valuewidget->grab_focus();
190         }
191         void set_canvas(const etl::handle<synfig::Canvas> &data)
192         {
193                 assert(data);
194                 if(valuewidget)
195                         valuewidget->set_canvas(data);
196         }
197         void set_param_desc(const synfig::ParamDesc &data)
198         {
199                 if(valuewidget)
200                         valuewidget->set_param_desc(data);
201         }
202
203         const synfig::ValueBase &get_value()
204         {
205                 if(valuewidget)
206                         return valuewidget->get_value();
207                 return synfig::ValueBase();
208         }
209         const Glib::ustring &get_path()
210         {
211                 return path;
212         }
213
214 };
215
216 /* === P R O C E D U R E S ================================================= */
217
218 bool get_paragraph(synfig::String& text)
219 {
220         Gtk::Dialog dialog(
221                 _("Paragraph"),         // Title
222                 true,           // Modal
223                 true            // use_separator
224         );
225         Gtk::Label label(_("Enter Paragraph Text Here:"));
226         label.show();
227         dialog.get_vbox()->pack_start(label);
228
229
230         Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
231         text_buffer->set_text(text);
232
233         Gtk::TextView text_view(text_buffer);
234         text_view.show();
235         dialog.get_vbox()->pack_start(text_view);
236
237 /*
238         Gtk::Entry entry;
239         entry.set_text(text);
240         entry.show();
241         entry.set_activates_default(true);
242         dialog.get_vbox()->pack_start(entry);
243 */
244
245         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
246         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
247         dialog.set_default_response(Gtk::RESPONSE_OK);
248
249         //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
250
251         dialog.show();
252
253         if(dialog.run()!=Gtk::RESPONSE_OK)
254                 return false;
255
256         text=text_buffer->get_text();
257
258         return true;
259 }
260
261 /* === M E T H O D S ======================================================= */
262
263 CellRenderer_ValueBase::CellRenderer_ValueBase():
264         Glib::ObjectBase        (typeid(CellRenderer_ValueBase)),
265         Gtk::CellRendererText   (),
266         property_value_ (*this,"value",synfig::ValueBase()),
267         property_canvas_(*this,"canvas",etl::handle<synfig::Canvas>()),
268         property_param_desc_(*this,"param_desc",synfig::ParamDesc())
269 {
270         CellRendererText::signal_edited().connect(sigc::mem_fun(*this,&CellRenderer_ValueBase::string_edited_));
271         value_entry=new ValueBase_Entry();
272         value_entry->hide();
273
274         Pango::AttrList attr_list;
275         {
276                 Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*8));
277                 pango_size.set_start_index(0);
278                 pango_size.set_end_index(64);
279                 attr_list.change(pango_size);
280         }
281         property_attributes()=attr_list;
282
283         property_foreground()=Glib::ustring("#7f7f7f");
284         property_inconsistant()=false;
285 }
286
287 CellRenderer_ValueBase::~CellRenderer_ValueBase()
288 {
289 //      synfig::info("CellRenderer_ValueBase::~CellRenderer_ValueBase(): deleted");
290 }
291
292 void
293 CellRenderer_ValueBase::string_edited_(const Glib::ustring&path,const Glib::ustring&str)
294 {
295         ValueBase old_value=property_value_.get_value();
296         ValueBase value;
297
298         if(old_value.get_type()==ValueBase::TYPE_TIME)
299         {
300                 value=ValueBase(Time((String)str,get_canvas()->rend_desc().get_frame_rate()));
301         }
302         else
303                 value=ValueBase((String)str);
304
305         if(old_value!=value)
306                 signal_edited_(path,value);
307 }
308
309 void
310 CellRenderer_ValueBase::render_vfunc(
311                 const Glib::RefPtr<Gdk::Drawable>& window,
312                 Gtk::Widget& widget,
313                 const Gdk::Rectangle& background_area,
314                 const Gdk::Rectangle& ca,
315                 const Gdk::Rectangle& expose_area,
316                 Gtk::CellRendererState flags)
317 {
318         if(!window)
319                 return;
320 //      const unsigned int cell_xpad = property_xpad();
321 //      const unsigned int cell_ypad = property_ypad();
322
323         //int x_offset = 0, y_offset = 0;
324 //      int     width = ca.get_width();
325         int     height = ca.get_height();
326 //      get_size(widget, ca, x_offset, y_offset, width, height);
327
328 //      width  -= cell_xpad * 2;
329 //      height -= cell_ypad * 2;
330
331 //      if(width <= 0 || height <= 0)
332 //              return;
333
334         Gtk::StateType state = Gtk::STATE_INSENSITIVE;
335         if(property_editable())
336                 state = Gtk::STATE_NORMAL;
337         if((flags & Gtk::CELL_RENDERER_SELECTED) != 0)
338                 state = (widget.has_focus()) ? Gtk::STATE_SELECTED : Gtk::STATE_ACTIVE;
339
340         ValueBase data=property_value_.get_value();
341
342         switch(data.get_type())
343         {
344         case ValueBase::TYPE_REAL:
345                 if(((synfig::ParamDesc)property_param_desc_).get_is_distance())
346                 {
347                         Distance x(data.get(Real()),Distance::SYSTEM_UNITS);
348                         x.convert(App::distance_system,get_canvas()->rend_desc());
349                         property_text()=(Glib::ustring)x.get_string(6).c_str();
350                 }
351                 else
352                         property_text()=(Glib::ustring)strprintf("%.6f",data.get(Real()));
353                 break;
354         case ValueBase::TYPE_TIME:
355                 property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format());
356                 break;
357         case ValueBase::TYPE_ANGLE:
358                 property_text()=(Glib::ustring)strprintf("%.2f DEG",(Real)Angle::deg(data.get(Angle())).get());
359                 break;
360         case ValueBase::TYPE_INTEGER:
361                 if(((synfig::ParamDesc)property_param_desc_).get_hint()!="enum")
362                 {
363                         property_text()=(Glib::ustring)strprintf("%i",data.get(int()));
364                 }
365                 else
366                 {
367                         property_text()=(Glib::ustring)strprintf("(%i)",data.get(int()));
368                         std::list<synfig::ParamDesc::EnumData> enum_list=((synfig::ParamDesc)property_param_desc_).get_enum_list();
369                         std::list<synfig::ParamDesc::EnumData>::iterator iter;
370
371                         for(iter=enum_list.begin();iter!=enum_list.end();iter++)
372                                 if(iter->value==data.get(int()))
373                                 {
374                                         property_text()=(Glib::ustring)iter->local_name;
375                                         break;
376                                 }
377                 }
378
379                 break;
380         case ValueBase::TYPE_VECTOR:
381                 {
382                         Vector vector=data.get(Vector());
383                         Distance x(vector[0],Distance::SYSTEM_UNITS),y(vector[1],Distance::SYSTEM_UNITS);
384                         x.convert(App::distance_system,get_canvas()->rend_desc());
385                         y.convert(App::distance_system,get_canvas()->rend_desc());
386                         property_text()=static_cast<Glib::ustring>(strprintf("%s,%s",x.get_string(6).c_str(),y.get_string(6).c_str()));
387                 }
388                 break;
389
390         case ValueBase::TYPE_STRING:
391
392                 if(data.get_type()==ValueBase::TYPE_STRING)
393                 {
394                         if(!data.get(synfig::String()).empty())
395                                 property_text()=static_cast<Glib::ustring>(data.get(synfig::String()));
396                         else
397                                 property_text()=Glib::ustring("<empty>");
398                 }
399                 break;
400         case ValueBase::TYPE_CANVAS:
401                 if(data.get(etl::handle<synfig::Canvas>()))
402                 {
403                         if(data.get(etl::handle<synfig::Canvas>())->is_inline())
404                                 property_text()="<Inline Canvas>";
405                         else
406                                 property_text()=(Glib::ustring)data.get(etl::handle<synfig::Canvas>())->get_id();
407                 }
408                 else
409                         property_text()="<No Image Selected>";
410                 break;
411         case ValueBase::TYPE_COLOR:
412                 {
413                         render_color_to_window(window,ca,data.get(Color()));
414                         return;
415                 }
416                 break;
417         case ValueBase::TYPE_BOOL:
418                 {
419                         widget.get_style()->paint_check(
420                                 Glib::RefPtr<Gdk::Window>::cast_static(window), state,
421                                 data.get(bool())?Gtk::SHADOW_IN:Gtk::SHADOW_OUT,
422                                 ca, widget, "cellcheck",
423                                 ca.get_x()/* + x_offset + cell_xpad*/,
424                                 ca.get_y()/* + y_offset + cell_ypad*/,
425                                 height-1,height-1);
426                         return;
427                 }
428                 break;
429         case ValueBase::TYPE_NIL:
430                 //property_text()=(Glib::ustring)" ";
431                 return;
432                 break;
433         case ValueBase::TYPE_SEGMENT:
434                 property_text()=(Glib::ustring)_("Segment");
435                 break;
436         case ValueBase::TYPE_GRADIENT:
437                 render_gradient_to_window(window,ca,data.get(Gradient()));
438                 return;
439                 break;
440         case ValueBase::TYPE_LIST:
441                 property_text()=(Glib::ustring)_("List");
442                 break;
443         case ValueBase::TYPE_BLINEPOINT:
444                 property_text()=(Glib::ustring)_("BLine Point");
445                 break;
446         default:
447                 property_text()=static_cast<Glib::ustring>(_("UNKNOWN"));
448                 break;
449         }
450         CellRendererText::render_vfunc(window,widget,background_area,ca,expose_area,flags);
451 }
452
453
454 /*
455 bool
456 CellRenderer_ValueBase::activate_vfunc( GdkEvent* event,
457         Gtk::Widget& widget,
458         const Glib::ustring& path,
459         const Gdk::Rectangle& background_area,
460         const Gdk::Rectangle& cell_area,
461         Gtk::CellRendererState flags)
462 {
463         ValueBase data=(ValueBase)property_value_.get_value();
464
465         switch(data.type)
466         {
467         case ValueBase::TYPE_BOOL:
468                 if(property_editable())
469                         signal_edited_(path,ValueBase(!data.get(bool())));
470         return true;
471         case ValueBase::TYPE_STRING:
472                 return CellRendererText::activate_vfunc(event,widget,path,background_area,cell_area,flags);
473         }
474         return false;
475 }
476 */
477
478 void
479 CellRenderer_ValueBase::gradient_edited(synfig::Gradient gradient, Glib::ustring path)
480 {
481         ValueBase old_value(property_value_.get_value());
482         ValueBase value(gradient);
483         if(old_value!=value)
484                 signal_edited_(path,value);
485 }
486
487 void
488 CellRenderer_ValueBase::color_edited(synfig::Color color, Glib::ustring path)
489 {
490         ValueBase old_value(property_value_.get_value());
491         ValueBase value(color);
492         if(old_value!=value)
493                 signal_edited_(path,value);
494 }
495
496 Gtk::CellEditable*
497 CellRenderer_ValueBase::start_editing_vfunc(
498         GdkEvent* event,
499         Gtk::Widget& widget,
500         const Glib::ustring& path,
501         const Gdk::Rectangle& background_area,
502         const Gdk::Rectangle& cell_area,
503         Gtk::CellRendererState flags)
504 {
505         // If we aren't editable, then there is nothing to do
506         if(!property_editable())
507                 return 0;
508
509         ValueBase data=property_value_.get_value();
510
511         switch(data.get_type())
512         {
513         case ValueBase::TYPE_BOOL:
514                 signal_edited_(path,ValueBase(!data.get(bool())));
515         return NULL;
516         //case ValueBase::TYPE_TIME:
517         //      property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format()|Time::FORMAT_FULL);
518         //      return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);
519
520         case ValueBase::TYPE_GRADIENT:
521                 App::dialog_gradient->reset();
522                 App::dialog_gradient->set_gradient(data.get(Gradient()));
523                 App::dialog_gradient->signal_edited().connect(
524                         sigc::bind(
525                                 sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::gradient_edited),
526                                 path
527                         )
528                 );
529                 App::dialog_gradient->present();
530
531                 return NULL;
532
533         case ValueBase::TYPE_COLOR:
534                 App::dialog_color->reset();
535                 App::dialog_color->set_color(data.get(Color()));
536                 App::dialog_color->signal_edited().connect(
537                         sigc::bind(
538                                 sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::color_edited),
539                                 path
540                         )
541                 );
542                 App::dialog_color->present();
543
544                 return NULL;
545         case ValueBase::TYPE_STRING:
546                 if(get_param_desc().get_hint()=="paragraph")
547                 {
548                         synfig::String string;
549                         string=data.get(string);
550                         if(get_paragraph(string))
551                         {
552                                 signal_edited_(path,ValueBase(string));
553                         }
554                         return NULL;
555                 }
556                 if(get_param_desc().get_hint()!="filename")
557                         return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);
558         default:
559                 {
560                         assert(get_canvas());
561                         //delete value_entry;
562                         value_entry=manage(new ValueBase_Entry());
563                         value_entry->set_path(path);
564                         value_entry->set_canvas(get_canvas());
565                         value_entry->set_param_desc(get_param_desc());
566                         value_entry->set_value(data);
567                         value_entry->set_parent(&widget);
568                         value_entry->signal_editing_done().connect(sigc::mem_fun(*this, &CellRenderer_ValueBase::on_value_editing_done));
569                         return value_entry;
570                 }
571         }
572         return NULL;
573 }
574
575 void
576 CellRenderer_ValueBase::on_value_editing_done()
577 {
578         if(value_entry)
579         {
580                 ValueBase old_value(property_value_.get_value());
581                 ValueBase value(value_entry->get_value());
582
583                 if(old_value!=value)
584                         signal_edited_(value_entry->get_path(),value);
585
586                 //delete value_entry;
587                 //value_entry=0;
588         }
589 }