Beispiel #1
0
        protected override void OnClick(EventArgs e)
        {
            MenuWithHandler definition = (Tag as MenuWithHandler);

            if (definition.Target != null)
            {
                definition.Target.OnCommand(definition.ID);
            }
            // Sometimes all menu texts are blank. I cannot reproduce it. I guess, it is because the menus are not disposed. I debugged by overriding Dispose of ContextMenuWithHandler
            // If a menu item is clicked, the menu disappears and can be disposed. This is forced here. But if the menu disappears because of some other reason (ESC, click somewhere else)
            // Dispose doesn't get called until maybe much later or when the form closes.
            ContextMenuWithHandler toDispose = null;
            Menu parent = this.Parent;

            while (parent != null)
            {
                if (parent is ContextMenuWithHandler cmh)
                {
                    toDispose = cmh;
                    break;
                }
                if (parent is MenuItem mi)
                {
                    parent = mi.Parent;
                }
                else
                {
                    break;
                }
            }
            toDispose?.Dispose();
        }
Beispiel #2
0
        protected override void OnMeasureItem(System.Windows.Forms.MeasureItemEventArgs e)
        {
            MenuWithHandler definition = Tag as MenuWithHandler;

            if (this.Text == "-")
            {
                e.ItemHeight = 3;
            }
            else
            {
                Font   f          = SystemInformation.MenuFont;
                string toMeassure = this.Text;
                if (!string.IsNullOrEmpty(definition.Shortcut))
                {
                    toMeassure += "    " + definition.Shortcut;
                }
                SizeF sz = e.Graphics.MeasureString(toMeassure, SystemInformation.MenuFont);
                if (this.Parent is MainMenu)
                {
                    e.ItemHeight = SystemInformation.MenuHeight;
                    e.ItemWidth  = (int)sz.Width; // TopLevel hat kein Bildchen
                }
                else
                {
                    e.ItemHeight = ButtonImages.ButtonImageList.ImageSize.Height + 6; // 3 oben, 3 unten
                    e.ItemWidth  = (ButtonImages.ButtonImageList.ImageSize.Width + 6) + ButtonImages.ButtonImageList.ImageSize.Width / 2 + (int)sz.Width;
                    // (Bildchen + 3 links + 3 rechts) + TextOffset + Textbreite
                }
            }
        }
Beispiel #3
0
 public MenuItemWithHandler(MenuWithHandler definition) : base()
 {
     OwnerDraw = true;
     Text      = definition.Text;
     Tag       = definition;
     if (!string.IsNullOrEmpty(definition.Shortcut))
     {
         Shortcut     = ShortcutFromString(definition.Shortcut);
         ShowShortcut = definition.ShowShortcut;
     }
     // no need for the following, if Update
     //CommandState commandState = new CommandState();
     //if (definition.Target.OnUpdateCommand(definition.ID, commandState))
     //{
     //    Enabled = commandState.Enabled;
     //    Checked = commandState.Checked;
     //    doubleChecked = commandState.Checked;
     //}
     if (definition.SubMenus != null)
     {
         MenuItem[] sub = new MenuItem[definition.SubMenus.Length];
         for (int i = 0; i < definition.SubMenus.Length; i++)
         {
             sub[i] = new MenuItemWithHandler(definition.SubMenus[i]);
         }
         MenuItems.AddRange(sub);
     }
 }
Beispiel #4
0
        protected override void OnSelect(EventArgs e)
        {
            MenuWithHandler definition = (Tag as MenuWithHandler);

            if (definition.Target != null)
            {
                definition.Target.OnSelected(definition, true);
            }
            base.OnSelect(e);
        }
Beispiel #5
0
 private void UpdateMRUMenu(MenuItem mi, string[] mruFiles)
 {
     if (mi.IsParent)
     {
         foreach (MenuItem mmi in mi.MenuItems)
         {
             UpdateMRUMenu(mmi, mruFiles);
         }
     }
     else
     {
         MenuItemWithHandler mid = mi as MenuItemWithHandler;
         if (mid != null)
         {
             MenuWithHandler mwh = mid.Tag as MenuWithHandler;
             if (mwh != null)
             {
                 string MenuId = mwh.ID;
                 if (MenuId.StartsWith("MenuId.File.Mru.File"))
                 {
                     string filenr = MenuId.Substring("MenuId.File.Mru.File".Length);
                     try
                     {
                         int n = int.Parse(filenr);
                         if (n <= mruFiles.Length && n > 0)
                         {
                             string[] parts = mruFiles[mruFiles.Length - n].Split(';');
                             if (parts.Length > 1)
                             {
                                 mid.Text = parts[0];
                             }
                         }
                     }
                     catch (FormatException) { }
                     catch (OverflowException) { }
                 }
             }
         }
     }
 }
