Allow scrolling left and right in time widgets
[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 **      Copyright (c) 2007, 2008 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 <gtkmm/label.h>
34 #include <ETL/stringf>
35 #include <gtkmm/celleditable.h>
36 #include <gtkmm/editable.h>
37 #include <gtkmm/entry.h>
38 #include <gtkmm/eventbox.h>
39 #include <gtk/gtkentry.h> /* see XXX below */
40
41 #include "app.h"
42 #include "widget_value.h"
43 #include "widget_vector.h"
44 #include "widget_filename.h"
45 #include "widget_enum.h"
46 #include "widget_color.h"
47 #include "widget_canvaschooser.h"
48 #include "widget_time.h"
49
50 #include "cellrenderer_gradient.h"
51 #include "cellrenderer_value.h"
52
53 #include "widget_gradient.h"
54 #include "dialog_gradient.h"
55 #include "dialog_color.h"
56 #include <gtkmm/textview.h>
57
58 #include "general.h"
59
60 #endif
61
62 using namespace synfig;
63 using namespace etl;
64 using namespace std;
65 using namespace studio;
66
67 /* === M A C R O S ========================================================= */
68
69 #define DIGITS          15
70
71 /* === G L O B A L S ======================================================= */
72
73 class studio::ValueBase_Entry : public Gtk::EventBox, public Gtk::CellEditable
74 {
75         Glib::ustring path;
76         Widget_ValueBase *valuewidget;
77         bool edit_done_called;
78         Gtk::Widget *parent;
79 public:
80         ValueBase_Entry():
81                 Glib::ObjectBase  (typeid(ValueBase_Entry)),
82                 Gtk::EventBox     (),
83                 Gtk::CellEditable ()
84         {
85                 parent=0;
86                 edit_done_called=false;
87 /*
88                   Gtk::HBox *const hbox = new Gtk::HBox(false, 0);
89                   add(*Gtk::manage(hbox));
90
91                   Gtk::Entry *entry_ = new Gtk::Entry();
92                         entry_->set_text("bleh");
93                   hbox->pack_start(*Gtk::manage(entry_), Gtk::PACK_EXPAND_WIDGET);
94                   entry_->set_has_frame(false);
95                   entry_->gobj()->is_cell_renderer = true; // XXX
96
97 */
98                 valuewidget=manage(new class Widget_ValueBase());
99                 valuewidget->inside_cellrenderer();
100                 add(*valuewidget);
101                 valuewidget->show();
102
103                 //set_flags(Gtk::CAN_FOCUS);
104                 //set_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
105
106                 /*
107                 set_events(//(Gdk::ALL_EVENTS_MASK)
108                 ~(      Gdk::EXPOSURE_MASK
109                         | Gdk::ENTER_NOTIFY_MASK
110                         | Gdk::LEAVE_NOTIFY_MASK
111                         | Gdk::FOCUS_CHANGE_MASK
112                         | Gdk::STRUCTURE_MASK
113                         | Gdk::PROPERTY_CHANGE_MASK
114                         | Gdk::VISIBILITY_NOTIFY_MASK
115                         | Gdk::PROXIMITY_IN_MASK
116                         | Gdk::PROXIMITY_OUT_MASK
117                         | Gdk::SUBSTRUCTURE_MASK
118                 )
119                 );
120                 */
121                 //signal_editing_done().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));
122                 //signal_remove_widget().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide));
123
124                 show_all_children();
125
126                 //signal_show().connect(sigc::mem_fun(*this, &ValueBase_Entry::grab_focus));
127         }
128         ~ValueBase_Entry()
129         {
130         }
131
132         void on_editing_done()
133         {
134                 hide();
135                 if(parent)parent->grab_focus();
136                 if(!edit_done_called)
137                 {
138                         edit_done_called=true;
139                         Gtk::CellEditable::on_editing_done();
140                 }
141                 else
142                 {
143                         synfig::error("on_editing_done(): Called twice!");
144                 }
145         }
146         void set_parent(Gtk::Widget*x) { parent=x; }
147         void on_remove_widget()
148         {
149                 hide();
150                 edit_done_called=true;
151                 if(parent)parent->grab_focus();
152                 Gtk::CellEditable::on_remove_widget();
153         }
154         void start_editing_vfunc(GdkEvent */*event*/)
155         {
156                 valuewidget->signal_activate().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::editing_done));
157                 show();
158                 //valuewidget->grab_focus();
159                 //get_window()->set_focus(*valuewidget);
160         }
161         bool on_event(GdkEvent *event)
162         {
163                 if(event->any.type==GDK_BUTTON_PRESS ||
164                         event->any.type==GDK_2BUTTON_PRESS ||
165                         event->any.type==GDK_KEY_PRESS ||
166                         event->any.type==GDK_KEY_RELEASE ||
167                         event->any.type==GDK_SCROLL ||
168                         event->any.type==GDK_3BUTTON_PRESS)
169                         return true;
170                 return Gtk::EventBox::on_event(event);
171         }
172         void on_grab_focus()
173         {
174                 Gtk::EventBox::on_grab_focus();
175                 if(valuewidget)
176                         valuewidget->grab_focus();
177         }
178         void set_path(const Glib::ustring &p)
179         {
180                 path=p;
181         }
182         void set_value(const synfig::ValueBase &data)
183         {
184                 if(valuewidget)
185                         valuewidget->set_value(data);
186                 //valuewidget->grab_focus();
187         }
188         void set_canvas(const etl::handle<synfig::Canvas> &data)
189         {
190                 assert(data);
191                 if(valuewidget)
192                         valuewidget->set_canvas(data);
193         }
194         void set_param_desc(const synfig::ParamDesc &data)
195         {
196                 if(valuewidget)
197                         valuewidget->set_param_desc(data);
198         }
199
200         const synfig::ValueBase &get_value()
201         {
202                 if(valuewidget)
203                         return valuewidget->get_value();
204
205                 warning("%s:%d this code shouldn't be reached", __FILE__, __LINE__);
206                 return *(new synfig::ValueBase());
207         }
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_inconsistent()=false;
285 }
286
287 CellRenderer_ValueBase::~CellRenderer_ValueBase()
288 {
289         if (getenv("SYNFIG_DEBUG_DESTRUCTORS"))
290                 synfig::info("CellRenderer_ValueBase::~CellRenderer_ValueBase(): Deleted");
291 }
292
293 void
294 CellRenderer_ValueBase::string_edited_(const Glib::ustring&path,const Glib::ustring&str)
295 {
296         ValueBase old_value=property_value_.get_value();
297         ValueBase value;
298
299         if(old_value.get_type()==ValueBase::TYPE_TIME)
300         {
301                 value=ValueBase(Time((String)str,get_canvas()->rend_desc().get_frame_rate()));
302         }
303         else
304                 value=ValueBase((String)str);
305
306         if(old_value!=value)
307                 signal_edited_(path,value);
308 }
309
310 void
311 CellRenderer_ValueBase::render_vfunc(
312                 const Glib::RefPtr<Gdk::Drawable>& window,
313                 Gtk::Widget& widget,
314                 const Gdk::Rectangle& background_area,
315                 const Gdk::Rectangle& ca,
316                 const Gdk::Rectangle& expose_area,
317                 Gtk::CellRendererState flags)
318 {
319         if(!window)
320                 return;
321 //      const unsigned int cell_xpad = property_xpad();
322 //      const unsigned int cell_ypad = property_ypad();
323
324         //int x_offset = 0, y_offset = 0;
325 //      int     width = ca.get_width();
326         int     height = ca.get_height();
327 //      get_size(widget, ca, x_offset, y_offset, width, height);
328
329 //      width  -= cell_xpad * 2;
330 //      height -= cell_ypad * 2;
331
332 //      if(width <= 0 || height <= 0)
333 //              return;
334
335         Gtk::StateType state = Gtk::STATE_INSENSITIVE;
336         if(property_editable())
337                 state = Gtk::STATE_NORMAL;
338         if((flags & Gtk::CELL_RENDERER_SELECTED) != 0)
339                 state = (widget.has_focus()) ? Gtk::STATE_SELECTED : Gtk::STATE_ACTIVE;
340
341         ValueBase data=property_value_.get_value();
342
343         switch(data.get_type())
344         {
345         case ValueBase::TYPE_REAL:
346                 if(((synfig::ParamDesc)property_param_desc_).get_is_distance())
347                 {
348                         Distance x(data.get(Real()),Distance::SYSTEM_UNITS);
349                         x.convert(App::distance_system,get_canvas()->rend_desc());
350                         property_text()=(Glib::ustring)x.get_string(6).c_str();
351                 }
352                 else
353                         property_text()=(Glib::ustring)strprintf("%.6f",data.get(Real()));
354                 break;
355         case ValueBase::TYPE_TIME:
356                 property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format());
357                 break;
358         case ValueBase::TYPE_ANGLE:
359                 property_text()=(Glib::ustring)strprintf("%.2f DEG",(Real)Angle::deg(data.get(Angle())).get());
360                 break;
361         case ValueBase::TYPE_INTEGER:
362                 if(((synfig::ParamDesc)property_param_desc_).get_hint()!="enum")
363                 {
364                         property_text()=(Glib::ustring)strprintf("%i",data.get(int()));
365                 }
366                 else
367                 {
368                         property_text()=(Glib::ustring)strprintf("(%i)",data.get(int()));
369                         std::list<synfig::ParamDesc::EnumData> enum_list=((synfig::ParamDesc)property_param_desc_).get_enum_list();
370                         std::list<synfig::ParamDesc::EnumData>::iterator iter;
371
372                         for(iter=enum_list.begin();iter!=enum_list.end();iter++)
373                                 if(iter->value==data.get(int()))
374                                 {
375                                         // don't show the key_board s_hortcut under_scores
376                                         String local_name = iter->local_name;
377                                         String::size_type pos = local_name.find_first_of('_');
378                                         if (pos != String::npos)
379                                                 property_text() = local_name.substr(0,pos) + local_name.substr(pos+1);
380                                         else
381                                                 property_text() = local_name;
382                                         break;
383                                 }
384                 }
385
386                 break;
387         case ValueBase::TYPE_VECTOR:
388                 {
389                         Vector vector=data.get(Vector());
390                         Distance x(vector[0],Distance::SYSTEM_UNITS),y(vector[1],Distance::SYSTEM_UNITS);
391                         x.convert(App::distance_system,get_canvas()->rend_desc());
392                         y.convert(App::distance_system,get_canvas()->rend_desc());
393                         property_text()=static_cast<Glib::ustring>(strprintf("%s,%s",x.get_string(6).c_str(),y.get_string(6).c_str()));
394                 }
395                 break;
396
397         case ValueBase::TYPE_STRING:
398
399                 if(data.get_type()==ValueBase::TYPE_STRING)
400                 {
401                         if(!data.get(synfig::String()).empty())
402                                 property_text()=static_cast<Glib::ustring>(data.get(synfig::String()));
403                         else
404                                 property_text()=Glib::ustring("<empty>");
405                 }
406                 break;
407         case ValueBase::TYPE_CANVAS:
408                 if(data.get(etl::handle<synfig::Canvas>()))
409                 {
410                         if(data.get(etl::handle<synfig::Canvas>())->is_inline())
411                                 property_text()=_("<Inline Canvas>");
412                         else
413                                 property_text()=(Glib::ustring)data.get(etl::handle<synfig::Canvas>())->get_id();
414                 }
415                 else
416                         property_text()="<No Image Selected>";
417                 break;
418         case ValueBase::TYPE_COLOR:
419                 {
420                         render_color_to_window(window,ca,data.get(Color()));
421                         return;
422                 }
423                 break;
424         case ValueBase::TYPE_BOOL:
425                 {
426                         widget.get_style()->paint_check(
427                                 Glib::RefPtr<Gdk::Window>::cast_static(window), state,
428                                 data.get(bool())?Gtk::SHADOW_IN:Gtk::SHADOW_OUT,
429                                 ca, widget, "cellcheck",
430                                 ca.get_x()/* + x_offset + cell_xpad*/,
431                                 ca.get_y()/* + y_offset + cell_ypad*/,
432                                 height-1,height-1);
433                         return;
434                 }
435                 break;
436         case ValueBase::TYPE_NIL:
437                 //property_text()=(Glib::ustring)" ";
438                 return;
439                 break;
440         case ValueBase::TYPE_SEGMENT:
441                 property_text()=(Glib::ustring)_("Segment");
442                 break;
443         case ValueBase::TYPE_GRADIENT:
444                 render_gradient_to_window(window,ca,data.get(Gradient()));
445                 return;
446                 break;
447         case ValueBase::TYPE_LIST:
448                 property_text()=(Glib::ustring)_("List");
449                 break;
450         case ValueBase::TYPE_BLINEPOINT:
451                 property_text()=(Glib::ustring)_("BLine Point");
452                 break;
453         default:
454                 property_text()=static_cast<Glib::ustring>(_("UNKNOWN"));
455                 break;
456         }
457         CellRendererText::render_vfunc(window,widget,background_area,ca,expose_area,flags);
458 }
459
460
461 /*
462 bool
463 CellRenderer_ValueBase::activate_vfunc( GdkEvent* event,
464         Gtk::Widget& widget,
465         const Glib::ustring& path,
466         const Gdk::Rectangle& background_area,
467         const Gdk::Rectangle& cell_area,
468         Gtk::CellRendererState flags)
469 {
470         ValueBase data=(ValueBase)property_value_.get_value();
471
472         switch(data.type)
473         {
474         case ValueBase::TYPE_BOOL:
475                 if(property_editable())
476                         signal_edited_(path,ValueBase(!data.get(bool())));
477         return true;
478         case ValueBase::TYPE_STRING:
479                 return CellRendererText::activate_vfunc(event,widget,path,background_area,cell_area,flags);
480         }
481         return false;
482 }
483 */
484
485 void
486 CellRenderer_ValueBase::gradient_edited(synfig::Gradient gradient, Glib::ustring path)
487 {
488         ValueBase old_value(property_value_.get_value());
489         ValueBase value(gradient);
490         if(old_value!=value)
491                 signal_edited_(path,value);
492 }
493
494 void
495 CellRenderer_ValueBase::color_edited(synfig::Color color, Glib::ustring path)
496 {
497         ValueBase old_value(property_value_.get_value());
498         ValueBase value(color);
499         if(old_value!=value)
500                 signal_edited_(path,value);
501 }
502
503 Gtk::CellEditable*
504 CellRenderer_ValueBase::start_editing_vfunc(
505         GdkEvent* event __attribute__ ((unused)),
506         Gtk::Widget& widget,
507         const Glib::ustring& path,
508         const Gdk::Rectangle& background_area __attribute__ ((unused)),
509         const Gdk::Rectangle& cell_area __attribute__ ((unused)),
510         Gtk::CellRendererState flags __attribute__ ((unused)))
511 {
512         // If we aren't editable, then there is nothing to do
513         if(!property_editable())
514                 return 0;
515
516         ValueBase data=property_value_.get_value();
517
518         switch(data.get_type())
519         {
520         case ValueBase::TYPE_BOOL:
521                 signal_edited_(path,ValueBase(!data.get(bool())));
522         return NULL;
523         //case ValueBase::TYPE_TIME:
524         //      property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format()|Time::FORMAT_FULL);
525         //      return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);
526
527         case ValueBase::TYPE_GRADIENT:
528                 App::dialog_gradient->reset();
529                 App::dialog_gradient->set_gradient(data.get(Gradient()));
530                 App::dialog_gradient->signal_edited().connect(
531                         sigc::bind(
532                                 sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::gradient_edited),
533                                 path
534                         )
535                 );
536                 App::dialog_gradient->present();
537
538                 return NULL;
539
540         case ValueBase::TYPE_COLOR:
541                 App::dialog_color->reset();
542                 App::dialog_color->set_color(data.get(Color()));
543                 App::dialog_color->signal_edited().connect(
544                         sigc::bind(
545                                 sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::color_edited),
546                                 path
547                         )
548                 );
549                 App::dialog_color->present();
550
551                 return NULL;
552         case ValueBase::TYPE_STRING:
553                 if(get_param_desc().get_hint()=="paragraph")
554                 {
555                         synfig::String string;
556                         string=data.get(string);
557                         if(get_paragraph(string))
558                                 signal_edited_(path,ValueBase(string));
559                         return NULL;
560                 }
561                 // if(get_param_desc().get_hint()!="filename")
562                         // return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags);
563         default:
564                 {
565                         assert(get_canvas());
566                         //delete value_entry;
567                         value_entry=manage(new ValueBase_Entry());
568                         value_entry->set_path(path);
569                         value_entry->set_canvas(get_canvas());
570                         value_entry->set_param_desc(get_param_desc());
571                         value_entry->set_value(data);
572                         value_entry->set_parent(&widget);
573                         value_entry->signal_editing_done().connect(sigc::mem_fun(*this, &CellRenderer_ValueBase::on_value_editing_done));
574                         return value_entry;
575                 }
576         }
577         return NULL;
578 }
579
580 void
581 CellRenderer_ValueBase::on_value_editing_done()
582 {
583         if(value_entry)
584         {
585                 ValueBase old_value(property_value_.get_value());
586                 ValueBase value(value_entry->get_value());
587
588                 if(old_value!=value)
589                         signal_edited_(value_entry->get_path(),value);
590
591                 //delete value_entry;
592                 //value_entry=0;
593         }
594 }