// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * Combobox for selecting dash patterns - implementation. */ /* Author: * Lauris Kaplinski * bulia byak * Maximilian Albert * * Copyright (C) 2002 Lauris Kaplinski * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "dash-selector.h" #include #include #include <2geom/coord.h> #include "preferences.h" #include "display/cairo-utils.h" #include "style.h" #include "ui/dialog-events.h" #include "ui/widget/spinbutton.h" namespace Inkscape { namespace UI { namespace Widget { gchar const *const DashSelector::_prefs_path = "/palette/dashes"; static double dash_0[] = {-1.0}; static double dash_1_1[] = {1.0, 1.0, -1.0}; static double dash_2_1[] = {2.0, 1.0, -1.0}; static double dash_4_1[] = {4.0, 1.0, -1.0}; static double dash_1_2[] = {1.0, 2.0, -1.0}; static double dash_1_4[] = {1.0, 4.0, -1.0}; static size_t BD_LEN = 7; // must correspond to the number of entries in the next line static double *builtin_dashes[] = {dash_0, dash_1_1, dash_2_1, dash_4_1, dash_1_2, dash_1_4, nullptr}; static double **dashes = nullptr; DashSelector::DashSelector() : preview_width(80), preview_height(16), preview_lineheight(2) { set_spacing(4); // TODO: find something more sensible here!! init_dashes(); dash_store = Gtk::ListStore::create(dash_columns); dash_combo.set_model(dash_store); dash_combo.pack_start(image_renderer); dash_combo.set_cell_data_func(image_renderer, sigc::mem_fun(*this, &DashSelector::prepareImageRenderer)); dash_combo.set_tooltip_text(_("Dash pattern")); dash_combo.get_style_context()->add_class("combobright"); dash_combo.show(); dash_combo.signal_changed().connect( sigc::mem_fun(*this, &DashSelector::on_selection) ); this->pack_start(dash_combo, true, true, 0); offset = Gtk::Adjustment::create(0.0, 0.0, 10.0, 0.1, 1.0, 0.0); offset->signal_value_changed().connect(sigc::mem_fun(*this, &DashSelector::offset_value_changed)); auto sb = new Inkscape::UI::Widget::SpinButton(offset, 0.1, 2); sb->set_tooltip_text(_("Pattern offset")); sp_dialog_defocus_on_enter_cpp(sb); sb->show(); this->pack_start(*sb, false, false, 0); int np=0; while (dashes[np]){ np++;} for (int i = 0; iappend()); row[dash_columns.dash] = dashes[i]; row[dash_columns.pixbuf] = Glib::wrap(sp_dash_to_pixbuf(dashes[i])); } // add the custom one Gtk::TreeModel::Row row = *(dash_store->append()); row[dash_columns.dash] = dashes[np-1]; row[dash_columns.pixbuf] = Glib::wrap(sp_text_to_pixbuf((char *)"Custom")); this->set_data("pattern", dashes[0]); } DashSelector::~DashSelector() { // FIXME: for some reason this doesn't get called; does the call to manage() in // sp_stroke_style_line_widget_new() not processed correctly? } void DashSelector::prepareImageRenderer( Gtk::TreeModel::const_iterator const &row ) { Glib::RefPtr pixbuf = (*row)[dash_columns.pixbuf]; image_renderer.property_pixbuf() = pixbuf; } void DashSelector::init_dashes() { if (!dashes) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); std::vector dash_prefs = prefs->getAllDirs(_prefs_path); int pos = 0; if (!dash_prefs.empty()) { SPStyle style; dashes = g_new (double *, dash_prefs.size() + 2); // +1 for custom slot, +1 for terminator slot for (auto & dash_pref : dash_prefs) { style.readFromPrefs( dash_pref ); if (!style.stroke_dasharray.values.empty()) { dashes[pos] = g_new (double, style.stroke_dasharray.values.size() + 1); double *d = dashes[pos]; unsigned i = 0; for (; i < style.stroke_dasharray.values.size(); i++) { d[i] = style.stroke_dasharray.values[i].value; } d[i] = -1; } else { dashes[pos] = dash_0; } pos += 1; } } else { // This code may never execute - a new preferences.xml is created for a new user. Maybe if the user deletes dashes from preferences.xml? dashes = g_new (double *, BD_LEN + 2); // +1 for custom slot, +1 for terminator slot unsigned i; for(i=0;i 0) { double delta = 0.0; for (int i = 0; i < ndash; i++) delta += dash[i]; delta /= 1000.0; for (int i = 0; dashes[i]; i++,count++) { double *pattern = dashes[i]; int np = 0; while (pattern[np] >= 0.0) np += 1; if (np == ndash) { int j; for (j = 0; j < ndash; j++) { if (!Geom::are_near(dash[j], pattern[j], delta)) { break; } } if (j == ndash) { pos = i; break; } } } } else if(ndash==0) { pos = 0; } if(pos>=0){ this->set_data("pattern", dashes[pos]); this->dash_combo.set_active(pos); this->offset->set_value(o); if(pos == 10) { this->offset->set_value(10.0); } } else { // Hit a custom pattern in the SVG, write it into the combobox. count--; // the one slot for custom patterns double *d = dashes[count]; int i=0; for(i=0;i< (ndash > 15 ? 15 : ndash) ;i++) { d[i]=dash[i]; } // store the custom pattern d[ndash]=-1.0; //terminate it this->set_data("pattern", dashes[count]); this->dash_combo.set_active(count); this->offset->set_value(o); // what does this do???? } } void DashSelector::get_dash(int *ndash, double **dash, double *off) { double *pattern = (double*) this->get_data("pattern"); int nd = 0; while (pattern[nd] >= 0.0) nd += 1; if (nd > 0) { if (ndash) *ndash = nd; if (dash) { *dash = g_new (double, nd); memcpy (*dash, pattern, nd * sizeof (double)); } if (off) *off = offset->get_value(); } else { if (ndash) *ndash = 0; if (dash) *dash = nullptr; if (off) *off = 0.0; } } /** * Fill a pixbuf with the dash pattern using standard cairo drawing */ GdkPixbuf* DashSelector::sp_dash_to_pixbuf(double *pattern) { int n_dashes; for (n_dashes = 0; pattern[n_dashes] >= 0.0; n_dashes ++) ; cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, preview_width, preview_height); cairo_t *ct = cairo_create(s); cairo_set_line_width (ct, preview_lineheight); cairo_scale (ct, preview_lineheight, 1); //cairo_set_source_rgb (ct, 0, 0, 0); cairo_move_to (ct, 0, preview_height/2); cairo_line_to (ct, preview_width, preview_height/2); cairo_set_dash(ct, pattern, n_dashes, 0); cairo_stroke (ct); cairo_destroy(ct); cairo_surface_flush(s); GdkPixbuf* pixbuf = ink_pixbuf_create_from_cairo_surface(s); return pixbuf; } /** * Fill a pixbuf with a text label using standard cairo drawing */ GdkPixbuf* DashSelector::sp_text_to_pixbuf(char *text) { cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, preview_width, preview_height); cairo_t *ct = cairo_create(s); cairo_select_font_face (ct, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size (ct, 12.0); cairo_set_source_rgb (ct, 0.0, 0.0, 0.0); cairo_move_to (ct, 16.0, 13.0); cairo_show_text (ct, text); cairo_stroke (ct); cairo_destroy(ct); cairo_surface_flush(s); GdkPixbuf* pixbuf = ink_pixbuf_create_from_cairo_surface(s); return pixbuf; } void DashSelector::on_selection () { double *pattern = dash_combo.get_active()->get_value(dash_columns.dash); this->set_data ("pattern", pattern); changed_signal.emit(); } void DashSelector::offset_value_changed() { changed_signal.emit(); } } // 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:fileencoding=utf-8:textwidth=99 :