]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/brushmanip.cpp
- Updated help menu web links (removed map-center.com, added ETB documentation) ...
[xonotic/netradiant.git] / radiant / brushmanip.cpp
index e9f6ca212ffe9a02e10c82b8fdf4b13cf027a2d1..a19095e1dc93752c7dedff9c915ce16dfd762225 100644 (file)
@@ -37,14 +37,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 #include <list>
 
-
 void Brush_ConstructCuboid(Brush& brush, const AABB& bounds, const char* shader, const TextureProjection& projection)
 {
   const unsigned char box[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };
   Vector3 mins(vector3_subtracted(bounds.origin, bounds.extents));
   Vector3 maxs(vector3_added(bounds.origin, bounds.extents));
 
-  brush.undoSave();
   brush.clear();
   brush.reserve(6);
 
@@ -107,7 +105,6 @@ void Brush_ConstructPrism(Brush& brush, const AABB& bounds, std::size_t sides, i
     return;
   }
 
-  brush.undoSave();
   brush.clear();
   brush.reserve(sides+2);
 
@@ -180,7 +177,6 @@ void Brush_ConstructCone(Brush& brush, const AABB& bounds, std::size_t sides, co
     return;
   }
 
-  brush.undoSave();
   brush.clear();
   brush.reserve(sides+1);
 
@@ -235,7 +231,6 @@ void Brush_ConstructSphere(Brush& brush, const AABB& bounds, std::size_t sides,
     return;
   }
 
-  brush.undoSave();
   brush.clear();
   brush.reserve(sides*sides);
 
@@ -362,168 +357,14 @@ void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins,
 }
 
 
-class BrushForEachFace
-{
-  const BrushInstanceVisitor& m_visitor;
-public:
-  BrushForEachFace(const BrushInstanceVisitor& visitor) : m_visitor(visitor)
-  {
-  }
-  void operator()(BrushInstance& brush) const
-  {
-    brush.forEachFaceInstance(m_visitor);
-  }
-};
-
-template<class Visitor>
-class FaceVisitAll : public BrushInstanceVisitor
-{
-  const Visitor& m_visitor;
-public:
-  FaceVisitAll(const Visitor& visitor)
-    : m_visitor(visitor)
-  {
-  }
-  void visit(FaceInstance& face) const
-  {
-    m_visitor.visit(face.getFace());
-  }
-};
-
-template<class Visitor>
-class FaceInstanceVisitAll : public BrushInstanceVisitor
-{
-  const Visitor& m_visitor;
-public:
-  FaceInstanceVisitAll(const Visitor& visitor)
-    : m_visitor(visitor)
-  {
-  }
-  void visit(FaceInstance& face) const
-  {
-    m_visitor.visit(face);
-  }
-};
-
-#if 0
-template<class Visitor>
-class FaceVisitSelected : public BrushInstanceVisitor
-{
-  const Visitor& m_visitor;
-public:
-  FaceVisitSelected(const Visitor& visitor)
-    : m_visitor(visitor)
-  {
-  }
-  void visit(FaceInstance& face) const
-  {
-    if(face.isSelected(SelectionSystem::eFace))
-    {
-      m_visitor.visit(face.getFace());
-    }
-  }
-};
-#endif
-
-template<typename Functor>
-inline void Scene_forEachBrush(scene::Graph& graph, const Functor& functor)
-{
-  graph.traverse(InstanceWalker< InstanceApply<BrushInstance, Functor> >(functor));
-}
-
-template<typename Type, typename Functor>
-class InstanceIfVisible : public Functor
-{
-public:
-  InstanceIfVisible(const Functor& functor) : Functor(functor)
-  {
-  }
-  void operator()(scene::Instance& instance)
-  {
-    if(instance.path().top().get().visible())
-    {
-      Functor::operator()(instance);
-    }
-  }
-};
-
-template<typename Functor>
-class BrushVisibleWalker : public scene::Graph::Walker
-{
-  const Functor& m_functor;
-public:
-  BrushVisibleWalker(const Functor& functor) : m_functor(functor)
-  {
-  }
-  bool pre(const scene::Path& path, scene::Instance& instance) const
-  {
-    if(path.top().get().visible())
-    {
-      BrushInstance* brush = Instance_getBrush(instance);
-      if(brush != 0)
-      {
-        m_functor(*brush);
-      }
-    }
-    return true;
-  }
-};
-
-template<typename Functor>
-inline void Scene_forEachVisibleBrush(scene::Graph& graph, const Functor& functor)
-{
-  graph.traverse(BrushVisibleWalker<Functor>(functor));
-}
-
-template<typename Visitor>
-inline void Scene_ForEachBrush_ForEachFace(scene::Graph& graph, const Visitor& visitor)
-{
-  Scene_forEachBrush(graph, BrushForEachFace(FaceVisitAll<Visitor>(visitor)));
-}
-
-template<typename Visitor>
-inline void Scene_ForEachSelectedBrush_ForEachFace(scene::Graph& graph, const Visitor& visitor)
-{
-  Scene_forEachSelectedBrush(BrushForEachFace(FaceVisitAll<Visitor>(visitor)));
-}
-
-template<typename Visitor>
-inline void Scene_ForEachSelectedBrush_ForEachFaceInstance(scene::Graph& graph, const Visitor& visitor)
-{
-  Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitAll<Visitor>(visitor)));
-}
-
-template<typename Visitor>
-class FaceVisitorWrapper
-{
-  Visitor& m_visitor;
-public:
-  FaceVisitorWrapper(Visitor& visitor) : m_visitor(visitor)
-  {
-  }
-
-  void operator()(FaceInstance& faceInstance)
-  {
-    m_visitor.visit(faceInstance.getFace());
-  }
-};
-
-template<typename Visitor>
-inline void Scene_ForEachSelectedBrushFace(scene::Graph& graph, Visitor& faceVisitor)
-{
-  g_SelectedFaceInstances.foreach(FaceVisitorWrapper<Visitor>(faceVisitor));
-}
-
-
-
-class FaceSetTexdefVisitor
+class FaceSetTexdef
 {
   const TextureProjection& m_projection;
 public:
-  FaceSetTexdefVisitor(const TextureProjection& projection) : m_projection(projection)
+  FaceSetTexdef(const TextureProjection& projection) : m_projection(projection)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.SetTexdef(m_projection);
   }
