[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