context: #region Copyright & License Information
  /*
   * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Linq;
  using OpenRA.Graphics;
  using OpenRA.Support;
  
  namespace OpenRA.Widgets
  {
  	public static class Ui
  	{
  		public static Widget Root = new ContainerWidget();
  
  		public static int LastTickTime = Environment.TickCount;
  
  		static Stack<Widget> WindowList = new Stack<Widget>();
  
  		public static Widget MouseFocusWidget;
  		public static Widget KeyboardFocusWidget;
  		public static Widget MouseOverWidget;
  
  		public static void CloseWindow()
  		{
  			if (WindowList.Count > 0)
  				Root.RemoveChild(WindowList.Pop());
  			if (WindowList.Count > 0)
  				Root.AddChild(WindowList.Peek());
  		}
  
  		public static Widget OpenWindow(string id)
  		{
  			return OpenWindow(id, new WidgetArgs());
  		}
  
  		public static Widget OpenWindow(string id, WidgetArgs args)
  		{
  			var window = Game.modData.WidgetLoader.LoadWidget(args, Root, id);
  			if (WindowList.Count > 0)
  				Root.RemoveChild(WindowList.Peek());
  			WindowList.Push(window);
  			return window;
  		}
  
  		public static T LoadWidget<T>(string id, Widget parent, WidgetArgs args) where T : Widget
  		{
  			var widget = LoadWidget(id, parent, args) as T;
  			if (widget == null)
  				throw new InvalidOperationException(
  					"Widget {0} is not of type {1}".F(id, typeof(T).Name));
  			return widget;
  		}
  
  		public static Widget LoadWidget(string id, Widget parent, WidgetArgs args)
  		{
  			return Game.modData.WidgetLoader.LoadWidget(args, parent, id);
  		}
  
  		public static void Tick() { Root.TickOuter(); }
  
  		public static void Draw() { Root.DrawOuter(); }
  
  		public static bool HandleInput(MouseInput mi)
  		{
  			var wasMouseOver = MouseOverWidget;
  
  			if (mi.Event == MouseInputEvent.Move)
  				MouseOverWidget = null;
  
  			bool handled = false;
  			if (MouseFocusWidget != null && MouseFocusWidget.HandleMouseInputOuter(mi))
  				handled = true;
  
  			if (!handled && Root.HandleMouseInputOuter(mi))
  				handled = true;
  
  			if (mi.Event == MouseInputEvent.Move)
  			{
  				Viewport.LastMousePos = mi.Location;
  				Viewport.TicksSinceLastMove = 0;
  			}
  
  			if (wasMouseOver != MouseOverWidget)
  			{
  				if (wasMouseOver != null)
  					wasMouseOver.MouseExited();
  
  				if (MouseOverWidget != null)
  					MouseOverWidget.MouseEntered();
  			}
  
  			return handled;
  		}
  
  		public static bool HandleKeyPress(KeyInput e)
  		{
  			if (KeyboardFocusWidget != null)
  				return KeyboardFocusWidget.HandleKeyPressOuter(e);
  
  			return Root.HandleKeyPressOuter(e);
  		}
  
  		public static bool HandleTextInput(string text)
  		{
  			if (KeyboardFocusWidget != null)
  				return KeyboardFocusWidget.HandleTextInputOuter(text);
  
  			return Root.HandleTextInputOuter(text);
  		}
  
  		public static void ResetAll()
  		{
  			Root.RemoveChildren();
  
  			while (WindowList.Count > 0)
  				CloseWindow();
  		}
  	}
  
  	public abstract class Widget
  	{
  		// Info defined in YAML
  		public string Id = null;
  		public string X = "0";
  		public string Y = "0";
  		public string Width = "0";
  		public string Height = "0";
  		public string Logic = null;
  		public object LogicObject { get; private set; }
  		public bool Visible = true;
  		public bool IgnoreMouseOver;
  		public bool IgnoreChildMouseOver;
  
  		// Calculated internally
  		public Rectangle Bounds;
  		public Widget Parent = null;
  		public Func<bool> IsVisible;
  		public Widget() { IsVisible = () => Visible; }
  		public readonly List<Widget> Children = new List<Widget>();
  
  		public Widget(Widget widget)
  		{
  			Id = widget.Id;
  			X = widget.X;
  			Y = widget.Y;
  			Width = widget.Width;
  			Height = widget.Height;
  			Logic = widget.Logic;
  			Visible = widget.Visible;
  
  			Bounds = widget.Bounds;
  			Parent = widget.Parent;
  
  			IsVisible = widget.IsVisible;
  			IgnoreChildMouseOver = widget.IgnoreChildMouseOver;
  
  			foreach (var child in widget.Children)
  				AddChild(child.Clone());
  		}
  
  		public virtual Widget Clone()
  		{
  			throw new InvalidOperationException("Widget type `{0}` is not cloneable.".F(GetType().Name));
  		}
  
  		public virtual int2 RenderOrigin
  		{
  			get
  			{
  				var offset = (Parent == null) ? int2.Zero : Parent.ChildOrigin;
  				return new int2(Bounds.X, Bounds.Y) + offset;
  			}
  		}
  
  		public virtual int2 ChildOrigin { get { return RenderOrigin; } }
  
  		public virtual Rectangle RenderBounds
  		{
  			get
  			{
  				var ro = RenderOrigin;
  				return new Rectangle(ro.X, ro.Y, Bounds.Width, Bounds.Height);
  			}
  		}
  
  		public virtual void Initialize(WidgetArgs args)
  		{
  			// Parse the YAML equations to find the widget bounds
  			var parentBounds = (Parent == null)
  				? new Rectangle(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height)
  				: Parent.Bounds;
  
  			var substitutions = args.ContainsKey("substitutions") ?
  				new Dictionary<string, int>((Dictionary<string, int>)args["substitutions"]) :
  				new Dictionary<string, int>();
  
  			substitutions.Add("WINDOW_RIGHT", Game.Renderer.Resolution.Width);
  			substitutions.Add("WINDOW_BOTTOM", Game.Renderer.Resolution.Height);
  			substitutions.Add("PARENT_RIGHT", parentBounds.Width);
  			substitutions.Add("PARENT_LEFT", parentBounds.Left);
  			substitutions.Add("PARENT_TOP", parentBounds.Top);
  			substitutions.Add("PARENT_BOTTOM", parentBounds.Height);
  			int width = Evaluator.Evaluate(Width, substitutions);
  			int height = Evaluator.Evaluate(Height, substitutions);
  
  			substitutions.Add("WIDTH", width);
  			substitutions.Add("HEIGHT", height);
  
  
gt: 			Bounds = new Rectangle(Evaluator.Evaluate(X, substitutions),