]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/select.cpp
more eol-style
[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_bNoClamp)
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_Ungroup(void)
1376 {
1377         int numselectedgroups;
1378         entity_t        *e;
1379         brush_t         *b,* sb;
1380
1381         numselectedgroups = 0;
1382         for (sb = selected_brushes.next; sb != &selected_brushes; sb = sb->next)
1383         {
1384                 e = sb->owner;
1385
1386                 if (e == world_entity || e->eclass->fixedsize)
1387                 {
1388                         continue;
1389                 }
1390
1391                 for (b = e->brushes.onext; b != &e->brushes; b = e->brushes.onext)
1392                 {
1393                         Entity_UnlinkBrush (b);
1394                         Entity_LinkBrush (world_entity, b);
1395                 }
1396                 Entity_Free (e);
1397                 numselectedgroups++;
1398         }
1399
1400         if (numselectedgroups <= 0)
1401         {
1402                 Sys_Printf("No grouped entities selected.\n");
1403                 return;
1404         }
1405         Sys_Printf("Ungrouped %d entit%s.\n", numselectedgroups, (numselectedgroups == 1)?"y":"ies");
1406         Sys_UpdateWindows (W_ALL);
1407 }
1408
1409 /*!
1410 group selected brushes into specified entity
1411 if an entity is empty afterwards, destroy it
1412 */
1413 void Select_GroupEntity(entity_t* group)
1414 {
1415   entity_t* e;
1416   brush_t *b;
1417
1418   if(group->eclass->fixedsize)
1419   {
1420     Sys_FPrintf (SYS_ERR, "Select_GroupEntity: can't group anything to a fixedsize entity\n");
1421     return;
1422   }
1423
1424   for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
1425   {
1426     if(b->owner->eclass->fixedsize) continue;
1427     e = b->owner; 
1428     Entity_UnlinkBrush(b);
1429     Entity_LinkBrush(group, b);
1430     if(e != world_entity && e->brushes.onext == &e->brushes)
1431     {
1432       Undo_AddEntity(e);
1433       Entity_Free(e);
1434     }
1435   }
1436 }
1437
1438 /*!
1439 merge all selected entities together into the first one selected
1440 NOTE: makes use of order of selected_brushes list
1441 can be used to move world brushes in an entity, or to merge several ents together
1442 NOTE: didn't devise a strategy on the epairs, we merge into the first entity and use those
1443 */
1444 void Select_MergeEntity()
1445 {
1446   entity_t* e = NULL;
1447   brush_t* b;
1448   for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
1449   {
1450     if(!b->owner->eclass->fixedsize)
1451     {
1452       e = b->owner;
1453       break;
1454     }
1455   }
1456
1457   if(e != NULL)
1458   {
1459     Select_GroupEntity(e);
1460
1461     int count = 0;
1462     for(b = e->brushes.onext; b != &e->brushes; b=b->onext)
1463     {
1464       //Brush_RemoveFromList (b);
1465           //Brush_AddToList(b, &active_brushes);
1466       count++;
1467     }
1468     Sys_Printf ("Merged %d brushes into %s entity\n", count, ValueForKey (e, "classname"));
1469   }
1470 }
1471
1472 /*
1473 ====================
1474 Select_Seperate
1475 ====================
1476 */
1477 void Select_Seperate( void ) {
1478   Select_GroupEntity( world_entity );
1479 }
1480
1481 /*
1482 ====================
1483 Select_MakeStructural
1484 ====================
1485 */
1486 void Select_MakeStructural (void)
1487 {
1488         brush_t *b;
1489         face_t  *f;
1490
1491         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1492         {
1493                 for (f=b->brush_faces ; f ; f=f->next)
1494                         f->texdef.contents &= ~CONTENTS_DETAIL;
1495                 b->bFiltered = FilterBrush(b);
1496         }
1497         Select_Deselect ();
1498         Sys_UpdateWindows (W_ALL);
1499 }
1500
1501 void Select_MakeDetail (void)
1502 {
1503         brush_t *b;
1504         face_t  *f;
1505
1506         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1507         {
1508                 for (f=b->brush_faces ; f ; f=f->next)
1509                         f->texdef.contents |= CONTENTS_DETAIL;
1510                 b->bFiltered = FilterBrush(b);
1511         }
1512         Select_Deselect ();
1513         Sys_UpdateWindows (W_ALL);
1514 }
1515
1516 // brush primitive texture adjustments, use the camera view to map adjustments
1517 // ShiftTextureRelative_BrushPrimit ( s , t ) will shift relative to the texture
1518 void ShiftTextureRelative_Camera(face_t *f, int x, int y)
1519 {
1520   vec3_t vecS, vecT;
1521   vec_t XY[2]; // the values we are going to send for translation
1522   vec_t sgn[2]; // +1 or -1
1523   int axis[2];
1524   CamWnd* pCam;
1525
1526   // get the two relative texture axes for the current texturing
1527   BrushPrimit_GetRelativeAxes(f, vecS, vecT);
1528
1529   // center point of the face, project it on the camera space
1530   vec3_t C;
1531   VectorClear(C);
1532   int i;
1533   for (i=0; i<f->face_winding->numpoints; i++)
1534   {
1535     VectorAdd(C,f->face_winding->points[i],C);
1536   }
1537   VectorScale(C,1.0/f->face_winding->numpoints,C);
1538
1539   pCam = g_pParentWnd->GetCamWnd();
1540   pCam->MatchViewAxes(C, vecS, axis[0], sgn[0]);
1541   pCam->MatchViewAxes(C, vecT, axis[1], sgn[1]);
1542   
1543   // this happens when the two directions can't be mapped on two different directions on the screen
1544   // then the move will occur against a single axis
1545   // (i.e. the user is not positioned well enough to send understandable shift commands)
1546   // NOTE: in most cases this warning is not very relevant because the user would use one of the two axes
1547   // for which the solution is easy (the other one being unknown)
1548   // so this warning could be removed
1549   if (axis[0] == axis[1])
1550     Sys_FPrintf(SYS_WRN, "Warning: degenerate in ShiftTextureRelative_Camera\n");
1551
1552   // compute the X Y geometric increments
1553   // those geometric increments will be applied along the texture axes (the ones we computed above)
1554   XY[0] = 0;
1555   XY[1] = 0;
1556   if (x!=0)
1557   {
1558     // moving right/left
1559     XY[axis[0]] += sgn[0]*x;
1560   }
1561   if (y!=0)
1562   {
1563     XY[axis[1]] += sgn[1]*y;
1564   }
1565   // we worked out a move along vecS vecT, and we now it's geometric amplitude
1566   // apply it
1567   ShiftTextureRelative_BrushPrimit(f, XY[0], XY[1]);
1568 }
1569
1570 void Select_ShiftTexture(int x, int y)
1571 {
1572         brush_t         *b;
1573         face_t          *f;
1574
1575   int nFaceCount = g_ptrSelectedFaces.GetSize();
1576
1577         if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1578                 return;
1579
1580         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1581         {
1582                 for (f=b->brush_faces ; f ; f=f->next)
1583                 {
1584                         if (g_qeglobals.m_bBrushPrimitMode)
1585                         {
1586         ShiftTextureRelative_Camera( f, x, y );
1587                         }
1588                         else
1589                         {
1590                                 f->texdef.shift[0] += x;
1591                                 f->texdef.shift[1] += y;
1592                         }
1593                 }
1594                 Brush_Build(b,true,true,false,false); // don't filter
1595                 if (b->patchBrush)
1596                 {
1597                         Patch_ShiftTexture(b->pPatch, x, y);
1598                 }
1599         }
1600
1601         if (nFaceCount > 0)
1602         {
1603     for (int i = 0; i < nFaceCount; i++)
1604     {
1605       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1606       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1607                 if (g_qeglobals.m_bBrushPrimitMode)
1608                 {
1609         ShiftTextureRelative_Camera( selFace, x, y );
1610       }
1611                 else
1612                 {
1613                         selFace->texdef.shift[0] += x;
1614                           selFace->texdef.shift[1] += y;
1615                 }
1616                 Brush_Build(selBrush,true,true,false,false); // don't filter
1617     }
1618         }
1619
1620         Sys_UpdateWindows (W_CAMERA);
1621 }
1622
1623 //  setting float as input
1624 void Select_ScaleTexture(float x, float y)
1625 {
1626         brush_t         *b;
1627         face_t          *f;
1628
1629   int nFaceCount = g_ptrSelectedFaces.GetSize();
1630
1631   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1632         {
1633                 return;
1634         }
1635
1636         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1637         {
1638                 for (f=b->brush_faces ; f ; f=f->next)
1639                 {
1640                         if (g_qeglobals.m_bBrushPrimitMode)
1641                         {
1642                                 // apply same scale as the spinner button of the surface inspector
1643                                 float   shift[2];
1644                                 float   rotate;
1645                                 float   scale[2];
1646                                 brushprimit_texdef_t bp; 
1647                                 // compute normalized texture matrix
1648                                 ConvertTexMatWithQTexture( &f->brushprimit_texdef, f->d_texture, &bp, NULL );
1649                                 // compute fake shift scale rot
1650                                 TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1651                                 // update
1652                                 scale[0]+=static_cast<float>(x)*0.1;
1653                                 scale[1]+=static_cast<float>(y)*0.1;
1654                                 // compute new normalized texture matrix
1655                                 FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1656                                 // apply to face texture matrix
1657                                 ConvertTexMatWithQTexture( &bp, NULL, &f->brushprimit_texdef, f->d_texture );
1658                         }
1659                         else
1660                         {
1661                                 f->texdef.scale[0] += x;
1662                                 f->texdef.scale[1] += y;
1663                         }
1664                 }
1665                 Brush_Build(b,true,true,false,false); // don't filter
1666                 if (b->patchBrush)
1667                 {
1668                         Patch_ScaleTexture(b->pPatch, x, y);
1669                 }
1670         }
1671
1672         if (nFaceCount > 0)
1673         {
1674     for (int i = 0; i < nFaceCount; i++)
1675     {
1676       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1677       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1678                   if (g_qeglobals.m_bBrushPrimitMode)
1679                   {
1680                           float shift[2];
1681                           float rotate;
1682                           float scale[2];
1683                           brushprimit_texdef_t bp; 
1684                           ConvertTexMatWithQTexture( &selFace->brushprimit_texdef, selFace->d_texture, &bp, NULL );
1685                           TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1686                           scale[0]+=static_cast<float>(x)*0.1;
1687                           scale[1]+=static_cast<float>(y)*0.1;
1688                           FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1689                           ConvertTexMatWithQTexture( &bp, NULL, &selFace->brushprimit_texdef, selFace->d_texture );
1690                   }
1691                   else
1692                   {
1693                           selFace->texdef.scale[0] += x;
1694                           selFace->texdef.scale[1] += y;
1695                   }
1696                   Brush_Build(selBrush,true,true,false,false); // don't filter
1697     }
1698         }
1699
1700         Sys_UpdateWindows (W_CAMERA);
1701 }
1702
1703 void Select_RotateTexture(int amt)
1704 {
1705         brush_t         *b;
1706         face_t          *f;
1707
1708   int nFaceCount = g_ptrSelectedFaces.GetSize();
1709
1710   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1711         {
1712                 return;
1713         }
1714
1715         for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1716         {
1717                 for (f=b->brush_faces ; f ; f=f->next)
1718                 {
1719                         if (g_qeglobals.m_bBrushPrimitMode)
1720                         {
1721                                 // apply same scale as the spinner button of the surface inspector
1722                                 float   shift[2];
1723                                 float   rotate;
1724                                 float   scale[2];
1725                                 brushprimit_texdef_t bp; 
1726                                 // compute normalized texture matrix
1727                                 ConvertTexMatWithQTexture( &f->brushprimit_texdef, f->d_texture, &bp, NULL );
1728                                 // compute fake shift scale rot
1729                                 TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1730                                 // update
1731                                 rotate += amt;
1732                                 // compute new normalized texture matrix
1733                                 FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1734                                 // apply to face texture matrix
1735                                 ConvertTexMatWithQTexture( &bp, NULL, &f->brushprimit_texdef, f->d_texture );
1736                         }
1737                         else
1738                         {
1739                                 f->texdef.rotate += amt;
1740                                 f->texdef.rotate = static_cast<int>(f->texdef.rotate) % 360;
1741                         }
1742                 }
1743                 Brush_Build(b,true,true,false,false); // don't filter
1744                 if (b->patchBrush)
1745                 {
1746                         //Patch_RotateTexture(b->nPatchID, amt);
1747                         Patch_RotateTexture(b->pPatch, amt);
1748                 }
1749         }
1750         
1751         if (nFaceCount > 0)
1752         {
1753     for (int i = 0; i < nFaceCount; i++)
1754     {
1755       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1756       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1757                   if (g_qeglobals.m_bBrushPrimitMode)
1758                   {
1759                           float shift[2];
1760                           float rotate;
1761                           float scale[2];
1762                           brushprimit_texdef_t bp; 
1763                           ConvertTexMatWithQTexture( &selFace->brushprimit_texdef, selFace->d_texture, &bp, NULL );
1764                           TexMatToFakeTexCoords( bp.coords, shift, &rotate, scale );
1765                           rotate += amt;
1766                           FakeTexCoordsToTexMat( shift, rotate, scale, bp.coords );
1767                           ConvertTexMatWithQTexture( &bp, NULL, &selFace->brushprimit_texdef, selFace->d_texture );
1768                   }
1769                   else
1770                   {
1771                           selFace->texdef.rotate += amt;
1772                           selFace->texdef.rotate = static_cast<int>(selFace->texdef.rotate) % 360;
1773                   }
1774                   Brush_Build(selBrush,true,true,false,false); // don't filter
1775     }
1776         }
1777
1778         Sys_UpdateWindows (W_CAMERA);
1779 }
1780
1781 // TTimo modified to handle shader architecture:
1782 // expects shader names at input, comparison relies on shader names .. texture names no longer relevant
1783 void FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected, bool bForce, bool bSelectMatchingFaces)
1784 {
1785   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=391
1786   if (strchr(pFind, ' ') || strchr(pReplace, ' '))
1787   {
1788     Sys_FPrintf(SYS_WRN, "FindReplaceTextures: '%s' or '%s' have spaces, aborted\n", pFind, pReplace);
1789     return;
1790   }
1791   
1792         brush_t* pList = (bSelected) ? &selected_brushes : &active_brushes;
1793         if (!bSelected)
1794                 Select_Deselect();
1795
1796   //++timo BP mode: replacing a texture in BP mode is not that easy, you need to recompute the texture matrix
1797   // if the size of the replacing texture differs, otherwise you get wrong scaling
1798   if (g_qeglobals.m_bBrushPrimitMode)
1799     Sys_Printf("TODO: finalize find/replace code for brush primitives");
1800
1801   CPtrArray mFaces;
1802   for (brush_t* pBrush = pList->next ; pBrush != pList; pBrush = pBrush->next)
1803   {
1804     if (!bSelectMatchingFaces && pBrush->patchBrush)
1805     {
1806       Patch_FindReplaceTexture(pBrush, pFind, pReplace, bForce);
1807     }
1808         
1809     bool found = false; //spog
1810     for (face_t* pFace = pBrush->brush_faces; pFace; pFace = pFace->next)
1811     {
1812       if(bForce || strcmpi(pFace->pShader->getName(), pFind) == 0)
1813       {
1814         if (!bSelectMatchingFaces) {
1815           pFace->pShader->DecRef();
1816           pFace->pShader = QERApp_Shader_ForName( pReplace );
1817           pFace->pShader->IncRef();
1818           pFace->d_texture = pFace->pShader->getTexture();
1819           pFace->texdef.SetName(pReplace);
1820           found = true;
1821         } else if (bSelectMatchingFaces) {
1822           mFaces.Add(pFace);
1823         }
1824       }
1825     }
1826
1827     if (found) // spog - speed increase, only build brushes that changed
1828       Brush_Build(pBrush);
1829
1830   }
1831
1832   if (bSelectMatchingFaces) {
1833     if (bSelected)
1834       Select_Deselect();
1835
1836     int nSize = mFaces.GetSize();
1837     for (int i = 0; i < nSize; i++) {
1838       g_SelectedFaces.Add(reinterpret_cast<face_t *>(mFaces.GetAt(i)));
1839     }
1840   }
1841
1842   Sys_UpdateWindows (W_CAMERA);
1843 }
1844
1845 void Select_AllOfType()
1846 {
1847         brush_t *b, *next;
1848         entity_t        *e;
1849   // if no brush selected, we will select based on texture
1850   //   the first selected face's texture if any, or the current texture
1851   // if a brush is selected, we will select entities (first non-worldspawn owner in selected brushes)
1852         if (selected_brushes.next == &selected_brushes)
1853         {
1854
1855     CString strName;
1856     if (g_ptrSelectedFaces.GetSize() == 0)
1857     {
1858       strName = g_qeglobals.d_texturewin.texdef.GetName();
1859     }
1860     else
1861     {
1862       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(0));
1863       strName = selFace->texdef.GetName();
1864     }
1865
1866     Sys_Printf("Selecting all brushes with the texture %s\n", strName.GetBuffer());
1867
1868     Select_Deselect();
1869           for (b=active_brushes.next ; b != &active_brushes ; b=next)
1870     {
1871                   next = b->next;
1872         
1873       if (b->bFiltered)
1874             continue;
1875
1876       if (b->patchBrush)
1877       {
1878         if (strcmpi(strName, b->pPatch->pShader->getName()) == 0)
1879         {
1880                             Brush_RemoveFromList (b);
1881                             Brush_AddToList (b, &selected_brushes);
1882         }
1883       }
1884       else
1885       {
1886         for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next)
1887         {
1888           if (strcmpi(strName, pFace->texdef.GetName()) == 0)
1889           {
1890                               Brush_RemoveFromList (b);
1891                               Brush_AddToList (b, &selected_brushes);
1892           }
1893         }
1894       }
1895     }
1896     Sys_UpdateWindows(W_ALL);
1897     return;
1898   }
1899
1900   
1901   b = selected_brushes.next;
1902         e = b->owner;
1903
1904   if (e != NULL)
1905   {
1906     if (e != world_entity)
1907     {
1908       CString strName = e->eclass->name;
1909       CString strKey, strVal;
1910       bool bCriteria = GetSelectAllCriteria(strKey, strVal);
1911       Sys_Printf("Selecting all %s entities\n", strName.GetBuffer());
1912       Select_Deselect();
1913
1914             for (b=active_brushes.next ; b != &active_brushes ; b=next)
1915         {
1916                     next = b->next;
1917                 
1918                         if (b->bFiltered)
1919                         continue;
1920
1921         e = b->owner;
1922         if (e != NULL)
1923         {
1924           if (strcmpi(e->eclass->name, strName) == 0)
1925           {
1926             bool doIt = true;
1927             if (bCriteria) {
1928               CString str = ValueForKey (e, strKey);
1929               if (str.CompareNoCase(strVal) != 0) {
1930                 doIt = false;
1931               }
1932             }
1933             if (doIt) {
1934                         Brush_RemoveFromList (b);
1935                         Brush_AddToList (b, &selected_brushes);
1936             }
1937           }
1938         }
1939       }
1940     }
1941   }
1942         Sys_UpdateWindows (W_ALL);
1943
1944 }
1945
1946 void Select_Reselect()
1947 {
1948   Select_Brush(selected_brushes.next);  
1949   Sys_UpdateWindows (W_ALL);
1950 }
1951
1952
1953 void Select_FitTexture(int nHeight, int nWidth)
1954 {
1955         brush_t         *b;
1956
1957   int nFaceCount = g_ptrSelectedFaces.GetSize();
1958
1959   if(selected_brushes.next == &selected_brushes && nFaceCount == 0)
1960                 return;
1961
1962   for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
1963         {
1964     Brush_FitTexture(b, nHeight, nWidth);
1965                 Brush_Build(b,true,true,false,false); // don't filter
1966         }
1967
1968         if (nFaceCount > 0)
1969         {
1970     for (int i = 0; i < nFaceCount; i++)
1971     {
1972       face_t *selFace = reinterpret_cast<face_t*>(g_ptrSelectedFaces.GetAt(i));
1973       brush_t *selBrush = reinterpret_cast<brush_t*>(g_ptrSelectedFaceBrushes.GetAt(i));
1974       Face_FitTexture(selFace, nHeight, nWidth);
1975                 Brush_Build(selBrush,true,true,false,false); // don't filter
1976     }
1977         }
1978
1979         Sys_UpdateWindows (W_CAMERA);
1980 }
1981
1982 void Select_Hide()
1983 {
1984   for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
1985   {
1986         b->hiddenBrush = true;
1987     b->bFiltered = true;
1988   }
1989   Sys_UpdateWindows (W_ALL);
1990 }
1991
1992 void Select_ShowAllHidden()
1993 {
1994   brush_t* b;
1995   for (b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
1996   {
1997         if (b->hiddenBrush)
1998         {
1999                 b->hiddenBrush = false;
2000                 b->bFiltered = FilterBrush(b);
2001         }
2002   }
2003   for (b=active_brushes.next ; b && b != &active_brushes ; b=b->next)
2004   {
2005         if (b->hiddenBrush)
2006         {
2007                 b->hiddenBrush = false;
2008                 b->bFiltered = FilterBrush(b);
2009         }
2010   }
2011   Sys_UpdateWindows (W_ALL);
2012 }
2013
2014
2015 /*
2016 ============
2017 Select_Invert
2018 ============
2019 */
2020 void Select_Invert(void)
2021 {
2022   brush_t *next, *prev, *b;
2023
2024   Sys_Printf("inverting selection...\n");
2025
2026   next = active_brushes.next;
2027   prev = active_brushes.prev;
2028   if (selected_brushes.next != &selected_brushes)
2029   {
2030     active_brushes.next = selected_brushes.next;
2031     active_brushes.prev = selected_brushes.prev;
2032     active_brushes.next->prev = &active_brushes;
2033     active_brushes.prev->next = &active_brushes;
2034   }
2035   else
2036   {
2037     active_brushes.next = &active_brushes;
2038     active_brushes.prev = &active_brushes;
2039   }
2040   if (next != &active_brushes)
2041   {
2042     selected_brushes.next = next;
2043     selected_brushes.prev = prev;
2044     selected_brushes.next->prev = &selected_brushes;
2045     selected_brushes.prev->next = &selected_brushes;
2046   }
2047   else
2048   {
2049     selected_brushes.next = &selected_brushes;
2050     selected_brushes.prev = &selected_brushes;
2051   }
2052
2053   // now check if any hidden brush is selected
2054   for (b = selected_brushes.next; b != &selected_brushes; )
2055   {
2056         if (b->patchBrush)
2057                 b->pPatch->bSelected = true;
2058
2059     if (b->bFiltered)
2060     {
2061       brush_t *pb = b;
2062                         b = b->next;
2063       Brush_RemoveFromList (pb);
2064       Brush_AddToList (pb, &active_brushes);
2065     }
2066     else b = b->next;
2067
2068   }
2069   
2070   for (b = active_brushes.next; b != &active_brushes; b = b->next)
2071   {
2072           if (b->patchBrush)
2073     {
2074                   b->pPatch->bSelected = false;
2075     }
2076   }
2077   
2078   // since invert selection only works at the brush level, 
2079   // set g_qeglobals.d_select_mode accordingly
2080   g_qeglobals.d_select_mode = sel_brush;
2081
2082   // since invert selection only works at the brush level,
2083   // set g_qeglobals.d_select_mode accordingly
2084   g_qeglobals.d_select_mode = sel_brush;
2085
2086   Sys_UpdateWindows(W_ALL);
2087
2088   Sys_Printf("done.\n");
2089 }
2090
2091 #ifdef ENABLE_GROUPS
2092 /* 
2093 ===========
2094 Select_Name
2095 ===========
2096 */
2097 void Select_Name(const char *pName)
2098 {
2099         if (g_qeglobals.m_bBrushPrimitMode)
2100   {
2101           for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2102           {
2103       Brush_SetEpair(b, "Name", pName);
2104           }
2105   }
2106 }
2107
2108 /* 
2109 =================
2110 Select_AddToGroup
2111 add selected brushes to a group, update the tree
2112 =================
2113 */
2114 void Select_AddToGroup(const char *pName)
2115 {
2116         if (g_qeglobals.m_bBrushPrimitMode)
2117   {
2118           for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next)
2119           {
2120       Brush_SetEpair(b, "group", pName);
2121                         Group_AddToProperGroup(b);
2122           }
2123   }
2124 }
2125 #endif