Beispiel #6
0
 protected override void OnPopup(EventArgs e)
 {
     if (this.IsParent)
     {
         foreach (MenuItem mi in MenuItems)
         {
             MenuWithHandler definition = (mi.Tag as MenuWithHandler);
             if (definition != null)
             {
                 if (definition.Target != null)
                 {
                     CommandState commandState = new CommandState();
                     if (definition.Target.OnUpdateCommand(definition.ID, commandState))
                     {
                         mi.Enabled = commandState.Enabled;
                         mi.Checked = commandState.Checked;
                         (mi as MenuItemWithHandler).doubleChecked = commandState.Checked;
                     }
                     else
                     {
                         //IFrameInternal frm = FrameImpl.MainFrame as IFrameInternal;
                         //bool handled = false;
                         //if (frm != null) frm.UpdateContextMenu(miid.mTarget, miid.mID, commandState, ref handled);
                         //if (handled)
                         //{
                         //    miid.Enabled = commandState.Enabled;
                         //    miid.Checked = commandState.Checked;
                         //    miid.doubleChecked = commandState.Checked;
                         //}
                     }
                 }
             }
         }
     }
     base.OnPopup(e);
 }
Beispiel #7
0
 void ICommandHandler.OnSelected(MenuWithHandler selectedMenuItem, bool selected)
 {
 }
Beispiel #8
0
 public virtual void OnSelected(MenuWithHandler selectedMenuItem, bool selected)
 {
 }
Beispiel #9
0
 public override void OnSelected(MenuWithHandler selectedMenuItem, bool selected)
 {
 }