@@ -531,26 +372,25 @@ public:
 
 void Scene_BrushSetTexdef_Selected(scene::Graph& graph, const TextureProjection& projection)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetTexdefVisitor(projection));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetTexdef(projection));
   SceneChangeNotify();
 }
 
 void Scene_BrushSetTexdef_Component_Selected(scene::Graph& graph, const TextureProjection& projection)
 {
-  FaceSetTexdefVisitor visitor(projection);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceSetTexdef(projection));
   SceneChangeNotify();
 }
 
 
-class FaceSetFlagsVisitor
+class FaceSetFlags
 {
   const ContentsFlagsValue& m_projection;
 public:
-  FaceSetFlagsVisitor(const ContentsFlagsValue& flags) : m_projection(flags)
+  FaceSetFlags(const ContentsFlagsValue& flags) : m_projection(flags)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.SetFlags(m_projection);
   }
@@ -558,25 +398,24 @@ public:
 
 void Scene_BrushSetFlags_Selected(scene::Graph& graph, const ContentsFlagsValue& flags)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetFlagsVisitor(flags));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetFlags(flags));
   SceneChangeNotify();
 }
 
 void Scene_BrushSetFlags_Component_Selected(scene::Graph& graph, const ContentsFlagsValue& flags)
 {
-  FaceSetFlagsVisitor visitor(flags);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceSetFlags(flags));
   SceneChangeNotify();
 }
 
-class FaceShiftTexdefVisitor
+class FaceShiftTexdef
 {
   float m_s, m_t;
 public:
-  FaceShiftTexdefVisitor(float s, float t) : m_s(s), m_t(t)
+  FaceShiftTexdef(float s, float t) : m_s(s), m_t(t)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.ShiftTexdef(m_s, m_t);
   }
@@ -584,25 +423,24 @@ public:
 
 void Scene_BrushShiftTexdef_Selected(scene::Graph& graph, float s, float t)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceShiftTexdefVisitor(s, t));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceShiftTexdef(s, t));
   SceneChangeNotify();
 }
 
 void Scene_BrushShiftTexdef_Component_Selected(scene::Graph& graph, float s, float t)
 {
-  FaceShiftTexdefVisitor visitor(s, t);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceShiftTexdef(s, t));
   SceneChangeNotify();
 }
 
-class FaceScaleTexdefVisitor
+class FaceScaleTexdef
 {
   float m_s, m_t;
 public:
-  FaceScaleTexdefVisitor(float s, float t) : m_s(s), m_t(t)
+  FaceScaleTexdef(float s, float t) : m_s(s), m_t(t)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.ScaleTexdef(m_s, m_t);
   }
