Commit 76e0300e authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix lockness of the shapes when the layer is locked/invisible

When the layer is locked, the shapes should be selectable, but
not editable. When the layer is invisible, the shapes should also
be invisible (thanks, cap!)

BUG:388336
parent 1980f39b
......@@ -111,7 +111,7 @@ void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
// only allow dropping when active layer is editable
KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
KoShapeLayer *activeLayer = selection->activeLayer();
if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected())) {
if (activeLayer && (!activeLayer->isShapeEditable() || activeLayer->isGeometryProtected())) {
event->ignore();
return;
}
......
......@@ -68,7 +68,7 @@ QRectF KoSelection::outlineRect() const
Q_D(const KoSelection);
QPolygonF globalPolygon;
Q_FOREACH (KoShape *shape, d->selectedShapes) {
Q_FOREACH (KoShape *shape, selectedVisibleShapes()) {
globalPolygon = globalPolygon.united(
shape->absoluteTransformation(0).map(QPolygonF(shape->outlineRect())));
}
......@@ -80,7 +80,7 @@ QRectF KoSelection::outlineRect() const
QRectF KoSelection::boundingRect() const
{
Q_D(const KoSelection);
return KoShape::boundingRect(d->selectedShapes);
return KoShape::boundingRect(selectedVisibleShapes());
}
void KoSelection::select(KoShape *shape)
......@@ -89,7 +89,7 @@ void KoSelection::select(KoShape *shape)
KIS_SAFE_ASSERT_RECOVER_RETURN(shape != this);
KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
if (!shape->isSelectable() || !shape->isVisible(true)) {
if (!shape->isSelectable() || !shape->isVisible()) {
return;
}
......@@ -160,6 +160,7 @@ bool KoSelection::hitTest(const QPointF &position) const
Q_D(const KoSelection);
Q_FOREACH (KoShape *shape, d->selectedShapes) {
if (shape->isVisible()) continue;
if (shape->hitTest(position)) return true;
}
......@@ -172,6 +173,19 @@ const QList<KoShape*> KoSelection::selectedShapes() const
return d->selectedShapes;
}
const QList<KoShape *> KoSelection::selectedVisibleShapes() const
{
Q_D(const KoSelection);
QList<KoShape*> shapes = selectedShapes();
KritaUtils::filterContainer (shapes, [](KoShape *shape) {
return shape->isVisible();
});
return shapes;
}
const QList<KoShape *> KoSelection::selectedEditableShapes() const
{
Q_D(const KoSelection);
......@@ -179,7 +193,7 @@ const QList<KoShape *> KoSelection::selectedEditableShapes() const
QList<KoShape*> shapes = selectedShapes();
KritaUtils::filterContainer (shapes, [](KoShape *shape) {
return shape->isEditable();
return shape->isShapeEditable();
});
return shapes;
......@@ -208,7 +222,7 @@ bool KoSelection::isSelected(const KoShape *shape) const
return true;
const KoShape *tmpShape = shape;
while (tmpShape && std::find(d->selectedShapes.begin(), d->selectedShapes.end(), tmpShape) == d->selectedShapes.end()/*d->selectedShapes.contains(tmpShape)*/) {
while (tmpShape && std::find(d->selectedShapes.begin(), d->selectedShapes.end(), tmpShape) == d->selectedShapes.end()) {
tmpShape = tmpShape->parent();
}
......
......@@ -101,11 +101,15 @@ public:
/**
* Return the list of selected shapes
* @return the list of selected shapes
* @param strip if StrippedSelection, the returned list will not include any children
* of a container shape if the container-parent is itself also in the set.
*/
const QList<KoShape*> selectedShapes() const;
/**
* Same as selectedShapes() but only for shapes in visible state. Used by
* the algorithms that draw shapes on the image
*/
const QList<KoShape*> selectedVisibleShapes() const;
/**
* Same as selectedShapes() but only for editable shapes. Used by
* the algorithms that modify the image
......
......@@ -809,7 +809,7 @@ void KoShape::copySettings(const KoShape *shape)
Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
addConnectionPoint(point);
d->zIndex = shape->zIndex();
d->visible = shape->isVisible();
d->visible = shape->isVisible(false);
// Ensure printable is true by default
if (!d->visible)
......@@ -1187,11 +1187,11 @@ bool KoShape::isVisible(bool recursive) const
return false;
KoShapeContainer * parentShape = parent();
while (parentShape) {
if (! parentShape->isVisible())
return false;
parentShape = parentShape->parent();
if (parentShape) {
return parentShape->isVisible(true);
}
return true;
}
......@@ -1419,14 +1419,15 @@ void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous
Q_UNUSED(asynchronous);
}
bool KoShape::isEditable() const
bool KoShape::isShapeEditable(bool recursive) const
{
Q_D(const KoShape);
if (!d->visible || d->geometryProtected)
return false;
if (d->parent && d->parent->isChildLocked(this))
return false;
if (recursive && d->parent) {
return d->parent->isShapeEditable(true);
}
return true;
}
......
......@@ -636,7 +636,7 @@ public:
* @return current visibility state of this shape.
* @see isGeometryProtected(), isContentProtected(), isSelectable()
*/
bool isVisible(bool recursive = false) const;
bool isVisible(bool recursive = true) const;
/**
* Changes the shape to be printable or not. The default is true.
......@@ -1054,7 +1054,7 @@ public:
virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const;
/// checks recursively if the shape or one of its parents is not visible or locked
bool isEditable() const;
virtual bool isShapeEditable(bool recursive = true) const;
/**
* Adds a shape which depends on this shape.
......
......@@ -102,14 +102,6 @@ int KoShapeContainer::shapeCount() const
return d->model->count();
}
bool KoShapeContainer::isChildLocked(const KoShape *child) const
{
Q_D(const KoShapeContainer);
if (d->model == 0)
return false;
return d->model->isChildLocked(child);
}
void KoShapeContainer::setClipped(const KoShape *child, bool clipping)
{
Q_D(KoShapeContainer);
......
......@@ -138,14 +138,6 @@ public:
*/
bool isClipped(const KoShape *child) const;
/**
* Return whether the child has the effective state of being locked for user modifications.
* This method is deferred to the model, which should call the KoShape::isGeometryProtected() on the child.
* @param child the shape that the user wants to move.
*/
bool isChildLocked(const KoShape *child) const;
/**
* Set the shape to inherit the container transform.
*
......
......@@ -116,14 +116,6 @@ public:
*/
virtual bool inheritsTransform(const KoShape *shape) const = 0;
/**
* Return wheather the child has the effective state of being locked for user modifications.
* The model has to call KoShape::isGeometryProtected() and base its return value upon that, it can
* additionally find rules on wheather the child is locked based on the container state.
* @param child the shape that the user wants to move.
*/
virtual bool isChildLocked(const KoShape *child) const = 0;
/**
* Return the current number of children registered.
* @return the current number of children registered.
......
......@@ -105,7 +105,7 @@ void KoShapeManager::Private::paintGroup(KoShapeGroup *group, QPainter &painter,
std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
Q_FOREACH (KoShape *child, shapes) {
// we paint recursively here, so we do not have to check recursively for visibility
if (!child->isVisible())
if (!child->isVisible(false))
continue;
KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(child);
if (childGroup) {
......@@ -267,7 +267,7 @@ void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter,
// also filter shapes with a parent which has filter effects applied
QList<KoShape*> sortedShapes;
foreach (KoShape *shape, unsortedShapes) {
if (!shape->isVisible(true))
if (!shape->isVisible())
continue;
bool addShapeToList = true;
// check if one of the shapes ancestors have filter effects
......@@ -488,7 +488,7 @@ KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelectio
KoShape *firstUnselectedShape = 0;
for (int count = sortedShapes.count() - 1; count >= 0; count--) {
KoShape *shape = sortedShapes.at(count);
if (omitHiddenShapes && ! shape->isVisible(true))
if (omitHiddenShapes && ! shape->isVisible())
continue;
if (! shape->hitTest(position))
continue;
......@@ -538,7 +538,7 @@ QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenSha
KoShape *shape = shapes.at(count);
if (omitHiddenShapes && !shape->isVisible(true)) {
if (omitHiddenShapes && !shape->isVisible()) {
shapes.removeAt(count);
} else {
const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline());
......
......@@ -217,7 +217,7 @@ void KoShapeSavingContext::saveLayerSet(KoXmlWriter &xmlWriter) const
xmlWriter.addAttribute("draw:name", layer->name());
if (layer->isGeometryProtected())
xmlWriter.addAttribute("draw:protected", "true");
if (! layer->isVisible())
if (! layer->isVisible(false))
xmlWriter.addAttribute("draw:display", "none");
xmlWriter.endElement(); // draw:layer
}
......
......@@ -70,7 +70,7 @@ void KoShapeShadow::Private::paintGroupShadow(KoShapeGroup *group, QPainter &pai
QList<KoShape*> shapes = group->shapes();
Q_FOREACH (KoShape *child, shapes) {
// we paint recursively here, so we do not have to check recursively for visibility
if (!child->isVisible())
if (!child->isVisible(false))
continue;
painter.save();
//apply group child's transformation
......
......@@ -76,7 +76,7 @@ QList<QPointF> KoSnapProxy::pointsFromShape(KoShape * shape)
{
QList<QPointF> snapPoints;
// no snapping to hidden shapes
if (! shape->isVisible(true))
if (! shape->isVisible())
return snapPoints;
// return the special snap points of the shape
......@@ -157,7 +157,7 @@ QList<KoShape*> KoSnapProxy::shapes(bool omitEditedShape)
// filter all hidden and ignored shapes
Q_FOREACH (KoShape * shape, allShapes) {
if (shape->isVisible(true) &&
if (shape->isVisible() &&
!ignoredShapes.contains(shape)) {
filteredShapes.append(shape);
......
......@@ -924,7 +924,7 @@ void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes)
void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer)
{
emit q->currentLayerChanged(canvasData->canvas, layer);
layerExplicitlyDisabled = layer && !layer->isEditable();
layerExplicitlyDisabled = layer && !layer->isShapeEditable();
updateToolForProxy();
debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled;
......
......@@ -118,7 +118,7 @@ bool KoToolProxyPrivate::isActiveLayerEditable()
KoShapeManager * shapeManager = activeTool->canvas()->shapeManager();
KoShapeLayer * activeLayer = shapeManager->selection()->activeLayer();
if (activeLayer && !activeLayer->isEditable())
if (activeLayer && !activeLayer->isShapeEditable())
return false;
return true;
}
......
......@@ -77,14 +77,6 @@ bool KoTosContainerModel::inheritsTransform(const KoShape *shape) const
return true;
}
bool KoTosContainerModel::isChildLocked(const KoShape *child) const
{
Q_ASSERT(child == m_textShape);
Q_ASSERT(child->parent());
// TODO do we need to guard this?
return child->isGeometryProtected() || child->parent()->isGeometryProtected();
}
int KoTosContainerModel::count() const
{
return m_textShape != 0 ? 1 : 0;
......
......@@ -33,7 +33,6 @@ public:
bool isClipped(const KoShape *shape) const override;
void setInheritsTransform(const KoShape *shape, bool inherit) override;
bool inheritsTransform(const KoShape *shape) const override;
bool isChildLocked(const KoShape *child) const override;
int count() const override;
QList<KoShape*> shapes() const override;
void containerChanged(KoShapeContainer *container, KoShape::ChangeType type) override;
......
......@@ -85,15 +85,7 @@ public:
return QList<KoShape*>(m_members);
}
void containerChanged(KoShapeContainer *, KoShape::ChangeType) override { }
bool isChildLocked(const KoShape *child) const override {
Q_ASSERT(child->parent());
if (child->parent()) {
return child->isGeometryProtected() || child->parent()->isGeometryProtected();
}
else {
return child->isGeometryProtected();
}
}
void setInheritsTransform(const KoShape *shape, bool value) override {
const int index = indexOf(shape);
KIS_SAFE_ASSERT_RECOVER_RETURN(index >= 0);
......@@ -110,7 +102,7 @@ public:
KoShapeContainer *parent = shape->parent();
bool allowedToMove = true;
while (allowedToMove && parent) {
allowedToMove = parent->isEditable();
allowedToMove = parent->isShapeEditable();
parent = parent->parent();
}
if (! allowedToMove) {
......
......@@ -48,7 +48,7 @@ void paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &c
std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
Q_FOREACH (KoShape *child, shapes) {
// we paint recursively here, so we do not have to check recursively for visibility
if (!child->isVisible())
if (!child->isVisible(false))
continue;
KoShapeGroup *childGroup = dynamic_cast<KoShapeGroup*>(child);
if (childGroup) {
......
......@@ -201,7 +201,7 @@ QRectF KoShapePainter::contentRect() const
{
QRectF bound;
foreach (KoShape *shape, d->canvas->shapeManager()->shapes()) {
if (!shape->isVisible(true))
if (!shape->isVisible())
continue;
if (dynamic_cast<KoShapeGroup*>(shape))
continue;
......
......@@ -77,7 +77,7 @@ void SvgStyleWriter::saveSvgStyle(KoShape *shape, SvgSavingContext &context)
void SvgStyleWriter::saveSvgBasicStyle(KoShape *shape, SvgSavingContext &context)
{
if (! shape->isVisible()) {
if (! shape->isVisible(false)) {
context.shapeWriter().addAttribute("display", "none");
} if (shape->transparency() > 0.0) {
context.shapeWriter().addAttribute("opacity", 1.0 - shape->transparency());
......
......@@ -202,10 +202,6 @@ public:
return false;
}// ignored
/// reimplemented
bool isChildLocked(const KoShape *child) const override {
return child->isGeometryProtected();
}
/// reimplemented
int count() const override {
return m_children.count();
}
......
......@@ -940,7 +940,7 @@ void KoPathTool::initializeWithShapes(const QList<KoShape*> shapes)
Q_FOREACH (KoShape *shape, shapes) {
KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
if (pathShape && pathShape->isEditable()) {
if (pathShape && pathShape->isShapeEditable()) {
selectedShapes.append(pathShape);
}
}
......
......@@ -380,7 +380,7 @@ public:
* Set the locked status of this node. Locked nodes cannot be
* edited.
*/
void setUserLocked(bool l);
virtual void setUserLocked(bool l);
/**
* @return true if the node can be edited:
......
......@@ -430,9 +430,24 @@ bool KisShapeLayer::visible(bool recursive) const
void KisShapeLayer::setVisible(bool visible, bool isLoading)
{
KoShapeLayer::setVisible(visible);
KisExternalLayer::setVisible(visible, isLoading);
}
void KisShapeLayer::setUserLocked(bool value)
{
KoShapeLayer::setGeometryProtected(value);
KisExternalLayer::setUserLocked(value);
}
bool KisShapeLayer::isShapeEditable(bool recursive) const
{
return KoShapeLayer::isShapeEditable(recursive) && isEditable(true);
}
// we do not override KoShape::setGeometryProtected() as we consider
// the user not being able to access the layer shape from Krita UI!
void KisShapeLayer::forceUpdateTimedNode()
{
m_d->canvas->forceRepaint();
......
......@@ -126,6 +126,10 @@ public:
bool visible(bool recursive = false) const override;
void setVisible(bool visible, bool isLoading = false) override;
void setUserLocked(bool value) override;
bool isShapeEditable(bool recursive) const override;
/**
* Forces a repaint of a shape layer without waiting for an event loop
* calling a delayed timer update. If you want to see the result of the shape
......
......@@ -182,11 +182,6 @@ void KisShapeSelectionModel::childChanged(KoShape * child, KoShape::ChangeType t
requestUpdate(changedRect.toAlignedRect());
}
bool KisShapeSelectionModel::isChildLocked(const KoShape *child) const
{
return child->isGeometryProtected() || child->parent()->isGeometryProtected();
}
void KisShapeSelectionModel::setShapeSelection(KisShapeSelection* selection)
{
m_shapeSelection = selection;
......
......@@ -53,7 +53,6 @@ public:
void containerChanged(KoShapeContainer *, KoShape::ChangeType) override;
void childChanged(KoShape * child, KoShape::ChangeType type) override;
bool isChildLocked(const KoShape *child) const override;
void setShapeSelection(KisShapeSelection* selection);
private Q_SLOTS:
......
......@@ -207,7 +207,7 @@ void KoToolBox::setButtonsVisible(const QList<QString> &codes)
void KoToolBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer)
{
Q_UNUSED(canvas);
const bool enabled = layer == 0 || (layer->isEditable() && layer->isVisible());
const bool enabled = layer == 0 || (layer->isShapeEditable() && layer->isVisible());
foreach (QToolButton *button, d->visibilityCodes.keys()) {
if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) {
continue;
......
......@@ -186,12 +186,6 @@ bool ShrinkToFitShapeContainerModel::inheritsTransform(const KoShape *child) con
return true;
}
bool ShrinkToFitShapeContainerModel::isChildLocked(const KoShape *child) const
{
Q_ASSERT(child == d->childShape); Q_UNUSED(child);
return true;
}
bool ShrinkToFitShapeContainerModel::isClipped(const KoShape *child) const
{
Q_ASSERT(child == d->childShape); Q_UNUSED(child);
......
......@@ -100,8 +100,6 @@ public:
// reimplemented
bool inheritsTransform(const KoShape *child) const override;
// reimplemented
bool isChildLocked(const KoShape *child) const override;
// reimplemented
bool isClipped(const KoShape *child) const override;
private Q_SLOTS:
......
......@@ -227,11 +227,6 @@ void KoTextShapeContainerModel::proposeMove(KoShape *child, QPointF &move)
move.setY(0);
}
bool KoTextShapeContainerModel::isChildLocked(const KoShape *child) const
{
return child->isGeometryProtected();
}
void KoTextShapeContainerModel::relayoutInlineObject(KoShape *child)
{
if (child == 0) {
......
......@@ -57,8 +57,6 @@ public:
/// reimplemented from KoShapeContainerModel
void childChanged(KoShape *child, KoShape::ChangeType type) override;
/// reimplemented from KoShapeContainerModel
bool isChildLocked(const KoShape *child) const override;
/// reimplemented from KoShapeContainerModel
void setInheritsTransform(const KoShape *shape, bool inherit) override;
/// reimplemented from KoShapeContainerModel
bool inheritsTransform(const KoShape *shape) const override;
......
......@@ -237,7 +237,7 @@ void ConnectionTool::repaintDecorations()
repaintRect = m_currentShape->boundingRect();
canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius));
KoConnectionShape *connectionShape = dynamic_cast<KoConnectionShape *>(m_currentShape);
if (!m_resetPaint && m_currentShape->isVisible(true) && !connectionShape) {
if (!m_resetPaint && m_currentShape->isVisible() && !connectionShape) {
// only paint connection points of textShapes not inside a tos container and other shapes
if (!(m_currentShape->shapeId() == TextShape_SHAPEID &&
dynamic_cast<KoTosContainer *>(m_currentShape->parent()))) {
......
......@@ -110,30 +110,6 @@ enum BooleanOp {
}
QPolygonF selectionPolygon(KoSelection *selection)
{
QPolygonF result;
QList<KoShape*> selectedShapes = selection->selectedShapes();
if (!selectedShapes.size()) {
return result;
}
if (selectedShapes.size() > 1) {
QTransform matrix = selection->absoluteTransformation(0);
result = matrix.map(QPolygonF(QRectF(QPointF(0, 0), selection->size())));
} else {
KoShape *selectedShape = selectedShapes.first();
QTransform matrix = selectedShape->absoluteTransformation(0);
result = matrix.map(QPolygonF(QRectF(QPointF(0, 0), selectedShape->size())));
}
return result;
}
class NopInteractionStrategy : public KoInteractionStrategy
{
public:
......
......@@ -71,14 +71,16 @@ void SelectionDecorator::setShowStrokeFillGradientHandles(bool value)
void SelectionDecorator::paint(QPainter &painter, const KoViewConverter &converter)
{
const bool haveOnlyOneEditableShape = m_selection->selectedEditableShapes().size() == 1;
QList<KoShape*> selectedShapes = m_selection->selectedVisibleShapes();
if (selectedShapes.isEmpty()) return;
bool editable = false;
const bool haveOnlyOneEditableShape =
m_selection->selectedEditableShapes().size() == 1 &&
selectedShapes.size() == 1;
QList<KoShape*> selectedShapes = m_selection->selectedShapes();
if (selectedShapes.isEmpty()) return;
bool editable = false;
foreach (KoShape *shape, KoShape::linearizeSubtree(selectedShapes)) {
Q_FOREACH (KoShape *shape, KoShape::linearizeSubtree(selectedShapes)) {
if (!haveOnlyOneEditableShape || !m_showStrokeFillGradientHandles) {
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
......@@ -87,7 +89,7 @@ void SelectionDecorator::paint(QPainter &painter, const KoViewConverter &convert
helper.drawRubberLine(shape->outlineRect());
}
if (!shape->isGeometryProtected()) {
if (shape->isShapeEditable()) {
editable = true;
}
}
......
......@@ -168,7 +168,7 @@ void KarbonPatternTool::initialize()
// remove all pattern strategies no longer applicable
Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
// is this gradient shape still selected ?
if (!selectedShapes.contains(strategy->shape()) || ! strategy->shape()->isEditable()) {
if (!selectedShapes.contains(strategy->shape()) || ! strategy->shape()->isShapeEditable()) {
m_strategies.remove(strategy->shape());
if (m_currentStrategy == strategy) {
m_currentStrategy = 0;
......@@ -197,7 +197,7 @@ void KarbonPatternTool::initialize()
// now create new strategies if needed
Q_FOREACH (KoShape *shape, selectedShapes) {
if (!shape->isEditable()) {
if (!shape->isShapeEditable()) {
continue;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment