using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Drawing.Drawing2D; using System.Drawing; using System.Windows.Forms; using DevComponents.DotNetBar.Rendering; namespace DevComponents.DotNetBar { public class RadialMenuContainer : BaseItem { #region Constructor /// /// Initializes a new instance of the RadialMenuContainer class. /// public RadialMenuContainer() { _Colors = new RadialMenuColorTable(); _Colors.PropertyChanged += new PropertyChangedEventHandler(ColorsPropertyChanged); m_IsContainer = true; this.AutoCollapseOnClick = false; this.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping; } void ColorsPropertyChanged(object sender, PropertyChangedEventArgs e) { this.Invalidate(); } /// /// Returns copy of the item. /// public override BaseItem Copy() { RadialMenuContainer copy = new RadialMenuContainer(); this.CopyToItem(copy); return copy; } /// /// Copies the RadialMenuItem specific properties to new instance of the item. /// /// New ProgressBarItem instance. internal void InternalCopyToItem(RadialMenuContainer copy) { CopyToItem(copy); } /// /// Copies the RadialMenuItem specific properties to new instance of the item. /// /// New RadialMenuItem instance. protected override void CopyToItem(BaseItem copy) { base.CopyToItem(copy); RadialMenuContainer item = copy as RadialMenuContainer; item.BackButtonSymbol = _BackSymbol; item.BackButtonSymbolSize = _BackSymbolSize; item.BackButtonSymbolSet = _BackButtonSymbolSet; item.CenterButtonDiameter = _CenterButtonDiameter; item.Colors.Apply(_Colors); item.Diameter = _Diameter; item.MenuLocation = _MenuLocation; item.MenuType = _MenuType; item.SubMenuEdgeItemSpacing = _SubMenuEdgeItemSpacing; item.SubMenuEdgeWidth = _SubMenuEdgeWidth; } protected override void Dispose(bool disposing) { if (_Popup != null) { _Popup.Dispose(); _Popup = null; } if (_PopupProxy != null) { _PopupProxy._ParentRadialMenu = null; _PopupProxy.Dispose(); _PopupProxy = null; } if (_CurrentAnimation != null) _CurrentAnimation.Stop(); base.Dispose(disposing); } #endregion #region Events /// /// Occurs after radial menu is opened. /// [Description("Occurs after radial menu is opened.")] public event EventHandler Opened; /// /// Raises Opened event. /// /// Provides event arguments. protected virtual void OnOpened(EventArgs e) { EventHandler handler = Opened; if (handler != null) handler(this, e); } /// /// Occurs after radial menu is closed. /// [Description("Occurs after radial menu is closed.")] public event EventHandler Closed; /// /// Raises Closed event. /// /// Provides event arguments. protected virtual void OnClosed(EventArgs e) { EventHandler handler = Closed; if (handler != null) handler(this, e); } #endregion #region Implementation private SizeF _ScaleFactor = new SizeF(1, 1); [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SizeF ScaleFactor { get { return _ScaleFactor; } set { _ScaleFactor = value; } } private RadialSubItemInfo[] _SubItemsInfo = null; private bool _RecalcSizeInProgress = false; private const int MinItemRadialAngle = 18; // Minimum space in angles that can be occupied by single item. 18 gives 20 items on radial menu. public override void RecalcSize() { if (_RecalcSizeInProgress) return; // Prevent re-entrancy _RecalcSizeInProgress = true; try { DisposeSubItemsInfo(); int diameter = ActualDiameter; m_Rect = new Rectangle(0, 0, diameter, diameter); //this.WidthInternal = _Diameter; //this.HeightInternal = _Diameter; Rectangle bounds = new Rectangle(this.LeftInternal, this.TopInternal, diameter, diameter); bounds.Width--; bounds.Height--; Rectangle radialMenuBounds = bounds; Rectangle centerBounds = bounds; int centerButtonDiameter = ActualCenterButtonDiameter; centerBounds.Inflate(-((diameter - centerButtonDiameter) / 2 + 1), -((diameter - centerButtonDiameter) / 2 + 1)); int subMenuEdgeItemSpacing = Dpi.Width(_SubMenuEdgeItemSpacing); if (_MenuType == eRadialMenuType.Segment) { int subMenuEdgeWidth = Dpi.Width(_SubMenuEdgeWidth); bounds.Inflate(-subMenuEdgeWidth, -subMenuEdgeWidth); bounds.Inflate(-subMenuEdgeItemSpacing, -subMenuEdgeItemSpacing); } SubItemsCollection displayCollection = GetDisplayCollection(); int visibleCount = GetVisibleItemsCount(displayCollection); if (visibleCount > 0) { int maxItemRadialAngle = _MaxItemRadialAngle; if (maxItemRadialAngle == 0) maxItemRadialAngle = 180; int itemPieAngle = Math.Max(MinItemRadialAngle, Math.Min(maxItemRadialAngle, 360 / visibleCount)); if (itemPieAngle > _MaxItemPieAngle) itemPieAngle = _MaxItemPieAngle; // Single item can consume at most half of the circle int currentAngle = -90 - itemPieAngle / 2; _SubItemsInfo = new RadialSubItemInfo[displayCollection.Count]; for (int i = 0; i < displayCollection.Count; i++) { BaseItem item = displayCollection[i]; if (!item.Visible) continue; RadialMenuItem menuItem = item as RadialMenuItem; if (menuItem != null) { menuItem.RecalcSize(); GraphicsPath path = new GraphicsPath(); path.AddArc(bounds, currentAngle, itemPieAngle); Rectangle cb = centerBounds; cb.Inflate(4, 4); path.AddArc(cb, currentAngle + itemPieAngle, -itemPieAngle); path.CloseAllFigures(); menuItem.DisplayPath = path; menuItem.OutterBounds = bounds; menuItem.CenterBounds = centerBounds; menuItem.ItemAngle = currentAngle; menuItem.ItemPieAngle = itemPieAngle; menuItem.Bounds = Rectangle.Round(path.GetBounds()); } else { item.RecalcSize(); // Position item along the imaginary inner circle Rectangle innerCircle = bounds; innerCircle.Width -= item.WidthInternal; innerCircle.Height -= item.HeightInternal; if (innerCircle.Width < centerBounds.Width) innerCircle.Inflate((centerBounds.Width - innerCircle.Width) / 2, 0); if (innerCircle.Height < centerBounds.Height) innerCircle.Inflate(0, (centerBounds.Height - innerCircle.Height) / 2); Point p = new Point(innerCircle.X + (int)((innerCircle.Width / 2) * Math.Cos((currentAngle + itemPieAngle / 2) * (Math.PI / 180))), innerCircle.Y + (int)((innerCircle.Height / 2) * Math.Sin((currentAngle + itemPieAngle / 2) * (Math.PI / 180)))); p.Offset(innerCircle.Width / 2, innerCircle.Height / 2); item.TopInternal = p.Y; item.LeftInternal = p.X; } if (item != null && AnyVisibleSubItems(item)) { Rectangle outerBounds = bounds; outerBounds.Inflate(subMenuEdgeItemSpacing, subMenuEdgeItemSpacing); GraphicsPath subPath = new GraphicsPath(); int expandAngle = currentAngle + 1; int expandPieAngle = itemPieAngle - 1; subPath.AddArc(outerBounds, expandAngle, expandPieAngle); subPath.AddArc(radialMenuBounds, expandAngle + expandPieAngle, -expandPieAngle); subPath.CloseAllFigures(); Rectangle signPathBounds = radialMenuBounds; signPathBounds.Inflate(-4, -4); Point p = new Point(signPathBounds.X + (int)((signPathBounds.Width / 2) * Math.Cos((currentAngle + itemPieAngle / 2) * (Math.PI / 180))), signPathBounds.Y + (int)((signPathBounds.Height / 2) * Math.Sin((currentAngle + itemPieAngle / 2) * (Math.PI / 180)))); p.Offset(signPathBounds.Width / 2, signPathBounds.Height / 2); GraphicsPath signPath = UIGraphics.GetTrianglePath(new Point(-6, -6), 12, eTriangleDirection.Right); Matrix m = new Matrix(); if (currentAngle + itemPieAngle / 2 != 0) { m.Rotate(currentAngle + itemPieAngle / 2); m.Translate(p.X, p.Y, MatrixOrder.Append); } else m.Translate(p.X, p.Y); signPath.Transform(m); _SubItemsInfo[i] = new RadialSubItemInfo(subPath, signPath, currentAngle, itemPieAngle); } currentAngle += itemPieAngle; item.Displayed = true; } } } finally { _RecalcSizeInProgress = false; } base.RecalcSize(); } private SubItemsCollection GetDisplayCollection() { SubItemsCollection displayCollection = this.SubItems; BaseItem expanded = this.ExpandedItem(); if (expanded != null) displayCollection = expanded.SubItems; return displayCollection; } Rectangle _InnerBounds = Rectangle.Empty; public override void Paint(ItemPaintArgs p) { p.ContextData = _Colors; // Pass to the children p.ContextData2 = _MenuType; #if TRIAL if (NativeFunctions.ColorExpAlt()) { StringFormat format=new StringFormat(StringFormat.GenericDefault); format.Alignment=StringAlignment.Center; format.FormatFlags=format.FormatFlags & ~(format.FormatFlags & StringFormatFlags.NoWrap); p.Graphics.DrawString("Trial period is over.",p.Font,SystemBrushes.Highlight,this.Bounds,format); format.Dispose(); return; } #endif if (_MenuType == eRadialMenuType.Circular) { PaintCircular(p); return; } Graphics g = p.Graphics; if (_OpenState < 100) // Open animation transform setup { //p.Graphics.RotateTransform(-45 * (float)(100 - _OpenState) / 100); int openState = Math.Max(5, _OpenState); g.ScaleTransform(((float)openState) / 100, ((float)openState) / 100, MatrixOrder.Append); g.TranslateTransform((this.WidthInternal / 2) * (float)(100 - openState) / 100, (this.HeightInternal / 2) * (float)(100 - openState) / 100, MatrixOrder.Append); } bool drawBackButton = (this.ExpandedItem() != null); RadialMenuColorTable renderTable = RadialMenuContainer.GetColorTable(); RadialMenuColorTable localTable = _Colors; Color borderColor = ColorScheme.GetColor(0x2B579A); Color borderInactiveColor = Color.FromArgb(128, borderColor); Color expandSignForeground = Color.White; Color expandMouseOverColor = Color.FromArgb(200, borderColor); Color backColor = Color.White; if (!localTable.RadialMenuBorder.IsEmpty) borderColor = localTable.RadialMenuBorder; else if (renderTable != null && !renderTable.RadialMenuBorder.IsEmpty) borderColor = renderTable.RadialMenuBorder; if (!localTable.RadialMenuBackground.IsEmpty) backColor = localTable.RadialMenuBackground; else if (renderTable != null && !renderTable.RadialMenuBackground.IsEmpty) backColor = renderTable.RadialMenuBackground; if (!localTable.RadialMenuInactiveBorder.IsEmpty) borderInactiveColor = localTable.RadialMenuInactiveBorder; else if (renderTable != null && !renderTable.RadialMenuInactiveBorder.IsEmpty) borderInactiveColor = renderTable.RadialMenuInactiveBorder; if (!localTable.RadialMenuExpandForeground.IsEmpty) expandSignForeground = localTable.RadialMenuExpandForeground; else if (renderTable != null && !renderTable.RadialMenuExpandForeground.IsEmpty) expandSignForeground = renderTable.RadialMenuExpandForeground; if (!localTable.RadialMenuMouseOverBorder.IsEmpty) expandMouseOverColor = localTable.RadialMenuMouseOverBorder; else if (renderTable != null && !renderTable.RadialMenuMouseOverBorder.IsEmpty) expandMouseOverColor = renderTable.RadialMenuMouseOverBorder; Brush expandSignBrush = new SolidBrush(expandSignForeground); Brush expandMouseOverBrush = new SolidBrush(expandMouseOverColor); Brush borderBrush = new SolidBrush(borderColor); Rectangle radialMenuBounds = this.Bounds; radialMenuBounds.Width--; radialMenuBounds.Height--; using (GraphicsPath path = new GraphicsPath()) { Rectangle b = radialMenuBounds; path.AddEllipse(b); Rectangle inner = b; int diameter = ActualDiameter; int centerButtonDiameter = ActualCenterButtonDiameter; inner.Inflate(-(diameter - centerButtonDiameter) / 2, -(diameter - centerButtonDiameter) / 2); _InnerBounds = inner; int subMenuEdgeWidth = Dpi.Width(_SubMenuEdgeWidth); b.Inflate(-subMenuEdgeWidth, -subMenuEdgeWidth); using (GraphicsPath clipPath = new GraphicsPath()) { if (!drawBackButton && this.Parent == null && string.IsNullOrEmpty(_SymbolRealized) && _Image == null) { clipPath.AddEllipse(inner); g.SetClip(clipPath, CombineMode.Exclude); } using (SolidBrush brush = new SolidBrush(backColor)) p.Graphics.FillPath(brush, path); if (!drawBackButton) g.ResetClip(); } int subMenuEdgeWidthAnimated = Dpi.Width(_SubMenuEdgeWidthAnimated); b.Inflate(-subMenuEdgeWidthAnimated, -subMenuEdgeWidthAnimated); // Used by animation to show transition from to sub-items path.AddEllipse(b); //p.Graphics.DrawEllipse(Pens.Red, this.Bounds); using (Brush brush = new SolidBrush(borderInactiveColor)) { g.FillPath(brush, path); using (Pen pen = new Pen(borderColor, 2)) g.DrawEllipse(pen, inner); } if (drawBackButton) { Font symFont = Symbols.GetFont(_BackSymbolSize, _BackButtonSymbolSet); Size symSize = TextDrawing.MeasureString(g, _BackSymbol, symFont); int descent = (int)Math.Ceiling((symFont.FontFamily.GetCellDescent(symFont.Style) * symFont.Size / symFont.FontFamily.GetEmHeight(symFont.Style))); symSize.Height -= descent; TextDrawing.DrawStringLegacy(g, _BackSymbol, symFont, borderColor, new Rectangle(inner.X + (inner.Width - symSize.Width) / 2, inner.Y + (inner.Height - symSize.Height) / 2, 0, 0), eTextFormat.Default); } else if (!string.IsNullOrEmpty(_SymbolRealized)) { Font symFont = Symbols.GetFont(_SymbolSize, _SymbolSet); if (_SymbolTextSize.IsEmpty) { _SymbolTextSize = TextDrawing.MeasureString(g, _SymbolRealized, symFont); int descent = (int)Math.Ceiling((symFont.FontFamily.GetCellDescent(symFont.Style) * symFont.Size / symFont.FontFamily.GetEmHeight(symFont.Style))); _SymbolTextSize.Height -= descent; } TextDrawing.DrawStringLegacy(g, _SymbolRealized, symFont, borderColor, new Rectangle(inner.X + (inner.Width - _SymbolTextSize.Width) / 2 + 1, inner.Y + (inner.Height - _SymbolTextSize.Height) / 2 + 1, 0, 0), eTextFormat.Default); } else if (_Image != null) { g.DrawImage(_Image, inner.X + (inner.Width - _Image.Width) / 2, inner.Y + (inner.Height - _Image.Height) / 2); } } if (_OpenState < 100) // Open animation transform setup { g.ResetTransform(); g.RotateTransform(-16 * (float)(100 - _OpenState) / 100); g.ScaleTransform(((float)Math.Max(1, _OpenState)) / 100, ((float)Math.Max(1, _OpenState)) / 100, MatrixOrder.Append); g.TranslateTransform((this.WidthInternal / 2) * (float)(100 - _OpenState) / 100, (this.HeightInternal / 2) * (float)(100 - _OpenState) / 100, MatrixOrder.Append); } else if (_InnerScale < 100) // For expand animation of inner content { int openState = _InnerScale; g.ScaleTransform(((float)openState) / 100, ((float)openState) / 100, MatrixOrder.Append); g.TranslateTransform((this.WidthInternal / 2) * (float)(100 - openState) / 100, (this.HeightInternal / 2) * (float)(100 - openState) / 100, MatrixOrder.Append); } SubItemsCollection displayCollection = GetDisplayCollection(); for (int i = 0; i < displayCollection.Count; i++) { BaseItem item = displayCollection[i]; if (item.Visible && item.Displayed) { if (p.ClipRectangle.IsEmpty || p.ClipRectangle.IntersectsWith(item.DisplayRectangle)) { Region oldClip = g.Clip as Region; g.SetClip(item.DisplayRectangle, CombineMode.Intersect); if (!g.IsClipEmpty) item.Paint(p); g.Clip = oldClip; if (oldClip != null) oldClip.Dispose(); } if (_SubItemsInfo != null && _SubItemsInfo[i] != null) { if (_HotSubItemInfoIndex == i) { g.FillPath(expandMouseOverBrush, _SubItemsInfo[i].Path); g.FillPath(expandSignBrush, _SubItemsInfo[i].SignPath); } else { g.FillPath(borderBrush, _SubItemsInfo[i].Path); g.FillPath(expandSignBrush, _SubItemsInfo[i].SignPath); } } } } expandSignBrush.Dispose(); expandSignBrush = null; expandMouseOverBrush.Dispose(); expandMouseOverBrush = null; borderBrush.Dispose(); borderBrush = null; g.ResetTransform(); } private void PaintCircular(ItemPaintArgs p) { Graphics g = p.Graphics; Rectangle radialMenuBounds = this.Bounds; radialMenuBounds.Width--; radialMenuBounds.Height--; if (_OpenState < 100) // Open animation transform setup { g.ResetTransform(); g.RotateTransform(-16 * (float)(100 - _OpenState) / 100); g.ScaleTransform(((float)Math.Max(1, _OpenState)) / 100, ((float)Math.Max(1, _OpenState)) / 100, MatrixOrder.Append); g.TranslateTransform((this.WidthInternal / 2) * (float)(100 - _OpenState) / 100, (this.HeightInternal / 2) * (float)(100 - _OpenState) / 100, MatrixOrder.Append); } else if (_InnerScale < 100) // For expand animation of inner content { int openState = _InnerScale; g.ScaleTransform(((float)openState) / 100, ((float)openState) / 100, MatrixOrder.Append); g.TranslateTransform((this.WidthInternal / 2) * (float)(100 - openState) / 100, (this.HeightInternal / 2) * (float)(100 - openState) / 100, MatrixOrder.Append); } SubItemsCollection displayCollection = GetDisplayCollection(); for (int i = 0; i < displayCollection.Count; i++) { BaseItem item = displayCollection[i]; if (item.Visible && item.Displayed) { if (p.ClipRectangle.IsEmpty || p.ClipRectangle.IntersectsWith(item.DisplayRectangle)) { Region oldClip = g.Clip as Region; g.SetClip(item.DisplayRectangle, CombineMode.Intersect); if (!g.IsClipEmpty) item.Paint(p); g.Clip = oldClip; if (oldClip != null) oldClip.Dispose(); } //if (_SubItemsInfo != null && _SubItemsInfo[i] != null) //{ // if (_HotSubItemInfoIndex == i) // { // g.FillPath(expandMouseOverBrush, _SubItemsInfo[i].Path); // g.FillPath(expandSignBrush, _SubItemsInfo[i].SignPath); // } // else // { // g.FillPath(borderBrush, _SubItemsInfo[i].Path); // g.FillPath(expandSignBrush, _SubItemsInfo[i].SignPath); // } //} } } g.ResetTransform(); } private bool AnyVisibleSubItems(BaseItem item) { if (!item.ShowSubItems) return false; foreach (BaseItem t in item.SubItems) { if (t.Visible) return true; } return false; } private int GetVisibleItemsCount(SubItemsCollection displayCollection) { int c = 0; foreach (BaseItem item in displayCollection) { if (item.Visible) c++; } return c; } private void DisposeSubItemsInfo() { if (_SubItemsInfo != null) { for (int i = 0; i < _SubItemsInfo.Length; i++) { if (_SubItemsInfo[i] != null) _SubItemsInfo[i].Dispose(); } } _SubItemsInfo = null; } protected internal override void OnExpandChange() { if (this.Expanded) Open(); else { if (HotSubItem != null) HotSubItem.InternalMouseLeave(); Close(); // Collapse all sub-items CollapseRecursively(this); } base.OnExpandChange(); } private void CollapseRecursively(BaseItem item) { foreach (BaseItem sub in item.SubItems) { sub.Expanded = false; CollapseRecursively(sub); } } private int _OpenState = 0; [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public int OpenState { get { return _OpenState; } set { RadialMenuPopup popup = _Popup; if (this.IsDisposed | popup == null || popup != null && popup.IsDisposed) { _OpenState = value; return; } if (popup != null && popup.InvokeRequired && !popup.IsDisposed) { try { popup.Invoke(new MethodInvoker(delegate { this.OpenState = value; })); } catch (ObjectDisposedException) { } return; } if (value < 0) value = 0; else if (value > 100) value = 100; if (value != _OpenState) { int oldValue = _OpenState; _OpenState = value; OnOpenStateChanged(oldValue, value); } } } /// /// Called when OpenState property has changed. /// /// Old property value /// New property value protected virtual void OnOpenStateChanged(int oldValue, int newValue) { //Application.DoEvents(); //OnPropertyChanged(new PropertyChangedEventArgs("OpenState")); this.Refresh(); if (newValue == 0) { RadialMenuPopup popup = _Popup; _Popup = null; if (popup != null) { popup.Hide(); popup.Dispose(); } OnClosed(EventArgs.Empty); } else if (newValue == 100) { OnOpened(EventArgs.Empty); } } private int _InnerScale = 100; [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public int InnerScale { get { return _InnerScale; } set { if (value > 100) value = 100; else if (value < 0) value = 0; if (_InnerScale != value) { _InnerScale = value; this.Refresh(); } } } //private bool _IsOpen; //internal bool IsOpen //{ // get { return _IsOpen; } // set // { // if (_IsOpen != value) // { // _IsOpen = value; // if (_IsOpen) // Open(); // else // Close(); // } // } //} /// /// Returns internal popup control for radial menu. /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] public RadialMenuPopup Popup { get { return _Popup; } } private Animation.Animation _CurrentAnimation = null; private RadialMenuPopup _Popup = null; private bool _IsOpening = false; private RadialMenuPopupProxy _PopupProxy = null; private int _DelayCount = 0; #region Open internal void Open() { if (_IsOpening) return; if (_OpenState > 0 && _DelayCount < 2) { BarUtilities.InvokeDelayed(new MethodInvoker(delegate { this.Open(); }), 300); return; } try { _DelayCount = 0; _IsOpening = true; this.Displayed = true; WaitForCurrentAnimationToComplete(); if (this.Parent != null) // Hosted elsewhere other than RadialMenu { IOwnerMenuSupport ownerMenu = this.GetIOwnerMenuSupport(); if (ownerMenu != null) { if (_PopupProxy == null) _PopupProxy = new RadialMenuPopupProxy(this); _PopupProxy.Expanded = true; ownerMenu.RegisterPopup(_PopupProxy); } } RadialMenuPopup popup = _Popup; if (popup == null) { popup = new RadialMenuPopup(); popup.DisplayItem = this; if (_Font != null) popup.Font = _Font; else if (_RadialMenu != null) popup.Font = _RadialMenu.Font; popup.CreateControl(); } if (_MenuLocation.IsEmpty) { int diameter = ActualDiameter; if (_RadialMenu != null) { Point p = _RadialMenu.PointToScreen( new Point(_RadialMenu.Width / 2, _RadialMenu.Height / 2)); p.X -= diameter / 2; p.Y -= diameter / 2; popup.Location = p; } else { Point p = Control.MousePosition; p.X -= diameter / 2; p.Y -= diameter / 2; popup.Location = p; } } else popup.Location = _MenuLocation; popup.Visible = true; popup.Refresh(); _Popup = popup; Animation.AnimationInt anim = new DevComponents.DotNetBar.Animation.AnimationInt( new Animation.AnimationRequest(this, "OpenState", 0, 100), Animation.AnimationEasing.EaseOutCubic, 300); anim.AutoDispose = true; _CurrentAnimation = anim; anim.Start(); } finally { _IsOpening = false; } } #endregion private void WaitForCurrentAnimationToComplete() { if (_CurrentAnimation != null) { DateTime start = DateTime.Now; while (!_CurrentAnimation.IsDisposed) { Application.DoEvents(); if (DateTime.Now.Subtract(start).TotalMilliseconds > 1000) { AbortCurrentAnimation(); break; } } _CurrentAnimation = null; } } private void AbortCurrentAnimation() { Animation.Animation anim = _CurrentAnimation; _CurrentAnimation = null; if (anim != null) { anim.Stop(); anim.Dispose(); } } protected override bool ShouldCollapseParentItem() { if (this.HotSubItem != null) return HotSubItem.AutoCollapseOnClick; return base.ShouldCollapseParentItem(); } /// /// Closes currently open popup. /// internal void Close() { WaitForCurrentAnimationToComplete(); Animation.AnimationInt anim = new DevComponents.DotNetBar.Animation.AnimationInt( new Animation.AnimationRequest(this, "OpenState", 100, 0), Animation.AnimationEasing.EaseOutCubic, 300); anim.AutoDispose = true; anim.Start(); //_CurrentAnimation = anim; RadialMenuPopupProxy popupProxy = _PopupProxy; if (popupProxy != null) // Hosted elsewhere other than RadialMenu { IOwnerMenuSupport ownerMenu = this.GetIOwnerMenuSupport(); if (ownerMenu != null) { ownerMenu.UnregisterPopup(popupProxy); popupProxy.Expanded = false; } } } protected internal IOwnerMenuSupport GetIOwnerMenuSupport() { return (this.GetOwner() as IOwnerMenuSupport); } private int _MaxItemRadialAngle = 0; /// /// Indicates maximum radial angle single item in menu can consume. By default this property is set to zero which indicates that /// radial menu is equally divided between visible menu items. /// [DefaultValue(0), Category("Appearance"), Description("Indicates maximum radial angle single item in menu can consume. Max value is 180.")] public int MaxItemRadialAngle { get { return _MaxItemRadialAngle; } set { if (value < 0) value = 0; else if (value > 180) value = 180; if (value != _MaxItemRadialAngle) { int oldValue = _MaxItemRadialAngle; _MaxItemRadialAngle = value; OnMaxItemRadialAngleChanged(oldValue, value); } } } /// /// Called when MaxItemRadialAngle property has changed. /// /// Old property value /// New property value protected virtual void OnMaxItemRadialAngleChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("MaxItemRadialAngle")); NeedRecalcSize = true; this.Refresh(); } /// /// Returns diameter which takes in account current scaling factor. /// private int ActualDiameter { get { return (int)(_Diameter * _ScaleFactor.Height); } } private int _Diameter = 180; /// /// Gets or sets radial menu diameter. Minimum value is 64. /// [DefaultValue(180), Category("Appearance"), Description("Radial menu diameter.")] public int Diameter { get { return _Diameter; } set { if (value < 64) value = 64; if (value != _Diameter) { int oldValue = _Diameter; _Diameter = value; OnDiameterChanged(oldValue, value); } } } /// /// Called when Diameter property has changed. /// /// Old property value /// New property value protected virtual void OnDiameterChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("Diameter")); NeedRecalcSize = true; this.Refresh(); } /// /// Returns actual center button diameter which takes in account current scale factor. /// private int ActualCenterButtonDiameter { get { return (int)(_CenterButtonDiameter * _ScaleFactor.Height); } } private int _CenterButtonDiameter = 32; /// /// Indicates diameter of center button of radial menu. /// [DefaultValue(32), Category("Appearance"), Description("Indicates diameter of center button of radial menu.")] public int CenterButtonDiameter { get { return _CenterButtonDiameter; } set { if (value != _CenterButtonDiameter) { int oldValue = _CenterButtonDiameter; _CenterButtonDiameter = value; OnCenterButtonDiameterChanged(oldValue, value); } } } /// /// Called when CenterButtonDiameter property has changed. /// /// Old property value /// New property value protected virtual void OnCenterButtonDiameterChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("CenterButtonDiameter")); } private int _SubMenuEdgeWidth = 18; /// /// Specifies the width of the sub-menu edge around the radial menu. /// [DefaultValue(18), Category("Appearance"), Description("Specifies the width of the sub-menu edge around the radial menu.")] public int SubMenuEdgeWidth { get { return _SubMenuEdgeWidth; } set { if (value < 0) value = 0; if (value != _SubMenuEdgeWidth) { int oldValue = _SubMenuEdgeWidth; _SubMenuEdgeWidth = value; OnSubMenuEdgeWidthChanged(oldValue, value); } } } /// /// Called when SubMenuEdgeWidth property has changed. /// /// Old property value /// New property value protected virtual void OnSubMenuEdgeWidthChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("SubMenuEdgeWidth")); NeedRecalcSize = true; this.Refresh(); } private int _SubMenuEdgeItemSpacing = 3; /// /// Indicates spacing between sub-menu marking edge and the item. /// [DefaultValue(3), Category("Appearance"), Description("Indicates spacing between sub-menu marking edge and the item.")] public int SubMenuEdgeItemSpacing { get { return _SubMenuEdgeItemSpacing; } set { if (value != _SubMenuEdgeItemSpacing) { int oldValue = _SubMenuEdgeItemSpacing; _SubMenuEdgeItemSpacing = value; OnSubMenuEdgeItemSpacingChanged(oldValue, value); } } } /// /// Called when SubMenuEdgeItemSpacing property has changed. /// /// Old property value /// New property value protected virtual void OnSubMenuEdgeItemSpacingChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("SubMenuEdgeItemSpacing")); NeedRecalcSize = true; this.Refresh(); } /// /// Return Sub Item at specified location /// public override BaseItem ItemAtLocation(int x, int y) { SubItemsCollection subItems = GetDisplayCollection(); foreach (BaseItem item in subItems) { if (!item.Visible || !item.Displayed) continue; RadialMenuItem rm = item as RadialMenuItem; if (rm != null) { if (rm.DisplayPath.IsVisible(x, y)) return rm; } else if ((item.Visible || IsOnCustomizeMenu) && item.Displayed && item.DisplayRectangle.Contains(x, y)) { if (m_ViewRectangle.IsEmpty || m_ViewRectangle.Contains(x, y)) return item; } } return null; //return base.ItemAtLocation(x, y); } protected override void Invalidate(Control containerControl) { if (this.DesignMode) { if (containerControl.InvokeRequired) containerControl.BeginInvoke(new MethodInvoker(delegate { containerControl.Invalidate(); })); else containerControl.Invalidate(); } else { if (containerControl.InvokeRequired) containerControl.BeginInvoke(new MethodInvoker(delegate { containerControl.Invalidate(m_Rect, true); })); else containerControl.Invalidate(m_Rect, true); } } private Point _MenuLocation = Point.Empty; /// /// Indicates the position of top-left corner of radial menu when shown in screen coordinates /// [Category("Behavior"), Description("Indicates the position of top-left corner of radial menu when shown in screen coordinates")] public Point MenuLocation { get { return _MenuLocation; } set { if (value != _MenuLocation) { Point oldValue = _MenuLocation; _MenuLocation = value; OnMenuLocationChanged(oldValue, value); } } } /// /// Called when MenuLocation property has changed. /// /// Old property value /// New property value protected virtual void OnMenuLocationChanged(Point oldValue, Point newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("MenuLocation")); if (_Popup != null) _Popup.Location = newValue; } private RadialMenu _RadialMenu; internal RadialMenu RadialMenu { get { return _RadialMenu; } set { if (value != _RadialMenu) { RadialMenu oldValue = _RadialMenu; _RadialMenu = value; OnRadialMenuChanged(oldValue, value); } } } /// /// Called when RadialMenu property has changed. /// /// Old property value /// New property value protected virtual void OnRadialMenuChanged(RadialMenu oldValue, RadialMenu newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("RadialMenu")); } public override void InternalMouseMove(MouseEventArgs objArg) { base.InternalMouseMove(objArg); UpateHotSubItemInfoIndex(objArg); } private void UpateHotSubItemInfoIndex(MouseEventArgs objArg) { if (HotSubItem == null && _SubItemsInfo != null) { bool found = false; // Check sub items for (int i = 0; i < _SubItemsInfo.Length; i++) { RadialSubItemInfo info = _SubItemsInfo[i]; if (info != null && info.Path.IsVisible(objArg.X, objArg.Y)) { HotSubItemInfoIndex = i; found = true; break; } } if (!found) HotSubItemInfoIndex = -1; } else HotSubItemInfoIndex = -1; } public override void InternalMouseLeave() { HotSubItemInfoIndex = -1; base.InternalMouseLeave(); } public override void InternalMouseDown(MouseEventArgs objArg) { UpateHotSubItemInfoIndex(objArg); int hotSubItemInfoIndex = HotSubItemInfoIndex; if (hotSubItemInfoIndex >= 0) { SubItemsCollection displayCollection = GetDisplayCollection(); if (hotSubItemInfoIndex < displayCollection.Count) { BaseItem expand = displayCollection[hotSubItemInfoIndex]; if (expand.ShowSubItems && expand.VisibleSubItems > 0) { expand.Expanded = true; return; } } } else if (ExpandedItem() == null && _InnerBounds.Contains(objArg.Location)) { this.Expanded = false; } base.InternalMouseDown(objArg); } Animation.Storyline _CurrentStoryline = null; private void RunExpandItemAnimation() { Animation.Storyline story = new DevComponents.DotNetBar.Animation.Storyline(); story.AutoDispose = true; Animation.AnimationInt anim = new DevComponents.DotNetBar.Animation.AnimationInt( new Animation.AnimationRequest(this, "InnerScale", 100, 85), Animation.AnimationEasing.EaseInCubic, 250); anim.Animations.Add(new DevComponents.DotNetBar.Animation.AnimationRequest(this, "SubMenuEdgeWidthAnimated", 0, 12)); anim.AutoDispose = true; story.Animations.Add(anim); anim = new DevComponents.DotNetBar.Animation.AnimationInt( new Animation.AnimationRequest(this, "InnerScale", 85, 100), Animation.AnimationEasing.EaseOutCubic, 250); anim.Animations.Add(new DevComponents.DotNetBar.Animation.AnimationRequest(this, "SubMenuEdgeWidthAnimated", 12, 0)); anim.AutoDispose = true; story.Animations.Add(anim); _CurrentStoryline = story; story.Run(); } protected internal override void OnSubItemExpandChange(BaseItem item) { NeedRecalcSize = true; if (this.Expanded) { if (_CurrentStoryline == null || _CurrentStoryline.IsDisposed) { WaitForCurrentAnimationToComplete(); RunExpandItemAnimation(); } else this.Invalidate(); } HotSubItemInfoIndex = -1; base.OnSubItemExpandChange(item); } private int _HotSubItemInfoIndex = -1; private int HotSubItemInfoIndex { get { return _HotSubItemInfoIndex; } set { if (_HotSubItemInfoIndex != value) { _HotSubItemInfoIndex = value; this.Refresh(); } } } private int _SubMenuEdgeWidthAnimated = 0; [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public int SubMenuEdgeWidthAnimated { get { return _SubMenuEdgeWidthAnimated; } set { if (_SubMenuEdgeWidthAnimated != value) { _SubMenuEdgeWidthAnimated = value; this.Refresh(); } } } protected internal override void OnContainerChanged(object objOldContainer) { object newContainer = this.ContainerControl; SetContainerRecursevly(this, newContainer); //base.OnContainerChanged(objOldContainer); } private void SetContainerRecursevly(BaseItem item, object newContainer) { foreach (BaseItem c in item.SubItems) { c.ContainerControl = newContainer; SetContainerRecursevly(c, newContainer); } } /// /// Gets the current expanded subitem. /// /// protected internal override BaseItem ExpandedItem() { return GetExpandedItemRecursevly(this); } private BaseItem GetExpandedItemRecursevly(BaseItem item) { foreach (BaseItem sub in item.SubItems) { if (sub.Expanded) { BaseItem inner = GetExpandedItemRecursevly(sub); if (inner != null) return inner; return sub; } } return null; } private int _BackSymbolSize = 18; /// /// Indicates the size of the back symbol that is displayed on center of radial menu /// [DefaultValue(18), Browsable(false), Description("Indicates the size of the back symbol that is displayed on center of radial menu")] public int BackButtonSymbolSize { get { return _BackSymbolSize; } set { if (value != _BackSymbolSize) { int oldValue = _BackSymbolSize; _BackSymbolSize = value; OnBackSymbolSizeChanged(oldValue, value); } } } /// /// Called when BackSymbolSize property has changed. /// /// Old property value /// New property value protected virtual void OnBackSymbolSizeChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("BackSymbolSize")); this.Invalidate(); } private string _BackSymbol = "\uf060"; /// /// Specifies the back button symbol. /// [DefaultValue("\uf060"), Category("Appearance"), Description("Specifies the back button symbol.")] [Editor("DevComponents.DotNetBar.Design.SymbolTypeEditor, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(System.Drawing.Design.UITypeEditor))] public string BackButtonSymbol { get { return _BackSymbol; } set { if (value != _BackSymbol) { string oldValue = _BackSymbol; _BackSymbol = value; OnBackSymbolChanged(oldValue, value); } } } /// /// Called when BackSymbol property has changed. /// /// Old property value /// New property value protected virtual void OnBackSymbolChanged(string oldValue, string newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("BackSymbol")); this.Invalidate(); } private eSymbolSet _BackButtonSymbolSet = eSymbolSet.Awesome; /// /// Gets or sets the symbol set used to represent the Symbol. /// [Browsable(false), DefaultValue(eSymbolSet.Awesome)] public eSymbolSet BackButtonSymbolSet { get { return _BackButtonSymbolSet; } set { if (_BackButtonSymbolSet != value) { eSymbolSet oldValue = _BackButtonSymbolSet; _BackButtonSymbolSet = value; OnBackButtonSymbolSetChanged(oldValue, value); } } } /// /// Called when BackButtonSymbolSet property value changes. /// /// Indciates old value /// Indicates new value protected virtual void OnBackButtonSymbolSetChanged(eSymbolSet oldValue, eSymbolSet newValue) { NeedRecalcSize = true; OnAppearanceChanged(); this.Refresh(); } private int _MaxItemPieAngle = 90; /// /// Specifies the maximum pie part angle an item will occupy. Maximum is 180, minimum is 1 degree. /// [DefaultValue(90), Category("Appearance"), Description("Specifies the maximum pie part angle an item will occupy. Maximum is 180, minimum is 1 degree.")] public int MaxItemPieAngle { get { return _MaxItemPieAngle; } set { if (value < 0) value = 90; if (value > 180) value = 180; if (value != _MaxItemPieAngle) { int oldValue = _MaxItemPieAngle; _MaxItemPieAngle = value; OnMaxItemPieAngleChanged(oldValue, value); } } } /// /// Called when MaxItemPieAngle property has changed. /// /// Old property value /// New property value protected virtual void OnMaxItemPieAngleChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("MaxItemPieAngle")); NeedRecalcSize = true; this.Refresh(); } private RadialMenuColorTable _Colors = null; /// /// Gets reference to colors used by the radial menu. /// [Category("Appearance"), Description("Gets reference to colors used by the radial menu."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public RadialMenuColorTable Colors { get { return _Colors; } } internal static RadialMenuColorTable GetColorTable() { if (GlobalManager.Renderer is Office2007Renderer) return ((Office2007Renderer)GlobalManager.Renderer).ColorTable.RadialMenu; else return null; } private eRadialMenuType _MenuType = eRadialMenuType.Segment; /// /// Indicates the radial menu type. eRadialMenuType.Radial menu type allows for display of image, text and any sub-menu items. /// eRadialMenuType.Circular allows only for display of Symbol or Image and it does not display either text or sub-menu items. /// eRadialMenuType.Circular is designed to be used for single level menus only that don't need text. /// [DefaultValue(eRadialMenuType.Segment), Category("Appearance"), Description("Indicates the radial menu type.")] public eRadialMenuType MenuType { get { return _MenuType; } set { if (value != _MenuType) { eRadialMenuType oldValue = _MenuType; _MenuType = value; OnMenuTypeChanged(oldValue, value); } } } /// /// Called when MenuType property has changed. /// /// Old property value /// New property value protected virtual void OnMenuTypeChanged(eRadialMenuType oldValue, eRadialMenuType newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("MenuType")); this.Refresh(); } private Size _SymbolTextSize = Size.Empty; /// /// Gets the realized symbol string. /// [Browsable(false)] public string SymbolRealized { get { return _SymbolRealized; } } private string _Symbol = "", _SymbolRealized = ""; /// /// Indicates the symbol displayed on face of the button instead of the image. Setting the symbol overrides the image setting. /// [DefaultValue(""), Category("Appearance"), Description("Indicates the symbol displayed on face of the button instead of the image. Setting the symbol overrides the image setting.")] [Editor("DevComponents.DotNetBar.Design.SymbolTypeEditor, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(System.Drawing.Design.UITypeEditor))] public string Symbol { get { return _Symbol; } set { if (value != _Symbol) { string oldValue = _Symbol; _Symbol = value; OnSymbolChanged(oldValue, value); } } } /// /// Called when Symbol property has changed. /// /// Old property value /// New property value protected virtual void OnSymbolChanged(string oldValue, string newValue) { if (string.IsNullOrEmpty(newValue)) _SymbolRealized = ""; else _SymbolRealized = Symbols.GetSymbol(newValue); //OnPropertyChanged(new PropertyChangedEventArgs("Symbol")); _SymbolTextSize = Size.Empty; NeedRecalcSize = true; OnAppearanceChanged(); this.Refresh(); } private eSymbolSet _SymbolSet = eSymbolSet.Awesome; /// /// Gets or sets the symbol set used to represent the Symbol. /// [Browsable(false), DefaultValue(eSymbolSet.Awesome)] public eSymbolSet SymbolSet { get { return _SymbolSet; } set { if (_SymbolSet != value) { eSymbolSet oldValue = _SymbolSet; _SymbolSet = value; OnSymbolSetChanged(oldValue, value); } } } /// /// Called when SymbolSet property value changes. /// /// Indciates old value /// Indicates new value protected virtual void OnSymbolSetChanged(eSymbolSet oldValue, eSymbolSet newValue) { NeedRecalcSize = true; OnAppearanceChanged(); this.Refresh(); } private float _SymbolSize = 0f; /// /// Indicates the size of the symbol in points. /// [DefaultValue(0f), Category("Appearance"), Description("Indicates the size of the symbol in points.")] public float SymbolSize { get { return _SymbolSize; } set { if (value != _SymbolSize) { float oldValue = _SymbolSize; _SymbolSize = value; OnSymbolSizeChanged(oldValue, value); } } } /// /// Called when SymbolSize property has changed. /// /// Old property value /// New property value protected virtual void OnSymbolSizeChanged(float oldValue, float newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("SymbolSize")); _SymbolTextSize = Size.Empty; NeedRecalcSize = true; OnAppearanceChanged(); this.Refresh(); } private Image _Image; /// /// Gets or sets the image displayed on the item. /// [DefaultValue(null), Category("Appearance"), Description("Indicates image displayed on the item.")] public Image Image { get { return _Image; } set { if (value != _Image) { Image oldValue = _Image; _Image = value; OnImageChanged(oldValue, value); } } } /// /// Called when Image property has changed. /// /// Old property value /// New property value protected virtual void OnImageChanged(Image oldValue, Image newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("Image")); NeedRecalcSize = true; OnAppearanceChanged(); this.Refresh(); } #region RadialSubItemInfo private class RadialSubItemInfo { /// /// Initializes a new instance of the RadialSubItemInfo class. /// /// /// /// /// public RadialSubItemInfo(GraphicsPath path, GraphicsPath signPath, int itemAngle, int itemPieAngle) { Path = path; SignPath = signPath; ItemAngle = itemAngle; ItemPieAngle = itemPieAngle; } public GraphicsPath Path; public GraphicsPath SignPath; public int ItemAngle; public int ItemPieAngle; public void Dispose() { if (Path != null) Path.Dispose(); if (SignPath != null) SignPath.Dispose(); Path = null; } } #endregion #endregion #region RadialMenuPopupProxy private class RadialMenuPopupProxy : PopupItem { internal RadialMenuContainer _ParentRadialMenu = null; /// /// Initializes a new instance of the RadialMenuPopupProxy class. /// /// public RadialMenuPopupProxy(RadialMenuContainer parentRadialMenu) { _ParentRadialMenu = parentRadialMenu; } protected internal override bool IsAnyOnHandle(IntPtr iHandle) { if (_ParentRadialMenu._Popup != null && _ParentRadialMenu._Popup.Handle == iHandle) return true; return false; } private bool _Expanded = false; public override bool Expanded { get { return _Expanded; } set { if (_Expanded != value) { _Expanded = value; if (!_Expanded && _ParentRadialMenu.Expanded) _ParentRadialMenu.Expanded = false; } } } public override void Paint(ItemPaintArgs p) { } public override BaseItem Copy() { return new RadialMenuPopupProxy(_ParentRadialMenu); } } private Font _Font; /// /// Specifies font for menu items. /// [DefaultValue(null), Description("Specifies font for menu items."), Category("Appearance")] public Font Font { get { return _Font; } set { if (value != _Font) { Font oldValue = _Font; _Font = value; OnFontChanged(oldValue, value); } } } /// /// Called when Font property has changed. /// /// Old property value /// New property value protected virtual void OnFontChanged(Font oldValue, Font newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("Font")); if (_Popup != null && this.Expanded) _Popup.Invalidate(); } #endregion } /// /// Defines available radial menu type. /// public enum eRadialMenuType { Segment, Circular } }