1 /***********************************************************************
2 created: 21/2/2004
3 author: Paul D Turner
4
5 purpose: Implements the WindowManager class
6 *************************************************************************/
7 /***************************************************************************
8 * Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 ***************************************************************************/
29 #include "CEGUI/WindowManager.h"
30 #include "CEGUI/WindowFactoryManager.h"
31 #include "CEGUI/WindowFactory.h"
32 #include "CEGUI/Window.h"
33 #include "CEGUI/Exceptions.h"
34 #include "CEGUI/GUILayout_xmlHandler.h"
35 #include "CEGUI/XMLParser.h"
36 #include "CEGUI/RenderEffectManager.h"
37 #include "CEGUI/RenderingWindow.h"
38 #include <iostream>
39 #include <sstream>
40 #include <algorithm>
41
42 // Start of CEGUI namespace section
43 namespace CEGUI
44 {
45 /*************************************************************************
46 Static Data Definitions
47 *************************************************************************/
48 // singleton instance pointer
49 template<> WindowManager* Singleton<WindowManager>::ms_Singleton = 0;
50 // default resource group
51 String WindowManager::d_defaultResourceGroup;
52
53 /*************************************************************************
54 Definition of constant data for WindowManager
55 *************************************************************************/
56 // Declared in WindowManager
57 const String WindowManager::GUILayoutSchemaName("GUILayout.xsd");
58 const String WindowManager::GeneratedWindowNameBase("__cewin_uid_");
59 const String WindowManager::EventNamespace("WindowManager");
60 const String WindowManager::EventWindowCreated("WindowCreated");
61 const String WindowManager::EventWindowDestroyed("WindowDestroyed");
62
63
64 /*************************************************************************
65 Constructor
66 *************************************************************************/
WindowManager(void)67 WindowManager::WindowManager(void) :
68 d_uid_counter(0),
69 d_lockCount(0)
70 {
71 char addr_buff[32];
72 sprintf(addr_buff, "(%p)", static_cast<void*>(this));
73 Logger::getSingleton().logEvent(
74 "CEGUI::WindowManager singleton created " + String(addr_buff));
75 }
76
77
78 /*************************************************************************
79 Destructor
80 *************************************************************************/
~WindowManager(void)81 WindowManager::~WindowManager(void)
82 {
83 destroyAllWindows();
84 cleanDeadPool();
85
86 char addr_buff[32];
87 sprintf(addr_buff, "(%p)", static_cast<void*>(this));
88 Logger::getSingleton().logEvent(
89 "CEGUI::WindowManager singleton destroyed " + String(addr_buff));
90 }
91
92
93 /*************************************************************************
94 Create a new window of the specified type
95 *************************************************************************/
createWindow(const String & type,const String & name)96 Window* WindowManager::createWindow(const String& type, const String& name)
97 {
98 // only allow creation of Window objects if we are in unlocked state
99 if (isLocked())
100 CEGUI_THROW(InvalidRequestException(
101 "WindowManager is in the locked state."));
102
103 String finalName(name.empty() ? generateUniqueWindowName() : name);
104
105 WindowFactoryManager& wfMgr = WindowFactoryManager::getSingleton();
106 WindowFactory* factory = wfMgr.getFactory(type);
107
108 Window* newWindow = factory->createWindow(finalName);
109
110 char addr_buff[32];
111 sprintf(addr_buff, "(%p)", static_cast<void*>(newWindow));
112 Logger::getSingleton().logEvent("Window '" + finalName +"' of type '" +
113 type + "' has been created. " + addr_buff, Informative);
114
115 // see if we need to assign a look to this window
116 if (wfMgr.isFalagardMappedType(type))
117 {
118 const WindowFactoryManager::FalagardWindowMapping& fwm = wfMgr.getFalagardMappingForType(type);
119 // this was a mapped type, so assign a look to the window so it can finalise
120 // its initialisation
121 newWindow->d_falagardType = type;
122 newWindow->setWindowRenderer(fwm.d_rendererType);
123 newWindow->setLookNFeel(fwm.d_lookName);
124
125 initialiseRenderEffect(newWindow, fwm.d_effectName);
126 }
127
128 d_windowRegistry.push_back(newWindow);
129
130 // fire event to notify interested parites about the new window.
131 WindowEventArgs args(newWindow);
132 fireEvent(EventWindowCreated, args, EventNamespace);
133
134 return newWindow;
135 }
136
137 //---------------------------------------------------------------------------//
initialiseRenderEffect(Window * wnd,const String & effect) const138 void WindowManager::initialiseRenderEffect(
139 Window* wnd, const String& effect) const
140 {
141 Logger& logger(Logger::getSingleton());
142
143 // nothing to do if effect is empty string
144 if (effect.empty())
145 return;
146
147 // if requested RenderEffect is not available, log that and continue
148 if (!RenderEffectManager::getSingleton().isEffectAvailable(effect))
149 {
150 logger.logEvent("Missing RenderEffect '" + effect + "' requested for "
151 "window '" + wnd->getName() + "' - continuing without effect...",
152 Errors);
153
154 return;
155 }
156
157 // If we do not have a RenderingSurface, enable AutoRenderingSurface to
158 // try and create one
159 if (!wnd->getRenderingSurface())
160 {
161 logger.logEvent("Enabling AutoRenderingSurface on '" +
162 wnd->getName() + "' for RenderEffect support.");
163
164 wnd->setUsingAutoRenderingSurface(true);
165 }
166
167 // If we have a RenderingSurface and it's a RenderingWindow
168 if (wnd->getRenderingSurface() &&
169 wnd->getRenderingSurface()->isRenderingWindow())
170 {
171 // Set an instance of the requested RenderEffect
172 static_cast<RenderingWindow*>(wnd->getRenderingSurface())->
173 setRenderEffect(&RenderEffectManager::getSingleton().
174 create(effect, wnd));
175 }
176 // log fact that we could not get a usable RenderingSurface
177 else
178 {
179 logger.logEvent("Unable to set effect for window '" +
180 wnd->getName() + "' since RenderingSurface is either missing "
181 "or of wrong type (i.e. not a RenderingWindow).",
182 Errors);
183 }
184 }
185
186 /*************************************************************************
187 Destroy the given window by pointer
188 *************************************************************************/
destroyWindow(Window * window)189 void WindowManager::destroyWindow(Window* window)
190 {
191 WindowVector::iterator iter =
192 std::find(d_windowRegistry.begin(),
193 d_windowRegistry.end(),
194 window);
195
196 char addr_buff[32];
197 sprintf(addr_buff, "(%p)", static_cast<void*>(&window));
198
199 if (iter == d_windowRegistry.end())
200 {
201 Logger::getSingleton().logEvent("[WindowManager] Attempt to delete "
202 "Window that does not exist! Address was: " + String(addr_buff) +
203 ". WARNING: This could indicate a double-deletion issue!!",
204 Errors);
205 return;
206 }
207
208 d_windowRegistry.erase(iter);
209
210 Logger::getSingleton().logEvent("Window at '" + window->getNamePath() +
211 "' will be added to dead pool. " + addr_buff, Informative);
212
213 // do 'safe' part of cleanup
214 window->destroy();
215
216 // add window to dead pool
217 d_deathrow.push_back(window);
218
219 // fire event to notify interested parites about window destruction.
220 // TODO: Perhaps this should fire first, so window is still usable?
221 WindowEventArgs args(window);
222 fireEvent(EventWindowDestroyed, args, EventNamespace);
223 }
224
225
226 /*************************************************************************
227 Destroy all Window objects
228 *************************************************************************/
destroyAllWindows(void)229 void WindowManager::destroyAllWindows(void)
230 {
231 while (!d_windowRegistry.empty())
232 destroyWindow(*d_windowRegistry.begin());
233 }
234
235 //----------------------------------------------------------------------------//
isAlive(const Window * window) const236 bool WindowManager::isAlive(const Window* window) const
237 {
238 WindowVector::const_iterator iter =
239 std::find(d_windowRegistry.begin(),
240 d_windowRegistry.end(),
241 window);
242
243 return iter != d_windowRegistry.end();
244 }
245
loadLayoutFromContainer(const RawDataContainer & source,PropertyCallback * callback,void * userdata)246 Window* WindowManager::loadLayoutFromContainer(const RawDataContainer& source, PropertyCallback* callback, void* userdata)
247 {
248 // log the fact we are about to load a layout
249 Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from a RawDataContainer ----", Informative);
250
251 // create handler object
252 GUILayout_xmlHandler handler(callback, userdata);
253
254 // do parse (which uses handler to create actual data)
255 CEGUI_TRY
256 {
257 System::getSingleton().getXMLParser()->parseXML(handler, source, GUILayoutSchemaName);
258 }
259 CEGUI_CATCH(...)
260 {
261 Logger::getSingleton().logEvent("WindowManager::loadWindowLayout - loading of layout from a RawDataContainer failed.", Errors);
262 CEGUI_RETHROW;
263 }
264
265 // log the completion of loading
266 Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from a RawDataContainer ----", Standard);
267
268 return handler.getLayoutRootWindow();
269 }
270
loadLayoutFromFile(const String & filename,const String & resourceGroup,PropertyCallback * callback,void * userdata)271 Window* WindowManager::loadLayoutFromFile(const String& filename, const String& resourceGroup, PropertyCallback* callback, void* userdata)
272 {
273 if (filename.empty())
274 {
275 CEGUI_THROW(InvalidRequestException(
276 "Filename supplied for gui-layout loading must be valid."));
277 }
278
279 // log the fact we are about to load a layout
280 Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from '" + filename + "' ----", Informative);
281
282 // create handler object
283 GUILayout_xmlHandler handler(callback, userdata);
284
285 // do parse (which uses handler to create actual data)
286 CEGUI_TRY
287 {
288 System::getSingleton().getXMLParser()->parseXMLFile(handler,
289 filename, GUILayoutSchemaName, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup);
290 }
291 CEGUI_CATCH(...)
292 {
293 Logger::getSingleton().logEvent("WindowManager::loadLayoutFromFile - loading of layout from file '" + filename +"' failed.", Errors);
294 CEGUI_RETHROW;
295 }
296
297 // log the completion of loading
298 Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from '" + filename + "' ----", Standard);
299
300 return handler.getLayoutRootWindow();
301 }
302
loadLayoutFromString(const String & source,PropertyCallback * callback,void * userdata)303 Window* WindowManager::loadLayoutFromString(const String& source, PropertyCallback* callback, void* userdata)
304 {
305 // log the fact we are about to load a layout
306 Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from string ----", Informative);
307
308 // create handler object
309 GUILayout_xmlHandler handler(callback, userdata);
310
311 // do parse (which uses handler to create actual data)
312 CEGUI_TRY
313 {
314 System::getSingleton().getXMLParser()->parseXMLString(handler, source, GUILayoutSchemaName);
315 }
316 CEGUI_CATCH(...)
317 {
318 Logger::getSingleton().logEvent("WindowManager::loadLayoutFromString - loading of layout from string failed.", Errors);
319 CEGUI_RETHROW;
320 }
321
322 // log the completion of loading
323 Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from string ----", Standard);
324
325 return handler.getLayoutRootWindow();
326 }
327
isDeadPoolEmpty(void) const328 bool WindowManager::isDeadPoolEmpty(void) const
329 {
330 return d_deathrow.empty();
331 }
332
cleanDeadPool(void)333 void WindowManager::cleanDeadPool(void)
334 {
335 WindowVector::reverse_iterator curr = d_deathrow.rbegin();
336 for (; curr != d_deathrow.rend(); ++curr)
337 {
338 // in debug mode, log what gets cleaned from the dead pool (insane level)
339 #if defined(DEBUG) || defined (_DEBUG)
340 CEGUI_LOGINSANE("Window '" + (*curr)->getName() + "' about to be finally destroyed from dead pool.");
341 #endif
342
343 WindowFactory* factory = WindowFactoryManager::getSingleton().getFactory((*curr)->getType());
344 factory->destroyWindow(*curr);
345 }
346
347 // all done here, so clear all pointers from dead pool
348 d_deathrow.clear();
349 }
350
writeLayoutToStream(const Window & window,OutStream & out_stream) const351 void WindowManager::writeLayoutToStream(const Window& window, OutStream& out_stream) const
352 {
353
354 XMLSerializer xml(out_stream);
355 // output GUILayout start element
356 xml.openTag(GUILayout_xmlHandler::GUILayoutElement);
357 xml.attribute(GUILayout_xmlHandler::GUILayoutVersionAttribute,
358 GUILayout_xmlHandler::NativeVersion);
359
360 // write windows
361 window.writeXMLToStream(xml);
362 // write closing GUILayout element
363 xml.closeTag();
364 }
365
getLayoutAsString(const Window & window) const366 String WindowManager::getLayoutAsString(const Window& window) const
367 {
368 std::ostringstream str;
369 writeLayoutToStream(window, str);
370
371 return String(reinterpret_cast<const encoded_char*>(str.str().c_str()));
372 }
373
374 //----------------------------------------------------------------------------//
saveLayoutToFile(const Window & window,const String & filename) const375 void WindowManager::saveLayoutToFile(const Window& window,
376 const String& filename) const
377 {
378 std::ofstream stream(filename.c_str());
379
380 if (!stream.good())
381 CEGUI_THROW(FileIOException(
382 "failed to create stream for writing."));
383
384 writeLayoutToStream(window, stream);
385 }
386
generateUniqueWindowName()387 String WindowManager::generateUniqueWindowName()
388 {
389 const String ret = GeneratedWindowNameBase +
390 PropertyHelper<unsigned long>::toString(d_uid_counter);
391
392 // update counter for next time
393 unsigned long old_uid = d_uid_counter;
394 ++d_uid_counter;
395
396 // log if we ever wrap-around (which should be pretty unlikely)
397 if (d_uid_counter < old_uid)
398 Logger::getSingleton().logEvent("UID counter for generated Window "
399 "names has wrapped around - the fun shall now commence!");
400
401 return ret;
402 }
403
404 /*************************************************************************
405 Return a WindowManager::WindowIterator object to iterate over the
406 currently defined Windows.
407 *************************************************************************/
getIterator(void) const408 WindowManager::WindowIterator WindowManager::getIterator(void) const
409 {
410 return WindowIterator(d_windowRegistry.begin(), d_windowRegistry.end());
411 }
412
413 /*************************************************************************
414 Outputs the names of ALL existing windows to log (DEBUG function).
415 *************************************************************************/
DEBUG_dumpWindowNames(String zone) const416 void WindowManager::DEBUG_dumpWindowNames(String zone) const
417 {
418 Logger::getSingleton().logEvent("WINDOW NAMES DUMP (" + zone + ")");
419 Logger::getSingleton().logEvent("-----------------");
420
421 for (WindowVector::const_iterator i = d_windowRegistry.begin();
422 i != d_windowRegistry.end();
423 ++i)
424 {
425 Logger::getSingleton().logEvent("Window : " + (*i)->getNamePath());
426 }
427 Logger::getSingleton().logEvent("-----------------");
428 }
429
430 //----------------------------------------------------------------------------//
lock()431 void WindowManager::lock()
432 {
433 ++d_lockCount;
434 }
435
436 //----------------------------------------------------------------------------//
unlock()437 void WindowManager::unlock()
438 {
439 if (d_lockCount > 0)
440 --d_lockCount;
441 }
442
443 //----------------------------------------------------------------------------//
isLocked() const444 bool WindowManager::isLocked() const
445 {
446 return d_lockCount != 0;
447 }
448
449 //----------------------------------------------------------------------------//
450
451 } // End of CEGUI namespace section
452