1 /* This file is part of Step.
2  *   Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
3  *
4  *   Step is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   Step is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with Step; if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include "clipboard.h"
20 
21 #include <QApplication>
22 #include <QBuffer>
23 #include <QClipboard>
24 #include <QDebug>
25 #include <QMimeData>
26 
27 #include <stepcore/factory.h>
28 #include <stepcore/world.h>
29 #include <stepcore/xmlfile.h>
30 
31 namespace
32 {
33 class CopyHelper
34 {
35 public:
36     void addItem(const StepCore::Item* item);
37     StepCore::World* createWorld();
38 
39 private:
40     void fillMap(const StepCore::Object* item, StepCore::Object* copy);
41     void fixItemLinks(StepCore::Item* item);
42 
43     QHash<const StepCore::Object*, StepCore::Object*> _copyMap;
44     QList<StepCore::Item*> _items;
45 };
46 
addItem(const StepCore::Item * item)47 void CopyHelper::addItem(const StepCore::Item* item)
48 {
49     StepCore::Object *copy = item->metaObject()->cloneObject(*item);
50 
51     _items << static_cast<StepCore::Item*>(copy);
52     fillMap(item, copy);
53 }
54 
createWorld()55 StepCore::World* CopyHelper::createWorld()
56 {
57     StepCore::World *world = new StepCore::World;
58 
59     foreach (StepCore::Item* item, _items) {
60         world->addItem(item);
61     }
62 
63     StepCore::ItemList items;
64     world->allItems(&items);
65     foreach (StepCore::Item* item, items) {
66         fixItemLinks(item);
67     }
68 
69     _items.clear();
70     _copyMap.clear();
71 
72     return world;
73 }
74 
fillMap(const StepCore::Object * item,StepCore::Object * copy)75 void CopyHelper::fillMap(const StepCore::Object* item, StepCore::Object* copy)
76 {
77     _copyMap.insert(item, copy);
78 
79     if (item->metaObject()->inherits<StepCore::ItemGroup>()) {
80         const StepCore::ItemGroup* group =
81             static_cast<const StepCore::ItemGroup*>(item);
82         StepCore::ItemGroup* copiedGroup =
83             static_cast<StepCore::ItemGroup*>(copy);
84 
85         StepCore::ItemList items;
86         group->allItems(&items);
87         StepCore::ItemList copiedItems;
88         copiedGroup->allItems(&copiedItems);
89 
90         for (StepCore::ItemList::size_type n = 0; n < items.size(); ++n) {
91             _copyMap.insert(items[n], copiedItems[n]);
92         }
93     }
94 }
95 
fixItemLinks(StepCore::Item * item)96 void CopyHelper::fixItemLinks(StepCore::Item* item)
97 {
98     const StepCore::MetaObject* mobj = item->metaObject();
99 
100     for (int i = 0; i < mobj->propertyCount(); ++i) {
101         const StepCore::MetaProperty* pr = mobj->property(i);
102 
103         if (pr->userTypeId() == qMetaTypeId<StepCore::Object*>()) {
104             QVariant v = pr->readVariant(item);
105             StepCore::Object *obj = v.value<StepCore::Object*>();
106             StepCore::Object *copy = _copyMap.value(obj, 0);
107             pr->writeVariant(item, QVariant::fromValue(copy));
108         }
109     }
110 }
111 }
112 
Clipboard(QObject * parent)113 Clipboard::Clipboard(QObject* parent) : QObject(parent), _canPaste(hasData())
114 {
115     connect(QApplication::clipboard(), &QClipboard::dataChanged,
116             this, &Clipboard::dataChanged);
117 }
118 
119 
copy(const QList<StepCore::Item * > & items)120 void Clipboard::copy(const QList<StepCore::Item*>& items)
121 {
122     CopyHelper helper;
123 
124     foreach (const StepCore::Item* item, items) {
125         helper.addItem(item);
126     }
127 
128     QScopedPointer<StepCore::World> world(helper.createWorld());
129 
130     QBuffer buffer;
131     buffer.open(QBuffer::WriteOnly);
132     StepCore::XmlFile xmlfile(&buffer);
133     if (!xmlfile.save(world.data())) {
134         // Serialization of items failed
135         qWarning() << xmlfile.errorString();
136         return;
137     }
138 
139     QMimeData *mimedata = new QMimeData;
140     mimedata->setData(QStringLiteral("application/x-step"), buffer.data());
141 
142     QClipboard *clipboard = QApplication::clipboard();
143     clipboard->setMimeData(mimedata);
144 }
145 
paste(const StepCore::Factory * factory)146 QList<StepCore::Item*> Clipboard::paste(const StepCore::Factory* factory)
147 {
148     QClipboard *clipboard = QApplication::clipboard();
149     const QMimeData *mimedata = clipboard->mimeData();
150 
151     if (!mimedata->hasFormat(QStringLiteral("application/x-step"))) {
152         // No Step data available
153         qWarning() << "No Step data on the clipboard";
154         return QList<StepCore::Item*>();
155     }
156 
157     QByteArray data(mimedata->data(QStringLiteral("application/x-step")));
158     QBuffer buffer(&data);
159     buffer.open(QBuffer::ReadOnly);
160     StepCore::XmlFile xmlfile(&buffer);
161 
162     StepCore::World world;
163     if (!xmlfile.load(&world, factory)) {
164         // Deserialization of items failed
165         qCritical() << xmlfile.errorString();
166         return QList<StepCore::Item*>();
167     }
168 
169     QList<StepCore::Item*> qitems;
170     foreach (StepCore::Item* item, world.items()) {
171         world.removeItem(item);
172         qitems << item;
173     }
174 
175     return qitems;
176 }
177 
dataChanged()178 void Clipboard::dataChanged()
179 {
180     bool canPaste = hasData();
181 
182     if (canPaste != _canPaste) {
183         _canPaste = canPaste;
184         emit canPasteChanged(canPaste);
185     }
186 }
187 
hasData() const188 bool Clipboard::hasData() const
189 {
190     QClipboard *clipboard = QApplication::clipboard();
191     const QMimeData *mimedata = clipboard->mimeData();
192 
193     return mimedata->hasFormat(QStringLiteral("application/x-step"));
194 }
195