gradient-tool.cpp revision 9ba77856a8823f85b53c0a861d220cd0347f2754
/*
* Gradient drawing and editing tool
*
* Authors:
* bulia byak <buliabyak@users.sf.net>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Abhishek Sharma
*
* Copyright (C) 2007 Johan Engelen
* Copyright (C) 2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gdk/gdkkeysyms.h>
#include "macros.h"
#include "document.h"
#include "selection.h"
#include "desktop.h"
#include "message-context.h"
#include "message-stack.h"
#include "pixmaps/cursor-gradient.xpm"
#include "pixmaps/cursor-gradient-add.xpm"
#include "ui/tools/gradient-tool.h"
#include "gradient-chemistry.h"
#include "preferences.h"
#include "gradient-drag.h"
#include "gradient-chemistry.h"
#include "sp-item.h"
#include "display/sp-ctrlline.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-stop.h"
#include "svg/css-ostringstream.h"
#include "svg/svg-color.h"
#include "snap.h"
#include "sp-namedview.h"
#include "rubberband.h"
#include "document-undo.h"
#include "verbs.h"
#include "selection-chemistry.h"
using Inkscape::DocumentUndo;
#include "ui/tool-factory.h"
namespace Inkscape {
namespace UI {
namespace Tools {
namespace {
return new GradientTool();
}
bool gradientContextRegistered = ToolFactory::instance().registerObject("/tools/gradient", createGradientContext);
}
return GradientTool::prefsPath;
}
, cursor_addnode(false)
, node_added(false)
// TODO: Why are these connections stored as pointers?
{
// TODO: This value is overwritten in the root handler
this->tolerance = 6;
}
GradientTool::~GradientTool() {
this->enableGrDrag(false);
this->selcon->disconnect();
delete this->selcon;
this->subselcon->disconnect();
delete this->subselcon;
}
const gchar *gr_handle_descr [] = {
N_("Linear gradient <b>end</b>"),
N_("Linear gradient <b>mid stop</b>"),
N_("Radial gradient <b>center</b>"),
N_("Radial gradient <b>radius</b>"),
N_("Radial gradient <b>radius</b>"),
N_("Radial gradient <b>mid stop</b>"),
N_("Radial gradient <b>mid stop</b>")
};
return;
}
return;
//The use of ngettext in the following code is intentional even if the English singular form would never be used
if (n_sel == 1) {
//TRANSLATORS: %s will be substituted with the point name (see previous messages); This is part of a compound message
_("%s selected"),
//TRANSLATORS: Mind the space in front. This is part of a compound message
} else {
//TRANSLATORS: This is a part of a compound message (out of two more indicating: grandint handle count & object count)
ngettext("One handle merging %d stop (drag with <b>Shift</b> to separate) selected",
"One handle merging %d stops (drag with <b>Shift</b> to separate) selected",drag->singleSelectedDraggerNumDraggables()),
rc->message_context->setF(Inkscape::NORMAL_MESSAGE,message,drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj);
}
} else if (n_sel > 1) {
//TRANSLATORS: The plural refers to number of selected gradient handles. This is part of a compound message (part two indicates selected object count)
gchar * message = g_strconcat(ngettext("<b>%d</b> gradient handle selected out of %d","<b>%d</b> gradient handles selected out of %d",n_sel),
//TRANSLATORS: Mind the space in front. (Refers to gradient handles selected). This is part of a compound message
} else if (n_sel == 0) {
//TRANSLATORS: The plural refers to number of selected objects
ngettext("<b>No</b> gradient handles selected out of %d on %d selected object",
}
}
void GradientTool::setup() {
this->enableSelectionCue();
}
this->enableGrDrag();
));
))
));
this->selection_changed(selection);
}
void
{
}
void
{
}
static bool
{
//Translate mouse point into proper coord system
if (SP_IS_CTRLLINE(item)) {
return close;
}
return false;
}
{
// for all selected draggers
// remember the coord of the dragger to reselect it later
// for all draggables of dragger
// find the gradient
// these draggable types cannot have a next draggabe to insert a stop between them
if (d->point_type == POINT_LG_END ||
d->point_type == POINT_RG_FOCUS ||
d->point_type == POINT_RG_R1 ||
d->point_type == POINT_RG_R2) {
continue;
}
// from draggables to stops
// if there's a next stop,
if (next_stop) {
// find its dragger
// (complex because it may have different types, and because in radial,
// more than one dragger may correspond to a stop, so we must distinguish)
} else {
}
} else { // radial
} else {
}
}
if ((type == POINT_RG_MID2) ||
} else {
}
}
}
// if both adjacent draggers selected,
// remember the coords of the future dragger to select it
// do not insert a stop now, it will confuse the loop;
// just remember the stops
}
}
}
}
return coords;
}
void
{
std::vector<Geom::Point> coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops);
// if a single stop is selected, add between that stop and the next one
if (d->point_type == POINT_RG_FOCUS) {
/*
* There are 2 draggables at the center (start) of a radial gradient
* To avoid creating 2 seperate stops, ignore this draggable point type
*/
continue;
}
if (this_stop) {
if (next_stop) {
}
}
}
}
// now actually create the new stops
GSList *i = these_stops;
GSList *j = next_stops;
if (SP_IS_GRADIENT (parent)) {
}
}
drag->updateDraggers();
// so that it does not automatically update draggers in idle loop, as this would deselect
drag->local_change = true;
// select the newly created stops
}
}
}
static double sqr(double x) {return x*x;}
static void
{
std::vector<Geom::Point> coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops);
GSList *i = these_stops;
GSList *j = next_stops;
if (i1 != -1) {
if (next_next) {
continue;
double diff =
}
}
}
}
if (g_slist_length(todel) > 0) {
drag->local_change = true;
drag->updateDraggers();
}
}
static void
sp_gradient_context_add_stop_near_point (GradientTool *rc, SPItem *item, Geom::Point mouse_p, guint32 /*etime*/)
{
// item is the selected item. mouse_p the location in doc coordinates of where to add the stop
SPStop *newstop = ec->get_drag()->addStopNearPoint (item, mouse_p, tolerance/desktop->current_zoom());
_("Add gradient stop"));
}
static bool dragging;
double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px
case GDK_2BUTTON_PRESS:
bool over_line = false;
over_line |= sp_gradient_context_is_over_line (this, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y));
}
}
if (over_line) {
// we take the first item in selection, because with doubleclick, the first click
// always resets selection to the single object under cursor
sp_gradient_context_add_stop_near_point(this, SP_ITEM(selection->itemList()->data), this->mousepoint_doc, event->button.time);
} else {
SPGradientType new_type = (SPGradientType) prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR);
Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
}
_("Create default gradient"));
}
}
break;
case GDK_BUTTON_PRESS:
// save drag origin
this->within_tolerance = true;
dragging = true;
} else {
// remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to
// enable Ctrl+doubleclick of exactly the selected item(s)
this->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE);
}
m.unSetup();
}
}
}
break;
case GDK_MOTION_NOTIFY:
if ( this->within_tolerance
break; // do not drag if we're within tolerance from origin
}
// Once the user has moved farther than tolerance from the original location
// (indicating they intend to draw, not click), then always process the
// motion notify coordinates as given (no snapping back to origin)
this->within_tolerance = false;
this->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw around</b> handles to select them"));
} else {
}
} else {
m.unSetup();
}
bool over_line = false;
over_line |= sp_gradient_context_is_over_line (this, (SPItem*) l->data, Geom::Point(event->motion.x, event->motion.y));
}
}
if (this->cursor_addnode && !over_line) {
this->cursor_shape = cursor_gradient_xpm;
this->sp_event_context_update_cursor();
this->cursor_addnode = false;
} else if (!this->cursor_addnode && over_line) {
this->cursor_shape = cursor_gradient_add_xpm;
this->sp_event_context_update_cursor();
this->cursor_addnode = true;
}
}
break;
case GDK_BUTTON_RELEASE:
bool over_line = false;
over_line = sp_gradient_context_is_over_line (this, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y));
if (over_line)
break;
}
}
}
} else {
dragging = false;
// unless clicked with Ctrl (to enable Ctrl+doubleclick).
break;
}
if (!this->within_tolerance) {
// we've been dragging, either do nothing (grdrag handles that),
// or rubberband-select if we have rubberband
if (r->is_started() && !this->within_tolerance) {
// this was a rubberband drag
if (r->getMode() == RUBBERBAND_MODE_RECT) {
drag->selectRect(*b);
}
}
} else if (this->item_to_select) {
// Clicked on an existing gradient line, dont change selection. This stops
// possible change in selection during a double click with overlapping objects
} else {
// no dragging, select clicked item if any
} else {
drag->deselectAll();
}
}
} else {
// click in an empty space; do the same as Esc
drag->deselectAll();
} else {
}
}
this->item_to_select = NULL;
}
}
break;
case GDK_KEY_PRESS:
case GDK_KEY_Alt_L:
case GDK_KEY_Alt_R:
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
case GDK_KEY_Shift_L:
case GDK_KEY_Shift_R:
case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine)
case GDK_KEY_Meta_R:
_("<b>Ctrl</b>: snap gradient angle"),
_("<b>Shift</b>: draw gradient around the starting point"),
NULL);
break;
case GDK_KEY_x:
case GDK_KEY_X:
if (MOD__ALT_ONLY(event)) {
}
break;
case GDK_KEY_A:
case GDK_KEY_a:
}
break;
case GDK_KEY_L:
case GDK_KEY_l:
sp_gradient_simplify(this, 1e-4);
}
break;
case GDK_KEY_Escape:
drag->deselectAll();
} else {
}
//TODO: make dragging escapable by Esc
break;
case GDK_KEY_Left: // move handle left
case GDK_KEY_KP_Left:
case GDK_KEY_KP_4:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Up: // move handle up
case GDK_KEY_KP_Up:
case GDK_KEY_KP_8:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Right: // move handle right
case GDK_KEY_KP_Right:
case GDK_KEY_KP_6:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Down: // move handle down
case GDK_KEY_KP_Down:
case GDK_KEY_KP_2:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_r:
case GDK_KEY_R:
if (MOD__SHIFT_ONLY(event)) {
}
break;
case GDK_KEY_Insert:
case GDK_KEY_KP_Insert:
// with any modifiers:
break;
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
break;
default:
break;
}
break;
case GDK_KEY_RELEASE:
case GDK_KEY_Alt_L:
case GDK_KEY_Alt_R:
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
case GDK_KEY_Shift_L:
case GDK_KEY_Shift_R:
case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt
case GDK_KEY_Meta_R:
this->defaultMessageContext()->clear();
break;
default:
break;
}
break;
default:
break;
}
if (!ret) {
}
return ret;
}
static void sp_gradient_drag(GradientTool &rc, Geom::Point const pt, guint /*state*/, guint32 etime)
{
Inkscape::PaintTarget fill_or_stroke = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
if (ec->item_to_select) {
// pick color from the object where drag started
} else {
// Starting from empty space:
// Sort items so that the topmost comes last
// take topmost
vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(g_slist_last(items)->data), fill_or_stroke);
}
// HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
//FIXME: see above
if (type == SP_GRADIENT_TYPE_LINEAR) {
sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_LG_BEGIN, 0, rc.origin, fill_or_stroke, true, false);
} else if (type == SP_GRADIENT_TYPE_RADIAL) {
sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_RG_CENTER, 0, rc.origin, fill_or_stroke, true, false);
}
}
// prevent regenerating draggers by selection modified signal, which sometimes
// comes too late and thus destroys the knot which we will now grab:
// and therefore are already out of tolerance
-1, // ignore number (though it is always 1)
}
// We did an undoable action, but SPDocumentUndo::done will be called by the knot when released
// status text; we do not track coords because this branch is run once, not all the time
// during drag
ngettext("<b>Gradient</b> for %d object; with <b>Ctrl</b> to snap angle",
"<b>Gradient</b> for %d objects; with <b>Ctrl</b> to snap angle", n_objects),
} else {
desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>objects</b> on which to create gradient."));
}
}
}
}
}
/*
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 :