Skip to content

Commit 5906898

Browse files
committed
copy/paste entity text data (fixes #57)
copying or cutting an entity will add its .ent data to the clipboard, which you can paste into a text editor or another bspguy window via Edit -> "Paste entities from clipboard".
1 parent 3c2b092 commit 5906898

File tree

7 files changed

+177
-0
lines changed

7 files changed

+177
-0
lines changed

src/bsp/Entity.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "util.h"
33
#include <set>
44
#include <algorithm>
5+
#include <sstream>
56

67
using namespace std;
78

@@ -493,3 +494,18 @@ bool Entity::isEverVisible() {
493494

494495
return true;
495496
}
497+
498+
string Entity::serialize() {
499+
stringstream ent_data;
500+
501+
ent_data << "{\n";
502+
503+
for (int k = 0; k < keyOrder.size(); k++) {
504+
string key = keyOrder[k];
505+
ent_data << "\"" << key << "\" \"" << keyvalues[key] << "\"\n";
506+
}
507+
508+
ent_data << "}\n";
509+
510+
return ent_data.str();
511+
}

src/bsp/Entity.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@ class Entity
4545
int getMemoryUsage(); // aproximate
4646

4747
bool isEverVisible();
48+
49+
string serialize();
4850
};
4951

src/editor/Command.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "Entity.h"
77
#include "util.h"
88
#include "globals.h"
9+
#include <sstream>
910

1011
#include "icons/aaatrigger.h"
1112

@@ -191,6 +192,109 @@ int CreateEntityCommand::memoryUsage() {
191192
return sizeof(CreateEntityCommand) + entData->getMemoryUsage();
192193
}
193194

195+
//
196+
// Create Entities From Text
197+
//
198+
CreateEntityFromTextCommand::CreateEntityFromTextCommand(string desc, int mapIdx, string textData) : Command(desc, mapIdx) {
199+
this->textData = textData;
200+
this->allowedDuringLoad = true;
201+
}
202+
203+
CreateEntityFromTextCommand::~CreateEntityFromTextCommand() {
204+
}
205+
206+
void CreateEntityFromTextCommand::execute() {
207+
Bsp* map = getBsp();
208+
209+
std::istringstream in(textData);
210+
211+
int lineNum = 0;
212+
int lastBracket = -1;
213+
Entity* ent = NULL;
214+
215+
vector<Entity*> ents;
216+
217+
string line = "";
218+
while (std::getline(in, line))
219+
{
220+
lineNum++;
221+
if (line.length() < 1 || line[0] == '\n')
222+
continue;
223+
224+
if (line[0] == '{')
225+
{
226+
if (lastBracket == 0)
227+
{
228+
logf("clipboard ent text data (line %d): Unexpected '{'\n", lineNum);
229+
continue;
230+
}
231+
lastBracket = 0;
232+
233+
if (ent != NULL)
234+
delete ent;
235+
ent = new Entity();
236+
}
237+
else if (line[0] == '}')
238+
{
239+
if (lastBracket == 1)
240+
logf("clipboard ent text data (line %d): Unexpected '}'\n", lineNum);
241+
lastBracket = 1;
242+
243+
if (ent == NULL)
244+
continue;
245+
246+
if (ent->keyvalues.count("classname"))
247+
ents.push_back(ent);
248+
else
249+
logf("Found unknown classname entity. Skip it.\n");
250+
ent = NULL;
251+
252+
// you can end/start an ent on the same line, you know
253+
if (line.find("{") != string::npos)
254+
{
255+
ent = new Entity();
256+
lastBracket = 0;
257+
}
258+
}
259+
else if (lastBracket == 0 && ent != NULL) // currently defining an entity
260+
{
261+
Keyvalue k(line);
262+
if (k.key.length() && k.value.length())
263+
ent->addKeyvalue(k);
264+
}
265+
}
266+
267+
for (Entity* ent : ents) {
268+
map->ents.push_back(ent);
269+
}
270+
createdEnts = ents.size();
271+
logf("Pasted %d entities from clipboard\n", createdEnts);
272+
273+
refresh();
274+
}
275+
276+
void CreateEntityFromTextCommand::undo() {
277+
Bsp* map = getBsp();
278+
279+
g_app->deselectObject();
280+
281+
for (int i = 0; i < createdEnts; i++) {
282+
delete map->ents[map->ents.size() - 1];
283+
map->ents.pop_back();
284+
}
285+
286+
refresh();
287+
}
288+
289+
void CreateEntityFromTextCommand::refresh() {
290+
BspRenderer* renderer = getBspRenderer();
291+
renderer->preRenderEnts();
292+
g_app->gui->refresh();
293+
}
294+
295+
int CreateEntityFromTextCommand::memoryUsage() {
296+
return sizeof(CreateEntityFromTextCommand) + textData.size();
297+
}
194298

195299
//
196300
// Duplicate BSP Model command

src/editor/Command.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ class CreateEntityCommand : public Command {
6666
};
6767

