static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item) { g_assert(SP_IS_ITEM(item)); g_assert(!SP_IS_ANCHOR(item)); SPDesktop *desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop"); g_return_if_fail(desktop != NULL); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *repr = xml_doc->createElement("svg:a"); SP_OBJECT_REPR(SP_OBJECT_PARENT(item))->addChild(repr, SP_OBJECT_REPR(item)); SPObject *object = SP_OBJECT_DOCUMENT(item)->getObjectByRepr(repr); g_return_if_fail(SP_IS_ANCHOR(object)); const char *id = SP_OBJECT_REPR(item)->attribute("id"); Inkscape::XML::Node *child = SP_OBJECT_REPR(item)->duplicate(xml_doc); SP_OBJECT(item)->deleteObject(false); repr->addChild(child, NULL); child->setAttribute("id", id); Inkscape::GC::release(repr); Inkscape::GC::release(child); sp_document_done(SP_OBJECT_DOCUMENT(object), SP_VERB_NONE, _("Create link")); sp_object_attributes_dialog(object, "SPAnchor"); sp_desktop_selection(desktop)->set(SP_ITEM(object)); }
//FIXME: must work with text selection void text_remove_all_kerns() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>text(s)</b> to remove kerns from.")); return; } bool did = false; for (GSList *items = g_slist_copy((GSList *) selection->itemList()); items != NULL; items = items->next) { SPObject *obj = SP_OBJECT(items->data); if (!SP_IS_TEXT(obj) && !SP_IS_TSPAN(obj) && !SP_IS_FLOWTEXT(obj)) { continue; } text_remove_all_kerns_recursively(obj); obj->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); did = true; } if (!did) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("Select <b>text(s)</b> to remove kerns from.")); } else { sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Remove manual kerns")); } }
static void vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data) { VPDragger *dragger = (VPDragger *) data; dragger->point_original = dragger->point = knot->pos; dragger->dragging_started = false; for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) { (*i).set_pos (knot->pos); (*i).updateBoxReprs(); (*i).updatePerspRepr(); } dragger->parent->updateDraggers (); dragger->parent->updateLines (); dragger->parent->updateBoxHandles (); // TODO: Update box's paths and svg representation dragger->parent->dragging = false; // TODO: Undo machinery!! g_return_if_fail (dragger->parent); g_return_if_fail (dragger->parent->document); sp_document_done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX, _("3D box: Move vanishing point")); }
void GuidelinePropertiesDialog::_onApply() { double deg_angle = _spin_angle.get_value(); if (!_mode) deg_angle += _oldangle; Geom::Point normal; if ( deg_angle == 90. || deg_angle == 270. || deg_angle == -90. || deg_angle == -270.) { normal = Geom::Point(1.,0.); } else if ( deg_angle == 0. || deg_angle == 180. || deg_angle == -180.) { normal = Geom::Point(0.,1.); } else { double rad_angle = Geom::deg_to_rad( deg_angle ); normal = Geom::rot90(Geom::Point::polar(rad_angle, 1.0)); } sp_guide_set_normal(*_guide, normal, true); SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(_unit_selector->gobj())); gdouble const raw_dist_x = _spin_button_x.get_value(); gdouble const points_x = sp_units_get_pixels(raw_dist_x, unit); gdouble const raw_dist_y = _spin_button_y.get_value(); gdouble const points_y = sp_units_get_pixels(raw_dist_y, unit); Geom::Point newpos(points_x, points_y); if (!_mode) newpos += _oldpos; sp_guide_moveto(*_guide, newpos, true); sp_document_done(SP_OBJECT_DOCUMENT(_guide), SP_VERB_NONE, _("Set guide properties")); }
void GuidelinePropertiesDialog::_onDelete() { SPDocument *doc = SP_OBJECT_DOCUMENT(_guide); sp_guide_remove(_guide); sp_document_done(doc, SP_VERB_NONE, _("Delete guide")); }
static void sp_box3d_finish(Box3DContext *bc) { bc->_message_context->clear(); bc->ctrl_dragged = false; bc->extruded = false; if ( bc->item != NULL ) { SPDesktop * desktop = SP_EVENT_CONTEXT_DESKTOP(bc); SPDocument *doc = sp_desktop_document(desktop); if (!doc || !doc->getCurrentPersp3D()) return; SPBox3D *box = SP_BOX3D(bc->item); box->orig_corner0 = bc->drag_origin_proj; box->orig_corner7 = bc->drag_ptC_proj; box->updateRepr(); box3d_relabel_corners(box); desktop->canvas->end_forced_full_redraws(); sp_desktop_selection(desktop)->set(bc->item); sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX, _("Create 3D box")); bc->item = NULL; } }
void LivePathEffectEditor::onApply() { Inkscape::Selection *sel = _getSelection(); if ( sel && !sel->isEmpty() ) { SPItem *item = sel->singleItem(); if ( item && SP_IS_LPE_ITEM(item) ) { SPDocument *doc = current_desktop->doc(); const Util::EnumData<LivePathEffect::EffectType>* data = combo_effecttype.get_active_data(); if (!data) return; // If item is a SPRect, convert it to path first: if ( SP_IS_RECT(item) ) { sp_selected_path_to_curves(current_desktop, false); item = sel->singleItem(); // get new item } LivePathEffect::Effect::createAndApply(data->key.c_str(), doc, item); sp_document_done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Create and apply path effect")); lpe_list_locked = false; onSelectionChanged(sel); } } }
static void sp_fill_style_widget_fill_rule_activate (GtkWidget *w, SPWidget *spw) { const GSList *items, *i, *r; GSList *reprs; SPCSSAttr *css; if (g_object_get_data (G_OBJECT (spw), "update")) return; if (spw->inkscape) { reprs = NULL; items = sp_widget_get_item_list (spw); for (i = items; i != NULL; i = i->next) { reprs = g_slist_prepend (reprs, SP_OBJECT_REPR (i->data)); } } else { reprs = g_slist_prepend (NULL, spw->repr); items = NULL; } css = sp_repr_css_attr_new (); sp_repr_css_set_property (css, "fill-rule", (const gchar *)g_object_get_data (G_OBJECT (w), "fill-rule")); for (r = reprs; r != NULL; r = r->next) { sp_repr_css_change_recursive ((SPRepr *) r->data, css, "style"); } sp_repr_css_attr_unref (css); if (spw->inkscape) sp_document_done (SP_WIDGET_DOCUMENT (spw)); g_slist_free (reprs); }
void sp_selected_path_reverse(SPDesktop *desktop) { Inkscape::Selection *selection = sp_desktop_selection(desktop); GSList *items = (GSList *) selection->itemList(); if (!items) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to reverse.")); return; } // set "busy" cursor desktop->setWaitingCursor(); bool did = false; desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Reversing paths...")); for (GSList *i = items; i != NULL; i = i->next) { if (!SP_IS_PATH(i->data)) continue; did = true; SPPath *path = SP_PATH(i->data); SPCurve *rcurve = sp_path_get_curve_reference(path)->create_reverse(); gchar *str = sp_svg_write_path(rcurve->get_pathvector()); if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(path)) ) { SP_OBJECT_REPR(path)->setAttribute("inkscape:original-d", str); } else { SP_OBJECT_REPR(path)->setAttribute("d", str); } g_free(str); rcurve->unref(); // reverse nodetypes order (Bug #179866) gchar *nodetypes = g_strdup(SP_OBJECT_REPR(path)->attribute("sodipodi:nodetypes")); if ( nodetypes ) { SP_OBJECT_REPR(path)->setAttribute("sodipodi:nodetypes", g_strreverse(nodetypes)); g_free(nodetypes); } } desktop->clearWaitingCursor(); if (did) { sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_REVERSE, _("Reverse path")); } else { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to reverse in the selection.")); } }
static void sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc) { SPDocument *doc = NULL; GrDrag *drag = rc->_grdrag; GSList *these_stops = NULL; GSList *next_stops = NULL; std::vector<Geom::Point> coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops); if (g_slist_length(these_stops) == 0 && drag->numSelected() == 1) { // if a single stop is selected, add between that stop and the next one GrDragger *dragger = (GrDragger *) drag->selected->data; for (GSList const* j = dragger->draggables; j != NULL; j = j->next) { GrDraggable *d = (GrDraggable *) j->data; SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false); SPStop *this_stop = sp_get_stop_i (vector, d->point_i); SPStop *next_stop = sp_next_stop (this_stop); if (this_stop && next_stop) { these_stops = g_slist_prepend (these_stops, this_stop); next_stops = g_slist_prepend (next_stops, next_stop); } } } // now actually create the new stops GSList *i = these_stops; GSList *j = next_stops; for (; i != NULL && j != NULL; i = i->next, j = j->next) { SPStop *this_stop = (SPStop *) i->data; SPStop *next_stop = (SPStop *) j->data; gfloat offset = 0.5*(this_stop->offset + next_stop->offset); SPObject *parent = SP_OBJECT_PARENT(this_stop); if (SP_IS_GRADIENT (parent)) { doc = SP_OBJECT_DOCUMENT (parent); sp_vector_add_stop (SP_GRADIENT (parent), this_stop, next_stop, offset); sp_gradient_ensure_vector (SP_GRADIENT (parent)); } } if (g_slist_length(these_stops) > 0 && doc) { sp_document_done (doc, SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop")); drag->updateDraggers(); // so that it does not automatically update draggers in idle loop, as this would deselect drag->local_change = true; // select all the old selected and new created draggers drag->selectByCoords(coords); } g_slist_free (these_stops); g_slist_free (next_stops); }
void sp_sel_trans_stamp (SPSelTrans * seltrans) { /* stamping mode */ SPItem * original_item, * copy_item; SPRepr * original_repr, * copy_repr; GSList * l; gchar tstr[80]; NRMatrixF i2d, i2dnew; NRMatrixF *new_affine; tstr[79] = '\0'; if (!seltrans->empty) { if (seltrans->stamp_cache) { l = seltrans->stamp_cache; } else { /* Build cache */ l = (GSList *) sp_selection_item_list (SP_DT_SELECTION (seltrans->desktop)); l = g_slist_copy (l); l = g_slist_sort (l, (GCompareFunc) sp_object_compare_position); seltrans->stamp_cache = l; } while (l) { original_item = SP_ITEM(l->data); original_repr = (SPRepr *)(SP_OBJECT (original_item)->repr); copy_repr = sp_repr_duplicate (original_repr); copy_item = (SPItem *) sp_document_add_repr (SP_DT_DOCUMENT (seltrans->desktop), copy_repr); if (seltrans->show == SP_SELTRANS_SHOW_OUTLINE) { sp_item_i2d_affine (original_item, &i2d); nr_matrix_multiply_ffd (&i2dnew, &i2d, &seltrans->current); sp_item_set_i2d_affine (copy_item, &i2dnew); new_affine = ©_item->transform; } else { new_affine = &original_item->transform; } if (sp_svg_transform_write (tstr, 80, new_affine)) { sp_repr_set_attr (copy_repr, "transform", tstr); } else { sp_repr_set_attr (copy_repr, "transform", NULL); } sp_repr_unref (copy_repr); l = l->next; } sp_document_done (SP_DT_DOCUMENT (seltrans->desktop)); } }
static void knot_ungrabbed_handler (SPKnot *knot, unsigned int state, SPKnotHolder *kh) { if (kh->released) { kh->released (kh->item); } else { SPObject *object; object = (SPObject *) kh->item; sp_object_invoke_write (object, object->repr, SP_OBJECT_WRITE_EXT); sp_document_done (SP_OBJECT_DOCUMENT (kh->item)); } }
/// \pre it is assumed that the license URI entry is a Gtk::Entry void LicenseItem::on_toggled() { if (_wr.isUpdating()) return; _wr.setUpdating (true); rdf_set_license (SP_ACTIVE_DOCUMENT, _lic->details ? _lic : 0); sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_NONE, /* TODO: annotate */ "licensor.cpp:65"); _wr.setUpdating (false); static_cast<Gtk::Entry*>(_eep->_packable)->set_text (_lic->uri); _eep->on_changed(); }
void LivePathEffectEditor::onDown() { Inkscape::Selection *sel = _getSelection(); if ( sel && !sel->isEmpty() ) { SPItem *item = sel->singleItem(); if ( item && SP_IS_LPE_ITEM(item) ) { sp_lpe_item_down_current_path_effect(SP_LPE_ITEM(item)); sp_document_done ( sp_desktop_document (current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Move path effect down") ); effect_list_reload(SP_LPE_ITEM(item)); } } }
void gr_item_activate (GtkMenuItem *menuitem, gpointer data) { SPGradient *gr = (SPGradient *) g_object_get_data (G_OBJECT (menuitem), "gradient"); gr = sp_gradient_ensure_vector_normalized(gr); SPDesktop *desktop = (SPDesktop *) data; Inkscape::Selection *selection = sp_desktop_selection (desktop); SPEventContext *ev = sp_desktop_event_context (desktop); gr_apply_gradient (selection, ev? ev->get_drag() : NULL, gr); sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT, _("Assign gradient to object")); }
static void sp_text_edit_dialog_apply( GtkButton */*button*/, GtkWidget *dlg ) { g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE)); GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); SPDesktop *desktop = SP_ACTIVE_DESKTOP; unsigned items = 0; const GSList *item_list = sp_desktop_selection(desktop)->itemList(); SPCSSAttr *css = sp_get_text_dialog_style (); sp_desktop_set_style(desktop, css, true); for (; item_list != NULL; item_list = item_list->next) { // apply style to the reprs of all text objects in the selection if (SP_IS_TEXT (item_list->data)) { // backwards compatibility: SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); ++items; } else if (SP_IS_FLOWTEXT (item_list->data)) // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext ++items; } if (items == 0) { // no text objects; apply style to prefs for new objects Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->mergeStyle("/tools/text/style", css); gtk_widget_set_sensitive (def, FALSE); } else if (items == 1) { /* exactly one text object; now set its text, too */ SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem(); if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) { sp_text_edit_dialog_update_object_text (item); } } // complete the transaction sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT, _("Set text style")); gtk_widget_set_sensitive (apply, FALSE); sp_repr_css_attr_unref (css); g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE)); }
void LivePathEffectEditor::on_visibility_toggled( Glib::ustring const& str ) { Gtk::TreeModel::Children::iterator iter = effectlist_view.get_model()->get_iter(str); Gtk::TreeModel::Row row = *iter; LivePathEffect::LPEObjectReference * lpeobjref = row[columns.lperef]; if ( lpeobjref && lpeobjref->lpeobject->get_lpe() ) { bool newValue = !row[columns.col_visible]; row[columns.col_visible] = newValue; /* FIXME: this explicit writing to SVG is wrong. The lpe_item should have a method to disable/enable an effect within its stack. * So one can call: lpe_item->setActive(lpeobjref->lpeobject); */ lpeobjref->lpeobject->get_lpe()->getRepr()->setAttribute("is_visible", newValue ? "true" : "false"); sp_document_done( sp_desktop_document(current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, newValue ? _("Activate path effect") : _("Deactivate path effect")); } }
static void sp_gradient_context_add_stop_near_point (SPGradientContext *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 SPEventContext *ec = SP_EVENT_CONTEXT(rc); SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop; double tolerance = (double) ec->tolerance; ec->get_drag()->addStopNearPoint (item, mouse_p, tolerance/desktop->current_zoom()); sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop")); ec->get_drag()->updateDraggers(); }
void sp_item_widget_hidden_toggled(GtkWidget *widget, SPWidget *spw) { if (gtk_object_get_data (GTK_OBJECT (spw), "blocked")) return; SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem(); g_return_if_fail (item != NULL); gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE)); item->setExplicitlyHidden(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))? _("Hide object") : _("Unhide object")); gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE)); }
/* This function is an entry point from GUI */ void sp_selected_path_to_curves(SPDesktop *desktop, bool interactive) { Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { if (interactive) sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to convert to path.")); return; } bool did = false; if (interactive) { desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Converting objects to paths...")); // set "busy" cursor desktop->setWaitingCursor(); } GSList *selected = g_slist_copy((GSList *) selection->itemList()); GSList *to_select = NULL; selection->clear(); GSList *items = g_slist_copy(selected); did = sp_item_list_to_curves(items, &selected, &to_select); g_slist_free (items); selection->setReprList(to_select); selection->addList(selected); g_slist_free (to_select); g_slist_free (selected); if (interactive) { desktop->clearWaitingCursor(); if (did) { sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_TO_CURVE, _("Object to path")); } else { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No objects</b> to convert to path in the selection.")); return; } } }
static void sp_star_finish (SPStarContext * sc) { if (sc->item != NULL) { SPDesktop *desktop; SPObject *object; desktop = SP_EVENT_CONTEXT (sc)->desktop; object = SP_OBJECT(sc->item); sp_shape_set_shape (SP_SHAPE (sc->item)); sp_object_invoke_write (object, NULL, SP_OBJECT_WRITE_EXT); sp_selection_set_item (SP_DT_SELECTION (desktop), sc->item); sp_document_done (SP_DT_DOCUMENT (desktop)); sc->item = NULL; } }
static void sp_spiral_finish(SPSpiralContext *sc) { sc->_message_context->clear(); if (sc->item != NULL) { SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop; SPSpiral *spiral = SP_SPIRAL(sc->item); sp_shape_set_shape(SP_SHAPE(spiral)); SP_OBJECT(spiral)->updateRepr(SP_OBJECT_WRITE_EXT); sp_canvas_end_forced_full_redraws(desktop->canvas); sp_desktop_selection(desktop)->set(sc->item); sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL, _("Create spiral")); sc->item = NULL; } }
void text_remove_from_path() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text on path</b> to remove it from path.")); return; } bool did = false; for (GSList *items = g_slist_copy((GSList *) selection->itemList()); items != NULL; items = items->next) { if (!SP_IS_TEXT_TEXTPATH(SP_OBJECT(items->data))) { continue; } SPObject *tp = sp_object_first_child(SP_OBJECT(items->data)); did = true; sp_textpath_to_text(tp); } if (!did) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> in the selection.")); } else { sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Remove text from path")); selection->setList(g_slist_copy((GSList *) selection->itemList())); // reselect to update statusbar description } }
static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEvent *event) { static bool dragging; SPDesktop *desktop = event_context->desktop; SPDocument *document = sp_desktop_document (desktop); Inkscape::Selection *selection = sp_desktop_selection (desktop); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); Box3DContext *bc = SP_BOX3D_CONTEXT(event_context); Persp3D *cur_persp = document->getCurrentPersp3D(); event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); gint ret = FALSE; switch (event->type) { case GDK_BUTTON_PRESS: if ( event->button.button == 1 && !event_context->space_panning) { Geom::Point const button_w(event->button.x, event->button.y); // save drag origin event_context->xp = (gint) button_w[Geom::X]; event_context->yp = (gint) button_w[Geom::Y]; event_context->within_tolerance = true; // remember clicked item, *not* disregarding groups (since a 3D box is a group), honoring Alt event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, event->button.state & GDK_CONTROL_MASK); dragging = true; /* */ Geom::Point button_dt(desktop->w2d(button_w)); bc->drag_origin = from_2geom(button_dt); bc->drag_ptB = from_2geom(button_dt); bc->drag_ptC = from_2geom(button_dt); // This can happen after saving when the last remaining perspective was purged and must be recreated. if (!cur_persp) { sp_box3d_context_ensure_persp_in_defs(document); cur_persp = document->getCurrentPersp3D(); } /* Projective preimages of clicked point under current perspective */ bc->drag_origin_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(button_dt), 0, Proj::Z); bc->drag_ptB_proj = bc->drag_origin_proj; bc->drag_ptC_proj = bc->drag_origin_proj; bc->drag_ptC_proj.normalize(); bc->drag_ptC_proj[Proj::Z] = 0.25; /* Snap center */ SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, bc->item); m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); bc->center = from_2geom(button_dt); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), ( GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK ), NULL, event->button.time); ret = TRUE; } break; case GDK_MOTION_NOTIFY: if ( dragging && ( event->motion.state & GDK_BUTTON1_MASK ) && !event_context->space_panning) { if ( event_context->within_tolerance && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance ) && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->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) event_context->within_tolerance = false; Geom::Point const motion_w(event->motion.x, event->motion.y); Geom::Point motion_dt(desktop->w2d(motion_w)); SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, bc->item); m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); bc->ctrl_dragged = event->motion.state & GDK_CONTROL_MASK; if (event->motion.state & GDK_SHIFT_MASK && !bc->extruded && bc->item) { // once shift is pressed, set bc->extruded bc->extruded = true; } if (!bc->extruded) { bc->drag_ptB = from_2geom(motion_dt); bc->drag_ptC = from_2geom(motion_dt); bc->drag_ptB_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), 0, Proj::Z); bc->drag_ptC_proj = bc->drag_ptB_proj; bc->drag_ptC_proj.normalize(); bc->drag_ptC_proj[Proj::Z] = 0.25; } else { // Without Ctrl, motion of the extruded corner is constrained to the // perspective line from drag_ptB to vanishing point Y. if (!bc->ctrl_dragged) { /* snapping */ Box3D::PerspectiveLine pline (bc->drag_ptB, Proj::Z, document->getCurrentPersp3D()); bc->drag_ptC = pline.closest_to (from_2geom(motion_dt)); bc->drag_ptB_proj.normalize(); bc->drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage (bc->drag_ptC, bc->drag_ptB_proj[Proj::X], Proj::X); } else { bc->drag_ptC = from_2geom(motion_dt); bc->drag_ptB_proj.normalize(); bc->drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), bc->drag_ptB_proj[Proj::X], Proj::X); } m.freeSnapReturnByRef(bc->drag_ptC, Inkscape::SNAPSOURCE_NODE_HANDLE); } sp_box3d_drag(*bc, event->motion.state); ret = TRUE; } else if (!sp_event_context_knot_mouseover(bc)) { SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); Geom::Point const motion_w(event->motion.x, event->motion.y); Geom::Point motion_dt(desktop->w2d(motion_w)); m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: event_context->xp = event_context->yp = 0; if ( event->button.button == 1 && !event_context->space_panning) { dragging = false; sp_event_context_discard_delayed_snap_event(event_context); if (!event_context->within_tolerance) { // we've been dragging, finish the box sp_box3d_finish(bc); } else if (event_context->item_to_select) { // no dragging, select clicked item if any if (event->button.state & GDK_SHIFT_MASK) { selection->toggle(event_context->item_to_select); } else { selection->set(event_context->item_to_select); } } else { // click in an empty space selection->clear(); } event_context->item_to_select = NULL; ret = TRUE; sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); } break; case GDK_KEY_PRESS: switch (get_group0_keyval (&event->key)) { case GDK_Up: case GDK_Down: case GDK_KP_Up: case GDK_KP_Down: // prevent the zoom field from activation if (!MOD__CTRL_ONLY) ret = TRUE; break; case GDK_bracketright: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::X, -180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_bracketleft: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::X, 180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_parenright: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Y, -180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_parenleft: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Y, 180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_braceright: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Z, -180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; case GDK_braceleft: persp3d_rotate_VP (document->getCurrentPersp3D(), Proj::Z, 180/snaps, MOD__ALT); sp_document_done(document, SP_VERB_CONTEXT_3DBOX, _("Change perspective (angle of PLs)")); ret = true; break; /* TODO: what is this??? case GDK_O: if (MOD__CTRL && MOD__SHIFT) { Box3D::create_canvas_point(persp3d_get_VP(document()->getCurrentPersp3D(), Proj::W).affine(), 6, 0xff00ff00); } ret = true; break; */ case GDK_g: case GDK_G: if (MOD__SHIFT_ONLY) { sp_selection_to_guides(desktop); ret = true; } break; case GDK_p: case GDK_P: if (MOD__SHIFT_ONLY) { if (document->getCurrentPersp3D()) { persp3d_print_debugging_info (document->getCurrentPersp3D()); } ret = true; } break; case GDK_x: case GDK_X: if (MOD__ALT_ONLY) { desktop->setToolboxFocusTo ("altx-box3d"); ret = TRUE; } if (MOD__SHIFT_ONLY) { persp3d_toggle_VPs(selection->perspList(), Proj::X); bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically? ret = true; } break; case GDK_y: case GDK_Y: if (MOD__SHIFT_ONLY) { persp3d_toggle_VPs(selection->perspList(), Proj::Y); bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically? ret = true; } break; case GDK_z: case GDK_Z: if (MOD__SHIFT_ONLY) { persp3d_toggle_VPs(selection->perspList(), Proj::Z); bc->_vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically? ret = true; } break; case GDK_Escape: sp_desktop_selection(desktop)->clear(); //TODO: make dragging escapable by Esc break; case GDK_space: if (dragging) { sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); dragging = false; sp_event_context_discard_delayed_snap_event(event_context); if (!event_context->within_tolerance) { // we've been dragging, finish the box sp_box3d_finish(bc); } // do not return true, so that space would work switching to selector } break; default: break; } break; default: break; } if (!ret) { if (((SPEventContextClass *) parent_class)->root_handler) { ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); } } return ret; }
static void sp_text_edit_dialog_update_object (SPText *text, SPRepr *repr) { g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE)); if (text) { GtkTextBuffer *tb; GtkTextIter start, end; gchar *str; tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text"); /* Content */ gtk_text_buffer_get_bounds (tb, &start, &end); str = gtk_text_buffer_get_text (tb, &start, &end, TRUE); sp_text_set_repr_text_multiline (text, str); g_free (str); } if (repr) { GtkWidget *fontsel, *preview, *b, *combo; SPCSSAttr *css; NRFont *font; gchar c[256]; const char *sstr; fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel"); preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview"); css = sp_repr_css_attr_new (); /* font */ font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel)); nr_typeface_family_name_get (NR_FONT_TYPEFACE (font), c, 256); sp_repr_css_set_property (css, "font-family", c); nr_typeface_attribute_get (NR_FONT_TYPEFACE (font), "weight", c, 256); g_strdown (c); sp_repr_css_set_property (css, "font-weight", c); nr_typeface_attribute_get (NR_FONT_TYPEFACE (font), "style", c, 256); g_strdown (c); sp_repr_css_set_property (css, "font-style", c); g_snprintf (c, 64, "%g", NR_FONT_SIZE (font)); sp_repr_css_set_property (css, "font-size", c); nr_font_unref (font); /* Layout */ b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start"); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { sp_repr_css_set_property (css, "text-anchor", "start"); } else { b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_middle"); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { sp_repr_css_set_property (css, "text-anchor", "middle"); } else { sp_repr_css_set_property (css, "text-anchor", "end"); } } b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "writing_mode_lr"); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { sp_repr_css_set_property (css, "writing-mode", "lr"); } else { sp_repr_css_set_property (css, "writing-mode", "tb"); } combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing"); sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry); sp_repr_set_attr (repr, "sodipodi:linespacing", sstr); sp_repr_css_change (repr, css, "style"); sp_repr_css_attr_unref (css); } if (text) { sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP)); sp_document_ensure_up_to_date (SP_OBJECT_DOCUMENT (text)); } g_object_set_data (G_OBJECT (dlg), "blocked", NULL); }
static gint sp_dropper_context_root_handler(SPEventContext *event_context, GdkEvent *event) { SPDropperContext *dc = (SPDropperContext *) event_context; int ret = FALSE; SPDesktop *desktop = event_context->desktop; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int pick = prefs->getInt("/tools/dropper/pick", SP_DROPPER_PICK_VISIBLE); bool setalpha = prefs->getBool("/tools/dropper/setalpha", true); switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1 && !event_context->space_panning) { dc->centre = Geom::Point(event->button.x, event->button.y); dc->dragging = TRUE; ret = TRUE; } break; case GDK_MOTION_NOTIFY: if (event->motion.state & GDK_BUTTON2_MASK) { // pass on middle-drag ret = FALSE; break; } else if (!event_context->space_panning) { // otherwise, constantly calculate color no matter is any button pressed or not double rw = 0.0; double W(0), R(0), G(0), B(0), A(0); if (dc->dragging) { // calculate average // radius rw = std::min(Geom::L2(Geom::Point(event->button.x, event->button.y) - dc->centre), 400.0); if (rw == 0) { // happens sometimes, little idea why... break; } Geom::Point const cd = desktop->w2d(dc->centre); Geom::Matrix const w2dt = desktop->w2d(); const double scale = rw * w2dt.descrim(); Geom::Matrix const sm( Geom::Scale(scale, scale) * Geom::Translate(cd) ); sp_canvas_item_affine_absolute(dc->area, sm); sp_canvas_item_show(dc->area); /* Get buffer */ const int x0 = (int) floor(dc->centre[Geom::X] - rw); const int y0 = (int) floor(dc->centre[Geom::Y] - rw); const int x1 = (int) ceil(dc->centre[Geom::X] + rw); const int y1 = (int) ceil(dc->centre[Geom::Y] + rw); if ((x1 > x0) && (y1 > y0)) { NRPixBlock pb; nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, TRUE); /* fixme: (Lauris) */ sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); for (int y = y0; y < y1; y++) { const unsigned char *s = NR_PIXBLOCK_PX(&pb) + (y - y0) * pb.rs; for (int x = x0; x < x1; x++) { const double dx = x - dc->centre[Geom::X]; const double dy = y - dc->centre[Geom::Y]; const double w = exp(-((dx * dx) + (dy * dy)) / (rw * rw)); W += w; R += w * s[0]; G += w * s[1]; B += w * s[2]; A += w * s[3]; s += 4; } } nr_pixblock_release(&pb); R = (R + 0.001) / (255.0 * W); G = (G + 0.001) / (255.0 * W); B = (B + 0.001) / (255.0 * W); A = (A + 0.001) / (255.0 * W); R = CLAMP(R, 0.0, 1.0); G = CLAMP(G, 0.0, 1.0); B = CLAMP(B, 0.0, 1.0); A = CLAMP(A, 0.0, 1.0); } } else { // pick single pixel NRPixBlock pb; int x = (int) floor(event->button.x); int y = (int) floor(event->button.y); nr_pixblock_setup_fast(&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, x, y, x+1, y+1, TRUE); sp_canvas_arena_render_pixblock(SP_CANVAS_ARENA(sp_desktop_drawing(desktop)), &pb); const unsigned char *s = NR_PIXBLOCK_PX(&pb); R = s[0] / 255.0; G = s[1] / 255.0; B = s[2] / 255.0; A = s[3] / 255.0; } if (pick == SP_DROPPER_PICK_VISIBLE) { // compose with page color guint32 bg = sp_desktop_namedview(desktop)->pagecolor; R = R + (SP_RGBA32_R_F(bg)) * (1 - A); G = G + (SP_RGBA32_G_F(bg)) * (1 - A); B = B + (SP_RGBA32_B_F(bg)) * (1 - A); A = 1.0; } else { // un-premultiply color channels if (A > 0) { R /= A; G /= A; B /= A; } } if (fabs(A) < 1e-4) { A = 0; // suppress exponentials, CSS does not allow that } // remember color dc->R = R; dc->G = G; dc->B = B; dc->alpha = A; // status message double alpha_to_set = setalpha? dc->alpha : 1.0; guint32 c32 = SP_RGBA32_F_COMPOSE(R, G, B, alpha_to_set); gchar c[64]; sp_svg_write_color(c, sizeof(c), c32); // alpha of color under cursor, to show in the statusbar // locale-sensitive printf is OK, since this goes to the UI, not into SVG gchar *alpha = g_strdup_printf(_(" alpha %.3g"), alpha_to_set); // where the color is picked, to show in the statusbar gchar *where = dc->dragging ? g_strdup_printf(_(", averaged with radius %d"), (int) rw) : g_strdup_printf(_(" under cursor")); // message, to show in the statusbar const gchar *message = dc->dragging ? _("<b>Release mouse</b> to set color.") : _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard"); event_context->defaultMessageContext()->setF( Inkscape::NORMAL_MESSAGE, "<b>%s%s</b>%s. %s", c, (pick == SP_DROPPER_PICK_VISIBLE)? "" : alpha, where, message ); g_free(where); g_free(alpha); ret = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1 && !event_context->space_panning) { sp_canvas_item_hide(dc->area); dc->dragging = FALSE; double alpha_to_set = setalpha? dc->alpha : 1.0; // do the actual color setting sp_desktop_set_color(desktop, (event->button.state & GDK_MOD1_MASK)? ColorRGBA(1 - dc->R, 1 - dc->G, 1 - dc->B, alpha_to_set) : ColorRGBA(dc->R, dc->G, dc->B, alpha_to_set), false, !(event->button.state & GDK_SHIFT_MASK)); // REJON: set aux. toolbar input to hex color! if (!(sp_desktop_selection(desktop)->isEmpty())) { sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_DROPPER, _("Set picked color")); } ret = TRUE; } break; case GDK_KEY_PRESS: switch (get_group0_keyval(&event->key)) { case GDK_Up: case GDK_Down: case GDK_KP_Up: case GDK_KP_Down: // prevent the zoom field from activation if (!MOD__CTRL_ONLY) { ret = TRUE; } break; case GDK_Escape: sp_desktop_selection(desktop)->clear(); default: break; } break; default: break; } if (!ret) { if (((SPEventContextClass *) parent_class)->root_handler) { ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); } } return ret; }
static void vp_knot_moved_handler (SPKnot */*knot*/, Geom::Point const *ppointer, guint state, gpointer data) { VPDragger *dragger = (VPDragger *) data; VPDrag *drag = dragger->parent; Geom::Point p = *ppointer; // FIXME: take from prefs double snap_dist = SNAP_DIST / inkscape_active_desktop()->current_zoom(); /* * We use dragging_started to indicate if we have already checked for the need to split Draggers up. * This only has the purpose of avoiding costly checks in the routine below. */ if (!dragger->dragging_started && (state & GDK_SHIFT_MASK)) { /* with Shift; if there is more than one box linked to this VP we need to split it and create a new perspective */ if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes(); std::list<SPBox3D *> sel_boxes; for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) { // for each VP that has selected boxes: Persp3D *old_persp = (*vp)->get_perspective(); sel_boxes = (*vp)->selectedBoxes(sp_desktop_selection(inkscape_active_desktop())); // we create a new perspective ... Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp); /* ... unlink the boxes from the old one and FIXME: We need to unlink the _un_selected boxes of each VP so that the correct boxes are kept with the VP being moved */ std::list<SPBox3D *> bx_lst = persp3d_list_of_boxes(old_persp); for (std::list<SPBox3D *>::iterator i = bx_lst.begin(); i != bx_lst.end(); ++i) { if (std::find(sel_boxes.begin(), sel_boxes.end(), *i) == sel_boxes.end()) { /* if a box in the VP is unselected, move it to the newly created perspective so that it doesn't get dragged **/ box3d_switch_perspectives(*i, old_persp, new_persp); } } } // FIXME: Do we need to create a new dragger as well? dragger->updateZOrders (); sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX, _("Split vanishing points")); return; } } if (!(state & GDK_SHIFT_MASK)) { // without Shift; see if we need to snap to another dragger for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) { VPDragger *d_new = (VPDragger *) di->data; if ((d_new != dragger) && (Geom::L2 (d_new->point - p) < snap_dist)) { if (have_VPs_of_same_perspective (dragger, d_new)) { // this would result in degenerate boxes, which we disallow for the time being continue; } // update positions ... (this is needed so that the perspectives are detected as identical) // FIXME: This is called a bit too often, isn't it? for (std::list<VanishingPoint>::iterator j = dragger->vps.begin(); j != dragger->vps.end(); ++j) { (*j).set_pos(d_new->point); } // ... join lists of VPs ... d_new->vps.merge(dragger->vps); // ... delete old dragger ... drag->draggers = g_list_remove (drag->draggers, dragger); delete dragger; dragger = NULL; // ... and merge any duplicate perspectives d_new->mergePerspectives(); // TODO: Update the new merged dragger d_new->updateTip(); d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger! d_new->updateZOrders (); drag->updateLines (); // TODO: Undo machinery; this doesn't work yet because perspectives must be created and // deleted according to changes in the svg representation, not based on any user input // as is currently the case. sp_document_done (sp_desktop_document (inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX, _("Merge vanishing points")); return; } } } dragger->point = p; // FIXME: Brauchen wir dragger->point überhaupt? dragger->updateVPs(p); dragger->updateBoxDisplays(); dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all) dragger->updateZOrders(); drag->updateLines(); dragger->dragging_started = true; }
void text_put_on_path() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) return; Inkscape::Selection *selection = sp_desktop_selection(desktop); SPItem *text = text_or_flowtext_in_selection(selection); SPItem *shape = shape_in_selection(selection); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path.")); return; } if (SP_IS_TEXT_TEXTPATH(text)) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put on a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path.")); return; } if (SP_IS_RECT(shape)) { // rect is the only SPShape which is not <path> yet, and thus SVG forbids us from putting text on it sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first.")); return; } // if a flowed text is selected, convert it to a regular text object if (SP_IS_FLOWTEXT(text)) { if (!SP_FLOWTEXT(text)->layout.outputExists()) { sp_desktop_message_stack(desktop)-> flash(Inkscape::WARNING_MESSAGE, _("The flowed text(s) must be <b>visible</b> in order to be put on a path.")); } Inkscape::XML::Node *repr = SP_FLOWTEXT(text)->getAsText(); if (!repr) return; Inkscape::XML::Node *parent = SP_OBJECT_REPR(text)->parent(); parent->appendChild(repr); SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); sp_item_write_transform(new_item, repr, text->transform); SP_OBJECT(new_item)->updateRepr(); Inkscape::GC::release(repr); text->deleteObject(); // delete the orignal flowtext sp_document_ensure_up_to_date(sp_desktop_document(desktop)); selection->clear(); text = new_item; // point to the new text } Inkscape::Text::Layout const *layout = te_get_layout(text); Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin()); // remove transform from text, but recursively scale text's fontsize by the expansion SP_TEXT(text)->_adjustFontsizeRecursive (text, NR::expansion(SP_ITEM(text)->transform)); SP_OBJECT_REPR(text)->setAttribute("transform", NULL); // make a list of text children GSList *text_reprs = NULL; for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) { text_reprs = g_slist_prepend(text_reprs, SP_OBJECT_REPR(o)); } // create textPath and put it into the text Inkscape::XML::Node *textpath = xml_doc->createElement("svg:textPath"); // reference the shape textpath->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(shape)->attribute("id"))); if (text_alignment == Inkscape::Text::Layout::RIGHT) textpath->setAttribute("startOffset", "100%"); else if (text_alignment == Inkscape::Text::Layout::CENTER) textpath->setAttribute("startOffset", "50%"); SP_OBJECT_REPR(text)->addChild(textpath, NULL); for ( GSList *i = text_reprs ; i ; i = i->next ) { // Make a copy of each text child Inkscape::XML::Node *copy = ((Inkscape::XML::Node *) i->data)->duplicate(xml_doc); // We cannot have multiline in textpath, so remove line attrs from tspans if (!strcmp(copy->name(), "svg:tspan")) { copy->setAttribute("sodipodi:role", NULL); copy->setAttribute("x", NULL); copy->setAttribute("y", NULL); } // remove the old repr from under text SP_OBJECT_REPR(text)->removeChild((Inkscape::XML::Node *) i->data); // put its copy into under textPath textpath->addChild(copy, NULL); // fixme: copy id } // x/y are useless with textpath, and confuse Batik 1.5 SP_OBJECT_REPR(text)->setAttribute("x", NULL); SP_OBJECT_REPR(text)->setAttribute("y", NULL); sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Put text on path")); g_slist_free(text_reprs); }
void flowtext_to_text() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>flowed text(s)</b> to convert.")); return; } bool did = false; GSList *reprs = NULL; GSList *items = g_slist_copy((GSList *) selection->itemList()); for (; items != NULL; items = items->next) { SPItem *item = (SPItem *) items->data; if (!SP_IS_FLOWTEXT(item)) continue; if (!SP_FLOWTEXT(item)->layout.outputExists()) { sp_desktop_message_stack(desktop)-> flash(Inkscape::WARNING_MESSAGE, _("The flowed text(s) must be <b>visible</b> in order to be converted.")); return; } Inkscape::XML::Node *repr = SP_FLOWTEXT(item)->getAsText(); if (!repr) break; did = true; Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent(); parent->addChild(repr, SP_OBJECT_REPR(item)); SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); sp_item_write_transform(new_item, repr, item->transform); SP_OBJECT(new_item)->updateRepr(); Inkscape::GC::release(repr); item->deleteObject(); reprs = g_slist_prepend(reprs, repr); } g_slist_free(items); if (did) { sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_FLOWTEXT_TO_TEXT, _("Convert flowed text to text")); selection->setReprList(reprs); } else { sp_desktop_message_stack(desktop)-> flash(Inkscape::ERROR_MESSAGE, _("<b>No flowed text(s)</b> to convert in the selection.")); } g_slist_free(reprs); }
void text_unflow () { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) return; SPDocument *doc = sp_desktop_document (desktop); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); Inkscape::Selection *selection = sp_desktop_selection(desktop); if (!flowtext_in_selection(selection) || g_slist_length((GSList *) selection->itemList()) < 1) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it.")); return; } GSList *new_objs = NULL; GSList *old_objs = NULL; for (GSList *items = g_slist_copy((GSList *) selection->itemList()); items != NULL; items = items->next) { if (!SP_IS_FLOWTEXT(SP_OBJECT(items->data))) { continue; } SPItem *flowtext = SP_ITEM(items->data); if (sp_te_get_string_multiline(flowtext) == NULL) { // flowtext is empty continue; } /* Create <text> */ Inkscape::XML::Node *rtext = xml_doc->createElement("svg:text"); rtext->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create /* Set style */ rtext->setAttribute("style", SP_OBJECT_REPR(flowtext)->attribute("style")); // fixme: transfer style attrs too; and from descendants NRRect bbox; sp_item_invoke_bbox(SP_ITEM(flowtext), &bbox, sp_item_i2doc_affine(SP_ITEM(flowtext)), TRUE); Geom::Point xy(bbox.x0, bbox.y0); if (xy[Geom::X] != 1e18 && xy[Geom::Y] != 1e18) { sp_repr_set_svg_double(rtext, "x", xy[Geom::X]); sp_repr_set_svg_double(rtext, "y", xy[Geom::Y]); } /* Create <tspan> */ Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan"); rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan? rtext->addChild(rtspan, NULL); gchar *text_string = sp_te_get_string_multiline(flowtext); Inkscape::XML::Node *text_repr = xml_doc->createTextNode(text_string); // FIXME: transfer all formatting!!! free(text_string); rtspan->appendChild(text_repr); SP_OBJECT_REPR(SP_OBJECT_PARENT(flowtext))->appendChild(rtext); SPObject *text_object = doc->getObjectByRepr(rtext); new_objs = g_slist_prepend (new_objs, text_object); old_objs = g_slist_prepend (old_objs, flowtext); Inkscape::GC::release(rtext); Inkscape::GC::release(rtspan); Inkscape::GC::release(text_repr); } selection->clear(); selection->setList(new_objs); for (GSList *i = old_objs; i; i = i->next) { SP_OBJECT(i->data)->deleteObject (true); } g_slist_free (old_objs); g_slist_free (new_objs); sp_document_done(doc, SP_VERB_CONTEXT_TEXT, _("Unflow flowed text")); }