From 877345db2877c63364e8aa4dfa89cfd1a85ea41a Mon Sep 17 00:00:00 2001 From: cilix42 Date: Thu, 4 Sep 2008 16:44:24 +0000 Subject: [PATCH] Add a zoom correction option to preferences (used when zooming to 1:1 etc. to display items in their true sizes) --- src/preferences-skeleton.h | 1 + src/ui/dialog/inkscape-preferences.cpp | 12 +- src/ui/dialog/inkscape-preferences.h | 1 + src/ui/widget/preferences-widget.cpp | 206 +++++++++++++++++++++++++ src/ui/widget/preferences-widget.h | 44 ++++++ src/verbs.cpp | 9 +- 6 files changed, 266 insertions(+), 7 deletions(-) diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 78c79ebd1..3ca113e85 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -222,6 +222,7 @@ static char const preferences_skeleton[] = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index ef6c030e8..dcfc4e575 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -960,19 +960,23 @@ void InkscapePreferences::initPageUI() _misc_small_toolbar.init( "toolbox", "small", sizeLabels, sizeValues, G_N_ELEMENTS(sizeLabels), 0 ); _page_ui.add_line( false, _("Commands bar icon size"), _misc_small_toolbar, "", - _("Set the size for the commands toolbar to use (requires restart)"), false); + _("Set the size for the commands toolbar to use (requires restart)"), false); _misc_small_secondary.init( "toolbox", "secondary", sizeLabels, sizeValues, G_N_ELEMENTS(sizeLabels), 1 ); _page_ui.add_line( false, _("Tool controls bar icon size"), _misc_small_secondary, "", - _("Set the size for the secondary toolbar to use (requires restart)"), false); + _("Set the size for the secondary toolbar to use (requires restart)"), false); _misc_small_tools.init( "toolbox.tools", "small", sizeLabels, sizeValues, G_N_ELEMENTS(sizeLabels), 0 ); _page_ui.add_line( false, _("Main toolbar icon size"), _misc_small_tools, "", - _("Set the size for the main tools to use (requires restart)"), false); + _("Set the size for the main tools to use (requires restart)"), false); _misc_recent.init("options.maxrecentdocuments", "value", 0.0, 1000.0, 1.0, 1.0, 1.0, true, false); _page_ui.add_line( false, _("Maximum documents in Open Recent:"), _misc_recent, "", - _("The maximum length of the Open Recent list in the File menu"), false); + _("The maximum length of the Open Recent list in the File menu"), false); + + _ui_zoom_correction.init(300, 30, 1.00, 200.0, 1.0, 10.0, 1.0); + _page_ui.add_line( false, _("Zoom correction factor (in %):"), _ui_zoom_correction, "", + _("Adjust the slider until the length of the ruler on your screen matches its real length. This information is used when zooming to 1:1, 1:2, etc., to display objects in their true sizes"), true); this->AddPage(_page_ui, _("Interface"), PREFS_PAGE_UI); } diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 2dd0ca4df..a6fc96a3f 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -166,6 +166,7 @@ protected: PrefCheckButton _sel_layer_deselects; PrefSpinButton _importexport_export, _misc_recent, _misc_simpl; + ZoomCorrRulerSlider _ui_zoom_correction; PrefSpinButton _misc_latency_skew; PrefCheckButton _misc_comment, _misc_forkvectors, _misc_scripts, _misc_namedicon_delay; PrefCombo _misc_small_toolbar; diff --git a/src/ui/widget/preferences-widget.cpp b/src/ui/widget/preferences-widget.cpp index 6c5473caa..d4164864b 100644 --- a/src/ui/widget/preferences-widget.cpp +++ b/src/ui/widget/preferences-widget.cpp @@ -242,6 +242,212 @@ void PrefSpinButton::on_value_changed() } } +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) { diff --git a/src/ui/widget/preferences-widget.h b/src/ui/widget/preferences-widget.h index 0b907ae34..9f10f8ca8 100644 --- a/src/ui/widget/preferences-widget.h +++ b/src/ui/widget/preferences-widget.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -82,6 +84,48 @@ protected: void on_value_changed(); }; +class ZoomCorrRuler : public Gtk::DrawingArea { +public: + ZoomCorrRuler(int width = 100, int height = 20); + void set_size(int x, int y); + void set_unit_conversion(double conv) { _unitconv = conv; } + void set_cairo_context(Cairo::RefPtr cr); + void redraw(); + + int width() { return _min_width + _border*2; } + + static const double textsize; + static const double textpadding; + +private: + bool on_expose_event(GdkEventExpose *event); + void draw_marks(Cairo::RefPtr cr, double dist, int major_interval); + + double _unitconv; + int _min_width; + int _height; + int _border; + int _drawing_width; +}; + +class ZoomCorrRulerSlider : public Gtk::VBox +{ +public: + void init(int ruler_width, int ruler_height, double lower, double upper, + double step_increment, double page_increment, double default_value); + +private: + void on_slider_value_changed(); + void on_spinbutton_value_changed(); + void on_unit_changed(); + + Gtk::SpinButton _sb; + UnitMenu _unit; + Gtk::HScale _slider; + ZoomCorrRuler _ruler; + bool freeze; // used to block recursive updates of slider and spinbutton +}; + class PrefCombo : public Gtk::ComboBoxText { public: diff --git a/src/verbs.cpp b/src/verbs.cpp index a19ac7753..6b8d0a1fa 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1645,20 +1645,23 @@ ZoomVerb::perform(SPAction *action, void *data, void */*pdata*/) } case SP_VERB_ZOOM_1_1: { + double zcorr = prefs_get_double_attribute ("options.zoomcorrection", "value", 1.0); NR::Rect const d = dt->get_display_area(); - dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 1.0 ); + dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 1.0 * zcorr ); break; } case SP_VERB_ZOOM_1_2: { + double zcorr = prefs_get_double_attribute ("options.zoomcorrection", "value", 1.0); NR::Rect const d = dt->get_display_area(); - dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 0.5); + dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 0.5 * zcorr ); break; } case SP_VERB_ZOOM_2_1: { + double zcorr = prefs_get_double_attribute ("options.zoomcorrection", "value", 1.0); NR::Rect const d = dt->get_display_area(); - dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 2.0 ); + dt->zoom_absolute( d.midpoint()[NR::X], d.midpoint()[NR::Y], 2.0 * zcorr ); break; } case SP_VERB_ZOOM_PAGE: -- 2.30.2