WindowManager.cs :  » Game » RealmForge » CrayzEdsGui » Base » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Game » RealmForge 
RealmForge » CrayzEdsGui » Base » WindowManager.cs
#region LGPL License

/*************************************************************************
    Crazy Eddie's GUI System (http://crayzedsgui.sourceforge.net)
    Copyright (C)2004 Paul D Turner (crayzed@users.sourceforge.net)
  
  C# Port developed by Chris McGuirk (leedgitar@latenitegames.com)
  Compatible with the Axiom 3D Engine (http://axiomengine.sf.net)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/

#endregion LGPL License

#region Using directives

using System;
using System.Collections;
using System.Text;

using System.IO;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

using System.Xml;
using System.Xml.Schema;

#endregion

namespace CrayzEdsGui.Base{

  /// <summary>
  ///    The WindowManager class describes an object that manages creation and lifetime of Window objects.
  /// </summary>
  /// <remarks>
  ///    The WindowManager is the means by which <see cref="Window"/> objects are created and destroyed.  
  ///    For each sub-class of Window that is to be created, there must exist a <see cref="WindowFactory"/> 
  ///    object which is registered with the <see cref="WindowFactoryManager"/>.  Additionally, the WindowManager 
  ///    tracks every Window object created, and can be used to access those Window objects by name.
  /// </remarks>
  public class WindowManager : IDisposable {
    #region Nested Classes and Structs

    struct PropertyContainer
    {
      public PropertyInfo property;
      public WidgetPropertyAttribute attribute;

      public PropertyContainer( PropertyInfo p, WidgetPropertyAttribute a )
      {
        property = p;
        attribute = a;
      }
    }

    #endregion
    
    #region Fields

    /// <summary>
    ///    The container that forms the Window registry.
    /// </summary>
    protected WindowList windowRegistry = new WindowList();

    /// <summary>
    ///    Assembly list used for compiling dynamic scripts.
    /// </summary>
    protected static string[] dynamicAssemblyRefs = new string[] 
    { "System.dll", "CrayzEdsGui.Base.dll", "RealmForge.Engine.dll"}; // njk-patch

    /// <summary>
    ///    Name of the XSD file to use for validation.
    /// </summary>
    const string SchemaFileName = "GUILayout.xsd";

    protected Hashtable typeMap = new Hashtable();

    /// <summary>
    /// Contains currently defined aliases for Window types
    /// </summary>
    protected Hashtable alternateNamesMap = new Hashtable();
    protected Hashtable mPropertyMap = new Hashtable();

    #endregion Fields

    #region Singleton Implementation

    /// <summary>
    ///    Singlton instance of this class.
    /// </summary>
    private static WindowManager instance;

    /// <summary>
    ///    Default constructor.
    /// </summary>
    internal WindowManager() {
      // only create once instance
      if (instance == null) {
        instance = this;

        Logger.Instance.LogEvent("WindowManager singleton created.");
      }
    }

    /// <summary>
    ///    Gets the singleton class instance.
    /// </summary>
    /// <value></value>
    public static WindowManager Instance {
      get {
        return instance;
      }
    }

    #endregion Singleton Implementation

    #region Window Related Methods

    /// <summary>
    ///    
    /// </summary>
    /// <param name="path"></param>
    public bool CompileScripts(string assemblyName, string path, bool debug) {
      // find all CSharp files
      // TODO: Make compilation more generic
      string[] scriptFiles = Directory.GetFiles(path, "*.cs");

      // create a code compiler
      CSharpCodeProvider provider = new CSharpCodeProvider();
      ICodeCompiler compiler = provider.CreateCompiler();

      // configure params for the script compilation
      CompilerParameters parms = new CompilerParameters(dynamicAssemblyRefs);

      // this is an in memory assembly
      // TODO: Consider compiling to file and loading if no changes have occurred on the next load
      parms.GenerateInMemory = true;
      parms.GenerateExecutable = false;
      parms.IncludeDebugInformation = debug;

      // compile the list of files found in the script directory
      CompilerResults results = compiler.CompileAssemblyFromFileBatch(parms, scriptFiles);

      // check for errors and log them
      if(results.Errors.Count != 0) {
        StringBuilder errorBuilder = new StringBuilder(results.Errors.Count);

        string errorMsg = "Script Error [File: {0}, Line: {1}]: {2}]" + Environment.NewLine;

        foreach(CompilerError error in results.Errors) {
          errorBuilder.AppendFormat(errorMsg, error.FileName, error.Line, error.ErrorText);
        }

        Logger.Instance.LogEvent(errorBuilder.ToString(), LoggingLevel.Errors);

        return false;
      }

      AttachAssembly(results.CompiledAssembly);

      // success
      return true;
    }

    /// <summary>
    ///   Registers all the classes in an assembly that decend from type Window so that they can be created using <see cref="CreateWindow"/>.
    /// </summary>
    /// <param name="assembly"></param>
    public void AttachAssembly(Assembly assembly) {
      // find all window types and add them to the map
      foreach(Type type in assembly.GetTypes()) {
        if(type.IsSubclassOf(typeof(Window))) {
          RegisterType(type, assembly);
        }
      }
    }

    /// <summary>
    ///    Creates a new <see cref="Window"/> object of the specified type, and gives 
    ///    it the specified unique name.
    /// </summary>
    /// <param name="type">
    ///    String that describes the type of Window to be created.  
    ///    A valid <see cref="WindowFactory"/> for the specified type must be registered.</param>
    /// <param name="name">A unique name that is to be given to the new window.</param>
    /// <returns>Reference to the newly created window.</returns>
    /// <exception cref="AlreadyExistsException">
    ///    A <see cref="Window"/> object with the specified <paramref name="name"/> already exists.
    /// </exception>
    /// <exception cref="UnknownObjectException">
    ///    No <see cref="WindowFactory"/> is registered for <paramref name="type"/> <see cref="Window"/> objects.
    /// </exception>
    /// <exception cref="GenericException">Some other error occurred (Exception message has details).</exception>
    public Window CreateWindow(string type, string name) {
      string typeName = type;

      // does the requested type exist?
      if(!typeMap.ContainsKey(type)) {
        // check for alternate names
        if( alternateNamesMap.ContainsKey( type ) )
        {
          typeName = (string)alternateNamesMap[type];
        }
        else
        {
        throw new UnknownObjectException("Window type '{0}' is not currently loaded.", type);
      }
      }

      // does a window with the specified name already exist?
      if(IsWindowPresent(name)) {
        throw new InvalidRequestException("A window with the name '{0}' already exists.", name);
      }

      // grab the assembly which hosts this type
      Assembly assembly = (Assembly)typeMap[typeName];

      // create an instance of the window and initialize it
      Window window = (Window)assembly.CreateInstance( typeName, true, 0, null, new object[] { name }, null, null );

      // keep track of windows we have created
      AttachWindow(window);

      // return the newly created window
      return window;
    }

    /// <summary>
    ///    Attaches a window to the window manager.
    /// </summary>
    /// <param name="window">The window object to attach.</param>
    /// <remarks>You need to call this after you create each window if you don't use <see cref="CreateWindow"/>.</remarks>
    public void AttachWindow(Window window) { 
      // does a window with the specified name already exist? 
      if(IsWindowPresent(window.Name)) { 
        throw new InvalidRequestException("A window with the name '{0}' already exists.", window.Name); 
      } 

      // TODO: Put this Initialize call back into CreateWindow?
      // (PDT) This method is doing more than it states.  It should be the job of whoever creates the window to call
      // the Initialize method.  How do we know that when attaching a window created externally that Initialize has
      // not already been called?
      window.Initialize();

      // keep track of windows we have created 
      windowRegistry.Add(window); 
    }

    /// <summary>
    ///    Destroys all <see cref="Window"/> objects within the system.
    /// </summary>
    /// <exception cref="InvalidRequestException">If the factory for any window has been removed.</exception>
    public void DestroyAllWindows() {
      GuiSystem.Instance.GuiSheet = null;
      for(int i = 0; i < windowRegistry.Count; i++) {
        Window window = windowRegistry[i];

        window.Dispose();
      }

      // clear the current window list
      windowRegistry.Clear();
    }

    /// <summary>
    ///    Destroy the specified <see cref="Window"/> object.
    /// </summary>
    /// <param name="window">Reference to the <see cref="Window"/> object to be destroyed.  
    ///    If <paramref name="window"/> is not recognized, nothing happens.
    /// </param>
    /// <exception cref="InvalidRequestException">
    ///    If the factory for <paramref name="window"/> has been removed.
    /// </exception>
    /// <exception cref="ArgumentNullException">If <paramref name="window"/> is null.</exception>
    public void DestroyWindow(Window window) {
      // destroy the window
      window.Dispose();

      // remove the window from the list
      windowRegistry.Remove(window.Name);
    }

    /// <summary>
    ///    Destroy the <see cref="Window"/> object with the specified name.
    /// </summary>
    /// <param name="name">
    ///    Name of the <see cref="Window"/> object to destroy.
    ///   If <paramref name="name"/> is not recognized, nothing happens.
    /// </param>
    /// <exception cref="InvalidRequestException">
    ///    If the factory for <paramref name="name"/> has been removed.
    /// </exception>
    /// <exception cref="ArgumentNullException">If <paramref name="name"/> is null.</exception>
    public void DestroyWindow(string name) {
      // find the window by name
      Window window = GetWindow(name);

      // destroy the window if there is one with this name
      if(window != null) {
        DestroyWindow(window);
      }
    }

    /// <summary>
    ///    Return a reference to the specified <see cref="Window"/> object.
    /// </summary>
    /// <param name="name">Name of the window to be returned.</param>
    /// <returns>A reference to a <see cref="Window"/> with the specified <paramref name="name"/></returns>
    /// <exception cref="UnknownObjectException">
    ///    If a window with the specified <paramref name="name"/> does not exist in the system.
    /// </exception>
    public Window GetWindow(string name) {
      Window window = windowRegistry[name];

      if (window != null) {
        return window;
      }

      throw new UnknownObjectException("A Window with the name {0} does not exist within the system.", name);
    }

    /// <summary>
    ///    Examines the list of <see cref="Window"/> objects to see if one exists with the given name.
    /// </summary>
    /// <param name="name">Name of the window to look for.</param>
    /// <returns>
    ///    True if the window was found with the specified <paramref name="name"/>.
    ///    False if no matching window was found.
    /// </returns>
    public bool IsWindowPresent(string name) {
      return windowRegistry[name] != null;
    }

    /// <summary>
    /// Examines the list of Properties of this type to find any that are flagged to be exported.
    /// </summary>
    /// <param name="type">The type of the object to check</param>
    public void RegisterProperties( Type type )
    {
      Hashtable propertyMap = new Hashtable();

      PropertyInfo[] properties = type.GetProperties();
      foreach( PropertyInfo property in properties )
      {
        WidgetPropertyAttribute[] attributes = 
          (WidgetPropertyAttribute[])property.GetCustomAttributes( typeof( WidgetPropertyAttribute ), true );

        foreach( WidgetPropertyAttribute attrib in attributes )
        {
          propertyMap[attrib.Name] = new PropertyContainer( property, attrib );
        }
      }

      mPropertyMap[type.FullName] = propertyMap;
    }

    /// <summary>
    ///    Regster an existing widget type with the system.
    /// </summary>
    /// <param name="type"></param>
    /// <param name="assembly"></param>
    public void RegisterType(Type type, Assembly assembly) {
      if(!typeMap.ContainsKey(type.Name)) {
        typeMap.Add(type.FullName, assembly);

        Logger.Instance.LogEvent("WindowManager: Window type '" + type.FullName + "' registered.");
      }

      // register all of the alternate names for the widget
      AlternateWidgetNameAttribute[] attributes =
        (AlternateWidgetNameAttribute[])type.GetCustomAttributes( typeof(AlternateWidgetNameAttribute), true );
      foreach( AlternateWidgetNameAttribute attr in attributes )
      {
        if( !typeMap.ContainsKey( attr.Name ) )
        {
          alternateNamesMap.Add( attr.Name, type.FullName );
        }
        else
        {
          Logger.Instance.LogEvent( "WindowManager: Window type '" + attr.Name + "' already registered.", LoggingLevel.Errors );
        }
      }

      // register the properties
      RegisterProperties( type );
    }

  #endregion Window Related Methods

    #region XML Layout Methods
    // (PDT) Changes made to JW's original patch:
    // Modified the public layout loading methods to return a single Window rather than an ArrayList.
    // The XML schema enforces the rule that there can only be one actual 'root' window in a layout, so the
    // ArrayList would only ever have one element anyway.  Various other changes were made that relate to
    // this.
    // Also changed the method names to use the same casing scheme as the rest of the system.
    // NB: There is a slight variation in the behavior here from what the C++ version does, here we may return null
    // to indicate failure, in C++ I basically state that if the method returns the Window is valid (exceptions are used
    // for error cases) Not sure which approach I prefer.

    /// <summary>
    ///    Creates a window from the information contained in the XML node.
    /// </summary>
    /// <param name="windowNode">The XmlNode that contains information about the window</param>
    /// <param name="guiHandler">The object that will handle the gui events</param>
    /// <returns>Returns the window that was created</returns>
    Window LoadWindow( XmlNode windowNode, object guiHandler, Hashtable eventMap )
    {
      string type = windowNode.Attributes["Type"].Value;
      string name = windowNode.Attributes["Name"].Value;

      // if the widget doesn't exist, then issue a warning and return null
      if( !typeMap.ContainsKey( type ) && !alternateNamesMap.ContainsKey( type ) )
      {
        Logger.Instance.LogEvent( "Window type '" + type + "' does not exist", LoggingLevel.Errors );
        return null;
      }

      Window window = CreateWindow( type, name );

      Hashtable properties;
      if( typeMap.ContainsKey( type ) )
      {
        properties = (Hashtable)mPropertyMap[type];
      }
      else
      {
        properties = (Hashtable)mPropertyMap[alternateNamesMap[type]];
      }

      if( null != window )
      {
        // read the properties
        foreach( XmlNode property in windowNode.SelectNodes( "Property" ) )
        {
          string propName = property.Attributes["Name"].Value;
          string propValue = property.Attributes["Value"].Value;

          if( properties.ContainsKey( propName ) )
          {
            PropertyContainer propertyContainer = (PropertyContainer)properties[propName];
            propertyContainer.attribute.SetValue( window, propertyContainer.property, propValue );
          }
          else
          {
            Logger.Instance.LogEvent( "The '" + propName + "' property does not exist in the " + type + " widget." );
          }
        }

        // map any events
        if( null != guiHandler )
        {
          // build a list of the widgets events
          Hashtable widgetEvents = new Hashtable();
          Type widgetType = window.GetType();
          foreach( EventInfo evt in widgetType.GetEvents() )
          {
            widgetEvents.Add( evt.Name, evt );
          }

          // loop through the event nodes, and create the mappings
          foreach( XmlNode evt in windowNode.SelectNodes( "Event" ) )
          {
            string eventName = evt.Attributes["Name"].Value;
            string eventFunction = evt.Attributes["Function"].Value;

            if( widgetEvents.ContainsKey( eventName ) && eventMap.ContainsKey( eventFunction ) )
            {
              EventInfo evtInfo = (EventInfo)widgetEvents[eventName];
              MethodInfo fnInfo = (MethodInfo)eventMap[eventFunction];

              evtInfo.AddEventHandler( window, Delegate.CreateDelegate( evtInfo.EventHandlerType, guiHandler, fnInfo.Name ) );
            }
            else
            {
              if( !widgetEvents.Contains( eventName ) )
              {
                Logger.Instance.LogEvent( "The '" + window.GetType().Name + "' widget does not contain the event '" + eventName + "'", LoggingLevel.Errors );
              }
              else if( !eventMap.Contains( eventFunction ) )
              {
                Logger.Instance.LogEvent( "The gui handler does not contain the event function '" + eventFunction + "'", LoggingLevel.Errors );
              }
            }
          }
        }

        // load any child nodes
        foreach( XmlNode child in windowNode.SelectNodes( "Window" ) )
        {
          Window temp = LoadWindow( child, guiHandler, eventMap );

          if( null != temp )
          {
            window.AddChild( temp );
          }
        }

        // import any nested layouts
        foreach( XmlNode child in windowNode.SelectNodes( "LayoutImport" ) )
        {
          Window importedLayout = LoadWindowLayout( child.Attributes["Filename"].Value );
          window.AddChild( importedLayout );
        }
      }

      return window;
    }

    /// <summary>
    ///    Loads a window layout file.
    /// </summary>
    /// <remarks>
    ///    This will load a layout file that follows the same format as the c++ CEGUI format.
    /// </remarks>
    /// <param name="fileName">Name of the layout file.</param>
    /// <returns>Returns the root window of the layout.</returns>
    public Window LoadWindowLayout( string fileName )
    {
      return LoadWindowLayout( fileName, null );
    }

    /// <summary>
    ///    Loads a window layout file.
    /// </summary>
    /// <remarks>
    ///    This will load a layout file that follows the same format as the c++ CEGUI format.
    /// </remarks>
    /// <param name="fileName">Name of the layout file.</param>
    /// <param name="guiHandler">The object that will handle gui events.</param>
    /// <returns>Returns the root window of the layout.</returns>
    public Window LoadWindowLayout( string fileName, object guiHandler )
    {
      Window root = null;

      XmlValidatingReader reader = new XmlValidatingReader( new XmlTextReader( fileName ) );
      reader.Schemas.Add( "", SchemaFileName );

      XmlDocument doc = new XmlDocument();
      doc.Load( reader );

      XmlNode rootNode = doc.SelectSingleNode( "//GUILayout" );

      // check the guiHandler for GuiEvents
      Hashtable eventMap = null;
      if( null != guiHandler )
      {
        eventMap = new Hashtable();
        Type handlerType = guiHandler.GetType();

        while( handlerType != typeof( object ) )
        {
          foreach( MethodInfo method in handlerType.GetMethods() )
          {
            GuiEventAttribute[] attributes = (GuiEventAttribute[])method.GetCustomAttributes( typeof( GuiEventAttribute ), true );

            if( attributes.Length > 0 )
            {
              eventMap.Add( method.Name, method );
            }
          }

          handlerType = handlerType.BaseType;
        }
      }

      // load the windows
      foreach( XmlNode windowNode in rootNode.SelectNodes( "Window" ) )
      {
        Window window = LoadWindow( windowNode, guiHandler, eventMap );

        if( null != window )
        {
          root = window;
        }
      }

      reader.Close();

      return root;
    }

    #endregion

    #region IDisposable Members

    /// <summary>
    ///    Called when the object should release and resources.
    /// </summary>
    public void Dispose() {
      // destroy existing windows
      DestroyAllWindows();

      Logger.Instance.LogEvent("WindowManager singleton destroyed.");
    }

  #endregion
  }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.