From bf814410ac8bf5fd78313dd537f5d48f00d18d07 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 19 Feb 2014 00:10:16 -0800 Subject: [PATCH] first cut at exposing clipboard support to JavaScript --- interface/src/Application.cpp | 48 ++++++++++++--- interface/src/Application.h | 13 +++- interface/src/ClipboardScriptingInterface.cpp | 59 +++++++++++++++++++ interface/src/ClipboardScriptingInterface.h | 31 ++++++++++ .../voxels/src/VoxelEditPacketSender.cpp | 4 +- libraries/voxels/src/VoxelEditPacketSender.h | 2 +- 6 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 interface/src/ClipboardScriptingInterface.cpp create mode 100644 interface/src/ClipboardScriptingInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b316548ad4..433158f413 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -62,6 +62,7 @@ #include #include "Application.h" +#include "ClipboardScriptingInterface.h" #include "DataServerClient.h" #include "InterfaceVersion.h" #include "Menu.h" @@ -1686,6 +1687,10 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { } void Application::exportVoxels() { + exportVoxels(_mouseVoxel); +} + +void Application::exportVoxels(const VoxelDetail& sourceVoxel) { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/voxels.svo"); @@ -1693,7 +1698,7 @@ void Application::exportVoxels() { tr("Sparse Voxel Octree Files (*.svo)")); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); - VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); if (selectedNode) { VoxelTree exportTree; _voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true); @@ -1721,11 +1726,19 @@ void Application::importVoxels() { } void Application::cutVoxels() { - copyVoxels(); - deleteVoxelUnderCursor(); + cutVoxels(_mouseVoxel); +} + +void Application::cutVoxels(const VoxelDetail& sourceVoxel) { + copyVoxels(sourceVoxel); + deleteVoxelAt(sourceVoxel); } void Application::copyVoxels() { + copyVoxels(_mouseVoxel); +} + +void Application::copyVoxels(const VoxelDetail& sourceVoxel) { // switch to and clear the clipboard first... _sharedVoxelSystem.killLocalVoxels(); if (_sharedVoxelSystem.getTree() != &_clipboard) { @@ -1734,7 +1747,7 @@ void Application::copyVoxels() { } // then copy onto it if there is something to copy - VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); if (selectedNode) { _voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true); } @@ -1756,8 +1769,12 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati } void Application::pasteVoxels() { + pasteVoxels(_mouseVoxel); +} + +void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { unsigned char* calculatedOctCode = NULL; - VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); // we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the // voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a @@ -1766,7 +1783,7 @@ void Application::pasteVoxels() { if (selectedNode) { octalCodeDestination = selectedNode->getOctalCode(); } else { - octalCodeDestination = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + octalCodeDestination = calculatedOctCode = pointToVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); } pasteVoxelsToOctalCode(octalCodeDestination); @@ -3789,18 +3806,27 @@ bool Application::maybeEditVoxelUnderCursor() { } void Application::deleteVoxelUnderCursor() { - if (_mouseVoxel.s != 0) { + deleteVoxelAt(_mouseVoxel); +} + +void Application::deleteVoxels(const VoxelDetail& voxel) { + deleteVoxelAt(voxel); +} + +void Application::deleteVoxelAt(const VoxelDetail& voxel) { + if (voxel.s != 0) { // sending delete to the server is sufficient, server will send new version so we see updates soon enough - _voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, _mouseVoxel); + _voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel); // delete it locally to see the effect immediately (and in case no voxel server is present) - _voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); } // remember the position for drag detection _justEditedVoxel = true; } + void Application::eyedropperVoxelUnderCursor() { VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); if (selectedNode && selectedNode->isColored()) { @@ -4138,6 +4164,10 @@ void Application::loadScript(const QString& fileNameString) { scriptEngine->registerGlobalObject("Camera", cameraScriptable); connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater())); + ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface(); + scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); + connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater())); + scriptEngine->registerGlobalObject("Overlays", &_overlays); QThread* workerThread = new QThread(this); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0b6907e0f5..7de1b74609 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -222,13 +222,19 @@ public slots: void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); - void exportVoxels(); - void importVoxels(); void cutVoxels(); void copyVoxels(); void pasteVoxels(); - void nudgeVoxels(); void deleteVoxels(); + void exportVoxels(); + void importVoxels(); + void nudgeVoxels(); + + void cutVoxels(const VoxelDetail& sourceVoxel); + void copyVoxels(const VoxelDetail& sourceVoxel); + void pasteVoxels(const VoxelDetail& sourceVoxel); + void deleteVoxels(const VoxelDetail& sourceVoxel); + void exportVoxels(const VoxelDetail& sourceVoxel); void setRenderVoxels(bool renderVoxels); void doKillLocalVoxels(); @@ -322,6 +328,7 @@ private: bool maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); + void deleteVoxelAt(const VoxelDetail& voxel); void eyedropperVoxelUnderCursor(); void setMenuShortcutsEnabled(bool enabled); diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp new file mode 100644 index 0000000000..c2a1a945a1 --- /dev/null +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -0,0 +1,59 @@ +// +// ClipboardScriptingInterface.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "Application.h" +#include "ClipboardScriptingInterface.h" + +ClipboardScriptingInterface::ClipboardScriptingInterface() { +} + +void ClipboardScriptingInterface::cutVoxels(float x, float y, float z, float s) { + VoxelDetail sourceVoxel = { x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + s / (float)TREE_SCALE }; + Application::getInstance()->cutVoxels(sourceVoxel); +} + +void ClipboardScriptingInterface::copyVoxels(float x, float y, float z, float s) { + VoxelDetail sourceVoxel = { x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + s / (float)TREE_SCALE }; + Application::getInstance()->copyVoxels(sourceVoxel); +} + +void ClipboardScriptingInterface::pasteVoxels(float x, float y, float z, float s) { + VoxelDetail sourceVoxel = { x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + s / (float)TREE_SCALE }; + + Application::getInstance()->pasteVoxels(sourceVoxel); +} + +void ClipboardScriptingInterface::deleteVoxels(float x, float y, float z, float s) { + VoxelDetail sourceVoxel = { x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + s / (float)TREE_SCALE }; + Application::getInstance()->deleteVoxels(sourceVoxel); +} + +void ClipboardScriptingInterface::exportVoxels(float x, float y, float z, float s) { + VoxelDetail sourceVoxel = { x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + s / (float)TREE_SCALE }; + + Application::getInstance()->exportVoxels(sourceVoxel); +} + +void ClipboardScriptingInterface::importVoxels() { + Application::getInstance()->importVoxels(); +} + diff --git a/interface/src/ClipboardScriptingInterface.h b/interface/src/ClipboardScriptingInterface.h new file mode 100644 index 0000000000..0b2908f173 --- /dev/null +++ b/interface/src/ClipboardScriptingInterface.h @@ -0,0 +1,31 @@ +// +// ClipboardScriptingInterface.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// Scriptable interface for the Application clipboard +// + +#ifndef __interface__Clipboard__ +#define __interface__Clipboard__ + +#include +#include + +class ClipboardScriptingInterface : public QObject { + Q_OBJECT +public: + ClipboardScriptingInterface(); + +public slots: + void cutVoxels(float x, float y, float z, float s); + void copyVoxels(float x, float y, float z, float s); + void pasteVoxels(float x, float y, float z, float s); + void deleteVoxels(float x, float y, float z, float s); + + void exportVoxels(float x, float y, float z, float s); + void importVoxels(); +}; + +#endif // __interface__Clipboard__ diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index a6d3668207..90884f19f4 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -22,7 +22,7 @@ /// PacketTypeVoxelSet, PacketTypeVoxelSetDestructive, or PacketTypeVoxelErase. The buffer is returned to caller becomes /// responsibility of caller and MUST be deleted by caller. bool createVoxelEditMessage(PacketType command, short int sequence, - int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) { + int voxelCount, const VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) { bool success = true; // assume the best int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now @@ -102,7 +102,7 @@ bool encodeVoxelEditMessageDetails(PacketType, int voxelCount, VoxelDetail* voxe return success; } -void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, VoxelDetail& detail) { +void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, const VoxelDetail& detail) { // allows app to disable sending if for example voxels have been disabled if (!_shouldSend) { return; // bail early diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index 90085635b0..4a1aa87a1c 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -19,7 +19,7 @@ class VoxelEditPacketSender : public OctreeEditPacketSender { Q_OBJECT public: /// Send voxel edit message immediately - void sendVoxelEditMessage(PacketType type, VoxelDetail& detail); + void sendVoxelEditMessage(PacketType type, const VoxelDetail& detail); /// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server /// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to