/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "propertiesdialog.h" #include "propbase.h" #include "widget/subpanel.h" #include "widget/wmisc.h" #include "util/matrix.h" #include "util/stringutil.h" #include "util/warning.h" #include #include #include #include #include #include #include #include #include "pptcore.h" #include "widget/spinner.h" #include "document/basicframe.h" #include "lengthunits.h" #include "config.h" // The basic properties that apply to all Pagent's. // Currently that means some kind of name, and the position and size of the // object. class PropBasic : public PropBase { enum ChangeId { NAME, LEFT, BOTTOM, WIDTH, HEIGHT, SCALEX, SCALEY, ROTATE, SHEAR, LOCKED, FLOWAROUND, MARGIN }; public: // | name: | "name of object" | // ----------------------------------------- // | left: | "left" | width: | "width" | // | right: | "right" | height: | "height" | PropBasic(const std::string &default_unit) :PropBase("_Basic"), object(0), e_left(0, true, &length_units, default_unit), e_bottom(0, true, &length_units, default_unit), e_width(0, true, &length_units, default_unit), e_height(0, true, &length_units, default_unit), e_scalex(0, true), e_scaley(0, true), e_rotate(0, true, &angle_units), e_shear(0, true), e_margin(0, true, &length_units, default_unit), c_flow("Text avoids object", 0), c_locked("Locked", 0) { Gtk::Label *label; Gtk::Box *line = manage(new Gtk::HBox(false, double_space)); line->pack_start(*(label = manage(new Gtk::Label("Object _name:", 0.0, 0.5, true))), Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_name); line->pack_start(e_name); // not sure if connecting to signal_activate adds anything e_name.signal_activate().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), NAME)); e_name.signal_focus_out_event().connect (sigc::hide (bind_return(bind(sigc::mem_fun(*this, &PropBasic::on_change), NAME), true))); pack_start(*line, Gtk::PACK_SHRINK); SubPanel *box = manage(new SubPanel("Geometry")); Glib::RefPtr sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); line = manage(new Gtk::HBox(false, double_space)); sizegroup->add_widget(*(label = manage(new Gtk::Label("_Left:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_left, Gtk::PACK_SHRINK); e_left.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), LEFT)); label->set_mnemonic_widget(e_left); sizegroup->add_widget(*(label = manage(new Gtk::Label("_Width:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_width, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_width); e_width.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), WIDTH)); box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); sizegroup->add_widget(*(label = manage(new Gtk::Label("_Bottom:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_bottom, Gtk::PACK_SHRINK); e_bottom.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), BOTTOM)); label->set_mnemonic_widget(e_bottom); sizegroup->add_widget(*(label = manage(new Gtk::Label("_Height:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_height, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_height); e_height.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), HEIGHT)); box->pack_start(*line, Gtk::PACK_SHRINK); pack_start(*box, Gtk::PACK_SHRINK); box = manage(new SubPanel("Transform")); line = manage(new Gtk::HBox(false, double_space)); sizegroup->add_widget(*(label = manage(new Gtk::Label("Scale _X", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_scalex, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_scalex); e_scalex.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), SCALEX)); sizegroup->add_widget(*(label = manage(new Gtk::Label("R_otate:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_rotate, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_rotate); e_rotate.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), ROTATE)); box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); sizegroup->add_widget(*(label = manage(new Gtk::Label("Scale _Y", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_scaley, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_scaley); e_scaley.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), SCALEY)); sizegroup->add_widget(*(label = manage(new Gtk::Label("_Shear:", 0.0, 0.5, true)))); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(e_shear, Gtk::PACK_SHRINK); label->set_mnemonic_widget(e_shear); e_shear.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), SHEAR)); box->pack_start(*line, Gtk::PACK_SHRINK); pack_start(*box, Gtk::PACK_SHRINK); pack_start(hsep2, Gtk::PACK_SHRINK); Gtk::HBox *flow_box = manage(new Gtk::HBox(false, double_space)); flow_box->pack_start(c_locked, Gtk::PACK_SHRINK, triple_space); c_locked.signal_toggled().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), LOCKED)); flow_box->pack_start(c_flow, Gtk::PACK_SHRINK, 0); c_flow.signal_toggled().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), FLOWAROUND)); flow_box->pack_start(e_margin, Gtk::PACK_SHRINK, 0); e_margin.signal_value_changed().connect (bind(sigc::mem_fun(*this, &PropBasic::on_change), MARGIN)); pack_start(*flow_box, Gtk::PACK_SHRINK); set_sensitive(false); } void on_geometry_changed() { update(); } void on_props_changed() { update(); } void setObject(Pagent* pagent) { geometry_changed_connection.disconnect(); props_changed_connection.disconnect(); object = pagent; if(object) { geometry_changed_connection = object->geometry_changed_signal.connect (sigc::mem_fun(*this, &PropBasic::on_geometry_changed)); props_changed_connection = object->props_changed_signal.connect (sigc::mem_fun(*this, &PropBasic::on_props_changed)); } set_sensitive(object); if(is_visible()) update(); } void update() { if(!object) { e_name.set_text(""); } else { // Todo: Use an "objecthider", so the toggle back works even if we get // an exception! Pagent* obj = object; object = 0; // Don't apply while updating e_name.set_text(obj->get_name()); c_locked.set_active(obj->get_lock()); // geometry const Matrix &m = obj->get_matrix(); const Vector size = obj->get_inherent_size(); e_left.set(m.tr_x()); e_bottom.set(m.tr_y()); e_width.set(size.x * m.sc_x()); e_height.set(size.y * m.sc_y()); // transform e_scalex.set(m.sc_x()); e_scaley.set(m.sc_y()); e_rotate.set(Matrix::rad2deg(m.rot())); e_shear.set(Matrix::rad2deg(m.sh())); const bool flowaround = obj->get_flow_around(); c_flow.set_active(flowaround); e_margin.set_sensitive(flowaround); e_margin.set(obj->get_obstacle_margin()); object = obj; } } void on_change(ChangeId what) { if(object) switch(what) { case NAME: object->set_name(e_name.get_text()); break; case LEFT: case BOTTOM: object->set_translation(Vector(e_left.get(), e_bottom.get())); break; case WIDTH: case HEIGHT: if(Pagent::Resizable* o = dynamic_cast(object)) o->set_size(e_width.get(), e_height.get()); else { const Vector size = object->get_inherent_size(); object->set_scaling(e_width.get() / size.x, e_height.get() / size.y); } break; case SCALEX: case SCALEY: object->set_scaling(e_scalex.get(), e_scaley.get()); break; case ROTATE: object->set_rotation(Matrix::deg2rad(e_rotate.get())); break; case SHEAR: object->set_shearing(Matrix::deg2rad(e_shear.get())); break; case LOCKED: object->set_lock(c_locked.get_active()); break; case FLOWAROUND: { const bool flow = c_flow.get_active(); e_margin.set_sensitive(flow); object->set_flow_around(flow); } break; case MARGIN: object->set_obstacle_margin(e_margin.get()); break; } } void set_sensitive(bool sensitive) { getLabel().set_sensitive(sensitive); Gtk::VBox::set_sensitive(sensitive); } private: Pagent* object; Gtk::HSeparator hsep1, hsep2; Gtk::Entry e_name; Spinner e_left, e_bottom, e_width, e_height, e_scalex, e_scaley, e_rotate, e_shear, e_margin; Gtk::CheckButton c_flow, c_locked; sigc::connection geometry_changed_connection, props_changed_connection; }; // - - - - back to the actual PropertiesDialog implementation - - - - PropertiesDialog *PropertiesDialog::_instance = 0; PropertiesDialog &PropertiesDialog::instance() { if(!_instance) _instance = new PropertiesDialog(); return *_instance; } PropertiesDialog::PropertiesDialog() : UtilityWindow("Object properties"), document(0) { add(book); book.signal_switch_page().connect (sigc::mem_fun(*this, &PropertiesDialog::show_page_contents)); PropBase* prop = new PropBasic(config.LengthUnit.values.front()); pages.push_back(prop); book.append_page(*prop, prop->getLabel()); for(PptCore::MetaMap::const_iterator i = core.m_begin(); i != core.m_end(); ++i) { if(PropBase* prop = i->second->getProp()) { pages.push_back(prop); book.append_page(*prop, prop->getLabel()); } } // listen to selection change signals: Document::selection_changed_signal.connect (sigc::mem_fun(*this, &PropertiesDialog::select_change)); } PropertiesDialog::~PropertiesDialog() {} void PropertiesDialog::show_raise() { show_all(); UtilityWindow::show_raise(); update(); } void PropertiesDialog::set_document(DocRef document_) { document = document_; update(); } void PropertiesDialog::update() { Document::Selection all_selected; if(document) all_selected = document->selected(); // Note: If there is more than one object selected, no properties are shown. // Maybe we should show the properties that are common // to all selected objects. Pagent* pagent = (all_selected.size()!=1 ? 0 : all_selected.front()); for(std::vector::const_iterator i = pages.begin(); i != pages.end(); ++i) (*i)->setObject(pagent); show_page_contents(0, book.get_current_page()); } void PropertiesDialog::select_change(DocRef doc) { if(document == doc) update(); } // this is part of the workaround for the problem with Gtk::Entry and // hidden tabs, see propbase.cc void PropertiesDialog::show_page_contents(GtkNotebookPage*, guint index) { dynamic_cast(book.get_nth_page(index))->show_all_children(); }