67#include <QPainterPath>
72#include <QActionGroup>
88#define HANDLE_DISTANCE 10
89#define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE)
91#define INNER_HANDLE_DISTANCE_SQ 16
94static const QString EditFillGradientFactoryId =
"edit_fill_gradient";
95static const QString EditStrokeGradientFactoryId =
"edit_stroke_gradient";
96static const QString EditFillMeshGradientFactoryId =
"edit_fill_meshgradient";
98enum TransformActionType {
100 TransformRotate90CCW,
154 void finishInteraction(Qt::KeyboardModifiers modifiers = QFlags<Qt::KeyboardModifier>())
override
168 Q_FOREACH (
KoShape * shape, shapes) {
235 if (shapes.size() == 1) {
236 shape = shapes.first();
248 const qreal distanceThresholdSq =
254 qreal minDistanceSq = std::numeric_limits<qreal>::max();
262 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
264 minDistanceSq = distanceSq;
356 if (shapes.size() == 1) {
357 shape = shapes.first();
371 const qreal distanceThresholdSq =
377 qreal minDistanceSq = std::numeric_limits<qreal>::max();
381 for (
const auto& handle: sh.
handles()) {
382 const QPointF handlePoint = converter->
documentToView(handle.pos);
385 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
387 minDistanceSq = distanceSq;
425 , m_hotPosition(
KoFlake::TopLeft)
426 , m_mouseWasInsideHandles(false)
429 , m_tabbedOptionWidget(0)
434 QPixmap rotatePixmap, shearPixmap;
435 rotatePixmap.load(
":/cursor_rotate.png");
436 Q_ASSERT(!rotatePixmap.isNull());
437 shearPixmap.load(
":/cursor_shear.png");
438 Q_ASSERT(!shearPixmap.isNull());
440 m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45)));
441 m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90)));
442 m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135)));
443 m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180)));
444 m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225)));
445 m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270)));
446 m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315)));
459 m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45)));
460 m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90)));
461 m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135)));
462 m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180)));
463 m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225)));
464 m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270)));
465 m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315)));
475 if (connectToSelectedShapesProxy) {
496 1, EditFillGradientFactoryId,
this));
508 0, EditStrokeGradientFactoryId,
this));
522 EditFillMeshGradientFactoryId,
this));
539 if (shapes.isEmpty())
return;
543 bool convertableShape =
false;
545 Q_FOREACH(
KoShape *shape, shapes) {
547 if (textShape && textShape->
textType() != type) {
549 if (!convertableShape) {
550 convertableShape =
true;
558 if (convertableShape) {
572 if (selectedShapes.isEmpty())
return;
574 Q_FOREACH(
KoShape *shape, selectedShapes) {
577 if (text && !textShape) {
579 }
else if (path && path->isClosedSubpath(0)) {
580 shapes.append(shape);
583 if (!textShape)
return;
584 if (shapes.isEmpty())
return;
594 Q_FOREACH(
KoShape *shape, shapes) {
613 if (selectedShapes.isEmpty())
return;
615 Q_FOREACH(
KoShape *shape, selectedShapes) {
617 if (text && !textShape) {
622 if (textShape && textPath) {
626 if (!(textShape && textPath))
return;
674 if (selectedShapes.isEmpty())
return;
676 Q_FOREACH(
KoShape *shape, selectedShapes) {
679 if (text && !textShape) {
681 }
else if (path && path->isClosedSubpath(0)) {
682 shapes.append(shape);
685 if (!textShape)
return;
686 if (shapes.isEmpty())
return;
696 Q_FOREACH(
KoShape *shape, shapes) {
714 if (!textShape)
return;
719 if (selectedShapes.isEmpty())
return;
724 Q_FOREACH(
KoShape *shape, selectedShapes) {
736 if (!textShape)
return;
741 if (selectedShapes.isEmpty())
return;
744 bool addToCanvas =
false;
745 Q_FOREACH(
KoShape *shape, selectedShapes) {
759 if (!textShape)
return;
764 if (selectedShapes.isEmpty())
return;
778 Q_FOREACH(
KoShape *shape, selectedShapes) {
779 if (!textShape->
shapesInside().contains(shape))
continue;
780 shapesInside.append(shape);
783 if (!shapesInside.isEmpty()) {
801 QAction *a =
action(actionId);
802 connect(a, SIGNAL(triggered()), mapper, SLOT(map()));
849 if (!
action(
"text_type_preformatted")->actionGroup()) {
850 QActionGroup *textTypeActions =
new QActionGroup(
this);
851 textTypeActions->addAction(
action(
"text_type_preformatted"));
852 textTypeActions->addAction(
action(
"text_type_inline_wrap"));
853 textTypeActions->addAction(
action(
"text_type_pre_positioned"));
854 textTypeActions->setExclusive(
false);
855 Q_FOREACH (QAction *a, textTypeActions->actions()) {
856 a->setCheckable(
false);
877 if (useEdgeRotation) {
883 direction = handlePosition - selectionCenter;
890 if (useEdgeRotation) {
896 direction = handlePosition - selectionCenter;
903 if (useEdgeRotation) {
909 direction = handlePosition - selectionCenter;
917 if (useEdgeRotation) {
923 direction = handlePosition - selectionCenter;
935 qreal rotation = atan2(direction.y(), direction.x()) * 180.0 /
M_PI;
939 if (useEdgeRotation) {
949 if (useEdgeRotation) {
959 if (useEdgeRotation) {
969 if (useEdgeRotation) {
982 if (rotation < 0.0) {
993 QCursor
cursor = Qt::ArrowCursor;
999 bool editable = !
selection->selectedEditableShapes().isEmpty();
1003 int rotOctant = 8 + int(8.5 +
m_angle / 45);
1005 bool rotateHandle =
false;
1006 bool shearHandle =
false;
1014 rotateHandle =
true;
1022 rotateHandle =
true;
1030 rotateHandle =
true;
1038 rotateHandle =
true;
1041 cursor = Qt::ArrowCursor;
1045 statusText = i18n(
"Left click rotates around center, right click around highlighted position.");
1048 statusText = i18n(
"Click and drag to shear selection.");
1053 statusText = i18n(
"Click and drag to resize selection.");
1055 int rotOctant = 8 + int(8.5 +
m_angle / 45);
1056 bool cornerHandle =
false;
1063 cornerHandle =
true;
1070 cornerHandle =
true;
1077 cornerHandle =
true;
1084 cornerHandle =
true;
1087 cursor = Qt::SizeAllCursor;
1088 statusText = i18n(
"Click and drag to move selection.");
1092 statusText = i18n(
"Click and drag to resize selection. Middle click to set highlighted position.");
1096 cursor = Qt::ArrowCursor;
1121 const bool isSelectionMask = node && node->inherits(
"KisSelectionMask");
1168 i18n(
"This tool only works on vector layers. You probably want the move tool."),
1190 if (bound.contains(event->
point)) {
1227 bound.adjust(-border.x(), -border.y(), border.x(), border.y());
1244 if (!(event->
modifiers() & Qt::ShiftModifier)) {
1256 bool result =
false;
1258 qreal x = 0.0, y = 0.0;
1259 if (direction == Qt::Key_Left) {
1261 }
else if (direction == Qt::Key_Right) {
1263 }
else if (direction == Qt::Key_Up) {
1265 }
else if (direction == Qt::Key_Down) {
1269 if (x != 0.0 || y != 0.0) {
1271 if ((modifiers & Qt::ShiftModifier) != 0) {
1274 }
else if ((modifiers & Qt::AltModifier) != 0) {
1281 if (!shapes.isEmpty()) {
1294 switch (event->key()) {
1333 if (!shapes.isEmpty()) {
1349 if (!shapes.empty()) {
1363 Q_ASSERT(
canvas()->selectedShapesProxy());
1376 Q_ASSERT(
canvas()->selectedShapesProxy());
1384 Q_ASSERT(
canvas()->selectedShapesProxy());
1412 if (innerHandleMeaning) {
1415 *innerHandleMeaning = path.contains(point) || path.intersects(
handlePaintRect(point));
1429 if (innerHandleMeaning) {
1431 *innerHandleMeaning =
true;
1445 QTransform matrix =
selection->absoluteTransformation();
1462 if (s->scaleX() < 0) {
1467 if (s->scaleY() < 0) {
1480 QAction *actionBringToFront =
action(
"object_order_front");
1481 connect(actionBringToFront, SIGNAL(triggered()),
this, SLOT(
selectionBringToFront()), Qt::UniqueConnection);
1483 QAction *actionRaise =
action(
"object_order_raise");
1484 connect(actionRaise, SIGNAL(triggered()),
this, SLOT(
selectionMoveUp()), Qt::UniqueConnection);
1486 QAction *actionLower =
action(
"object_order_lower");
1489 QAction *actionSendToBack =
action(
"object_order_back");
1490 connect(actionSendToBack, SIGNAL(triggered()),
this, SLOT(
selectionSendToBack()), Qt::UniqueConnection);
1492 QAction *actionGroupBottom =
action(
"object_group");
1493 connect(actionGroupBottom, SIGNAL(triggered()),
this, SLOT(
selectionGroup()), Qt::UniqueConnection);
1495 QAction *actionUngroupBottom =
action(
"object_ungroup");
1496 connect(actionUngroupBottom, SIGNAL(triggered()),
this, SLOT(
selectionUngroup()), Qt::UniqueConnection);
1498 QAction *actionSplit =
action(
"object_split");
1499 connect(actionSplit, SIGNAL(triggered()),
this, SLOT(
selectionSplitShapes()), Qt::UniqueConnection);
1508 QAction *actionTextInside =
action(
"add_shape_to_flow_area");
1509 connect(actionTextInside, SIGNAL(triggered()),
this, SLOT(
slotAddShapesToFlow()), Qt::UniqueConnection);
1511 QAction *actionTextSubtract =
action(
"subtract_shape_from_flow_area");
1514 QAction *actionTextOnPath =
action(
"put_text_on_path");
1515 connect(actionTextOnPath, SIGNAL(triggered()),
this, SLOT(
slotPutTextOnPath()), Qt::UniqueConnection);
1517 QAction *actionTextRemoveFlow =
action(
"remove_shapes_from_text_flow");
1520 QAction *actionTextFlowToggle =
action(
"flow_shape_type_toggle");
1521 connect(actionTextFlowToggle, SIGNAL(triggered()),
this, SLOT(
slotToggleFlowShapeType()), Qt::UniqueConnection);
1529 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->
canvas());
1544 QAction *actionBringToFront =
action(
"object_order_front");
1545 disconnect(actionBringToFront, 0,
this, 0);
1547 QAction *actionRaise =
action(
"object_order_raise");
1548 disconnect(actionRaise, 0,
this, 0);
1550 QAction *actionLower =
action(
"object_order_lower");
1551 disconnect(actionLower, 0,
this, 0);
1553 QAction *actionSendToBack =
action(
"object_order_back");
1554 disconnect(actionSendToBack, 0,
this, 0);
1556 QAction *actionGroupBottom =
action(
"object_group");
1557 disconnect(actionGroupBottom, 0,
this, 0);
1559 QAction *actionUngroupBottom =
action(
"object_ungroup");
1560 disconnect(actionUngroupBottom, 0,
this, 0);
1562 QAction *actionSplit =
action(
"object_split");
1563 disconnect(actionSplit, 0,
this, 0);
1572 QAction *actionTextInside =
action(
"add_shape_to_flow_area");
1573 disconnect(actionTextInside, 0,
this, 0);
1574 QAction *actionTextSubtract =
action(
"subtract_shape_from_flow_area");
1575 disconnect(actionTextSubtract, 0,
this, 0);
1576 QAction *actionTextOnPath =
action(
"put_text_on_path");
1577 disconnect(actionTextOnPath, 0,
this, 0);
1578 QAction *actionTextRemoveFlow =
action(
"remove_shapes_from_text_flow");
1579 disconnect(actionTextRemoveFlow, 0,
this, 0);
1580 QAction *actionTextFlowToggle =
action(
"flow_shape_type_toggle");
1581 disconnect(actionTextFlowToggle, 0,
this, 0);
1583 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->
canvas());
1601 if (selectedShapes.isEmpty())
return;
1603 const int groupZIndex = selectedShapes.last()->zIndex();
1632 Q_FOREACH (
KoShape *shape, selectedShapes) {
1639 newShapes << group->
shapes();
1658 if (editableShapes.isEmpty()) {
1662 QTransform applyTransform;
1663 bool shouldReset =
false;
1667 switch (TransformActionType(transformAction)) {
1668 case TransformRotate90CW:
1669 applyTransform.rotate(90.0);
1670 actionName =
kundo2_i18n(
"Rotate Object 90° CW");
1672 case TransformRotate90CCW:
1673 applyTransform.rotate(-90.0);
1674 actionName =
kundo2_i18n(
"Rotate Object 90° CCW");
1676 case TransformRotate180:
1677 applyTransform.rotate(180.0);
1680 case TransformMirrorX:
1681 applyTransform.scale(-1.0, 1.0);
1682 actionName =
kundo2_i18n(
"Mirror Object Horizontally");
1684 case TransformMirrorY:
1685 applyTransform.scale(1.0, -1.0);
1686 actionName =
kundo2_i18n(
"Mirror Object Vertically");
1688 case TransformReset:
1690 actionName =
kundo2_i18n(
"Reset Object Transformations");
1694 if (!shouldReset && applyTransform.isIdentity())
return;
1700 const QPointF centerPoint = outlineRect.center();
1701 const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y());
1702 const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y());
1708 Q_FOREACH (
KoShape *shape, transformedShapes) {
1715 t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->
transformation();
1717 const QPointF center = shape->
outlineRect().center();
1718 const QPointF offset = shape->
transformation().map(center) - center;
1719 t = QTransform::fromTranslate(offset.x(), offset.y());
1722 newTransforms.append(t);
1736 if (editableShapes.isEmpty()) {
1741 QPainterPath dstOutline;
1745 const int referenceShapeIndex = 0;
1746 KoShape *referenceShape = editableShapes[referenceShapeIndex];
1750 const QTransform booleanWorkaroundTransform =
1753 Q_FOREACH (
KoShape *shape, editableShapes) {
1755 booleanWorkaroundTransform.map(
1760 if (booleanOp == BooleanUnion) {
1761 Q_FOREACH (
const QPainterPath &path, srcOutlines) {
1765 }
else if (booleanOp == BooleanIntersection) {
1766 for (
int i = 0; i < srcOutlines.size(); i++) {
1768 dstOutline = srcOutlines[i];
1770 dstOutline &= srcOutlines[i];
1776 dstOutline.closeSubpath();
1780 }
else if (booleanOp == BooleanSubtraction) {
1781 for (
int i = 0; i < srcOutlines.size(); i++) {
1782 dstOutline = srcOutlines[referenceShapeIndex];
1783 if (i != referenceShapeIndex) {
1784 dstOutline -= srcOutlines[i];
1791 dstOutline = booleanWorkaroundTransform.inverted().map(dstOutline);
1795 if (!dstOutline.isEmpty()) {
1813 newSelectedShapes << newShape;
1829 if (editableShapes.isEmpty()) {
1838 Q_FOREACH (
KoShape *shape, editableShapes) {
1840 if (!pathShape)
return;
1843 if (pathShape->
separate(splitShapes)) {
1844 QList<KoShape*> normalShapes = implicitCastList<KoShape*>(splitShapes);
1849 newShapes << normalShapes;
1867 if (editableShapes.isEmpty()) {
1877 if (editableShapes.count() == 1) {
1899 if (editableShapes.size() < 3) {
1936 if (selectedShapes.isEmpty()) {
1958 SIGNAL(sigSwitchModeEditFillGradient(
bool)),
1962 SIGNAL(sigSwitchModeEditStrokeGradient(
bool)),
1966 SIGNAL(sigSwitchModeEditFillGradient(
bool)),
1971 SIGNAL(sigMeshGradientResetted()),
1990 bool insideSelection =
false;
1993 bool editableShape = !
selection->selectedEditableShapes().isEmpty();
1995 const bool selectMultiple =
event->modifiers() & Qt::ShiftModifier;
1996 const bool selectNextInStack =
event->modifiers() & Qt::ControlModifier;
1997 const bool avoidSelection =
event->modifiers() & Qt::AltModifier;
1999 if (selectNextInStack) {
2049 if (!avoidSelection && editableShape) {
2053 if (insideSelection) {
2072 if (!selectMultiple && !selectNextInStack) {
2074 if (insideSelection) {
2083 if (!selectMultiple) {
2090 if (selectMultiple) {
2094 if (!selectMultiple) {
2115 const bool hasEditableShapes = !editableShapes.isEmpty();
2117 action(
"object_order_front")->setEnabled(hasEditableShapes);
2118 action(
"object_order_raise")->setEnabled(hasEditableShapes);
2119 action(
"object_order_lower")->setEnabled(hasEditableShapes);
2120 action(
"object_order_back")->setEnabled(hasEditableShapes);
2122 action(
"object_transform_rotate_90_cw")->setEnabled(hasEditableShapes);
2123 action(
"object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes);
2124 action(
"object_transform_rotate_180")->setEnabled(hasEditableShapes);
2125 action(
"object_transform_mirror_horizontally")->setEnabled(hasEditableShapes);
2126 action(
"object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
2127 action(
"object_transform_reset")->setEnabled(hasEditableShapes);
2129 const bool multipleSelected = editableShapes.size() > 1;
2131 const bool alignmentEnabled =
2133 (!editableShapes.isEmpty() &&
2136 action(
"object_align_horizontal_left")->setEnabled(alignmentEnabled);
2137 action(
"object_align_horizontal_center")->setEnabled(alignmentEnabled);
2138 action(
"object_align_horizontal_right")->setEnabled(alignmentEnabled);
2139 action(
"object_align_vertical_top")->setEnabled(alignmentEnabled);
2140 action(
"object_align_vertical_center")->setEnabled(alignmentEnabled);
2141 action(
"object_align_vertical_bottom")->setEnabled(alignmentEnabled);
2143 const bool distributionEnabled = editableShapes.size() > 2;
2145 action(
"object_distribute_horizontal_left")->setEnabled(distributionEnabled);
2146 action(
"object_distribute_horizontal_center")->setEnabled(distributionEnabled);
2147 action(
"object_distribute_horizontal_right")->setEnabled(distributionEnabled);
2148 action(
"object_distribute_horizontal_gaps")->setEnabled(distributionEnabled);
2150 action(
"object_distribute_vertical_top")->setEnabled(distributionEnabled);
2151 action(
"object_distribute_vertical_center")->setEnabled(distributionEnabled);
2152 action(
"object_distribute_vertical_bottom")->setEnabled(distributionEnabled);
2153 action(
"object_distribute_vertical_gaps")->setEnabled(distributionEnabled);
2156 bool textShape =
false;
2157 bool otherShapes =
false;
2158 bool filledShapes =
false;
2159 bool shapesInside =
false;
2161 const bool editFlowShapes = bool(currentTextShapeGroup);
2162 Q_FOREACH(
KoShape *shape, editableShapes) {
2164 if (text && !textShape) {
2169 filledShapes = filledShapes? filledShapes: (path && path->isClosedSubpath(0));
2170 if (editFlowShapes) {
2171 if (!shapesInside && currentTextShapeGroup->
shapesInside().contains(shape)) {
2172 shapesInside =
true;
2176 if (textShape && otherShapes)
break;
2178 const bool editContours = textShape && otherShapes;
2179 const bool editFilledContours = textShape && filledShapes;
2181 action(
"add_shape_to_flow_area")->setEnabled(editFilledContours);
2182 action(
"subtract_shape_from_flow_area")->setEnabled(editFilledContours);
2183 action(
"put_text_on_path")->setEnabled(editContours);
2184 action(
"remove_shapes_from_text_flow")->setEnabled(editFlowShapes);
2185 action(
"flow_shape_type_toggle")->setEnabled(editFlowShapes);
2186 action(
"flow_shape_order_back")->setEnabled(shapesInside);
2187 action(
"flow_shape_order_earlier")->setEnabled(shapesInside);
2188 action(
"flow_shape_order_later")->setEnabled(shapesInside);
2189 action(
"flow_shape_order_front")->setEnabled(shapesInside);
2197 const bool multipleSelected = editableShapes.size() > 1;
2199 action(
"object_group")->setEnabled(multipleSelected);
2201 action(
"object_unite")->setEnabled(multipleSelected);
2202 action(
"object_intersect")->setEnabled(multipleSelected);
2203 action(
"object_subtract")->setEnabled(multipleSelected);
2205 bool hasShapesWithMultipleSegments =
false;
2206 Q_FOREACH (
KoShape *shape, editableShapes) {
2209 hasShapesWithMultipleSegments =
true;
2213 action(
"object_split")->setEnabled(hasShapesWithMultipleSegments);
2216 bool hasGroupShape =
false;
2217 foreach (
KoShape *shape, editableShapes) {
2219 hasGroupShape =
true;
2223 action(
"object_ungroup")->setEnabled(hasGroupShape);
2225 bool enablePreformatted =
false;
2226 bool enablePrePositioned =
false;
2227 bool enableInlineWrapped =
false;
2229 Q_FOREACH (
KoShape *shape, editableShapes) {
2234 enablePreformatted =
true;
2237 enablePrePositioned =
true;
2240 enableInlineWrapped =
true;
2244 QActionGroup *group =
action(
"text_type_preformatted")->actionGroup();
2246 group->setEnabled(text);
2249 action(
"text_type_preformatted")->setEnabled(enablePreformatted);
2250 action(
"text_type_pre_positioned")->setEnabled(enablePrePositioned);
2251 action(
"text_type_inline_wrap")->setEnabled(enableInlineWrapped);
2268 QMenu *transform =
m_contextMenu->addMenu(i18n(
"Transform"));
2270 transform->addAction(
action(
"object_transform_rotate_90_cw"));
2271 transform->addAction(
action(
"object_transform_rotate_90_ccw"));
2272 transform->addAction(
action(
"object_transform_rotate_180"));
2273 transform->addSeparator();
2274 transform->addAction(
action(
"object_transform_mirror_horizontally"));
2275 transform->addAction(
action(
"object_transform_mirror_vertically"));
2276 transform->addSeparator();
2277 transform->addAction(
action(
"object_transform_reset"));
2279 if (
action(
"object_unite")->isEnabled() ||
2280 action(
"object_intersect")->isEnabled() ||
2281 action(
"object_subtract")->isEnabled() ||
2282 action(
"object_split")->isEnabled()) {
2284 QMenu *transform =
m_contextMenu->addMenu(i18n(
"Logical Operations"));
2285 transform->addAction(
action(
"object_unite"));
2286 transform->addAction(
action(
"object_intersect"));
2287 transform->addAction(
action(
"object_subtract"));
2288 transform->addAction(
action(
"object_split"));
2305 if (
action(
"object_group")->isEnabled() ||
action(
"object_ungroup")->isEnabled()) {
2315 text->addAction(
action(
"add_shape_to_flow_area"));
2316 text->addAction(
action(
"subtract_shape_from_flow_area"));
2317 text->addAction(
action(
"put_text_on_path"));
2318 text->addSeparator();
2319 text->addAction(
action(
"text_type_preformatted"));
2320 text->addAction(
action(
"text_type_inline_wrap"));
2321 text->addAction(
action(
"text_type_pre_positioned"));
2322 text->addSeparator();
2323 text->addAction(
action(
"remove_shapes_from_text_flow"));
2324 text->addAction(
action(
"flow_shape_type_toggle"));
2325 text->addSeparator();
2326 text->addAction(
action(
"flow_shape_order_back"));
2327 text->addAction(
action(
"flow_shape_order_earlier"));
2328 text->addAction(
action(
"flow_shape_order_later"));
2329 text->addAction(
action(
"flow_shape_order_front"));
2336 menu->addAction(
action(
"object_transform_rotate_90_cw"));
2337 menu->addAction(
action(
"object_transform_rotate_90_ccw"));
2338 menu->addAction(
action(
"object_transform_rotate_180"));
2339 menu->addSeparator();
2340 menu->addAction(
action(
"object_transform_mirror_horizontally"));
2341 menu->addAction(
action(
"object_transform_mirror_vertically"));
2342 menu->addSeparator();
2343 menu->addAction(
action(
"object_transform_reset"));
2350 QTimer::singleShot(0, [tool = std::move(tool)]() {
2381 if (!
d->parent->selection()->hasSelection())
return props;
2384 for (
auto it =
shapes.begin(); it !=
shapes.end(); it++) {
2386 if (!textShape)
continue;
2406 if (
d->shapes.isEmpty())
return;
2409 d->parent->canvas()->addCommand(cmd);
2415 Q_UNUSED(properties)
2416 Q_UNUSED(removeProperties)
2434 d->compressor.start();
2439 d->compressor.start();
2446 d->compressor.start();
2451 Q_FOREACH(
KoShape *shape,
d->shapes) {
2459 if (
d->parent->updateTextContourMode())
return;
2460 Q_FOREACH(
KoShape *shape,
d->shapes) {
2461 if (!shape)
continue;
2465 auto *textShapeGroup =
d->parent->tryFetchCurrentShapeManagerOwnerTextShape();
2467 if (textShapeGroup) {
2468 d->shapes = {textShapeGroup};
2470 d->shapes =
d->parent->canvas()->selectedShapesProxy()->selection()->selectedEditableShapes();
2473 Q_FOREACH(
KoShape *shape,
d->shapes) {
2474 if (!shape)
continue;
2477 d->compressor.start();
float value(const T *src, size_t ch)
void setText(const KUndo2MagicString &text)
KUndo2MagicString text() const
KisImageWSP image() const
KoShapeManager * localShapeManager() const
KisViewManager * viewManager() const
KisNodeSP activeNode()
Convenience function to get the active layer or mask.
The KisSignalMapper class bundles signals from identifiable senders.
void setMapping(QObject *sender, int id)
void setTextPropertiesInterface(KoSvgTextPropertiesInterface *interface)
setTextPropertiesInterface set the text properties interface. This should be done on tool activation....
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
KisTextPropertiesManager * textPropertyManager() const
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
virtual KoShape * currentShapeManagerOwnerShape() const
the shape that owns the currently active shape manager
QPointer< KoShapeController > shapeController
virtual KoShapeManager * shapeManager() const =0
virtual const KoViewConverter * viewConverter() const =0
virtual void updateCanvas(const QRectF &rc)=0
virtual void addCommand(KUndo2Command *command)=0
QPointer< KoCanvasResourceProvider > resourceManager
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
bool setSvg(const QList< KoShape * > shapes)
KoToolBase * tool() const
The position of a path point within a path shape.
bool separate(QList< KoPathShape * > &separatedPaths)
Creates separate path shapes, one for each existing subpath.
int subpathCount() const
Returns the number of subpaths in the path.
static KoPathShape * createShapeFromPainterPath(const QPainterPath &path)
Creates path shape from given QPainterPath.
Qt::MouseButtons buttons() const
return buttons pressed (see QMouseEvent::buttons());
bool isTabletEvent() const
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
virtual KoSelection * selection()=0
void deselectAll()
clear the selections list
const QList< KoShape * > selectedEditableShapesAndDelegates() const
void select(KoShape *shape)
const QList< KoShape * > selectedEditableShapes() const
KoShape * firstSelectedShape() const
const QList< KoShape * > selectedShapes() const
The undo / redo command for aligning shapes.
Align
The different alignment options for this command.
@ VerticalTopAlignment
Align top.
@ VerticalCenterAlignment
Align centered vertically.
@ HorizontalLeftAlignment
Align left.
@ HorizontalCenterAlignment
Align Centered horizontally.
@ VerticalBottomAlignment
Align bottom.
@ HorizontalRightAlignment
Align Right.
QList< KoShape * > shapes() const
The undo / redo command for distributing shapes.
Distribute
The different options to distribute with this command.
@ HorizontalRightDistribution
Horizontal Right.
@ VerticalTopDistribution
Vertical top.
@ VerticalCenterDistribution
Vertical centered.
@ HorizontalGapsDistribution
Horizontal Gaps.
@ VerticalBottomDistribution
Vertical bottom.
@ HorizontalCenterDistribution
Horizontal centered.
@ HorizontalLeftDistribution
Horizontal Left.
@ VerticalGapsDistribution
Vertical Gaps.
QVector< Handle > handles() const
The undo / redo command for grouping shapes.
QList< KoShape * > topLevelShapes() const
KoShape * shapeAt(const QPointF &position, KoFlake::ShapeSelection selection=KoFlake::ShapeOnTop, bool omitHiddenShapes=true)
The KoShapeMergeTextPropertiesCommand class This sets text properties on given text shapes....
QVector< Handle > handles() const
get all nodes in the mesh, don't use this for drawing the path but use path()
The undo / redo command for shape moving.
MoveShapeType
An enum for defining what kind of reordering to use.
@ RaiseShape
raise the selected shape to the level that it is above the shape that is on top of it.
@ SendToBack
Lower the selected shape to be below all other shapes.
@ LowerShape
Lower the selected shape to the level that it is below the shape that is below it.
@ BringToFront
Raise the selected shape to be on top of all shapes.
static KoShapeReorderCommand * createCommand(const QList< KoShape * > &shapes, KoShapeManager *manager, MoveShapeType move, KUndo2Command *parent=0)
virtual SelectionMode currentMode() const
QRectF selectedRectangle() const
void paint(QPainter &painter, const KoViewConverter &converter) override
The undo / redo command for ungrouping shapes.
virtual QRectF outlineRect() const
void addShapeChangeListener(ShapeChangeListener *listener)
void setZIndex(qint16 zIndex)
virtual QPainterPath outline() const
bool isSelectable() const
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
void removeShapeChangeListener(ShapeChangeListener *listener)
virtual KoShapeStrokeModelSP stroke() const
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
bool isGeometryProtected() const
QRectF absoluteOutlineRect() const
KoShapeContainer * parent() const
virtual void setStroke(KoShapeStrokeModelSP stroke)
QTransform absoluteTransformation() const
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
ChangeType
Used by shapeChanged() to select which change was made.
virtual QSharedPointer< KoShapeBackground > background() const
QTransform transformation() const
Returns the shapes local transformation matrix.
void paint(QPainter &painter, const KoViewConverter &converter)
paints the guide
QRectF boundingRect()
returns the bounding rect of the guide
bool isSnapping() const
returns if snapping is enabled
The SvgConvertTextTypeCommand class This command allows textshapes to be converted between preformatt...
The KoSvgTextPropertiesInterface class.
void textSelectionChanged()
Emit to signal to KisTextPropertiesManager to call getSelectedProperties.
@ TextAnchorId
KoSvgText::TextAnchor.
QVariant propertyOrDefault(PropertyId id) const
static void removeContourShapesFromFlow(KoSvgTextShape *textShape, KUndo2Command *parent, bool textInShape, bool textPaths)
removeContourShapesFromFlow Create a command to remove all contour shapes of a certain type from the ...
The KoSvgTextReorderShapeInsideCommand class Within a text shape, the order of the shapes inside dete...
The KoSvgTextShapeOutlineHelper class helper class that draws the text outlines and contour mode butt...
KoSvgText::Direction direction() const
direction Whether the text is left to right or right to left.
@ PreformattedText
Text-on-Path falls under this or PrePositionedText depending on collapse of lines.
@ TextInShape
Uses shape-inside to wrap and preserves spaces.
@ InlineWrap
Uses inline size to wrap and preserves spaces.
QList< KoShape * > shapesInside
bool shapeInContours(KoShape *shape)
shapeInContours
TextType textType() const
textType This enum gives an indication of what kind of text this shape is. The different text types a...
int posForIndex(int index, bool firstIndex=false, bool skipSynthetic=false) const
posForIndex Get the cursor position for a given index in a string.
QList< KoShape * > shapesSubtract
KoSvgTextProperties textProperties() const
virtual QPointF viewToDocument(const QPointF &viewPoint) const
virtual QPointF documentToView(const QPointF &documentPoint) const
KUndo2Command * createCommand() override
void paint(QPainter &painter, const KoViewConverter &converter) override
void finishInteraction(Qt::KeyboardModifiers) override
NopInteractionStrategy(KoToolBase *parent)
void handleMouseMove(const QPointF &, Qt::KeyboardModifiers) override
QPointer< KoSelection > m_selection
SelectionHandler(DefaultTool *parent)
bool hasSelection() override
return true if the tool currently has something selected that can be copied or deleted.
void cancelInteraction() override
void finishInteraction(Qt::KeyboardModifiers modifiers=QFlags< Qt::KeyboardModifier >()) override
void paint(QPainter &painter, const KoViewConverter &converter) override
SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_RETURN(cond)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
bool isSelectionMask(KisNodeSP node)
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2MagicString kundo2_noi18n(const QString &text)
@ PageSize
The size of the (current) page in postscript points.
@ ShapeOnTop
return the shape highest z-ordering, regardless of selection.
@ NextUnselected
return the first unselected directly under a selected shape, or the top most one if nothing is select...
SelectionHandle
Enum determining which handle is meant, used in KoInteractionTool.
@ BottomRightHandle
The handle that is at the bottom right of a selection.
@ BottomLeftHandle
The handle that is at the bottom left of a selection.
@ RightMiddleHandle
The handle that is at the right - center of a selection.
@ TopRightHandle
The handle that is at the top - right of a selection.
@ TopLeftHandle
The handle that is at the top left of a selection.
@ LeftMiddleHandle
The handle that is at the left center of a selection.
@ NoHandle
Value to indicate no handle.
@ TopMiddleHandle
The handle that is at the top - center of a selection.
@ BottomMiddleHandle
The handle that is at the bottom center of a selection.
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
@ AnchorEnd
Anchor right for LTR, left for RTL.
@ AnchorStart
Anchor left for LTR, right for RTL.
@ AnchorMiddle
Anchor to the middle.
QTransform pathShapeBooleanSpaceWorkaround(KisImageSP image)
Interface to interact with the text property manager.
virtual bool spanSelection() override
Whether the tool is currently selecting a set of characters instead of whole paragraphs.
void slotSelectionChanged()
virtual bool characterPropertiesEnabled() override
Whether character selections are possible at all.
~DefaultToolTextPropertiesInterface()
virtual QList< KoSvgTextProperties > getSelectedProperties() override
getSelectedProperties
const QScopedPointer< Private > d
virtual void notifyShapeChanged(KoShape::ChangeType type, KoShape *shape) override
DefaultToolTextPropertiesInterface(DefaultTool *parent)
virtual KoSvgTextProperties getInheritedProperties() override
getInheritedProperties The properties that should be visible when a given property isn't available in...
QList< KoShape * > shapes
virtual void notifyCursorPosChanged(int pos, int anchor) override
Private(DefaultTool *parent)
KisSignalCompressor compressor
virtual void notifyMarkupChanged() override
virtual void setCharacterPropertiesOnSelected(KoSvgTextProperties properties, QSet< KoSvgTextProperties::PropertyId > removeProperties=QSet< KoSvgTextProperties::PropertyId >()) override
setCharacterPropertiesOnSelected This sets the properties for a character selection instead of the fu...
virtual QList< KoSvgTextProperties > getCharacterProperties() override
getSelectedProperties
virtual void setPropertiesOnSelected(KoSvgTextProperties properties, QSet< KoSvgTextProperties::PropertyId > removeProperties=QSet< KoSvgTextProperties::PropertyId >()) override
setPropertiesOnSelected This sets the properties on the selection. The implementation is responsible ...
bool startOffsetIsPercentage