diff --git a/.github/workflows/test-builds.yml b/.github/workflows/test-builds.yml new file mode 100644 index 000000000..8f026ba7a --- /dev/null +++ b/.github/workflows/test-builds.yml @@ -0,0 +1,36 @@ +name: Test Builds + +on: + workflow_dispatch: + push: + branches: [ development ] + pull_request: + branches: [ development ] + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + - name: Setup NuGet + uses: NuGet/setup-nuget@v1.0.5 + - name: setup-msbuild + uses: microsoft/setup-msbuild@v1.1 + - name: Restore Packages + run: nuget restore MySolution.sln + - name: Build Release solution + run: msbuild XenAdmin.sln -property:Configuration=Debug + - name: Build Debug solution + run: msbuild XenAdmin.sln -property:Configuration=Release + - name: Upload Release Artifacts + uses: actions/upload-artifact@v4 + with: + name: drop-release + path: XenAdmin/bin/Release/net481 + - name: Upload Debug Artifacts + uses: actions/upload-artifact@v4 + with: + name: drop-debug + path: XenAdmin/bin/Debug/net481 \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/CustomTabControl.cs b/XenAdmin/Controls/TabControl/CustomTabControl.cs new file mode 100644 index 000000000..b97f5af22 --- /dev/null +++ b/XenAdmin/Controls/TabControl/CustomTabControl.cs @@ -0,0 +1,1361 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.Security.Permissions; +using System.Windows.Forms; + +namespace XenAdmin.Controls.TabControl +{ + [ToolboxBitmap(typeof(System.Windows.Forms.TabControl))] + public class CustomTabControl : System.Windows.Forms.TabControl + { + #region String formatting + + private StringFormat GetStringFormat() + { + StringFormat format = null; + + // Rotate Text by 90 degrees for left and right tabs + switch (Alignment) + { + case TabAlignment.Top: + case TabAlignment.Bottom: + format = new StringFormat(); + break; + case TabAlignment.Left: + case TabAlignment.Right: + format = new StringFormat(StringFormatFlags.DirectionVertical); + break; + } + + format.Alignment = StringAlignment.Center; + format.LineAlignment = StringAlignment.Center; + if (FindForm() != null && FindForm().KeyPreview) + format.HotkeyPrefix = HotkeyPrefix.Show; + else + format.HotkeyPrefix = HotkeyPrefix.Hide; + if (RightToLeft == RightToLeft.Yes) + format.FormatFlags = format.FormatFlags | StringFormatFlags.DirectionRightToLeft; + return format; + } + + #endregion + + #region Construction + + public CustomTabControl() + { + SetStyle( + ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque | + ControlStyles.ResizeRedraw, true); + + _BackBuffer = new Bitmap(Width, Height); + _BackBufferGraphics = Graphics.FromImage(_BackBuffer); + _TabBuffer = new Bitmap(Width, Height); + _TabBufferGraphics = Graphics.FromImage(_TabBuffer); + + DisplayStyle = TabStyle.Default; + } + + protected override void OnCreateControl() + { + base.OnCreateControl(); + OnFontChanged(EventArgs.Empty); + } + + + protected override CreateParams CreateParams + { + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + var cp = base.CreateParams; + if (RightToLeftLayout) + cp.ExStyle = cp.ExStyle | NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT; + return cp; + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + if (_BackImage != null) _BackImage.Dispose(); + if (_BackBufferGraphics != null) _BackBufferGraphics.Dispose(); + if (_BackBuffer != null) _BackBuffer.Dispose(); + if (_TabBufferGraphics != null) _TabBufferGraphics.Dispose(); + if (_TabBuffer != null) _TabBuffer.Dispose(); + + if (_StyleProvider != null) _StyleProvider.Dispose(); + } + } + + #endregion + + #region Private variables + + private Bitmap _BackImage; + private Bitmap _BackBuffer; + private Graphics _BackBufferGraphics; + private Bitmap _TabBuffer; + private Graphics _TabBufferGraphics; + + private int _oldValue; + private Point _dragStartPosition = Point.Empty; + + private TabStyle _Style; + private TabStyleProvider _StyleProvider; + + private List _TabPages; + + #endregion + + #region Public properties + + [Category("Appearance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public TabStyleProvider DisplayStyleProvider + { + get + { + if (_StyleProvider == null) DisplayStyle = TabStyle.Default; + + return _StyleProvider; + } + set => _StyleProvider = value; + } + + [Category("Appearance")] + [DefaultValue(typeof(TabStyle), "Default")] + [RefreshProperties(RefreshProperties.All)] + public TabStyle DisplayStyle + { + get => _Style; + set + { + if (_Style != value) + { + _Style = value; + _StyleProvider = TabStyleProvider.CreateProvider(this); + Invalidate(); + } + } + } + + [Category("Appearance")] + [RefreshProperties(RefreshProperties.All)] + public new bool Multiline + { + get => base.Multiline; + set + { + base.Multiline = value; + Invalidate(); + } + } + + + // Hide the Padding attribute so it can not be changed + // We are handling this on the Style Provider + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new Point Padding + { + get => DisplayStyleProvider.Padding; + set => DisplayStyleProvider.Padding = value; + } + + public override bool RightToLeftLayout + { + get => base.RightToLeftLayout; + set + { + base.RightToLeftLayout = value; + UpdateStyles(); + } + } + + + // Hide the HotTrack attribute so it can not be changed + // We are handling this on the Style Provider + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new bool HotTrack + { + get => DisplayStyleProvider.HotTrack; + set => DisplayStyleProvider.HotTrack = value; + } + + [Category("Appearance")] + public new TabAlignment Alignment + { + get => base.Alignment; + set + { + base.Alignment = value; + switch (value) + { + case TabAlignment.Top: + case TabAlignment.Bottom: + Multiline = false; + break; + case TabAlignment.Left: + case TabAlignment.Right: + Multiline = true; + break; + } + } + } + + // Hide the Appearance attribute so it can not be changed + // We don't want it as we are doing all the painting. + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value")] + public new TabAppearance Appearance + { + get => base.Appearance; + set => + // Don't permit setting to other appearances as we are doing all the painting + base.Appearance = TabAppearance.Normal; + } + + public override Rectangle DisplayRectangle + { + get + { + var tabStripHeight = 0; + var itemHeight = 0; + + if (Alignment <= TabAlignment.Bottom) + itemHeight = ItemSize.Height; + else + itemHeight = ItemSize.Width; + + tabStripHeight = 5 + itemHeight * RowCount; + + var rect = new Rectangle(4, tabStripHeight, Width - 8, Height - tabStripHeight - 4); + switch (Alignment) + { + case TabAlignment.Top: + rect = new Rectangle(4, tabStripHeight, Width - 8, Height - tabStripHeight - 4); + break; + case TabAlignment.Bottom: + rect = new Rectangle(4, 4, Width - 8, Height - tabStripHeight - 4); + break; + case TabAlignment.Left: + rect = new Rectangle(tabStripHeight, 4, Width - tabStripHeight - 4, Height - 8); + break; + case TabAlignment.Right: + rect = new Rectangle(4, 4, Width - tabStripHeight - 4, Height - 8); + break; + } + + return rect; + } + } + + [Browsable(false)] + public int ActiveIndex + { + get + { + var hitTestInfo = new NativeMethods.TCHITTESTINFO(PointToClient(Control.MousePosition)); + var index = NativeMethods.SendMessage(Handle, NativeMethods.TCM_HITTEST, IntPtr.Zero, + NativeMethods.ToIntPtr(hitTestInfo)).ToInt32(); + if (index == -1) return -1; + + if (TabPages[index].Enabled) + return index; + return -1; + } + } + + [Browsable(false)] + public TabPage ActiveTab + { + get + { + var activeIndex = ActiveIndex; + if (activeIndex > -1) + return TabPages[activeIndex]; + return null; + } + } + + #endregion + + #region Extension methods + + public void HideTab(TabPage page) + { + if (page != null && TabPages.Contains(page)) + { + BackupTabPages(); + TabPages.Remove(page); + } + } + + public void HideTab(int index) + { + if (IsValidTabIndex(index)) HideTab(_TabPages[index]); + } + + public void HideTab(string key) + { + if (TabPages.ContainsKey(key)) HideTab(TabPages[key]); + } + + public void ShowTab(TabPage page) + { + if (page != null) + { + if (_TabPages != null) + { + if (!TabPages.Contains(page) + && _TabPages.Contains(page)) + { + // Get insert point from backup of pages + var pageIndex = _TabPages.IndexOf(page); + if (pageIndex > 0) + { + var start = pageIndex - 1; + + // Check for presence of earlier pages in the visible tabs + for (var index = start; index >= 0; index--) + if (TabPages.Contains(_TabPages[index])) + { + // Set insert point to the right of the last present tab + pageIndex = TabPages.IndexOf(_TabPages[index]) + 1; + break; + } + } + + // Insert the page, or add to the end + if (pageIndex >= 0 && pageIndex < TabPages.Count) + TabPages.Insert(pageIndex, page); + else + TabPages.Add(page); + } + } + else + { + // If the page is not found at all then just add it + if (!TabPages.Contains(page)) TabPages.Add(page); + } + } + } + + public void ShowTab(int index) + { + if (IsValidTabIndex(index)) ShowTab(_TabPages[index]); + } + + public void ShowTab(string key) + { + if (_TabPages != null) + { + var tab = _TabPages.Find(delegate(TabPage page) + { + return page.Name.Equals(key, StringComparison.OrdinalIgnoreCase); + }); + ShowTab(tab); + } + } + + private bool IsValidTabIndex(int index) + { + BackupTabPages(); + return index >= 0 && index < _TabPages.Count; + } + + private void BackupTabPages() + { + if (_TabPages == null) + { + _TabPages = new List(); + foreach (TabPage page in TabPages) _TabPages.Add(page); + } + } + + #endregion + + #region Drag 'n' Drop + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (AllowDrop) _dragStartPosition = new Point(e.X, e.Y); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + if (AllowDrop) _dragStartPosition = Point.Empty; + } + + protected override void OnDragOver(DragEventArgs drgevent) + { + base.OnDragOver(drgevent); + + if (drgevent.Data.GetDataPresent(typeof(TabPage))) + drgevent.Effect = DragDropEffects.Move; + else + drgevent.Effect = DragDropEffects.None; + } + + protected override void OnDragDrop(DragEventArgs drgevent) + { + base.OnDragDrop(drgevent); + if (drgevent.Data.GetDataPresent(typeof(TabPage))) + { + drgevent.Effect = DragDropEffects.Move; + + var dragTab = (TabPage)drgevent.Data.GetData(typeof(TabPage)); + + if (ActiveTab == dragTab) return; + + // Capture insert point and adjust for removal of tab + // We cannot assess this after removal as differeing tab sizes will cause + // inaccuracies in the activeTab at insert point. + var insertPoint = ActiveIndex; + if (dragTab.Parent.Equals(this) && TabPages.IndexOf(dragTab) < insertPoint) insertPoint--; + if (insertPoint < 0) insertPoint = 0; + + // Remove from current position (could be another tabcontrol) + ((System.Windows.Forms.TabControl)dragTab.Parent).TabPages.Remove(dragTab); + + // Add to current position + TabPages.Insert(insertPoint, dragTab); + SelectedTab = dragTab; + + // deal with hidden tab handling? + } + } + + private void StartDragDrop() + { + if (!_dragStartPosition.IsEmpty) + { + var dragTab = SelectedTab; + if (dragTab != null) + { + // Test for movement greater than the drag activation trigger area + var dragTestRect = new Rectangle(_dragStartPosition, Size.Empty); + dragTestRect.Inflate(SystemInformation.DragSize); + var pt = PointToClient(Control.MousePosition); + if (!dragTestRect.Contains(pt)) + { + DoDragDrop(dragTab, DragDropEffects.All); + _dragStartPosition = Point.Empty; + } + } + } + } + + #endregion + + #region Events + + [Category("Action")] public event ScrollEventHandler HScroll; + [Category("Action")] public event EventHandler TabImageClick; + [Category("Action")] public event EventHandler TabClosing; + + #endregion + + #region Base class event processing + + protected override void OnFontChanged(EventArgs e) + { + var hFont = Font.ToHfont(); + NativeMethods.SendMessage(Handle, NativeMethods.WM_SETFONT, hFont, (IntPtr)(-1)); + NativeMethods.SendMessage(Handle, NativeMethods.WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero); + UpdateStyles(); + if (Visible) Invalidate(); + } + + protected override void OnResize(EventArgs e) + { + // Recreate the buffer for manual double buffering + if (Width > 0 && Height > 0) + { + if (_BackImage != null) + { + _BackImage.Dispose(); + _BackImage = null; + } + + if (_BackBufferGraphics != null) _BackBufferGraphics.Dispose(); + if (_BackBuffer != null) _BackBuffer.Dispose(); + + _BackBuffer = new Bitmap(Width, Height); + _BackBufferGraphics = Graphics.FromImage(_BackBuffer); + + if (_TabBufferGraphics != null) _TabBufferGraphics.Dispose(); + if (_TabBuffer != null) _TabBuffer.Dispose(); + + _TabBuffer = new Bitmap(Width, Height); + _TabBufferGraphics = Graphics.FromImage(_TabBuffer); + + if (_BackImage != null) + { + _BackImage.Dispose(); + _BackImage = null; + } + } + + base.OnResize(e); + } + + protected override void OnParentBackColorChanged(EventArgs e) + { + if (_BackImage != null) + { + _BackImage.Dispose(); + _BackImage = null; + } + + base.OnParentBackColorChanged(e); + } + + protected override void OnParentBackgroundImageChanged(EventArgs e) + { + if (_BackImage != null) + { + _BackImage.Dispose(); + _BackImage = null; + } + + base.OnParentBackgroundImageChanged(e); + } + + private void OnParentResize(object sender, EventArgs e) + { + if (Visible) Invalidate(); + } + + + protected override void OnParentChanged(EventArgs e) + { + base.OnParentChanged(e); + if (Parent != null) Parent.Resize += OnParentResize; + } + + protected override void OnSelecting(TabControlCancelEventArgs e) + { + base.OnSelecting(e); + + // Do not allow selecting of disabled tabs + if (e.Action == TabControlAction.Selecting && e.TabPage != null && !e.TabPage.Enabled) e.Cancel = true; + } + + protected override void OnMove(EventArgs e) + { + if (Width > 0 && Height > 0) + if (_BackImage != null) + { + _BackImage.Dispose(); + _BackImage = null; + } + + base.OnMove(e); + Invalidate(); + } + + protected override void OnControlAdded(ControlEventArgs e) + { + base.OnControlAdded(e); + if (Visible) Invalidate(); + } + + protected override void OnControlRemoved(ControlEventArgs e) + { + base.OnControlRemoved(e); + if (Visible) Invalidate(); + } + + + [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)] + protected override bool ProcessMnemonic(char charCode) + { + foreach (TabPage page in TabPages) + if (IsMnemonic(charCode, page.Text)) + { + SelectedTab = page; + return true; + } + + return base.ProcessMnemonic(charCode); + } + + protected override void OnSelectedIndexChanged(EventArgs e) + { + base.OnSelectedIndexChanged(e); + } + + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + [DebuggerStepThrough] + protected override void WndProc(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_HSCROLL: + + // Raise the scroll event when the scroller is scrolled + base.WndProc(ref m); + OnHScroll(new ScrollEventArgs((ScrollEventType)NativeMethods.LoWord(m.WParam), _oldValue, + NativeMethods.HiWord(m.WParam), ScrollOrientation.HorizontalScroll)); + break; +// case NativeMethods.WM_PAINT: +// +// // Handle painting ourselves rather than call the base OnPaint. +// CustomPaint(ref m); +// break; + + default: + base.WndProc(ref m); + break; + } + } + + protected override void OnMouseClick(MouseEventArgs e) + { + var index = ActiveIndex; + + // If we are clicking on an image then raise the ImageClicked event before raising the standard mouse click event + // if there if a handler. + if (index > -1 && TabImageClick != null + && (TabPages[index].ImageIndex > -1 || !string.IsNullOrEmpty(TabPages[index].ImageKey)) + && GetTabImageRect(index).Contains(MousePosition)) + { + OnTabImageClick(new TabControlEventArgs(TabPages[index], index, TabControlAction.Selected)); + + // Fire the base event + base.OnMouseClick(e); + } + else + { + // Fire the base event + base.OnMouseClick(e); + } + } + + protected virtual void OnTabImageClick(TabControlEventArgs e) + { + if (TabImageClick != null) TabImageClick(this, e); + } + + protected virtual void OnTabClosing(TabControlCancelEventArgs e) + { + if (TabClosing != null) TabClosing(this, e); + } + + protected virtual void OnHScroll(ScrollEventArgs e) + { + // repaint the moved tabs + Invalidate(); + + // Raise the event + if (HScroll != null) HScroll(this, e); + + if (e.Type == ScrollEventType.EndScroll) _oldValue = e.NewValue; + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // Initialise Drag Drop + if (AllowDrop && e.Button == MouseButtons.Left) StartDragDrop(); + } + + #endregion + + #region Basic drawing methods + +// private void CustomPaint(ref Message m){ +// NativeMethods.PAINTSTRUCT paintStruct = new NativeMethods.PAINTSTRUCT(); +// NativeMethods.BeginPaint(m.HWnd, ref paintStruct); +// using (Graphics screenGraphics = this.CreateGraphics()) { +// this.CustomPaint(screenGraphics); +// } +// NativeMethods.EndPaint(m.HWnd, ref paintStruct); +// } + + protected override void OnPaint(PaintEventArgs e) + { + if (layoutSuspended) return; + + // We must always paint the entire area of the tab control + if (e.ClipRectangle.Equals(ClientRectangle)) + CustomPaint(e.Graphics); + else + // it is less intensive to just reinvoke the paint with the whole surface available to draw on. + Invalidate(); + } + + private bool layoutSuspended; + + public new void SuspendLayout() + { + layoutSuspended = true; + base.SuspendLayout(); + } + + public new void ResumeLayout() + { + layoutSuspended = false; + base.ResumeLayout(); + } + + private void CustomPaint(Graphics screenGraphics) + { + // We render into a bitmap that is then drawn in one shot rather than using + // double buffering built into the control as the built in buffering + // messes up the background painting. + // Equally the .Net 2.0 BufferedGraphics object causes the background painting + // to mess up, which is why we use this .Net 1.1 buffering technique. + + // Buffer code from Gil. Schmidt http://www.codeproject.com/KB/graphics/DoubleBuffering.aspx + + if (Width > 0 && Height > 0) + { + if (_BackImage == null) + { + // Cached Background Image + _BackImage = new Bitmap(Width, Height); + var backGraphics = Graphics.FromImage(_BackImage); + backGraphics.Clear(Color.Transparent); + PaintTransparentBackground(backGraphics, ClientRectangle); + } + + _BackBufferGraphics.Clear(Color.Transparent); + _BackBufferGraphics.DrawImageUnscaled(_BackImage, 0, 0); + + _TabBufferGraphics.Clear(Color.Transparent); + + if (TabCount > 0) + { + // When top or bottom and scrollable we need to clip the sides from painting the tabs. + // Left and right are always multiline. + if (Alignment <= TabAlignment.Bottom && !Multiline) + _TabBufferGraphics.Clip = new Region(new RectangleF(ClientRectangle.X + 3, ClientRectangle.Y, + ClientRectangle.Width - 6, ClientRectangle.Height)); + + // Draw each tabpage from right to left. We do it this way to handle + // the overlap correctly. + if (Multiline) + { + for (var row = 0; row < RowCount; row++) + for (var index = TabCount - 1; index >= 0; index--) + if (index != SelectedIndex && (RowCount == 1 || GetTabRow(index) == row)) + DrawTabPage(index, _TabBufferGraphics); + } + else + { + for (var index = TabCount - 1; index >= 0; index--) + if (index != SelectedIndex) + DrawTabPage(index, _TabBufferGraphics); + } + + // The selected tab must be drawn last so it appears on top. + if (SelectedIndex > -1) DrawTabPage(SelectedIndex, _TabBufferGraphics); + } + + _TabBufferGraphics.Flush(); + + // Paint the tabs on top of the background + + // Create a new color matrix and set the alpha value to 0.5 + var alphaMatrix = new ColorMatrix(); + alphaMatrix.Matrix00 = alphaMatrix.Matrix11 = alphaMatrix.Matrix22 = alphaMatrix.Matrix44 = 1; + alphaMatrix.Matrix33 = _StyleProvider.Opacity; + + // Create a new image attribute object and set the color matrix to + // the one just created + using (var alphaAttributes = new ImageAttributes()) + { + alphaAttributes.SetColorMatrix(alphaMatrix); + + // Draw the original image with the image attributes specified + _BackBufferGraphics.DrawImage(_TabBuffer, + new Rectangle(0, 0, _TabBuffer.Width, _TabBuffer.Height), + 0, 0, _TabBuffer.Width, _TabBuffer.Height, GraphicsUnit.Pixel, + alphaAttributes); + } + + _BackBufferGraphics.Flush(); + + // Now paint this to the screen + + + // We want to paint the whole tabstrip and border every time + // so that the hot areas update correctly, along with any overlaps + + // paint the tabs etc. + if (RightToLeftLayout) + screenGraphics.DrawImageUnscaled(_BackBuffer, -1, 0); + else + screenGraphics.DrawImageUnscaled(_BackBuffer, 0, 0); + } + } + + protected void PaintTransparentBackground(Graphics graphics, Rectangle clipRect) + { + if (Parent != null) + { + // Set the cliprect to be relative to the parent + clipRect.Offset(Location); + + // Save the current state before we do anything. + var state = graphics.Save(); + + // Set the graphicsobject to be relative to the parent + graphics.TranslateTransform(-Location.X, -Location.Y); + graphics.SmoothingMode = SmoothingMode.HighSpeed; + + // Paint the parent + var e = new PaintEventArgs(graphics, clipRect); + try + { + InvokePaintBackground(Parent, e); + InvokePaint(Parent, e); + } + finally + { + // Restore the graphics state and the clipRect to their original locations + graphics.Restore(state); + clipRect.Offset(-Location.X, -Location.Y); + } + } + } + + private void DrawTabPage(int index, Graphics graphics) + { + graphics.SmoothingMode = SmoothingMode.HighSpeed; + + // Get TabPageBorder + using (var tabPageBorderPath = GetTabPageBorder(index)) + { + // Paint the background + using (var fillBrush = _StyleProvider.GetPageBackgroundBrush(index)) + { + graphics.FillPath(fillBrush, tabPageBorderPath); + } + + // Paint the tab + _StyleProvider.PaintTab(index, graphics); + + // Draw any image + DrawTabImage(index, graphics); + + // Draw the text + DrawTabText(index, graphics); + + // Paint the border + DrawTabBorder(tabPageBorderPath, index, graphics); + } + } + + private void DrawTabBorder(GraphicsPath path, int index, Graphics graphics) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + Color borderColor; + if (index == SelectedIndex) + borderColor = _StyleProvider.BorderColorSelected; + else if (_StyleProvider.HotTrack && index == ActiveIndex) + borderColor = _StyleProvider.BorderColorHot; + else + borderColor = _StyleProvider.BorderColor; + + using (var borderPen = new Pen(borderColor)) + { + graphics.DrawPath(borderPen, path); + } + } + + private void DrawTabText(int index, Graphics graphics) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; + var tabBounds = GetTabTextRect(index); + + if (SelectedIndex == index) + { + using (Brush textBrush = new SolidBrush(_StyleProvider.TextColorSelected)) + { + graphics.DrawString(TabPages[index].Text, Font, textBrush, tabBounds, GetStringFormat()); + } + } + else + { + if (TabPages[index].Enabled) + using (Brush textBrush = new SolidBrush(_StyleProvider.TextColor)) + { + graphics.DrawString(TabPages[index].Text, Font, textBrush, tabBounds, GetStringFormat()); + } + else + using (Brush textBrush = new SolidBrush(_StyleProvider.TextColorDisabled)) + { + graphics.DrawString(TabPages[index].Text, Font, textBrush, tabBounds, GetStringFormat()); + } + } + } + + private void DrawTabImage(int index, Graphics graphics) + { + Image tabImage = null; + if (TabPages[index].ImageIndex > -1 && ImageList != null && + ImageList.Images.Count > TabPages[index].ImageIndex) + tabImage = ImageList.Images[TabPages[index].ImageIndex]; + else if (!string.IsNullOrEmpty(TabPages[index].ImageKey) && !TabPages[index].ImageKey + .Equals("(none)", + StringComparison.OrdinalIgnoreCase) + && ImageList != null && + ImageList.Images.ContainsKey(TabPages[index] + .ImageKey)) + tabImage = ImageList.Images[TabPages[index].ImageKey]; + + if (tabImage != null) + { + if (RightToLeftLayout) tabImage.RotateFlip(RotateFlipType.RotateNoneFlipX); + var imageRect = GetTabImageRect(index); + if (TabPages[index].Enabled) + graphics.DrawImage(tabImage, imageRect); + else + ControlPaint.DrawImageDisabled(graphics, tabImage, imageRect.X, imageRect.Y, Color.Transparent); + } + } + + #endregion + + #region Tab borders and bounds properties + + private GraphicsPath GetTabPageBorder(int index) + { + var path = new GraphicsPath(); + var pageBounds = GetPageBounds(index); + var tabBounds = _StyleProvider.GetTabRect(index); + _StyleProvider.AddTabBorder(path, tabBounds); + AddPageBorder(path, pageBounds, tabBounds); + + path.CloseFigure(); + return path; + } + + public Rectangle GetPageBounds(int index) + { + var pageBounds = TabPages[index].Bounds; + pageBounds.Width += 1; + pageBounds.Height += 1; + pageBounds.X -= 1; + pageBounds.Y -= 1; + + if (pageBounds.Bottom > Height - 4) pageBounds.Height -= pageBounds.Bottom - Height + 4; + return pageBounds; + } + + private Rectangle GetTabTextRect(int index) + { + var textRect = new Rectangle(); + using (var path = _StyleProvider.GetTabBorder(index)) + { + var tabBounds = path.GetBounds(); + + textRect = new Rectangle((int)tabBounds.X, (int)tabBounds.Y, (int)tabBounds.Width, + (int)tabBounds.Height); + + // Make it shorter or thinner to fit the height or width because of the padding added to the tab for painting + switch (Alignment) + { + case TabAlignment.Top: + textRect.Y += 4; + textRect.Height -= 6; + break; + case TabAlignment.Bottom: + textRect.Y += 2; + textRect.Height -= 6; + break; + case TabAlignment.Left: + textRect.X += 4; + textRect.Width -= 6; + break; + case TabAlignment.Right: + textRect.X += 2; + textRect.Width -= 6; + break; + } + + // If there is an image allow for it + if (ImageList != null && (TabPages[index].ImageIndex > -1 + || (!string.IsNullOrEmpty(TabPages[index].ImageKey) + && !TabPages[index].ImageKey.Equals("(none)", + StringComparison.OrdinalIgnoreCase)))) + { + var imageRect = GetTabImageRect(index); + if ((_StyleProvider.ImageAlign & NativeMethods.AnyLeftAlign) != 0) + { + if (Alignment <= TabAlignment.Bottom) + { + textRect.X = imageRect.Right + 4; + textRect.Width -= textRect.Right - (int)tabBounds.Right; + } + else + { + textRect.Y = imageRect.Y + 4; + textRect.Height -= textRect.Bottom - (int)tabBounds.Bottom; + } + } + else + { + if (Alignment <= TabAlignment.Bottom) + textRect.Width -= (int)tabBounds.Right - imageRect.X + 4; + else + textRect.Height -= (int)tabBounds.Bottom - imageRect.Y + 4; + } + } + + // Ensure it fits inside the path at the centre line + if (Alignment <= TabAlignment.Bottom) + { + while (!path.IsVisible(textRect.Right, textRect.Y) && textRect.Width > 0) textRect.Width -= 1; + while (!path.IsVisible(textRect.X, textRect.Y) && textRect.Width > 0) + { + textRect.X += 1; + textRect.Width -= 1; + } + } + else + { + while (!path.IsVisible(textRect.X, textRect.Bottom) && textRect.Height > 0) textRect.Height -= 1; + while (!path.IsVisible(textRect.X, textRect.Y) && textRect.Height > 0) + { + textRect.Y += 1; + textRect.Height -= 1; + } + } + } + + return textRect; + } + + public int GetTabRow(int index) + { + // All calculations will use this rect as the base point + // because the itemsize does not return the correct width. + var rect = GetTabRect(index); + + var row = -1; + + switch (Alignment) + { + case TabAlignment.Top: + row = (rect.Y - 2) / rect.Height; + break; + case TabAlignment.Bottom: + row = (Height - rect.Y - 2) / rect.Height - 1; + break; + case TabAlignment.Left: + row = (rect.X - 2) / rect.Width; + break; + case TabAlignment.Right: + row = (Width - rect.X - 2) / rect.Width - 1; + break; + } + + return row; + } + + public Point GetTabPosition(int index) + { + // If we are not multiline then the column is the index and the row is 0. + if (!Multiline) return new Point(0, index); + + // If there is only one row then the column is the index + if (RowCount == 1) return new Point(0, index); + + // We are in a true multi-row scenario + var row = GetTabRow(index); + var rect = GetTabRect(index); + var column = -1; + + // Scan from left to right along rows, skipping to next row if it is not the one we want. + for (var testIndex = 0; testIndex < TabCount; testIndex++) + { + var testRect = GetTabRect(testIndex); + if (Alignment <= TabAlignment.Bottom) + { + if (testRect.Y == rect.Y) column += 1; + } + else + { + if (testRect.X == rect.X) column += 1; + } + + if (testRect.Location.Equals(rect.Location)) return new Point(row, column); + } + + return new Point(0, 0); + } + + public bool IsFirstTabInRow(int index) + { + if (index < 0) return false; + var firstTabinRow = index == 0; + if (!firstTabinRow) + { + if (Alignment <= TabAlignment.Bottom) + { + if (GetTabRect(index).X == 2) firstTabinRow = true; + } + else + { + if (GetTabRect(index).Y == 2) firstTabinRow = true; + } + } + + return firstTabinRow; + } + + private void AddPageBorder(GraphicsPath path, Rectangle pageBounds, Rectangle tabBounds) + { + switch (Alignment) + { + case TabAlignment.Top: + path.AddLine(tabBounds.Right, pageBounds.Y, pageBounds.Right, pageBounds.Y); + path.AddLine(pageBounds.Right, pageBounds.Y, pageBounds.Right, pageBounds.Bottom); + path.AddLine(pageBounds.Right, pageBounds.Bottom, pageBounds.X, pageBounds.Bottom); + path.AddLine(pageBounds.X, pageBounds.Bottom, pageBounds.X, pageBounds.Y); + path.AddLine(pageBounds.X, pageBounds.Y, tabBounds.X, pageBounds.Y); + break; + case TabAlignment.Bottom: + path.AddLine(tabBounds.X, pageBounds.Bottom, pageBounds.X, pageBounds.Bottom); + path.AddLine(pageBounds.X, pageBounds.Bottom, pageBounds.X, pageBounds.Y); + path.AddLine(pageBounds.X, pageBounds.Y, pageBounds.Right, pageBounds.Y); + path.AddLine(pageBounds.Right, pageBounds.Y, pageBounds.Right, pageBounds.Bottom); + path.AddLine(pageBounds.Right, pageBounds.Bottom, tabBounds.Right, pageBounds.Bottom); + break; + case TabAlignment.Left: + path.AddLine(pageBounds.X, tabBounds.Y, pageBounds.X, pageBounds.Y); + path.AddLine(pageBounds.X, pageBounds.Y, pageBounds.Right, pageBounds.Y); + path.AddLine(pageBounds.Right, pageBounds.Y, pageBounds.Right, pageBounds.Bottom); + path.AddLine(pageBounds.Right, pageBounds.Bottom, pageBounds.X, pageBounds.Bottom); + path.AddLine(pageBounds.X, pageBounds.Bottom, pageBounds.X, tabBounds.Bottom); + break; + case TabAlignment.Right: + path.AddLine(pageBounds.Right, tabBounds.Bottom, pageBounds.Right, pageBounds.Bottom); + path.AddLine(pageBounds.Right, pageBounds.Bottom, pageBounds.X, pageBounds.Bottom); + path.AddLine(pageBounds.X, pageBounds.Bottom, pageBounds.X, pageBounds.Y); + path.AddLine(pageBounds.X, pageBounds.Y, pageBounds.Right, pageBounds.Y); + path.AddLine(pageBounds.Right, pageBounds.Y, pageBounds.Right, tabBounds.Y); + break; + } + } + + private Rectangle GetTabImageRect(int index) + { + using (var tabBorderPath = _StyleProvider.GetTabBorder(index)) + { + return GetTabImageRect(tabBorderPath); + } + } + + private Rectangle GetTabImageRect(GraphicsPath tabBorderPath) + { + var imageRect = new Rectangle(); + var rect = tabBorderPath.GetBounds(); + + // Make it shorter or thinner to fit the height or width because of the padding added to the tab for painting + switch (Alignment) + { + case TabAlignment.Top: + rect.Y += 4; + rect.Height -= 6; + break; + case TabAlignment.Bottom: + rect.Y += 2; + rect.Height -= 6; + break; + case TabAlignment.Left: + rect.X += 4; + rect.Width -= 6; + break; + case TabAlignment.Right: + rect.X += 2; + rect.Width -= 6; + break; + } + + // Ensure image is fully visible + if (Alignment <= TabAlignment.Bottom) + { + if ((_StyleProvider.ImageAlign & NativeMethods.AnyLeftAlign) != 0) + { + imageRect = new Rectangle((int)rect.X, + (int)rect.Y + (int)Math.Floor((double)((int)rect.Height - 16) / 2), 16, 16); + while (!tabBorderPath.IsVisible(imageRect.X, imageRect.Y)) imageRect.X += 1; + imageRect.X += 4; + } + else if ((_StyleProvider.ImageAlign & NativeMethods.AnyCenterAlign) != 0) + { + imageRect = new Rectangle( + (int)rect.X + + (int)Math.Floor((double)(((int)rect.Right - (int)rect.X - (int)rect.Height + 2) / 2)), + (int)rect.Y + (int)Math.Floor((double)((int)rect.Height - 16) / 2), 16, 16); + } + else + { + imageRect = new Rectangle((int)rect.Right, + (int)rect.Y + (int)Math.Floor((double)((int)rect.Height - 16) / 2), 16, 16); + while (!tabBorderPath.IsVisible(imageRect.Right, imageRect.Y)) imageRect.X -= 1; + imageRect.X -= 4; + } + } + else + { + if ((_StyleProvider.ImageAlign & NativeMethods.AnyLeftAlign) != 0) + { + imageRect = new Rectangle((int)rect.X + (int)Math.Floor((double)((int)rect.Width - 16) / 2), + (int)rect.Y, 16, 16); + while (!tabBorderPath.IsVisible(imageRect.X, imageRect.Y)) imageRect.Y += 1; + imageRect.Y += 4; + } + else if ((_StyleProvider.ImageAlign & NativeMethods.AnyCenterAlign) != 0) + { + imageRect = new Rectangle((int)rect.X + (int)Math.Floor((double)((int)rect.Width - 16) / 2), + (int)rect.Y + + (int)Math.Floor((double)(((int)rect.Bottom - (int)rect.Y - (int)rect.Width + 2) / 2)), 16, 16); + } + else + { + imageRect = new Rectangle((int)rect.X + (int)Math.Floor((double)((int)rect.Width - 16) / 2), + (int)rect.Bottom, 16, 16); + while (!tabBorderPath.IsVisible(imageRect.X, imageRect.Bottom)) imageRect.Y -= 1; + imageRect.Y -= 4; + } + } + + return imageRect; + } + + public Rectangle GetTabCloserRect(int index) + { + var closerRect = new Rectangle(); + using (var path = _StyleProvider.GetTabBorder(index)) + { + var rect = path.GetBounds(); + + // Make it shorter or thinner to fit the height or width because of the padding added to the tab for painting + switch (Alignment) + { + case TabAlignment.Top: + rect.Y += 4; + rect.Height -= 6; + break; + case TabAlignment.Bottom: + rect.Y += 2; + rect.Height -= 6; + break; + case TabAlignment.Left: + rect.X += 4; + rect.Width -= 6; + break; + case TabAlignment.Right: + rect.X += 2; + rect.Width -= 6; + break; + } + + if (Alignment <= TabAlignment.Bottom) + { + if (RightToLeftLayout) + { + closerRect = new Rectangle((int)rect.Left, + (int)rect.Y + (int)Math.Floor((double)((int)rect.Height - 6) / 2), 6, 6); + while (!path.IsVisible(closerRect.Left, closerRect.Y) && closerRect.Right < Width) + closerRect.X += 1; + closerRect.X += 4; + } + else + { + closerRect = new Rectangle((int)rect.Right, + (int)rect.Y + (int)Math.Floor((double)((int)rect.Height - 6) / 2), 6, 6); + while (!path.IsVisible(closerRect.Right, closerRect.Y) && closerRect.Right > -6) + closerRect.X -= 1; + closerRect.X -= 4; + } + } + else + { + if (RightToLeftLayout) + { + closerRect = new Rectangle((int)rect.X + (int)Math.Floor((double)((int)rect.Width - 6) / 2), + (int)rect.Top, 6, 6); + while (!path.IsVisible(closerRect.X, closerRect.Top) && closerRect.Bottom < Height) + closerRect.Y += 1; + closerRect.Y += 4; + } + else + { + closerRect = new Rectangle((int)rect.X + (int)Math.Floor((double)((int)rect.Width - 6) / 2), + (int)rect.Bottom, 6, 6); + while (!path.IsVisible(closerRect.X, closerRect.Bottom) && closerRect.Top > -6) + closerRect.Y -= 1; + closerRect.Y -= 4; + } + } + } + + return closerRect; + } + + public new Point MousePosition + { + get + { + var loc = PointToClient(Control.MousePosition); + if (RightToLeftLayout) loc.X = Width - loc.X; + return loc; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/NativeMethods.cs b/XenAdmin/Controls/TabControl/NativeMethods.cs new file mode 100644 index 000000000..99fc418e4 --- /dev/null +++ b/XenAdmin/Controls/TabControl/NativeMethods.cs @@ -0,0 +1,255 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace XenAdmin.Controls.TabControl +{ + /// + /// Description of NativeMethods. + /// + //[SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] + internal sealed class NativeMethods + { + private NativeMethods() + { + } + + #region User32.dll + +// [DllImport("user32.dll"), SecurityPermission(SecurityAction.Demand)] +// public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam); + + public static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) + { + // This Method replaces the User32 method SendMessage, but will only work for sending + // messages to Managed controls. + var control = Control.FromHandle(hWnd); + if (control == null) return IntPtr.Zero; + + var message = new Message(); + message.HWnd = hWnd; + message.LParam = lParam; + message.WParam = wParam; + message.Msg = msg; + + var wproc = control.GetType().GetMethod("WndProc" + , BindingFlags.NonPublic + | BindingFlags.InvokeMethod + | BindingFlags.FlattenHierarchy + | BindingFlags.IgnoreCase + | BindingFlags.Instance); + + object[] args = { message }; + wproc.Invoke(control, args); + + return ((Message)args[0]).Result; + } + + +// [DllImport("user32.dll")] +// public static extern IntPtr BeginPaint(IntPtr hWnd, ref PAINTSTRUCT paintStruct); +// +// [DllImport("user32.dll")] +// [return: MarshalAs(UnmanagedType.Bool)] +// public static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT paintStruct); +// + + #endregion + + #region Windows Constants + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WM_GETTABRECT = 0x130a; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WS_EX_TRANSPARENT = 0x20; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WM_SETFONT = 0x30; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WM_FONTCHANGE = 0x1d; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WM_HSCROLL = 0x114; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int TCM_HITTEST = 0x130D; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WM_PAINT = 0xf; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WS_EX_LAYOUTRTL = 0x400000; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public const int WS_EX_NOINHERITLAYOUT = 0x100000; + + #endregion + + #region Content Alignment + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyRightAlign = + ContentAlignment.BottomRight | ContentAlignment.MiddleRight | ContentAlignment.TopRight; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyLeftAlign = + ContentAlignment.BottomLeft | ContentAlignment.MiddleLeft | ContentAlignment.TopLeft; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyTopAlign = + ContentAlignment.TopRight | ContentAlignment.TopCenter | ContentAlignment.TopLeft; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyBottomAlign = + ContentAlignment.BottomRight | ContentAlignment.BottomCenter | ContentAlignment.BottomLeft; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyMiddleAlign = + ContentAlignment.MiddleRight | ContentAlignment.MiddleCenter | ContentAlignment.MiddleLeft; + + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + public static readonly ContentAlignment AnyCenterAlign = + ContentAlignment.BottomCenter | ContentAlignment.MiddleCenter | ContentAlignment.TopCenter; + + #endregion + + #region Misc Functions + + public static int LoWord(IntPtr dWord) + { + return dWord.ToInt32() & 0xffff; + } + + public static int HiWord(IntPtr dWord) + { + if ((dWord.ToInt32() & 0x80000000) == 0x80000000) + return dWord.ToInt32() >> 16; + return (dWord.ToInt32() >> 16) & 0xffff; + } + + [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] + public static IntPtr ToIntPtr(object structure) + { + var lparam = IntPtr.Zero; + lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(structure)); + Marshal.StructureToPtr(structure, lparam, false); + return lparam; + } + + #endregion + + #region Windows Structures and Enums + + [Flags] + public enum TCHITTESTFLAGS + { + TCHT_NOWHERE = 1, + TCHT_ONITEMICON = 2, + TCHT_ONITEMLABEL = 4, + TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL + } + + + [StructLayout(LayoutKind.Sequential)] + public struct TCHITTESTINFO + { + public TCHITTESTINFO(Point location) + { + pt = location; + flags = TCHITTESTFLAGS.TCHT_ONITEM; + } + + public Point pt; + public TCHITTESTFLAGS flags; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct PAINTSTRUCT + { + public IntPtr hdc; + public int fErase; + public RECT rcPaint; + public int fRestore; + public int fIncUpdate; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] rgbReserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public RECT(int left, int top, int right, int bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public RECT(Rectangle r) + { + left = r.Left; + top = r.Top; + right = r.Right; + bottom = r.Bottom; + } + + public static RECT FromXYWH(int x, int y, int width, int height) + { + return new RECT(x, y, x + width, y + height); + } + + public static RECT FromIntPtr(IntPtr ptr) + { + var rect = (RECT)Marshal.PtrToStructure(ptr, typeof(RECT)); + return rect; + } + + public Size Size => new Size(right - left, bottom - top); + } + + #endregion + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/TabStyle.cs b/XenAdmin/Controls/TabControl/TabStyle.cs new file mode 100644 index 000000000..1398e7a54 --- /dev/null +++ b/XenAdmin/Controls/TabControl/TabStyle.cs @@ -0,0 +1,38 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +namespace XenAdmin.Controls.TabControl +{ + public enum TabStyle + { + Default = 0, + Angled = 1 + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/TabStyleProvider.cs b/XenAdmin/Controls/TabControl/TabStyleProvider.cs new file mode 100644 index 000000000..1d06b1527 --- /dev/null +++ b/XenAdmin/Controls/TabControl/TabStyleProvider.cs @@ -0,0 +1,664 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using XenAdmin.Controls.TabControl.TabStyleProviders; + +namespace XenAdmin.Controls.TabControl +{ + [ToolboxItem(false)] + public abstract class TabStyleProvider : Component + { + #region Constructor + + protected TabStyleProvider(CustomTabControl tabControl) + { + _TabControl = tabControl; + + _BorderColor = Color.Empty; + _BorderColorSelected = Color.Empty; + _FocusColor = Color.Orange; + + if (_TabControl.RightToLeftLayout) + _ImageAlign = ContentAlignment.MiddleRight; + else + _ImageAlign = ContentAlignment.MiddleLeft; + + HotTrack = true; + + // Must set after the _Overlap as this is used in the calculations of the actual padding + Padding = new Point(6, 3); + } + + #endregion + + #region Factory Methods + + public static TabStyleProvider CreateProvider(CustomTabControl tabControl) + { + TabStyleProvider provider; + + // Depending on the display style of the tabControl generate an appropriate provider. + switch (tabControl.DisplayStyle) + { + case TabStyle.Default: + provider = new TabStyleDefaultProvider(tabControl); + break; + + case TabStyle.Angled: + provider = new TabStyleAngledProvider(tabControl); + break; + + default: + provider = new TabStyleDefaultProvider(tabControl); + break; + } + + provider._Style = tabControl.DisplayStyle; + return provider; + } + + #endregion + + #region Tab border and rect + + public GraphicsPath GetTabBorder(int index) + { + var path = new GraphicsPath(); + var tabBounds = GetTabRect(index); + + AddTabBorder(path, tabBounds); + + path.CloseFigure(); + return path; + } + + #endregion + + #region Protected variables + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected CustomTabControl _TabControl; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Point _Padding; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected bool _HotTrack; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected TabStyle _Style = TabStyle.Default; + + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected ContentAlignment _ImageAlign; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected int _Radius = 1; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected int _Overlap; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected bool _FocusTrack; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected float _Opacity = 1; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _BorderColorSelected = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _BorderColor = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _BorderColorHot = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _FocusColor = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _TextColor = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _TextColorSelected = Color.Empty; + + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + protected Color _TextColorDisabled = Color.Empty; + + #endregion + + #region overridable Methods + + public abstract void AddTabBorder(GraphicsPath path, Rectangle tabBounds); + + public virtual Rectangle GetTabRect(int index) + { + if (index < 0) return new Rectangle(); + var tabBounds = _TabControl.GetTabRect(index); + if (_TabControl.RightToLeftLayout) tabBounds.X = _TabControl.Width - tabBounds.Right; + var firstTabinRow = _TabControl.IsFirstTabInRow(index); + + // Expand to overlap the tabpage + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + tabBounds.Height += 2; + break; + case TabAlignment.Bottom: + tabBounds.Height += 2; + tabBounds.Y -= 2; + break; + case TabAlignment.Left: + tabBounds.Width += 2; + break; + case TabAlignment.Right: + tabBounds.X -= 2; + tabBounds.Width += 2; + break; + } + + + // Greate Overlap unless first tab in the row to align with tabpage + if ((!firstTabinRow || _TabControl.RightToLeftLayout) && _Overlap > 0) + { + if (_TabControl.Alignment <= TabAlignment.Bottom) + { + tabBounds.X -= _Overlap; + tabBounds.Width += _Overlap; + } + else + { + tabBounds.Y -= _Overlap; + tabBounds.Height += _Overlap; + } + } + + // Adjust first tab in the row to align with tabpage + EnsureFirstTabIsInView(ref tabBounds, index); + + return tabBounds; + } + + + [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")] + protected virtual void EnsureFirstTabIsInView(ref Rectangle tabBounds, int index) + { + // Adjust first tab in the row to align with tabpage + // Make sure we only reposition visible tabs, as we may have scrolled out of view. + + var firstTabinRow = _TabControl.IsFirstTabInRow(index); + + if (firstTabinRow) + { + if (_TabControl.Alignment <= TabAlignment.Bottom) + { + if (_TabControl.RightToLeftLayout) + { + if (tabBounds.Left < _TabControl.Right) + { + var tabPageRight = _TabControl.GetPageBounds(index).Right; + if (tabBounds.Right > tabPageRight) tabBounds.Width -= tabBounds.Right - tabPageRight; + } + } + else + { + if (tabBounds.Right > 0) + { + var tabPageX = _TabControl.GetPageBounds(index).X; + if (tabBounds.X < tabPageX) + { + tabBounds.Width -= tabPageX - tabBounds.X; + tabBounds.X = tabPageX; + } + } + } + } + else + { + if (_TabControl.RightToLeftLayout) + { + if (tabBounds.Top < _TabControl.Bottom) + { + var tabPageBottom = _TabControl.GetPageBounds(index).Bottom; + if (tabBounds.Bottom > tabPageBottom) tabBounds.Height -= tabBounds.Bottom - tabPageBottom; + } + } + else + { + if (tabBounds.Bottom > 0) + { + var tabPageY = _TabControl.GetPageBounds(index).Location.Y; + if (tabBounds.Y < tabPageY) + { + tabBounds.Height -= tabPageY - tabBounds.Y; + tabBounds.Y = tabPageY; + } + } + } + } + } + } + + protected virtual Brush GetTabBackgroundBrush(int index) + { + LinearGradientBrush fillBrush = null; + + // Capture the colours dependant on selection state of the tab + var dark = Color.FromArgb(207, 207, 207); + var light = Color.FromArgb(242, 242, 242); + + if (_TabControl.SelectedIndex == index) + { + dark = SystemColors.ControlLight; + light = SystemColors.Window; + } + else if (!_TabControl.TabPages[index].Enabled) + { + light = dark; + } + else if (_HotTrack && index == _TabControl.ActiveIndex) + { + // Enable hot tracking + light = Color.FromArgb(234, 246, 253); + dark = Color.FromArgb(167, 217, 245); + } + + // Get the correctly aligned gradient + var tabBounds = GetTabRect(index); + tabBounds.Inflate(3, 3); + tabBounds.X -= 1; + tabBounds.Y -= 1; + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + if (_TabControl.SelectedIndex == index) dark = light; + fillBrush = new LinearGradientBrush(tabBounds, light, dark, LinearGradientMode.Vertical); + break; + case TabAlignment.Bottom: + fillBrush = new LinearGradientBrush(tabBounds, light, dark, LinearGradientMode.Vertical); + break; + case TabAlignment.Left: + fillBrush = new LinearGradientBrush(tabBounds, dark, light, LinearGradientMode.Horizontal); + break; + case TabAlignment.Right: + fillBrush = new LinearGradientBrush(tabBounds, light, dark, LinearGradientMode.Horizontal); + break; + } + + // Add the blend + fillBrush.Blend = GetBackgroundBlend(); + + return fillBrush; + } + + #endregion + + #region Base Properties + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public TabStyle DisplayStyle + { + get => _Style; + set => _Style = value; + } + + [Category("Appearance")] + public ContentAlignment ImageAlign + { + get => _ImageAlign; + set + { + _ImageAlign = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + public Point Padding + { + get => _Padding; + set + { + _Padding = value; + // This line will trigger the handle to recreate, therefore invalidating the control + + if (value.X + _Radius / 2 < 1) + ((System.Windows.Forms.TabControl)_TabControl).Padding = new Point(0, value.Y); + else + ((System.Windows.Forms.TabControl)_TabControl).Padding = + new Point(value.X + _Radius / 2 - 1, value.Y); + } + } + + + [Category("Appearance")] + [DefaultValue(1)] + [Browsable(true)] + public int Radius + { + get => _Radius; + set + { + if (value < 1) throw new ArgumentException("The radius must be greater than 1", "value"); + _Radius = value; + // Adjust padding + Padding = _Padding; + } + } + + [Category("Appearance")] + public int Overlap + { + get => _Overlap; + set + { + if (value < 0) throw new ArgumentException("The tabs cannot have a negative overlap", "value"); + _Overlap = value; + } + } + + + [Category("Appearance")] + public bool FocusTrack + { + get => _FocusTrack; + set + { + _FocusTrack = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + public bool HotTrack + { + get => _HotTrack; + set + { + _HotTrack = value; + ((System.Windows.Forms.TabControl)_TabControl).HotTrack = value; + } + } + + [Category("Appearance")] + public float Opacity + { + get => _Opacity; + set + { + if (value < 0) throw new ArgumentException("The opacity must be between 0 and 1", "value"); + if (value > 1) throw new ArgumentException("The opacity must be between 0 and 1", "value"); + _Opacity = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color BorderColorSelected + { + get + { + if (_BorderColorSelected.IsEmpty) + return ThemedColors.ToolBorder; + return _BorderColorSelected; + } + set + { + if (value.Equals(ThemedColors.ToolBorder)) + _BorderColorSelected = Color.Empty; + else + _BorderColorSelected = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color BorderColorHot + { + get + { + if (_BorderColorHot.IsEmpty) + return SystemColors.ControlDark; + return _BorderColorHot; + } + set + { + if (value.Equals(SystemColors.ControlDark)) + _BorderColorHot = Color.Empty; + else + _BorderColorHot = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color BorderColor + { + get + { + if (_BorderColor.IsEmpty) + return SystemColors.ControlDark; + return _BorderColor; + } + set + { + if (value.Equals(SystemColors.ControlDark)) + _BorderColor = Color.Empty; + else + _BorderColor = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color TextColor + { + get + { + if (_TextColor.IsEmpty) + return SystemColors.ControlText; + return _TextColor; + } + set + { + if (value.Equals(SystemColors.ControlText)) + _TextColor = Color.Empty; + else + _TextColor = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color TextColorSelected + { + get + { + if (_TextColorSelected.IsEmpty) + return SystemColors.ControlText; + return _TextColorSelected; + } + set + { + if (value.Equals(SystemColors.ControlText)) + _TextColorSelected = Color.Empty; + else + _TextColorSelected = value; + _TabControl.Invalidate(); + } + } + + [Category("Appearance")] + [DefaultValue(typeof(Color), "")] + public Color TextColorDisabled + { + get + { + if (_TextColor.IsEmpty) + return SystemColors.ControlDark; + return _TextColorDisabled; + } + set + { + if (value.Equals(SystemColors.ControlDark)) + _TextColorDisabled = Color.Empty; + else + _TextColorDisabled = value; + _TabControl.Invalidate(); + } + } + + + [Category("Appearance")] + [DefaultValue(typeof(Color), "Orange")] + public Color FocusColor + { + get => _FocusColor; + set + { + _FocusColor = value; + _TabControl.Invalidate(); + } + } + + #endregion + + #region Painting + + public void PaintTab(int index, Graphics graphics) + { + using (var tabpath = GetTabBorder(index)) + { + using (var fillBrush = GetTabBackgroundBrush(index)) + { + // Paint the background + graphics.FillPath(fillBrush, tabpath); + + // Paint a focus indication + if (_TabControl.Focused) DrawTabFocusIndicator(tabpath, index, graphics); + } + } + } + + private void DrawTabFocusIndicator(GraphicsPath tabpath, int index, Graphics graphics) + { + if (_FocusTrack && _TabControl.Focused && index == _TabControl.SelectedIndex) + { + Brush focusBrush = null; + var pathRect = tabpath.GetBounds(); + var focusRect = Rectangle.Empty; + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + focusRect = new Rectangle((int)pathRect.X, (int)pathRect.Y, (int)pathRect.Width, 4); + focusBrush = new LinearGradientBrush(focusRect, _FocusColor, SystemColors.Window, + LinearGradientMode.Vertical); + break; + case TabAlignment.Bottom: + focusRect = new Rectangle((int)pathRect.X, (int)pathRect.Bottom - 4, (int)pathRect.Width, 4); + focusBrush = new LinearGradientBrush(focusRect, SystemColors.ControlLight, _FocusColor, + LinearGradientMode.Vertical); + break; + case TabAlignment.Left: + focusRect = new Rectangle((int)pathRect.X, (int)pathRect.Y, 4, (int)pathRect.Height); + focusBrush = new LinearGradientBrush(focusRect, _FocusColor, SystemColors.ControlLight, + LinearGradientMode.Horizontal); + break; + case TabAlignment.Right: + focusRect = new Rectangle((int)pathRect.Right - 4, (int)pathRect.Y, 4, (int)pathRect.Height); + focusBrush = new LinearGradientBrush(focusRect, SystemColors.ControlLight, _FocusColor, + LinearGradientMode.Horizontal); + break; + } + + // Ensure the focus stip does not go outside the tab + var focusRegion = new Region(focusRect); + focusRegion.Intersect(tabpath); + graphics.FillRegion(focusBrush, focusRegion); + focusRegion.Dispose(); + focusBrush.Dispose(); + } + } + + #endregion + + #region Background brushes + + private Blend GetBackgroundBlend() + { + float[] relativeIntensities = { 0f, 0.7f, 1f }; + float[] relativePositions = { 0f, 0.6f, 1f }; + + // Glass look to top aligned tabs + if (_TabControl.Alignment == TabAlignment.Top) + { + relativeIntensities = new[] { 0f, 0.5f, 1f, 1f }; + relativePositions = new[] { 0f, 0.5f, 0.51f, 1f }; + } + + var blend = new Blend(); + blend.Factors = relativeIntensities; + blend.Positions = relativePositions; + + return blend; + } + + public virtual Brush GetPageBackgroundBrush(int index) + { + // Capture the colours dependant on selection state of the tab + var light = Color.FromArgb(242, 242, 242); + if (_TabControl.Alignment == TabAlignment.Top) light = Color.FromArgb(207, 207, 207); + + if (_TabControl.SelectedIndex == index) + light = SystemColors.Window; + else if (!_TabControl.TabPages[index].Enabled) + light = Color.FromArgb(207, 207, 207); + else if (_HotTrack && index == _TabControl.ActiveIndex) + // Enable hot tracking + light = Color.FromArgb(234, 246, 253); + + return new SolidBrush(light); + } + + #endregion + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleAngledProvider.cs b/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleAngledProvider.cs new file mode 100644 index 000000000..3453f1df2 --- /dev/null +++ b/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleAngledProvider.cs @@ -0,0 +1,78 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace XenAdmin.Controls.TabControl.TabStyleProviders +{ + [ToolboxItem(false)] + public class TabStyleAngledProvider : TabStyleProvider + { + public TabStyleAngledProvider(CustomTabControl tabControl) : base(tabControl) + { + _ImageAlign = ContentAlignment.MiddleRight; + _Overlap = 7; + _Radius = 10; + + // Must set after the _Radius as this is used in the calculations of the actual padding + Padding = new Point(10, 3); + } + + public override void AddTabBorder(GraphicsPath path, Rectangle tabBounds) + { + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + path.AddLine(tabBounds.X, tabBounds.Bottom, tabBounds.X + _Radius - 2, tabBounds.Y + 2); + path.AddLine(tabBounds.X + _Radius, tabBounds.Y, tabBounds.Right - _Radius, tabBounds.Y); + path.AddLine(tabBounds.Right - _Radius + 2, tabBounds.Y + 2, tabBounds.Right, tabBounds.Bottom); + break; + case TabAlignment.Bottom: + path.AddLine(tabBounds.Right, tabBounds.Y, tabBounds.Right - _Radius + 2, tabBounds.Bottom - 2); + path.AddLine(tabBounds.Right - _Radius, tabBounds.Bottom, tabBounds.X + _Radius, tabBounds.Bottom); + path.AddLine(tabBounds.X + _Radius - 2, tabBounds.Bottom - 2, tabBounds.X, tabBounds.Y); + break; + case TabAlignment.Left: + path.AddLine(tabBounds.Right, tabBounds.Bottom, tabBounds.X + 2, tabBounds.Bottom - _Radius + 2); + path.AddLine(tabBounds.X, tabBounds.Bottom - _Radius, tabBounds.X, tabBounds.Y + _Radius); + path.AddLine(tabBounds.X + 2, tabBounds.Y + _Radius - 2, tabBounds.Right, tabBounds.Y); + break; + case TabAlignment.Right: + path.AddLine(tabBounds.X, tabBounds.Y, tabBounds.Right - 2, tabBounds.Y + _Radius - 2); + path.AddLine(tabBounds.Right, tabBounds.Y + _Radius, tabBounds.Right, tabBounds.Bottom - _Radius); + path.AddLine(tabBounds.Right - 2, tabBounds.Bottom - _Radius + 2, tabBounds.X, tabBounds.Bottom); + break; + } + } + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleDefaultProvider.cs b/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleDefaultProvider.cs new file mode 100644 index 000000000..d10a77ce4 --- /dev/null +++ b/XenAdmin/Controls/TabControl/TabStyleProviders/TabStyleDefaultProvider.cs @@ -0,0 +1,173 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace XenAdmin.Controls.TabControl.TabStyleProviders +{ + [ToolboxItem(false)] + public class TabStyleDefaultProvider : TabStyleProvider + { + public TabStyleDefaultProvider(CustomTabControl tabControl) : base(tabControl) + { + _FocusTrack = true; + _Radius = 2; + } + + public override void AddTabBorder(GraphicsPath path, Rectangle tabBounds) + { + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + path.AddLine(tabBounds.X, tabBounds.Bottom, tabBounds.X, tabBounds.Y); + path.AddLine(tabBounds.X, tabBounds.Y, tabBounds.Right, tabBounds.Y); + path.AddLine(tabBounds.Right, tabBounds.Y, tabBounds.Right, tabBounds.Bottom); + break; + case TabAlignment.Bottom: + path.AddLine(tabBounds.Right, tabBounds.Y, tabBounds.Right, tabBounds.Bottom); + path.AddLine(tabBounds.Right, tabBounds.Bottom, tabBounds.X, tabBounds.Bottom); + path.AddLine(tabBounds.X, tabBounds.Bottom, tabBounds.X, tabBounds.Y); + break; + case TabAlignment.Left: + path.AddLine(tabBounds.Right, tabBounds.Bottom, tabBounds.X, tabBounds.Bottom); + path.AddLine(tabBounds.X, tabBounds.Bottom, tabBounds.X, tabBounds.Y); + path.AddLine(tabBounds.X, tabBounds.Y, tabBounds.Right, tabBounds.Y); + break; + case TabAlignment.Right: + path.AddLine(tabBounds.X, tabBounds.Y, tabBounds.Right, tabBounds.Y); + path.AddLine(tabBounds.Right, tabBounds.Y, tabBounds.Right, tabBounds.Bottom); + path.AddLine(tabBounds.Right, tabBounds.Bottom, tabBounds.X, tabBounds.Bottom); + break; + } + } + + public override Rectangle GetTabRect(int index) + { + if (index < 0) return new Rectangle(); + + var tabBounds = base.GetTabRect(index); + var firstTabinRow = _TabControl.IsFirstTabInRow(index); + + // Make non-SelectedTabs smaller and selected tab bigger + if (index != _TabControl.SelectedIndex) + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + tabBounds.Y += 1; + tabBounds.Height -= 1; + break; + case TabAlignment.Bottom: + tabBounds.Height -= 1; + break; + case TabAlignment.Left: + tabBounds.X += 1; + tabBounds.Width -= 1; + break; + case TabAlignment.Right: + tabBounds.Width -= 1; + break; + } + else + switch (_TabControl.Alignment) + { + case TabAlignment.Top: + if (tabBounds.Y > 0) + { + tabBounds.Y -= 1; + tabBounds.Height += 1; + } + + if (firstTabinRow) + { + tabBounds.Width += 1; + } + else + { + tabBounds.X -= 1; + tabBounds.Width += 2; + } + + break; + case TabAlignment.Bottom: + if (tabBounds.Bottom < _TabControl.Bottom) tabBounds.Height += 1; + if (firstTabinRow) + { + tabBounds.Width += 1; + } + else + { + tabBounds.X -= 1; + tabBounds.Width += 2; + } + + break; + case TabAlignment.Left: + if (tabBounds.X > 0) + { + tabBounds.X -= 1; + tabBounds.Width += 1; + } + + if (firstTabinRow) + { + tabBounds.Height += 1; + } + else + { + tabBounds.Y -= 1; + tabBounds.Height += 2; + } + + break; + case TabAlignment.Right: + if (tabBounds.Right < _TabControl.Right) tabBounds.Width += 1; + if (firstTabinRow) + { + tabBounds.Height += 1; + } + else + { + tabBounds.Y -= 1; + tabBounds.Height += 2; + } + + break; + } + + // Adjust first tab in the row to align with tabpage + EnsureFirstTabIsInView(ref tabBounds, index); + + return tabBounds; + } + } +} \ No newline at end of file diff --git a/XenAdmin/Controls/TabControl/ThemedColors.cs b/XenAdmin/Controls/TabControl/ThemedColors.cs new file mode 100644 index 000000000..58074d5ae --- /dev/null +++ b/XenAdmin/Controls/TabControl/ThemedColors.cs @@ -0,0 +1,112 @@ +/* Copyright (c) XCP-ng Project. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +namespace XenAdmin.Controls.TabControl +{ + internal sealed class ThemedColors + { + public enum ColorScheme + { + NormalColor = 0, + HomeStead = 1, + Metallic = 2, + NoTheme = 3 + } + + private static ColorScheme GetCurrentThemeIndex() + { + var theme = ColorScheme.NoTheme; + + if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser && + Application.RenderWithVisualStyles) + switch (VisualStyleInformation.ColorScheme) + { + case NormalColor: + theme = ColorScheme.NormalColor; + break; + case HomeStead: + theme = ColorScheme.HomeStead; + break; + case Metallic: + theme = ColorScheme.Metallic; + break; + default: + theme = ColorScheme.NoTheme; + break; + } + + return theme; + } + + #region " Variables and Constants " + + private const string NormalColor = "NormalColor"; + private const string HomeStead = "HomeStead"; + private const string Metallic = "Metallic"; + private const string NoTheme = "NoTheme"; + + private static readonly Color[] _toolBorder; + + #endregion + + #region " Properties " + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static ColorScheme CurrentThemeIndex => GetCurrentThemeIndex(); + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static Color ToolBorder => _toolBorder[(int)CurrentThemeIndex]; + + #endregion + + #region " Constructors " + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static ThemedColors() + { + _toolBorder = new[] + { + Color.FromArgb(127, 157, 185), Color.FromArgb(164, 185, 127), Color.FromArgb(165, 172, 178), + Color.FromArgb(132, 130, 132) + }; + } + + private ThemedColors() + { + } + + #endregion + } +} \ No newline at end of file diff --git a/XenAdmin/Dialogs/AboutDialog.Designer.cs b/XenAdmin/Dialogs/AboutDialog.Designer.cs index 648463f2d..32579b60f 100644 --- a/XenAdmin/Dialogs/AboutDialog.Designer.cs +++ b/XenAdmin/Dialogs/AboutDialog.Designer.cs @@ -35,6 +35,7 @@ namespace XenAdmin.Dialogs this.label2 = new System.Windows.Forms.Label(); this.VersionLabel = new System.Windows.Forms.Label(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.AdditionalVersionData = new System.Windows.Forms.Label(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); @@ -52,11 +53,12 @@ namespace XenAdmin.Dialogs // resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1"); this.tableLayoutPanel1.BackColor = System.Drawing.Color.White; - this.tableLayoutPanel1.Controls.Add(this.linkLabel1, 1, 3); - this.tableLayoutPanel1.Controls.Add(this.label2, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.AdditionalVersionData, 1, 2); this.tableLayoutPanel1.Controls.Add(this.VersionLabel, 1, 1); this.tableLayoutPanel1.Controls.Add(this.OkButton, 1, 7); this.tableLayoutPanel1.Controls.Add(this.pictureBox1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.linkLabel1, 1, 6); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; // // linkLabel1 @@ -86,6 +88,12 @@ namespace XenAdmin.Dialogs this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.TabStop = false; // + // AdditionalVersionData + // + resources.ApplyResources(this.AdditionalVersionData, "AdditionalVersionData"); + this.AdditionalVersionData.BackColor = System.Drawing.Color.Transparent; + this.AdditionalVersionData.Name = "AdditionalVersionData"; + // // AboutDialog // this.AcceptButton = this.OkButton; @@ -113,5 +121,6 @@ namespace XenAdmin.Dialogs private System.Windows.Forms.Button OkButton; private System.Windows.Forms.LinkLabel linkLabel1; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label AdditionalVersionData; } } diff --git a/XenAdmin/Dialogs/AboutDialog.cs b/XenAdmin/Dialogs/AboutDialog.cs index 0d9f59bd4..53b19102b 100644 --- a/XenAdmin/Dialogs/AboutDialog.cs +++ b/XenAdmin/Dialogs/AboutDialog.cs @@ -48,6 +48,10 @@ namespace XenAdmin.Dialogs VersionLabel.Text = string.Format(Messages.VERSION_NUMBER, BrandManager.BrandConsole, Program.VersionText, Program.Version.Revision, IntPtr.Size * 8); + AdditionalVersionData.Text = string.Format(Messages.VERSION_ADDITIONAL, ThisAssembly.Git.SourceRevisionId, + ThisAssembly.InformationalData.LabId, ThisAssembly.InformationalData.BuildDateTime, + ThisAssembly.InformationalData.BuildStage); + label2.Text = BrandManager.Copyright; } diff --git a/XenAdmin/Dialogs/AboutDialog.resx b/XenAdmin/Dialogs/AboutDialog.resx index ee3f62bbb..aa9090c16 100644 --- a/XenAdmin/Dialogs/AboutDialog.resx +++ b/XenAdmin/Dialogs/AboutDialog.resx @@ -129,7 +129,7 @@ NoControl - 333, 164 + 333, 191 75, 23 @@ -151,7 +151,7 @@ tableLayoutPanel1 - 3 + 2 True @@ -162,6 +162,99 @@ 3 + + AdditionalVersionData + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + VersionLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + pictureBox1 + + + System.Windows.Forms.PictureBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + linkLabel1 + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + Segoe UI, 9pt + + + 0, 0 + + + 0, 0, 0, 0 + + + 10 + + + 420, 226 + + + 19 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="AdditionalVersionData" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="VersionLabel" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="OkButton" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="pictureBox1" Row="0" RowSpan="1" Column="0" ColumnSpan="3" /><Control Name="label2" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="linkLabel1" Row="6" RowSpan="1" Column="1" ColumnSpan="1" /></Controls><Columns Styles="Absolute,9,AutoSize,0,Absolute,9" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,Absolute,9" /></TableLayoutSettings> + Bottom, Left @@ -175,7 +268,7 @@ NoControl - 12, 146 + 12, 173 3, 12, 3, 0 @@ -199,7 +292,7 @@ tableLayoutPanel1 - 0 + 5 True @@ -211,7 +304,7 @@ NoControl - 12, 119 + 12, 146 3, 12, 3, 0 @@ -235,7 +328,7 @@ tableLayoutPanel1 - 1 + 4 True @@ -271,7 +364,7 @@ tableLayoutPanel1 - 2 + 1 Segoe UI, 9pt @@ -307,41 +400,44 @@ tableLayoutPanel1 - 4 + 3 - + + True + + Segoe UI, 9pt - - 0, 0 + + NoControl - - 0, 0, 0, 0 + + 12, 119 - - 9 + + 3, 12, 3, 0 - - 420, 199 + + 45, 15 - - 19 + + 20 - + + version + + + AdditionalVersionData + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + tableLayoutPanel1 - - System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - + 0 - - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="linkLabel1" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="label2" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="VersionLabel" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="OkButton" Row="7" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="pictureBox1" Row="0" RowSpan="1" Column="0" ColumnSpan="3" /></Controls><Columns Styles="Absolute,9,AutoSize,0,Absolute,9" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,AutoSize,0,Absolute,9" /></TableLayoutSettings> - True @@ -355,11 +451,184 @@ GrowAndShrink - 437, 346 + 487, 311 Tahoma, 8pt + + + AAABAAYAICAQAAAAAADoAgAAZgAAABAQEAAAAAAAKAEAAE4DAAAgIAAAAQAIAKgIAAB2BAAAEBAAAAEA + CABoBQAAHg0AACAgAAABACAAqBAAAIYSAAAQEAAAAQAgAGgEAAAuIwAAKAAAACAAAABAAAAAAQAEAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA + /wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIoiI + iIiIiIiIiIiIiIiIiIiCIigiIiIozMzMzMzMyCIogiIoIiIiKM7m5ubm5sgiKIIiKCIiIijObm5ubm7I + IiiCIigiIiIozubm5ubmyCIogiIoIiIiKM5ubm5ubsgiKIIiKCIiIijO5ubm5ubIIiiIiIiIiIiIzm5u + bm5uyCIogRERERERGM7u7u7u7sgiKIHZWVlZWRjMzMzMzMzIIiiB1ZWVlZUYiIiIiIiIiIiIgdlZWVlZ + GDMzMzMzMzMzOIHVlZWVlRg/uLi4uLi4uDiB2VlZWVkYP7uLi4uLi4s4gdWVlZWVGD+4uLi4uLi4OIHZ + WVlZWRg/u4uLi4uLiziB1ZWVlZUYP7i4uLi4uLg4gdlZWVlZGD+7i4uLi4uLOIHVlZWVlRg/uLi4uLi4 + uDiB3d3d3d0YP7uLi4uLi4s4gRERERERGD+4uLi4uLi4OIiIiIiIiIg/u4uLi4uLiziCIiIiIiIoP7i4 + uLi4uLg4giIiIiIiKD+7i4uLi4uLOIIiIiIiIig/uLi4uLi4uDiCIiIiIiIoP7u7u7u7u7s4giIiIiIi + KD//////////OIIiIiIiIigzMzMzMzMzMziIiIiIiIiIiIiIiIiIiIiIIiIiIiIiIiIiIiIiIiIiIv// + ////////AAAAAHv4AA57+AAOe/gADnv4AA57+AAOe/gADgAAAA4AAAAOAAAADgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAAAA + AAD/////KAAAABAAAAAgAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAACA + gACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAiIiIiIiIiIoiI + iIiIiIiIgigijMzMyCiCKCKM5mbIKIiIiIzu7sgogRERjMzMyCiB2ZGIiIiIiIHZkYMzMzM4gdmRg/u7 + uziB3dGD+7u7OIEREYP7u7s4iIiIg/u7uziCIiKD+7u7OIIiIoP///84giIigzMzMziIiIiIiIiIiP// + KCIAACjObALm5mwCIigAAoiIAAKIzgAAbm4AACIoAAAREQAAGM4AAO7uAAAiKHwAWVl8ABjMfADMzAAA + IigoAAAAIAAAAEAAAAABAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADA3MAA8MqmAKo/KgD/PyoAAF8qAFVfKgCqXyoA/18qAAB/KgBVfyoAqn8qAP9/ + KgAAnyoAVZ8qAKqfKgD/nyoAAL8qAFW/KgCqvyoA/78qAADfKgBV3yoAqt8qAP/fKgAA/yoAVf8qAKr/ + KgD//yoAAABVAFUAVQCqAFUA/wBVAAAfVQBVH1UAqh9VAP8fVQAAP1UAVT9VAKo/VQD/P1UAAF9VAFVf + VQCqX1UA/19VAAB/VQBVf1UAqn9VAP9/VQAAn1UAVZ9VAKqfVQD/n1UAAL9VAFW/VQCqv1UA/79VAADf + VQBV31UAqt9VAP/fVQAA/1UAVf9VAKr/VQD//1UAAAB/AFUAfwCqAH8A/wB/AAAffwBVH38Aqh9/AP8f + fwAAP38AVT9/AKo/fwD/P38AAF9/AFVffwCqX38A/19/AAB/fwBVf38Aqn9/AP9/fwAAn38AVZ9/AKqf + fwD/n38AAL9/AFW/fwCqv38A/79/AADffwBV338Aqt9/AP/ffwAA/38AVf9/AKr/fwD//38AAACqAFUA + qgCqAKoA/wCqAAAfqgBVH6oAqh+qAP8fqgAAP6oAVT+qAKo/qgD/P6oAAF+qAFVfqgCqX6oA/1+qAAB/ + qgBVf6oAqn+qAP9/qgAAn6oAVZ+qAKqfqgD/n6oAAL+qAFW/qgCqv6oA/7+qAADfqgBV36oAqt+qAP/f + qgAA/6oAVf+qAKr/qgD//6oAAADUAFUA1ACqANQA/wDUAAAf1ABVH9QAqh/UAP8f1AAAP9QAVT/UAKo/ + 1AD/P9QAAF/UAFVf1ACqX9QA/1/UAAB/1ABVf9QAqn/UAP9/1AAAn9QAVZ/UAKqf1AD/n9QAAL/UAFW/ + 1ACqv9QA/7/UAADf1ABV39QAqt/UAP/f1AAA/9QAVf/UAKr/1AD//9QAVQD/AKoA/wAAH/8AVR//AKof + /wD/H/8AAD//AFU//wCqP/8A/z//AABf/wBVX/8Aql//AP9f/wAAf/8AVX//AKp//wD/f/8AAJ//AFWf + /wCqn/8A/5//AAC//wBVv/8Aqr//AP+//wAA3/8AVd//AKrf/wD/3/8AVf//AKr//wD/zMwA/8z/AP// + MwD//2YA//+ZAP//zAAAfwAAVX8AAKp/AAD/fwAAAJ8AAFWfAACqnwAA/58AAAC/AABVvwAAqr8AAP+/ + AAAA3wAAVd8AAKrfAAD/3wAAVf8AAKr/AAAAACoAVQAqAKoAKgD/ACoAAB8qAFUfKgCqHyoA/x8qAAA/ + KgBVPyoA8Pv/AKSgoACAgIAAAAD/AAD/AAAA//8A/wAAAAAAAAD//wAA////AP39/f39/f39/f39/f39 + /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39 + /f39/f39/f39/f39/f39/f39/f39/f39qoYIqoYIhqoIqgiqCaoIqgiqhqqGhoYIhoYIqv39/f0I/f39 + /ar9/f39/YY2Ng4yDg4ODgoOCgoKCgqG/f39/Yb9/f39CP39/f39qjY7Ozs3Nzc3NjMSMjIOCqr9/f39 + qv39/f2G/f39/f0IN19fOzs3Nzc3NjcODg4KCP39/f0I/f39/ar9/f39/ao6X19fXzs7Ozc3NzY3NgqG + /f39/Yb9/f39CP39/f39hl9jY19jX187Ozs7Nzc3Dqr9/f39qv39/f2G/f39/f0IOodjh19jX19fXztf + OzcOCP39/f0ICAmqCAiqCKoICapfCYdjh2ODY19fXzs7Ow6q/f39/QhITEwoSCUoKSQoqmMJCYcJCWNj + Y2NfY19fNgj9/f39qkyZmZmYmJRwlCmqX19fXl9fX186WzY3Njc2gv39/f0JcJ2dmZmZlJmUJAmqCaoJ + hggIqggICKoIqggI/f39/YZwnp2dnZmZmJVMqnx8fHx8fFR8VHhUVFRUVKr9/f39CHChoZ2dnZ2ZmUwJ + fKSkxqSkxqSkpKSkpKBUCP39/f2qcKLDoqGdnZ2ZTKp8ysakxqSkxqSkxqSkpFSq/f39/QiUpqbDoqHE + nZ1Mq3ykqMakyqSkxqSkpKSkVAj9/f39hpTIyKbHoqGhoXAIfMrLpMqkxqSkxqTGpKRUqv39/f0IlMym + yKbIpcShcAh8y6jKpMqkxsqkpKSkxlQI/f39/aqUzMzMyKbIpqJwqnzLy8qpxsqkpMakxqSkeAj9/f39 + CJSUlJSUlJSUlJQJgMupy8qpysqkyqSkxqRUqv39/f2GCKoIqgiqCKoIhgigrcvPqcuoy8qkxsqkxnyG + /f39/ar9/f39/f39/f39qnzPz6nLy8uoyqnKpKTKVAj9/f39CP39/f39/f39/f0IfNDPz8+py8upyqjG + yqR8hv39/f2G/f39/f39/f39/Qik0K7P0M+ty8vLy6jKpXyq/f39/ar9/f39/f39/f39CHzQ09Ctz8/P + qcupy6jKeAj9/f39CP39/f39/f39/f2qoNPQ0NPQ0M/Qz8vLy6l8CP39/f2G/f39/f39/f39/QmkfKR8 + oHx8fHx8fHx8fHyG/f39/aoIqgiqCKoIqgiqCKoIqgiqCKoIqgiqCKoIqgj9/f39/f39/f39/f39/f39 + /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3///////////// + ///AAAAD3vgAA974AAPe+AAD3vgAA974AAPe+AADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AA + AAPAAAADwAAAA8AAAAPAAAADwAAAA9/4AAPf+AAD3/gAA9/4AAPf+AAD3/gAA8AAAAP//////////ygA + AAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAA + gACAgAAAgICAAMDcwADwyqYAqj8qAP8/KgAAXyoAVV8qAKpfKgD/XyoAAH8qAFV/KgCqfyoA/38qAACf + KgBVnyoAqp8qAP+fKgAAvyoAVb8qAKq/KgD/vyoAAN8qAFXfKgCq3yoA/98qAAD/KgBV/yoAqv8qAP// + KgAAAFUAVQBVAKoAVQD/AFUAAB9VAFUfVQCqH1UA/x9VAAA/VQBVP1UAqj9VAP8/VQAAX1UAVV9VAKpf + VQD/X1UAAH9VAFV/VQCqf1UA/39VAACfVQBVn1UAqp9VAP+fVQAAv1UAVb9VAKq/VQD/v1UAAN9VAFXf + VQCq31UA/99VAAD/VQBV/1UAqv9VAP//VQAAAH8AVQB/AKoAfwD/AH8AAB9/AFUffwCqH38A/x9/AAA/ + fwBVP38Aqj9/AP8/fwAAX38AVV9/AKpffwD/X38AAH9/AFV/fwCqf38A/39/AACffwBVn38Aqp9/AP+f + fwAAv38AVb9/AKq/fwD/v38AAN9/AFXffwCq338A/99/AAD/fwBV/38Aqv9/AP//fwAAAKoAVQCqAKoA + qgD/AKoAAB+qAFUfqgCqH6oA/x+qAAA/qgBVP6oAqj+qAP8/qgAAX6oAVV+qAKpfqgD/X6oAAH+qAFV/ + qgCqf6oA/3+qAACfqgBVn6oAqp+qAP+fqgAAv6oAVb+qAKq/qgD/v6oAAN+qAFXfqgCq36oA/9+qAAD/ + qgBV/6oAqv+qAP//qgAAANQAVQDUAKoA1AD/ANQAAB/UAFUf1ACqH9QA/x/UAAA/1ABVP9QAqj/UAP8/ + 1AAAX9QAVV/UAKpf1AD/X9QAAH/UAFV/1ACqf9QA/3/UAACf1ABVn9QAqp/UAP+f1AAAv9QAVb/UAKq/ + 1AD/v9QAAN/UAFXf1ACq39QA/9/UAAD/1ABV/9QAqv/UAP//1ABVAP8AqgD/AAAf/wBVH/8Aqh//AP8f + /wAAP/8AVT//AKo//wD/P/8AAF//AFVf/wCqX/8A/1//AAB//wBVf/8Aqn//AP9//wAAn/8AVZ//AKqf + /wD/n/8AAL//AFW//wCqv/8A/7//AADf/wBV3/8Aqt//AP/f/wBV//8Aqv//AP/MzAD/zP8A//8zAP// + ZgD//5kA///MAAB/AABVfwAAqn8AAP9/AAAAnwAAVZ8AAKqfAAD/nwAAAL8AAFW/AACqvwAA/78AAADf + AABV3wAAqt8AAP/fAABV/wAAqv8AAAAAKgBVACoAqgAqAP8AKgAAHyoAVR8qAKofKgD/HyoAAD8qAFU/ + KgDw+/8ApKCgAICAgAAAAP8AAP8AAAD//wD/AAAAAAAAAP//AAD///8A/f39/f39/f39/f39/f39/f0I + hgiqCKoICKoICKaGCP39qv39hv2GNg4ODjII/ar9/Yb9/ar9qjdjXzsOCP2G/f0IhquGCAleCWNfNob9 + qv39qkxMTEgIX19fX18I/Qj9/QhwnZlMqoYIqggIqgiG/f2qcKadcAl8fFQDVFQDqv39CHDMpnCqfMvL + ysrKVAj9/QiUlHBwCYDPy8/LylSG/f2GqoYIqgig0M/Py8t8qv39CP39/f2GpNDQ0M/PfAn9/ar9/f39 + qqT20NDQ0Hyq/f2G/f39/QmkpKSloKR8CP39CKoIhgiqCIYIqgiGCKr9/f39/f39/f39/f39/f39/f// + hv2AAf0ItAX9/bQFX2OABWNfgAU7O4ABNzeAAf39gAGq/YAB/YaAAf39vAE6h7wBX2O8AV9fgAE7N/// + /f0oAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADCv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/ + wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/ + wf/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAA + AAAAAAAAwr/B/7Z3Sf+zckT/rm0//6toO/+nYjb/pF4y/6BZLv+dVCr/mlEn/5dNI/+VSiH/kkce/5FE + HP+RRBz/kUUb/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAA + AAAAAAAAAAAAAAAAAADCv8H/v4JS//+aZv//lWD/+5Bc//WLV//uh1P/54FO/997S//Wdkb/zXBD/8Vr + QP+9Zj3/tGI5/65dN/+RRRz/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAMK/ + wf8AAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf/GjFv//6Rz//+fbf//m2f//5Zh//yRXf/3jVj/8IhV/+mD + UP/hfUz/2HhI/9ByRP/HbED/v2c9/5VJIf/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAA + AAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAwr/B/86WZP//r4L//6p7//+mdf//oW7//5xo//+X + Yv/9kl7/+I5a//KJVf/rhFH/4n5N/9t4SP/Sc0X/mlEm/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAA + AAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAADCv8H/1J9s//+4kf//tIv//6+E//+r + ff//p3f//6Jw//+eav//mWT//pRf//qQWv/0i1b/7IVS/+V/Tv+gWC7/wr/B/wAAAAAAAAAAAAAAAAAA + AADCv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf/apnP//7+d//+7 + mP//uJL//7WM//+whv//rH///6d4//+jcf//n2v//5ll//+VYP/6kVv/9YxY/6diN//Cv8H/AAAAAAAA + AAAAAAAAAAAAAMK/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/96t + eP//wqL//8Gi//+/nv//vJn//7mT//+2jv//sYj//66A//+pev//pHP//6Bt//+bZ///l2L/r20//8K/ + wf8AAAAAAAAAAAAAAAAAAAAAwr/B/xYXev8XF3b/GBVx/xkUbf8ZFGr/GhNm/xoSY/8bEV//HBFd/xwQ + W//Cv8H/4K96///Cov//wqL//8Ki///Cov//wJ///72b//+6lf//t4///7KJ//+ugv//qnv//6V0//+h + bv+3d0n/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/FRqE/0dN1v8/RNL/Nz3Q/y40zv8nLcz/ISfK/xwh + yf8WHMf/GxJh/8K/wf/gr3r/4K96/+Cvev/gr3r/3614/9yqdf/apnL/16Nw/9Sea//Rmmj/zZZk/8qR + X//GjFz/w4dW/7+CUv/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8SHZD/WF3a/05U1/9FS9X/PUPS/zU7 + 0P8uM83/JyzL/yAmyf8aFGn/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/ + wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/xAfnP9obt7/YGTc/1Zb + 2f9NU9f/RUrU/ztB0v80OdD/LDHO/xgWcv/Cv8H/Dn+n/w18pP8MeqH/DHie/wt1m/8Kc5j/CXGV/wlv + k/8JbJD/CGqN/wdpi/8HZ4j/BmWH/wZkhf8GYoP/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/DiKp/3l+ + 4/9vdeH/Zmze/11i2/9UWtn/S1HW/0NI1P86P9H/Fhh9/8K/wf8Ogar/Barp/wGo6P8Apef/AKPm/wCi + 5P8An+L/AJ7h/wCd3/8AnN7/AJnc/wCY2/8AmNn/AJbX/wZjhP/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/ + wf8MJbX/iI7n/4CF5v93fOP/bnPg/2Vr3f9bYdv/UljY/0lP1v8UGoj/wr/B/w+Erf8Lrur/Bqvq/wOo + 6f8Apuf/AKTm/wCi5f8AoOT/AJ/i/wCd4f8AnN//AJrd/wCZ2/8AmNr/BmWH/8K/wf8AAAAAAAAAAAAA + AAAAAAAAwr/B/wkowP+WnOz/jpTq/4aL6P9+hOX/dXri/2xx4P9jaN3/WV/b/xEek//Cv8H/EIaw/xay + 7P8Or+z/Cavr/wWq6v8Bp+j/AKbn/wCj5f8AoeT/AJ/j/wCe4f8AnOD/AJve/wCa3f8HZ4n/wr/B/wAA + AAAAAAAAAAAAAAAAAADCv8H/CCrK/6Ko7/+coe7/lZrr/42T6f+Fiub/fIHl/3N54v9rcN//ECGg/8K/ + wf8QiLP/I7nu/xq07f8Ssez/C63r/war6v8Cqen/AKbo/wCk5v8AouX/AKHk/wCf4f8AneH/AJzf/who + i//Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8GLNP/q7Hy/6as8P+hpu//mp/u/5OY6/+LkOj/g4nm/3qA + 5P8NI6z/wr/B/xCKtv8xvvD/J7rv/x627f8Vsuz/Dq/s/wmr6/8Equn/Aafo/wCl5/8Ao+X/AKHk/wCf + 4v8AnuH/CGqO/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wUu2/+vtPP/r7Tz/6qv8v+mq/D/oKXv/5me + 7f+Sl+v/io/p/wsmt//Cv8H/Eo24/0HF8f82wfD/LLzv/yK47v8atO3/EbHs/wut6/8Gq+r/A6np/wCm + 6P8Apeb/AKLl/wCh5P8IbJD/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/BC/h/wQv3/8FL9z/BS3Z/wYt + 1v8GLNL/ByvP/wgqy/8IKcb/CSnC/8K/wf8Sjrv/Uszy/0fH8f87w/H/Mb7v/ye67/8et+7/FbPt/w6v + 6/8IrOv/BKnp/wGo6P8Apef/AKPl/wluk//Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf/Cv8H/wr/B/8K/ + wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/xKRvf9j0/P/WM/z/0zK8f9BxfH/N8Hw/yy8 + 7/8iuO7/GbTt/xGx7P8Lruv/Bqrq/wOo6f8Apuf/CnGV/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCv8H/E5LA/3Ta8/9q1fP/XtHz/1LM + 8v9Hx/H/O8Pw/zG+7/8nu+//Hrbt/xay7f8Or+v/CKzq/wSq6f8Kc5j/wr/B/wAAAAAAAAAAAAAAAAAA + AADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK/wf8UlMH/hOD1/3rc + 9f9v2PP/ZNTy/1jO8v9NyvH/Qsbx/zbB8P8svO//I7ju/xm07f8SsOz/C67r/wt2m//Cv8H/AAAAAAAA + AAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwr/B/xSW + w/+T5vb/iuL1/3/e9P912vT/adbz/13R8/9SzPL/R8jx/zzD8P8xvvD/J7rv/x627v8Vsuz/C3ie/8K/ + wf8AAAAAAAAAAAAAAAAAAAAAwr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADCv8H/FJbG/57r9/+X6Pb/juT1/4Th9f963fX/b9j0/2PT8/9Yz/L/TMrx/0HF8f83wO//LLzv/yK4 + 7v8MeqH/wr/B/wAAAAAAAAAAAAAAAAAAAADCv8H/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAMK/wf8VmMf/qO/3/6Lt9/+b6vb/kub2/4rj9f9/3vX/dNrz/2rV8/9d0fP/Uszy/0fI + 8f88w/D/Mr7v/w19pP/Cv8H/AAAAAAAAAAAAAAAAAAAAAMK/wf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAwr/B/xWZyP8UmMf/FZfF/xSVw/8TlML/E5K//xOQvf8Sjrv/EYy4/xGK + tv8QiLL/D4Ww/w+Erf8Pgar/Dn+n/8K/wf8AAAAAAAAAAAAAAAAAAAAAwr/B/8K/wf/Cv8H/wr/B/8K/ + wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/ + wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/8K/wf/Cv8H/wr/B/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /////////////8AAAAPe+AAD3vgAA974AAPe+AAD3vgAA974AAPAAAADwAAAA8AAAAPAAAADwAAAA8AA + AAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAD3/gAA9/4AAPf+AAD3/gAA9/4AAPf+AADwAAAA/// + ////////KAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDA/8DA + wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP8AAAAAAAAAAMDA + wP8AAAAAAAAAAMDAwP8AAAAAwMDA/8F2R/+9bj//umc6/7diNf+3YjX/wMDA/wAAAADAwMD/AAAAAAAA + AADAwMD/AAAAAAAAAADAwMD/AAAAAMDAwP/RkmD//7aP//+ldP/8kl3/vW0//8DAwP8AAAAAwMDA/wAA + AAAAAAAAwMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/3ap2///Cov//to7//6V0/8uJWP/AwMD/AAAAAMDA + wP8AAAAAAAAAAMDAwP8THI7/FBqF/xYYfP8XFnP/wMDA/+Cvev/gr3r/4K96/92qdv/ao3D/wMDA/wAA + AADAwMD/AAAAAAAAAADAwMD/ECCd/2Fn3P8zOc//FRmC/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA + wP/AwMD/wMDA/wAAAAAAAAAAwMDA/w0krP+Pler/YWbd/xIcj//AwMD/DHmf/wpzmP8Ib5L/B2uO/wdq + jf8Gao3/B2qN/8DAwP8AAAAAAAAAAMDAwP8KJrv/r7Tz/5CU6v8PIJ//wMDA/w+Dq/87y/z/Kcb8/xrD + /P8QwPv/EMD7/wdqjf/AwMD/AAAAAAAAAADAwMD/CCrI/woowP8LJrf/DSSu/8DAwP8Sjbj/Zdb9/0/Q + /P88y/v/Kcf7/xrC+/8IbZD/wMDA/wAAAAAAAAAAwMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/FpfG/43h + /f962/3/Zdb8/0/Q/P87zPz/CXSZ/8DAwP8AAAAAAAAAAMDAwP8AAAAAAAAAAAAAAAAAAAAAwMDA/xif + z/+u6f7/n+X9/47h/f953P3/ZNb9/w19pP/AwMD/AAAAAAAAAADAwMD/AAAAAAAAAAAAAAAAAAAAAMDA + wP8apNX/uez+/7ns/v+u6f7/oOX9/43h/f8Rh7H/wMDA/wAAAAAAAAAAwMDA/wAAAAAAAAAAAAAAAAAA + AADAwMD/GqTV/xqk1f8apNX/GaHR/xecy/8WmMb/FJK+/8DAwP8AAAAAAAAAAMDAwP/AwMD/wMDA/8DA + wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAgAEAALQF + wf+0BQAAgAUAAIAFAACAAQAAgAHB/4ABAACAAQAAgAEAALwBAAC8AQAAvAHB/4ABbP///5H/ + + CenterParent @@ -370,6 +639,6 @@ AboutDialog - XenAdmin.Dialogs.XenDialogBase, XCP-ng Center, Version=99.99.99.9999, Culture=neutral, PublicKeyToken=null + XenAdmin.Dialogs.XenDialogBase, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null \ No newline at end of file diff --git a/XenAdmin/MainWindow.Designer.cs b/XenAdmin/MainWindow.Designer.cs index 885e1441b..75dd441f9 100644 --- a/XenAdmin/MainWindow.Designer.cs +++ b/XenAdmin/MainWindow.Designer.cs @@ -46,7 +46,7 @@ namespace XenAdmin System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow)); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.navigationPane = new XenAdmin.Controls.MainWindowControls.NavigationPane(); - this.TheTabControl = new System.Windows.Forms.TabControl(); + this.TheTabControl = new XenAdmin.Controls.TabControl.CustomTabControl(); this.TabPageHome = new System.Windows.Forms.TabPage(); this.TabPageGeneral = new System.Windows.Forms.TabPage(); this.TabPageBallooning = new System.Windows.Forms.TabPage(); @@ -343,6 +343,24 @@ namespace XenAdmin this.TheTabControl.Controls.Add(this.TabPageDockerProcess); this.TheTabControl.Controls.Add(this.TabPageDockerDetails); this.TheTabControl.Controls.Add(this.TabPageUSB); + this.TheTabControl.DisplayStyle = XenAdmin.Controls.TabControl.TabStyle.Angled; + // + // + // + this.TheTabControl.DisplayStyleProvider.BorderColor = System.Drawing.SystemColors.ControlDark; + this.TheTabControl.DisplayStyleProvider.BorderColorHot = System.Drawing.SystemColors.ControlDark; + this.TheTabControl.DisplayStyleProvider.BorderColorSelected = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(157)))), ((int)(((byte)(185))))); + this.TheTabControl.DisplayStyleProvider.FocusTrack = false; + this.TheTabControl.DisplayStyleProvider.HotTrack = true; + this.TheTabControl.DisplayStyleProvider.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.TheTabControl.DisplayStyleProvider.Opacity = 1F; + this.TheTabControl.DisplayStyleProvider.Overlap = 7; + this.TheTabControl.DisplayStyleProvider.Padding = new System.Drawing.Point(10, 3); + this.TheTabControl.DisplayStyleProvider.Radius = 10; + this.TheTabControl.DisplayStyleProvider.TextColor = System.Drawing.SystemColors.ControlText; + this.TheTabControl.DisplayStyleProvider.TextColorDisabled = System.Drawing.SystemColors.ControlDark; + this.TheTabControl.DisplayStyleProvider.TextColorSelected = System.Drawing.SystemColors.ControlText; + this.TheTabControl.HotTrack = true; this.TheTabControl.Name = "TheTabControl"; this.TheTabControl.SelectedIndex = 4; this.TheTabControl.SelectedIndexChanged += new System.EventHandler(this.TheTabControl_SelectedIndexChanged); @@ -2041,7 +2059,7 @@ namespace XenAdmin private System.Windows.Forms.ToolStripSeparator toolStripSeparator24; private System.Windows.Forms.ToolStripMenuItem toolbarToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem ShowHiddenObjectsToolStripMenuItem; - internal System.Windows.Forms.TabControl TheTabControl; + internal XenAdmin.Controls.TabControl.CustomTabControl TheTabControl; private System.Windows.Forms.TabPage TabPageHome; internal System.Windows.Forms.TabPage TabPageSearch; internal System.Windows.Forms.TabPage TabPageGeneral; diff --git a/XenAdmin/MainWindow.resx b/XenAdmin/MainWindow.resx index d5ccf74fb..212bbe44e 100644 --- a/XenAdmin/MainWindow.resx +++ b/XenAdmin/MainWindow.resx @@ -145,7 +145,7 @@ navigationPane - XenAdmin.Controls.MainWindowControls.NavigationPane, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.MainWindowControls.NavigationPane, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null splitContainer1.Panel1 @@ -181,13 +181,13 @@ Microsoft Sans Serif, 12pt - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 8 @@ -211,13 +211,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 4 @@ -241,10 +241,10 @@ 1 - 4, 22 + 4, 23 - 753, 591 + 753, 593 14 @@ -268,13 +268,13 @@ Top, Bottom, Left, Right - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 1 @@ -295,10 +295,10 @@ 3 - 4, 22 + 4, 23 - 753, 591 + 753, 593 22 @@ -322,13 +322,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 6 @@ -352,13 +352,13 @@ 5 - 4, 22 + 4, 23 3, 3, 3, 3 - 753, 591 + 753, 593 12 @@ -382,13 +382,13 @@ Top, Bottom, Left, Right - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 7 @@ -412,13 +412,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 7 @@ -442,13 +442,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 8 @@ -475,13 +475,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 2 @@ -502,13 +502,13 @@ 10 - 4, 22 + 4, 23 3, 3, 3, 3 - 753, 591 + 753, 593 10 @@ -529,13 +529,13 @@ 11 - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 17 @@ -556,10 +556,10 @@ 12 - 4, 22 + 4, 23 - 753, 591 + 753, 593 14 @@ -580,10 +580,10 @@ 13 - 4, 22 + 4, 23 - 753, 591 + 753, 593 18 @@ -604,10 +604,10 @@ 14 - 4, 22 + 4, 23 - 753, 591 + 753, 593 20 @@ -631,13 +631,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 0 @@ -658,13 +658,13 @@ 16 - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 8 @@ -691,13 +691,13 @@ Fill - 4, 22 + 4, 23 0, 0, 0, 0 - 753, 591 + 753, 593 0 @@ -718,13 +718,13 @@ 18 - 4, 22 + 4, 23 3, 3, 3, 3 - 753, 591 + 753, 593 24 @@ -748,13 +748,10 @@ Verdana, 8.25pt - 0, 30 - - - 0, 0 + 0, 26 - 761, 617 + 761, 620 0 @@ -763,7 +760,7 @@ TheTabControl - System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + XenAdmin.Controls.TabControl.CustomTabControl, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null splitContainer1.Panel2 @@ -793,7 +790,7 @@ alertPage - XenAdmin.TabPages.AlertSummaryPage, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.TabPages.AlertSummaryPage, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null splitContainer1.Panel2 @@ -823,7 +820,7 @@ eventsPage - XenAdmin.TabPages.HistoryPage, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.TabPages.HistoryPage, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null splitContainer1.Panel2 @@ -922,7 +919,7 @@ loggedInLabel1 - XenAdmin.Controls.LoggedInLabel, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.LoggedInLabel, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null tableLayoutPanel1 @@ -1003,7 +1000,7 @@ TitleBackPanel - XenAdmin.Controls.GradientPanel.VerticalGradientPanel, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.GradientPanel.VerticalGradientPanel, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null splitContainer1.Panel2 @@ -1645,7 +1642,7 @@ ToolStrip - XenAdmin.Controls.ToolStripEx, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.ToolStripEx, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null $this @@ -2590,7 +2587,7 @@ MainMenuBar - XenAdmin.Controls.MenuStripEx, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.MenuStripEx, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null MenuPanel @@ -2728,7 +2725,7 @@ StatusStrip - XenAdmin.Controls.StatusStripEx, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Controls.StatusStripEx, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null $this @@ -2779,7 +2776,7 @@ AddServerToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator11 @@ -2791,19 +2788,19 @@ AddPoolToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null newStorageToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null NewVmToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator12 @@ -2815,91 +2812,91 @@ shutDownToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null powerOnHostToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null startVMToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null RebootToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null resumeToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null SuspendToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null PauseVmToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null UnpauseVmToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ForceShutdownToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ForceRebootToolbarButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null stopContainerToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null startContainerToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null restartContainerToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null resumeContainerToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null pauseContainerToolStripButton - XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripButton, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null statusToolTip @@ -2923,13 +2920,13 @@ FileImportVMToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null importSearchToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator21 @@ -3025,7 +3022,7 @@ AddPoolToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator8 @@ -3037,25 +3034,25 @@ addServerToolStripMenuItem - XenAdmin.Commands.AddHostToSelectedPoolToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.AddHostToSelectedPoolToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null removeServerToolStripMenuItem - XenAdmin.Commands.PoolRemoveServerToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.PoolRemoveServerToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null poolReconnectAsToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null disconnectPoolToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator27 @@ -3067,7 +3064,7 @@ virtualAppliancesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator30 @@ -3079,73 +3076,73 @@ highAvailabilityToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemHaConfigure - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemHaDisable - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null disasterRecoveryToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null drConfigureToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null DrWizardToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null VMSnapshotScheduleToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null exportResourceReportPoolToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemWlb - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null wlbReportsToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null wlbDisconnectToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null conversionToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator9 @@ -3157,19 +3154,19 @@ changePoolPasswordToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemRotateSecret - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemEnableTls - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItem1 @@ -3181,7 +3178,7 @@ deleteToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator26 @@ -3199,7 +3196,7 @@ PoolPropertiesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null HostMenuItem @@ -3211,7 +3208,7 @@ AddHostToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItem11 @@ -3223,25 +3220,25 @@ RebootHostToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null powerOnToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ShutdownHostToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null restartToolstackToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator1 @@ -3259,19 +3256,19 @@ ReconnectToolStripMenuItem1 - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null DisconnectToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null reconnectAsToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator4 @@ -3283,25 +3280,25 @@ connectAllToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null disconnectAllToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null addServerToPoolMenuItem - XenAdmin.Commands.AddSelectedHostToPoolToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.AddSelectedHostToPoolToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null menuItemRemoveFromPool - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator3 @@ -3313,13 +3310,13 @@ backupToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null restoreFromBackupToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator23 @@ -3331,49 +3328,49 @@ toolStripMenuItemCertificate - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemInstallCertificate - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItemResetCertificate - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null maintenanceModeToolStripMenuItem1 - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null controlDomainMemoryToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null HostPasswordToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ChangeRootPasswordToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null forgetSavedPasswordToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator25 @@ -3385,13 +3382,13 @@ destroyServerToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null removeHostToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator15 @@ -3409,7 +3406,7 @@ ServerPropertiesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null VMToolStripMenuItem @@ -3421,31 +3418,31 @@ NewVmToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null startShutdownToolStripMenuItem - XenAdmin.Commands.VMLifeCycleToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.VMLifeCycleToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null resumeOnToolStripMenuItem - XenAdmin.Commands.ResumeVMOnHostToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.ResumeVMOnHostToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null relocateToolStripMenuItem - XenAdmin.Commands.MigrateVMToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.MigrateVMToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null startOnHostToolStripMenuItem - XenAdmin.Commands.StartVMOnHostToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.StartVMOnHostToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator20 @@ -3457,13 +3454,13 @@ assignSnapshotScheduleToolStripMenuItem - XenAdmin.Commands.AssignGroupToolStripMenuItemVMSS, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.AssignGroupToolStripMenuItemVMSS, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null assignToVirtualApplianceToolStripMenuItem - XenAdmin.Commands.AssignGroupToolStripMenuItemVM_appliance, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.AssignGroupToolStripMenuItemVM_appliance, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItem9 @@ -3475,31 +3472,31 @@ copyVMtoSharedStorageMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null MoveVMToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null snapshotToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null convertToTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null exportToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator32 @@ -3511,25 +3508,25 @@ toolStripMenuItemVtpm - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null disableCbtToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null enablePVSReadcachingToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null disablePVSReadcachingToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItem12 @@ -3541,7 +3538,7 @@ installToolsToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null sendCtrlAltDelToolStripMenuItem @@ -3559,7 +3556,7 @@ uninstallToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator10 @@ -3577,7 +3574,7 @@ VMPropertiesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripMenuItem8 @@ -3595,7 +3592,7 @@ AddStorageToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator22 @@ -3607,13 +3604,13 @@ RepairStorageToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null DefaultSRToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator2 @@ -3625,25 +3622,25 @@ virtualDisksToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null addVirtualDiskToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null attachVirtualDiskToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null reclaimFreedSpacetripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator19 @@ -3655,25 +3652,25 @@ DetachStorageToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ReattachStorageRepositoryToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null ForgetStorageRepositoryToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null DestroyStorageRepositoryToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator18 @@ -3691,7 +3688,7 @@ SRPropertiesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null templatesToolStripMenuItem @@ -3703,19 +3700,19 @@ CreateVmFromTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null newVMFromTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null InstantVmToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator29 @@ -3727,13 +3724,13 @@ exportTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null duplicateTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator16 @@ -3745,7 +3742,7 @@ uninstallTemplateToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolStripSeparator28 @@ -3763,7 +3760,7 @@ templatePropertiesToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null toolsToolStripMenuItem @@ -3853,7 +3850,7 @@ securityGroupsToolStripMenuItem - XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=20.3.1.0, Culture=neutral, PublicKeyToken=null + XenAdmin.Commands.CommandToolStripMenuItem, XCP-ng Center, Version=0.0.0.24039, Culture=neutral, PublicKeyToken=null statusProgressBar diff --git a/XenModel/FriendlyNameManager.cs b/XenModel/FriendlyNameManager.cs index 631900c83..9588c8e4d 100644 --- a/XenModel/FriendlyNameManager.cs +++ b/XenModel/FriendlyNameManager.cs @@ -44,10 +44,10 @@ namespace XenAdmin.Core /// public static string GetFriendlyName(string s) { - var result = FriendlyNames.GetString(s); -#if DEBUG - Debug.Assert(result != null, $"{s} doesn't exist in FriendlyNames"); -#endif + var result = FriendlyNames.GetString(s) ; +//#if DEBUG +// Debug.Assert(result != null, $"{s} doesn't exist in FriendlyNames"); +//#endif switch (s) { case "Label-host.XenMemory": diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index d7a0bcf6d..58ed10354 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -40015,6 +40015,15 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Rev: {0} BuildLab: {1}-{2}-{3}. + /// + public static string VERSION_ADDITIONAL { + get { + return ResourceManager.GetString("VERSION_ADDITIONAL", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} version {1} (build {2}) {3}-bit.. /// diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index a12e4ef83..ef4578a2f 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -168,7 +168,7 @@ Change log destination - + Install certificate... @@ -1079,7 +1079,7 @@ This could be because the data source is not generating any data. Ensure the pre Saving virtual GPU configuration - + The selected virtual GPU type supports multiple instances. @@ -7695,21 +7695,6 @@ This might result in failure to migrate VMs to this server during the RPU or to Install pending &updates... - - Install certificate... - - - Installing server certificate... - - - You cannot install a server certificate when HA is on. - - - &Install certificate... - - - Installing certificate on server {0}... - Install {0} @@ -8961,7 +8946,7 @@ To ensure system stability, it is strongly recommended that you use multipathing You must select a network - + Name @@ -9881,15 +9866,6 @@ When you configure an NFS storage repository, you simply provide the host name o Windows - - The selected virtual GPU type supports multiple instances. - - - The selected virtual GPU type does not support multiple instances. - - - Assign a virtual GPU - This pool is only licensed for {0} Virtual Apps and Desktops workloads @@ -15260,4 +15236,7 @@ Do you want to synchronize immediately? Only synchronize &visible + + Rev: {0} BuildLab: {1}-{2}-{3} + \ No newline at end of file diff --git a/branding-xcp-ng/HomePage/index.html b/branding-xcp-ng/HomePage/index.html index 4ea8743e6..2df40bfa0 100644 --- a/branding-xcp-ng/HomePage/index.html +++ b/branding-xcp-ng/HomePage/index.html @@ -4,7 +4,7 @@


- Add a Server + Add a Server