1 //------------------------------------------------------------------------
2 //  LEVEL CUT 'N' PASTE
3 //------------------------------------------------------------------------
4 //
5 //  Eureka DOOM Editor
6 //
7 //  Copyright (C) 2009-2019 Andrew Apted
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; either version 2
12 //  of the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20 
21 #include "main.h"
22 
23 #include <map>
24 
25 #include "e_basis.h"
26 #include "e_cutpaste.h"
27 #include "e_hover.h"
28 #include "e_linedef.h"
29 #include "e_main.h"
30 #include "e_sector.h"
31 #include "e_vertex.h"
32 #include "m_game.h"
33 #include "e_objects.h"
34 #include "r_grid.h"
35 #include "r_render.h"
36 #include "w_rawdef.h"
37 
38 #include "ui_window.h"
39 
40 
41 #define INVALID_SECTOR  (-999999)
42 
43 
44 class clipboard_data_c
45 {
46 public:
47 	// the mode used when the objects were Copied
48 	obj_type_e  mode;
49 
50 	// NOTE:
51 	//
52 	// The references here normally refer to other objects in here
53 	// (as though this was a little self-contained map).  For example,
54 	// the LineDef::right field is an index into sides[].
55 	//
56 	// The exception is sector references in the SideDefs.  These
57 	// values refer to the real map when >= 0, and refer to local
58 	// sectors when < 0 (negate and add 1).
59 	//
60 	// That's because we generally want a copy'n'pasted piece of the
61 	// map (linedefs or sectors) to "fit in" with nearby sections of
62 	// the map.
63 
64 	bool uses_real_sectors;
65 
66 	std::vector<Thing *>   things;
67 	std::vector<Vertex *>  verts;
68 	std::vector<Sector *>  sectors;
69 	std::vector<SideDef *> sides;
70 	std::vector<LineDef *> lines;
71 
72 public:
clipboard_data_c(obj_type_e _mode)73 	clipboard_data_c(obj_type_e _mode) :
74 		mode(_mode), uses_real_sectors(false),
75 		things(), verts(), sectors(),
76 		sides(), lines()
77 	{
78 		if (mode == OBJ_LINEDEFS || mode == OBJ_SECTORS)
79 			uses_real_sectors = true;
80 	}
81 
~clipboard_data_c()82 	~clipboard_data_c()
83 	{
84 		unsigned int i;
85 
86 		for (i = 0 ; i < things.size()   ; i++) delete things[i];
87 		for (i = 0 ; i < verts.size()    ; i++) delete verts[i];
88 		for (i = 0 ; i < sectors.size()  ; i++) delete sectors[i];
89 		for (i = 0 ; i < sides.size()    ; i++) delete sides[i];
90 		for (i = 0 ; i < lines.size()    ; i++) delete lines[i];
91 	}
92 
TotalSize() const93 	int TotalSize() const
94 	{
95 		size_t num = things.size() + verts.size() + sectors.size() + sides.size() + lines.size();
96 
97 		return (int)num;
98 	}
99 
Paste_BA_Message() const100 	void Paste_BA_Message() const
101 	{
102 		size_t t = things.size();
103 		size_t v = verts.size();
104 		size_t s = sectors.size();
105 		size_t l = lines.size();
106 
107 		if (s > 0)
108 		{
109 			const char *name = (s == 1) ? "sector" : "sectors";
110 			BA_Message("pasted %d %s", s, name);
111 		}
112 		else if (l > 0)
113 		{
114 			const char *name = (l == 1) ? "linedef" : "linedefs";
115 			BA_Message("pasted %d %s", l, name);
116 		}
117 		else if (t > 0)
118 		{
119 			const char *name = (t == 1) ? "thing" : "things";
120 			BA_Message("pasted %d %s", t, name);
121 		}
122 		else if (v > 0)
123 		{
124 			const char *name = (v == 1) ? "vertex" : "vertices";
125 			BA_Message("pasted %d %s", v, name);
126 		}
127 		else
128 		{
129 			BA_Message("pasted something");
130 		}
131 	}
132 
CentreOfThings(double * cx,double * cy)133 	void CentreOfThings(double *cx, double *cy)
134 	{
135 		*cx = *cy = 0;
136 
137 		if (things.empty())
138 			return;
139 
140 		double sum_x = 0;
141 		double sum_y = 0;
142 
143 		for (unsigned int i = 0 ; i < things.size() ; i++)
144 		{
145 			sum_x += things[i]->x();
146 			sum_y += things[i]->y();
147 		}
148 
149 		sum_x /= (double)things.size();
150 		sum_y /= (double)things.size();
151 
152 		*cx = sum_x;
153 		*cy = sum_y;
154 	}
155 
CentreOfVertices(double * cx,double * cy)156 	void CentreOfVertices(double *cx, double *cy)
157 	{
158 		*cx = *cy = 0;
159 
160 		if (verts.empty())
161 			return;
162 
163 		double sum_x = 0;
164 		double sum_y = 0;
165 
166 		for (unsigned int i = 0 ; i < verts.size() ; i++)
167 		{
168 			sum_x += verts[i]->x();
169 			sum_y += verts[i]->y();
170 		}
171 
172 		sum_x /= (double)verts.size();
173 		sum_y /= (double)verts.size();
174 
175 		*cx = sum_x;
176 		*cy = sum_y;
177 	}
178 
HasSectorRefs(int s1,int s2)179 	bool HasSectorRefs(int s1, int s2)
180 	{
181 		if (! uses_real_sectors)
182 			return false;
183 
184 		for (unsigned int i = 0 ; i < sides.size() ; i++)
185 		{
186 			if (s1 <= sides[i]->sector && sides[i]->sector <= s2)
187 				return true;
188 		}
189 
190 		return false;
191 	}
192 
InsertRealSector(int snum)193 	void InsertRealSector(int snum)
194 	{
195 		if (! uses_real_sectors)
196 			return;
197 
198 		for (unsigned int i = 0 ; i < sides.size() ; i++)
199 		{
200 			if (sides[i]->sector >= snum)
201 				sides[i]->sector++;
202 		}
203 	}
204 
DeleteRealSector(int snum)205 	void DeleteRealSector(int snum)
206 	{
207 		if (! uses_real_sectors)
208 			return;
209 
210 		for (unsigned int i = 0 ; i < sides.size() ; i++)
211 		{
212 			if (sides[i]->sector == snum)
213 				sides[i]->sector = INVALID_SECTOR;
214 			else if (sides[i]->sector > snum)
215 				sides[i]->sector--;
216 		}
217 	}
218 
RemoveSectorRefs()219 	void RemoveSectorRefs()
220 	{
221 		if (! uses_real_sectors)
222 			return;
223 
224 		for (unsigned int i = 0 ; i < sides.size() ; i++)
225 		{
226 			if (sides[i]->sector >= 0)
227 				sides[i]->sector = INVALID_SECTOR;
228 		}
229 	}
230 };
231 
232 
233 static clipboard_data_c * clip_board;
234 
235 static bool clip_doing_paste;
236 
237 
Clipboard_Clear()238 void Clipboard_Clear()
239 {
240 	if (clip_board)
241 	{
242 		delete clip_board;
243 		clip_board = NULL;
244 	}
245 }
246 
247 
248 //
249 // this remove sidedefs which refer to local sectors, allowing the
250 // clipboard geometry to persist when changing maps.
251 //
Clipboard_ClearLocals()252 void Clipboard_ClearLocals()
253 {
254 	if (clip_board)
255 		clip_board->RemoveSectorRefs();
256 }
257 
258 
Clipboard_HasStuff()259 static bool Clipboard_HasStuff()
260 {
261 	return clip_board ? true : false;
262 }
263 
264 
Clipboard_NotifyBegin()265 void Clipboard_NotifyBegin()
266 { }
267 
Clipboard_NotifyEnd()268 void Clipboard_NotifyEnd()
269 { }
270 
271 
Clipboard_NotifyInsert(obj_type_e type,int objnum)272 void Clipboard_NotifyInsert(obj_type_e type, int objnum)
273 {
274 	// this function notifies us that a new sector is about to be
275 	// inserted in the map (causing other sectors to be moved).
276 
277 	if (type != OBJ_SECTORS)
278 		return;
279 
280 	if (! clip_board)
281 		return;
282 
283 	// paste operations should only insert new sectors at the end
284 	if (objnum < NumSectors)
285 	{
286 		SYS_ASSERT(! clip_doing_paste);
287 	}
288 
289 #if 0  // OLD WAY
290 	if (clip_board->HasSectorRefs(objnum, NumSectors-1))
291 	{
292 		Clipboard_Clear();
293 	}
294 #else
295 	clip_board->InsertRealSector(objnum);
296 #endif
297 }
298 
299 
Clipboard_NotifyDelete(obj_type_e type,int objnum)300 void Clipboard_NotifyDelete(obj_type_e type, int objnum)
301 {
302 	// this function notifies us that a sector is about to be deleted
303 	// (causing other sectors to be moved).
304 
305 	if (type != OBJ_SECTORS)
306 		return;
307 
308 	if (! clip_board)
309 		return;
310 
311 	SYS_ASSERT(! clip_doing_paste);
312 
313 	clip_board->DeleteRealSector(objnum);
314 }
315 
316 
Clipboard_NotifyChange(obj_type_e type,int objnum,int field)317 void Clipboard_NotifyChange(obj_type_e type, int objnum, int field)
318 {
319 	// field changes never affect the clipboard
320 }
321 
322 
323 //----------------------------------------------------------------------
324 //  Texture Clipboard
325 //----------------------------------------------------------------------
326 
327 namespace tex_clipboard
328 {
329 	std::string tex;
330 	std::string flat;
331 
332 	int thing;
333 };
334 
335 
Texboard_Clear()336 void Texboard_Clear()
337 {
338 	tex_clipboard::tex.clear();
339 	tex_clipboard::flat.clear();
340 	tex_clipboard::thing = 0;
341 }
342 
Texboard_GetTexNum()343 int Texboard_GetTexNum()
344 {
345 	if (tex_clipboard::tex.empty())
346 		tex_clipboard::tex = default_wall_tex;
347 
348 	return BA_InternaliseString(tex_clipboard::tex.c_str());
349 }
350 
Texboard_GetFlatNum()351 int Texboard_GetFlatNum()
352 {
353 	if (tex_clipboard::flat.empty())
354 		tex_clipboard::flat = default_floor_tex;
355 
356 	return BA_InternaliseString(tex_clipboard::flat.c_str());
357 }
358 
Texboard_GetThing()359 int Texboard_GetThing()
360 {
361 	if (tex_clipboard::thing == 0)
362 		return default_thing;
363 
364 	return tex_clipboard::thing;
365 }
366 
Texboard_SetTex(const char * new_tex)367 void Texboard_SetTex(const char *new_tex)
368 {
369 	tex_clipboard::tex = new_tex;
370 
371 	if (Features.mix_textures_flats)
372 		tex_clipboard::flat = new_tex;
373 }
374 
Texboard_SetFlat(const char * new_flat)375 void Texboard_SetFlat(const char *new_flat)
376 {
377 	tex_clipboard::flat = new_flat;
378 
379 	if (Features.mix_textures_flats)
380 		tex_clipboard::tex = new_flat;
381 }
382 
Texboard_SetThing(int new_id)383 void Texboard_SetThing(int new_id)
384 {
385 	tex_clipboard::thing = new_id;
386 }
387 
388 
389 //------------------------------------------------------------------------
390 
CopyGroupOfObjects(selection_c * list)391 static void CopyGroupOfObjects(selection_c *list)
392 {
393 	// this is used for LineDefs and Sectors, where we need to copy
394 	// groups of objects and create internal references between them.
395 
396 	bool is_sectors = (list->what_type() == OBJ_SECTORS) ? true : false;
397 
398 	selection_c vert_sel(OBJ_VERTICES);
399 	selection_c side_sel(OBJ_SIDEDEFS);
400 	selection_c line_sel(OBJ_LINEDEFS);
401 
402 	ConvertSelection(list, &line_sel);
403 	ConvertSelection(&line_sel, &vert_sel);
404 
405 	// determine needed sidedefs
406 	for (sel_iter_c it(line_sel) ; !it.done() ; it.next())
407 	{
408 		LineDef *L = LineDefs[*it];
409 
410 		if (L->right >= 0) side_sel.set(L->right);
411 		if (L->left  >= 0) side_sel.set(L->left);
412 	}
413 
414 
415 	// these hold the mapping from real index --> clipboard index
416 	std::map<int, int> vert_map;
417 	std::map<int, int> sector_map;
418 	std::map<int, int> side_map;
419 
420 
421 	for (sel_iter_c it(vert_sel) ; !it.done() ; it.next())
422 	{
423 		vert_map[*it] = (int)clip_board->verts.size();
424 
425 		Vertex * SD = new Vertex;
426 		SD->RawCopy(Vertices[*it]);
427 		clip_board->verts.push_back(SD);
428 	}
429 
430 	if (is_sectors)
431 	{
432 		for (sel_iter_c it(list) ; !it.done() ; it.next())
433 		{
434 			sector_map[*it] = (int)clip_board->sectors.size();
435 
436 			Sector * S = new Sector;
437 			S->RawCopy(Sectors[*it]);
438 			clip_board->sectors.push_back(S);
439 		}
440 	}
441 
442 	for (sel_iter_c it(side_sel) ; !it.done() ; it.next())
443 	{
444 		side_map[*it] = (int)clip_board->sides.size();
445 
446 		SideDef * SD = new SideDef;
447 		SD->RawCopy(SideDefs[*it]);
448 		clip_board->sides.push_back(SD);
449 
450 		// adjust sector references, if needed
451 		if (is_sectors && list->get(SD->sector))
452 		{
453 			SYS_ASSERT(sector_map.find(SD->sector) != sector_map.end());
454 			SD->sector = -1 - sector_map[SD->sector];
455 		}
456 	}
457 
458 	for (sel_iter_c it(line_sel) ; !it.done() ; it.next())
459 	{
460 		LineDef * L = new LineDef;
461 		L->RawCopy(LineDefs[*it]);
462 		clip_board->lines.push_back(L);
463 
464 		// adjust vertex references
465 		SYS_ASSERT(vert_map.find(L->start) != vert_map.end());
466 		SYS_ASSERT(vert_map.find(L->end)   != vert_map.end());
467 
468 		L->start = vert_map[L->start];
469 		L->end   = vert_map[L->end  ];
470 
471 		// adjust sidedef references
472 		if (L->right >= 0)
473 		{
474 			SYS_ASSERT(side_map.find(L->right) != side_map.end());
475 			L->right = side_map[L->right];
476 		}
477 
478 		if (L->left >= 0)
479 		{
480 			SYS_ASSERT(side_map.find(L->left) != side_map.end());
481 			L->left = side_map[L->left];
482 		}
483 	}
484 
485 	// in sectors mode, copy things too
486 	if (is_sectors)
487 	{
488 		selection_c thing_sel(OBJ_THINGS);
489 
490 		ConvertSelection(list, &thing_sel);
491 
492 		for (sel_iter_c it(thing_sel) ; !it.done() ; it.next())
493 		{
494 			Thing * T = new Thing;
495 			T->RawCopy(Things[*it]);
496 			clip_board->things.push_back(T);
497 		}
498 	}
499 }
500 
501 
Clipboard_DoCopy()502 static bool Clipboard_DoCopy()
503 {
504 	soh_type_e unselect = Selection_Or_Highlight();
505 	if (unselect == SOH_Empty)
506 		return false;
507 
508 	// create storage for the copied objects
509 	if (clip_board)
510 		delete clip_board;
511 
512 	bool result = true;
513 
514 	clip_board = new clipboard_data_c(edit.mode);
515 
516 	switch (edit.mode)
517 	{
518 		case OBJ_THINGS:
519 			for (sel_iter_c it(edit.Selected) ; !it.done() ; it.next())
520 			{
521 				Thing * T = new Thing;
522 				T->RawCopy(Things[*it]);
523 				clip_board->things.push_back(T);
524 			}
525 			break;
526 
527 		case OBJ_VERTICES:
528 			for (sel_iter_c it(edit.Selected) ; !it.done() ; it.next())
529 			{
530 				Vertex * V = new Vertex;
531 				V->RawCopy(Vertices[*it]);
532 				clip_board->verts.push_back(V);
533 			}
534 			break;
535 
536 		case OBJ_LINEDEFS:
537 		case OBJ_SECTORS:
538 			CopyGroupOfObjects(edit.Selected);
539 			break;
540 
541 		default:
542 			result = false;
543 			break;
544 	}
545 
546 	int total = edit.Selected->count_obj();
547 	if (total == 1)
548 		Status_Set("copied %s #%d", NameForObjectType(edit.Selected->what_type()), edit.Selected->find_first());
549 	else
550 		Status_Set("copied %d %s", total, NameForObjectType(edit.Selected->what_type(), true /* plural */));
551 
552 	if (unselect == SOH_Unselect)
553 		Selection_Clear(true /* nosave */);
554 
555 	return result;
556 }
557 
558 
559 //------------------------------------------------------------------------
560 
PasteGroupOfObjects(double pos_x,double pos_y)561 static void PasteGroupOfObjects(double pos_x, double pos_y)
562 {
563 	double cx, cy;
564 	clip_board->CentreOfVertices(&cx, &cy);
565 
566 	// these hold the mapping from clipboard index --> real index
567 	std::map<int, int> vert_map;
568 	std::map<int, int> sector_map;
569 	std::map<int, int> side_map;
570 
571 	unsigned int i;
572 
573 	for (i = 0 ; i < clip_board->verts.size() ; i++)
574 	{
575 		int new_v = BA_New(OBJ_VERTICES);
576 		Vertex * V = Vertices[new_v];
577 
578 		vert_map[i] = new_v;
579 
580 		V->RawCopy(clip_board->verts[i]);
581 
582 		V->SetRawX(V->x() + pos_x - cx);
583 		V->SetRawY(V->y() + pos_y - cy);
584 	}
585 
586 	for (i = 0 ; i < clip_board->sectors.size() ; i++)
587 	{
588 		int new_s = BA_New(OBJ_SECTORS);
589 		Sector * S = Sectors[new_s];
590 
591 		sector_map[i] = new_s;
592 
593 		S->RawCopy(clip_board->sectors[i]);
594 	}
595 
596 	for (i = 0 ; i < clip_board->sides.size() ; i++)
597 	{
598 		// handle invalidated sectors (as if sidedef had been deleted)
599 		if (clip_board->sides[i]->sector == INVALID_SECTOR)
600 		{
601 			side_map[i] = -1;
602 			continue;
603 		}
604 
605 		int new_sd = BA_New(OBJ_SIDEDEFS);
606 		SideDef * SD = SideDefs[new_sd];
607 
608 		side_map[i] = new_sd;
609 
610 		SD->RawCopy(clip_board->sides[i]);
611 
612 		if (SD->sector < 0)
613 		{
614 			int local = -1 - SD->sector;
615 			SYS_ASSERT(sector_map.find(local) != sector_map.end());
616 			SD->sector = sector_map[local];
617 		}
618 	}
619 
620 	for (i = 0 ; i < clip_board->lines.size() ; i++)
621 	{
622 		int new_l = BA_New(OBJ_LINEDEFS);
623 		LineDef * L = LineDefs[new_l];
624 
625 		L->RawCopy(clip_board->lines[i]);
626 
627 		// adjust vertex references
628 		SYS_ASSERT(vert_map.find(L->start) != vert_map.end());
629 		SYS_ASSERT(vert_map.find(L->end)   != vert_map.end());
630 
631 		L->start = vert_map[L->start];
632 		L->end   = vert_map[L->end  ];
633 
634 		// adjust sidedef references
635 		if (L->Right())
636 		{
637 			SYS_ASSERT(side_map.find(L->right) != side_map.end());
638 			L->right = side_map[L->right];
639 		}
640 
641 		if (L->Left())
642 		{
643 			SYS_ASSERT(side_map.find(L->left) != side_map.end());
644 			L->left = side_map[L->left];
645 		}
646 
647 		// flip linedef if necessary
648 		if (L->Left() && ! L->Right())
649 		{
650 			FlipLineDef(new_l);
651 		}
652 
653 		// if the linedef lost a side, fix texturing
654 		if (L->OneSided() && is_null_tex(L->Right()->MidTex()))
655 			LD_FixForLostSide(new_l);
656 	}
657 
658 	for (i = 0 ; i < clip_board->things.size() ; i++)
659 	{
660 		int new_t = BA_New(OBJ_THINGS);
661 		Thing * T = Things[new_t];
662 
663 		T->RawCopy(clip_board->things[i]);
664 
665 		T->SetRawX(T->x() + pos_x - cx);
666 		T->SetRawY(T->y() + pos_y - cy);
667 	}
668 }
669 
670 
ReselectGroup()671 static void ReselectGroup()
672 {
673 	// this assumes all the new objects are at the end of their
674 	// array (currently true, but not a guarantee of BA_New).
675 
676 	if (edit.mode == OBJ_THINGS)
677 	{
678 		if (clip_board->mode == OBJ_THINGS ||
679 		    clip_board->mode == OBJ_SECTORS)
680 		{
681 			int count = (int)clip_board->things.size();
682 
683 			Selection_Clear();
684 
685 			edit.Selected->frob_range(NumThings - count, NumThings-1, BOP_ADD);
686 		}
687 		return;
688 	}
689 
690 	bool was_mappy = (clip_board->mode == OBJ_VERTICES ||
691 			    	  clip_board->mode == OBJ_LINEDEFS ||
692 					  clip_board->mode == OBJ_SECTORS);
693 
694 	bool is_mappy =  (edit.mode == OBJ_VERTICES ||
695 			    	  edit.mode == OBJ_LINEDEFS ||
696 					  edit.mode == OBJ_SECTORS);
697 
698 	if (! (was_mappy && is_mappy))
699 		return;
700 
701 	selection_c new_sel(clip_board->mode);
702 
703 	if (clip_board->mode == OBJ_VERTICES)
704 	{
705 		int count = (int)clip_board->verts.size();
706 		new_sel.frob_range(NumVertices - count, NumVertices-1, BOP_ADD);
707 	}
708 	else if (clip_board->mode == OBJ_LINEDEFS)
709 	{
710 		// Note: this doesn't behave as expected if the editing mode is
711 		// SECTORS, because the pasted lines do not completely surround
712 		// the sectors (non-pasted lines refer to them too).
713 
714 		int count = (int)clip_board->lines.size();
715 		new_sel.frob_range(NumLineDefs - count, NumLineDefs-1, BOP_ADD);
716 	}
717 	else
718 	{
719 		SYS_ASSERT(clip_board->mode == OBJ_SECTORS);
720 
721 		int count = (int)clip_board->sectors.size();
722 		new_sel.frob_range(NumSectors - count, NumSectors-1, BOP_ADD);
723 	}
724 
725 	Selection_Clear();
726 
727 	ConvertSelection(&new_sel, edit.Selected);
728 }
729 
730 
Clipboard_DoPaste()731 static bool Clipboard_DoPaste()
732 {
733 	bool reselect = true;  // CONFIG TODO
734 
735 	unsigned int i;
736 
737 	if (! Clipboard_HasStuff())
738 		return false;
739 
740 	// figure out where to put stuff
741 	double pos_x = edit.map_x;
742 	double pos_y = edit.map_y;
743 
744 	if (! edit.pointer_in_window)
745 	{
746 		pos_x = grid.orig_x;
747 		pos_y = grid.orig_y;
748 	}
749 
750 	// honor the grid snapping setting
751 	pos_x = grid.SnapX(pos_x);
752 	pos_y = grid.SnapY(pos_y);
753 
754 	BA_Begin();
755 	clip_board->Paste_BA_Message();
756 
757 	clip_doing_paste = true;
758 
759 	switch (clip_board->mode)
760 	{
761 		case OBJ_THINGS:
762 		{
763 			double cx, cy;
764 			clip_board->CentreOfThings(&cx, &cy);
765 
766 			for (unsigned int i = 0 ; i < clip_board->things.size() ; i++)
767 			{
768 				int new_t = BA_New(OBJ_THINGS);
769 				Thing * T = Things[new_t];
770 
771 				T->RawCopy(clip_board->things[i]);
772 
773 				T->SetRawX(T->x() + pos_x - cx);
774 				T->SetRawY(T->y() + pos_y - cy);
775 
776 				recent_things.insert_number(T->type);
777 			}
778 			break;
779 		}
780 
781 		case OBJ_VERTICES:
782 		{
783 			double cx, cy;
784 			clip_board->CentreOfVertices(&cx, &cy);
785 
786 			for (i = 0 ; i < clip_board->verts.size() ; i++)
787 			{
788 				int new_v = BA_New(OBJ_VERTICES);
789 				Vertex * V = Vertices[new_v];
790 
791 				V->RawCopy(clip_board->verts[i]);
792 
793 				V->SetRawX(V->x() + pos_x - cx);
794 				V->SetRawY(V->y() + pos_y - cy);
795 			}
796 			break;
797 		}
798 
799 		case OBJ_LINEDEFS:
800 		case OBJ_SECTORS:
801 		{
802 			PasteGroupOfObjects(pos_x, pos_y);
803 			break;
804 		}
805 
806 		default:
807 			break;
808 	}
809 
810 	clip_doing_paste = false;
811 
812 	BA_End();
813 
814 	edit.error_mode = false;
815 
816 	if (reselect)
817 		ReselectGroup();
818 
819 	return true;
820 }
821 
822 
823 //------------------------------------------------------------------------
824 
CMD_CopyAndPaste()825 void CMD_CopyAndPaste()
826 {
827 	if (edit.Selected->empty() && edit.highlight.is_nil())
828 	{
829 		Beep("Nothing to copy and paste");
830 		return;
831 	}
832 
833 	if (Clipboard_DoCopy())
834 	{
835 		Clipboard_DoPaste();
836 	}
837 }
838 
839 
CMD_Clipboard_Cut()840 void CMD_Clipboard_Cut()
841 {
842 	if (main_win->ClipboardOp('x'))
843 		return;
844 
845 	if (edit.render3d && edit.mode != OBJ_THINGS)
846 	{
847 		Render3D_CB_Cut();
848 		return;
849 	}
850 
851 	if (! Clipboard_DoCopy())
852 	{
853 		Beep("Nothing to cut");
854 		return;
855 	}
856 
857 	ExecuteCommand("Delete");
858 }
859 
860 
CMD_Clipboard_Copy()861 void CMD_Clipboard_Copy()
862 {
863 	if (main_win->ClipboardOp('c'))
864 		return;
865 
866 	if (edit.render3d && edit.mode != OBJ_THINGS)
867 	{
868 		Render3D_CB_Copy();
869 		return;
870 	}
871 
872 	if (! Clipboard_DoCopy())
873 	{
874 		Beep("Nothing to copy");
875 		return;
876 	}
877 }
878 
879 
CMD_Clipboard_Paste()880 void CMD_Clipboard_Paste()
881 {
882 	if (main_win->ClipboardOp('v'))
883 		return;
884 
885 	if (edit.render3d && edit.mode != OBJ_THINGS)
886 	{
887 		Render3D_CB_Paste();
888 		return;
889 	}
890 
891 	if (! Clipboard_DoPaste())
892 	{
893 		Beep("Clipboard is empty");
894 		return;
895 	}
896 }
897 
898 
899 //------------------------------------------------------------------------
900 
UnusedVertices(selection_c * lines,selection_c * result)901 void UnusedVertices(selection_c *lines, selection_c *result)
902 {
903 	SYS_ASSERT(lines->what_type() == OBJ_LINEDEFS);
904 
905 	ConvertSelection(lines, result);
906 
907 	for (int n = 0 ; n < NumLineDefs ; n++)
908 	{
909 		// we are interested in the lines we are NOT deleting
910 		if (lines->get(n))
911 			continue;
912 
913 		const LineDef *L = LineDefs[n];
914 
915 		result->clear(L->start);
916 		result->clear(L->end);
917 	}
918 }
919 
920 
UnusedSideDefs(selection_c * lines,selection_c * secs,selection_c * result)921 void UnusedSideDefs(selection_c *lines, selection_c *secs, selection_c *result)
922 {
923 	SYS_ASSERT(lines->what_type() == OBJ_LINEDEFS);
924 
925 	ConvertSelection(lines, result);
926 
927 	for (int n = 0 ; n < NumLineDefs ; n++)
928 	{
929 		// we are interested in the lines we are NOT deleting
930 		if (lines->get(n))
931 			continue;
932 
933 		const LineDef *L = LineDefs[n];
934 
935 		if (L->Right()) result->clear(L->right);
936 		if (L->Left())  result->clear(L->left);
937 	}
938 
939 	for (int i = 0 ; i < NumSideDefs ; i++)
940 	{
941 		const SideDef *SD = SideDefs[i];
942 
943 		if (secs && secs->get(SD->sector))
944 			result->set(i);
945 	}
946 }
947 
948 
UnusedLineDefs(selection_c * sectors,selection_c * result)949 void UnusedLineDefs(selection_c *sectors, selection_c *result)
950 {
951 	SYS_ASSERT(sectors->what_type() == OBJ_SECTORS);
952 
953 	for (int n = 0 ; n < NumLineDefs ; n++)
954 	{
955 		const LineDef *L = LineDefs[n];
956 
957 		// check if touches a to-be-deleted sector
958 		//    -1 : no side
959 		//     0 : deleted side
960 		//    +1 : kept side
961 		int right_m = (L->right < 0) ? -1 : sectors->get(L->Right()->sector) ? 0 : 1;
962 		int  left_m = (L->left  < 0) ? -1 : sectors->get(L->Left() ->sector) ? 0 : 1;
963 
964 		if (MAX(right_m, left_m) == 0)
965 		{
966 			result->set(n);
967 		}
968 	}
969 }
970 
971 
DuddedSectors(selection_c * verts,selection_c * lines,selection_c * result)972 void DuddedSectors(selection_c *verts, selection_c *lines, selection_c *result)
973 {
974 	SYS_ASSERT(verts->what_type() == OBJ_VERTICES);
975 	SYS_ASSERT(lines->what_type() == OBJ_LINEDEFS);
976 
977 	// collect all the sectors that touch a linedef being removed.
978 
979 	bitvec_c del_lines(NumLineDefs);
980 
981 	for (int n = 0 ; n < NumLineDefs ; n++)
982 	{
983 		const LineDef *L = LineDefs[n];
984 
985 		if (lines->get(n) || verts->get(L->start) || verts->get(L->end))
986 		{
987 			del_lines.set(n);
988 
989 			if (L->WhatSector(SIDE_LEFT ) >= 0) result->set(L->WhatSector(SIDE_LEFT ));
990 			if (L->WhatSector(SIDE_RIGHT) >= 0) result->set(L->WhatSector(SIDE_RIGHT));
991 		}
992 	}
993 
994 	// visit all linedefs NOT being removed, and see if the sector(s)
995 	// on it will actually be OK after the delete.
996 
997 	for (int n = 0 ; n < NumLineDefs ; n++)
998 	{
999 		const LineDef *L = LineDefs[n];
1000 
1001 		if (lines->get(n) || verts->get(L->start) || verts->get(L->end))
1002 			continue;
1003 
1004 		for (int pass = 0 ; pass < 2 ; pass++)
1005 		{
1006 			int what_side = pass ? SIDE_LEFT : SIDE_RIGHT;
1007 
1008 			int sec_num = L->WhatSector(what_side);
1009 
1010 			if (sec_num < 0)
1011 				continue;
1012 
1013 			// skip sectors that are not potentials for removal,
1014 			// and prevent the expensive tests below...
1015 			if (! result->get(sec_num))
1016 				continue;
1017 
1018 			// check if the linedef opposite faces this sector (BUT
1019 			// IGNORING any lines being deleted).  when found, we
1020 			// know that this sector should be kept.
1021 
1022 			int opp_side;
1023 			int opp_ld = OppositeLineDef(n, what_side, &opp_side, &del_lines);
1024 
1025 			if (opp_ld < 0)
1026 				continue;
1027 
1028 			const LineDef *L2 = LineDefs[opp_ld];
1029 
1030 			if (L2->WhatSector(opp_side) == sec_num)
1031 				result->clear(sec_num);
1032 		}
1033 	}
1034 }
1035 
1036 
FixupLineDefs(selection_c * lines,selection_c * sectors)1037 static void FixupLineDefs(selection_c *lines, selection_c *sectors)
1038 {
1039 	for (sel_iter_c it(lines) ; !it.done() ; it.next())
1040 	{
1041 		const LineDef *L = LineDefs[*it];
1042 
1043 		// the logic is ugly here mainly to handle flipping (in particular,
1044 		// not to flip the line when _both_ sides are unlinked).
1045 
1046 		bool do_right = L->Right() ? sectors->get(L->Right()->sector) : false;
1047 		bool do_left  = L->Left()  ? sectors->get(L->Left() ->sector) : false;
1048 
1049 		// line shouldn't be in list unless it touches the sector
1050 		SYS_ASSERT(do_right || do_left);
1051 
1052 		if (do_right && do_left)
1053 		{
1054 			LD_RemoveSideDef(*it, SIDE_RIGHT);
1055 			LD_RemoveSideDef(*it, SIDE_LEFT);
1056 		}
1057 		else if (do_right)
1058 		{
1059 			LD_RemoveSideDef(*it, SIDE_RIGHT);
1060 			FlipLineDef(*it);
1061 		}
1062 		else // do_left
1063 		{
1064 			LD_RemoveSideDef(*it, SIDE_LEFT);
1065 		}
1066 	}
1067 }
1068 
1069 
DeleteVertex_MergeLineDefs(int v_num)1070 static bool DeleteVertex_MergeLineDefs(int v_num)
1071 {
1072 	// returns true if OK, false if would create an overlapping linedef
1073 	// [ meaning the vertex should be deleted normally ]
1074 
1075 	// find the linedefs
1076 	int ld1 = -1;
1077 	int ld2 = -1;
1078 
1079 	for (int n = 0 ; n < NumLineDefs ; n++)
1080 	{
1081 		const LineDef *L = LineDefs[n];
1082 
1083 		if (L->start == v_num || L->end == v_num)
1084 		{
1085 			SYS_ASSERT(ld2 < 0);
1086 
1087 			if (ld1 < 0)
1088 				ld1 = n;
1089 			else
1090 				ld2 = n;
1091 		}
1092 	}
1093 
1094 	SYS_ASSERT(ld1 >= 0);
1095 	SYS_ASSERT(ld2 >= 0);
1096 
1097 	LineDef *L1 = LineDefs[ld1];
1098 	LineDef *L2 = LineDefs[ld2];
1099 
1100 	// we merge L2 into L1, unless L1 is significantly shorter
1101 	if (L1->CalcLength() < L2->CalcLength() * 0.7)
1102 	{
1103 		std::swap(ld1, ld2);
1104 		std::swap(L1,  L2);
1105 	}
1106 
1107 	// determine the remaining vertices
1108 	int v1 = (L1->start == v_num) ? L1->end : L1->start;
1109 	int v2 = (L2->start == v_num) ? L2->end : L2->start;
1110 
1111 	// ensure we don't create two directly overlapping linedefs
1112 	if (LineDefAlreadyExists(v1, v2))
1113 		return false;
1114 
1115 	// see what sidedefs would become unused
1116 	selection_c line_sel(OBJ_LINEDEFS);
1117 	selection_c side_sel(OBJ_SIDEDEFS);
1118 
1119 	line_sel.set(ld2);
1120 
1121 	UnusedSideDefs(&line_sel, NULL /* sec_sel */, &side_sel);
1122 
1123 
1124 	BA_Begin();
1125 	BA_Message("deleted vertex #%d\n", v_num);
1126 
1127 	if (L1->start == v_num)
1128 		BA_ChangeLD(ld1, LineDef::F_START, v2);
1129 	else
1130 		BA_ChangeLD(ld1, LineDef::F_END, v2);
1131 
1132 	// NOT-DO: update X offsets on existing linedef
1133 
1134 	BA_Delete(OBJ_LINEDEFS, ld2);
1135 	BA_Delete(OBJ_VERTICES, v_num);
1136 
1137 	DeleteObjects(&side_sel);
1138 
1139 	BA_End();
1140 
1141 	return true;
1142 }
1143 
1144 
DeleteObjects_WithUnused(selection_c * list,bool keep_things,bool keep_verts,bool keep_lines)1145 void DeleteObjects_WithUnused(selection_c *list, bool keep_things,
1146 							  bool keep_verts, bool keep_lines)
1147 {
1148 	selection_c vert_sel(OBJ_VERTICES);
1149 	selection_c side_sel(OBJ_SIDEDEFS);
1150 	selection_c line_sel(OBJ_LINEDEFS);
1151 	selection_c  sec_sel(OBJ_SECTORS);
1152 
1153 	switch (list->what_type())
1154 	{
1155 		case OBJ_VERTICES:
1156 			vert_sel.merge(*list);
1157 			break;
1158 
1159 		case OBJ_LINEDEFS:
1160 			line_sel.merge(*list);
1161 			break;
1162 
1163 		case OBJ_SECTORS:
1164 			sec_sel.merge(*list);
1165 			break;
1166 
1167 		default: /* OBJ_THINGS or OBJ_SIDEDEFS */
1168 			DeleteObjects(list);
1169 			return;
1170 	}
1171 
1172 	if (list->what_type() == OBJ_VERTICES)
1173 	{
1174 		for (int n = 0 ; n < NumLineDefs ; n++)
1175 		{
1176 			const LineDef *L = LineDefs[n];
1177 
1178 			if (list->get(L->start) || list->get(L->end))
1179 				line_sel.set(n);
1180 		}
1181 	}
1182 
1183 	if (!keep_lines && list->what_type() == OBJ_SECTORS)
1184 	{
1185 		UnusedLineDefs(&sec_sel, &line_sel);
1186 
1187 		if (line_sel.notempty())
1188 		{
1189 			UnusedSideDefs(&line_sel, &sec_sel, &side_sel);
1190 
1191 			if (!keep_verts)
1192 				UnusedVertices(&line_sel, &vert_sel);
1193 		}
1194 	}
1195 
1196 	if (!keep_verts && list->what_type() == OBJ_LINEDEFS)
1197 	{
1198 		UnusedVertices(&line_sel, &vert_sel);
1199 	}
1200 
1201 	// try to detect sectors that become "dudded", where all the
1202 	// remaining linedefs of the sector face into the void.
1203 	if (list->what_type() == OBJ_VERTICES || list->what_type() == OBJ_LINEDEFS)
1204 	{
1205 		DuddedSectors(&vert_sel, &line_sel, &sec_sel);
1206 		UnusedSideDefs(&line_sel, &sec_sel, &side_sel);
1207 	}
1208 
1209 	// delete things from each deleted sector
1210 	if (!keep_things && sec_sel.notempty())
1211 	{
1212 		selection_c thing_sel(OBJ_THINGS);
1213 
1214 		ConvertSelection(&sec_sel, &thing_sel);
1215 		DeleteObjects(&thing_sel);
1216 	}
1217 
1218 	// perform linedef fixups here (when sectors get removed)
1219 	if (sec_sel.notempty())
1220 	{
1221 		selection_c fixups(OBJ_LINEDEFS);
1222 
1223 		ConvertSelection(&sec_sel, &fixups);
1224 
1225 		// skip lines which will get deleted
1226 		fixups.unmerge(line_sel);
1227 
1228 		FixupLineDefs(&fixups, &sec_sel);
1229 	}
1230 
1231 	// actually delete stuff, in the correct order
1232 	DeleteObjects(&line_sel);
1233 	DeleteObjects(&side_sel);
1234 	DeleteObjects(&vert_sel);
1235 	DeleteObjects( &sec_sel);
1236 }
1237 
1238 
CMD_Delete()1239 void CMD_Delete()
1240 {
1241 	if (main_win->ClipboardOp('d'))
1242 		return;
1243 
1244 	soh_type_e unselect = Selection_Or_Highlight();
1245 	if (unselect == SOH_Empty)
1246 	{
1247 		Beep("Nothing to delete");
1248 		return;
1249 	}
1250 
1251 	bool keep = Exec_HasFlag("/keep");
1252 
1253 	// special case for a single vertex connected to two linedefs,
1254 	// we delete the vertex but merge the two linedefs.
1255 	if (edit.mode == OBJ_VERTICES && edit.Selected->count_obj() == 1)
1256 	{
1257 		int v_num = edit.Selected->find_first();
1258 		SYS_ASSERT(v_num >= 0);
1259 
1260 		if (Vertex_HowManyLineDefs(v_num) == 2)
1261 		{
1262 			if (DeleteVertex_MergeLineDefs(v_num))
1263 				goto success;
1264 		}
1265 
1266 		// delete vertex normally
1267 	}
1268 
1269 	BA_Begin();
1270 	BA_MessageForSel("deleted", edit.Selected);
1271 
1272 	DeleteObjects_WithUnused(edit.Selected, keep, false /* keep_verts */, keep);
1273 
1274 	BA_End();
1275 
1276 success:
1277 	Editor_ClearAction();
1278 
1279 	// always clear the selection (deleting objects invalidates it)
1280 	Selection_Clear();
1281 
1282 	edit.highlight.clear();
1283 	edit.split_line.clear();
1284 
1285 	RedrawMap();
1286 }
1287 
1288 
1289 //--- editor settings ---
1290 // vi:ts=4:sw=4:noexpandtab
1291