72df1baab7747ff06af0b171e1eb216679fd8b8d
1 /**
2 * \brief Inkscape Preferences dialog
3 *
4 * Authors:
5 * Marco Scholten
6 * Bruno Dilly <bruno.dilly@gmail.com>
7 *
8 * Copyright (C) 2004, 2006, 2007 Authors
9 *
10 * Released under GNU GPL. Read the file 'COPYING' for more information.
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include <gtkmm/frame.h>
18 #include <gtkmm/alignment.h>
19 #include <gtkmm/box.h>
21 #include "preferences.h"
22 #include "ui/widget/preferences-widget.h"
23 #include "verbs.h"
24 #include "selcue.h"
25 #include <iostream>
26 #include "enums.h"
27 #include "inkscape.h"
28 #include "desktop-handles.h"
29 #include "message-stack.h"
30 #include "style.h"
31 #include "selection.h"
32 #include "selection-chemistry.h"
33 #include "xml/repr.h"
35 using namespace Inkscape::UI::Widget;
37 namespace Inkscape {
38 namespace UI {
39 namespace Widget {
41 DialogPage::DialogPage()
42 {
43 this->set_border_width(12);
44 this->set_col_spacings(12);
45 this->set_row_spacings(6);
46 }
48 void DialogPage::add_line(bool indent, Glib::ustring const &label, Gtk::Widget &widget, Glib::ustring const &suffix, const Glib::ustring &tip, bool expand_widget)
49 {
50 int start_col;
51 int row = this->property_n_rows();
52 Gtk::Widget* w;
53 if (expand_widget)
54 {
55 w = &widget;
56 }
57 else
58 {
59 Gtk::HBox* hb = Gtk::manage(new Gtk::HBox());
60 hb->set_spacing(12);
61 hb->pack_start(widget,false,false);
62 w = (Gtk::Widget*) hb;
63 }
64 if (label != "")
65 {
66 Gtk::Label* label_widget;
67 label_widget = Gtk::manage(new Gtk::Label(label , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true));
68 label_widget->set_mnemonic_widget(widget);
69 if (indent)
70 {
71 Gtk::Alignment* alignment = Gtk::manage(new Gtk::Alignment());
72 alignment->set_padding(0, 0, 12, 0);
73 alignment->add(*label_widget);
74 this->attach(*alignment , 0, 1, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0);
75 }
76 else
77 this->attach(*label_widget , 0, 1, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0);
78 start_col = 1;
79 }
80 else
81 start_col = 0;
83 if (start_col == 0 && indent) //indent this widget
84 {
85 Gtk::Alignment* alignment = Gtk::manage(new Gtk::Alignment());
86 alignment->set_padding(0, 0, 12, 0);
87 alignment->add(*w);
88 this->attach(*alignment, start_col, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::AttachOptions(), 0, 0);
89 }
90 else
91 {
92 this->attach(*w, start_col, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::AttachOptions(), 0, 0);
93 }
95 if (suffix != "")
96 {
97 Gtk::Label* suffix_widget = Gtk::manage(new Gtk::Label(suffix , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true));
98 if (expand_widget)
99 this->attach(*suffix_widget, 2, 3, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0);
100 else
101 ((Gtk::HBox*)w)->pack_start(*suffix_widget,false,false);
102 }
104 if (tip != "")
105 {
106 _tooltips.set_tip (widget, tip);
107 }
109 }
111 void DialogPage::add_group_header(Glib::ustring name)
112 {
113 int row = this->property_n_rows();
114 if (name != "")
115 {
116 Gtk::Label* label_widget = Gtk::manage(new Gtk::Label(Glib::ustring(/*"<span size='large'>*/"<b>") + name +
117 Glib::ustring("</b>"/*</span>"*/) , Gtk::ALIGN_LEFT , Gtk::ALIGN_CENTER, true));
118 label_widget->set_use_markup(true);
119 this->attach(*label_widget , 0, 4, row, row + 1, Gtk::FILL, Gtk::AttachOptions(), 0, 0);
120 if (row != 1)
121 this->set_row_spacing(row - 1, 18);
122 }
123 }
125 void DialogPage::set_tip(Gtk::Widget& widget, Glib::ustring const &tip)
126 {
127 _tooltips.set_tip (widget, tip);
128 }
130 void PrefCheckButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
131 bool default_value)
132 {
133 _prefs_path = prefs_path;
134 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
135 this->set_label(label);
136 this->set_active( prefs->getBool(_prefs_path, default_value) );
137 }
139 void PrefCheckButton::on_toggled()
140 {
141 if (this->is_visible()) //only take action if the user toggled it
142 {
143 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
144 prefs->setBool(_prefs_path, this->get_active());
145 }
146 }
148 void PrefRadioButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
149 Glib::ustring const &string_value, bool default_value, PrefRadioButton* group_member)
150 {
151 _prefs_path = prefs_path;
152 _value_type = VAL_STRING;
153 _string_value = string_value;
154 (void)default_value;
155 this->set_label(label);
156 if (group_member)
157 {
158 Gtk::RadioButtonGroup rbg = group_member->get_group();
159 this->set_group(rbg);
160 }
161 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
162 Glib::ustring val = prefs->getString(_prefs_path);
163 if ( !val.empty() )
164 this->set_active(val == _string_value);
165 else
166 this->set_active( false );
167 }
169 void PrefRadioButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
170 int int_value, bool default_value, PrefRadioButton* group_member)
171 {
172 _prefs_path = prefs_path;
173 _value_type = VAL_INT;
174 _int_value = int_value;
175 this->set_label(label);
176 if (group_member)
177 {
178 Gtk::RadioButtonGroup rbg = group_member->get_group();
179 this->set_group(rbg);
180 }
181 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
182 if (default_value)
183 this->set_active( prefs->getInt(_prefs_path, int_value) == _int_value );
184 else
185 this->set_active( prefs->getInt(_prefs_path, int_value + 1) == _int_value );
186 }
188 void PrefRadioButton::on_toggled()
189 {
190 this->changed_signal.emit(this->get_active());
191 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
193 if (this->is_visible() && this->get_active() ) //only take action if toggled by user (to active)
194 {
195 if ( _value_type == VAL_STRING )
196 prefs->setString(_prefs_path, _string_value);
197 else if ( _value_type == VAL_INT )
198 prefs->setInt(_prefs_path, _int_value);
199 }
200 }
202 void PrefSpinButton::init(Glib::ustring const &prefs_path,
203 double lower, double upper, double step_increment, double page_increment,
204 double default_value, bool is_int, bool is_percent)
205 {
206 _prefs_path = prefs_path;
207 _is_int = is_int;
208 _is_percent = is_percent;
209 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
210 double value;
211 if (is_int) {
212 if (is_percent) {
213 value = 100 * prefs->getDoubleLimited(prefs_path, default_value, lower/100.0, upper/100.0);
214 } else {
215 value = (double) prefs->getIntLimited(prefs_path, (int) default_value, (int) lower, (int) upper);
216 }
217 } else {
218 value = prefs->getDoubleLimited(prefs_path, default_value, lower, upper);
219 }
221 this->set_range (lower, upper);
222 this->set_increments (step_increment, page_increment);
223 this->set_numeric();
224 this->set_value (value);
225 this->set_width_chars(6);
226 if (is_int)
227 this->set_digits(0);
228 else if (step_increment < 0.1)
229 this->set_digits(4);
230 else
231 this->set_digits(2);
233 }
235 void PrefSpinButton::on_value_changed()
236 {
237 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
238 if (this->is_visible()) //only take action if user changed value
239 {
240 if (_is_int) {
241 if (_is_percent) {
242 prefs->setDouble(_prefs_path, this->get_value()/100.0);
243 } else {
244 prefs->setInt(_prefs_path, (int) this->get_value());
245 }
246 } else {
247 prefs->setDouble(_prefs_path, this->get_value());
248 }
249 }
250 }
252 const double ZoomCorrRuler::textsize = 7;
253 const double ZoomCorrRuler::textpadding = 5;
255 ZoomCorrRuler::ZoomCorrRuler(int width, int height) :
256 _unitconv(1.0),
257 _border(5)
258 {
259 set_size(width, height);
260 }
262 void ZoomCorrRuler::set_size(int x, int y)
263 {
264 _min_width = x;
265 _height = y;
266 set_size_request(x + _border*2, y + _border*2);
267 }
269 // The following two functions are borrowed from 2geom's toy-framework-2; if they are useful in
270 // other locations, we should perhaps make them (or adapted versions of them) publicly available
271 static void
272 draw_text(cairo_t *cr, Geom::Point loc, const char* txt, bool bottom = false,
273 double fontsize = ZoomCorrRuler::textsize, std::string fontdesc = "Sans") {
274 PangoLayout* layout = pango_cairo_create_layout (cr);
275 pango_layout_set_text(layout, txt, -1);
277 // set font and size
278 std::ostringstream sizestr;
279 sizestr << fontsize;
280 fontdesc = fontdesc + " " + sizestr.str();
281 PangoFontDescription *font_desc = pango_font_description_from_string(fontdesc.c_str());
282 pango_layout_set_font_description(layout, font_desc);
283 pango_font_description_free (font_desc);
285 PangoRectangle logical_extent;
286 pango_layout_get_pixel_extents(layout, NULL, &logical_extent);
287 cairo_move_to(cr, loc[Geom::X], loc[Geom::Y] - (bottom ? logical_extent.height : 0));
288 pango_cairo_show_layout(cr, layout);
289 }
291 static void
292 draw_number(cairo_t *cr, Geom::Point pos, double num) {
293 std::ostringstream number;
294 number << num;
295 draw_text(cr, pos, number.str().c_str(), true);
296 }
298 /*
299 * \arg dist The distance between consecutive minor marks
300 * \arg major_interval Number of marks after which to draw a major mark
301 */
302 void
303 ZoomCorrRuler::draw_marks(Cairo::RefPtr<Cairo::Context> cr, double dist, int major_interval) {
304 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
305 const double zoomcorr = prefs->getDouble("/options/zoomcorrection/value", 1.0);
306 double mark = 0;
307 int i = 0;
308 while (mark <= _drawing_width) {
309 cr->move_to(mark, _height);
310 if ((i % major_interval) == 0) {
311 // major mark
312 cr->line_to(mark, 0);
313 Geom::Point textpos(mark + 3, ZoomCorrRuler::textsize + ZoomCorrRuler::textpadding);
314 draw_number(cr->cobj(), textpos, dist * i);
315 } else {
316 // minor mark
317 cr->line_to(mark, ZoomCorrRuler::textsize + 2 * ZoomCorrRuler::textpadding);
318 }
319 mark += dist * zoomcorr / _unitconv;
320 ++i;
321 }
322 }
324 void
325 ZoomCorrRuler::redraw() {
326 Glib::RefPtr<Gdk::Window> window = get_window();
327 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
329 int w, h;
330 window->get_size(w, h);
331 _drawing_width = w - _border * 2;
333 cr->set_source_rgb(1.0, 1.0, 1.0);
334 cr->set_fill_rule(Cairo::FILL_RULE_WINDING);
335 cr->rectangle(0, 0, w, _height + _border*2);
336 cr->fill();
338 cr->set_source_rgb(0.0, 0.0, 0.0);
339 cr->set_line_width(0.5);
341 cr->translate(_border, _border); // so that we have a small white border around the ruler
342 cr->move_to (0, _height);
343 cr->line_to (_drawing_width, _height);
345 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
346 Glib::ustring abbr = prefs->getString("/options/zoomcorrection/unit");
347 if (abbr == "cm") {
348 draw_marks(cr, 0.1, 10);
349 } else if (abbr == "ft") {
350 draw_marks(cr, 1/12.0, 12);
351 } else if (abbr == "in") {
352 draw_marks(cr, 0.25, 4);
353 } else if (abbr == "m") {
354 draw_marks(cr, 1/10.0, 10);
355 } else if (abbr == "mm") {
356 draw_marks(cr, 10, 10);
357 } else if (abbr == "pc") {
358 draw_marks(cr, 1, 10);
359 } else if (abbr == "pt") {
360 draw_marks(cr, 10, 10);
361 } else if (abbr == "px") {
362 draw_marks(cr, 10, 10);
363 } else {
364 draw_marks(cr, 1, 1);
365 }
366 cr->stroke();
367 }
369 bool
370 ZoomCorrRuler::on_expose_event(GdkEventExpose */*event*/) {
371 this->redraw();
372 return true;
373 }
375 void
376 ZoomCorrRulerSlider::on_slider_value_changed()
377 {
378 if (this->is_visible() || freeze) //only take action if user changed value
379 {
380 freeze = true;
381 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
382 prefs->setDouble("/options/zoomcorrection/value", _slider.get_value() / 100.0);
383 _sb.set_value(_slider.get_value());
384 _ruler.redraw();
385 freeze = false;
386 }
387 }
389 void
390 ZoomCorrRulerSlider::on_spinbutton_value_changed()
391 {
392 if (this->is_visible() || freeze) //only take action if user changed value
393 {
394 freeze = true;
395 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
396 prefs->setDouble("/options/zoomcorrection/value", _sb.get_value() / 100.0);
397 _slider.set_value(_sb.get_value());
398 _ruler.redraw();
399 freeze = false;
400 }
401 }
403 void
404 ZoomCorrRulerSlider::on_unit_changed() {
405 if (GPOINTER_TO_INT(_unit.get_data("sensitive")) == 0) {
406 // when the unit menu is initialized, the unit is set to the default but
407 // it needs to be reset later so we don't perform the change in this case
408 return;
409 }
410 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
411 prefs->setString("/options/zoomcorrection/unit", _unit.getUnitAbbr());
412 double conv = _unit.getConversion(_unit.getUnitAbbr(), "px");
413 _ruler.set_unit_conversion(conv);
414 if (_ruler.is_visible()) {
415 _ruler.redraw();
416 }
417 }
419 void
420 ZoomCorrRulerSlider::init(int ruler_width, int ruler_height, double lower, double upper,
421 double step_increment, double page_increment, double default_value)
422 {
423 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
424 double value = prefs->getDoubleLimited("/options/zoomcorrection/value", default_value, lower, upper) * 100.0;
426 freeze = false;
428 _ruler.set_size(ruler_width, ruler_height);
430 _slider.set_size_request(_ruler.width(), -1);
431 _slider.set_range (lower, upper);
432 _slider.set_increments (step_increment, page_increment);
433 _slider.set_value (value);
434 _slider.set_digits(2);
436 _slider.signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_slider_value_changed));
437 _sb.signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_spinbutton_value_changed));
438 _unit.signal_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_unit_changed));
440 _sb.set_range (lower, upper);
441 _sb.set_increments (step_increment, page_increment);
442 _sb.set_value (value);
443 _sb.set_digits(2);
445 _unit.set_data("sensitive", GINT_TO_POINTER(0));
446 _unit.setUnitType(UNIT_TYPE_LINEAR);
447 _unit.set_data("sensitive", GINT_TO_POINTER(1));
448 _unit.setUnit(prefs->getString("/options/zoomcorrection/unit"));
450 Gtk::Table *table = Gtk::manage(new Gtk::Table());
451 Gtk::Alignment *alignment1 = Gtk::manage(new Gtk::Alignment(0.5,1,0,0));
452 Gtk::Alignment *alignment2 = Gtk::manage(new Gtk::Alignment(0.5,1,0,0));
453 alignment1->add(_sb);
454 alignment2->add(_unit);
456 table->attach(_slider, 0, 1, 0, 1);
457 table->attach(*alignment1, 1, 2, 0, 1, static_cast<Gtk::AttachOptions>(0));
458 table->attach(_ruler, 0, 1, 1, 2);
459 table->attach(*alignment2, 1, 2, 1, 2, static_cast<Gtk::AttachOptions>(0));
461 this->pack_start(*table, Gtk::PACK_EXPAND_WIDGET);
462 }
464 void PrefCombo::init(Glib::ustring const &prefs_path,
465 Glib::ustring labels[], int values[], int num_items, int default_value)
466 {
467 _prefs_path = prefs_path;
468 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
469 int row = 0;
470 int value = prefs->getInt(_prefs_path, default_value);
472 for (int i = 0 ; i < num_items; ++i)
473 {
474 this->append_text(labels[i]);
475 _values.push_back(values[i]);
476 if (value == values[i])
477 row = i;
478 }
479 this->set_active(row);
480 }
482 void PrefCombo::on_changed()
483 {
484 if (this->is_visible()) //only take action if user changed value
485 {
486 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
487 prefs->setInt(_prefs_path, _values[this->get_active_row_number()]);
488 }
489 }
491 void PrefEntryButtonHBox::init(Glib::ustring const &prefs_path,
492 bool visibility, Glib::ustring const &default_string)
493 {
494 _prefs_path = prefs_path;
495 _default_string = default_string;
496 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
497 relatedEntry = new Gtk::Entry();
498 relatedButton = new Gtk::Button(_("Reset"));
499 relatedEntry->set_invisible_char('*');
500 relatedEntry->set_visibility(visibility);
501 relatedEntry->set_text(prefs->getString(_prefs_path));
502 this->pack_start(*relatedEntry);
503 this->pack_start(*relatedButton);
504 relatedButton->signal_clicked().connect(
505 sigc::mem_fun(*this, &PrefEntryButtonHBox::onRelatedButtonClickedCallback));
506 relatedEntry->signal_changed().connect(
507 sigc::mem_fun(*this, &PrefEntryButtonHBox::onRelatedEntryChangedCallback));
508 }
510 void PrefEntryButtonHBox::onRelatedEntryChangedCallback()
511 {
512 if (this->is_visible()) //only take action if user changed value
513 {
514 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
515 prefs->setString(_prefs_path, relatedEntry->get_text());
516 }
517 }
519 void PrefEntryButtonHBox::onRelatedButtonClickedCallback()
520 {
521 if (this->is_visible()) //only take action if user changed value
522 {
523 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
524 prefs->setString(_prefs_path, _default_string);
525 relatedEntry->set_text(_default_string);
526 }
527 }
530 void PrefFileButton::init(Glib::ustring const &prefs_path)
531 {
532 _prefs_path = prefs_path;
533 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
534 select_filename(Glib::filename_from_utf8(prefs->getString(_prefs_path)));
536 signal_selection_changed().connect(sigc::mem_fun(*this, &PrefFileButton::onFileChanged));
537 }
539 void PrefFileButton::onFileChanged()
540 {
541 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
542 prefs->setString(_prefs_path, Glib::filename_to_utf8(get_filename()));
543 }
545 void PrefEntry::init(Glib::ustring const &prefs_path, bool visibility)
546 {
547 _prefs_path = prefs_path;
548 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
549 this->set_invisible_char('*');
550 this->set_visibility(visibility);
551 this->set_text(prefs->getString(_prefs_path));
552 }
554 void PrefEntry::on_changed()
555 {
556 if (this->is_visible()) //only take action if user changed value
557 {
558 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
559 prefs->setString(_prefs_path, this->get_text());
560 }
561 }
563 void PrefColorPicker::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
564 guint32 default_rgba)
565 {
566 _prefs_path = prefs_path;
567 _title = label;
568 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
569 this->setRgba32( prefs->getInt(_prefs_path, (int)default_rgba) );
570 }
572 void PrefColorPicker::on_changed (guint32 rgba)
573 {
574 if (this->is_visible()) //only take action if the user toggled it
575 {
576 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
577 prefs->setInt(_prefs_path, (int) rgba);
578 }
579 }
581 void PrefUnit::init(Glib::ustring const &prefs_path)
582 {
583 _prefs_path = prefs_path;
584 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
585 setUnitType(UNIT_TYPE_LINEAR);
586 setUnit(prefs->getString(_prefs_path));
587 }
589 void PrefUnit::on_changed()
590 {
591 if (this->is_visible()) //only take action if user changed value
592 {
593 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
594 prefs->setString(_prefs_path, getUnitAbbr());
595 }
596 }
598 } // namespace Widget
599 } // namespace UI
600 } // namespace Inkscape
602 /*
603 Local Variables:
604 mode:c++
605 c-file-style:"stroustrup"
606 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
607 indent-tabs-mode:nil
608 fill-column:99
609 End:
610 */
611 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :