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; #pragma warning disable 1591 namespace OSGeo.MapGuide.Viewer { /// <summary> /// Defines a map viewer component /// </summary> public interface IMapComponent { /// <summary> /// Gets the list of component properties /// </summary> IEnumerable<PropertyInfo> ComponentProperties { get; } /// <summary> /// Sets the value of the specified component property /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> void SetPropertyValue(string propertyName, object value); /// <summary> /// Gets the value of the specified component property /// </summary> /// <param name="propertyName"></param> /// <returns></returns> object GetPropertyValue(string propertyName); } /// <summary> /// Indicates that a given CLR property is dynamically invokable. Primarily used for property /// value assignment by the AppLayout engine /// </summary> [AttributeUsage(AttributeTargets.Property, Inherited = true)] public class MgComponentPropertyAttribute : Attribute { } /// <summary> /// 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. /// </summary> [ToolboxItem(false)] public class MgComponent : Component, IMapComponent { private IMapViewer _viewer; /// <summary> /// Gets whether to disable invocation entry points (eg. buttons, menus, etc) to this component when the /// viewer is digitizing /// </summary> [Category("MapGuide Component Properties")] //NOXLATE [Description("Disables this component while the digitizing is happening")] //NOXLATE [MgComponentProperty] public virtual bool DisableWhenDigitizing { get { return true; } } /// <summary> /// Gets whether to disable invocation entry points (eg. buttons, menus, etc) to this component when the /// viewer is busy /// </summary> [Category("MapGuide Component Properties")] //NOXLATE [Description("Disables this component while the map is loading")] //NOXLATE [MgComponentProperty] public virtual bool DisableWhenMapIsLoading { get { return true; } } /// <summary> /// Gets or sets the description of this component /// </summary> [Category("MapGuide Component Properties")] //NOXLATE [Description("The description of this component")] //NOXLATE [MgComponentProperty] public string Label { get; set; } /// <summary> /// Gets or sets the tooltip text of this component /// </summary> [Category("MapGuide Component Properties")] //NOXLATE [Description("The tooltip text of this component")] //NOXLATE [MgComponentProperty] public string ToolTipText { get; set; } /// <summary> /// The icon for this component /// </summary> [Category("MapGuide Component Properties")] //NOXLATE [Description("The icon for this component")] //NOXLATE [MgComponentProperty] public Image Icon { get; set; } /// <summary> /// Gets or sets the view instance to subscribe events to /// </summary> [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); } } } /// <summary> /// Unsubscribes events from the specified viewer instance /// </summary> /// <param name="viewer"></param> 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); } } } /// <summary> /// 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 /// </summary> protected virtual bool RequiresLoadedMap { get { return true; } } protected List<IButtonStateListener> _listeners = new List<IButtonStateListener>(); 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); } /// <summary> /// Invokes this component /// </summary> public virtual void Invoke() { } private Dictionary<string, PropertyInfo> _properties; /// <summary> /// Gets the defined properties of this component /// </summary> public IEnumerable<PropertyInfo> ComponentProperties { get { CheckAndInitProperties(); return _properties.Values; } } private void CheckAndInitProperties() { if (_properties == null) { _properties = new Dictionary<string, PropertyInfo>(); 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; } } } } } /// <summary> /// Sets a value for the given property /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> 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); } /// <summary> /// Gets the value of the given property /// </summary> /// <param name="propertyName"></param> /// <returns></returns> public object GetPropertyValue(string propertyName) { CheckAndInitProperties(); if (!_properties.ContainsKey(propertyName)) throw new InvalidOperationException(string.Format(Strings.ErrorInvalidComponentProperty, propertyName)); return _properties[propertyName].GetValue(propertyName, null); } } /// <summary> /// Defines a method that's called when the viewer's busy state changes /// </summary> /// <param name="busy"></param> public delegate void ViewerBusyStateEventHandler(bool busy); /// <summary> /// Defines a listener for button state changes /// </summary> public interface IButtonStateListener { /// <summary> /// The button needs to be enabled or disabled /// </summary> /// <param name="enabled"></param> void SetEnabled(bool enabled); /// <summary> /// The button needs to be highlighted or un-highlighted /// </summary> /// <param name="outlined"></param> void SetActive(bool outlined); /// <summary> /// The text needs to be set on the button /// </summary> /// <param name="text"></param> void SetText(string text); /// <summary> /// The icon needs to be set on the button /// </summary> /// <param name="icon"></param> void SetIcon(Image icon); } /// <summary> /// Indicates the target a UI-based component should display its UI view in /// </summary> public enum MgViewerTarget { /// <summary> /// Display the UI view within the specified parent container /// </summary> TaskPane, /// <summary> /// Display the UI view within a new window /// </summary> NewWindow } /// <summary> /// The base class of all UI-based components /// </summary> [ToolboxItem(false)] public class MgViewerComponent : MgComponent, ISupportInitialize { /// <summary> /// Constructor /// </summary> protected MgViewerComponent() { this.TaskPane = null; this.Target = MgViewerTarget.NewWindow; } /// <summary> /// /// </summary> [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; /// <summary> /// Gets or sets the target that this component should display its UI view in /// </summary> [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; } } /// <summary> /// Gets or sets the owner form that any new windows displayed by this component will belong to. /// </summary> [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; } /// <summary> /// Creates the associated view. Must be overridden by subclasses /// </summary> /// <returns></returns> protected virtual MgControlView CreateControlView() { throw new NotImplementedException(); } internal MgControlView CreateControl() { return CreateControlView(); } /// <summary> /// Invokes the component /// </summary> 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); } } }