stroke-style.cpp revision bd3d5a41afe4498f769d5a8425bf8eb36daa3add
/**
* @file
* Stroke style dialog.
*/
/* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* Bryce Harrington <brycehar@bryceharrington.org>
* bulia byak <buliabyak@users.sf.net>
* Maximilian Albert <maximilian.albert@gmail.com>
* Josh Andler <scislac@users.sf.net>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
*
* Copyright (C) 2001-2005 authors
* Copyright (C) 2001 Ximian, Inc.
* Copyright (C) 2004 John Cliff
* Copyright (C) 2008 Maximilian Albert (gtkmm-ification)
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#define noSP_SS_VERBOSE
#include "desktop-handles.h"
#include "desktop-style.h"
#include "dialogs/dialog-events.h"
#include "display/canvas-bpath.h" // for SP_STROKE_LINEJOIN_*
#include "document-private.h"
#include "gradient-chemistry.h"
#include "helper/stock-items.h"
#include "helper/unit-menu.h"
#include "inkscape.h"
#include "marker.h"
#include "path-prefix.h"
#include "selection.h"
#include "sp-linear-gradient.h"
#include "sp-namedview.h"
#include "sp-pattern.h"
#include "sp-radial-gradient.h"
#include "sp-rect.h"
#include "sp-text.h"
#include "style.h"
#include "svg/css-ostringstream.h"
#include "ui/cache/svg_preview_cache.h"
#include "ui/icon-names.h"
#include "widgets/dash-selector.h"
#include "widgets/paint-selector.h"
#include "widgets/sp-widget.h"
#include "widgets/spw-utilities.h"
#include "ui/widget/spinbutton.h"
#include "stroke-style.h"
#include "fill-style.h" // to get sp_fill_style_widget_set_desktop
#include "fill-n-stroke-factory.h"
using Inkscape::DocumentUndo;
/** Marker selection option menus */
{
}
{
}
/* Line */
static void sp_stroke_style_line_selection_modified(SPWidget *spw, Inkscape::Selection *selection, guint flags, gpointer data);
static void sp_stroke_style_line_selection_changed(SPWidget *spw, Inkscape::Selection *selection, gpointer data);
/**
* Helper function for creating radio buttons. This should probably be re-thought out
* when reimplementing this with Gtkmm.
*/
static Gtk::RadioButton *
{
} else {
}
return tb;
}
/**
* Create sa copy of the marker named mname, determines its visible and renderable
* area in menu_id's bounding box, and then renders it. This allows us to fill in
* preview images of each marker in the marker menu.
*/
{
// Retrieve the marker named 'mname' from the source SVG document
return NULL;
}
// Create a copy repr of the marker with id="sample"
// Replace the old sample in the sandbox by the new one
if (oldmarker) {
oldmarker->deleteObject(false);
}
// Uncomment this to get the sandbox documents saved (useful for debugging)
//FILE *fp = fopen (g_strconcat(menu_id, mname, ".svg", NULL), "w");
//sp_repr_save_stream(sandbox->getReprDoc(), fp);
//fclose (fp);
// object to render; note that the id is the same as that of the menu we're building
return NULL; // sandbox broken?
}
// Find object's bbox in document
if (!dbox) {
return NULL;
}
/* Update to renderable state */
double sf = 0.8;
g_free (cache_name);
// TODO: is this correct?
if (!pixbuf) {
}
// Create widget
return pb;
}
/**
* Returns a list of markers in the defs of the given source document as a GSList object
* Returns NULL if there are no markers in the document.
*/
GSList *
{
return NULL;
{
if (SP_IS_MARKER(child)) {
}
}
return ml;
}
#define MARKER_ITEM_MARGIN 0
/**
* Adds previews of markers in marker_list to the given menu widget
*/
static void
sp_marker_menu_build (Gtk::Menu *m, GSList *marker_list, SPDocument *source, SPDocument *sandbox, gchar const *menu_id)
{
// Do this here, outside of loop, to speed up preview generation:
i->show();
} else {
}
// generate preview
// create label
l->show();
hb->pack_start(*l, true, true, 0);
m->append(*i);
}
}
/**
* Pick up all markers from source, except those that are in
* current_doc (if non-NULL), and add items to the m menu.
*/
static void sp_marker_list_from_doc(Gtk::Menu *m, SPDocument * /*current_doc*/, SPDocument *source, SPDocument * /*markers_doc*/, SPDocument *sandbox, gchar const *menu_id)
{
continue;
// Add to the list of markers we really do wish to show
}
g_slist_free (ml);
}
/**
* Returns a new document containing default start, mid, and end markers.
*/
{
gchar const *buffer = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
" <defs id=\"defs\" />"
" <g id=\"marker-start\">"
" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:url(#sample);marker-mid:none;marker-end:none\""
" d=\"M 12.5,13 L 25,13\" id=\"path1\" />"
" <rect style=\"fill:none;stroke:none\" id=\"rect2\""
" width=\"25\" height=\"25\" x=\"0\" y=\"0\" />"
" </g>"
" <g id=\"marker-mid\">"
" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:url(#sample);marker-end:none\""
" d=\"M 0,113 L 12.5,113 L 25,113\" id=\"path11\" />"
" <rect style=\"fill:none;stroke:none\" id=\"rect22\""
" width=\"25\" height=\"25\" x=\"0\" y=\"100\" />"
" </g>"
" <g id=\"marker-end\">"
" <path style=\"fill:none;stroke:black;stroke-width:1.7;marker-start:none;marker-mid:none;marker-end:url(#sample)\""
" d=\"M 0,213 L 12.5,213\" id=\"path111\" />"
" <rect style=\"fill:none;stroke:none\" id=\"rect222\""
" width=\"25\" height=\"25\" x=\"0\" y=\"200\" />"
" </g>"
"</svg>";
}
static void
ink_marker_menu_create_menu(Gtk::Menu *m, gchar const *menu_id, SPDocument *doc, SPDocument *sandbox)
{
// add "None"
i->show();
l->show();
hb->pack_start(*l, true, true, 0);
m->append(*i);
// find and load markers.svg
if (markers_doc == NULL) {
}
}
// suck in from current doc
// add separator
{
//Gtk::Separator *i = gtk_separator_menu_item_new();
i->show();
m->append(*i);
}
// suck in from markers.svg
if (markers_doc) {
doc->ensureUpToDate();
}
}
/**
* Creates a menu widget to display markers from markers.svg
*/
static Gtk::OptionMenu *
{
/* Create new menu widget */
m->show();
if (!doc) {
i->show();
m->append(*i);
mnu->set_sensitive(false);
} else {
mnu->set_sensitive(true);
}
/* Set history */
mnu->set_history(0);
return mnu;
}
/**
* Handles when user selects one of the markers from the marker menu.
* Defines a uri string to refer to it, then applies it to all selected
* items in the current desktop.
*/
static void
{
return;
}
if (!document) {
return;
}
/* Get Marker */
{
return;
}
if (mark) {
}
} else {
}
// Also update the marker dropdown menus, so the document's markers
// show up at the top of the menu
// sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? sp_desktop_selection(desktop) : NULL);
if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) { // can't set marker to rect, until it's converted to using <path>
continue;
}
if (selrepr) {
}
}
css = 0;
_("Set markers"));
};
static unsigned int
{
return 0;
unsigned int i = 0;
break;
}
++i;
}
return i;
}
static void
int pos;
// TODO: this code can be shortened by abstracting out marker_(start|mid|end)_...
switch (which) {
case SP_MARKER_LOC_START:
m->show();
marker_start_menu->set_menu(*m);
break;
case SP_MARKER_LOC_MID:
m->show();
marker_mid_menu->set_menu(*m);
break;
case SP_MARKER_LOC_END:
m->show();
marker_end_menu->set_menu(*m);
break;
default:
}
}
/**
* Sets the stroke width units for all selected items.
* Also handles absolute and dimensionless units.
*/
{
if (!desktop) {
return FALSE;
}
return FALSE;
/* Absolute to percentage */
return FALSE;
return TRUE;
/* Percentage to absolute */
return TRUE;
}
return FALSE;
}
/**
* Creates a new widget for the line stroke style.
*/
{
Gtk::Adjustment *a;
f->show();
t->show();
t->set_border_width(4);
t->set_row_spacings(4);
f->add(*t);
gint i = 0;
//spw_label(t, C_("Stroke width", "_Width:"), 0, i);
// TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate
// spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use
// setHundredPercent to remember the aeraged width corresponding to 100%. Then the
// stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and
// with it, the two remaining calls of stroke_average_width, allowing us to get rid of that
// function in desktop-style.
if (desktop)
i++;
/* Join type */
// TRANSLATORS: The line join style specifies the shape to be used at the
// corners of paths. It can be "miter", "round" or "bevel".
// TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner.
// For an example, draw a triangle with a large stroke width and modify the
// "Join" option (in the Fill and Stroke dialog).
// TRANSLATORS: Round join: joining lines with a rounded corner.
// For an example, draw a triangle with a large stroke width and modify the
// "Join" option (in the Fill and Stroke dialog).
// TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner.
// For an example, draw a triangle with a large stroke width and modify the
// "Join" option (in the Fill and Stroke dialog).
i++;
/* Miterlimit */
// TRANSLATORS: Miter limit: only for "miter join", this limits the length
// of the sharp "spike" when the lines connect at too sharp an angle.
// When two line segments meet at a sharp angle, a miter join results in a
// spike that extends well beyond the connection point. The purpose of the
// miter limit is to cut off such spikes (i.e. convert them into bevels)
// when they become too long.
//spw_label(t, _("Miter _limit:"), 0, i);
a->signal_value_changed().connect(sigc::bind(sigc::ptr_fun(&sp_stroke_style_miterlimit_changed), spw));
i++;
/* Cap type */
// TRANSLATORS: cap type specifies the shape for the ends of lines
//spw_label(t, _("_Cap:"), 0, i);
// TRANSLATORS: Butt cap: the line shape does not extend beyond the end point
// of the line; the ends of the line are square
// TRANSLATORS: Round cap: the line shape extends beyond the end point of the
// line; the ends of the line are rounded
// TRANSLATORS: Square cap: the line shape extends beyond the end point of the
// line; the ends of the line are square
i++;
/* Dash */
//decide what to do:
// implement a set_mnemonic_source function in the
// SPDashSelector class, so that we do not have to
// expose any of the underlying widgets?
i++;
/* Drop down marker selectors*/
// TODO: this code can be shortened by iterating over the possible menus!
// doing this here once, instead of for each preview, to speed things up
// TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes
// (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path.
//spw_label(t, _("_Start Markers:"), 0, i);
t->attach(*marker_start_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
i++;
//spw_label(t, _("_Mid Markers:"), 0, i);
tt->set_tip(*marker_mid_menu, _("Mid Markers are drawn on every node of a path or shape except the first and last nodes"));
marker_mid_menu->show();
t->attach(*marker_mid_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
i++;
//spw_label(t, _("_End Markers:"), 0, i);
marker_end_menu->show();
t->attach(*marker_end_menu, 1, 4, i, i+1, (Gtk::EXPAND | Gtk::FILL), static_cast<Gtk::AttachOptions>(0), 0, 0);
i++;
// FIXME: we cheat and still use gtk+ signals
spw);
spw);
return spw;
}
/**
* Callback for when stroke style widget is modified.
* Triggers update action.
*/
static void
{
}
}
/**
* Callback for when stroke style widget is changed.
* Triggers update action.
*/
static void
{
}
/**
* Sets selector widgets' dash style from an SPStyle object.
*/
static void
{
double d[64];
for (int i = 0; i < len; i++) {
else
}
} else {
}
}
/**
* Sets the join type for a line, and updates the stroke style widget's buttons
*/
static void
{
switch (jointype) {
case SP_STROKE_LINEJOIN_MITER:
break;
case SP_STROKE_LINEJOIN_ROUND:
break;
case SP_STROKE_LINEJOIN_BEVEL:
break;
default:
break;
}
}
/**
* Sets the cap type for a line, and updates the stroke style widget's buttons
*/
static void
{
switch (captype) {
case SP_STROKE_LINECAP_BUTT:
break;
case SP_STROKE_LINECAP_ROUND:
break;
case SP_STROKE_LINECAP_SQUARE:
break;
default:
break;
}
}
/**
* Callback for when stroke style widget is updated, including markers, cap type,
* join type, etc.
*/
static void
{
return;
}
// create temporary style
// query into it
int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT);
int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN);
// Nothing selected, grey-out all controls in the stroke-style dialog
sset->set_sensitive(false);
return;
} else {
sset->set_sensitive(true);
if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) {
} else {
// same width, or only one object; no sense to keep percent, switch to absolute
}
}
} else {
}
// if none of the selected objects has a stroke, than quite some controls should be disabled
// The markers might still be shown though, so these will not be disabled
/* No objects stroked, set insensitive */
}
if (result_ml != QUERY_STYLE_NOTHING)
if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) {
} else {
}
if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) {
} else {
}
return;
/* Markers */
/* Dash */
sset->set_sensitive(true);
}
/**
* Sets a line's dash properties in a CSS style object.
*/
static void
double scale)
{
if (ndash > 0) {
for (int i = 0; i < ndash; i++) {
if (i < (ndash - 1)) {
osarray << ",";
}
}
} else {
}
}
/**
* Sets line properties like width, dashes, markers, etc. on all currently selected items.
*/
static void
{
return;
}
/* TODO: Create some standardized method */
if (items) {
int ndash;
/* Set stroke width */
double width;
} else { // percentage
}
{
}
{
os_ml << miterlimit;
}
/* Set dash */
}
// reset to 100 percent
}
}
// we have already changed the items, so set style without changing selection
// FIXME: move the above stroke-setting stuff, including percentages, to desktop-style
css = 0;
_("Set stroke style"));
}
/**
* Callback for when the stroke style's width changes.
* Causes all line styles to be applied to all selected items.
*/
static void
{
return;
}
}
/**
* Callback for when the stroke style's miterlimit changes.
* Causes all line styles to be applied to all selected items.
*/
static void
{
return;
}
}
/**
* Callback for when the stroke style's dash changes.
* Causes all line styles to be applied to all selected items.
*/
static void
{
return;
}
}
/**
* This routine handles toggle events for buttons in the stroke style dialog.
*
* When activated, this routine gets the data for the various widgets, and then
* calls the respective routines to update css properties, etc.
*
*/
{
return;
}
if (tb->get_active()) {
if (join) {
}
/* TODO: Create some standardized method */
if (join) {
} else if (cap) {
}
css = 0;
_("Set stroke style"));
}
}
/**
* Updates the join style toggle buttons
*/
static void
{
}
/**
* Updates the cap style toggle buttons
*/
static void
{
}
/**
* Sets the current marker in the marker menu.
*/
static void
{
bool mark_is_stock = false;
mark_is_stock = true;
}
if (mark_is_stock) {
} else {
}
}
else {
mnu->set_history(0);
}
}
/**
* Updates the marker menus to highlight the appropriate marker and scroll to
* that marker.
*/
static void
{
{ "start_mark_menu", SP_MARKER_LOC_START },
{ "mid_mark_menu", SP_MARKER_LOC_MID },
{ "end_mark_menu", SP_MARKER_LOC_END }
};
bool all_texts = true;
if (!SP_IS_TEXT (i->data)) {
all_texts = false;
}
}
for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
// Per SVG spec, text objects cannot have markers; disable menus if only texts are selected
}
// We show markers of the first object in the list only
// FIXME: use the first in the list that has the marker of each type, if any
for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) {
// For all three marker types,
// find the corresponding menu
// Quit if we're in update state
return;
}
// If the object has this type of markers,
// Extract the name of the marker that the object uses
SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value, object->document);
// Scroll the menu to that marker
} else {
mnu->set_history(0);
}
}
}
/**
* Extract the actual name of the link
* e.g. get mTriangle from url(#mTriangle).
* \return Buffer containing the actual name, allocated from GLib;
* the caller should free the buffer when they no longer need it.
*/
static SPObject*
{
gchar const *p = n;
while (*p != '\0' && *p != '#') {
p++;
}
if (*p == '\0' || p[1] == '\0') {
return NULL;
}
p++;
int c = 0;
while (p[c] != '\0' && p[c] != ')') {
c++;
}
if (p[c] == '\0') {
return NULL;
}
b[c] = '\0';
// FIXME: get the document from the object and let the caller pass it in
return marker;
}
/*
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 :