using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using System.Globalization; namespace OSGeo.MapGuide.Viewer.AppLayoutEngine { /// /// The top-level window of an AppLayout application /// public partial class Shell : Form, IShell { private Shell() { InitializeComponent(); UpdateLegendTabText(); } private static Shell _instance; /// /// The shell instance /// public static IShell Instance { get { if (null == _instance) { _instance = new Shell(); } return _instance; } } /// /// Initializes this map viewer and constructs the user interface based on the /// given AppLayout document /// /// /// public void Initialize(AppLayout layout, MgMapViewerProvider provider) { this.Text = layout.Title; if (!string.IsNullOrEmpty(layout.Language)) { try { var ci = CultureInfo.GetCultureInfo(layout.Language); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture = ci; SetLanguage(ci); } catch (Exception ex) { MessageBox.Show(ex.Message, Strings.SetLanguage); } } _menuInvoker = new MgMenuItemComponentInvoker(); _toolInvoker = new MgToolButtonComponentInvoker(); ValidateMapNames(layout); InitializeComponentSet(layout); InitializeMenu(layout.Menu); InitializeToolbar(layout.Toolbar); InitializeContextMenu(layout.ContextMenu); InitializeTaskPaneMenu(layout.TaskPane.TaskMenu); _layout = layout; if (!string.IsNullOrEmpty(_layout.Icon) && System.IO.File.Exists(_layout.Icon)) { this.Icon = new System.Drawing.Icon(_layout.Icon); } SetInfoPaneWidth(_layout.InfoPane.Width); SetInfoPaneVisible(_layout.InfoPane.IsVisible); if (_layout.InfoPane.IsVisible) { SetLegendVisbility(_layout.InfoPane.Legend.Visible); legend.ShowTooltips = _layout.InfoPane.Legend.ShowTooltips; if (_layout.InfoPane.Legend.ThemeCompressionLimit.HasValue) legend.ThemeCompressionLimit = _layout.InfoPane.Legend.ThemeCompressionLimit.Value; SetPropertyPaneVisbility(_layout.InfoPane.PropertyPane.Visible); } SetTaskPaneWidth(_layout.TaskPane.Width); if (!string.IsNullOrEmpty(_layout.TaskPane.InitialComponentID)) { var cmp = GetComponent(_layout.TaskPane.InitialComponentID) as MgViewerComponent; if (cmp != null) taskPane.SetInitialComponent(cmp); else MessageBox.Show(string.Format(Strings.WarnInitialTaskComponentNotFound, _layout.TaskPane.InitialComponentID)); } _provider = provider; mapViewer.PropertyChanged += new PropertyChangedEventHandler(OnMapViewerPropertyChanged); } const string RESERVED_CHARS = "\\:*?\"<>|&'%=/"; //NOXLATE private static void ValidateMapNames(AppLayout layout) { string mapName = layout.Map.Name; if (mapName.Contains(" ")) //NOXLATE throw new InvalidOperationException(string.Format(Strings.ErrorInvalidMapName, mapName)); foreach (char c in mapName) { if (RESERVED_CHARS.IndexOf(c) >= 0) throw new InvalidOperationException(string.Format(Strings.ErrorInvalidMapName, mapName)); } } private void SetLanguage(CultureInfo lang) { //The reason we have to do this is because we're setting the language after this object //has been initialized and resources applied. We basically have to re-apply against the //current language ComponentResourceManager resources = new ComponentResourceManager(this.GetType()); ApplyResourceToControl(resources, this, lang); resources.ApplyResources(this, "$this", lang); //NOXLATE //NOTE: Property pane is a separate case that has to be handled individually propertyPane.SetLanguage(lang); } private static void ApplyResourceToControl(ComponentResourceManager resources, Control control, CultureInfo lang) { foreach (Control c in control.Controls) { ApplyResourceToControl(resources, c, lang); resources.ApplyResources(c, c.Name, lang); } } internal void SetTaskPaneWidth(uint width) { if (appContainer.Panel2Collapsed) appContainer.Panel2Collapsed = false; //appContainer.Panel2.Width = (int)width; appContainer.SplitterDistance = (int)(appContainer.Width - width); } internal void SetInfoPaneWidth(uint width) { if (infoPaneViewerContainer.Panel1Collapsed) infoPaneViewerContainer.Panel1Collapsed = false; //infoPaneViewerContainer.Panel1.Width = (int)width; infoPaneViewerContainer.SplitterDistance = (int)width; } internal void SetLegendVisbility(bool visible) { layerPropertiesContainer.Panel1Collapsed = !visible; } internal void SetPropertyPaneVisbility(bool visible) { layerPropertiesContainer.Panel2Collapsed = !visible; } internal void SetTaskPaneVisible(bool visible) { appContainer.Panel2Collapsed = !visible; } internal void SetInfoPaneVisible(bool visible) { infoPaneViewerContainer.Panel1Collapsed = !visible; } void OnMapViewerPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsBusy") //NOXLATE { if (_loader != null) _loader.Visible = mapViewer.IsBusy; } } private AppLayout _layout; private MgMapViewerProvider _provider; private string _invokeComponentOnStartup; /// /// /// /// protected override void OnLoad(EventArgs e) { //Optimization: If legend or property pane aren't visible //don't wire them up to the controller var theLegend = legend; var thePropertyPane = propertyPane; if (!_layout.InfoPane.Legend.Visible) theLegend = null; if (!_layout.InfoPane.PropertyPane.Visible) thePropertyPane = null; new MapViewerController(mapViewer, theLegend, this, thePropertyPane); mapViewer.Init(_provider); if (!string.IsNullOrEmpty(_invokeComponentOnStartup)) { if (_components.ContainsKey(_invokeComponentOnStartup)) { _components[_invokeComponentOnStartup].Invoke(); } else { MessageBox.Show(string.Format(Strings.WarnInvokeNonExistentComponent, _invokeComponentOnStartup)); } } } private MgMenuItemComponentInvoker _menuInvoker; private MgToolButtonComponentInvoker _toolInvoker; private Dictionary _components; private void InitializeTaskPaneMenu(MenuDefinition menuDefinition) { taskMenu.DropDown.Items.Clear(); taskMenu.DropDownDirection = ToolStripDropDownDirection.BelowLeft; foreach (var item in menuDefinition.Items) { taskMenu.DropDown.Items.Add(CreateMenuItem(item)); } } private void InitializeContextMenu(MenuDefinition menuDefinition) { viewerContextMenu.Items.Clear(); foreach (var item in menuDefinition.Items) { viewerContextMenu.Items.Add(CreateMenuItem(item)); } } private ToolStripLabel _loader; private void InitializeToolbar(ToolbarDefinition toolbarDefinition) { viewerToolbar.Items.Clear(); foreach (var item in toolbarDefinition.Items) { viewerToolbar.Items.Add(CreateToolbarItem(item)); } if (_loader == null) { _loader = new ToolStripLabel(Properties.Resources.icon_loading); _loader.Alignment = ToolStripItemAlignment.Right; _loader.Text = string.Empty; _loader.ImageScaling = ToolStripItemImageScaling.None; } viewerToolbar.Items.Add(_loader); _loader.Visible = mapViewer.IsBusy; } private ToolStripItem CreateToolbarItem(ItemBase item) { if (item == null) throw new ArgumentNullException("item"); //NOXLATE if (item is SeparatorItem) { return new ToolStripSeparator(); } else if (item is CommandItem) { var cmd = (CommandItem)item; var tsi = new ToolStripButton(); _toolInvoker.SetTargetComponent(tsi, _components[cmd.ComponentID]); if (cmd.ShowLabel) tsi.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; else tsi.DisplayStyle = ToolStripItemDisplayStyle.Image; return tsi; } else if (item is SubMenu) { var sm = (SubMenu)item; var tsi = new ToolStripDropDownButton(); tsi.Image = Properties.Resources.icon_tasks; tsi.Text = sm.Label; tsi.ToolTipText = sm.Label; foreach (var child in sm.Items) { tsi.DropDown.Items.Add(CreateMenuItem(child)); } return tsi; } else { throw new NotSupportedException(string.Format(Strings.ErrorUnsupportedItemType, item.GetType().Name)); } } private ToolStripItem CreateMenuItem(ItemBase item) { if (item == null) throw new ArgumentNullException("item"); //NOXLATE if (item is SeparatorItem) { return new ToolStripSeparator(); } else if (item is CommandItem) { var cmd = (CommandItem)item; var tsi = new ToolStripMenuItem(); _menuInvoker.SetTargetComponent(tsi, _components[cmd.ComponentID]); //Disregard ShowLabel property tsi.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; //if (cmd.ShowLabel) // tsi.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; //else // tsi.DisplayStyle = ToolStripItemDisplayStyle.Image; return tsi; } else if (item is SubMenu) { var sm = (SubMenu)item; var tsi = new ToolStripMenuItem(); //tsi.Image = Properties.Resources.icon_tasks; tsi.Text = sm.Label; tsi.ToolTipText = sm.Label; foreach (var child in sm.Items) { tsi.DropDown.Items.Add(CreateMenuItem(child)); } return tsi; } else { throw new NotSupportedException(string.Format(Strings.ErrorUnsupportedItemType, item.GetType().Name)); } } private void InitializeMenu(MenuDefinition menuDefinition) { mainMenu.Items.Clear(); foreach (var item in menuDefinition.Items) { mainMenu.Items.Add(CreateMenuItem(item)); } } private void InitializeComponentSet(AppLayout layout) { var thisAsm = this.GetType().Assembly; var thisPublicTypes = thisAsm.GetExportedTypes(); var assemblies = new Dictionary(); _components = new Dictionary(); // We do this in 4 passes: // // 1. Create the components in the component set // 2. Then set the properties of the instantiated components // 3. Assign the viewer to all these components // 4. Set the owner parent and Task Pane of any MgViewerComponent instances to this instance // 1st pass foreach (var compDef in layout.Components) { if (compDef.Assembly == null) { foreach (var type in thisPublicTypes) { if (type.FullName == compDef.ClassName) { if (_components.ContainsKey(compDef.ComponentID)) throw new InvalidOperationException(string.Format(Strings.ErrorComponentAlreadyExists, compDef.ComponentID)); var comp = (MgComponent)Activator.CreateInstance(type); _components[compDef.ComponentID] = comp; //Override default label if specified if (!string.IsNullOrEmpty(compDef.Label)) comp.Label = compDef.Label; break; } } } else { if (!assemblies.ContainsKey(compDef.Assembly)) { assemblies[compDef.Assembly] = Assembly.LoadFrom(compDef.Assembly); } foreach (var type in assemblies[compDef.Assembly].GetExportedTypes()) { if (type.FullName == compDef.ClassName) { if (_components.ContainsKey(compDef.ComponentID)) throw new InvalidOperationException(string.Format(Strings.ErrorComponentAlreadyExists, compDef.ComponentID)); var comp = (MgComponent)Activator.CreateInstance(type); _components[compDef.ComponentID] = comp; break; } } } } //2nd pass foreach (var compDef in layout.Components) { var comp = _components[compDef.ComponentID]; if (compDef.Properties == null) continue; foreach (var prop in compDef.Properties) { if (prop.Value.StartsWith(StringPrefixes.MAPDEFINITION)) { var mapName = prop.Value.Substring(StringPrefixes.MAPDEFINITION.Length); //TODO: Update for multi-maps if/when we support it if (layout.Map.Name == mapName) comp.SetPropertyValue(prop.Name, layout.Map.MapDefinition); else throw new InvalidOperationException(string.Format(Strings.ErrorMapNotFound, mapName)); } else if (prop.Value.StartsWith(StringPrefixes.COLOR)) { var colorStr = prop.Value.Substring(StringPrefixes.COLOR.Length); var color = Util.FromHtmlColor(colorStr); comp.SetPropertyValue(prop.Name, color); } else if (prop.Value.StartsWith(StringPrefixes.COMPONENTID)) { var compID = prop.Value.Substring(StringPrefixes.COMPONENTID.Length); if (!_components.ContainsKey(compID)) throw new InvalidOperationException(string.Format(Strings.ErrorComponentNotFound, compID)); comp.SetPropertyValue(prop.Name, _components[compID]); } else if (prop.Value.StartsWith(StringPrefixes.ENUM)) { string [] tokens = prop.Value.Split(':'); //NOXLATE if (tokens.Length != 3) throw new InvalidOperationException(Strings.ErrorMalformedEnumString); comp.SetPropertyValue(prop.Name, Enum.Parse(Type.GetType(tokens[1]), tokens[2])); } else if (prop.Value.StartsWith(StringPrefixes.STRINGARRAY)) { var csvList = prop.Value.Substring(StringPrefixes.STRINGARRAY.Length); var values = csvList.Split(','); comp.SetPropertyValue(prop.Name, values); } else if (prop.Value.StartsWith(StringPrefixes.TASKPANEID)) //NOTE: only one taskpane instance, but we're checking this as a forward-looking measure { comp.SetPropertyValue(prop.Name, taskPane); } else if (prop.Value.StartsWith(StringPrefixes.VIEWERID)) //NOTE: only one viewer instance, but we're checking this as a forward-looking measure { comp.SetPropertyValue(prop.Name, mapViewer); } else { comp.SetPropertyValue(prop.Name, prop.Value); } } } //Apply viewer properties. We do this here because we want to respect the viewer options component //So we apply before the viewer options component gets its chance to foreach (var prop in layout.Settings) { //Special case if (prop.Name == "InvokeOnStartup") //NOXLATE { _invokeComponentOnStartup = prop.Value.Substring(StringPrefixes.COMPONENTID.Length); continue; } if (prop.Value.StartsWith(StringPrefixes.MAPDEFINITION)) { var mapName = prop.Value.Substring(StringPrefixes.MAPDEFINITION.Length); //TODO: Update for multi-maps if/when we support it if (layout.Map.Name == mapName) mapViewer.SetPropertyValue(prop.Name, layout.Map.MapDefinition); else throw new InvalidOperationException(string.Format(Strings.ErrorMapNotFound, mapName)); } else if (prop.Value.StartsWith(StringPrefixes.COLOR)) { var colorStr = prop.Value.Substring(StringPrefixes.COLOR.Length); var color = Util.FromHtmlColor(colorStr); mapViewer.SetPropertyValue(prop.Name, color); } else if (prop.Value.StartsWith(StringPrefixes.COMPONENTID)) { var compID = prop.Value.Substring(StringPrefixes.COMPONENTID.Length); if (!_components.ContainsKey(compID)) throw new InvalidOperationException(string.Format(Strings.ErrorComponentNotFound, compID)); mapViewer.SetPropertyValue(prop.Name, _components[compID]); } else if (prop.Value.StartsWith(StringPrefixes.ENUM)) { string[] tokens = prop.Value.Split(':'); //NOXLATE if (tokens.Length != 3) throw new InvalidOperationException(Strings.ErrorMalformedEnumString); mapViewer.SetPropertyValue(prop.Name, Enum.Parse(Type.GetType(tokens[1]), tokens[2])); } else if (prop.Value.StartsWith(StringPrefixes.TASKPANEID)) //NOTE: only one taskpane instance, but we're checking this as a forward-looking measure { mapViewer.SetPropertyValue(prop.Name, taskPane); } else if (prop.Value.StartsWith(StringPrefixes.VIEWERID)) //NOTE: only one viewer instance, but we're checking this as a forward-looking measure { mapViewer.SetPropertyValue(prop.Name, mapViewer); } else { mapViewer.SetPropertyValue(prop.Name, prop.Value); } } //3rd pass foreach (var compDef in layout.Components) { _components[compDef.ComponentID].Viewer = mapViewer; } //4th pass, set the owner parent and Task Pane of any MgViewerComponent instances //to this instance foreach (var comp in _components.Values) { var vc = comp as MgViewerComponent; if (vc != null) { vc.OwnerParent = this; if (vc.Target == MgViewerTarget.TaskPane) vc.TaskPane = taskPane; } } } /// /// Sets the message for the cursor position element of the status bar /// /// public void SetCursorPositionMessage(string message) { lblCoordinates.Text = message; } /// /// Sets the message for the selected feature count element of the status bar /// /// public void SetFeatureSelectedMessage(string message) { lblSelected.Text = message; } /// /// Sets the message for the map scale element of the status bar /// /// public void SetMapScaleMessage(string message) { lblMapScale.Text = message; } /// /// Sets the message for the map size element of the status bar /// /// public void SetMapSizeMessage(string message) { lblMapSize.Text = message; } private void btnInitialTask_Click(object sender, EventArgs e) { taskPane.LoadInitialTask(); } /// /// Gets the component by its registered component id /// /// /// public MgComponent GetComponent(string componentId) { return _components.ContainsKey(componentId) ? _components[componentId] : null; } /// /// Gets the legend component /// public IMapLegend Legend { get { return legend; } } /// /// Gets the property pane component /// public IPropertyPane PropertyPane { get { return propertyPane; } } /// /// Gets the task pane component /// public MgTaskPane TaskPane { get { return taskPane; } } /// /// Reloads the map viewer /// /// public void ReloadViewer(MgMapViewerProvider provider) { _provider = provider; mapViewer.Init(_provider); } private void legend_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsBusy") { UpdateLegendTabText(); } } private void UpdateLegendTabText() { TAB_LEGEND.Text = legend.IsBusy ? Strings.TextLayersRefreshing : Strings.TextLayers; } } /// /// Defines the top-level window of an AppLayout application /// public interface IShell : IMapStatusBar { /// /// Gets the component by its registered component id /// /// /// MgComponent GetComponent(string componentId); /// /// Gets the legend component /// IMapLegend Legend { get; } /// /// Gets the property pane component /// IPropertyPane PropertyPane { get; } /// /// Gets the task pane component /// MgTaskPane TaskPane { get; } /// /// Reloads the map viewer /// /// void ReloadViewer(MgMapViewerProvider provider); } }