File indexing completed on 2025-02-02 04:11:25
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "selection_manager.hpp" 0008 0009 #include <QGuiApplication> 0010 #include <QMimeData> 0011 #include <QClipboard> 0012 0013 #include "model/assets/assets.hpp" 0014 #include "model/shapes/precomp_layer.hpp" 0015 #include "model/assets/pending_asset.hpp" 0016 0017 #include "command/shape_commands.hpp" 0018 #include "command/structure_commands.hpp" 0019 #include "command/undo_macro_guard.hpp" 0020 0021 #include "glaxnimate_app.hpp" 0022 0023 using namespace glaxnimate::gui; 0024 using namespace glaxnimate; 0025 0026 0027 model::ShapeElement* glaxnimate::gui::SelectionManager::current_shape() const 0028 { 0029 model::DocumentNode* curr = current_document_node(); 0030 if ( curr ) 0031 { 0032 if ( auto curr_shape = qobject_cast<model::ShapeElement*>(curr) ) 0033 return curr_shape; 0034 } 0035 return nullptr; 0036 } 0037 0038 model::ShapeListProperty* glaxnimate::gui::SelectionManager::current_shape_container() const 0039 { 0040 model::DocumentNode* sh = current_document_node(); 0041 0042 if ( !sh ) 0043 return ¤t_composition()->shapes; 0044 0045 if ( auto lay = qobject_cast<model::Composition*>(sh) ) 0046 return &lay->shapes; 0047 0048 if ( !qobject_cast<model::Layer*>(sh) ) 0049 sh = sh->docnode_parent(); 0050 0051 while ( sh ) 0052 { 0053 if ( auto grp = qobject_cast<model::Group*>(sh) ) 0054 return &grp->shapes; 0055 if ( auto lay = qobject_cast<model::Composition*>(sh) ) 0056 return &lay->shapes; 0057 sh = sh->docnode_parent(); 0058 } 0059 0060 return ¤t_composition()->shapes; 0061 } 0062 0063 std::vector<model::VisualNode *> glaxnimate::gui::SelectionManager::copy() const 0064 { 0065 auto selection = cleaned_selection(); 0066 0067 if ( !selection.empty() ) 0068 { 0069 QMimeData* data = new QMimeData; 0070 for ( const auto& serializer : supported_mimes() ) 0071 { 0072 serializer->to_mime_data(*data, std::vector<model::DocumentNode*>(selection.begin(), selection.end())); 0073 } 0074 0075 GlaxnimateApp::instance()->set_clipboard_data(data); 0076 } 0077 0078 return selection; 0079 } 0080 0081 void glaxnimate::gui::SelectionManager::cut() 0082 { 0083 auto selection = copy(); 0084 0085 delete_shapes_impl(i18n("Cut"), selection); 0086 } 0087 0088 void glaxnimate::gui::SelectionManager::delete_selected() 0089 { 0090 delete_shapes_impl(i18n("Delete"), cleaned_selection()); 0091 } 0092 0093 void glaxnimate::gui::SelectionManager::delete_shapes_impl(const QString &undo_string, const std::vector<model::VisualNode *>& selection) 0094 { 0095 if ( selection.empty() ) 0096 return; 0097 0098 auto doc = document(); 0099 command::UndoMacroGuard macro(undo_string, doc); 0100 auto current = this->current_document_node(); 0101 0102 for ( auto item : selection ) 0103 { 0104 if ( auto shape = qobject_cast<model::ShapeElement*>(item) ) 0105 { 0106 if ( !shape->docnode_locked_recursive() ) 0107 { 0108 if ( current->is_descendant_of(shape) ) 0109 current = shape->docnode_visual_parent(); 0110 doc->push_command(new command::RemoveShape(shape, shape->owner())); 0111 } 0112 } 0113 } 0114 0115 if ( current ) 0116 set_current_document_node(current); 0117 } 0118 0119 void glaxnimate::gui::SelectionManager::paste() 0120 { 0121 paste_impl(false); 0122 } 0123 0124 void glaxnimate::gui::SelectionManager::paste_as_composition() 0125 { 0126 paste_impl(true); 0127 } 0128 0129 template<class T> 0130 static void paste_assets(model::SubObjectProperty<T> (model::Assets::* p), model::Document* source, model::Document* current_document) 0131 { 0132 T* subject = (source->assets()->*p).get(); 0133 T* target = (current_document->assets()->*p).get(); 0134 0135 for ( auto& item : subject->values.raw() ) 0136 { 0137 if ( !current_document->assets()->find_by_uuid(item->uuid.get()) ) 0138 { 0139 item->transfer(current_document); 0140 current_document->push_command(new command::AddObject( 0141 &target->values, 0142 std::move(item), 0143 target->values.size() 0144 )); 0145 } 0146 } 0147 } 0148 0149 void glaxnimate::gui::SelectionManager::paste_impl(bool as_comp) 0150 { 0151 const QMimeData* data = GlaxnimateApp::instance()->get_clipboard_data(); 0152 0153 io::mime::DeserializedData raw_pasted; 0154 for ( const auto& serializer : supported_mimes() ) 0155 { 0156 raw_pasted = serializer->from_mime_data(*data); 0157 if ( !raw_pasted.empty() ) 0158 break; 0159 } 0160 if ( raw_pasted.empty() ) 0161 { 0162 // status_message(i18n("Nothing to paste")); 0163 return; 0164 } 0165 0166 paste_document(raw_pasted.document.get(), i18n("Paste"), as_comp); 0167 } 0168 0169 void glaxnimate::gui::SelectionManager::paste_document(model::Document* document, const QString& macro_name, bool as_comp) 0170 { 0171 auto doc = this->document(); 0172 0173 auto& comps = document->assets()->compositions->values; 0174 0175 command::UndoMacroGuard macro(macro_name, doc); 0176 paste_assets(&model::Assets::colors, document, doc); 0177 paste_assets(&model::Assets::images, document, doc); 0178 paste_assets(&model::Assets::gradient_colors, document, doc); 0179 paste_assets(&model::Assets::gradients, document, doc); 0180 0181 model::ShapeListProperty* shape_cont = current_shape_container(); 0182 std::vector<model::VisualNode*> select; 0183 0184 if ( comps.empty() ) 0185 return; 0186 if ( comps.size() > 1 ) 0187 as_comp = true; 0188 0189 model::Composition* comp = comps[0]; 0190 0191 if ( !as_comp ) 0192 { 0193 if ( !comp->shapes.empty() ) 0194 { 0195 int shape_insertion_point = shape_cont->size(); 0196 for ( auto& shape : comp->shapes.raw() ) 0197 { 0198 auto ptr = shape.get(); 0199 shape->clear_owner(); 0200 shape->refresh_uuid(); 0201 if ( !as_comp ) 0202 select.push_back(ptr); 0203 shape->transfer(doc); 0204 doc->push_command(new command::AddShape(shape_cont, std::move(shape), shape_insertion_point++)); 0205 if ( !as_comp ) 0206 ptr->recursive_rename(); 0207 } 0208 } 0209 } 0210 else 0211 { 0212 paste_assets(&model::Assets::compositions, document, doc); 0213 } 0214 0215 for ( const auto& pending : document->pending_assets() ) 0216 doc->add_pending_asset(pending); 0217 0218 if ( as_comp ) 0219 { 0220 select.push_back(layer_new_comp(comp)); 0221 } 0222 0223 set_selection(select); 0224 } 0225 0226 void glaxnimate::gui::SelectionManager::layer_new_impl(std::unique_ptr<model::ShapeElement> layer) 0227 { 0228 auto doc = document(); 0229 if ( layer->name.get().isEmpty() ) 0230 doc->set_best_name(layer.get(), {}); 0231 auto curr_dn = current_document_node(); 0232 layer->set_time(curr_dn ? curr_dn->time() : doc->current_time()); 0233 0234 model::ShapeElement* ptr = layer.get(); 0235 0236 auto cont = current_shape_container(); 0237 int position = cont->index_of(current_shape()); 0238 if ( position >= 0 ) 0239 position += 1; 0240 doc->push_command(new command::AddShape(cont, std::move(layer), position)); 0241 0242 set_current_document_node(ptr); 0243 } 0244 0245 0246 model::PreCompLayer* glaxnimate::gui::SelectionManager::layer_new_comp(model::Composition* comp) 0247 { 0248 auto doc = document(); 0249 auto layer = std::make_unique<model::PreCompLayer>(doc); 0250 layer->composition.set(comp); 0251 layer->name.set(comp->name.get()); 0252 layer->size.set(comp->size()); 0253 QPointF pos = current_composition()->rect().center(); 0254 layer->transform.get()->anchor_point.set(pos); 0255 layer->transform.get()->position.set(pos); 0256 auto ptr = layer.get(); 0257 layer_new_impl(std::move(layer)); 0258 return ptr; 0259 }