| 1 | // Aseprite Document Library |
|---|---|
| 2 | // Copyright (C) 2020 Igara Studio S.A. |
| 3 | // Copyright (C) 2016-2018 David Capello |
| 4 | // |
| 5 | // This file is released under the terms of the MIT license. |
| 6 | // Read LICENSE.txt for more information. |
| 7 | |
| 8 | #ifdef HAVE_CONFIG_H |
| 9 | #include "config.h" |
| 10 | #endif |
| 11 | |
| 12 | #include "doc/selected_layers.h" |
| 13 | |
| 14 | #include "base/base.h" |
| 15 | #include "base/debug.h" |
| 16 | #include "base/serialization.h" |
| 17 | #include "doc/layer.h" |
| 18 | #include "doc/sprite.h" |
| 19 | |
| 20 | #include <iostream> |
| 21 | |
| 22 | namespace doc { |
| 23 | |
| 24 | using namespace base::serialization; |
| 25 | using namespace base::serialization::little_endian; |
| 26 | |
| 27 | void SelectedLayers::clear() |
| 28 | { |
| 29 | m_set.clear(); |
| 30 | } |
| 31 | |
| 32 | void SelectedLayers::insert(Layer* layer) |
| 33 | { |
| 34 | ASSERT(layer); |
| 35 | m_set.insert(layer); |
| 36 | } |
| 37 | |
| 38 | void SelectedLayers::erase(const Layer* layer) |
| 39 | { |
| 40 | m_set.erase(const_cast<Layer*>(layer)); |
| 41 | } |
| 42 | |
| 43 | bool SelectedLayers::contains(const Layer* layer) const |
| 44 | { |
| 45 | return m_set.find(const_cast<Layer*>(layer)) != m_set.end(); |
| 46 | } |
| 47 | |
| 48 | bool SelectedLayers::hasSameParent() const |
| 49 | { |
| 50 | Layer* parent = nullptr; |
| 51 | for (auto layer : *this) { |
| 52 | if (parent) { |
| 53 | if (layer->parent() != parent) |
| 54 | return false; |
| 55 | } |
| 56 | else |
| 57 | parent = layer->parent(); |
| 58 | } |
| 59 | return true; |
| 60 | } |
| 61 | |
| 62 | LayerList SelectedLayers::toAllLayersList() const |
| 63 | { |
| 64 | LayerList output; |
| 65 | |
| 66 | if (empty()) |
| 67 | return output; |
| 68 | |
| 69 | ASSERT(*begin()); |
| 70 | ASSERT((*begin())->sprite()); |
| 71 | |
| 72 | for (Layer* layer = (*begin())->sprite()->firstLayer(); |
| 73 | layer != nullptr; |
| 74 | layer = layer->getNextInWholeHierarchy()) { |
| 75 | if (contains(layer)) |
| 76 | output.push_back(layer); |
| 77 | } |
| 78 | |
| 79 | return output; |
| 80 | } |
| 81 | |
| 82 | LayerList SelectedLayers::toBrowsableLayerList() const |
| 83 | { |
| 84 | LayerList output; |
| 85 | |
| 86 | if (empty()) |
| 87 | return output; |
| 88 | |
| 89 | ASSERT(*begin()); |
| 90 | ASSERT((*begin())->sprite()); |
| 91 | |
| 92 | for (Layer* layer = (*begin())->sprite()->firstBrowsableLayer(); |
| 93 | layer != nullptr; |
| 94 | layer = layer->getNextBrowsable()) { |
| 95 | if (contains(layer)) |
| 96 | output.push_back(layer); |
| 97 | } |
| 98 | |
| 99 | return output; |
| 100 | } |
| 101 | |
| 102 | void SelectedLayers::removeChildrenIfParentIsSelected() |
| 103 | { |
| 104 | SelectedLayers removeThese; |
| 105 | |
| 106 | for (Layer* child : *this) { |
| 107 | Layer* parent = child->parent(); |
| 108 | while (parent) { |
| 109 | if (contains(parent)) { |
| 110 | removeThese.insert(child); |
| 111 | break; |
| 112 | } |
| 113 | parent = parent->parent(); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | for (Layer* child : removeThese) |
| 118 | erase(child); |
| 119 | } |
| 120 | |
| 121 | void SelectedLayers::selectAllLayers(LayerGroup* group) |
| 122 | { |
| 123 | for (Layer* layer : group->layers()) { |
| 124 | if (layer->isGroup()) |
| 125 | selectAllLayers(static_cast<LayerGroup*>(layer)); |
| 126 | insert(layer); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | void SelectedLayers::expandCollapsedGroups() |
| 131 | { |
| 132 | auto copy = m_set; |
| 133 | for (Layer* layer : copy) { |
| 134 | if (layer->isGroup() && layer->isCollapsed()) |
| 135 | selectAllLayers(static_cast<LayerGroup*>(layer)); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | void SelectedLayers::displace(layer_t layerDelta) |
| 140 | { |
| 141 | // Do nothing case |
| 142 | if (layerDelta == 0) |
| 143 | return; |
| 144 | |
| 145 | const SelectedLayers original = *this; |
| 146 | |
| 147 | retry:; |
| 148 | clear(); |
| 149 | |
| 150 | for (auto it : original) { |
| 151 | Layer* layer = it; |
| 152 | |
| 153 | if (layerDelta > 0) { |
| 154 | for (layer_t i=0; layer && i<layerDelta; ++i) |
| 155 | layer = layer->getNextBrowsable(); |
| 156 | } |
| 157 | else if (layerDelta < 0) { |
| 158 | for (layer_t i=0; layer && i>layerDelta; --i) { |
| 159 | layer = layer->getPreviousBrowsable(); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | if (layer) { |
| 164 | insert(layer); |
| 165 | } |
| 166 | // If some layer is outside the range it means that the delta is |
| 167 | // too big (out of bounds), we reduce the delta and try again the |
| 168 | // whole process. |
| 169 | else { |
| 170 | layerDelta -= SGN(layerDelta); |
| 171 | if (layerDelta == 0) { |
| 172 | m_set = original.m_set; |
| 173 | break; |
| 174 | } |
| 175 | goto retry; |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | // This will select: |
| 181 | // 1. all visible children in case the parent is selected and none of |
| 182 | // its children is selected. |
| 183 | // 2. all parent if one children is selected |
| 184 | void SelectedLayers::propagateSelection() |
| 185 | { |
| 186 | SelectedLayers newSel; |
| 187 | |
| 188 | for (Layer* layer : *this) { |
| 189 | if (!layer->isGroup()) |
| 190 | continue; |
| 191 | |
| 192 | LayerList children; |
| 193 | static_cast<LayerGroup*>(layer)->allLayers(children); |
| 194 | |
| 195 | bool allDeselected = true; |
| 196 | for (Layer* child : children) { |
| 197 | if (contains(child)) { |
| 198 | allDeselected = false; |
| 199 | break; |
| 200 | } |
| 201 | } |
| 202 | if (allDeselected) { |
| 203 | for (Layer* child : children) { |
| 204 | if (child->isVisible()) |
| 205 | newSel.insert(child); |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | for (Layer* layer : *this) { |
| 211 | Layer* parent = layer->parent(); |
| 212 | while (parent != layer->sprite()->root() && |
| 213 | !contains(parent)) { |
| 214 | newSel.insert(parent); |
| 215 | parent = parent->parent(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | for (Layer* layer : newSel) |
| 220 | insert(layer); |
| 221 | } |
| 222 | |
| 223 | bool SelectedLayers::write(std::ostream& os) const |
| 224 | { |
| 225 | write32(os, size()); |
| 226 | for (const Layer* layer : *this) |
| 227 | write32(os, layer->id()); |
| 228 | return os.good(); |
| 229 | } |
| 230 | |
| 231 | bool SelectedLayers::read(std::istream& is) |
| 232 | { |
| 233 | clear(); |
| 234 | |
| 235 | int nlayers = read32(is); |
| 236 | for (int i=0; i<nlayers && is; ++i) { |
| 237 | ObjectId id = read32(is); |
| 238 | Layer* layer = doc::get<Layer>(id); |
| 239 | |
| 240 | // Check that the layer does exist. You will see a little trick in |
| 241 | // UndoCommand::onExecute() deserializing the DocumentRange stream |
| 242 | // after the undo/redo is executed so layers exist at this point. |
| 243 | |
| 244 | // TODO This should be an assert, but there is a bug that make this fail |
| 245 | //ASSERT(layer); |
| 246 | |
| 247 | if (layer) |
| 248 | insert(layer); |
| 249 | } |
| 250 | return is.good(); |
| 251 | } |
| 252 | |
| 253 | } // namespace doc |
| 254 |