using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using System.Reflection;
namespace OSGeo.MapGuide.Viewer
{
///
/// Defines a map viewer component
///
public interface IMapComponent
{
///
/// Gets the list of component properties
///
IEnumerable ComponentProperties { get; }
///
/// Sets the value of the specified component property
///
///
///
void SetPropertyValue(string propertyName, object value);
///
/// Gets the value of the specified component property
///
///
///
object GetPropertyValue(string propertyName);
}
///
/// Indicates that a given CLR property is dynamically invokable. Primarily used for property
/// value assignment by the AppLayout engine
///
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class MgComponentPropertyAttribute : Attribute
{
}
///
/// The base class of all viewer components. This is analogous to a command in the MapGuide
/// AJAX viewer and a widget in the Fusion viewer.
///
[ToolboxItem(false)]
public class MgComponent : Component, IMapComponent
{
private IMapViewer _viewer;
///
/// Gets whether to disable invocation entry points (eg. buttons, menus, etc) to this component when the
/// viewer is digitizing
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("Disables this component while the digitizing is happening")] //NOXLATE
[MgComponentProperty]
public virtual bool DisableWhenDigitizing { get { return true; } }
///
/// Gets whether to disable invocation entry points (eg. buttons, menus, etc) to this component when the
/// viewer is busy
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("Disables this component while the map is loading")] //NOXLATE
[MgComponentProperty]
public virtual bool DisableWhenMapIsLoading { get { return true; } }
///
/// Gets or sets the description of this component
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The description of this component")] //NOXLATE
[MgComponentProperty]
public string Label { get; set; }
///
/// Gets or sets the tooltip text of this component
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The tooltip text of this component")] //NOXLATE
[MgComponentProperty]
public string ToolTipText { get; set; }
///
/// The icon for this component
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The icon for this component")] //NOXLATE
[MgComponentProperty]
public Image Icon { get; set; }
///
/// Gets or sets the view instance to subscribe events to
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The viewer instance to subscribe events to")] //NOXLATE
[MgComponentProperty]
public IMapViewer Viewer
{
get { return _viewer; }
set
{
if (_viewer != null)
UnsubscribeViewerEvents(_viewer);
_viewer = value;
SubscribeViewerEvents(_viewer);
}
}
protected virtual void SubscribeViewerEvents(IMapViewer viewer)
{
if (viewer != null)
{
viewer.PropertyChanged += OnViewerPropertyChanged;
viewer.MapLoaded += OnViewerMapLoaded;
viewer.MapRefreshed += OnMapRefreshed;
if (this.RequiresLoadedMap)
{
foreach (var l in _listeners)
l.SetEnabled(viewer.HasLoadedMap);
}
}
}
///
/// Unsubscribes events from the specified viewer instance
///
///
protected virtual void UnsubscribeViewerEvents(IMapViewer viewer)
{
if (viewer != null)
{
viewer.PropertyChanged -= OnViewerPropertyChanged;
viewer.MapLoaded -= OnViewerMapLoaded;
}
}
protected virtual void OnViewerMapLoaded(object sender, EventArgs e)
{
foreach (var l in _listeners)
l.SetEnabled(this.Viewer.HasLoadedMap);
}
protected virtual void OnMapRefreshed(object sender, EventArgs e) { }
protected virtual void OnViewerPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsBusy") //NOXLATE
{
var busy = this.Viewer.IsBusy;
//Trace.TraceInformation("Dispatching busy state event to " + _listeners.Count + " listeners"); //NOXLATE
if (this.DisableWhenMapIsLoading)
{
foreach (var l in _listeners)
l.SetEnabled(!busy);
}
}
else if (e.PropertyName == "DigitizingType") //NOXLATE
{
var bDigitizing = (this.Viewer.DigitizingType != MapDigitizationType.None);
if (this.DisableWhenDigitizing)
{
foreach (var l in _listeners)
l.SetEnabled(!bDigitizing);
}
}
}
///
/// Gets whether this component requires a loaded map. If true, and no map is loaded this
/// component will be disabled until a map is loaded. If false, this component stays enabled
/// and can operate without a loaded map
///
protected virtual bool RequiresLoadedMap { get { return true; } }
protected List _listeners = new List();
public void AddListener(IButtonStateListener listener)
{
_listeners.Add(listener);
if (this.RequiresLoadedMap)
listener.SetEnabled(this.Viewer != null && this.Viewer.HasLoadedMap);
}
public void RemoveListener(IButtonStateListener listener)
{
_listeners.Remove(listener);
}
///
/// Invokes this component
///
public virtual void Invoke()
{
}
private Dictionary _properties;
public IEnumerable ComponentProperties
{
get
{
CheckAndInitProperties();
return _properties.Values;
}
}
private void CheckAndInitProperties()
{
if (_properties == null)
{
_properties = new Dictionary();
var props = this.GetType().GetProperties();
foreach (var p in props)
{
var attributes = p.GetCustomAttributes(true);
foreach (var att in attributes)
{
var compAttr = att as MgComponentPropertyAttribute;
if (compAttr != null)
{
_properties[p.Name] = p;
break;
}
}
}
}
}
public void SetPropertyValue(string propertyName, object value)
{
CheckAndInitProperties();
if (!_properties.ContainsKey(propertyName))
throw new InvalidOperationException(string.Format(Strings.ErrorInvalidComponentProperty, propertyName));
var prop = _properties[propertyName];
prop.SetValue(this, Convert.ChangeType(value, prop.PropertyType), null);
}
public object GetPropertyValue(string propertyName)
{
CheckAndInitProperties();
if (!_properties.ContainsKey(propertyName))
throw new InvalidOperationException(string.Format(Strings.ErrorInvalidComponentProperty, propertyName));
return _properties[propertyName].GetValue(propertyName, null);
}
}
public delegate void ViewerBusyStateEventHandler(bool busy);
public interface IButtonStateListener
{
void SetEnabled(bool enabled);
void SetActive(bool outlined);
void SetText(string text);
void SetIcon(Image icon);
}
///
/// Indicates the target a UI-based component should display its UI view in
///
public enum MgViewerTarget
{
///
/// Display the UI view within the specified parent container
///
TaskPane,
///
/// Display the UI view within a new window
///
NewWindow
}
///
/// The base class of all UI-based components
///
[ToolboxItem(false)]
public class MgViewerComponent : MgComponent, ISupportInitialize
{
protected MgViewerComponent()
{
this.TaskPane = null;
this.Target = MgViewerTarget.NewWindow;
}
///
///
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The task pane which will host the UI view")] //NOXLATE
[MgComponentProperty]
public MgTaskPane TaskPane
{
get;
set;
}
private MgViewerTarget _target;
///
/// Gets or sets the target that this component should display its UI view in
///
[Category("MapGuide Component Properties")] //NOXLATE
[DefaultValue(MgViewerTarget.NewWindow)]
[Description("Target that this component should display its UI view in")] //NOXLATE
[MgComponentProperty]
public MgViewerTarget Target
{
get { return _target; }
set
{
//Must have a task pane assigned, but we suppress this check during initialization
if (!_init && (value == MgViewerTarget.TaskPane && this.TaskPane == null))
throw new ArgumentException(Strings.ErrorTargetNoTaskPaneAssigned);
_target = value;
}
}
///
/// Gets or sets the owner form that any new windows displayed by this component will belong to.
///
[Category("MapGuide Component Properties")] //NOXLATE
[Description("The parent form which will be the owner for any new windows displayed by this component")] //NOXLATE
public Form OwnerParent
{
get;
set;
}
///
/// Creates the associated view. Must be overridden by subclasses
///
///
protected virtual MgControlView CreateControlView() { throw new NotImplementedException(); }
internal MgControlView CreateControl() { return CreateControlView(); }
public override void Invoke()
{
var control = CreateControlView();
control.Dock = DockStyle.Fill;
if (this.Target == MgViewerTarget.TaskPane)
{
new TaskPaneContentCloser(this.TaskPane, control);
}
else //New Window
{
new NewWindowContentCloser(control, this.OwnerParent);
}
}
class TaskPaneContentCloser : IContentCloser
{
private MgTaskPane _taskPane;
private MgControlView _control;
public TaskPaneContentCloser(MgTaskPane taskPane, MgControlView control)
{
_taskPane = taskPane;
_control = control;
_control.Closer = this;
_taskPane.SetContent(_control);
}
public void Close()
{
_control.Dispose();
_taskPane.LoadInitialTask();
}
}
class NewWindowContentCloser : IContentCloser
{
private Form _frm;
private MgControlView _control;
public NewWindowContentCloser(MgControlView control, Form owner)
{
_frm = new Form();
_control = control;
Rectangle screenRectangle = _frm.RectangleToScreen(_frm.ClientRectangle);
int titleHeight = screenRectangle.Top - _frm.Top;
_frm.Width = Math.Max(_control.Size.Width, _control.PreferredSize.Width);
_frm.Height = Math.Max(_control.Size.Height, _control.PreferredSize.Height) + titleHeight + 10; //HACK: height calculation is imperfect, so pad out
_frm.Text = _control.Title;
_frm.Controls.Add(_control);
_control.Closer = this;
if (control.ModalWindow)
_frm.ShowDialog(owner);
else
_frm.Show(owner);
}
public void Close()
{
if (_control.ModalWindow)
_frm.DialogResult = DialogResult.OK;
else
_frm.Close();
if (!_control.IsDisposed)
_control.Dispose();
}
}
private bool _init = true;
public void BeginInit()
{
_init = true;
}
public void EndInit()
{
_init = false;
//Now it's safe to check that task pane must be assigned if target is task pane
if (this.TaskPane == null && this.Target == MgViewerTarget.TaskPane)
throw new InvalidOperationException(Strings.ErrorEndInitNoTaskPaneAssigned);
}
}
}