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 { /// /// 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; /// /// Gets the defined properties of this component /// 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; } } } } } /// /// Sets a value for the given property /// /// /// 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); } /// /// Gets the value of the given property /// /// /// public object GetPropertyValue(string propertyName) { CheckAndInitProperties(); if (!_properties.ContainsKey(propertyName)) throw new InvalidOperationException(string.Format(Strings.ErrorInvalidComponentProperty, propertyName)); return _properties[propertyName].GetValue(propertyName, null); } } /// /// Defines a method that's called when the viewer's busy state changes /// /// public delegate void ViewerBusyStateEventHandler(bool busy); /// /// Defines a listener for button state changes /// public interface IButtonStateListener { /// /// The button needs to be enabled or disabled /// /// void SetEnabled(bool enabled); /// /// The button needs to be highlighted or un-highlighted /// /// void SetActive(bool outlined); /// /// The text needs to be set on the button /// /// void SetText(string text); /// /// The icon needs to be set on the button /// /// 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 { /// /// Constructor /// 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(); } /// /// Invokes the component /// 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); } } }