6868

69+
class CreateEntityFromTextCommand : public Command {
70+
public:
71+
string textData;
72+
int createdEnts;
73+
74+
CreateEntityFromTextCommand(string desc, int mapIdx, string textData);
75+
~CreateEntityFromTextCommand();
76+
77+
void execute();
78+
void undo();
79+
void refresh();
80+
int memoryUsage();
81+
};
82+
83+
6984
class DuplicateBspModelCommand : public Command {
7085
public:
7186
int oldModelIdx;

src/editor/Gui.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,15 @@ void Gui::drawMenuBar() {
888888
if (ImGui::MenuItem("Paste at original origin", 0, false, entSelected && app->copiedEnt != NULL)) {
889889
app->pasteEnt(true);
890890
}
891+
892+
const char* clipBoardText = ImGui::GetClipboardText();
893+
if (ImGui::MenuItem("Paste entities from clipboard", 0, false, clipBoardText && clipBoardText[0] == '{')) {
894+
app->pasteEntsFromText(clipBoardText);
895+
}
896+
tooltip(g, "Creates entities from text data. You can use this to transfer entities "
897+
"from one bspguy window to another, or paste from .ent file text. Copy any entity "
898+
"in the viewer then paste to a text editor to see the format of the text data.");
899+
891900
if (ImGui::MenuItem("Delete", "Del", false, nonWorldspawnEntSelected)) {
892901
app->deleteEnt();
893902
}

src/editor/Renderer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,6 +2721,8 @@ void Renderer::cutEnt() {
27212721
DeleteEntityCommand* deleteCommand = new DeleteEntityCommand("Cut Entity", pickInfo);
27222722
deleteCommand->execute();
27232723
pushUndoCommand(deleteCommand);
2724+
2725+
ImGui::SetClipboardText(copiedEnt->serialize().c_str());
27242726
}
27252727

27262728
void Renderer::copyEnt() {
@@ -2733,6 +2735,8 @@ void Renderer::copyEnt() {
27332735
Bsp* map = mapRenderers[pickInfo.mapIdx]->map;
27342736
copiedEnt = new Entity();
27352737
*copiedEnt = *map->ents[pickInfo.entIdx];
2738+
2739+
ImGui::SetClipboardText(copiedEnt->serialize().c_str());
27362740
}
27372741

27382742
void Renderer::pasteEnt(bool noModifyOrigin) {
@@ -2770,6 +2774,31 @@ void Renderer::pasteEnt(bool noModifyOrigin) {
27702774
selectEnt(map, map->ents.size() - 1);
27712775
}
27722776

2777+
void Renderer::pasteEntsFromText(string text) {
2778+
Bsp* map = pickInfo.map;
2779+
2780+
CreateEntityFromTextCommand* createCommand =
2781+
new CreateEntityFromTextCommand("Paste entities from clipboard", pickInfo.mapIdx, text);
2782+
createCommand->execute();
2783+
pushUndoCommand(createCommand);
2784+
2785+
if (createCommand->createdEnts == 1) {
2786+
Entity* createdEnt = map->ents[map->ents.size()-1];
2787+
vec3 oldOrigin = getEntOrigin(map, createdEnt);
2788+
vec3 modelOffset = getEntOffset(map, createdEnt);
2789+
vec3 mapOffset = mapRenderers[pickInfo.mapIdx]->mapOffset;
2790+
2791+
vec3 moveDist = (cameraOrigin + cameraForward * 100) - oldOrigin;
2792+
vec3 newOri = (oldOrigin + moveDist) - (modelOffset + mapOffset);
2793+
vec3 rounded = gridSnappingEnabled ? snapToGrid(newOri) : newOri;
2794+
createdEnt->setOrAddKeyvalue("origin", rounded.toKeyvalueString(!gridSnappingEnabled));
2795+
createCommand->refresh();
2796+
}
2797+
2798+
pickInfo.valid = true;
2799+
selectEnt(map, map->ents.size() - 1);
2800+
}
2801+
27732802
void Renderer::deleteEnt() {
27742803
if (!pickInfo.valid || pickInfo.entIdx <= 0)
27752804
return;

src/editor/Renderer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Renderer {
4949
friend class EditEntityCommand;
5050
friend class DeleteEntityCommand;
5151
friend class CreateEntityCommand;
52+
friend class CreateEntityFromTextCommand;
5253
friend class DuplicateBspModelCommand;
5354
friend class CreateBspModelCommand;
5455
friend class EditBspModelCommand;
@@ -256,6 +257,7 @@ class Renderer {
256257
void cutEnt();
257258
void copyEnt();
258259
void pasteEnt(bool noModifyOrigin);
260+
void pasteEntsFromText(string text);
259261
void deleteEnt();
260262
void scaleSelectedObject(float x, float y, float z);
261263
void scaleSelectedObject(vec3 dir, vec3 fromDir);

0 commit comments

Comments
 (0)