1 /*
2  Copyright (C) 2001-2006, William Joseph.
3  All Rights Reserved.
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 #if !defined (INCLUDED_SELECTIONLIB_H)
23 #define INCLUDED_SELECTIONLIB_H
24 
25 #include "iselection.h"
26 #include "selectable.h"
27 #include "generic/callback.h"
28 #include "scenelib.h"
29 #include <stdlib.h>
30 
31 /** greebo: A structure containing information about the current
32  * Selection. An instance of this is maintained by the
33  * RadiantSelectionSystem, and a const reference can be
34  * retrieved via the according getSelectionInfo() method.
35  */
36 class SelectionInfo {
37 
38 	public:
39 		int totalCount; // number of selected items
40 		int brushCount; // -- " -- brushes
41 		int entityCount; // -- " -- entities
42 
SelectionInfo()43 		SelectionInfo () :
44 			totalCount(0),
45 			brushCount(0),
46 			entityCount(0)
47 		{
48 		}
49 
50 		// Zeroes all the counters
clear()51 		void clear ()
52 		{
53 			totalCount = 0;
54 			brushCount = 0;
55 			entityCount = 0;
56 		}
57 };
58 
59 
60 namespace selection
61 {
62 
63 /**
64  * The selection "WorkZone" defines the bounds of the most
65  * recent selection. On each selection, the workzone is
66  * recalculated, nothing happens on deselection.
67  */
68 struct WorkZone
69 {
70 	// The corner points defining the selection workzone
71 	Vector3 min;
72 	Vector3 max;
73 
74 	// The bounds of the selection workzone (equivalent to min/max)
75 	AABB bounds;
76 
WorkZoneWorkZone77 	WorkZone() :
78 		min(-64,-64,-64),
79 		max(64,64,64),
80 		bounds(AABB::createFromMinMax(min, max))
81 	{}
82 };
83 
84 } // namespace selection
85 
86 class SelectableBool: public Selectable
87 {
88 		bool m_selected;
89 	public:
SelectableBool()90 		SelectableBool () :
91 			m_selected(false)
92 		{
93 		}
94 
95 		void setSelected (bool select = true)
96 		{
97 			m_selected = select;
98 		}
isSelected()99 		bool isSelected () const
100 		{
101 			return m_selected;
102 		}
103 
invertSelected()104 		void invertSelected() {
105 			m_selected = !m_selected;
106 		}
107 };
108 
109 // greebo: A selectable with an attached callback function that is called upon selection (change)
110 class ObservedSelectable: public Selectable
111 {
112 		SelectionChangeCallback m_onchanged;
113 		bool m_selected;
114 	public:
ObservedSelectable(const SelectionChangeCallback & onchanged)115 		ObservedSelectable (const SelectionChangeCallback& onchanged) :
116 			m_onchanged(onchanged), m_selected(false)
117 		{
118 		}
ObservedSelectable(const ObservedSelectable & other)119 		ObservedSelectable (const ObservedSelectable& other) :
120 			Selectable(other), m_onchanged(other.m_onchanged), m_selected(false)
121 		{
122 			setSelected(other.isSelected());
123 		}
124 		ObservedSelectable& operator= (const ObservedSelectable& other)
125 		{
126 			setSelected(other.isSelected());
127 			return *this;
128 		}
~ObservedSelectable()129 		~ObservedSelectable ()
130 		{
131 			setSelected(false);
132 		}
133 
setSelected(bool select)134 		void setSelected (bool select)
135 		{
136 			if (select ^ m_selected) {
137 				m_selected = select;
138 
139 				m_onchanged(*this);
140 			}
141 		}
isSelected()142 		bool isSelected () const
143 		{
144 			return m_selected;
145 		}
146 
invertSelected()147 		void invertSelected() {
148 			setSelected(!isSelected());
149 		}
150 };
151 
152 class SelectableInstance: public scene::Instance, public Selectable
153 {
154 		ObservedSelectable m_selectable;
155 	public:
156 
SelectableInstance(const scene::Path & path,scene::Instance * parent)157 		SelectableInstance (const scene::Path& path, scene::Instance* parent) :
158 			Instance(path, parent), m_selectable(SelectedChangedCaller(*this))
159 		{
160 		}
161 
getSelectable()162 		Selectable& getSelectable ()
163 		{
164 			return m_selectable;
165 		}
getSelectable()166 		const Selectable& getSelectable () const
167 		{
168 			return m_selectable;
169 		}
170 
selectedChanged(const Selectable & selectable)171 		void selectedChanged (const Selectable& selectable)
172 		{
173 			GlobalSelectionSystem().onSelectedChanged(*this, selectable);
174 
175 			Instance::selectedChanged();
176 		}
177 		typedef MemberCaller1<SelectableInstance, const Selectable&, &SelectableInstance::selectedChanged>
178 				SelectedChangedCaller;
179 
180 		/** greebo: Selectable implementation
181 		 */
setSelected(bool select)182 		virtual void setSelected(bool select) {
183 			m_selectable.setSelected(select);
184 		}
185 
isSelected()186 		virtual bool isSelected() const {
187 			return m_selectable.isSelected();
188 		}
189 
invertSelected()190 		virtual void invertSelected() {
191 			m_selectable.invertSelected();
192 		}
193 };
194 
195 template<typename Iterator>
range_check(Iterator start,Iterator finish,Iterator iter)196 inline bool range_check (Iterator start, Iterator finish, Iterator iter)
197 {
198 	for (; start != finish; ++start) {
199 		if (start == iter) {
200 			return true;
201 		}
202 	}
203 	return false;
204 }
205 
206 #include <list>
207 
208 template<typename Selected>
209 class SelectionList
210 {
211 		typedef std::list<Selected*> List;
212 		List m_selection;
213 	public:
214 		typedef typename List::iterator iterator;
215 		typedef typename List::const_iterator const_iterator;
216 
begin()217 		iterator begin ()
218 		{
219 			return m_selection.begin();
220 		}
begin()221 		const_iterator begin () const
222 		{
223 			return m_selection.begin();
224 		}
end()225 		iterator end ()
226 		{
227 			return m_selection.end();
228 		}
end()229 		const_iterator end () const
230 		{
231 			return m_selection.end();
232 		}
empty()233 		bool empty () const
234 		{
235 			return m_selection.empty();
236 		}
size()237 		std::size_t size () const
238 		{
239 			return m_selection.size();
240 		}
back()241 		Selected& back ()
242 		{
243 			return *m_selection.back();
244 		}
back()245 		Selected& back () const
246 		{
247 			return *m_selection.back();
248 		}
append(Selected & selected)249 		void append (Selected& selected)
250 		{
251 			m_selection.push_back(&selected);
252 		}
erase(Selected & selected)253 		void erase (Selected& selected)
254 		{
255 			typename List::reverse_iterator i = std::find(m_selection.rbegin(), m_selection.rend(), &selected);
256 			ASSERT_MESSAGE(i != m_selection.rend(), "selection-tracking error");
257 			ASSERT_MESSAGE(range_check(m_selection.begin(), m_selection.end(), --i.base()), "selection-tracking error");
258 			m_selection.erase(--i.base());
259 		}
260 };
261 
262 class OccludeSelector : public Selector
263 {
264 	SelectionIntersection& _bestIntersection;
265 	bool& _occluded;
266 public:
OccludeSelector(SelectionIntersection & bestIntersection,bool & occluded)267 	OccludeSelector(SelectionIntersection& bestIntersection, bool& occluded) :
268 		_bestIntersection(bestIntersection),
269 		_occluded(occluded)
270 	{
271 		_occluded = false;
272 	}
273 
pushSelectable(Selectable & selectable)274 	void pushSelectable(Selectable& selectable) {}
popSelectable()275 	void popSelectable() {}
276 
addIntersection(const SelectionIntersection & intersection)277 	void addIntersection(const SelectionIntersection& intersection) {
278 		if (SelectionIntersection_closer(intersection, _bestIntersection)) {
279 			_bestIntersection = intersection;
280 			_occluded = true;
281 		}
282 	}
283 }; // class OccludeSelector
284 
285 #endif
286