@@ -610,25 +448,24 @@ public:
 
 void Scene_BrushScaleTexdef_Selected(scene::Graph& graph, float s, float t)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceScaleTexdefVisitor(s, t));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceScaleTexdef(s, t));
   SceneChangeNotify();
 }
 
 void Scene_BrushScaleTexdef_Component_Selected(scene::Graph& graph, float s, float t)
 {
-  FaceScaleTexdefVisitor visitor(s, t);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceScaleTexdef(s, t));
   SceneChangeNotify();
 }
 
-class FaceRotateTexdefVisitor
+class FaceRotateTexdef
 {
   float m_angle;
 public:
-  FaceRotateTexdefVisitor(float angle) : m_angle(angle)
+  FaceRotateTexdef(float angle) : m_angle(angle)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.RotateTexdef(m_angle);
   }
@@ -636,24 +473,23 @@ public:
 
 void Scene_BrushRotateTexdef_Selected(scene::Graph& graph, float angle)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceRotateTexdefVisitor(angle));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceRotateTexdef(angle));
   SceneChangeNotify();
 }
 
 void Scene_BrushRotateTexdef_Component_Selected(scene::Graph& graph, float angle)
 {
-  FaceRotateTexdefVisitor visitor(angle);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceRotateTexdef(angle));
   SceneChangeNotify();
 }
 
 
-class FaceSetShaderVisitor
+class FaceSetShader
 {
   const char* m_name;
 public:
-  FaceSetShaderVisitor(const char* name) : m_name(name) {}
-  void visit(Face& face) const
+  FaceSetShader(const char* name) : m_name(name) {}
+  void operator()(Face& face) const
   {
     face.SetShader(m_name);
   }
@@ -661,25 +497,24 @@ public:
 
 void Scene_BrushSetShader_Selected(scene::Graph& graph, const char* name)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetShaderVisitor(name));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetShader(name));
   SceneChangeNotify();
 }
 
 void Scene_BrushSetShader_Component_Selected(scene::Graph& graph, const char* name)
 {
-  FaceSetShaderVisitor visitor(name);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceSetShader(name));
   SceneChangeNotify();
 }
 
-class FaceSetDetailVisitor
+class FaceSetDetail
 {
   bool m_detail;
 public:
-  FaceSetDetailVisitor(bool detail) : m_detail(detail)
+  FaceSetDetail(bool detail) : m_detail(detail)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.setDetail(m_detail);
   }
@@ -687,7 +522,7 @@ public:
 
 void Scene_BrushSetDetail_Selected(scene::Graph& graph, bool detail)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetDetailVisitor(detail));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceSetDetail(detail));
   SceneChangeNotify();
 }
 
@@ -701,15 +536,15 @@ bool Face_FindReplaceShader(Face& face, const char* find, const char* replace)
   return false;
 }
 
-class FaceFindReplaceShaderVisitor
+class FaceFindReplaceShader
 {
   const char* m_find;
   const char* m_replace;
 public:
-  FaceFindReplaceShaderVisitor(const char* find, const char* replace) : m_find(find), m_replace(replace)
+  FaceFindReplaceShader(const char* find, const char* replace) : m_find(find), m_replace(replace)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     Face_FindReplaceShader(face, m_find, m_replace);
   }
@@ -717,29 +552,28 @@ public:
 
 void Scene_BrushFindReplaceShader(scene::Graph& graph, const char* find, const char* replace)
 {
-  Scene_ForEachBrush_ForEachFace(graph, FaceFindReplaceShaderVisitor(find, replace));
+  Scene_ForEachBrush_ForEachFace(graph, FaceFindReplaceShader(find, replace));
 }
 
 void Scene_BrushFindReplaceShader_Selected(scene::Graph& graph, const char* find, const char* replace)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceFindReplaceShaderVisitor(find, replace));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceFindReplaceShader(find, replace));
 }
 
 void Scene_BrushFindReplaceShader_Component_Selected(scene::Graph& graph, const char* find, const char* replace)
 {
-  FaceFindReplaceShaderVisitor visitor(find, replace);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceFindReplaceShader(find, replace));
 }
 
 
