/* === S Y N F I G ========================================================= */ /*! \file cellrenderer_value.cpp ** \brief Template File ** ** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley ** Copyright (c) 2007, 2008 Chris Moore ** Copyright (c) 2011 Carlos López ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License, or (at your option) any later version. ** ** This package is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** \endlegal */ /* ========================================================================= */ /* === H E A D E R S ======================================================= */ #ifdef USING_PCH # include "pch.h" #else #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "app.h" #include "widgets/widget_value.h" #include "widgets/widget_vector.h" #include "widgets/widget_filename.h" #include "widgets/widget_enum.h" #include "widgets/widget_color.h" #include "widgets/widget_canvaschooser.h" #include "widgets/widget_time.h" #include "cellrenderer_gradient.h" #include "cellrenderer_value.h" #include #include "widgets/widget_gradient.h" #include "dialogs/dialog_gradient.h" #include "dialogs/dialog_color.h" #include #include #include "general.h" #endif using namespace synfig; using namespace etl; using namespace std; using namespace studio; /* === M A C R O S ========================================================= */ #define DIGITS 15 /* === G L O B A L S ======================================================= */ #if GLIB_CHECK_VERSION(2, 37, 5) class studio::ValueBase_Entry : public Gtk::CellEditable, public Gtk::EventBox #else class studio::ValueBase_Entry : public Gtk::EventBox, public Gtk::CellEditable #endif { Glib::ustring path; Widget_ValueBase *valuewidget; bool edit_done_called; Gtk::Widget *parent; public: #if GLIB_CHECK_VERSION(2, 37, 5) ValueBase_Entry(): Gtk::CellEditable (), Gtk::EventBox (), Glib::ObjectBase (typeid(ValueBase_Entry)) #else ValueBase_Entry(): Glib::ObjectBase (typeid(ValueBase_Entry)), Gtk::CellEditable (), Gtk::EventBox () #endif { parent=0; edit_done_called=false; /* Gtk::HBox *const hbox = new Gtk::HBox(false, 0); add(*Gtk::manage(hbox)); Gtk::Entry *entry_ = new Gtk::Entry(); entry_->set_text("bleh"); hbox->pack_start(*Gtk::manage(entry_), Gtk::PACK_EXPAND_WIDGET); entry_->set_has_frame(false); entry_->gobj()->is_cell_renderer = true; // XXX */ valuewidget=manage(new class Widget_ValueBase()); valuewidget->inside_cellrenderer(); add(*valuewidget); valuewidget->show(); //set_can_focus(true); //set_events(Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); /* set_events(//(Gdk::ALL_EVENTS_MASK) ~( Gdk::EXPOSURE_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::FOCUS_CHANGE_MASK | Gdk::STRUCTURE_MASK | Gdk::PROPERTY_CHANGE_MASK | Gdk::VISIBILITY_NOTIFY_MASK | Gdk::PROXIMITY_IN_MASK | Gdk::PROXIMITY_OUT_MASK | Gdk::SUBSTRUCTURE_MASK ) ); */ //signal_editing_done().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide)); //signal_remove_widget().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::hide)); show_all_children(); //signal_show().connect(sigc::mem_fun(*this, &ValueBase_Entry::grab_focus)); } ~ValueBase_Entry() { } void on_editing_done() { hide(); if(parent)parent->grab_focus(); if(!edit_done_called) { edit_done_called=true; Gtk::CellEditable::on_editing_done(); } else { synfig::error("on_editing_done(): Called twice!"); } } void set_parent(Gtk::Widget*x) { parent=x; } void on_remove_widget() { hide(); edit_done_called=true; if(parent)parent->grab_focus(); Gtk::CellEditable::on_remove_widget(); } void start_editing_vfunc(GdkEvent */*event*/) { valuewidget->signal_activate().connect(sigc::mem_fun(*this, &studio::ValueBase_Entry::editing_done)); show(); //valuewidget->grab_focus(); //get_window()->set_focus(*valuewidget); } bool on_event(GdkEvent *event) { if(event->any.type==GDK_BUTTON_PRESS || event->any.type==GDK_2BUTTON_PRESS || event->any.type==GDK_KEY_PRESS || event->any.type==GDK_KEY_RELEASE || event->any.type==GDK_SCROLL || event->any.type==GDK_3BUTTON_PRESS) return true; return Gtk::EventBox::on_event(event); } void on_grab_focus() { Gtk::EventBox::on_grab_focus(); if(valuewidget) valuewidget->grab_focus(); } void set_path(const Glib::ustring &p) { path=p; } void set_value(const synfig::ValueBase &data) { if(valuewidget) valuewidget->set_value(data); //valuewidget->grab_focus(); } void set_canvas(const etl::handle &data) { assert(data); if(valuewidget) valuewidget->set_canvas(data); } void set_param_desc(const synfig::ParamDesc &data) { if(valuewidget) valuewidget->set_param_desc(data); } void set_value_desc(const synfigapp::ValueDesc &data) { if(valuewidget) valuewidget->set_value_desc(data); } void set_child_param_desc(const synfig::ParamDesc &data) { if(valuewidget) valuewidget->set_child_param_desc(data); } const synfig::ValueBase &get_value() { if(valuewidget) return valuewidget->get_value(); warning("%s:%d this code shouldn't be reached", __FILE__, __LINE__); return *(new synfig::ValueBase()); } const Glib::ustring &get_path() { return path; } }; /* === P R O C E D U R E S ================================================= */ bool get_paragraph(synfig::String& text) { return App::dialog_paragraph(_("Text Paragraph"), _("Enter text here:"), text); } /* === M E T H O D S ======================================================= */ #if GLIB_CHECK_VERSION(2, 37, 5) CellRenderer_ValueBase::CellRenderer_ValueBase(): Gtk::CellRendererText (), property_value_ (*this,"value",synfig::ValueBase()), property_canvas_(*this,"canvas",etl::handle()), property_param_desc_(*this,"param_desc",synfig::ParamDesc()), property_value_desc_(*this,"value_desc",synfigapp::ValueDesc()), property_child_param_desc_(*this,"child_param_desc", synfig::ParamDesc()), Glib::ObjectBase (typeid(CellRenderer_ValueBase)) #else CellRenderer_ValueBase::CellRenderer_ValueBase(): Glib::ObjectBase (typeid(CellRenderer_ValueBase)), Gtk::CellRendererText (), property_value_ (*this,"value",synfig::ValueBase()), property_canvas_(*this,"canvas",etl::handle()), property_param_desc_(*this,"param_desc",synfig::ParamDesc()), property_value_desc_(*this,"value_desc",synfigapp::ValueDesc()), property_child_param_desc_(*this,"child_param_desc", synfig::ParamDesc()) #endif { CellRendererText::signal_edited().connect(sigc::mem_fun(*this,&CellRenderer_ValueBase::string_edited_)); Pango::AttrList attr_list; { Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*8)); pango_size.set_start_index(0); pango_size.set_end_index(64); attr_list.change(pango_size); } property_attributes()=attr_list; property_foreground()=Glib::ustring("#7f7f7f"); property_inconsistent()=false; } CellRenderer_ValueBase::~CellRenderer_ValueBase() { if (getenv("SYNFIG_DEBUG_DESTRUCTORS")) synfig::info("CellRenderer_ValueBase::~CellRenderer_ValueBase(): Deleted"); } void CellRenderer_ValueBase::string_edited_(const Glib::ustring&path,const Glib::ustring&str) { ValueBase old_value=property_value_.get_value(); ValueBase value; if(old_value.get_type()==type_time) { value=ValueBase(Time((String)str,get_canvas()->rend_desc().get_frame_rate())); } else value=ValueBase((String)str); if(old_value!=value) signal_edited_(path,value); } void CellRenderer_ValueBase::render_vfunc( const ::Cairo::RefPtr< ::Cairo::Context>& cr, Gtk::Widget& widget, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) { if(!cr) return; Gdk::Rectangle aligned_area; get_aligned_area(widget, flags, cell_area, aligned_area); int height = cell_area.get_height(); /* TODO: is widget state equals this state variable? for checkbox only Gtk::StateType state = Gtk::STATE_INSENSITIVE; if(property_editable()) state = Gtk::STATE_NORMAL; if((flags & Gtk::CELL_RENDERER_SELECTED) != 0) state = (widget.has_focus()) ? Gtk::STATE_SELECTED : Gtk::STATE_ACTIVE; */ ValueBase data=property_value_.get_value(); Type &type(data.get_type()); if (type == type_real) { if(((synfig::ParamDesc)property_param_desc_).get_is_distance()) { Distance x(data.get(Real()),Distance::SYSTEM_UNITS); x.convert(App::distance_system,get_canvas()->rend_desc()); property_text()=(Glib::ustring)x.get_string(6).c_str(); } else property_text()=(Glib::ustring)strprintf("%.6f",data.get(Real())); } else if (type == type_time) { property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format()); } else if (type == type_angle) { property_text()=(Glib::ustring)strprintf("%.2fᵒ",(Real)Angle::deg(data.get(Angle())).get()); } else if (type == type_integer) { String param_hint, child_param_hint; param_hint=get_param_desc().get_hint(); child_param_hint=get_child_param_desc().get_hint(); if(param_hint!="enum" && child_param_hint!="enum") { property_text()=(Glib::ustring)strprintf("%i",data.get(int())); } else { property_text()=(Glib::ustring)strprintf("(%i)",data.get(int())); std::list enum_list; if(param_hint=="enum") enum_list=((synfig::ParamDesc)property_param_desc_).get_enum_list(); else if(child_param_hint=="enum") enum_list=((synfig::ParamDesc)property_child_param_desc_).get_enum_list(); std::list::iterator iter; for(iter=enum_list.begin();iter!=enum_list.end();iter++) if(iter->value==data.get(int())) { // don't show the key_board s_hortcut under_scores String local_name = iter->local_name; String::size_type pos = local_name.find_first_of('_'); if (pos != String::npos) property_text() = local_name.substr(0,pos) + local_name.substr(pos+1); else property_text() = local_name; break; } } } else if (type == type_vector) { Vector vector=data.get(Vector()); Distance x(vector[0],Distance::SYSTEM_UNITS),y(vector[1],Distance::SYSTEM_UNITS); x.convert(App::distance_system,get_canvas()->rend_desc()); y.convert(App::distance_system,get_canvas()->rend_desc()); property_text()=static_cast(strprintf("%s,%s",x.get_string(6).c_str(),y.get_string(6).c_str())); } else if (type == type_transformation) { const Transformation &transformation=data.get(Transformation()); const Vector &offset = transformation.offset; const Angle::deg angle(transformation.angle); const Vector &scale = transformation.scale; Distance x(offset[0],Distance::SYSTEM_UNITS),y(offset[1],Distance::SYSTEM_UNITS); x.convert(App::distance_system,get_canvas()->rend_desc()); y.convert(App::distance_system,get_canvas()->rend_desc()); Distance sx(scale[0],Distance::SYSTEM_UNITS),sy(scale[1],Distance::SYSTEM_UNITS); sx.convert(App::distance_system,get_canvas()->rend_desc()); sy.convert(App::distance_system,get_canvas()->rend_desc()); property_text()=static_cast(strprintf( "%s,%s,%.2fᵒ,%s,%s", x.get_string(6).c_str(), y.get_string(6).c_str(), (Real)angle.get(), sx.get_string(6).c_str(), sy.get_string(6).c_str() )); } else if (type == type_string) { if(!data.get(synfig::String()).empty()) property_text()=static_cast(data.get(synfig::String())); else property_text()=Glib::ustring(""); } else if (type == type_canvas) { if(data.get(etl::handle())) { if(data.get(etl::handle())->is_inline()) property_text()=_(""); else property_text()=(Glib::ustring)data.get(etl::handle())->get_id(); } else property_text()=_(""); } else if (type == type_color) { render_color_to_window(cr,cell_area,data.get(Color())); return; } else if (type == type_bool) { Glib::RefPtr context = widget.get_style_context(); context->context_save(); Gtk::StateFlags state = get_state(widget, flags); #if (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 14) state &= ~(Gtk::STATE_FLAG_INCONSISTENT | Gtk::STATE_FLAG_ACTIVE | Gtk::STATE_FLAG_CHECKED); #else state &= ~(Gtk::STATE_FLAG_INCONSISTENT | Gtk::STATE_FLAG_ACTIVE); #endif if ((flags & Gtk::CELL_RENDERER_SELECTED) != 0 && widget.has_focus()) state |= Gtk::STATE_FLAG_SELECTED; if (!property_editable()) state |= Gtk::STATE_FLAG_INSENSITIVE; if (data.get(bool())) #if (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 14) state |= Gtk::STATE_FLAG_CHECKED; #else state |= Gtk::STATE_FLAG_ACTIVE; #endif cr->save(); Gdk::Cairo::add_rectangle_to_path(cr, cell_area); cr->clip(); context->add_class("check"); context->set_state(state); context->render_check( cr, aligned_area.get_x(), aligned_area.get_y(), aligned_area.get_height(), aligned_area.get_height() ); cr->restore(); context->context_restore(); return; } else if (type == type_nil) { //property_text()=(Glib::ustring)" "; return; } else if (type == type_gradient) { render_gradient_to_window(cr,cell_area,data.get(Gradient())); return; } else if (type == type_bone_object || type == type_segment || type == type_list || type == type_bline_point || type == type_width_point || type == type_dash_item) { property_text()=(Glib::ustring)(data.get_type().description.local_name); } else if (type == type_bone_valuenode) { ValueNode_Bone::Handle bone_node(data.get(ValueNode_Bone::Handle())); String name(_("No Parent")); if (!bone_node->is_root()) { name = (*(bone_node->get_link("name")))(get_canvas()->get_time()).get(String()); if (name.empty()) name = bone_node->get_guid().get_string(); } property_text()=(Glib::ustring)(name); } else { property_text()=static_cast(type.description.local_name); } CellRendererText::render_vfunc(cr,widget,background_area,cell_area,flags); } /* bool CellRenderer_ValueBase::activate_vfunc( GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) { ValueBase data=(ValueBase)property_value_.get_value(); if (data.type == type_bool) { if(property_editable()) signal_edited_(path,ValueBase(!data.get(bool()))); return true; } else if (data.type == type_string) { return CellRendererText::activate_vfunc(event,widget,path,background_area,cell_area,flags); } return false; } */ void CellRenderer_ValueBase::gradient_edited(synfig::Gradient gradient, Glib::ustring path) { ValueBase old_value(property_value_.get_value()); ValueBase value(gradient); if(old_value!=value) signal_edited_(path,value); } void CellRenderer_ValueBase::color_edited(synfig::Color color, Glib::ustring path) { ValueBase old_value(property_value_.get_value()); ValueBase value(color); if(old_value!=value) signal_edited_(path,value); } Gtk::CellEditable* CellRenderer_ValueBase::start_editing_vfunc( GdkEvent* event __attribute__ ((unused)), Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area __attribute__ ((unused)), const Gdk::Rectangle& cell_area __attribute__ ((unused)), Gtk::CellRendererState flags __attribute__ ((unused))) { edit_value_done_called = false; // If we aren't editable, then there is nothing to do if(!property_editable()) return 0; ValueBase data=property_value_.get_value(); Type &type(data.get_type()); if (type == type_bool) { signal_edited_(path,ValueBase(!data.get(bool()))); return NULL; } //else //if (type == type_time) //{ // property_text()=(Glib::ustring)data.get(Time()).get_string(get_canvas()->rend_desc().get_frame_rate(),App::get_time_format()|Time::FORMAT_FULL); // return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags); //} else if (type == type_gradient) { App::dialog_gradient->reset(); App::dialog_gradient->set_gradient(data.get(Gradient())); App::dialog_gradient->signal_edited().connect( sigc::bind( sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::gradient_edited), path ) ); App::dialog_gradient->set_default_button_set_sensitive(true); App::dialog_gradient->present(); return NULL; } else if (type == type_color) { App::dialog_color->reset(); App::dialog_color->set_color(data.get(Color())); App::dialog_color->signal_edited().connect( sigc::bind( sigc::mem_fun(*this,&studio::CellRenderer_ValueBase::color_edited), path ) ); App::dialog_color->present(); return NULL; } else if (type == type_string && get_param_desc().get_hint()=="paragraph") { synfig::String string; string=data.get(string); if(get_paragraph(string)) signal_edited_(path,ValueBase(string)); return NULL; } // if (type == type_string) && (get_param_desc().get_hint()!="filename") // return CellRendererText::start_editing_vfunc(event,widget,path,background_area,cell_area,flags); else { assert(get_canvas()); //delete value_entry; value_entry=manage(new ValueBase_Entry()); value_entry->set_path(path); value_entry->set_canvas(get_canvas()); value_entry->set_param_desc(get_param_desc()); value_entry->set_value_desc(get_value_desc()); value_entry->set_child_param_desc(get_child_param_desc()); value_entry->set_value(data); value_entry->set_parent(&widget); value_entry->signal_editing_done().connect(sigc::mem_fun(*this, &CellRenderer_ValueBase::on_value_editing_done)); return value_entry; } return NULL; } void CellRenderer_ValueBase::on_value_editing_done() { if (edit_value_done_called) { synfig::error("on_value_editing_done(): Called twice!"); return; } edit_value_done_called = true; if(value_entry) { ValueBase old_value(property_value_.get_value()); ValueBase value(value_entry->get_value()); if(old_value!=value) signal_edited_(value_entry->get_path(),value); //delete value_entry; //value_entry=0; } }