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