-class FaceFitTextureVisitor
+class FaceFitTexture
 {
   float m_s_repeat, m_t_repeat;
 public:
-  FaceFitTextureVisitor(float s_repeat, float t_repeat) : m_s_repeat(s_repeat), m_t_repeat(t_repeat)
+  FaceFitTexture(float s_repeat, float t_repeat) : m_s_repeat(s_repeat), m_t_repeat(t_repeat)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     face.FitTexture(m_s_repeat, m_t_repeat);
   }
@@ -747,17 +581,22 @@ public:
 
 void Scene_BrushFitTexture_Selected(scene::Graph& graph, float s_repeat, float t_repeat)
 {
-  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceFitTextureVisitor(s_repeat, t_repeat));
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceFitTexture(s_repeat, t_repeat));
   SceneChangeNotify();
 }
 
 void Scene_BrushFitTexture_Component_Selected(scene::Graph& graph, float s_repeat, float t_repeat)
 {
-  FaceFitTextureVisitor visitor(s_repeat, t_repeat);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceFitTexture(s_repeat, t_repeat));
   SceneChangeNotify();
 }
 
+TextureProjection g_defaultTextureProjection;
+const TextureProjection& TextureTransform_getDefault()
+{
+  TexDef_Construct_Default(g_defaultTextureProjection);
+  return g_defaultTextureProjection;
+}
 
 void Scene_BrushConstructPrefab(scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader)
 {
@@ -768,10 +607,8 @@ void Scene_BrushConstructPrefab(scene::Graph& graph, EBrushPrefab type, std::siz
     Brush* brush = Node_getBrush(path.top());
     if(brush != 0)
     {
-      AABB bounds = brush->localAABB();
-      TextureProjection projection;
-      TexDef_Construct_Default(projection);
-      Brush_ConstructPrefab(*brush, type, bounds, sides, shader, projection);
+      AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
+      Brush_ConstructPrefab(*brush, type, bounds, sides, shader, TextureTransform_getDefault());
       SceneChangeNotify();
     }
   }
@@ -786,9 +623,7 @@ void Scene_BrushResize_Selected(scene::Graph& graph, const AABB& bounds, const c
     Brush* brush = Node_getBrush(path.top());
     if(brush != 0)
     {
-      TextureProjection projection;
-      TexDef_Construct_Default(projection);
-      Brush_ConstructCuboid(*brush, bounds, shader, projection);
+      Brush_ConstructCuboid(*brush, bounds, shader, TextureTransform_getDefault());
       SceneChangeNotify();
     }
   }
@@ -833,15 +668,15 @@ void Scene_BrushSelectByShader(scene::Graph& graph, const char* name)
   graph.traverse(BrushSelectByShaderWalker(name));
 }
 
-class FaceSelectByShaderVisitor : public BrushInstanceVisitor
+class FaceSelectByShader
 {
   const char* m_name;
 public:
-  FaceSelectByShaderVisitor(const char* name)
+  FaceSelectByShader(const char* name)
     : m_name(name)
   {
   }
-  void visit(FaceInstance& face) const
+  void operator()(FaceInstance& face) const
   {
     if(shader_equal(face.getFace().GetShader(), m_name))
     {
@@ -852,24 +687,24 @@ public:
 
 void Scene_BrushSelectByShader_Component(scene::Graph& graph, const char* name)
 {
-  Scene_ForEachSelectedBrush_ForEachFaceInstance(graph, FaceSelectByShaderVisitor(name));
+  Scene_ForEachSelectedBrush_ForEachFaceInstance(graph, FaceSelectByShader(name));
 }
 
-class FaceGetTexdefVisitor
+class FaceGetTexdef
 {
   TextureProjection& m_projection;
   mutable bool m_done;
 public:
-  FaceGetTexdefVisitor(TextureProjection& projection)
+  FaceGetTexdef(TextureProjection& projection)
     : m_projection(projection), m_done(false)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     if(!m_done)
     {
       m_done = true;
-      FaceTexdef_getTexdef(face.getTexdef(), m_projection);
+      face.GetTexdef(m_projection);
     }
   }
 };
@@ -877,8 +712,7 @@ public:
 
 void Scene_BrushGetTexdef_Selected(scene::Graph& graph, TextureProjection& projection)
 {
-  FaceGetTexdefVisitor visitor(projection);
-  Scene_ForEachSelectedBrush_ForEachFace(graph, visitor);
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceGetTexdef(projection));
 }
 
 void Scene_BrushGetTexdef_Component_Selected(scene::Graph& graph, TextureProjection& projection)
@@ -887,30 +721,30 @@ void Scene_BrushGetTexdef_Component_Selected(scene::Graph& graph, TextureProject
   if(!g_SelectedFaceInstances.empty())
   {
     FaceInstance& faceInstance = g_SelectedFaceInstances.last();
-    FaceTexdef_getTexdef(faceInstance.getFace().getTexdef(), projection);
+    faceInstance.getFace().GetTexdef(projection);
   }
 #else
-  FaceGetTexdefVisitor visitor(projection);
+  FaceGetTexdef visitor(projection);
   Scene_ForEachSelectedBrushFace(graph, visitor);
 #endif
 }
 
 
-class FaceGetFlagsVisitor
+class FaceGetFlags
 {
   ContentsFlagsValue& m_flags;
   mutable bool m_done;
 public:
-  FaceGetFlagsVisitor(ContentsFlagsValue& flags)
+  FaceGetFlags(ContentsFlagsValue& flags)
     : m_flags(flags), m_done(false)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     if(!m_done)
     {
       m_done = true;
-      FaceShader_getFlags(face.getShader(), m_flags);
+      face.GetFlags(m_flags);
     }
   }
 };
