[kde-doc-english] [step] step: Added copy-and-paste support
Sebastian Voecking
kde at voecking.net
Sun May 8 14:09:50 CEST 2011
Git commit 8e68940e2cc526b87594ba6a0573e72c645672bc by Sebastian Voecking.
Committed on 08/05/2011 at 13:57.
Pushed by voecking into branch 'master'.
Added copy-and-paste support
FEATURE: 183919
GUI:
M +1 -0 step/CMakeLists.txt
A +195 -0 step/clipboard.cc [License: GPL (v2+)]
A +56 -0 step/clipboard.h [License: GPL (v2+)]
M +17 -0 step/mainwindow.cc
M +3 -0 step/mainwindow.h
M +4 -8 step/worldgraphics.cc
M +0 -3 step/worldgraphics.h
M +73 -0 step/worldmodel.cc
M +9 -0 step/worldmodel.h
http://commits.kde.org/step/8e68940e2cc526b87594ba6a0573e72c645672bc
diff --git a/step/CMakeLists.txt b/step/CMakeLists.txt
index 4d571ea..03d9f75 100644
--- a/step/CMakeLists.txt
+++ b/step/CMakeLists.txt
@@ -1,5 +1,6 @@
set(step_SRCS
arrow.cc
+ clipboard.cc
mainwindow.cc
worldmodel.cc
worldscene.cc
diff --git a/step/clipboard.cc b/step/clipboard.cc
new file mode 100644
index 0000000..19aa48c
--- /dev/null
+++ b/step/clipboard.cc
@@ -0,0 +1,195 @@
+/* This file is part of Step.
+ * Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir at gmail.com>
+ *
+ * Step is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Step is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Step; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "clipboard.h"
+
+#include <QApplication>
+#include <QBuffer>
+#include <QClipboard>
+#include <QMimeData>
+
+#include <KDebug>
+
+#include <stepcore/factory.h>
+#include <stepcore/world.h>
+#include <stepcore/xmlfile.h>
+
+namespace
+{
+class CopyHelper
+{
+public:
+ void addItem(const StepCore::Item* item);
+ StepCore::World* createWorld();
+
+private:
+ void fillMap(const StepCore::Object* item, StepCore::Object* copy);
+ void fixItemLinks(StepCore::Item* item);
+
+ QHash<const StepCore::Object*, StepCore::Object*> _copyMap;
+ QList<StepCore::Item*> _items;
+};
+
+void CopyHelper::addItem(const StepCore::Item* item)
+{
+ StepCore::Object *copy = item->metaObject()->cloneObject(*item);
+
+ _items << static_cast<StepCore::Item*>(copy);
+ fillMap(item, copy);
+}
+
+StepCore::World* CopyHelper::createWorld()
+{
+ StepCore::World *world = new StepCore::World;
+
+ foreach (StepCore::Item* item, _items) {
+ world->addItem(item);
+ }
+
+ StepCore::ItemList items;
+ world->allItems(&items);
+ foreach (StepCore::Item* item, items) {
+ fixItemLinks(item);
+ }
+
+ _items.clear();
+ _copyMap.clear();
+
+ return world;
+}
+
+void CopyHelper::fillMap(const StepCore::Object* item, StepCore::Object* copy)
+{
+ _copyMap.insert(item, copy);
+
+ if (item->metaObject()->inherits<StepCore::ItemGroup>()) {
+ const StepCore::ItemGroup* group =
+ static_cast<const StepCore::ItemGroup*>(item);
+ StepCore::ItemGroup* copiedGroup =
+ static_cast<StepCore::ItemGroup*>(copy);
+
+ StepCore::ItemList items;
+ group->allItems(&items);
+ StepCore::ItemList copiedItems;
+ copiedGroup->allItems(&copiedItems);
+
+ for (StepCore::ItemList::size_type n = 0; n < items.size(); ++n) {
+ _copyMap.insert(items[n], copiedItems[n]);
+ }
+ }
+}
+
+void CopyHelper::fixItemLinks(StepCore::Item* item)
+{
+ const StepCore::MetaObject* mobj = item->metaObject();
+
+ for (int i = 0; i < mobj->propertyCount(); ++i) {
+ const StepCore::MetaProperty* pr = mobj->property(i);
+
+ if (pr->userTypeId() == qMetaTypeId<StepCore::Object*>()) {
+ QVariant v = pr->readVariant(item);
+ StepCore::Object *obj = v.value<StepCore::Object*>();
+ StepCore::Object *copy = _copyMap.value(obj, 0);
+ pr->writeVariant(item, QVariant::fromValue(copy));
+ }
+ }
+}
+}
+
+Clipboard::Clipboard(QObject* parent) : QObject(parent), _canPaste(hasData())
+{
+ connect(QApplication::clipboard(), SIGNAL(dataChanged()),
+ this, SLOT(dataChanged()));
+}
+
+
+void Clipboard::copy(const QList<StepCore::Item*>& items)
+{
+ CopyHelper helper;
+
+ foreach (const StepCore::Item* item, items) {
+ helper.addItem(item);
+ }
+
+ QScopedPointer<StepCore::World> world(helper.createWorld());
+
+ QBuffer buffer;
+ buffer.open(QBuffer::WriteOnly);
+ StepCore::XmlFile xmlfile(&buffer);
+ if (!xmlfile.save(world.data())) {
+ // Serialization of items failed
+ kWarning() << xmlfile.errorString();
+ return;
+ }
+
+ QMimeData *mimedata = new QMimeData;
+ mimedata->setData("application/x-step", buffer.data());
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setMimeData(mimedata);
+}
+
+QList<StepCore::Item*> Clipboard::paste(const StepCore::Factory* factory)
+{
+ QClipboard *clipboard = QApplication::clipboard();
+ const QMimeData *mimedata = clipboard->mimeData();
+
+ if (!mimedata->hasFormat("application/x-step")) {
+ // No Step data available
+ kWarning() << "No Step data on the clipboard";
+ return QList<StepCore::Item*>();
+ }
+
+ QByteArray data(mimedata->data("application/x-step"));
+ QBuffer buffer(&data);
+ buffer.open(QBuffer::ReadOnly);
+ StepCore::XmlFile xmlfile(&buffer);
+
+ StepCore::World world;
+ if (!xmlfile.load(&world, factory)) {
+ // Deserialization of items failed
+ kError() << xmlfile.errorString();
+ return QList<StepCore::Item*>();
+ }
+
+ QList<StepCore::Item*> qitems;
+ foreach (StepCore::Item* item, world.items()) {
+ world.removeItem(item);
+ qitems << item;
+ }
+
+ return qitems;
+}
+
+void Clipboard::dataChanged()
+{
+ bool canPaste = hasData();
+
+ if (canPaste != _canPaste) {
+ _canPaste = canPaste;
+ emit canPasteChanged(canPaste);
+ }
+}
+
+bool Clipboard::hasData() const
+{
+ QClipboard *clipboard = QApplication::clipboard();
+ const QMimeData *mimedata = clipboard->mimeData();
+
+ return mimedata->hasFormat("application/x-step");
+}
diff --git a/step/clipboard.h b/step/clipboard.h
new file mode 100644
index 0000000..831991c
--- /dev/null
+++ b/step/clipboard.h
@@ -0,0 +1,56 @@
+/* This file is part of Step.
+ * Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir at gmail.com>
+ *
+ * Step is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Step is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Step; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STEP_CLIPBOARD_H
+#define STEP_CLIPBOARD_H
+
+#include <QList>
+#include <QObject>
+#include <QVector>
+
+namespace StepCore
+{
+ class Factory;
+ class Item;
+}
+
+class Clipboard : public QObject
+{
+public:
+ Clipboard(QObject* parent = 0);
+
+ void copy(const QList<StepCore::Item*>& items);
+ QList<StepCore::Item*> paste(const StepCore::Factory* factory);
+
+ bool canPaste() const { return _canPaste; }
+
+signals:
+ void canPasteChanged(bool value);
+
+private slots:
+ void dataChanged();
+
+private:
+ bool hasData() const;
+
+ bool _canPaste;
+
+ Q_OBJECT
+};
+
+#endif
diff --git a/step/mainwindow.cc b/step/mainwindow.cc
index fdc0e50..cb3d575 100644
--- a/step/mainwindow.cc
+++ b/step/mainwindow.cc
@@ -21,6 +21,7 @@
#include "ui_configure_step_general.h"
+#include "clipboard.h"
#include "worldmodel.h"
#include "worldscene.h"
#include "worldbrowser.h"
@@ -181,6 +182,18 @@ void MainWindow::setupActions()
this, SLOT(undoTextChanged(const QString&)));
connect(worldModel->undoStack(), SIGNAL(redoTextChanged(const QString&)),
this, SLOT(redoTextChanged(const QString&)));
+
+ actionCut = KStandardAction::cut(worldModel, SLOT(cutSelectedItems()),
+ actionCollection());
+ actionCopy = KStandardAction::copy(worldModel, SLOT(copySelectedItems()),
+ actionCollection());
+ actionPaste = KStandardAction::paste(worldModel, SLOT(pasteItems()),
+ actionCollection());
+ actionCut->setEnabled(false);
+ actionCopy->setEnabled(false);
+ actionPaste->setEnabled(worldModel->clipboard()->canPaste());
+ connect(worldModel->clipboard(), SIGNAL(canPasteChanged(bool)),
+ actionPaste, SLOT(setEnabled(bool)));
actionDelete = actionCollection()->add<KAction>("edit_delete", worldModel, SLOT(deleteSelectedItems()));
actionDelete->setText(i18n("&Delete"));
@@ -556,11 +569,15 @@ void MainWindow::worldSelectionChanged()
worldModel->selectionModel()->selection().indexes()) {
if (index != worldModel->worldIndex() && worldModel->item(index)) {
actionDelete->setEnabled(true);
+ actionCut->setEnabled(true);
+ actionCopy->setEnabled(true);
return;
}
}
}
actionDelete->setEnabled(false);
+ actionCut->setEnabled(false);
+ actionCopy->setEnabled(false);
}
void MainWindow::configureStep()
diff --git a/step/mainwindow.h b/step/mainwindow.h
index dea9918..5e46245 100644
--- a/step/mainwindow.h
+++ b/step/mainwindow.h
@@ -114,6 +114,9 @@ protected:
KAction* actionUndo;
KAction* actionRedo;
KAction* actionDelete;
+ KAction* actionCut;
+ KAction* actionCopy;
+ KAction* actionPaste;
KRecentFilesAction* actionRecentFiles;
diff --git a/step/worldgraphics.cc b/step/worldgraphics.cc
index 825c8b3..c6edf92 100644
--- a/step/worldgraphics.cc
+++ b/step/worldgraphics.cc
@@ -763,14 +763,10 @@ ItemMenuHandler::ItemMenuHandler(StepCore::Object* object, WorldModel* worldMode
void ItemMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions)
{
StepCore::Item* item = dynamic_cast<StepCore::Item*>(_object);
- if(item && item->world() != item) {
+
+ if (item && item->world() != item) {
+ menu->addAction(actions->action("edit_cut"));
+ menu->addAction(actions->action("edit_copy"));
menu->addAction(actions->action("edit_delete"));
- //menu->addAction(KIcon("edit-delete"), i18n("&Delete"), this, SLOT(deleteItem()));
}
}
-
-void ItemMenuHandler::deleteItem()
-{
- _worldModel->deleteItem(static_cast<StepCore::Item*>(_object));
-}
-
diff --git a/step/worldgraphics.h b/step/worldgraphics.h
index da5d9d5..9fcf63e 100644
--- a/step/worldgraphics.h
+++ b/step/worldgraphics.h
@@ -433,9 +433,6 @@ public:
* Default implementation adds delete action. */
virtual void populateMenu(QMenu* menu, KActionCollection* actions);
-protected slots:
- void deleteItem();
-
protected:
StepCore::Object* _object;
WorldModel* _worldModel;
diff --git a/step/worldmodel.cc b/step/worldmodel.cc
index 513135b..7051905 100644
--- a/step/worldmodel.cc
+++ b/step/worldmodel.cc
@@ -17,6 +17,8 @@
*/
#include "worldmodel.h"
+
+#include "clipboard.h"
#include "simulationthread.h"
#include "worldgraphics.h"
#include "worldmodel.moc"
@@ -309,6 +311,7 @@ WorldModel::WorldModel(QObject* parent)
{
_selectionModel = new QItemSelectionModel(this, this);
_undoStack = new KUndoStack(this);
+ _clipboard = new Clipboard(this);
_worldFactory = new WorldFactory();
_world = new StepCore::World();
@@ -1015,3 +1018,73 @@ void WorldModel::simulationFrameEnd(int result)
}
}
+QList<StepCore::Item*> WorldModel::selectedItems()
+{
+ QList<StepCore::Item*> items;
+ foreach (QModelIndex index, selectionModel()->selectedIndexes()) {
+ // Do not delete world item
+ if (index == worldIndex()) continue;
+
+ StepCore::Item* it = item(index);
+ if (it) items << it;
+ }
+
+ foreach (StepCore::Item* it, items) {
+ for (StepCore::Item* it1 = it->group(); it1 != 0 && it1 != _world; it1 = it1->group()) {
+ if (items.contains(it1)) {
+ items.removeOne(it);
+ break;
+ }
+ }
+ }
+
+ return items;
+}
+
+void WorldModel::cutSelectedItems()
+{
+ simulationPause();
+
+ QList<StepCore::Item*> items = selectedItems();
+
+ _clipboard->copy(items);
+
+ if (!items.isEmpty()) {
+ beginMacro(items.count() == 1 ? i18n("Cut %1", items[0]->name()) :
+ i18n("Cut several items"));
+ foreach (StepCore::Item* it, items) deleteItem(it);
+ endMacro();
+ }
+}
+
+void WorldModel::copySelectedItems()
+{
+ simulationPause();
+
+ QList<StepCore::Item*> items = selectedItems();
+
+ _clipboard->copy(items);
+}
+
+void WorldModel::pasteItems()
+{
+ simulationPause();
+
+ QList<StepCore::Item*> items = _clipboard->paste(_worldFactory);
+ if (items.isEmpty()) return;
+
+ beginMacro(items.count() == 1 ? i18n("Pasted %1", items[0]->name()) :
+ i18n("Pasted several items"));
+ QItemSelection selection;
+ foreach (StepCore::Item* item, items) {
+ QString className = item->metaObject()->className();
+ item->setName(getUniqueName(className));
+ addItem(item, _world);
+ QModelIndex index = objectIndex(item);
+ selection.select(index, index);
+ }
+ if (!selection.isEmpty()) {
+ selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+ }
+ endMacro();
+}
diff --git a/step/worldmodel.h b/step/worldmodel.h
index a441edd..1306826 100644
--- a/step/worldmodel.h
+++ b/step/worldmodel.h
@@ -40,6 +40,8 @@ namespace StepCore {
class QItemSelectionModel;
class QTimer;
class QMenu;
+
+class Clipboard;
class WorldFactory;
class CommandSimulate;
class SimulationThread;
@@ -126,6 +128,8 @@ public:
void pushCommand(QUndoCommand* command); ///< Push new undo command
void beginMacro(const QString& text); ///< Begin undo macro
void endMacro(); ///< End undo macro
+
+ Clipboard* clipboard() const { return _clipboard; }
// Property edit
/** Modify object property.
@@ -209,6 +213,9 @@ public slots:
void simulationStart(); ///< Start simulation
void simulationStop(); ///< Stop simulation
+ void cutSelectedItems();
+ void copySelectedItems();
+ void pasteItems();
void deleteSelectedItems(); ///< Delete all selected items
protected slots:
@@ -233,6 +240,7 @@ protected:
void addCreatedItem(StepCore::Item* item, StepCore::ItemGroup* parent = 0);
void removeCreatedItem(StepCore::Item* item);
StepCore::Solver* swapSolver(StepCore::Solver* solver);
+ QList<StepCore::Item*> selectedItems();
// Only for UndoCommand* classes
//void objectChanged(const StepCore::Object* object);
@@ -241,6 +249,7 @@ protected:
StepCore::World* _world;
QItemSelectionModel* _selectionModel;
KUndoStack* _undoStack;
+ Clipboard* _clipboard;
const WorldFactory* _worldFactory;
QString _errorString;
More information about the kde-doc-english
mailing list