Beispiel #10
0
        protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
        {
            MenuWithHandler definition = Tag as MenuWithHandler;

            if (this.Text == "-")
            {
                Rectangle LeftSquare = e.Bounds;
                Rectangle Text       = e.Bounds;
                LeftSquare.Width = ButtonImages.ButtonImageList.ImageSize.Width + 6;
                Text.Offset(LeftSquare.Width, 0);
                Text.Width -= LeftSquare.Width;
                LinearGradientBrush leftBrush = new LinearGradientBrush(LeftSquare, SystemColors.ControlLightLight, SystemColors.ControlDark, LinearGradientMode.Horizontal);
                e.Graphics.FillRectangle(leftBrush, LeftSquare);
                e.Graphics.FillRectangle(SystemBrushes.ControlLightLight, Text);
                e.Graphics.DrawLine(SystemPens.Control, e.Bounds.Left + (ButtonImages.ButtonImageList.ImageSize.Width + 6) + ButtonImages.ButtonImageList.ImageSize.Width / 2, e.Bounds.Top + 1, e.Bounds.Right, e.Bounds.Top + 1);
            }
            else
            {
                if (this.Parent is MainMenu)
                {
                    if ((e.State & DrawItemState.HotLight) != 0)
                    {
                        Color c = Color.FromArgb(
                            ((int)SystemColors.ActiveCaption.R + 2 * (int)SystemColors.ControlLightLight.R) / 3,
                            ((int)SystemColors.ActiveCaption.G + 2 * (int)SystemColors.ControlLightLight.G) / 3,
                            ((int)SystemColors.ActiveCaption.B + 2 * (int)SystemColors.ControlLightLight.B) / 3);
                        e.Graphics.FillRectangle(new SolidBrush(c), e.Bounds);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Left, e.Bounds.Top, e.Bounds.Right - 1, e.Bounds.Top);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Right - 1, e.Bounds.Top, e.Bounds.Right - 1, e.Bounds.Bottom - 1);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Right - 1, e.Bounds.Bottom - 1, e.Bounds.Left, e.Bounds.Bottom - 1);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Left, e.Bounds.Top);
                    }
                    else
                    {
                        e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds);
                    }
                    float        TextX = e.Bounds.Left;
                    float        TextY = e.Bounds.Top + (e.Bounds.Height - SystemInformation.MenuFont.GetHeight(e.Graphics)) / 2;
                    StringFormat sf    = StringFormat.GenericDefault;
                    sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
                    Brush textBrush;
                    if (!Enabled)
                    {
                        textBrush = SystemBrushes.FromSystemColor(SystemColors.GrayText);
                    }
                    else
                    {
                        textBrush = SystemBrushes.WindowText;
                    }
                    e.Graphics.DrawString(this.Text, SystemInformation.MenuFont, textBrush, TextX, TextY, sf);
                }
                else
                {
                    Rectangle LeftSquare = e.Bounds;
                    Rectangle Text       = e.Bounds;
                    LeftSquare.Width = ButtonImages.ButtonImageList.ImageSize.Width + 6;
                    Text.Offset(LeftSquare.Width, 0);
                    Text.Width -= LeftSquare.Width;
                    LinearGradientBrush leftBrush = new LinearGradientBrush(LeftSquare, SystemColors.ControlLightLight, SystemColors.ControlDark, LinearGradientMode.Horizontal);
                    if ((e.State & DrawItemState.Selected) != 0)
                    {
                        Color c = Color.FromArgb(
                            ((int)SystemColors.ActiveCaption.R + 2 * (int)SystemColors.ControlLightLight.R) / 3,
                            ((int)SystemColors.ActiveCaption.G + 2 * (int)SystemColors.ControlLightLight.G) / 3,
                            ((int)SystemColors.ActiveCaption.B + 2 * (int)SystemColors.ControlLightLight.B) / 3);
                        e.Graphics.FillRectangle(new SolidBrush(c), e.Bounds);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Left, e.Bounds.Top, e.Bounds.Right - 1, e.Bounds.Top);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Right - 1, e.Bounds.Top, e.Bounds.Right - 1, e.Bounds.Bottom - 1);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Right - 1, e.Bounds.Bottom - 1, e.Bounds.Left, e.Bounds.Bottom - 1);
                        e.Graphics.DrawLine(SystemPens.ControlText, e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Left, e.Bounds.Top);
                    }
                    else
                    {
                        e.Graphics.FillRectangle(leftBrush, LeftSquare);
                        e.Graphics.FillRectangle(SystemBrushes.ControlLightLight, Text);
                    }
                    float        TextX = e.Bounds.Left + (ButtonImages.ButtonImageList.ImageSize.Width + 6) + ButtonImages.ButtonImageList.ImageSize.Width / 2;
                    float        TextY = e.Bounds.Top + (e.Bounds.Height - SystemInformation.MenuFont.GetHeight(e.Graphics)) / 2;
                    StringFormat sf    = StringFormat.GenericDefault;
                    sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
                    Brush textBrush;
                    if (!Enabled)
                    {
                        textBrush = SystemBrushes.FromSystemColor(SystemColors.GrayText);
                    }
                    else
                    {
                        textBrush = SystemBrushes.WindowText;
                    }
                    e.Graphics.DrawString(this.Text, SystemInformation.MenuFont, textBrush, TextX, TextY, sf);
                    string shortcut = definition.Shortcut;// GetShortcutString();
                    if (!string.IsNullOrEmpty(shortcut))
                    {
                        SizeF sz = e.Graphics.MeasureString(shortcut, SystemInformation.MenuFont);
                        e.Graphics.DrawString(shortcut, SystemInformation.MenuFont, textBrush, e.Bounds.Right - e.Bounds.Height / 2 - sz.Width, TextY, sf);
                    }
                    int dx = (e.Bounds.Height - ButtonImages.ButtonImageList.ImageSize.Height) / 2;
                    if (definition.ImageIndex >= 0)
                    {
                        int ind = definition.ImageIndex;
                        if (ind >= 10000)
                        {
                            ind = ind - 10000 + ButtonImages.OffsetUserImages;
                        }
                        if (Enabled)
                        {
                            ButtonImages.ButtonImageList.Draw(e.Graphics, e.Bounds.Left + 3, e.Bounds.Top + 3, ind);
                        }
                        else
                        {
                            ControlPaint.DrawImageDisabled(e.Graphics, ButtonImages.ButtonImageList.Images[ind], e.Bounds.Left + 3, e.Bounds.Top + 3, Color.FromArgb(0, 0, 0, 0));
                        }
                    }
                    if ((e.State & DrawItemState.Checked) != 0 || doubleChecked)
                    {
                        if (definition.ImageIndex < 0)
                        {
                            ButtonImages.ButtonImageList.Draw(e.Graphics, e.Bounds.Left + 3, e.Bounds.Top + 3, 84); // the red check mark
                        }
                        int x1 = e.Bounds.Left + 1;
                        int y1 = e.Bounds.Top + 1;
                        int x2 = x1 + ButtonImages.ButtonImageList.ImageSize.Width + 4;
                        int y2 = y1 + ButtonImages.ButtonImageList.ImageSize.Height + 4;
                        e.Graphics.DrawLine(SystemPens.ControlText, x1, y1, x2, y1);
                        e.Graphics.DrawLine(SystemPens.ControlText, x2, y1, x2, y2);
                        e.Graphics.DrawLine(SystemPens.ControlText, x2, y2, x1, y2);
                        e.Graphics.DrawLine(SystemPens.ControlText, x1, y2, x1, y1);
                    }
                }
            }
        }
