]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/select.cpp
merge branch work back into trunk
[xonotic/netradiant.git] / radiant / select.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 // select.c
23 #include "stdafx.h"
24 #include <assert.h>
25 #include "filters.h"
26
27 // externs
28 CPtrArray g_SelectedFaces;
29 CPtrArray g_SelectedFaceBrushes;
30 CPtrArray& g_ptrSelectedFaces = g_SelectedFaces;
31 CPtrArray& g_ptrSelectedFaceBrushes = g_SelectedFaceBrushes;
32
33 /*
34 ===========
35 Test_Ray
36 ===========
37 */
38 #define DIST_START      999999
39 trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags)
40 {
41         brush_t *brush;
42         face_t  *face;
43         float   dist;
44         trace_t t;
45
46         memset (&t, 0, sizeof(t));
47         t.dist = DIST_START;
48
49         if (flags & SF_CYCLE)
50         {
51                 CPtrArray array;
52                 brush_t *pToSelect = (selected_brushes.next != &selected_brushes) ? selected_brushes.next : NULL;
53                 Select_Deselect();
54
55                 // go through active brushes and accumulate all "hit" brushes
56                 for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
57                 {
58                         //if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity)
59                         //  continue;
60
61                         if (brush->bFiltered)
62                                 continue;
63
64                         if (!g_PrefsDlg.m_bSelectCurves && brush->patchBrush)
65                                 continue;
66
67                         if (!g_PrefsDlg.m_bSelectModels && (brush->owner->eclass->nShowFlags & ECLASS_MISCMODEL))
68                                 continue;
69
70                         //if (!g_bShowPatchBounds && brush->patchBrush)
71                         //  continue;
72
73                         face = Brush_Ray (origin, dir, brush, &dist, flags);
74
75                         if (face)
76                                 array.Add(brush);
77                 }
78
79                 int nSize = array.GetSize();
80                 if (nSize > 0)
81                 {
82                         bool bFound = false;
83                         for (int i = 0; i < nSize; i++)
84                         {
85                                 brush_t *b = reinterpret_cast<brush_t*>(array.GetAt(i));
86                                 // did we hit the last one selected yet ?
87                                 if (b == pToSelect)
88                                 {
89                                         // yes we want to select the next one in the list
90                                         int n = (i > 0) ? i-1 : nSize-1;
91                                         pToSelect = reinterpret_cast<brush_t*>(array.GetAt(n));
92                                         bFound = true;
93                                         break;
94                                 }
95                         }
96                         if (!bFound)
97                                 pToSelect = reinterpret_cast<brush_t*>(array.GetAt(0));
98                 }
99                 if (pToSelect)
100                 {
101                         face = Brush_Ray (origin, dir, pToSelect, &dist, flags);
102                         t.dist = dist;
103                         t.brush = pToSelect;
104                         t.face = face;
105                         t.selected = false;
106                         return t;
107                 }
108         }
109
110         if (! (flags & SF_SELECTED_ONLY) )
111   {
112                 for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
113                 {
114       if ( (flags & SF_ENTITIES_FIRST) && (brush->owner == world_entity || !brush->owner->eclass->fixedsize))
115                                 continue;
116
117                         if (brush->bFiltered)
118                                 continue;
119
120       if (!g_PrefsDlg.m_bSelectCurves && brush->patchBrush)
121         continue;
122
123                         if (!g_PrefsDlg.m_bSelectModels && (brush->owner->eclass->nShowFlags & ECLASS_MISCMODEL))
124                                 continue;
125
126       //if (!g_bShowPatchBounds && brush->patchBrush)
127       //  continue;
128
129                         face = Brush_Ray (origin, dir, brush, &dist, flags);
130                         if (face && dist > 0 && dist < t.dist)
131                         {
132                                 t.dist = dist;
133                                 t.brush = brush;
134                                 t.face = face;
135                                 t.selected = false;
136                         }
137                 }
138   }
139
140
141         for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
142         {
143                 if ( (flags & SF_ENTITIES_FIRST) && (brush->owner == world_entity || !brush->owner->eclass->fixedsize))
144                         continue;
145
146                 if (brush->bFiltered)
147                         continue;
148
149     if (!g_PrefsDlg.m_bSelectCurves && brush->patchBrush)
150       continue;
151
152           if (!g_PrefsDlg.m_bSelectModels && (brush->owner->eclass->nShowFlags & ECLASS_MISCMODEL))
153                         continue;
154
155                 face = Brush_Ray (origin, dir, brush, &dist, flags);
156                 if (dist > 0 && dist < t.dist)
157                 {
158                         t.dist = dist;
159                         t.brush = brush;
160                         t.face = face;
161                         t.selected = true;
162                 }
163         }
164
165         // if entites first, but didn't find any, check regular
166
167         if ( (flags & SF_ENTITIES_FIRST) && t.brush == NULL)
168                 return Test_Ray (origin, dir, flags & ~SF_ENTITIES_FIRST);
169
170         return t;
171
172 }
173
174
175 /*
176 ============
177 Select_Brush
178
179 ============
180 */
181 void Select_Brush (brush_t *brush, bool bComplete, bool bStatus)
182 {
183         brush_t *b;
184         entity_t        *e;
185
186   g_ptrSelectedFaces.RemoveAll();
187   g_ptrSelectedFaceBrushes.RemoveAll();
188         if (g_qeglobals.d_select_count < 2)
189                 g_qeglobals.d_select_order[g_qeglobals.d_select_count] = brush;
190         g_qeglobals.d_select_count++;
191
192         e = brush->owner;
193         if (e)
194         {
195                 // select complete entity on first click
196                 if (e != world_entity && bComplete == true)
197                 {
198                         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
199                                 if (b->owner == e)
200                                         goto singleselect;
201                         for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
202                         {
203                                 if( b == brush ) // make sure we add the actual selected brush last, mainly for cycle select
204                                         continue;
205                                 Brush_RemoveFromList (b);
206                                 Brush_AddToList (b, &selected_brushes);
207                         }
208                         Brush_RemoveFromList (brush);
209                         Brush_AddToList (brush, &selected_brushes);
210                 }
211                 else
212                 {
213                 singleselect:
214                         Brush_RemoveFromList (brush);
215                         Brush_AddToList (brush, &selected_brushes);
216                         UpdatePatchInspector();
217                 }
218
219                 if (e->eclass)
220                 {
221                         UpdateEntitySel(brush->owner->eclass);
222                 }
223
224         UpdateSurfaceDialog();
225         }
226
227   if (bStatus)
228   {
229     vec3_t vMin, vMax, vSize;
230           Select_GetBounds (vMin, vMax);
231     VectorSubtract(vMax, vMin, vSize);
232     CString strStatus;
233     strStatus.Format("Selection X:: %.1f  Y:: %.1f  Z:: %.1f", vSize[0], vSize[1], vSize[2]);
234     g_pParentWnd->SetStatusText(2, strStatus);
235   }
236 }
237
238 /*
239 =============
240 Select_FaceInSelectedBrushes
241 =============
242 */
243 bool Select_FaceInSelectedBrushes( face_t *face )
244 {
245   brush_t *brush;
246   face_t  *tface;
247
248   for(brush = selected_brushes.next; brush != &selected_brushes; brush = brush->next)
249   {
250     for(tface = brush->brush_faces; tface; tface = tface->next)
251     {
252       if(tface == face)
253       {
254         return true;
255       }
256     }
257   }
258
259         return false;
260 }
261
262 /*
263 ============
264 Select_Ray
265
266 If the origin is inside a brush, that brush will be ignored.
267 ============
268 */
269 void Select_Ray (vec3_t origin, vec3_t dir, int flags)
270 {
271         trace_t t;
272         face_t  *tface;
273   bool    bOk;
274   static  trace_t lastTrace = {
275     NULL,       //      brush
276     NULL,       //      face
277     0,          //      dist
278     false       //      selected
279   };
280
281         t = Test_Ray (origin, dir, flags);
282         if (!t.brush)
283                 return;
284
285   if (flags & SF_SINGLEFACE)
286   {
287     if( flags & SF_DRAG )
288     {
289       if ( t.brush == lastTrace.brush && t.face == lastTrace.face )
290         return;
291                 }
292     lastTrace = t;
293
294     if(Select_FaceInSelectedBrushes(t.face))
295     {
296       // Deselect the brush
297       Brush_RemoveFromList (t.brush);
298       Brush_AddToList (t.brush, &active_brushes);
299       UpdatePatchInspector();
300
301       // Select all of the brush's faces except the one we are pointing at
302       for( tface = t.brush->brush_faces; tface; tface = tface->next )
303       {
304         if( tface == t.face )
305           continue;
306
307         bOk = true;
308         // NOTE: keep the size check in the loop, we remove stuff inside
309         for (int i = 0; i < g_SelectedFaces.GetSize(); i++)
310         {
311           if (tface == reinterpret_cast<face_t*>(g_SelectedFaces.GetAt(i)))
312                                                 bOk = false;
313         }
314
315         if(bOk)
316         {
317           g_SelectedFaces.Add(tface);
318           g_SelectedFaceBrushes.Add(t.brush);
319         }
320       }
321       g_qeglobals.d_select_mode = sel_facets_off;
322     }
323     else
324     {
325       bOk = true;
326       // NOTE: keep the size check in the loop, we remove stuff inside
327       for (int i = 0; i < g_SelectedFaces.GetSize(); i++)
328       {
329         if (t.face == reinterpret_cast<face_t*>(g_SelectedFaces.GetAt(i)))
330         {
331           bOk = false;
332           if( flags & SF_DRAG_ON )
333             continue;
334
335           g_qeglobals.d_select_mode = sel_facets_off;
336           // need to remove i'th entry
337           g_SelectedFaces.RemoveAt(i, 1);
338           g_SelectedFaceBrushes.RemoveAt(i, 1);
339         }
340       }
341
342       if (bOk && !(flags & SF_DRAG_OFF))
343       {
344         g_SelectedFaces.Add(t.face);
345         g_SelectedFaceBrushes.Add(t.brush);
346         g_qeglobals.d_select_mode = sel_facets_on;
347       }
348     }
349     UpdateSurfaceDialog();
350     Sys_UpdateWindows (W_ALL);
351     //g_qeglobals.d_select_mode = sel_brush;
352     // Texture_SetTexture requires a brushprimit_texdef fitted to the default width=2 height=2 texture
353     brushprimit_texdef_t brushprimit_texdef;
354     ConvertTexMatWithQTexture ( &t.face->brushprimit_texdef, t.face->d_texture, &brushprimit_texdef, NULL );
355     Texture_SetTexture ( &t.face->texdef, &brushprimit_texdef, false, NULL, false );
356     return;
357   }
358
359         // move the brush to the other list
360   if (t.selected)
361   {
362     if( flags & SF_DRAG_ON )
363       return;
364
365     g_qeglobals.d_select_mode = sel_brush_off;
366     Brush_RemoveFromList (t.brush);
367     Brush_AddToList (t.brush, &active_brushes);
368
369     UpdatePatchInspector();
370   }
371   else
372   {
373     if( flags & SF_DRAG_OFF )
374       return;
375
376     g_qeglobals.d_select_mode = sel_brush_on;
377     Select_Brush (t.brush, g_PrefsDlg.m_nCamDragMultiSelect == 1 ? Sys_AltDown () : !Sys_AltDown ());
378   }
379   UpdateSurfaceDialog();
380   Sys_UpdateWindows (W_ALL);
381 }
382
383
384 void Select_Delete (void)
385 {
386   brush_t *brush;
387   entity_t *e;
388
389   g_ptrSelectedFaces.RemoveAll();
390   g_ptrSelectedFaceBrushes.RemoveAll();
391
392   g_qeglobals.d_select_mode = sel_brush;
393
394   g_qeglobals.d_select_count = 0;
395   g_qeglobals.d_num_move_points = 0;
396   while (selected_brushes.next != &selected_brushes)
397   {
398     brush = selected_brushes.next;
399     if (brush->patchBrush)
400     {
401       Patch_Delete(brush->pPatch);
402     }
403     e = brush->owner;
404     Brush_Free (brush);
405     // remove if no brushes
406     if (e != world_entity && e->brushes.onext == &e->brushes)
407       Entity_Free(e);
408   }
409
410   Sys_MarkMapModified ();
411   UpdateSurfaceDialog();
412   Sys_UpdateWindows (W_ALL);
413 }
414
415 // update the workzone to a given brush
416 void UpdateWorkzone_ForBrush( brush_t* b )
417 {
418   VectorCopy( b->mins, g_qeglobals.d_work_min );
419   VectorCopy( b->maxs, g_qeglobals.d_work_max );
420   //++timo clean
421 #if 0
422         // will update the workzone to the given brush
423         // g_pParentWnd->ActiveXY()->GetViewType()
424         // cf VIEWTYPE defintion: enum VIEWTYPE {YZ, XZ, XY};
425         // we fit our work zone to the last brush on the list (b)
426         int nViewType = g_pParentWnd->ActiveXY()->GetViewType();
427   int nDim1 = (nViewType == YZ) ? 1 : 0;
428   int nDim2 = (nViewType == XY) ? 1 : 2;
429         g_qeglobals.d_work_min[nDim1] = b->mins[nDim1];
430         g_qeglobals.d_work_max[nDim1] = b->maxs[nDim1];
431         g_qeglobals.d_work_min[nDim2] = b->mins[nDim2];
432         g_qeglobals.d_work_max[nDim2] = b->maxs[nDim2];
433 #endif
434 }
435
436 // here to filter new brushes once unselected
437 extern void PerformFiltering();
438
439 void Select_Deselect (bool bDeselectFaces)
440 {
441         brush_t *b;
442
443   Patch_Deselect();
444
445   g_pParentWnd->ActiveXY()->UndoClear();
446
447   g_qeglobals.d_workcount++;
448         g_qeglobals.d_select_count = 0;
449         g_qeglobals.d_num_move_points = 0;
450         b = selected_brushes.next;
451
452         if (b == &selected_brushes)
453         {
454                 if (bDeselectFaces)
455                 {
456                         g_ptrSelectedFaces.RemoveAll();
457       g_ptrSelectedFaceBrushes.RemoveAll();
458                 }
459     PerformFiltering();
460                 UpdateSurfaceDialog();
461                 Sys_UpdateWindows (W_ALL);
462                 return;
463         }
464
465   if (bDeselectFaces)
466   {
467         g_ptrSelectedFaces.RemoveAll();
468     g_ptrSelectedFaceBrushes.RemoveAll();
469   }
470
471         g_qeglobals.d_select_mode = sel_brush;
472
473         UpdateWorkzone_ForBrush(b);
474
475         selected_brushes.next->prev = &active_brushes;
476         selected_brushes.prev->next = active_brushes.next;
477         active_brushes.next->prev = selected_brushes.prev;
478         active_brushes.next = selected_brushes.next;
479         selected_brushes.prev = selected_brushes.next = &selected_brushes;
480
481   // filter newly created stuff once it's unselected
482   PerformFiltering();
483         UpdateSurfaceDialog();
484         Sys_UpdateWindows (W_ALL);
485 }
486
487 /*
488 ============
489 Select_Move
490 ============
491 */
492 /*! Moves the currently selected brush/patch
493     \param delta How far to move the selection (x,y,z)
494     \param bSnap If the move should snap to grid points
495 */
496 void Select_Move (vec3_t delta, bool bSnap)
497 {
498         brush_t *b;
499
500   // actually move the selected brushes
501   for (b = selected_brushes.next ; b != &selected_brushes ; b=b->next)
502     Brush_Move (b, delta, bSnap);
503
504   vec3_t vMin, vMax;
505         Select_GetBounds (vMin, vMax);
506   CString strStatus;
507   strStatus.Format("Origin X:: %.1f  Y:: %.1f  Z:: %.1f", vMin[0], vMax[1], vMax[2]);
508   g_pParentWnd->SetStatusText(2, strStatus);
509
510   //Sys_UpdateWindows (W_ALL);
511 }
512
513 /*
514 =================
515 Select_NudgeVerts
516 =================
517 */
518 /*! Moves the currently selected brush/patch vertices
519     \param delta How far to move the vertices (x,y,z)
520     \param bSnap If the move should snap to grid points
521 */
522 void Select_NudgePoint(vec3_t delta, qboolean bSnap)
523 {
524   if (g_qeglobals.d_select_mode == sel_vertex)
525   {
526     // move selected verts
527     brush_t *b;
528     vec3_t end;
529     qboolean success = true;
530     for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
531     {
532       success &= (qboolean)Brush_MoveVertex(b, g_qeglobals.d_move_points[0], delta, end, bSnap);
533     }
534     if (success)
535       VectorCopy(end, g_qeglobals.d_move_points[0]);
536   }
537   else if (g_qeglobals.d_select_mode == sel_curvepoint)
538   {
539     // move selected patch control points
540     Patch_UpdateSelected(delta);
541   }
542 }
543
544 /*
545 ============
546 Select_Clone
547
548 Creates an exact duplicate of the selection in place, then moves
549 the selected brushes off of their old positions
550 ============
551 */
552 void Select_Clone (void)
553 {
554   g_bScreenUpdates = false;
555   g_pParentWnd->Copy();
556   Select_Deselect();
557   g_pParentWnd->Paste();
558   g_pParentWnd->NudgeSelection(2, g_qeglobals.d_gridsize);
559   g_pParentWnd->NudgeSelection(3, g_qeglobals.d_gridsize);
560   Undo_Start("clone");
561   Undo_EndBrushList(&selected_brushes);
562   Undo_End();
563   g_bScreenUpdates = true;
564   Sys_UpdateWindows(W_ALL);
565 }
566
567 //++timo clean
568 #if 0
569 /*
570 ============
571 Select_SetTexture
572 Timo : bFitScale to compute scale on the plane and counteract plane / axial plane snapping
573 Timo :  brush primitive texturing
574                 the brushprimit_texdef given must be understood as a qtexture_t width=2 height=2 ( HiRes )
575 Timo :  texture plugin, added an IPluginTexdef* parameter
576                 must be casted to an IPluginTexdef!
577                 if not NULL, get ->Copy() of it into each face or brush ( and remember to hook )
578                 if NULL, means we have no information, ask for a default
579 TTimo - shader code cleanup
580   added IShader* parameter
581 ============
582 */
583 void WINAPI Select_SetTexture2 (IShader* pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, void* pPlugTexdef )
584 {
585         brush_t *b;
586         int nCount = g_ptrSelectedFaces.GetSize();
587         if (nCount > 0)
588         {
589                 Undo_Start("set face textures");
590                 ASSERT(g_ptrSelectedFaces.GetSize() == g_ptrSelectedFaceBrushes.GetSize());
591                 for (int i = 0; i < nCount; i++)
592                 {
593                         face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
594                         brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
595                         Undo_AddBrush(selBrush);
596                         //++timo TODO: propagate the IShader* ..
597                         SetFaceTexdef (selFace, texdef, brushprimit_texdef, bFitScale, static_cast<IPluginTexdef *>(pPlugTexdef) );
598                         Brush_Build(selBrush, bFitScale);
599                         Undo_EndBrush(selBrush);
600                 }
601                 Undo_End();
602         }
603         else if (selected_brushes.next != &selected_brushes)
604         {
605                 Undo_Start("set brush textures");
606                 for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
607                         if (!b->owner->eclass->fixedsize)
608                         {
609                                 Undo_AddBrush(b);
610                                 Brush_SetTexture2 (b, pShader, texdef, brushprimit_texdef, bFitScale, static_cast<IPluginTexdef *>(pPlugTexdef) );
611                                 Undo_EndBrush(b);
612                         }
613                 Undo_End();
614         }
615         Sys_UpdateWindows (W_ALL);
616 }
617 #endif
618
619 /*
620 ============
621 Select_SetTexture
622 Timo : bFitScale to compute scale on the plane and counteract plane / axial plane snapping
623 Timo :  brush primitive texturing
624                 the brushprimit_texdef given must be understood as a qtexture_t width=2 height=2 ( HiRes )
625 Timo :  texture plugin, added an IPluginTexdef* parameter
626                 must be casted to an IPluginTexdef!
627                 if not NULL, get ->Copy() of it into each face or brush ( and remember to hook )
628                 if NULL, means we have no information, ask for a default
629 ============
630 */
631 void WINAPI Select_SetTexture (texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, void* pPlugTexdef )
632 {
633   /*
634 #ifdef _DEBUG
635   static int count = 0;
636 #endif
637   */
638         brush_t *b;
639   /*
640 #ifdef _DEBUG
641   count++;
642   Sys_Printf("count: %d\n", count);
643   if(count==4)
644     Sys_Printf("break!\n");
645 #endif
646   */
647         int nCount = g_ptrSelectedFaces.GetSize();
648         if (nCount > 0)
649         {
650                 Undo_Start("set face textures");
651                 assert(g_ptrSelectedFaces.GetSize() == g_ptrSelectedFaceBrushes.GetSize());
652                 for (int i = 0; i < nCount; i++)
653                 {
654                         face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
655                         brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
656                         Undo_AddBrush(selBrush);
657                         SetFaceTexdef (selFace, texdef, brushprimit_texdef, bFitScale, static_cast<IPluginTexdef *>(pPlugTexdef) );
658                         Brush_Build(selBrush, bFitScale);
659                         Undo_EndBrush(selBrush);
660                 }
661                 Undo_End();
662         }
663         else if (selected_brushes.next != &selected_brushes)
664         {
665                 Undo_Start("set brush textures");
666                 for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
667                         if (!b->owner->eclass->fixedsize)
668                         {
669                                 Undo_AddBrush(b);
670                                 Brush_SetTexture (b, texdef, brushprimit_texdef, bFitScale, static_cast<IPluginTexdef *>(pPlugTexdef) );
671                                 Undo_EndBrush(b);
672                         }
673                 Undo_End();
674         }
675         //++timo FIXME: not necessary in every cases, write a message defering / move one level up
676         Sys_UpdateWindows (W_ALL);
677 }
678
679
680 /*
681 ================================================================
682
683   TRANSFORMATIONS
684
685 ================================================================
686 */
687
688 void Select_GetBounds (vec3_t mins, vec3_t maxs)
689 {
690         brush_t *b;
691         int             i;
692
693         for (i=0 ; i<3 ; i++)
694         {
695                 mins[i] = 99999;
696                 maxs[i] = -99999;
697         }
698
699         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
700         {
701                 if (b->owner->eclass->fixedsize)
702                 {
703                         for (i=0 ; i<3 ; i++)
704                         {
705                                 if (b->owner->origin[i] < mins[i])
706                                         mins[i] = b->owner->origin[i];
707                                 if (b->owner->origin[i] > maxs[i])
708                                         maxs[i] = b->owner->origin[i];
709                         }
710                 }
711                 else
712                 {
713                         for (i=0 ; i<3 ; i++)
714                         {
715                                 if (b->mins[i] < mins[i])
716                                         mins[i] = b->mins[i];
717                                 if (b->maxs[i] > maxs[i])
718                                         maxs[i] = b->maxs[i];
719                         }
720                 }
721         }
722 }
723
724 void Select_GetTrueMid (vec3_t mid)
725 {
726         vec3_t  mins, maxs;
727         Select_GetBounds (mins, maxs);
728
729   for (int i=0 ; i<3 ; i++)
730     mid[i] = (mins[i] + ((maxs[i] - mins[i]) / 2));
731 }
732
733 void Select_GetMid (vec3_t mid)
734 {
735         vec3_t  mins, maxs;
736         int             i;
737
738   if (!g_PrefsDlg.m_bSnap)
739   {
740     Select_GetTrueMid(mid);
741     return;
742   }
743
744   Select_GetBounds (mins, maxs);
745
746   for (i=0 ; i<3 ; i++)
747                 mid[i] = g_qeglobals.d_gridsize*floor ( ( (mins[i] + maxs[i])*0.5 )/g_qeglobals.d_gridsize );
748 }
749
750 vec3_t  select_origin;
751 vec3_t  select_matrix[3];
752 qboolean        select_fliporder;
753
754 // FIXME: bApplyBPrimit is supposed to be temporary
755 // TODO: manage Brush_Build calls, too many of them with the texture processing
756 // FIXME: the undo doesn't seem to work correctly on texturing and flip/rotate operations?? this is not supposed to be related to the texture locking code, so what is happening?
757 // FIXME: ApplyMatrix works on flipping operation, b0rks on Rotations (so does the "regular" rotation code??)
758 // FIXME: what is getting called in free rotation mode? that used to work right?
759 void Select_ApplyMatrix (bool bSnap, bool bRotation, int nAxis, float fDeg)//, qboolean bApplyBPrimit)
760 {
761         brush_t *b;
762         face_t  *f;
763         int             i, j;
764         vec3_t  temp, tmporigin;
765
766         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
767         {
768                 if(b->owner->eclass->fixedsize)
769                 {
770                         VectorCopy (b->owner->origin, tmporigin);
771                         // transform the origin point
772                         VectorSubtract (b->owner->origin, select_origin, temp);
773                         for (j=0 ; j<3 ; j++)
774                                 b->owner->origin[j] = DotProduct(temp, select_matrix[j]) + select_origin[j];
775
776       // update the origin key
777       char text[64];
778       sprintf (text, "%i %i %i",
779         (int)b->owner->origin[0], (int)b->owner->origin[1], (int)b->owner->origin[2]);
780       SetKeyValue(b->owner, "origin", text);
781
782       /*\todo remove brush-based bounding box for fixedsize entities */
783       VectorSubtract (b->owner->origin, tmporigin, temp);
784                         for (f=b->brush_faces ; f ; f=f->next)
785                         {
786                                 // move fixedsize bbox to new origin
787                                 for (i=0 ; i<3 ; i++)
788                                         VectorAdd (f->planepts[i], temp, f->planepts[i]);
789                         }
790                         Brush_Build(b, bSnap,true,false,false); // don't filter
791
792                 }
793                 else if (b->patchBrush)
794                 {
795                         if (!bRotation && !((g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points != 0) || g_bPatchBendMode))
796                                 // invert patch if this is a mirroring operation, unless points are selected or bendmode is active
797                                 patchInvert(b->pPatch);
798                         // NOTE: does not clamp points to integers
799                         Patch_ApplyMatrix(b->pPatch, select_origin, select_matrix, false);
800                 }
801                 else
802                 {
803                         for (f=b->brush_faces ; f ; f=f->next)
804                         {
805                                 // FIXME: only in BP mode!
806                                 // if we are using Brush Primitives texturing, we need to compute the texture matrix after the geometric transformation
807                                 // (with the default texturing you don't need to compute anything for flipping and mirroring operations)
808                                 //      if (bApplyBPrimit) {
809                                 //        ApplyMatrix_BrushPrimit (f, select_matrix, select_origin, select_fliporder);
810                                 //      }
811                                 for (i=0 ; i<3 ; i++)
812                                 {
813                                         VectorSubtract (f->planepts[i], select_origin, temp);
814                                         for (j=0 ; j<3 ; j++)
815                                                 f->planepts[i][j] = DotProduct(temp, select_matrix[j]) + select_origin[j];
816                                 }
817                                 if (select_fliporder)
818                                 {
819                                         VectorCopy (f->planepts[0], temp);
820                                         VectorCopy (f->planepts[2], f->planepts[0]);
821                                         VectorCopy (temp, f->planepts[2]);
822                                 }
823                         }
824                         Brush_Build(b, bSnap,true,false,false); // don't filter
825                 }
826         }
827 }
828
829 void ProjectOnPlane(vec3_t& normal,float dist,vec3_t& ez, vec3_t& p)
830 {
831         if (fabs(ez[0]) == 1)
832                 p[0] = (dist - normal[1] * p[1] - normal[2] * p[2]) / normal[0];
833         else if (fabs(ez[1]) == 1)
834                 p[1] = (dist - normal[0] * p[0] - normal[2] * p[2]) / normal[1];
835         else
836                 p[2] = (dist - normal[0] * p[0] - normal[1] * p[1]) / normal[2];
837 }
838
839 void Back(vec3_t& dir, vec3_t& p)
840 {
841         if (fabs(dir[0]) == 1)
842                 p[0] = 0;
843         else if (fabs(dir[1]) == 1)
844                 p[1] = 0;
845         else p[2] = 0;
846 }
847
848
849
850 // using scale[0] and scale[1]
851 void ComputeScale(vec3_t& rex, vec3_t& rey, vec3_t& p, face_t* f)
852 {
853         float px = DotProduct(rex, p);
854         float py = DotProduct(rey, p);
855         px *= f->texdef.scale[0];
856         py *= f->texdef.scale[1];
857   vec3_t aux;
858   VectorCopy(rex, aux);
859   VectorScale(aux, px, aux);
860   VectorCopy(aux, p);
861   VectorCopy(rey, aux);
862   VectorScale(aux, py, aux);
863   VectorAdd(p, aux, p);
864 }
865
866 void ComputeAbsolute(face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3)
867 {
868         vec3_t ex,ey,ez;                // local axis base
869
870 #ifdef _DEBUG
871         if (g_qeglobals.m_bBrushPrimitMode)
872                 Sys_Printf("Warning : illegal call of ComputeAbsolute in brush primitive mode\n");
873 #endif
874
875   // compute first local axis base
876   TextureAxisFromPlane(&f->plane, ex, ey);
877   CrossProduct(ex, ey, ez);
878
879         vec3_t aux;
880   VectorCopy(ex, aux);
881   VectorScale(aux, -f->texdef.shift[0], aux);
882   VectorCopy(aux, p1);
883   VectorCopy(ey, aux);
884   VectorScale(aux, -f->texdef.shift[1], aux);
885   VectorAdd(p1, aux, p1);
886   VectorCopy(p1, p2);
887   VectorAdd(p2, ex, p2);
888   VectorCopy(p1, p3);
889   VectorAdd(p3, ey, p3);
890   VectorCopy(ez, aux);
891   VectorScale(aux, -f->texdef.rotate, aux);
892   VectorRotate(p1, aux, p1);
893   VectorRotate(p2, aux, p2);
894   VectorRotate(p3, aux, p3);
895         // computing rotated local axis base
896         vec3_t rex,rey;
897   VectorCopy(ex, rex);
898   VectorRotate(rex, aux, rex);
899   VectorCopy(ey, rey);
900   VectorRotate(rey, aux, rey);
901
902   ComputeScale(rex,rey,p1,f);
903         ComputeScale(rex,rey,p2,f);
904         ComputeScale(rex,rey,p3,f);
905
906         // project on normal plane
907         // along ez
908         // assumes plane normal is normalized
909         ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p1);
910         ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p2);
911         ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p3);
912 };
913
914
915 void AbsoluteToLocal(plane_t normal2, face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3)
916 {
917         vec3_t ex,ey,ez;
918
919 #ifdef _DEBUG
920         if (g_qeglobals.m_bBrushPrimitMode)
921                 Sys_Printf("Warning : illegal call of AbsoluteToLocal in brush primitive mode\n");
922 #endif
923
924         // computing new local axis base
925   TextureAxisFromPlane(&normal2, ex, ey);
926   CrossProduct(ex, ey, ez);
927
928   // projecting back on (ex,ey)
929         Back(ez,p1);
930         Back(ez,p2);
931         Back(ez,p3);
932
933         vec3_t aux;
934         // rotation
935   VectorCopy(p2, aux);
936   VectorSubtract(aux, p1,aux);
937
938         float x = DotProduct(aux,ex);
939         float y = DotProduct(aux,ey);
940   f->texdef.rotate = 180 * atan2(y,x) / Q_PI;
941
942         vec3_t rex,rey;
943         // computing rotated local axis base
944   VectorCopy(ez, aux);
945   VectorScale(aux, f->texdef.rotate, aux);
946   VectorCopy(ex, rex);
947   VectorRotate(rex, aux, rex);
948   VectorCopy(ey, rey);
949   VectorRotate(rey, aux, rey);
950
951         // scale
952   VectorCopy(p2, aux);
953   VectorSubtract(aux, p1, aux);
954   f->texdef.scale[0] = DotProduct(aux, rex);
955   VectorCopy(p3, aux);
956   VectorSubtract(aux, p1, aux);
957   f->texdef.scale[1] = DotProduct(aux, rey);
958
959         // shift
960         // only using p1
961         x = DotProduct(rex,p1);
962         y = DotProduct(rey,p1);
963         x /= f->texdef.scale[0];
964         y /= f->texdef.scale[1];
965
966   VectorCopy(rex, p1);
967   VectorScale(p1, x, p1);
968   VectorCopy(rey, aux);
969   VectorScale(aux, y, aux);
970   VectorAdd(p1, aux, p1);
971   VectorCopy(ez, aux);
972   VectorScale(aux, -f->texdef.rotate, aux);
973   VectorRotate(p1, aux, p1);
974         f->texdef.shift[0] = -DotProduct(p1, ex);
975         f->texdef.shift[1] = -DotProduct(p1, ey);
976
977         // stored rot is good considering local axis base
978         // change it if necessary
979         f->texdef.rotate = -f->texdef.rotate;
980
981   Clamp(f->texdef.shift[0], f->d_texture->width);
982   Clamp(f->texdef.shift[1], f->d_texture->height);
983   Clamp(f->texdef.rotate, 360);
984
985 }
986
987 void RotateFaceTexture(face_t* f, int nAxis, float fDeg)
988 {
989         vec3_t p1,p2,p3, rota;
990         p1[0] = p1[1] = p1[2] = 0;
991         VectorCopy(p1, p2);
992         VectorCopy(p1, p3);
993         VectorCopy(p1, rota);
994         ComputeAbsolute(f, p1, p2, p3);
995
996         rota[nAxis] = fDeg;
997         VectorRotateOrigin (p1, rota, select_origin, p1);
998         VectorRotateOrigin (p2, rota, select_origin, p2);
999         VectorRotateOrigin (p3, rota, select_origin, p3);
1000
1001         plane_t normal2;
1002         vec3_t vNormal;
1003         vNormal[0] = f->plane.normal[0];
1004         vNormal[1] = f->plane.normal[1];
1005         vNormal[2] = f->plane.normal[2];
1006         VectorRotate(vNormal, rota, vNormal);
1007         normal2.normal[0] = vNormal[0];
1008         normal2.normal[1] = vNormal[1];
1009         normal2.normal[2] = vNormal[2];
1010         AbsoluteToLocal(normal2, f, p1, p2 ,p3);
1011
1012 }
1013
1014 void RotateTextures(int nAxis, float fDeg, vec3_t vOrigin)
1015 {
1016         for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1017         {
1018                 for (face_t* f=b->brush_faces ; f ; f=f->next)
1019                 {
1020                         if (g_qeglobals.m_bBrushPrimitMode)
1021                                 RotateFaceTexture_BrushPrimit (f, nAxis, fDeg, vOrigin);
1022                         else
1023                                 RotateFaceTexture (f, nAxis, fDeg);
1024                 }
1025                 Brush_Build(b, false,true,false,false); // don't filter
1026         }
1027 }
1028
1029 void Select_ApplyMatrix_BrushPrimit()
1030 {
1031   #ifdef _DEBUG
1032   if (!g_qeglobals.m_bBrushPrimitMode) {
1033     Sys_FPrintf(SYS_ERR,"ERROR: Select_ApplyMatrix_BrushPrimit called in non-BP mode\n");
1034   }
1035   #endif
1036         for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1037         {
1038                 for (face_t* f=b->brush_faces ; f ; f=f->next)
1039                 {
1040       ApplyMatrix_BrushPrimit (f, select_matrix, select_origin);
1041     }
1042   }
1043 }
1044
1045 void Select_FlipAxis (int axis)
1046 {
1047         int             i;
1048
1049         Select_GetMid (select_origin);
1050         for (i=0 ; i<3 ; i++)
1051         {
1052                 VectorCopy (vec3_origin, select_matrix[i]);
1053                 select_matrix[i][i] = 1;
1054         }
1055         select_matrix[axis][axis] = -1;
1056         select_fliporder = true;
1057
1058   // texture locking
1059   if (g_PrefsDlg.m_bRotateLock) {
1060     // axis flipping inverts space orientation, we have to use a general texture locking algorithm instead of the RotateFaceTexture
1061     if (g_qeglobals.m_bBrushPrimitMode) {
1062       Select_ApplyMatrix_BrushPrimit();
1063     }
1064     else
1065     {
1066       // there's never been flip locking for non BP mode, this would be tricky to write and there's not much interest for it with the coming of BP format
1067       // what could be done is converting regular to BP, locking, then back to regular :)
1068       Sys_FPrintf(SYS_WRN, "WARNING: regular texturing doesn't have texture lock on flipping operations\n");
1069     }
1070   }
1071   // geometric transformation
1072         Select_ApplyMatrix (true, false, 0, 0);
1073         Sys_UpdateWindows (W_ALL);
1074 }
1075
1076
1077 void Select_Scale(float x, float y, float z)
1078 {
1079   Select_GetMid (select_origin);
1080         for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1081         {
1082     // ignore fixedsize entities
1083     if(b->owner->eclass->fixedsize) continue;
1084                 for (face_t* f=b->brush_faces ; f ; f=f->next)
1085                 {
1086                         for (int i=0 ; i<3 ; i++)
1087                         {
1088         f->planepts[i][0] -= select_origin[0];
1089         f->planepts[i][1] -= select_origin[1];
1090         f->planepts[i][2] -= select_origin[2];
1091         f->planepts[i][0] *= x;
1092         f->planepts[i][1] *= y;
1093         f->planepts[i][2] *= z;
1094
1095         f->planepts[i][0] += select_origin[0];
1096         f->planepts[i][1] += select_origin[1];
1097         f->planepts[i][2] += select_origin[2];
1098                         }
1099                 }
1100                 Brush_Build(b, false,true,false,false); // don't filter
1101     if (b->patchBrush)
1102     {
1103       vec3_t v;
1104       v[0] = x;
1105       v[1] = y;
1106       v[2] = z;
1107       Patch_Scale(b->pPatch, select_origin, v);
1108     }
1109         }
1110 }
1111
1112 void Select_RotateAxis (int axis, float deg, bool bPaint, bool bMouse)
1113 {
1114         int             i;
1115         vec_t   c, s;
1116
1117         if (deg == 0)
1118   {
1119                 return;
1120   }
1121
1122   if (bMouse)
1123   {
1124     VectorCopy(g_pParentWnd->ActiveXY()->RotateOrigin(), select_origin);
1125   }
1126   else
1127   {
1128           Select_GetMid (select_origin);
1129   }
1130
1131   /*
1132   if(axis == 2)
1133   {
1134     vec3_t rotation;
1135     VectorSet(rotation, 0, 0, 360 - deg);
1136     for(brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next)
1137       if(b->owner->model.pEdit)
1138         b->owner->model.pEdit->Rotate(select_origin, rotation);
1139   }
1140   */
1141
1142         select_fliporder = false;
1143
1144   // the "90" degrees algorithm is mostly used on axis rotate as a speedup and possibly avoiding rounding errors as much as possible
1145   // previous implementation was doing an indirect-oriented rotation over the plane whereas the general algo below was doing a direct-oriented rotation
1146   // this was confusing the texture locking algorithms, fixed it to be direct-oriented (side consequence is that the axis rotate toolbar button rotates the other way now)
1147   // NOTE: previous algo was using vec3_origin in the matrix computation..
1148   //   I don't see what an origin does in linear transformations (3x3 matrixes always relate to a (0,0,0) origin)
1149   //   in Radiant it's initialized as (0,0,0) and never set to another value
1150   //   so I got rid of it when it's not used for initialisation tasks (and even if it's not (0,0,0) it should not matter
1151         if (deg == 90)
1152         {
1153                 c = 0;
1154                 s = 1;
1155         }
1156         else
1157         {
1158     c = cos(deg * Q_PI / 180.0);
1159     s = sin(deg * Q_PI / 180.0);
1160         }
1161
1162         for (i=0 ; i<3 ; i++)
1163         {
1164                 VectorCopy (vec3_origin, select_matrix[i]);
1165                 select_matrix[i][i] = 1;
1166         }
1167
1168         switch (axis)
1169         {
1170         case 0:
1171                 select_matrix[1][1] = c;
1172                 select_matrix[1][2] = s;
1173                 select_matrix[2][1] = -s;
1174                 select_matrix[2][2] = c;
1175                 break;
1176         case 1:
1177                 select_matrix[0][0] = c;
1178                 select_matrix[0][2] = s;
1179                 select_matrix[2][0] = -s;
1180                 select_matrix[2][2] = c;
1181                 break;
1182         case 2:
1183                 select_matrix[0][0] = c;
1184                 select_matrix[0][1] = s;
1185                 select_matrix[1][0] = -s;
1186                 select_matrix[1][1] = c;
1187                 break;
1188         }
1189
1190
1191   // texture locking
1192         if (g_PrefsDlg.m_bRotateLock)
1193   {
1194                 // Terrible hack, reversing input rotation angle to correct
1195                 // texture rotation direction for X and Z axes.
1196                 // RotateTextures needs to be changed to fix this properly?
1197                 if (axis == 1)
1198                         RotateTextures(axis, deg, select_origin);
1199                 else
1200                         RotateTextures(axis, deg * -1, select_origin);
1201   }
1202   // geometric transformation
1203         Select_ApplyMatrix(!bMouse, true, axis, deg);//, false);
1204
1205         if (bPaint)
1206                 Sys_UpdateWindows (W_ALL);
1207 }
1208
1209 /*
1210 ================================================================
1211
1212 GROUP SELECTIONS
1213
1214 ================================================================
1215 */
1216
1217 void Select_RealCompleteTall(vec3_t mins, vec3_t maxs)
1218 {
1219         brush_t *b, *next;
1220
1221   int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
1222   int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
1223
1224   g_qeglobals.d_select_mode = sel_brush;
1225
1226         for (b=active_brushes.next ; b != &active_brushes ; b=next)
1227         {
1228                 next = b->next;
1229
1230                 if (b->bFiltered)
1231                         continue;
1232
1233                 if ( (b->maxs[nDim1] > maxs[nDim1] || b->mins[nDim1] < mins[nDim1])
1234                         || (b->maxs[nDim2] > maxs[nDim2] || b->mins[nDim2] < mins[nDim2]) )
1235                         continue;
1236
1237                 Brush_RemoveFromList (b);
1238                 Brush_AddToList (b, &selected_brushes);
1239         }
1240 }
1241
1242 void Select_CompleteTall (void)
1243 {
1244   vec3_t mins, maxs;
1245
1246   if (!QE_SingleBrush ())
1247     return;
1248
1249   Undo_Start ("select complete tall");
1250   Undo_AddBrushList (&selected_brushes);
1251   Undo_End();
1252
1253   VectorCopy (selected_brushes.next->mins, mins);
1254   VectorCopy (selected_brushes.next->maxs, maxs);
1255   Select_Delete ();
1256
1257   Select_RealCompleteTall(mins, maxs);
1258   Sys_UpdateWindows (W_ALL);
1259 }
1260
1261 void Select_PartialTall (void)
1262 {
1263         brush_t *b, *next;
1264         vec3_t  mins, maxs;
1265
1266         if (!QE_SingleBrush ())
1267                 return;
1268
1269   Undo_Start ("select complete tall");
1270   Undo_AddBrushList (&selected_brushes);
1271   Undo_End();
1272
1273         g_qeglobals.d_select_mode = sel_brush;
1274
1275         VectorCopy (selected_brushes.next->mins, mins);
1276         VectorCopy (selected_brushes.next->maxs, maxs);
1277         Select_Delete ();
1278
1279   int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
1280   int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
1281
1282         for (b=active_brushes.next ; b != &active_brushes ; b=next)
1283         {
1284                 next = b->next;
1285
1286                 if (b->bFiltered)
1287                         continue;
1288
1289                 if ( (b->mins[nDim1] > maxs[nDim1] || b->maxs[nDim1] < mins[nDim1])
1290                         || (b->mins[nDim2] > maxs[nDim2] || b->maxs[nDim2] < mins[nDim2]) )
1291                         continue;
1292
1293
1294                 Brush_RemoveFromList (b);
1295                 Brush_AddToList (b, &selected_brushes);
1296         }
1297
1298         Sys_UpdateWindows (W_ALL);
1299 }
1300
1301 void Select_Touching (void)
1302 {
1303         brush_t *b, *next;
1304         int             i;
1305         vec3_t  mins, maxs;
1306
1307         if (!QE_SingleBrush ())
1308                 return;
1309
1310         g_qeglobals.d_select_mode = sel_brush;
1311
1312         VectorCopy (selected_brushes.next->mins, mins);
1313         VectorCopy (selected_brushes.next->maxs, maxs);
1314
1315         for (b=active_brushes.next ; b != &active_brushes ; b=next)
1316         {
1317                 next = b->next;
1318
1319                 if (b->bFiltered)
1320                         continue;
1321
1322                 for (i=0 ; i<3 ; i++)
1323                         if (b->mins[i] > maxs[i]+1 || b->maxs[i] < mins[i]-1)
1324                                 break;
1325
1326                 if (i == 3)
1327                 {
1328                         Brush_RemoveFromList (b);
1329                         Brush_AddToList (b, &selected_brushes);
1330                 }
1331         }
1332
1333         Sys_UpdateWindows (W_ALL);
1334 }
1335
1336 void Select_Inside (void)
1337 {
1338         brush_t *b, *next;
1339         int             i;
1340         vec3_t  mins, maxs;
1341
1342         if (!QE_SingleBrush ())
1343                 return;
1344
1345   Undo_Start ("select inside");
1346   Undo_AddBrushList (&selected_brushes);
1347   Undo_End();
1348
1349         g_qeglobals.d_select_mode = sel_brush;
1350
1351         VectorCopy (selected_brushes.next->mins, mins);
1352         VectorCopy (selected_brushes.next->maxs, maxs);
1353         Select_Delete ();
1354
1355         for (b=active_brushes.next ; b != &active_brushes ; b=next)
1356         {
1357                 next = b->next;
1358
1359                 if (b->bFiltered)
1360                         continue;
1361
1362                 for (i=0 ; i<3 ; i++)
1363                         if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i])
1364                                 break;
1365                 if (i == 3)
1366                 {
1367                         Brush_RemoveFromList (b);
1368                         Brush_AddToList (b, &selected_brushes);
1369                 }
1370         }
1371
1372         Sys_UpdateWindows (W_ALL);
1373 }
1374
1375 void Select_SelectGroup(entity_t* group)
1376 {
1377         brush_t*  b;
1378         //brush_t*  next;
1379
1380         Undo_Start ("select func group");
1381         Undo_AddBrushList (&selected_brushes);
1382         Undo_End();
1383
1384         Select_Deselect();
1385
1386         b = &group->brushes;
1387
1388         do
1389         {
1390                 b = b->onext;
1391                 Brush_RemoveFromList(b);
1392                 Brush_AddToList(b, &selected_brushes);
1393         } while( b->onext != &group->brushes );
1394
1395         Sys_UpdateWindows (W_ALL);
1396 }
1397
1398
1399 void Select_Ungroup(void)
1400 {
1401         int numselectedgroups;
1402         entity_t        *e;
1403         brush_t         *b,* sb;
1404
1405         numselectedgroups = 0;
1406         for (sb = selected_brushes.next; sb != &selected_brushes; sb = sb->next)
1407         {
1408                 e = sb->owner;
1409
1410                 if (e == world_entity || e->eclass->fixedsize)
1411                 {
1412                         continue;
1413                 }
1414
1415                 for (b = e->brushes.onext; b != &e->brushes; b = e->brushes.onext)
1416                 {
1417                         Entity_UnlinkBrush (b);
1418                         Entity_LinkBrush (world_entity, b);
1419                 }
1420                 Entity_Free (e);
1421                 numselectedgroups++;
1422         }
1423
1424         if (numselectedgroups <= 0)
1425         {
1426                 Sys_Printf("No grouped entities selected.\n");
1427                 return;
1428         }
1429         Sys_Printf("Ungrouped %d entit%s.\n", numselectedgroups, (numselectedgroups == 1)?"y":"ies");
1430         Sys_UpdateWindows (W_ALL);
1431 }
1432
1433 /*!
1434 group selected brushes into specified entity
1435 if an entity is empty afterwards, destroy it
1436 */
1437 void Select_GroupEntity(entity_t* group)
1438 {
1439   entity_t* e;
1440   brush_t *b;
1441
1442   if(group->eclass->fixedsize)
1443   {
1444     Sys_FPrintf (SYS_ERR, "Select_GroupEntity: can't group anything to a fixedsize entity\n");
1445     return;
1446   }
1447
1448   for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
1449   {
1450     if(b->owner->eclass->fixedsize) continue;
1451     e = b->owner;
1452     Entity_UnlinkBrush(b);
1453     Entity_LinkBrush(group, b);
1454     if(e != world_entity && e->brushes.onext == &e->brushes)
1455     {
1456       Undo_AddEntity(e);
1457       Entity_Free(e);
1458     }
1459   }
1460 }
1461
1462 /*!
1463 merge all selected entities together into the first one selected
1464 NOTE: makes use of order of selected_brushes list
1465 can be used to move world brushes in an entity, or to merge several ents together
1466 NOTE: didn't devise a strategy on the epairs, we merge into the first entity and use those
1467 */
1468 void Select_MergeEntity()
1469 {
1470   entity_t* e = NULL;
1471   brush_t* b;
1472   for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
1473   {
1474     if(!b->owner->eclass->fixedsize)
1475     {
1476       e = b->owner;
1477       break;
1478     }
1479   }
1480
1481   if(e != NULL)
1482   {
1483     Select_GroupEntity(e);
1484
1485     int count = 0;
1486     for(b = e->brushes.onext; b != &e->brushes; b=b->onext)
1487     {
1488       //Brush_RemoveFromList (b);
1489           //Brush_AddToList(b, &active_brushes);
1490       count++;
1491     }
1492     Sys_Printf ("Merged %d brushes into %s entity\n", count, ValueForKey (e, "classname"));
1493   }
1494 }
1495
1496 /*
1497 ====================
1498 Select_Seperate
1499 ====================
1500 */
1501 void Select_Seperate( void ) {
1502   Select_GroupEntity( world_entity );
1503 }
1504
1505 /*
1506 ====================
1507 Select_MakeStructural
1508 ====================
1509 */
1510 void Select_MakeStructural (void)
1511 {
1512         brush_t *b;
1513         face_t  *f;
1514
1515         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1516         {
1517                 for (f=b->brush_faces ; f ; f=f->next)
1518                         f->texdef.contents &= ~CONTENTS_DETAIL;
1519                 b->bFiltered = FilterBrush(b);
1520         }
1521         Select_Deselect ();
1522         Sys_UpdateWindows (W_ALL);
1523 }
1524
1525 void Select_MakeDetail (void)
1526 {
1527         brush_t *b;
1528         face_t  *f;
1529
1530         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1531         {
1532                 for (f=b->brush_faces ; f ; f=f->next)
1533                         f->texdef.contents |= CONTENTS_DETAIL;
1534                 b->bFiltered = FilterBrush(b);
1535         }
1536         Select_Deselect ();
1537         Sys_UpdateWindows (W_ALL);
1538 }
1539
1540 // brush primitive texture adjustments, use the camera view to map adjustments
1541 // ShiftTextureRelative_BrushPrimit ( s , t ) will shift relative to the texture
1542 void ShiftTextureRelative_Camera(face_t *f, int x, int y)
1543 {
1544   vec3_t vecS, vecT;
1545   vec_t XY[2]; // the values we are going to send for translation
1546   vec_t sgn[2]; // +1 or -1
1547   int axis[2];
1548   CamWnd* pCam;
1549
1550   // get the two relative texture axes for the current texturing
1551   BrushPrimit_GetRelativeAxes(f, vecS, vecT);
1552
1553   // center point of the face, project it on the camera space
1554   vec3_t C;
1555   VectorClear(C);
1556   int i;
1557   for (i=0; i<f->face_winding->numpoints; i++)
1558   {
1559     VectorAdd(C,f->face_winding->points[i],C);
1560   }
1561   VectorScale(C,1.0/f->face_winding->numpoints,C);
1562
1563   pCam = g_pParentWnd->GetCamWnd();
1564   pCam->MatchViewAxes(C, vecS, axis[0], sgn[0]);
1565   pCam->MatchViewAxes(C, vecT, axis[1], sgn[1]);
1566
1567   // this happens when the two directions can't be mapped on two different directions on the screen
1568   // then the move will occur against a single axis
1569   // (i.e. the user is not positioned well enough to send understandable shift commands)
1570   // NOTE: in most cases this warning is not very relevant because the user would use one of the two axes
1571   // for which the solution is easy (the other one being unknown)
1572   // so this warning could be removed
1573   if (axis[0] == axis[1])
1574     Sys_FPrintf(SYS_WRN, "Warning: degenerate in ShiftTextureRelative_Camera\n");
1575
1576   // compute the X Y geometric increments
1577   // those geometric increments will be applied along the texture axes (the ones we computed above)
1578   XY[0] = 0;
1579   XY[1] = 0;
1580   if (x!=0)
1581   {
1582     // moving right/left
1583     XY[axis[0]] += sgn[0]*x;
1584   }
1585   if (y!=0)
1586   {
1587     XY[axis[1]] += sgn[1]*y;
1588   }
1589   // we worked out a move along vecS vecT, and we now it's geometric amplitude
1590   // apply it
1591   ShiftTextureRelative_BrushPrimit(f, XY[0], XY[1]);
1592 }
1593
1594 void Select_ShiftTexture(int x, int y)
1595 {
1596         brush_t         *b;
1597         face_t          *f;
1598
1599   int nFaceCount = g_ptrSelectedFaces.GetSize();
1600
1601         if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1602                 return;
1603
1604         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1605         {
1606                 for (f=b->brush_faces ; f ; f=f->next)
1607                 {
1608                         if (g_qeglobals.m_bBrushPrimitMode)
1609                         {
1610         ShiftTextureRelative_Camera( f, x, y );
1611                         }
1612                         else
1613                         {
1614                                 f->texdef.shift[0] += x;
1615                                 f->texdef.shift[1] += y;
1616                         }
1617                 }
1618                 Brush_Build(b,true,true,false,false); // don't filter
1619                 if (b->patchBrush)
1620                 {
1621                         Patch_ShiftTexture(b->pPatch, x, y);
1622                 }
1623         }
1624
1625         if (nFaceCount > 0)
1626         {
1627     for (int i = 0; i < nFaceCount; i++)
1628     {
1629       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1630       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1631                 if (g_qeglobals.m_bBrushPrimitMode)
1632                 {
1633         ShiftTextureRelative_Camera( selFace, x, y );
1634       }
1635                 else
1636                 {
1637                         selFace->texdef.shift[0] += x;
1638                           selFace->texdef.shift[1] += y;
1639                 }
1640                 Brush_Build(selBrush,true,true,false,false); // don't filter
1641     }
1642         }
1643
1644         Sys_UpdateWindows (W_CAMERA);
1645 }
1646
1647 //  setting float as input
1648 void Select_ScaleTexture(float x, float y)
1649 {
1650         brush_t         *b;
1651         face_t          *f;
1652
1653   int nFaceCount = g_ptrSelectedFaces.GetSize();
1654
1655   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1656         {
1657                 return;
1658         }
1659
1660         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1661         {
1662                 for (f=b->brush_faces ; f ; f=f->next)
1663                 {
1664                         if (g_qeglobals.m_bBrushPrimitMode)
1665                         {
1666                                 // apply same scale as the spinner button of the surface inspector
1667                                 float   shift[2];
1668                                 float   rotate;
1669                                 float   scale[2];
1670                                 brushprimit_texdef_t bp;
1671                                 // compute normalized texture matrix
1672                                 ConvertTexMatWithQTexture( &f->brushprimit_texdef, f->d_texture, &bp, NULL );
1673                                 // compute fake shift scale rot
1674                                 TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1675                                 // update
1676                                 scale[0]+=static_cast<float>(x)*0.1;
1677                                 scale[1]+=static_cast<float>(y)*0.1;
1678                                 // compute new normalized texture matrix
1679                                 FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1680                                 // apply to face texture matrix
1681                                 ConvertTexMatWithQTexture( &bp, NULL, &f->brushprimit_texdef, f->d_texture );
1682                         }
1683                         else
1684                         {
1685                                 f->texdef.scale[0] += x;
1686                                 f->texdef.scale[1] += y;
1687                         }
1688                 }
1689                 Brush_Build(b,true,true,false,false); // don't filter
1690                 if (b->patchBrush)
1691                 {
1692                         Patch_ScaleTexture(b->pPatch, x, y);
1693                 }
1694         }
1695
1696         if (nFaceCount > 0)
1697         {
1698     for (int i = 0; i < nFaceCount; i++)
1699     {
1700       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1701       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1702                   if (g_qeglobals.m_bBrushPrimitMode)
1703                   {
1704                           float shift[2];
1705                           float rotate;
1706                           float scale[2];
1707                           brushprimit_texdef_t bp;
1708                           ConvertTexMatWithQTexture( &selFace->brushprimit_texdef, selFace->d_texture, &bp, NULL );
1709                           TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1710                           scale[0]+=static_cast<float>(x)*0.1;
1711                           scale[1]+=static_cast<float>(y)*0.1;
1712                           FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1713                           ConvertTexMatWithQTexture( &bp, NULL, &selFace->brushprimit_texdef, selFace->d_texture );
1714                   }
1715                   else
1716                   {
1717                           selFace->texdef.scale[0] += x;
1718                           selFace->texdef.scale[1] += y;
1719                   }
1720                   Brush_Build(selBrush,true,true,false,false); // don't filter
1721     }
1722         }
1723
1724         Sys_UpdateWindows (W_CAMERA);
1725 }
1726
1727 void Select_RotateTexture(int amt)
1728 {
1729         brush_t         *b;
1730         face_t          *f;
1731
1732   int nFaceCount = g_ptrSelectedFaces.GetSize();
1733
1734   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1735         {
1736                 return;
1737         }
1738
1739         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1740         {
1741                 for (f=b->brush_faces ; f ; f=f->next)
1742                 {
1743                         if (g_qeglobals.m_bBrushPrimitMode)
1744                         {
1745                                 // apply same scale as the spinner button of the surface inspector
1746                                 float   shift[2];
1747                                 float   rotate;
1748                                 float   scale[2];
1749                                 brushprimit_texdef_t bp;
1750                                 // compute normalized texture matrix
1751                                 ConvertTexMatWithQTexture( &f->brushprimit_texdef, f->d_texture, &bp, NULL );
1752                                 // compute fake shift scale rot
1753                                 TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1754                                 // update
1755                                 rotate += amt;
1756                                 // compute new normalized texture matrix
1757                                 FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1758                                 // apply to face texture matrix
1759                                 ConvertTexMatWithQTexture( &bp, NULL, &f->brushprimit_texdef, f->d_texture );
1760                         }
1761                         else
1762                         {
1763                                 f->texdef.rotate += amt;
1764                                 f->texdef.rotate = static_cast<int>(f->texdef.rotate) % 360;
1765                         }
1766                 }
1767                 Brush_Build(b,true,true,false,false); // don't filter
1768                 if (b->patchBrush)
1769                 {
1770                         //Patch_RotateTexture(b->nPatchID, amt);
1771                         Patch_RotateTexture(b->pPatch, amt);
1772                 }
1773         }
1774
1775         if (nFaceCount > 0)
1776         {
1777     for (int i = 0; i < nFaceCount; i++)
1778     {
1779       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1780       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1781                   if (g_qeglobals.m_bBrushPrimitMode)
1782                   {
1783                           float shift[2];
1784                           float rotate;
1785                           float scale[2];
1786                           brushprimit_texdef_t bp;
1787                           ConvertTexMatWithQTexture( &selFace->brushprimit_texdef, selFace->d_texture, &bp, NULL );
1788                           TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1789                           rotate += amt;
1790                           FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1791                           ConvertTexMatWithQTexture( &bp, NULL, &selFace->brushprimit_texdef, selFace->d_texture );
1792                   }
1793                   else
1794                   {
1795                           selFace->texdef.rotate += amt;
1796                           selFace->texdef.rotate = static_cast<int>(selFace->texdef.rotate) % 360;
1797                   }
1798                   Brush_Build(selBrush,true,true,false,false); // don't filter
1799     }
1800         }
1801
1802         Sys_UpdateWindows (W_CAMERA);
1803 }
1804
1805 // TTimo modified to handle shader architecture:
1806 // expects shader names at input, comparison relies on shader names .. texture names no longer relevant
1807 void FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected, bool bForce, bool bSelectMatchingFaces)
1808 {
1809   if (strchr(pFind, ' ') || strchr(pReplace, ' '))
1810   {
1811     Sys_FPrintf(SYS_WRN, "FindReplaceTextures: '%s' or '%s' have spaces, aborted\n", pFind, pReplace);
1812     return;
1813   }
1814
1815         brush_t* pList = (bSelected) ? &selected_brushes : &active_brushes;
1816         if (!bSelected)
1817                 Select_Deselect();
1818
1819   //++timo BP mode: replacing a texture in BP mode is not that easy, you need to recompute the texture matrix
1820   // if the size of the replacing texture differs, otherwise you get wrong scaling
1821   if (g_qeglobals.m_bBrushPrimitMode)
1822     Sys_Printf("TODO: finalize find/replace code for brush primitives");
1823
1824   CPtrArray mFaces;
1825   for (brush_t* pBrush = pList->next ; pBrush != pList; pBrush = pBrush->next)
1826   {
1827     if (!bSelectMatchingFaces && pBrush->patchBrush)
1828     {
1829       Patch_FindReplaceTexture(pBrush, pFind, pReplace, bForce);
1830     }
1831
1832     bool found = false; //spog
1833     for (face_t* pFace = pBrush->brush_faces; pFace; pFace = pFace->next)
1834     {
1835       if(bForce || strcmpi(pFace->pShader->getName(), pFind) == 0)
1836       {
1837         if (!bSelectMatchingFaces) {
1838           pFace->pShader->DecRef();
1839           pFace->pShader = QERApp_Shader_ForName( pReplace );
1840           pFace->pShader->IncRef();
1841           pFace->d_texture = pFace->pShader->getTexture();
1842           pFace->texdef.SetName(pReplace);
1843           found = true;
1844         } else if (bSelectMatchingFaces) {
1845           mFaces.Add(pFace);
1846         }
1847       }
1848     }
1849
1850     if (found) // spog - speed increase, only build brushes that changed
1851       Brush_Build(pBrush);
1852
1853   }
1854
1855   if (bSelectMatchingFaces) {
1856     if (bSelected)
1857       Select_Deselect();
1858
1859     int nSize = mFaces.GetSize();
1860     for (int i = 0; i < nSize; i++) {
1861       g_SelectedFaces.Add(reinterpret_cast<face_t *>(mFaces.GetAt(i)));
1862     }
1863   }
1864
1865   Sys_UpdateWindows (W_CAMERA);
1866 }
1867
1868 void Select_AllOfType()
1869 {
1870         brush_t *b, *next;
1871         entity_t        *e;
1872   // if no brush selected, we will select based on texture
1873   //   the first selected face's texture if any, or the current texture
1874   // if a brush is selected, we will select entities (first non-worldspawn owner in selected brushes)
1875         if (selected_brushes.next == &selected_brushes)
1876         {
1877
1878     CString strName;
1879     if (g_ptrSelectedFaces.GetSize() == 0)
1880     {
1881       strName = g_qeglobals.d_texturewin.texdef.GetName();
1882     }
1883     else
1884     {
1885       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(0));
1886       strName = selFace->texdef.GetName();
1887     }
1888
1889     Sys_Printf("Selecting all brushes with the texture %s\n", strName.GetBuffer());
1890
1891     Select_Deselect();
1892           for (b=active_brushes.next ; b != &active_brushes ; b=next)
1893     {
1894                   next = b->next;
1895
1896       if (b->bFiltered)
1897             continue;
1898
1899       if (b->patchBrush)
1900       {
1901         if (strcmpi(strName, b->pPatch->pShader->getName()) == 0)
1902         {
1903                             Brush_RemoveFromList (b);
1904                             Brush_AddToList (b, &selected_brushes);
1905         }
1906       }
1907       else
1908       {
1909         for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next)
1910         {
1911           if (strcmpi(strName, pFace->texdef.GetName()) == 0)
1912           {
1913                               Brush_RemoveFromList (b);
1914                               Brush_AddToList (b, &selected_brushes);
1915           }
1916         }
1917       }
1918     }
1919     Sys_UpdateWindows(W_ALL);
1920     return;
1921   }
1922
1923
1924   b = selected_brushes.next;
1925         e = b->owner;
1926
1927   if (e != NULL)
1928   {
1929     if (e != world_entity)
1930     {
1931       CString strName = e->eclass->name;
1932       CString strKey, strVal;
1933       bool bCriteria = GetSelectAllCriteria(strKey, strVal);
1934       Sys_Printf("Selecting all %s entities\n", strName.GetBuffer());
1935       Select_Deselect();
1936
1937             for (b=active_brushes.next ; b != &active_brushes ; b=next)
1938         {
1939                     next = b->next;
1940
1941                         if (b->bFiltered)
1942                         continue;
1943
1944         e = b->owner;
1945         if (e != NULL)
1946         {
1947           if (strcmpi(e->eclass->name, strName) == 0)
1948           {
1949             bool doIt = true;
1950             if (bCriteria) {
1951               CString str = ValueForKey (e, strKey);
1952               if (str.CompareNoCase(strVal) != 0) {
1953                 doIt = false;
1954               }
1955             }
1956             if (doIt) {
1957                         Brush_RemoveFromList (b);
1958                         Brush_AddToList (b, &selected_brushes);
1959             }
1960           }
1961         }
1962       }
1963     }
1964   }
1965         Sys_UpdateWindows (W_ALL);
1966
1967 }
1968
1969 void Select_Reselect()
1970 {
1971   Select_Brush(selected_brushes.next);
1972   Sys_UpdateWindows (W_ALL);
1973 }
1974
1975
1976 void Select_FitTexture(int nHeight, int nWidth)
1977 {
1978         brush_t         *b;
1979
1980   int nFaceCount = g_ptrSelectedFaces.GetSize();
1981
1982   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1983                 return;
1984
1985   for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1986         {
1987     Brush_FitTexture(b, nHeight, nWidth);
1988                 Brush_Build(b,true,true,false,false); // don't filter
1989         }
1990
1991         if (nFaceCount > 0)
1992         {
1993     for (int i = 0; i < nFaceCount; i++)
1994     {
1995       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1996       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1997       Face_FitTexture(selFace, nHeight, nWidth);
1998                 Brush_Build(selBrush,true,true,false,false); // don't filter
1999     }
2000         }
2001
2002         Sys_UpdateWindows (W_CAMERA);
2003 }
2004
2005 void Select_Hide()
2006 {
2007   for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2008   {
2009         b->hiddenBrush = true;
2010     b->bFiltered = true;
2011   }
2012   Sys_UpdateWindows (W_ALL);
2013 }
2014
2015 void Select_ShowAllHidden()
2016 {
2017   brush_t* b;
2018   for (b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2019   {
2020         if (b->hiddenBrush)
2021         {
2022                 b->hiddenBrush = false;
2023                 b->bFiltered = FilterBrush(b);
2024         }
2025   }
2026   for (b=active_brushes.next ; b && b != &active_brushes ; b=b->next)
2027   {
2028         if (b->hiddenBrush)
2029         {
2030                 b->hiddenBrush = false;
2031                 b->bFiltered = FilterBrush(b);
2032         }
2033   }
2034   Sys_UpdateWindows (W_ALL);
2035 }
2036
2037
2038 /*
2039 ============
2040 Select_Invert
2041 ============
2042 */
2043 void Select_Invert(void)
2044 {
2045   brush_t *next, *prev, *b;
2046
2047   Sys_Printf("inverting selection...\n");
2048
2049   next = active_brushes.next;
2050   prev = active_brushes.prev;
2051   if (selected_brushes.next != &selected_brushes)
2052   {
2053     active_brushes.next = selected_brushes.next;
2054     active_brushes.prev = selected_brushes.prev;
2055     active_brushes.next->prev = &active_brushes;
2056     active_brushes.prev->next = &active_brushes;
2057   }
2058   else
2059   {
2060     active_brushes.next = &active_brushes;
2061     active_brushes.prev = &active_brushes;
2062   }
2063   if (next != &active_brushes)
2064   {
2065     selected_brushes.next = next;
2066     selected_brushes.prev = prev;
2067     selected_brushes.next->prev = &selected_brushes;
2068     selected_brushes.prev->next = &selected_brushes;
2069   }
2070   else
2071   {
2072     selected_brushes.next = &selected_brushes;
2073     selected_brushes.prev = &selected_brushes;
2074   }
2075
2076   // now check if any hidden brush is selected
2077   for (b = selected_brushes.next; b != &selected_brushes; )
2078   {
2079         if (b->patchBrush)
2080                 b->pPatch->bSelected = true;
2081
2082     if (b->bFiltered)
2083     {
2084       brush_t *pb = b;
2085                         b = b->next;
2086       Brush_RemoveFromList (pb);
2087       Brush_AddToList (pb, &active_brushes);
2088     }
2089     else b = b->next;
2090
2091   }
2092
2093   for (b = active_brushes.next; b != &active_brushes; b = b->next)
2094   {
2095           if (b->patchBrush)
2096     {
2097                   b->pPatch->bSelected = false;
2098     }
2099   }
2100
2101   // since invert selection only works at the brush level,
2102   // set g_qeglobals.d_select_mode accordingly
2103   g_qeglobals.d_select_mode = sel_brush;
2104
2105   // since invert selection only works at the brush level,
2106   // set g_qeglobals.d_select_mode accordingly
2107   g_qeglobals.d_select_mode = sel_brush;
2108
2109   Sys_UpdateWindows(W_ALL);
2110
2111   Sys_Printf("done.\n");
2112 }
2113
2114 #ifdef ENABLE_GROUPS
2115 /*
2116 ===========
2117 Select_Name
2118 ===========
2119 */
2120 void Select_Name(const char *pName)
2121 {
2122         if (g_qeglobals.m_bBrushPrimitMode)
2123   {
2124           for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2125           {
2126       Brush_SetEpair(b, "Name", pName);
2127           }
2128   }
2129 }
2130
2131 /*
2132 =================
2133 Select_AddToGroup
2134 add selected brushes to a group, update the tree
2135 =================
2136 */
2137 void Select_AddToGroup(const char *pName)
2138 {
2139         if (g_qeglobals.m_bBrushPrimitMode)
2140   {
2141           for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2142           {
2143       Brush_SetEpair(b, "group", pName);
2144                         Group_AddToProperGroup(b);
2145           }
2146   }
2147 }
2148 #endif