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