@@ -918,8 +752,18 @@ public:
 
 void Scene_BrushGetFlags_Selected(scene::Graph& graph, ContentsFlagsValue& flags)
 {
-  FaceGetFlagsVisitor visitor(flags);
-  Scene_ForEachSelectedBrush_ForEachFace(graph, visitor);
+#if 1
+  if(GlobalSelectionSystem().countSelected() != 0)
+  {
+    BrushInstance* brush = Instance_getBrush(GlobalSelectionSystem().ultimateSelected());
+    if(brush != 0)
+    {
+      Brush_forEachFace(*brush, FaceGetFlags(flags));
+    }
+  }
+#else
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceGetFlags(flags));
+#endif
 }
 
 void Scene_BrushGetFlags_Component_Selected(scene::Graph& graph, ContentsFlagsValue& flags)
@@ -928,38 +772,47 @@ void Scene_BrushGetFlags_Component_Selected(scene::Graph& graph, ContentsFlagsVa
   if(!g_SelectedFaceInstances.empty())
   {
     FaceInstance& faceInstance = g_SelectedFaceInstances.last();
-    FaceShader_getFlags(faceInstance.getFace().getShader(), flags);
+    faceInstance.getFace().GetFlags(flags);
   }
 #else
-  FaceGetFlagsVisitor visitor(flags);
-  Scene_ForEachSelectedBrushFace(graph, visitor);
+  Scene_ForEachSelectedBrushFace(graph, FaceGetFlags(flags));
 #endif
 }
 
 
-class FaceGetShaderVisitor
+class FaceGetShader
 {
   CopiedString& m_shader;
   mutable bool m_done;
 public:
-  FaceGetShaderVisitor(CopiedString& shader)
+  FaceGetShader(CopiedString& shader)
     : m_shader(shader), m_done(false)
   {
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     if(!m_done)
     {
       m_done = true;
-      m_shader = face.getShader().getShader();
+      m_shader = face.GetShader();
     }
   }
 };
 
 void Scene_BrushGetShader_Selected(scene::Graph& graph, CopiedString& shader)
 {
-  FaceGetShaderVisitor visitor(shader);
-  Scene_ForEachSelectedBrush_ForEachFace(graph, visitor);
+#if 1
+  if(GlobalSelectionSystem().countSelected() != 0)
+  {
+    BrushInstance* brush = Instance_getBrush(GlobalSelectionSystem().ultimateSelected());
+    if(brush != 0)
+    {
+      Brush_forEachFace(*brush, FaceGetShader(shader));
+    }
+  }
+#else
+  Scene_ForEachSelectedBrush_ForEachFace(graph, FaceGetShader(shader));
+#endif
 }
 
 void Scene_BrushGetShader_Component_Selected(scene::Graph& graph, CopiedString& shader)
@@ -968,10 +821,10 @@ void Scene_BrushGetShader_Component_Selected(scene::Graph& graph, CopiedString&
   if(!g_SelectedFaceInstances.empty())
   {
     FaceInstance& faceInstance = g_SelectedFaceInstances.last();
-    shader = faceInstance.getFace().getShader().getShader();
+    shader = faceInstance.getFace().GetShader();
   }
 #else
-  FaceGetShaderVisitor visitor(shader);
+  FaceGetShader visitor(shader);
   Scene_ForEachSelectedBrushFace(graph, visitor);
 #endif
 }
