/** * \brief Inkscape Preferences dialog * * Authors: * Marco Scholten * Bruno Dilly * * Copyright (C) 2004, 2006, 2007 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "prefs-utils.h" #include "ui/widget/preferences-widget.h" #include "verbs.h" #include "selcue.h" #include #include "enums.h" #include "inkscape.h" #include "desktop-handles.h" #include "message-stack.h" #include "style.h" #include "selection.h" #include "selection-chemistry.h" #include "xml/repr.h" using namespace Inkscape::UI::Widget; namespace Inkscape { namespace UI { namespace Widget { DialogPage::DialogPage() { this->set_border_width(12); this->set_col_spacings(12); this->set_row_spacings(6); } void DialogPage::add_line(bool indent, const Glib::ustring label, Gtk::Widget& widget, const Glib::ustring suffix, const Glib::ustring& tip, bool expand_widget) { int start_col; int row = this->property_n_rows(); Gtk::Widget* w; if (expand_widget) { w = &widget; } else { Gtk::HBox* hb = Gtk::manage(new Gtk::HBox()); hb->set_spacing(12); hb->pack_start(widget,false,false); w = (Gtk::Widget*) hb; } if (label != "") { Gtk::Label* label_widget; label_widget = Gtk::manage(new Gtk::Label(label , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true)); label_widget->set_mnemonic_widget(widget); if (indent) { Gtk::Alignment* alignment = Gtk::manage(new Gtk::Alignment()); alignment->set_padding(0, 0, 12, 0); alignment->add(*label_widget); this->attach(*alignment , 0, 1, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0); } else this->attach(*label_widget , 0, 1, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0); start_col = 1; } else start_col = 0; if (start_col == 0 && indent) //indent this widget { Gtk::Alignment* alignment = Gtk::manage(new Gtk::Alignment()); alignment->set_padding(0, 0, 12, 0); alignment->add(*w); this->attach(*alignment, start_col, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::AttachOptions(), 0, 0); } else { this->attach(*w, start_col, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::AttachOptions(), 0, 0); } if (suffix != "") { Gtk::Label* suffix_widget = Gtk::manage(new Gtk::Label(suffix , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true)); if (expand_widget) this->attach(*suffix_widget, 2, 3, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0); else ((Gtk::HBox*)w)->pack_start(*suffix_widget,false,false); } if (tip != "") { _tooltips.set_tip (widget, tip); } } void DialogPage::add_group_header(Glib::ustring name) { int row = this->property_n_rows(); if (name != "") { Gtk::Label* label_widget = Gtk::manage(new Gtk::Label(Glib::ustring(/*"*/"") + name + Glib::ustring(""/*"*/) , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true)); label_widget->set_use_markup(true); this->attach(*label_widget , 0, 4, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0); if (row != 1) this->set_row_spacing(row - 1, 18); } } void DialogPage::set_tip(Gtk::Widget& widget, const Glib::ustring& tip) { _tooltips.set_tip (widget, tip); } void PrefCheckButton::init(const Glib::ustring& label, const std::string& prefs_path, const std::string& attr, bool default_value) { _prefs_path = prefs_path; _attr = attr; this->set_label(label); this->set_active( prefs_get_int_attribute (_prefs_path.c_str(), _attr.c_str(), (int)default_value) ); } void PrefCheckButton::on_toggled() { if (this->is_visible()) //only take action if the user toggled it { prefs_set_int_attribute (_prefs_path.c_str(), _attr.c_str(), (int) this->get_active()); } } void PrefRadioButton::init(const Glib::ustring& label, const std::string& prefs_path, const std::string& attr, const std::string& string_value, bool default_value, PrefRadioButton* group_member) { (void)default_value; _value_type = VAL_STRING; _prefs_path = prefs_path; _attr = attr; _string_value = string_value; this->set_label(label); if (group_member) { Gtk::RadioButtonGroup rbg = group_member->get_group(); this->set_group(rbg); } const gchar* val = prefs_get_string_attribute( _prefs_path.c_str(), _attr.c_str() ); if ( val ) this->set_active( std::string( val ) == _string_value); else this->set_active( false ); } void PrefRadioButton::init(const Glib::ustring& label, const std::string& prefs_path, const std::string& attr, int int_value, bool default_value, PrefRadioButton* group_member) { _value_type = VAL_INT; _prefs_path = prefs_path; _attr = attr; _int_value = int_value; this->set_label(label); if (group_member) { Gtk::RadioButtonGroup rbg = group_member->get_group(); this->set_group(rbg); } if (default_value) this->set_active( prefs_get_int_attribute( _prefs_path.c_str(), _attr.c_str(), int_value ) == _int_value); else this->set_active( prefs_get_int_attribute( _prefs_path.c_str(), _attr.c_str(), int_value + 1 )== _int_value); } void PrefRadioButton::on_toggled() { this->changed_signal.emit(this->get_active()); if (this->is_visible() && this->get_active() ) //only take action if toggled by user (to active) { if ( _value_type == VAL_STRING ) prefs_set_string_attribute ( _prefs_path.c_str(), _attr.c_str(), _string_value.c_str()); else if ( _value_type == VAL_INT ) prefs_set_int_attribute ( _prefs_path.c_str(), _attr.c_str(), _int_value); } } void PrefSpinButton::init(const std::string& prefs_path, const std::string& attr, double lower, double upper, double step_increment, double page_increment, double default_value, bool is_int, bool is_percent) { _prefs_path = prefs_path; _attr = attr; _is_int = is_int; _is_percent = is_percent; double value; if (is_int) if (is_percent) value = 100 * prefs_get_double_attribute_limited (prefs_path.c_str(), attr.c_str(), default_value, lower/100.0, upper/100.0); else value = (double) prefs_get_int_attribute_limited (prefs_path.c_str(), attr.c_str(), (int) default_value, (int) lower, (int) upper); else value = prefs_get_double_attribute_limited (prefs_path.c_str(), attr.c_str(), default_value, lower, upper); this->set_range (lower, upper); this->set_increments (step_increment, page_increment); this->set_numeric(); this->set_value (value); this->set_width_chars(6); if (is_int) this->set_digits(0); else if (step_increment < 0.1) this->set_digits(4); else this->set_digits(2); } void PrefSpinButton::on_value_changed() { if (this->is_visible()) //only take action if user changed value { if (_is_int) if (_is_percent) prefs_set_double_attribute(_prefs_path.c_str(), _attr.c_str(), this->get_value()/100.0); else prefs_set_int_attribute(_prefs_path.c_str(), _attr.c_str(), (int) this->get_value()); else prefs_set_double_attribute (_prefs_path.c_str(), _attr.c_str(), this->get_value()); } } const double ZoomCorrRuler::textsize = 7; const double ZoomCorrRuler::textpadding = 5; ZoomCorrRuler::ZoomCorrRuler(int width, int height) : _unitconv(1.0), _border(5) { set_size(width, height); } void ZoomCorrRuler::set_size(int x, int y) { _min_width = x; _height = y; set_size_request(x + _border*2, y + _border*2); } // The following two functions are borrowed from 2geom's toy-framework-2; if they are useful in // other locations, we should perhaps make them (or adapted versions of them) publicly available static void draw_text(cairo_t *cr, Geom::Point loc, const char* txt, bool bottom = "false", double fontsize = ZoomCorrRuler::textsize, std::string fontdesc = "Sans") { PangoLayout* layout = pango_cairo_create_layout (cr); pango_layout_set_text(layout, txt, -1); // set font and size std::ostringstream sizestr; sizestr << fontsize; fontdesc = fontdesc + " " + sizestr.str(); PangoFontDescription *font_desc = pango_font_description_from_string(fontdesc.c_str()); pango_layout_set_font_description(layout, font_desc); pango_font_description_free (font_desc); PangoRectangle logical_extent; pango_layout_get_pixel_extents(layout, NULL, &logical_extent); cairo_move_to(cr, loc[Geom::X], loc[Geom::Y] - (bottom ? logical_extent.height : 0)); pango_cairo_show_layout(cr, layout); } static void draw_number(cairo_t *cr, Geom::Point pos, double num) { std::ostringstream number; number << num; draw_text(cr, pos, number.str().c_str(), true); } /* * \arg dist The distance between consecutive minor marks * \arg major_interval Number of marks after which to draw a major mark */ void ZoomCorrRuler::draw_marks(Cairo::RefPtr cr, double dist, int major_interval) { const double zoomcorr = prefs_get_double_attribute("options.zoomcorrection", "value", 1.0); double mark = 0; int i = 0; while (mark <= _drawing_width) { cr->move_to(mark, _height); if ((i % major_interval) == 0) { // major mark cr->line_to(mark, 0); Geom::Point textpos(mark + 3, ZoomCorrRuler::textsize + ZoomCorrRuler::textpadding); draw_number(cr->cobj(), textpos, dist * i); } else { // minor mark cr->line_to(mark, ZoomCorrRuler::textsize + 2 * ZoomCorrRuler::textpadding); } mark += dist * zoomcorr / _unitconv; ++i; } } void ZoomCorrRuler::redraw() { Glib::RefPtr window = get_window(); Cairo::RefPtr cr = window->create_cairo_context(); int w, h; window->get_size(w, h); _drawing_width = w - _border * 2; cr->set_source_rgb(1.0, 1.0, 1.0); cr->set_fill_rule(Cairo::FILL_RULE_WINDING); cr->rectangle(0, 0, w, _height + _border*2); cr->fill(); cr->set_source_rgb(0.0, 0.0, 0.0); cr->set_line_width(0.5); cr->translate(_border, _border); // so that we have a small white border around the ruler cr->move_to (0, _height); cr->line_to (_drawing_width, _height); const char *abbr = prefs_get_string_attribute("options.zoomcorrection", "unit"); if (!strcmp(abbr, "cm")) { draw_marks(cr, 0.1, 10); } else if (!strcmp(abbr, "ft")) { draw_marks(cr, 1/12.0, 12); } else if (!strcmp(abbr, "in")) { draw_marks(cr, 0.25, 4); } else if (!strcmp(abbr, "m")) { draw_marks(cr, 1/10.0, 10); } else if (!strcmp(abbr, "mm")) { draw_marks(cr, 10, 10); } else if (!strcmp(abbr, "pc")) { draw_marks(cr, 1, 10); } else if (!strcmp(abbr, "pt")) { draw_marks(cr, 10, 10); } else if (!strcmp(abbr, "px")) { draw_marks(cr, 10, 10); } else { draw_marks(cr, 1, 1); } cr->stroke(); } bool ZoomCorrRuler::on_expose_event(GdkEventExpose *event) { this->redraw(); return true; } void ZoomCorrRulerSlider::on_slider_value_changed() { if (this->is_visible() || freeze) //only take action if user changed value { freeze = true; prefs_set_double_attribute ("options.zoomcorrection", "value", _slider.get_value() / 100.0); _sb.set_value(_slider.get_value()); _ruler.redraw(); freeze = false; } } void ZoomCorrRulerSlider::on_spinbutton_value_changed() { if (this->is_visible() || freeze) //only take action if user changed value { freeze = true; prefs_set_double_attribute ("options.zoomcorrection", "value", _sb.get_value() / 100.0); _slider.set_value(_sb.get_value()); _ruler.redraw(); freeze = false; } } void ZoomCorrRulerSlider::on_unit_changed() { if (GPOINTER_TO_INT(_unit.get_data("sensitive")) == 0) { // when the unit menu is initialized, the unit is set to the default but // it needs to be reset later so we don't perform the change in this case return; } prefs_set_string_attribute ("options.zoomcorrection", "unit", _unit.getUnitAbbr().c_str()); double conv = _unit.getConversion(_unit.getUnitAbbr(), "px"); _ruler.set_unit_conversion(conv); if (_ruler.is_visible()) { _ruler.redraw(); } } void ZoomCorrRulerSlider::init(int ruler_width, int ruler_height, double lower, double upper, double step_increment, double page_increment, double default_value) { double value = prefs_get_double_attribute_limited ("options.zoomcorrection", "value", default_value, lower, upper) * 100.0; freeze = false; _ruler.set_size(ruler_width, ruler_height); _slider.set_size_request(_ruler.width(), -1); _slider.set_range (lower, upper); _slider.set_increments (step_increment, page_increment); _slider.set_value (value); _slider.set_digits(2); _slider.signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_slider_value_changed)); _sb.signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_spinbutton_value_changed)); _unit.signal_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_unit_changed)); _sb.set_range (lower, upper); _sb.set_increments (step_increment, page_increment); _sb.set_value (value); _sb.set_digits(2); _unit.set_data("sensitive", GINT_TO_POINTER(0)); _unit.setUnitType(UNIT_TYPE_LINEAR); _unit.set_data("sensitive", GINT_TO_POINTER(1)); _unit.setUnit(prefs_get_string_attribute ("options.zoomcorrection", "unit")); Gtk::Table *table = Gtk::manage(new Gtk::Table()); Gtk::Alignment *alignment1 = Gtk::manage(new Gtk::Alignment(0.5,1,0,0)); Gtk::Alignment *alignment2 = Gtk::manage(new Gtk::Alignment(0.5,1,0,0)); alignment1->add(_sb); alignment2->add(_unit); table->attach(_slider, 0, 1, 0, 1); table->attach(*alignment1, 1, 2, 0, 1, static_cast(0)); table->attach(_ruler, 0, 1, 1, 2); table->attach(*alignment2, 1, 2, 1, 2, static_cast(0)); this->pack_start(*table, Gtk::PACK_EXPAND_WIDGET); } void PrefCombo::init(const std::string& prefs_path, const std::string& attr, Glib::ustring labels[], int values[], int num_items, int default_value) { _prefs_path = prefs_path; _attr = attr; int row = 0; int value = prefs_get_int_attribute (_prefs_path.c_str(), _attr.c_str(), default_value); for (int i = 0 ; i < num_items; ++i) { this->append_text(labels[i]); _values.push_back(values[i]); if (value == values[i]) row = i; } this->set_active(row); } void PrefCombo::on_changed() { if (this->is_visible()) //only take action if user changed value { prefs_set_int_attribute (_prefs_path.c_str(), _attr.c_str(), _values[this->get_active_row_number()]); } } void PrefEntryButtonHBox::init(const std::string& prefs_path, const std::string& attr, bool visibility, gchar* default_string) { _prefs_path = prefs_path; _attr = attr; _default_string = default_string; relatedEntry = new Gtk::Entry(); relatedButton = new Gtk::Button(_("Reset")); relatedEntry->set_invisible_char('*'); relatedEntry->set_visibility(visibility); relatedEntry->set_text(prefs_get_string_attribute(_prefs_path.c_str(), _attr.c_str())); this->pack_start(*relatedEntry); this->pack_start(*relatedButton); relatedButton->signal_clicked().connect( sigc::mem_fun(*this, &PrefEntryButtonHBox::onRelatedButtonClickedCallback)); relatedEntry->signal_changed().connect( sigc::mem_fun(*this, &PrefEntryButtonHBox::onRelatedEntryChangedCallback)); } void PrefEntryButtonHBox::onRelatedEntryChangedCallback() { if (this->is_visible()) //only take action if user changed value { prefs_set_string_attribute(_prefs_path.c_str(), _attr.c_str(), relatedEntry->get_text().c_str()); } } void PrefEntryButtonHBox::onRelatedButtonClickedCallback() { if (this->is_visible()) //only take action if user changed value { prefs_set_string_attribute(_prefs_path.c_str(), _attr.c_str(), _default_string); relatedEntry->set_text(_default_string); } } void PrefFileButton::init(const std::string& prefs_path, const std::string& attr) { _prefs_path = prefs_path; _attr = attr; select_filename(Glib::filename_from_utf8(prefs_get_string_attribute(_prefs_path.c_str(), _attr.c_str()))); signal_selection_changed().connect(sigc::mem_fun(*this, &PrefFileButton::onFileChanged)); } void PrefFileButton::onFileChanged() { prefs_set_string_attribute(_prefs_path.c_str(), _attr.c_str(), Glib::filename_to_utf8(get_filename()).c_str()); } void PrefEntry::init(const std::string& prefs_path, const std::string& attr, bool visibility) { _prefs_path = prefs_path; _attr = attr; this->set_invisible_char('*'); this->set_visibility(visibility); this->set_text(prefs_get_string_attribute(_prefs_path.c_str(), _attr.c_str())); } void PrefEntry::on_changed() { if (this->is_visible()) //only take action if user changed value { prefs_set_string_attribute(_prefs_path.c_str(), _attr.c_str(), this->get_text().c_str()); } } void PrefColorPicker::init(const Glib::ustring& label, const std::string& prefs_path, const std::string& attr, guint32 default_rgba) { _prefs_path = prefs_path; _attr = attr; _title = label; this->setRgba32( prefs_get_int_attribute (_prefs_path.c_str(), _attr.c_str(), (int)default_rgba) ); } void PrefColorPicker::on_changed (guint32 rgba) { if (this->is_visible()) //only take action if the user toggled it { prefs_set_int_attribute (_prefs_path.c_str(), _attr.c_str(), (int) rgba); } } void PrefUnit::init(const std::string& prefs_path, const std::string& attr) { _prefs_path = prefs_path; _attr = attr; setUnitType(UNIT_TYPE_LINEAR); gchar const * prefval = prefs_get_string_attribute(_prefs_path.c_str(), _attr.c_str()); setUnit(prefval); } void PrefUnit::on_changed() { if (this->is_visible()) //only take action if user changed value { prefs_set_string_attribute(_prefs_path.c_str(), _attr.c_str(), getUnitAbbr().c_str()); } } } // namespace Widget } // namespace UI } // namespace Inkscape /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :