void Node::assignToLayers(const LayerList& newLayers) { if (!newLayers.empty()) { _layers = newLayers; } }
static DocRange drop_range_op( Doc* doc, const Op op, const DocRange& from, DocRangePlace place, const TagsHandling tagsHandling, DocRange to) { // Convert "first child" operation into a insert after last child. LayerGroup* parent = nullptr; if (to.type() == DocRange::kLayers && !to.selectedLayers().empty()) { if (place == kDocRangeFirstChild && (*to.selectedLayers().begin())->isGroup()) { place = kDocRangeAfter; parent = static_cast<LayerGroup*>((*to.selectedLayers().begin())); to.clearRange(); to.startRange(parent->lastLayer(), -1, DocRange::kLayers); to.endRange(parent->lastLayer(), -1); } else { parent = (*to.selectedLayers().begin())->parent(); } // Check that we're not moving a group inside itself for (auto moveThis : from.selectedLayers()) { if (moveThis == parent || (moveThis->isGroup() && has_child(static_cast<LayerGroup*>(moveThis), parent))) return from; } } if (place != kDocRangeBefore && place != kDocRangeAfter) { ASSERT(false); throw std::invalid_argument("Invalid 'place' argument"); } Sprite* sprite = doc->sprite(); // Check noop/trivial/do nothing cases, i.e., move a range to the same place. // Also check invalid cases, like moving a Background layer. switch (from.type()) { case DocRange::kCels: if (from == to) return from; break; case DocRange::kFrames: if (op == Move) { // Simple cases with one continuos range of frames that are a // no-op. if ((from.selectedFrames().ranges() == 1) && ((to.firstFrame() >= from.firstFrame() && to.lastFrame() <= from.lastFrame()) || (place == kDocRangeBefore && to.firstFrame() == from.lastFrame()+1) || (place == kDocRangeAfter && to.lastFrame() == from.firstFrame()-1)) && // If there are tags, this might not be a no-op (sprite->frameTags().empty() || tagsHandling == kDontAdjustTags)) { return from; } } break; case DocRange::kLayers: if (op == Move) { SelectedLayers srcSelLayers = from.selectedLayers(); SelectedLayers dstSelLayers = to.selectedLayers(); LayerList srcLayers = srcSelLayers.toLayerList(); LayerList dstLayers = dstSelLayers.toLayerList(); ASSERT(!srcLayers.empty()); if (srcLayers.empty()) return from; // dstLayers can be nullptr when we insert the first child in // a group. // Check no-ops when we move layers at the same level (all // layers with the same parent), all adjacents, and which are // moved to the same place. if (!dstSelLayers.empty() && srcSelLayers.hasSameParent() && dstSelLayers.hasSameParent() && are_layers_adjacent(srcLayers) && are_layers_adjacent(dstLayers)) { for (Layer* srcLayer : srcLayers) if (dstSelLayers.contains(srcLayer)) return from; if ((place == kDocRangeBefore && dstLayers.front() == srcLayers.back()->getNext()) || (place == kDocRangeAfter && dstLayers.back() == srcLayers.front()->getPrevious())) return from; } // We cannot move the background for (Layer* layer : srcSelLayers) if (layer->isBackground()) throw std::runtime_error("The background layer cannot be moved"); } // Before background if (place == kDocRangeBefore) { for (Layer* background : to.selectedLayers()) { if (background && background->isBackground()) throw std::runtime_error("You cannot move or copy something below the background layer"); } } break; } const char* undoLabel = NULL; switch (op) { case Move: undoLabel = "Move Range"; break; case Copy: undoLabel = "Copy Range"; break; default: ASSERT(false); throw std::invalid_argument("Invalid 'op' argument"); } DocRange resultRange; { const app::Context* context = static_cast<app::Context*>(doc->context()); const ContextReader reader(context); ContextWriter writer(reader, 500); Transaction transaction(writer.context(), undoLabel, ModifyDocument); DocApi api = doc->getApi(transaction); // TODO Try to add the range with just one call to DocApi // methods, to avoid generating a lot of cmd::SetCelFrame (see // DocApi::setCelFramePosition() function). switch (from.type()) { case DocRange::kCels: { LayerList allLayers = sprite->allBrowsableLayers(); if (allLayers.empty()) break; LayerList srcLayers = from.selectedLayers().toLayerList(); LayerList dstLayers = to.selectedLayers().toLayerList(); if (srcLayers.empty() || dstLayers.empty()) throw std::invalid_argument("You need to specify a non-empty cels range"); if (find_layer_index(allLayers, srcLayers.front()) < find_layer_index(allLayers, dstLayers.front())) { std::reverse(srcLayers.begin(), srcLayers.end()); std::reverse(dstLayers.begin(), dstLayers.end()); } if (from.firstFrame() < to.firstFrame()) { auto srcFrames = from.selectedFrames().makeReverse(); auto dstFrames = to.selectedFrames().makeReverse(); move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); } else { const auto& srcFrames = from.selectedFrames(); const auto& dstFrames = to.selectedFrames(); move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames); } resultRange = to; break; } case DocRange::kFrames: { frame_t dstFrame; if (place == kDocRangeBefore) dstFrame = to.firstFrame(); else dstFrame = to.lastFrame(); resultRange = move_or_copy_frames(api, op, sprite, from, dstFrame, place, tagsHandling); break; } case DocRange::kLayers: { LayerList allLayers = sprite->allBrowsableLayers(); if (allLayers.empty()) break; LayerList srcLayers = from.selectedLayers().toLayerList(); LayerList dstLayers = to.selectedLayers().toLayerList(); ASSERT(!srcLayers.empty()); switch (op) { case Move: if (place == kDocRangeBefore) { Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr); Layer* afterThis = nullptr; for (Layer* srcLayer : srcLayers) { if (afterThis) api.restackLayerAfter(srcLayer, parent, afterThis); else api.restackLayerBefore(srcLayer, parent, beforeThis); afterThis = srcLayer; } } else if (place == kDocRangeAfter) { Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr); for (Layer* srcLayer : srcLayers) { api.restackLayerAfter(srcLayer, parent, afterThis); afterThis = srcLayer; } } // Same set of layers than the "from" range resultRange = from; break; case Copy: { if (place == kDocRangeBefore) { Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr); for (Layer* srcLayer : srcLayers) { Layer* copiedLayer = api.duplicateLayerBefore( srcLayer, parent, beforeThis); resultRange.startRange(copiedLayer, -1, DocRange::kLayers); resultRange.endRange(copiedLayer, -1); } } else if (place == kDocRangeAfter) { std::reverse(srcLayers.begin(), srcLayers.end()); Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr); for (Layer* srcLayer : srcLayers) { Layer* copiedLayer = api.duplicateLayerAfter( srcLayer, parent, afterThis); resultRange.startRange(copiedLayer, -1, DocRange::kLayers); resultRange.endRange(copiedLayer, -1); } } break; } } break; } } if (resultRange.type() != DocRange::kNone) transaction.setNewDocRange(resultRange); transaction.commit(); } return resultRange; }