@@ -990,16 +843,16 @@ public:
   }
 };
 
-class filter_face_shader_substring : public FaceFilter
+class filter_face_shader_prefix : public FaceFilter
 {
-  const char* m_shader;
+  const char* m_prefix;
 public:
-  filter_face_shader_substring(const char* shader) : m_shader(shader)
+  filter_face_shader_prefix(const char* prefix) : m_prefix(prefix)
   {
   }
   bool filter(const Face& face) const
   {
-    return shader_equal_n(face.GetShader(), m_shader, strlen(m_shader));
+    return shader_equal_n(face.GetShader(), m_prefix, strlen(m_prefix));
   }
 };
 
@@ -1031,16 +884,16 @@ public:
 
 
 
-class FaceFilterAnyVisitor : public BrushVisitor
+class FaceFilterAny
 {
   FaceFilter* m_filter;
   bool& m_filtered;
 public:
-  FaceFilterAnyVisitor(FaceFilter* filter, bool& filtered) : m_filter(filter), m_filtered(filtered)
+  FaceFilterAny(FaceFilter* filter, bool& filtered) : m_filter(filter), m_filtered(filtered)
   {
     m_filtered = false;
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     if(m_filter->filter(face))
     {
@@ -1059,21 +912,21 @@ public:
   bool filter(const Brush& brush) const
   {
     bool filtered;
-    brush.forEachFace(FaceFilterAnyVisitor(m_filter, filtered));
+    Brush_forEachFace(brush, FaceFilterAny(m_filter, filtered));
     return filtered;
   }   
 };
 
-class FaceFilterAllVisitor : public BrushVisitor
+class FaceFilterAll
 {
   FaceFilter* m_filter;
   bool& m_filtered;
 public:
-  FaceFilterAllVisitor(FaceFilter* filter, bool& filtered) : m_filter(filter), m_filtered(filtered)
+  FaceFilterAll(FaceFilter* filter, bool& filtered) : m_filter(filter), m_filtered(filtered)
   {
     m_filtered = true;
   }
-  void visit(Face& face) const
+  void operator()(Face& face) const
   {
     if(!m_filter->filter(face))
     {
@@ -1092,7 +945,7 @@ public:
   bool filter(const Brush& brush) const
   {
     bool filtered;
-    brush.forEachFace(FaceFilterAllVisitor(m_filter, filtered));
+    Brush_forEachFace(brush, FaceFilterAll(m_filter, filtered));
     return filtered;
   }   
 };
@@ -1110,13 +963,13 @@ filter_brush_all_faces g_filter_brush_weapclip(&g_filter_face_weapclip);
 filter_face_shader g_filter_face_botclip("textures/common/botclip");
 filter_brush_all_faces g_filter_brush_botclip(&g_filter_face_botclip);
 
-filter_face_shader g_filter_face_caulk("textures/common/caulk");
+filter_face_shader_prefix g_filter_face_caulk("textures/common/caulk");
 filter_brush_all_faces g_filter_brush_caulk(&g_filter_face_caulk);
 
-filter_face_shader g_filter_face_caulk_ja("textures/system/caulk");
+filter_face_shader_prefix g_filter_face_caulk_ja("textures/system/caulk");
 filter_brush_all_faces g_filter_brush_caulk_ja(&g_filter_face_caulk_ja);
 
-filter_face_shader_substring g_filter_face_liquids("textures/liquids/");
+filter_face_shader_prefix g_filter_face_liquids("textures/liquids/");
 filter_brush_any_face g_filter_brush_liquids(&g_filter_face_liquids);
 
 filter_face_shader g_filter_face_hint("textures/common/hint");
@@ -1143,7 +996,7 @@ filter_brush_all_faces g_filter_brush_lightgrid(&g_filter_face_lightgrid);
 filter_face_flags g_filter_face_translucent(QER_TRANS);
 filter_brush_all_faces g_filter_brush_translucent(&g_filter_face_translucent);
 
-filter_face_contents g_filter_face_detail(CONTENTS_DETAIL);
+filter_face_contents g_filter_face_detail(BRUSH_DETAIL_MASK);
 filter_brush_all_faces g_filter_brush_detail(&g_filter_face_detail);
 
 
@@ -1155,6 +1008,8 @@ void BrushFilters_construct()
   add_brush_filter(g_filter_brush_botclip, EXCLUDE_BOTCLIP);
   add_brush_filter(g_filter_brush_caulk, EXCLUDE_CAULK);
   add_brush_filter(g_filter_brush_caulk_ja, EXCLUDE_CAULK);
+  add_face_filter(g_filter_face_caulk, EXCLUDE_CAULK);
+  add_face_filter(g_filter_face_caulk_ja, EXCLUDE_CAULK);
   add_brush_filter(g_filter_brush_liquids, EXCLUDE_LIQUIDS);
   add_brush_filter(g_filter_brush_hint, EXCLUDE_HINTSSKIPS);
   add_brush_filter(g_filter_brush_hint_q2, EXCLUDE_HINTSSKIPS);
@@ -1509,193 +1364,6 @@ void Texdef_ToggleMoveLock()
 
 
 
-void Face_getClosest(Face& face, SelectionTest& test, SelectionIntersection& bestIntersection, Face*& closestFace)
-{
-  SelectionIntersection intersection;
-  face.testSelect(test, intersection);
-  if(intersection.valid()
-    && SelectionIntersection_closer(intersection, bestIntersection))
-  {
-    bestIntersection = intersection;
-    closestFace = &face;
-  }
-}
-
-
-class OccludeSelector : public Selector
-{
-  SelectionIntersection& m_bestIntersection;
-  bool& m_occluded;
-public:
-  OccludeSelector(SelectionIntersection& bestIntersection, bool& occluded) : m_bestIntersection(bestIntersection), m_occluded(occluded)
-  {
-    m_occluded = false;
-  }
-  void pushSelectable(Selectable& selectable)
-  {
-  }
-  void popSelectable()
-  {
-  }
-  void addIntersection(const SelectionIntersection& intersection)
-  {
-    if(SelectionIntersection_closer(intersection, m_bestIntersection))
-    {
-      m_bestIntersection = intersection;
-      m_occluded = true;
-    }
-  }
-};
-
-class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker
-{
-  SelectionTest& m_test;
-  Face*& m_closestFace;
-  mutable SelectionIntersection m_bestIntersection;
-public:
-  BrushGetClosestFaceVisibleWalker(SelectionTest& test, Face*& closestFace) : m_test(test), m_closestFace(closestFace)
-  {
-  }
-  bool pre(const scene::Path& path, scene::Instance& instance) const
-  {
-    if(path.top().get().visible())
-    {
-      BrushInstance* brush = Instance_getBrush(instance);
-      if(brush != 0)
-      {
-        m_test.BeginMesh(brush->localToWorld());
-
-        for(Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i)
-        {
-          Face_getClosest(*(*i), m_test, m_bestIntersection, m_closestFace);
-        }
-      }
-      else
-      {
-        SelectionTestable* selectionTestable = Instance_getSelectionTestable(instance);
-        if(selectionTestable)
-        {
-          bool occluded;
-          OccludeSelector selector(m_bestIntersection, occluded);
-          selectionTestable->testSelect(selector, m_test);
-          if(occluded)
-          {
-            m_closestFace = 0;
-          }
-        }
-      }
-    }
-    return true;
-  }
-};
-
-Face* Scene_BrushGetClosestFace(scene::Graph& graph, SelectionTest& test)
-{
-  Face* closestFace = 0;
-  graph.traverse(BrushGetClosestFaceVisibleWalker(test, closestFace));
-  return closestFace;
-}
-
-bool Scene_BrushGetClosestFaceTexture(scene::Graph& graph, SelectionTest& test, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags)
-{
-  Face* face = Scene_BrushGetClosestFace(graph, test);
-  if(face != 0)
-  {
-    shader = face->GetShader();
-    FaceTexdef_getTexdef(face->getTexdef(), projection);
-    flags = face->getShader().m_flags;
-    return true;
-  }
-  return false;
-}
-
-void Scene_BrushSetClosestFaceTexture(scene::Graph& graph, SelectionTest& test, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags)
-{
-  Face* face = Scene_BrushGetClosestFace(graph, test);
-  if(face != 0)
-  {
-    face->SetShader(shader);
-    face->SetTexdef(projection);
-    face->SetFlags(flags);
-  }
-}
-
-
-class FaceTexture
-{
-public:
-  TextureProjection m_projection;
-  ContentsFlagsValue m_flags;
-};
-
-FaceTexture g_faceTextureClipboard;
-
-void FaceTextureClipboard_setDefault()
-{
-  g_faceTextureClipboard.m_flags = ContentsFlagsValue(0, 0, 0, false);
-  TexDef_Construct_Default(g_faceTextureClipboard.m_projection);
-}
-
-void TextureClipboard_textureSelected(const char* shader)
-{
-  FaceTextureClipboard_setDefault();
-}
-
-class TextureBrowser;
-extern TextureBrowser g_TextureBrowser;
-void TextureBrowser_SetSelectedShader(TextureBrowser& textureBrowser, const char* shader);
-const char* TextureBrowser_GetSelectedShader(TextureBrowser& textureBrowser);
-
-void Scene_copyClosestFaceTexture(SelectionTest& test)
-{
-  CopiedString shader;
-  if(Scene_BrushGetClosestFaceTexture(GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags))
-  {
-    TextureBrowser_SetSelectedShader(g_TextureBrowser, shader.c_str());
-  }
-}
-
-void Scene_applyClosestFaceTexture(SelectionTest& test)
-{
-  UndoableCommand command("facePaintTexture");
-
-  Scene_BrushSetClosestFaceTexture(GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader(g_TextureBrowser), g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags);
-
-  SceneChangeNotify();
-}
-
-
-
-void SelectedFaces_copyTexture()
-{
-  if(!g_SelectedFaceInstances.empty())
-  {
-    Face& face = g_SelectedFaceInstances.last().getFace();
-    FaceTexdef_getTexdef(face.getTexdef(), g_faceTextureClipboard.m_projection);
-    g_faceTextureClipboard.m_flags = face.getShader().m_flags;
-
-    TextureBrowser_SetSelectedShader(g_TextureBrowser, face.getShader().getShader());
-  }
-}
-
-void FaceInstance_pasteTexture(FaceInstance& faceInstance)
-{
-  faceInstance.getFace().SetTexdef(g_faceTextureClipboard.m_projection);
-  faceInstance.getFace().SetShader(TextureBrowser_GetSelectedShader(g_TextureBrowser));
-  faceInstance.getFace().SetFlags(g_faceTextureClipboard.m_flags);
-  SceneChangeNotify();
-}
-
-bool SelectedFaces_empty()
-{
-  return g_SelectedFaceInstances.empty();
-}
-
-void SelectedFaces_pasteTexture()
-{
-  UndoableCommand command("facePasteTexture");
-  g_SelectedFaceInstances.foreach(FaceInstance_pasteTexture);
-}
 
 void Brush_registerCommands()
 {
@@ -1717,9 +1385,6 @@ void Brush_registerCommands()
   GlobalCommands_insert("SplitSelected", FreeCaller<SplitSelected>(), Accelerator(GDK_Return, (GdkModifierType)GDK_SHIFT_MASK));
   GlobalCommands_insert("FlipClip", FreeCaller<FlipClipper>(), Accelerator(GDK_Return, (GdkModifierType)GDK_CONTROL_MASK));
 
-  GlobalCommands_insert("FaceCopyTexture", FreeCaller<SelectedFaces_copyTexture>());
-  GlobalCommands_insert("FacePasteTexture", FreeCaller<SelectedFaces_pasteTexture>());
-
   GlobalCommands_insert("MakeDetail", FreeCaller<Select_MakeDetail>(), Accelerator('M', (GdkModifierType)GDK_CONTROL_MASK));
   GlobalCommands_insert("MakeStructural", FreeCaller<Select_MakeStructural>(), Accelerator('S', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
 }
@@ -1732,6 +1397,8 @@ void Brush_constructMenu(GtkMenu* menu)
   menu_separator (menu);
   {
     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "CSG");
+    if (g_Layout_enableDetachableMenus.m_value)
+      menu_tearoff (menu_in_menu);
     create_menu_item_with_mnemonic(menu_in_menu, "Make _Hollow", "CSGHollow");
     create_menu_item_with_mnemonic(menu_in_menu, "CSG _Subtract", "CSGSubtract");
     create_menu_item_with_mnemonic(menu_in_menu, "CSG _Merge", "CSGMerge");
@@ -1739,6 +1406,8 @@ void Brush_constructMenu(GtkMenu* menu)
   menu_separator(menu);
   {
     GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic (menu, "Clipper");
+    if (g_Layout_enableDetachableMenus.m_value)
+      menu_tearoff (menu_in_menu);
 
     create_menu_item_with_mnemonic(menu_in_menu, "Clip selection", "ClipSelected");
     create_menu_item_with_mnemonic(menu_in_menu, "Split selection", "SplitSelected");