pencil-toolbar.cpp revision 981d851779765e303cf167e86f0f17b31c85da4e
235N/A/**
235N/A * @file
822N/A * Pencil aux toolbar
822N/A */
822N/A/* Authors:
235N/A * MenTaLguY <mental@rydia.net>
1252N/A * Lauris Kaplinski <lauris@kaplinski.com>
235N/A * bulia byak <buliabyak@users.sf.net>
235N/A * Frank Felfe <innerspace@iname.com>
919N/A * John Cliff <simarilius@yahoo.com>
919N/A * David Turner <novalis@gnu.org>
919N/A * Josh Andler <scislac@scislac.com>
919N/A * Jon A. Cruz <jon@joncruz.org>
919N/A * Maximilian Albert <maximilian.albert@gmail.com>
919N/A * Tavmjong Bah <tavmjong@free.fr>
919N/A * Abhishek Sharma
919N/A * Kris De Gussem <Kris.DeGussem@gmail.com>
919N/A *
919N/A * Copyright (C) 2004 David Turner
919N/A * Copyright (C) 2003 MenTaLguY
919N/A * Copyright (C) 1999-2011 authors
919N/A * Copyright (C) 2001-2002 Ximian, Inc.
919N/A *
919N/A * Released under GNU GPL, read the file 'COPYING' for more information
919N/A */
919N/A
235N/A#ifdef HAVE_CONFIG_H
235N/A# include "config.h"
235N/A#endif
822N/A
235N/A#include <gtkmm.h>
970N/A#include <glibmm/i18n.h>
970N/A#include <list>
970N/A
970N/A#include "pencil-toolbar.h"
970N/A#include "desktop.h"
970N/A#include "widgets/ege-adjustment-action.h"
970N/A#include "widgets/ege-select-one-action.h"
1003N/A#include "widgets/ink-action.h"
970N/A#include "preferences.h"
235N/A#include "toolbox.h"
1252N/A#include "ui/tools-switch.h"
235N/A#include "ui/icon-names.h"
911N/A#include "ui/tools/pen-tool.h"
1252N/A#include "ui/uxmanager.h"
1252N/A#include "widgets/spinbutton-events.h"
911N/A#include <selection.h>
235N/A#include "display/curve.h"
493N/A#include "live_effects/effect.h"
493N/A#include "live_effects/lpe-simplify.h"
235N/A#include "live_effects/lpe-powerstroke.h"
235N/A#include "live_effects/effect-enum.h"
235N/A#include "live_effects/lpeobject.h"
235N/A#include "live_effects/lpeobject-reference.h"
822N/A#include "sp-lpe-item.h"
235N/A#include "util/glib-list-iterators.h"
1124N/A
1124N/Ausing Inkscape::UI::UXManager;
1124N/Ausing Inkscape::UI::ToolboxFactory;
235N/Ausing Inkscape::UI::PrefPusher;
1252N/A
235N/A//########################
235N/A//## Pen/Pencil ##
235N/A//########################
235N/A
1124N/A/* This is used in generic functions below to share large portions of code between pen and pencil tool */
1124N/Astatic Glib::ustring const freehand_tool_name(GObject *dataKludge)
1124N/A{
1124N/A SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(dataKludge, "desktop"));
1124N/A return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
822N/A ? "/tools/freehand/pen"
822N/A : "/tools/freehand/pencil" );
981N/A}
235N/A
1124N/Astatic void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
1124N/A{
1124N/A gint mode = ege_select_one_action_get_active(act);
970N/A
970N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
970N/A prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
970N/A
970N/A SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
970N/A
970N/A // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
947N/A // preparatory work here
235N/A if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
247N/A Inkscape::UI::Tools::PenTool *pc = SP_PEN_CONTEXT(desktop->event_context);
947N/A pc->setPolylineMode();
235N/A }
247N/A}
947N/A
947N/Astatic void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
970N/A{
970N/A /* Freehand mode toggle buttons */
947N/A {
947N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
947N/A guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
947N/A Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
947N/A
947N/A {
235N/A GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
241N/A
493N/A GtkTreeIter iter;
493N/A gtk_list_store_append( model, &iter );
235N/A gtk_list_store_set( model, &iter,
493N/A 0, _("Bezier"),
493N/A 1, _("Create regular Bezier path"),
1124N/A 2, INKSCAPE_ICON("path-mode-bezier"),
963N/A -1 );
1124N/A
1124N/A gtk_list_store_append( model, &iter );
1097N/A gtk_list_store_set( model, &iter,
493N/A 0, _("Spiro"),
1097N/A 1, _("Create Spiro path"),
1097N/A 2, INKSCAPE_ICON("path-mode-spiro"),
493N/A -1 );
235N/A gtk_list_store_append( model, &iter );
1124N/A gtk_list_store_set( model, &iter,
1124N/A 0, _("BSpline"),
1124N/A 1, _("Create BSpline path"),
1124N/A 2, INKSCAPE_ICON("path-mode-bspline"),
1124N/A -1 );
1124N/A if (!tool_is_pencil) {
1124N/A gtk_list_store_append( model, &iter );
1124N/A gtk_list_store_set( model, &iter,
1124N/A 0, _("Zigzag"),
1124N/A 1, _("Create a sequence of straight line segments"),
970N/A 2, INKSCAPE_ICON("path-mode-polyline"),
970N/A -1 );
970N/A
1097N/A gtk_list_store_append( model, &iter );
1097N/A gtk_list_store_set( model, &iter,
1097N/A 0, _("Paraxial"),
1030N/A 1, _("Create a sequence of paraxial line segments"),
822N/A 2, INKSCAPE_ICON("path-mode-polyline-paraxial"),
235N/A -1 );
235N/A }
822N/A
822N/A EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
963N/A "FreehandModeActionPencil" :
963N/A "FreehandModeActionPen",
935N/A (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
935N/A gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
935N/A
935N/A ege_select_one_action_set_appearance( act, "full" );
935N/A ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1088N/A g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
235N/A ege_select_one_action_set_icon_column( act, 2 );
935N/A ege_select_one_action_set_icon_size( act, secondarySize );
963N/A ege_select_one_action_set_tooltip_column( act, 1 );
963N/A
935N/A ege_select_one_action_set_active( act, freehandMode);
963N/A g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
963N/A }
963N/A }
963N/A}
235N/A
963N/Astatic void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
235N/A gint shape = ege_select_one_action_get_active( act );
935N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
935N/A prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
963N/A}
963N/A
935N/Astatic void freehand_simplify_lpe(InkToggleAction* itact, GObject *dataKludge) {
963N/A gint simplify = gtk_toggle_action_get_active( GTK_TOGGLE_ACTION(itact) );
963N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
963N/A prefs->setInt(freehand_tool_name(dataKludge) + "/simplify", simplify);
235N/A}
493N/A
1252N/A/**
1252N/A * Generate the list of freehand advanced shape option entries.
1252N/A */
1252N/Astatic GList * freehand_shape_dropdown_items_list() {
1252N/A GList *glist = NULL;
1252N/A
1252N/A glist = g_list_append (glist, const_cast<gchar *>(C_("Freehand shape", "None")));
1252N/A glist = g_list_append (glist, _("Triangle in"));
1252N/A glist = g_list_append (glist, _("Triangle out"));
1252N/A glist = g_list_append (glist, _("Ellipse"));
822N/A glist = g_list_append (glist, _("From clipboard"));
493N/A glist = g_list_append (glist, _("Bend from clipboard"));
822N/A glist = g_list_append (glist, _("Last applied"));
241N/A
235N/A return glist;
493N/A}
235N/A
493N/Astatic void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
235N/A{
606N/A /*advanced shape options */
606N/A {
606N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
606N/A GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
235N/A
922N/A GList* items = 0;
235N/A gint count = 0;
599N/A for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
1124N/A {
235N/A GtkTreeIter iter;
822N/A gtk_list_store_append( model, &iter );
1147N/A gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
1147N/A count++;
1124N/A }
606N/A g_list_free( items );
606N/A items = 0;
606N/A EgeSelectOneAction* act1 = ege_select_one_action_new(
235N/A tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
1030N/A _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
235N/A g_object_set( act1, "short_label", _("Shape:"), NULL );
606N/A ege_select_one_action_set_appearance( act1, "compact" );
606N/A ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
1097N/A g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
822N/A gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
822N/A g_object_set_data( holder, "shape_action", act1 );
1030N/A }
963N/A}
235N/A
235N/Avoid sp_pen_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
247N/A{
822N/A sp_add_freehand_mode_toggle(mainActions, holder, false);
1019N/A freehand_add_advanced_shape_options(mainActions, holder, false);
1030N/A}
1030N/A
1030N/A
1030N/Astatic void sp_pencil_tb_defaults(GtkWidget * /*widget*/, GObject *obj)
963N/A{
235N/A GtkWidget *tbl = GTK_WIDGET(obj);
963N/A
235N/A GtkAdjustment *adj;
963N/A
235N/A // fixme: make settable
963N/A gdouble tolerance = 4;
247N/A
963N/A adj = GTK_ADJUSTMENT(g_object_get_data(obj, "tolerance"));
963N/A gtk_adjustment_set_value(adj, tolerance);
822N/A gtk_adjustment_value_changed(adj);
963N/A
980N/A spinbutton_defocus(tbl);
822N/A}
822N/A
822N/Astatic void sp_simplify_flatten(GtkWidget * /*widget*/, GObject *obj)
822N/A{
822N/A SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(obj, "desktop"));
822N/A std::vector<SPItem *> selected = desktop->getSelection()->itemList();
822N/A for (std::vector<SPItem *>::iterator it(selected.begin()); it != selected.end(); ++it){
1124N/A SPLPEItem* lpeitem = dynamic_cast<SPLPEItem*>(*it);
1124N/A if (lpeitem && lpeitem->hasPathEffect()){
1124N/A PathEffectList lpelist = lpeitem->getEffectList();
822N/A std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
822N/A for (i = lpelist.begin(); i != lpelist.end(); ++i) {
822N/A LivePathEffectObject *lpeobj = (*i)->lpeobject;
822N/A if (lpeobj) {
822N/A Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
822N/A if (dynamic_cast<Inkscape::LivePathEffect::LPESimplify *>(lpe)) {
822N/A SPShape * shape = dynamic_cast<SPShape *>(lpeitem);
822N/A if(shape){
822N/A SPCurve * c = shape->getCurveBeforeLPE();
1124N/A lpe->doEffect(c);
1124N/A lpeitem->setCurrentPathEffect(*i);
822N/A if (lpelist.size() > 1){
963N/A lpeitem->removeCurrentPathEffect(true);
1124N/A shape->setCurveBeforeLPE(c);
970N/A } else {
970N/A lpeitem->removeCurrentPathEffect(false);
970N/A shape->setCurve(c,0);
970N/A }
970N/A break;
970N/A }
970N/A }
970N/A }
970N/A }
970N/A }
970N/A }
970N/A}
970N/A
970N/Astatic void sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
970N/A{
970N/A // quit if run by the attr_changed listener
if (g_object_get_data( tbl, "freeze" )) {
return;
}
// in turn, prevent listener from responding
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
prefs->setDouble("/tools/freehand/pencil/tolerance",
gtk_adjustment_get_value(adj));
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
std::vector<SPItem *> selected = desktop->getSelection()->itemList();
for (std::vector<SPItem *>::iterator it(selected.begin()); it != selected.end(); ++it){
SPLPEItem* lpeitem = dynamic_cast<SPLPEItem*>(*it);
if (lpeitem && lpeitem->hasPathEffect()){
Inkscape::LivePathEffect::Effect* simplify = lpeitem->getPathEffectOfType(Inkscape::LivePathEffect::SIMPLIFY);
if(simplify){
Inkscape::LivePathEffect::LPESimplify *lpe_simplify = dynamic_cast<Inkscape::LivePathEffect::LPESimplify*>(simplify->getLPEObj()->get_lpe());
if (lpe_simplify) {
double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0);
tol = tol/(100.0*(102.0-tol));
std::ostringstream ss;
ss << tol;
Inkscape::LivePathEffect::Effect* powerstroke = lpeitem->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE);
bool simplified = false;
if(powerstroke){
Inkscape::LivePathEffect::LPEPowerStroke *lpe_powerstroke = dynamic_cast<Inkscape::LivePathEffect::LPEPowerStroke*>(powerstroke->getLPEObj()->get_lpe());
if(lpe_powerstroke){
lpe_powerstroke->getRepr()->setAttribute("is_visible", "false");
sp_lpe_item_update_patheffect(lpeitem, false, false);
SPShape *sp_shape = dynamic_cast<SPShape *>(lpeitem);
if (sp_shape) {
guint previous_curve_length = sp_shape->getCurve()->get_segment_count();
lpe_simplify->getRepr()->setAttribute("threshold", ss.str());
sp_lpe_item_update_patheffect(lpeitem, false, false);
simplified = true;
guint curve_length = sp_shape->getCurve()->get_segment_count();
std::vector<Geom::Point> ts = lpe_powerstroke->offset_points.data();
double factor = (double)curve_length/ (double)previous_curve_length;
for (size_t i = 0; i < ts.size(); i++) {
ts[i][Geom::X] = ts[i][Geom::X] * factor;
}
lpe_powerstroke->offset_points.param_setValue(ts);
}
lpe_powerstroke->getRepr()->setAttribute("is_visible", "true");
sp_lpe_item_update_patheffect(lpeitem, false, false);
}
}
if(!simplified){
lpe_simplify->getRepr()->setAttribute("threshold", ss.str());
}
}
}
}
}
}
/*
class PencilToleranceObserver : public Inkscape::Preferences::Observer {
public:
PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
{
g_object_set_data(_obj, "prefobserver", this);
}
virtual ~PencilToleranceObserver() {
if (g_object_get_data(_obj, "prefobserver") == this) {
g_object_set_data(_obj, "prefobserver", NULL);
}
}
virtual void notify(Inkscape::Preferences::Entry const &val) {
GObject* tbl = _obj;
if (g_object_get_data( tbl, "freeze" )) {
return;
}
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
GtkAdjustment * adj = GTK_ADJUSTMENT(g_object_get_data(tbl, "tolerance"));
double v = val.getDouble(adj->value);
gtk_adjustment_set_value(adj, v);
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
}
private:
GObject *_obj;
};
*/
void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
{
sp_add_freehand_mode_toggle(mainActions, holder, true);
EgeAdjustmentAction* eact = 0;
/* Tolerance */
{
gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
eact = create_adjustment_action( "PencilToleranceAction",
_("Smoothing:"), _("Smoothing: "),
_("How much smoothing (simplifying) is applied to the line"),
"/tools/freehand/pencil/tolerance",
3.0,
GTK_WIDGET(desktop->canvas),
holder, TRUE, "altx-pencil",
1, 100.0, 0.5, 1.0,
labels, values, G_N_ELEMENTS(labels),
sp_pencil_tb_tolerance_value_changed,
NULL /*unit tracker*/,
1, 2);
ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
}
/* advanced shape options */
freehand_add_advanced_shape_options(mainActions, holder, true);
/* Reset */
{
InkAction* inky = ink_action_new( "PencilResetAction",
_("Defaults"),
_("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
INKSCAPE_ICON("edit-clear"),
Inkscape::ICON_SIZE_SMALL_TOOLBAR );
g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
}
/* LPE simplify based tolerance */
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
InkToggleAction* itact = ink_toggle_action_new( "PencilLpeSimplify",
_("LPE based interactive simplify"),
_("LPE based interactive simplify"),
INKSCAPE_ICON("interactive_simplify"),
Inkscape::ICON_SIZE_SMALL_TOOLBAR );
gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(itact), prefs->getInt("/tools/freehand/pencil/simplify", 0) );
g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(freehand_simplify_lpe), holder) ;
gtk_action_group_add_action( mainActions, GTK_ACTION(itact) );
}
/* LPE simplify flatten */
{
InkAction* inky = ink_action_new( "PencilLpeSimplifyFlatten",
_("LPE simplify flatten"),
_("LPE simplify flatten"),
INKSCAPE_ICON("flatten_simplify"),
Inkscape::ICON_SIZE_SMALL_TOOLBAR );
g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_simplify_flatten), holder );
gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
}
g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
}
/*
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 :