Beispiel #11
0
        private List <MenuWithHandler> GetFacesSubmenus(Face face)
        {
            List <MenuWithHandler> res = new List <MenuWithHandler>();

            if (face.IsFillet())
            {
                HashSet <Face> connectedFillets = new HashSet <Face>();
                CollectConnectedFillets(face, connectedFillets);
                FeatureCommandHandler fch = new FeatureCommandHandler(connectedFillets.ToArray(), this, "MenuId.Fillet");
                MenuWithHandler       fr  = new MenuWithHandler("MenuId.Fillet.ChangeRadius");
                fr.OnCommand = (menuId) =>
                {
                    ParametricsRadius pr = new ParametricsRadius(connectedFillets.ToArray(), soa.Frame, true);
                    soa.Frame.SetAction(pr);
                    return(true);
                };
                fr.OnSelected = (mh, selected) =>
                {
                    currentMenuSelection.Clear();
                    currentMenuSelection.AddRange(connectedFillets.ToArray());
                    currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                };
                MenuWithHandler fd = new MenuWithHandler("MenuId.Fillet.Remove");
                fd.OnCommand = (menuId) =>
                {
                    Face[] involvedFaces = connectedFillets.ToArray();
                    Shell  orgShell      = involvedFaces[0].Owner as Shell;
                    if (orgShell != null)
                    {
                        RemoveFillet rf = new RemoveFillet(involvedFaces[0].Owner as Shell, new HashSet <Face>(involvedFaces));
                        Shell        sh = rf.Result();
                        if (sh != null)
                        {
                            using (soa.Frame.Project.Undo.UndoFrame)
                            {
                                sh.CopyAttributes(orgShell);
                                IGeoObjectOwner owner = orgShell.Owner;
                                owner.Remove(orgShell);
                                owner.Add(sh);
                            }
                        }
                    }
                    soa.ResetMode();
                    return(true);
                };
                fd.OnSelected = (mh, selected) =>
                {
                    currentMenuSelection.Clear();
                    currentMenuSelection.AddRange(connectedFillets.ToArray());
                    currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                };
                fch.SubMenus = new MenuWithHandler[] { fr, fd };
                res.Add(fch);
            }
            IEnumerable <Face> connected = face.GetSameSurfaceConnected();

            // if (connected.Any())
            {
                List <Face> lconnected = new List <Face>(connected);
                lconnected.Add(face);
                BoundingCube ext = BoundingCube.EmptyBoundingCube;
                foreach (Face fc in connected)
                {
                    ext.MinMax(fc.GetExtent(0.0));
                }
                // maybe a full sphere, cone, cylinder or torus:
                // except for the sphere: position axis
                // except for the cone: change radius or diameter
                // for the cone: smaller and larger diameter
                // for cone and cylinder: total length
                if (face.Surface is CylindricalSurface || face.Surface is CylindricalSurfaceNP || face.Surface is ToroidalSurface)
                {
                    MenuWithHandler mh = new MenuWithHandler("MenuId.FeatureDiameter");
                    mh.OnCommand = (menuId) =>
                    {
                        ParametricsRadius pr = new ParametricsRadius(lconnected.ToArray(), soa.Frame, false);
                        soa.Frame.SetAction(pr);
                        return(true);
                    };
                    mh.OnSelected = (menuId, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.AddRange(lconnected.ToArray());
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };
                    res.Add(mh);
                }
                if (face.Surface is CylindricalSurface || face.Surface is CylindricalSurfaceNP || face.Surface is ConicalSurface)
                {
                    Line axis = null;

                    if (face.Surface is ICylinder cyl)
                    {
                        axis = cyl.Axis.Clip(ext);
                    }
                    if (face.Surface is ConicalSurface cone)
                    {
                        axis = cone.AxisLine(face.Domain.Bottom, face.Domain.Top);
                    }
                    MenuWithHandler mh = new MenuWithHandler("MenuId.AxisPosition");
                    mh.OnCommand = (menuId) =>
                    {
                        ParametricsDistanceActionOld pd = new ParametricsDistanceActionOld(lconnected, axis, soa.Frame);
                        soa.Frame.SetAction(pd);
                        return(true);
                    };
                    mh.OnSelected = (menuId, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.AddRange(lconnected.ToArray());
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };
                    res.Add(mh);
                }
            }
            if (face.Surface is PlaneSurface pls)
            {
                // try to find parallel outline edges to modify the distance
                Edge[] outline = face.OutlineEdges;
                for (int j = 0; j < outline.Length - 1; j++)
                {
                    for (int k = j + 1; k < outline.Length; k++)
                    {
                        if (outline[j].Curve3D is Line l1 && outline[k].Curve3D is Line l2)
                        {
                            if (Precision.SameDirection(l1.StartDirection, l2.StartDirection, false))
                            {
                                // two parallel outline lines, we could parametrize the distance
                                Edge   o1   = outline[j];
                                Edge   o2   = outline[k]; // outline[i] is not captured correctly for the anonymous method. I don't know why. With local copies, it works.
                                double lmin = double.MaxValue;
                                double lmax = double.MinValue;
                                double p    = Geometry.LinePar(l1.StartPoint, l1.EndPoint, l2.StartPoint);
                                lmin = Math.Min(lmin, p);
                                lmax = Math.Max(lmax, p);
                                p    = Geometry.LinePar(l1.StartPoint, l1.EndPoint, l2.EndPoint);
                                lmin = Math.Max(Math.Min(lmin, p), 0);
                                lmax = Math.Min(Math.Max(lmax, p), 1);
                                GeoPoint p1 = Geometry.LinePos(l1.StartPoint, l1.EndPoint, (lmin + lmax) / 2.0);
                                GeoPoint p2 = Geometry.DropPL(p1, l2.StartPoint, l2.EndPoint);
                                if ((p1 | p2) > Precision.eps)
                                {
                                    MenuWithHandler mh            = new MenuWithHandler("MenuId.EdgeDistance");
                                    Line            feedback      = Line.TwoPoints(p1, p2);
                                    GeoObjectList   feedbackArrow = currentView.Projection.MakeArrow(p1, p2, pls.Plane, Projection.ArrowMode.circleArrow);
                                    mh.OnCommand = (menuId) =>
                                    {
                                        ParametricsDistanceActionOld pd = new ParametricsDistanceActionOld(o1, o2, feedback, pls.Plane, soa.Frame);
                                        soa.Frame.SetAction(pd);
                                        return(true);
                                    };
                                    mh.OnSelected = (m, selected) =>
                                    {
                                        currentMenuSelection.Clear();
                                        currentMenuSelection.AddRange(feedbackArrow);
                                        currentMenuSelection.Add(o1.OtherFace(face));
                                        currentMenuSelection.Add(o2.OtherFace(face));
                                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                                    };
                                    res.Add(mh);
                                }
                            }
                        }
                    }
                }
            }
            if (res.Count > 6)
            {
                List <MenuWithHandler> lm = new List <MenuWithHandler>();
                for (int i = 4; i < res.Count; i++)
                {
                    lm.Add(res[i]);
                }
                res.RemoveRange(4, lm.Count);
                MenuWithHandler subMenu = new MenuWithHandler("MenuId.More");
                subMenu.SubMenus = lm.ToArray();
                res.Add(subMenu);
            }
            return(res);
        }
Beispiel #12
0
 void ICommandHandler.OnSelected(MenuWithHandler selectedMenuItem, bool selected)
 {
     selectActionContextMenu.currentMenuSelection.Clear();
     selectActionContextMenu.currentMenuSelection.AddRange(involvedFaces);
     selectActionContextMenu.currentView.Invalidate(PaintBuffer.DrawingAspect.Select, selectActionContextMenu.currentView.DisplayRectangle);
 }
Beispiel #13
0
        private void ShowContextMenu(Point mousePoint, IView vw)
        {
            currentView = vw;
            GeoObjectList result = new GeoObjectList();

            int pickRadius = soa.Frame.GetIntSetting("Select.PickRadius", 5);

            Projection.PickArea pa = vw.Projection.GetPickSpace(new Rectangle(mousePoint.X - pickRadius, mousePoint.Y - pickRadius, pickRadius * 2, pickRadius * 2));
            IActionInputView    pm = vw as IActionInputView;
            GeoObjectList       fl = vw.Model.GetObjectsFromRect(pa, new Set <Layer>(pm.GetVisibleLayers()), PickMode.onlyFaces, null); // returns all the face under the cursor

            // in most cases there is only a single face, which is of interest, only when we have two solids with same or overlapping faces
            // and one of them is not selectable without also selecting the other, we want both.
            faces    = new List <Face>();
            shells   = new List <Shell>(); // only Shells, which are not part of a Solid
            solids   = new List <Solid>();
            features = new List <IEnumerable <Face> >();
            double delta   = vw.Model.Extent.Size * 1e-4;
            double mindist = double.MaxValue;

            for (int i = 0; i < fl.Count; i++)
            {
                if (fl[i] is Face face) // this should always be the case
                {
                    double z = face.Position(pa.FrontCenter, pa.Direction, 0);
                    if (z < mindist)
                    {
                        if (z < mindist - delta)
                        {
                            faces.Clear();
                        }
                        faces.Add(face);
                        mindist = z;
                    }
                }
            }
            HashSet <Edge> relevantEdges = new HashSet <Edge>();

            for (int i = 0; i < faces.Count; i++)
            {
                relevantEdges.UnionWith(faces[i].AllEdges);
                if (faces[i].Owner is Shell shell)
                {
                    if (shell.Owner is Solid sld)
                    {
                        if (!solids.Contains(sld))
                        {
                            solids.Add(sld);
                        }
                    }
                    else
                    {
                        if (!shells.Contains(shell))
                        {
                            shells.Add(shell);
                        }
                    }
                }
            }
            for (int i = 0; i < faces.Count; i++)
            {
                if (faces[i].Owner is Shell shell)
                {
                    for (int j = 0; j < faces[i].OutlineEdges.Length; j++)
                    {
                        Edge edg = faces[i].OutlineEdges[j];
                        if (edg.IsPartOfHole(edg.OtherFace(faces[i])))
                        {
                            Face otherFace = edg.OtherFace(faces[i]);
                            if (otherFace != null)
                            {
                                Edge[]        hole  = otherFace.GetHole(edg);
                                List <Edge[]> loops = new List <Edge[]> {
                                    hole
                                };
                                List <IEnumerable <Face> > lfeatures = new List <IEnumerable <Face> >();
                                List <IEnumerable <Face> > lids      = new List <IEnumerable <Face> >();
                                HashSet <Face>             startWith = new HashSet <Face>();
                                foreach (Edge edge in hole)
                                {
                                    startWith.Add(edge.OtherFace(otherFace)); // all faces that are connected to the hole in "otherFace"
                                }
                                shell.FeaturesFromEdges(loops, lfeatures, lids, startWith);
                                for (int k = 0; k < lfeatures.Count; k++)
                                {
                                    if (lfeatures[k].Count() < shell.Faces.Length * 0.5)
                                    {
                                        features.Add(lfeatures[k]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            curves = vw.Model.GetObjectsFromRect(pa, new Set <Layer>(pm.GetVisibleLayers()), PickMode.onlyEdges, null); // returns edges and curves
            edges  = new List <Edge>();
            // we only accept edges, which belong to one of the selected faces
            for (int i = curves.Count - 1; i >= 0; --i)
            {
                if (curves[i].Owner is Edge edge)
                {
                    if (relevantEdges.Contains(edge))
                    {
                        edges.Add(edge);
                    }
                    else
                    {
                        double z = curves[i].Position(pa.FrontCenter, pa.Direction, 0);
                        if (z - delta < mindist)
                        {
                            edges.Add(edge);                      // this is an edge in front of the closest face
                        }
                    }
                    curves.Remove(i);
                }
                else
                {
                    double z = curves[i].Position(pa.FrontCenter, pa.Direction, 0);
                    if (z - delta > mindist)
                    {
                        curves.Remove(i);
                    }
                }
            }

            // now we have curves, edges, faces, shells and solids (text, hatch, block, dimension not yet implemented) from the right click

            // we also need access to the already selected edges and faces to
            GeoObjectList  selobj        = soa.GetSelectedObjects();
            HashSet <Edge> selectedEdges = new HashSet <Edge>();
            HashSet <Face> selectedFaces = new HashSet <Face>();

            foreach (IGeoObject go in selobj)
            {
                if (go is ICurve && go.Owner is Edge edg)
                {
                    selectedEdges.Add(edg);
                }
                if (go is Face fc)
                {
                    selectedFaces.Add(fc);
                }
            }
            List <MenuWithHandler> cm = new List <MenuWithHandler>();

            Shell owningShell = null;

            if (edges.Count > 0)
            {
                owningShell = (edges[0].Owner as Face).Owner as Shell;
            }
            if (owningShell == null && faces.Count > 0)
            {
                owningShell = (faces[0].Owner as Shell);
            }
            if (owningShell == null && selectedFaces.Count > 0)
            {
                owningShell = selectedFaces.First().Owner as Shell;
            }
            if (owningShell == null && selectedEdges.Count > 0)
            {
                owningShell = (selectedEdges.First().Owner as Face).Owner as Shell;
            }
            if (owningShell != null)
            {
                for (int i = 0; i < features.Count; i++)
                {
                    List <Face>     featureI = new List <Face>(features[i]);
                    MenuWithHandler mh       = new MenuWithHandler("MenuId.Feature");
                    mh.OnSelected = (m, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.AddRange(featureI.ToArray());
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };
                    MenuWithHandler positionFeature = new MenuWithHandler("MenuId.Feature.Position");
                    MenuWithHandler nameFeature     = new MenuWithHandler("MenuId.Feature.Name");
                    mh.SubMenus = new MenuWithHandler[] { positionFeature, nameFeature };
                    // this is very rudimentary. We have to provide a version of ParametricsDistanceAction, where you can select from and to object. Only axis is implemented
                    Shell         shell = featureI[0].Owner as Shell;
                    GeoObjectList fa    = shell.FeatureAxis;
                    Line          axis  = null;
                    foreach (IGeoObject geoObject in fa)
                    {
                        Face axisOf = geoObject.UserData.GetData("CADability.AxisOf") as Face;
                        if (axisOf != null)
                        {
                            if (featureI.Contains(axisOf))
                            {
                                axis = geoObject as Line;
                            }
                        }
                        if (axis != null)
                        {
                            break;
                        }
                    }
                    positionFeature.OnCommand = (menuId) =>
                    {
                        ParametricsDistanceActionOld pd = new ParametricsDistanceActionOld(featureI, soa.Frame);
                        soa.Frame.SetAction(pd);
                        return(true);
                    };
                    positionFeature.OnSelected = (menuId, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.AddRange(featureI.ToArray());
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };
                    nameFeature.OnCommand = (menuId) =>
                    {
                        string name = shell.GetNewFeatureName();
                        shell.AddFeature(name, featureI);
                        if (shell.Owner is Solid sld)
                        {
                            soa.SetSelectedObject(sld);
                            IPropertyEntry toEdit = soa.Frame.ControlCenter.FindItem(name);
                            if (toEdit != null)
                            {
                                List <IPropertyEntry> parents = new List <IPropertyEntry>();
                                if (toEdit != null)
                                {
                                    IPropertyEntry p = toEdit;
                                    while ((p = p.Parent as IPropertyEntry) != null)
                                    {
                                        parents.Add(p);
                                    }
                                    IPropertyPage propertyPage = parents[parents.Count - 1].Parent as IPropertyPage;
                                    if (propertyPage != null)
                                    {
                                        for (int k = parents.Count - 1; k >= 0; --k)
                                        {
                                            propertyPage.OpenSubEntries(parents[k], true);
                                        }
                                        toEdit.StartEdit(false);
                                    }
                                }
                            }
                        }

                        return(true);
                    };
                    nameFeature.OnSelected = (menuId, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.AddRange(featureI.ToArray());
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };

                    cm.Add(mh);
                }
            }
            for (int i = 0; i < curves.Count; i++)
            {   // No implementation for simple curves yet.
                // We would need a logical connection structure as in vertex->edge->face->shell in 2d curves, which we currently do not have.
                Face faceWithAxis = (curves[i] as IGeoObject).UserData.GetData("CADability.AxisOf") as Face;
                if (faceWithAxis != null)
                {
                    MenuWithHandler mh = new MenuWithHandler("MenuId.Axis");
                    mh.SubMenus = GetFacesSubmenus(faceWithAxis).ToArray();
                    mh.Target   = this;
                    IGeoObject curveI = curves[i] as IGeoObject;
                    mh.OnSelected = (m, selected) =>
                    {
                        currentMenuSelection.Clear();
                        currentMenuSelection.Add(curveI);
                        currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                    };
                    cm.Add(mh);
                }
            }
            for (int i = 0; i < edges.Count; i++)
            {
                MenuWithHandler mh = new MenuWithHandler();
                mh.ID   = "MenuId.Edge." + i.ToString();
                mh.Text = StringTable.GetString("MenuId.Edge", StringTable.Category.label);
                List <MenuWithHandler> lmh             = new List <MenuWithHandler>();
                MenuWithHandler        selectMoreEdges = new MenuWithHandler("MenuId.SelectMoreEdges");
                lmh.Add(selectMoreEdges);
                Edge edgeI = edges[i];
                selectMoreEdges.OnCommand = (menuId) =>
                {
                    soa.SetSelectedObject(edgeI.Curve3D as IGeoObject);
                    soa.OverwriteMode(PickMode.onlyEdges);
                    return(true);
                };
                lmh.AddRange(edges[i].GetContextMenu(soa.Frame));
                mh.SubMenus = lmh.ToArray();
                mh.Target   = this;
                cm.Add(mh);
            }
            for (int i = 0; i < faces.Count; i++)
            {
                MenuWithHandler mh = new MenuWithHandler();
                mh.ID   = "MenuId.Face." + i.ToString();
                mh.Text = StringTable.GetString("MenuId.Face", StringTable.Category.label);
                List <MenuWithHandler> lmh             = new List <MenuWithHandler>();
                MenuWithHandler        selectMoreFaces = new MenuWithHandler("MenuId.SelectMoreFaces");
                lmh.Add(selectMoreFaces);
                Face faceI = faces[i];
                selectMoreFaces.OnCommand = (menuId) =>
                {
                    soa.SetSelectedObject(faceI);
                    soa.OverwriteMode(PickMode.onlyFaces);
                    return(true);
                };
                lmh.AddRange(GetFacesSubmenus(faces[i]));
                if (owningShell != null)
                {
                    double thickness = owningShell.GetGauge(faces[i], out HashSet <Face> frontSide, out HashSet <Face> backSide);
                    if (thickness != double.MaxValue && frontSide.Count > 0)
                    {
                        MenuWithHandler gauge = new MenuWithHandler("MenuId.Gauge");
                        gauge.OnCommand = (menuId) =>
                        {
                            ParametricsOffset po = new ParametricsOffset(frontSide, backSide, soa.Frame, thickness);
                            soa.Frame.SetAction(po);
                            return(true);
                        };
                        gauge.OnSelected = (menuId, selected) =>
                        {
                            currentMenuSelection.Clear();
                            currentMenuSelection.AddRange(frontSide.ToArray());
                            currentMenuSelection.AddRange(backSide.ToArray());
                            currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                        };
                        lmh.Add(gauge);
                    }
                    int n = owningShell.GetFaceDistances(faces[i], out List <Face> distanceTo, out List <double> distance, out List <GeoPoint> pointsFrom, out List <GeoPoint> pointsTo);
                    for (int j = 0; j < n; j++)
                    {
                        if (backSide == null || !backSide.Contains(distanceTo[j])) // this is not already used as gauge
                        {
                            HashSet <Face>  capturedFaceI    = new HashSet <Face>(new Face[] { faces[i] });
                            HashSet <Face>  capturedDistTo   = new HashSet <Face>(new Face[] { distanceTo[j] });
                            double          capturedDistance = distance[j];
                            GeoPoint        capturedPoint1   = pointsFrom[j];
                            GeoPoint        capturedPoint2   = pointsTo[j];
                            GeoObjectList   feedbackArrow    = currentView.Projection.MakeArrow(pointsFrom[j], pointsTo[j], currentView.Projection.ProjectionPlane, Projection.ArrowMode.circleArrow);
                            MenuWithHandler faceDist         = new MenuWithHandler("MenuId.FaceDistance");
                            faceDist.OnCommand = (menuId) =>
                            {
                                ParametricsDistanceAction pd = new ParametricsDistanceAction(capturedFaceI, capturedDistTo, capturedPoint1, capturedPoint2, soa.Frame);
                                soa.Frame.SetAction(pd);
                                return(true);
                            };
                            faceDist.OnSelected = (menuId, selected) =>
                            {
                                currentMenuSelection.Clear();
                                currentMenuSelection.AddRange(capturedFaceI.ToArray());
                                currentMenuSelection.AddRange(capturedDistTo.ToArray());
                                currentMenuSelection.AddRange(feedbackArrow);
                                currentView.Invalidate(PaintBuffer.DrawingAspect.Select, currentView.DisplayRectangle);
                            };
                            lmh.Add(faceDist);
                        }
                    }
                }
                mh.SubMenus = lmh.ToArray();
                mh.Target   = this;
                cm.Add(mh);

                //cm.AddRange(GetFacesSubmenus(faces));
            }
            for (int i = 0; i < faces.Count; i++)
            {
                if (faces[i].Owner is Shell shell)
                {
                    Shell sh = shell.FeatureFromFace(faces[i]);
                }
            }
            for (int i = 0; i < edges.Count; i++)
            {
                if (edges[i].PrimaryFace.Owner is Shell shell)
                {
                    //shell.FeatureFromEdges(edges[i].PrimaryFace);
                }
            }
            for (int i = 0; i < shells.Count; i++)
            {
                MenuWithHandler mh = new MenuWithHandler();
                mh.ID       = "MenuId.Shell." + i.ToString();
                mh.Text     = StringTable.GetString("MenuId.Shell", StringTable.Category.label);
                mh.SubMenus = shells[i].GetContextMenu(soa.Frame);
                mh.Target   = this;
                cm.Add(mh);
            }
            for (int i = 0; i < solids.Count; i++)
            {
                MenuWithHandler mh = new MenuWithHandler();
                mh.ID       = "MenuId.Solid." + i.ToString();
                mh.Text     = StringTable.GetString("MenuId.Solid", StringTable.Category.label);
                mh.SubMenus = solids[i].GetContextMenu(soa.Frame);
                mh.Target   = this;
                cm.Add(mh);
            }
            {   // selection independent menu items
                MenuWithHandler mh = new MenuWithHandler();
                mh.ID   = "MenuId.Show";
                mh.Text = StringTable.GetString("MenuId.Show", StringTable.Category.label);
                MenuWithHandler mhShowHidden = new MenuWithHandler();
                mhShowHidden.ID        = "MenuId.ShowHidden";
                mhShowHidden.Text      = StringTable.GetString("MenuId.ShowHidden", StringTable.Category.label);
                mhShowHidden.OnCommand = (menuId) =>
                {
                    foreach (IGeoObject geoObject in vw.Model)
                    {
                        if (geoObject.Layer != null && geoObject.Layer.Name == "CADability.Hidden")
                        {
                            Layer layer = geoObject.UserData.GetData("CADability.OriginalLayer") as Layer;
                            if (layer != null)
                            {
                                geoObject.Layer = layer;
                            }
                        }
                    }
                    return(true);
                };
                MenuWithHandler mhShowAxis = new MenuWithHandler();
                mhShowAxis.ID        = "MenuId.ShowAxis";
                mhShowAxis.Text      = StringTable.GetString("MenuId.ShowAxis", StringTable.Category.label);
                mhShowAxis.OnCommand = (menuId) =>
                {
                    bool isOn = false;
                    foreach (IGeoObject geoObject in vw.Model)
                    {
                        Shell shell = null;
                        if (geoObject is Solid solid)
                        {
                            shell = solid.Shells[0];
                        }
                        else if (geoObject is Shell sh)
                        {
                            shell = sh;
                        }
                        if (shell != null && shell.FeatureAxis.Count > 0)
                        {
                            isOn = shell.FeatureAxis[0].IsVisible;
                            break;
                        }
                    }

                    using (vw.Canvas.Frame.Project.Undo.UndoFrame)
                    {
                        foreach (IGeoObject geoObject in vw.Model)
                        {
                            if (geoObject is Solid solid)
                            {
                                solid.ShowFeatureAxis = !isOn;
                            }
                            else if (geoObject is Shell shell)
                            {
                                shell.ShowFeatureAxis = !isOn;
                            }
                        }
                    }
                    return(true);
                };
                mhShowAxis.OnUpdateCommand = (menuId, commandState) =>
                {
                    bool isOn = false;
                    foreach (IGeoObject geoObject in vw.Model)
                    {
                        Shell shell = null;
                        if (geoObject is Solid solid)
                        {
                            shell = solid.Shells[0];
                        }
                        else if (geoObject is Shell sh)
                        {
                            shell = sh;
                        }
                        if (shell != null && shell.FeatureAxis.Count > 0)
                        {
                            isOn = shell.FeatureAxis[0].IsVisible;
                            break;
                        }
                    }
                    commandState.Checked = isOn;
                    return(true);
                };
                mh.SubMenus = new MenuWithHandler[] { mhShowHidden, mhShowAxis };
                mh.Target   = this;
                cm.Add(mh);
            }
            vw.SetPaintHandler(PaintBuffer.DrawingAspect.Select, new PaintView(OnRepaintSelect));
            mousePoint.X += vw.DisplayRectangle.Width / 8; // find a better place for the menu position, using the extent of the objects
            vw.Canvas.ShowContextMenu(cm.ToArray(), mousePoint, ContextMenuCollapsed);
        }