コード例 #1
0
ファイル: ObjectEdits.cs プロジェクト: stuart2w/SAW
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            bool changed = false;

            foreach (Shape shape in CurrentPage.SelectedShapes)
            {
                transaction.Edit(shape);                 // must add to Tx before Tidy to get the previous state.  Only after Tidy do we know if it actually changed
                if (shape.Tidy(Mode, CurrentPage))
                {
                    changed = true;
                }
                else
                {
                    transaction.Disregard(shape);
                }
            }
            if (changed)
            {
                pnlView.ForceUpdateGrabSpots();
            }
            // main invalidation done automatically by tx
        }
コード例 #2
0
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            Item item = SelectedItem;

            if (item == null)
            {
                return;
            }
            // write the list of shapes into a byte buffer; this way we control the serialisation
            DataObject data = new DataObject();

            using (MemoryStream buffer = new MemoryStream(1000))
                using (DataWriter writer = new DataWriter(buffer, (FileMarkers)Shape.Shapes.SAWItem))
                {
                    writer.Write(item);
                    data.SetData("Splash presentation", false, buffer.GetBuffer());
                }
            // No text is stored in this case

            Clipboard.SetDataObject(data);
            Debug.WriteLine("Copied presentation with image: " + item.Image.ID);
        }
コード例 #3
0
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            string filename = FileDialog.ShowSave(FileDialog.Context.Document);

            if (string.IsNullOrEmpty(filename))
            {
                return;
            }
            List <Page> list = new List <Page> {
                CurrentPage
            };
            Document  document   = new Document(list);          // constructs the document containing this page
            DatumList referenced = new DatumList();

            CurrentPage.AddRequiredReferencesRecurseToContained(referenced.Add, Mapping.Ignore);
            foreach (Datum datum in referenced.Values)
            {
                switch (datum.TypeByteAsFileMarker)
                {
                case FileMarkers.Paper:
                    break;

                case FileMarkers.SharedBitmap:
                    document.AddSharedResource((SharedImage)datum);
                    break;

                default:
                    Debug.Fail("Unexpected external reference from page");
                    break;
                }
            }
            using (DataWriter writer = new DataWriter(filename, FileMarkers.Splash))
            {
                writer.Write(document);
            }

            // We don't dispose the document because that would dispose the pages; we can just let the garbage collector cleanup the document
        }
コード例 #4
0
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            string filename = FileDialog.ShowSave(FileDialog.Context.Image, "*.svg|*.svg");

            if (string.IsNullOrEmpty(filename))
            {
                return;
            }
            float scale = GUIUtilities.SystemDPI / Geometry.INCH;             // convert from mm to nominal pixels (needed to get fonts the right size)

            using (SVGCanvas canvas = new SVGCanvas(CurrentPage.Size))
            {
                //canvas.TranslateTransform(0, m_objPage.Size.Height) - not needed because the SVG viewbox is created with a negative starting y-coordinate
                CurrentPage.DrawBackground(canvas, scale, true, true, false);                 // Not sure if the origin should be drawn or not
                CurrentPage.DrawShapes(canvas, scale, scale, pnlView);
                // no attempt to do pixels - should not be called if pixel data present
                var str = canvas.ToString();
                File.WriteAllText(filename, str);
#if DEBUG
                File.WriteAllText(filename.Replace(".svg", ".html"), "<html><head></head><body>" + str + "</body></html>");
#endif
            }
        }
コード例 #5
0
ファイル: Drawing.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     SetMouseStep(Step);
 }
コード例 #6
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     CurrentPage.DeleteSelected(transaction);
 }
コード例 #7
0
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            try
            {
                DataObject data = (DataObject)Clipboard.GetDataObject();
                if (data.GetDataPresent("Splash data", false))
                {
                    PasteSplashData(transaction, pnlView);
                }
#if SAW && !SPLASH
                else if (data.GetDataPresent("Splash scripts", false))
                {
                    CopyScripts.Paste(transaction);
                }
                else if (data.GetDataPresent("Splash presentation", false))
                {
                    CopyPresentation.Paste(transaction, pnlView);
                }
#endif
                else if (data.ContainsImage())
                {
                    Image        image      = data.GetImage();
                    MemoryStream strmMemory = new MemoryStream(10000);
                    image.Save(strmMemory, ImageFormat.Png);
                    image.Dispose();
                    ImportedImage objImported = new ImportedImage(strmMemory, CurrentDocument, transaction);                     //= ImportedImage.CreateForPaste(strmMemory)
                    transaction.Create(objImported);
                    objImported.PlaceAt(pnlView.ViewableArea().Centre());
                    //  strmMemory.Dispose()
                    CurrentPage.AddNew(objImported, transaction);
                }
                else if (ContainsFileImage(data))
                {
                    ImportedImage imported = new ImportedImage(data.GetFileDropList()[0], CurrentDocument, transaction);
                    transaction.Create(imported);
                    imported.PlaceAt(pnlView.ViewableArea().Centre());
                    CurrentPage.AddNew(imported, transaction);
                    CurrentPage.SelectOnly(imported);
                }
                else if (data.ContainsText())
                {
                    string text = data.GetText();
                    if (!string.IsNullOrEmpty(text))
                    {
                        // try and apply it to a shape - only if there is one and only one selected
                        Shape shape = pnlView.TypingShape();
                        if (shape != null && shape.SupportsTextLabel)                         // Second condition is needed because some "typing" shapes only actually do custom processing of certain keys
                        {
                            transaction.Edit(shape);
                            shape.TextType(text);
                        }
                        else if (CurrentPage.SelectedCount == 1)
                        {
                            shape = CurrentPage.SelectedShapes[0];
                            if (shape.SupportsTextLabel)
                            {
                                // When pasting in without the cursor, the entire current text is replaced, if any
                                transaction.Edit(shape);
                                if (!shape.HasText(true))
                                {
                                    shape.CreateLabel((Shape.TextStyleC)Editor.StyleParameterDefaultObject(Parameters.FontFace));
                                }
                                // StyleParameterDefaultObject returns the object for that param - which will be all the text styles
                                shape.LabelText = text.Replace("\r\n", "\r");
                            }
                        }
                    }
                }
            }
            catch (Exception ex) when(!Globals.Root.IsDebug)
            {
                Utilities.LogSubError(ex);
                transaction.Cancel();                 // must be before message box
                MessageBox.Show(Strings.Item("Paste_Failed"));
            }
        }
コード例 #8
0
ファイル: Verb.cs プロジェクト: stuart2w/SAW
 {    // it's OK to construct these - they are added as SAW/Splash stubs in places.  But they shouldn't be triggered
     public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
     {
         Utilities.LogSubError("Triggering null verb");
     }
コード例 #9
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Editor.TriggerPalette(Purpose, true);
 }
コード例 #10
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Editor.UserChangeSnapMode(Mode);
 }
コード例 #11
0
ファイル: Selection.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     pnlView.IterateSelectionTo(Globals.Root.CurrentPage.NextSelection(-1));
 }
コード例 #12
0
ファイル: Selection.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Globals.Root.CurrentPage.SelectAll();
 }
コード例 #13
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Globals.Root.User = Users.Editor;
 }
コード例 #14
0
ファイル: ObjectEdits.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Globals.Root.CurrentPage.SelectedShapes[0].DoDoubleClick(pnlView, EditableView.ClickPosition.Sources.VerbButton);
 }
コード例 #15
0
ファイル: ObjectEdits.cs プロジェクト: stuart2w/SAW
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            if (CurrentPage == null || CurrentPage.SelectedCount < 2)
            {
                return;
            }
            bool       resize = Code == Codes.EqualiseHeight || Code == Codes.EqualiseWidth || Code == Codes.SmallestWidth || Code == Codes.SmallestHeight || Code == Codes.LargestWidth || Code == Codes.LargestHeight;
            RectangleF bounds = RectangleF.Empty; // not using Page.SelectedBounds as I think it might be better to use the minimal bounds
            SizeF      size   = SizeF.Empty;      // total of all shape sizes added up.  Used to equalise width/height.  If going to smallest/largest this is overwritten later

            foreach (Shape shape in CurrentPage.SelectedShapes)
            {
                RectangleF rect = shape.MinimalBounds;
                Geometry.Extend(ref bounds, rect);
                if (resize && (shape.Allows & (Shape.AllowedActions.TransformScale | Shape.AllowedActions.TransformLinearStretch)) == 0)
                {
                    MessageBox.Show(Strings.Item("Cannot_Resize"));
                    return;
                }
                if (Code == Codes.EqualiseWidth && rect.Width < Geometry.NEGLIGIBLE || Code == Codes.EqualiseHeight && rect.Height < Geometry.NEGLIGIBLE)
                {
                    // can't stretch a shape which is currently zero dimension in the direction we are changing; this would require infinite scale
                    MessageBox.Show(Strings.Item("Cannot_Resize_Zero"));
                    return;
                }
                size += rect.Size;
                Debug.Assert((shape.Allows & Shape.AllowedActions.TransformMove) > 0);
            }
            RectangleF   refreshBoundary = CurrentPage.SelectedRefreshBoundary();
            List <Shape> shapes          = new List <Shape>(CurrentPage.SelectedShapes); // can't use the actual m_Page.SelectedShapes because we might need to rearrange the list
            float        spread          = 0;                                            // current coordinates for spread horizontal and vertical

            switch (Code)
            {
            case Codes.SpreadHorizontal:
                shapes.Sort(Shape.XSort);
                spread = bounds.Left;
                break;

            case Codes.SpreadVertical:
                shapes.Sort(Shape.YSort);
                spread = bounds.Top;
                break;

            case Codes.SmallestWidth:
                size.Width = (from Shape shape in CurrentPage.SelectedShapes select shape.MinimalBounds.Width).Min();
                break;

            case Codes.LargestWidth:
                size.Width = (from Shape shape in CurrentPage.SelectedShapes select shape.MinimalBounds.Width).Max();
                break;

            case Codes.SmallestHeight:
                size.Height = (from Shape shape in CurrentPage.SelectedShapes select shape.MinimalBounds.Height).Min();
                break;

            case Codes.LargestHeight:
                size.Height = (from Shape shape in CurrentPage.SelectedShapes select shape.MinimalBounds.Height).Max();
                break;
            }
            if (resize && (size.Width == 0 || size.Height == 0))             // Note we cannot use size.IsEmpty because that is only true if both are zero
            {
                MessageBox.Show(Strings.Item("Cannot_Resize_Zero"));
                return;
            }
            for (int index = 0; index <= CurrentPage.SelectedCount - 1; index++)
            {
                Shape shape = shapes[index];
                transaction.Edit(shape);
                // several of the actions below need the middle of the current shape
                RectangleF minimalBounds = shape.MinimalBounds;
                PointF     middle        = new PointF((minimalBounds.Left + minimalBounds.Right) / 2, (minimalBounds.Top + minimalBounds.Bottom) / 2);
                switch (Code)
                {
                case Codes.AlignLeft:
                    shape.ApplyTransformation(new TransformMove(bounds.Left - shape.MinimalBounds.Left, 0));
                    break;

                case Codes.AlignRight:
                    shape.ApplyTransformation(new TransformMove(bounds.Right - shape.MinimalBounds.Right, 0));
                    break;

                case Codes.AlignCentre:
                    shape.ApplyTransformation(new TransformMove((bounds.Left + bounds.Right) / 2 - middle.X, 0));
                    break;

                case Codes.AlignTop:
                    shape.ApplyTransformation(new TransformMove(0, bounds.Top - shape.MinimalBounds.Top));
                    break;

                case Codes.AlignBottom:
                    shape.ApplyTransformation(new TransformMove(0, bounds.Bottom - shape.MinimalBounds.Bottom));
                    break;

                case Codes.AlignMiddle:
                    shape.ApplyTransformation(new TransformMove(0, (bounds.Top + bounds.Bottom) / 2 - middle.Y));
                    break;

                case Codes.SmallestWidth:
                case Codes.LargestWidth:                         // these can be implemented in one because the required dimension is already set in size
                case Codes.EqualiseWidth:                        // Where as this will need a different scale calculation, but the rest is the same
                    float widthScale = size.Width / shape.MinimalBounds.Width;
                    if (Code == Codes.EqualiseWidth)
                    {
                        widthScale = size.Width / CurrentPage.SelectedCount / shape.MinimalBounds.Width;
                    }
                    if ((shape.Allows & Shape.AllowedActions.TransformLinearStretch) > 0)
                    {
                        shape.ApplyTransformation(new TransformLinearScale(middle, widthScale, true));
                    }
                    else
                    {
                        shape.ApplyTransformation(new TransformScale(middle, widthScale, true));                                 // changed to use centres v8
                    }
                    break;

                case Codes.SmallestHeight:
                case Codes.LargestHeight:
                case Codes.EqualiseHeight:
                    float heightScale = size.Height / shape.MinimalBounds.Height;
                    if (Code == Codes.EqualiseHeight)
                    {
                        heightScale = size.Height / CurrentPage.SelectedCount / shape.MinimalBounds.Height;
                    }
                    if ((shape.Allows & Shape.AllowedActions.TransformLinearStretch) > 0)
                    {
                        shape.ApplyTransformation(new TransformLinearScale(middle, heightScale, false));
                    }
                    else
                    {
                        shape.ApplyTransformation(new TransformScale(middle, heightScale, true));                                // changed to use centres v8
                    }
                    break;

                case Codes.SpreadHorizontal:
                    shape.ApplyTransformation(new TransformMove(spread - minimalBounds.Left, 0));
                    spread += minimalBounds.Width + (bounds.Width - size.Width) / (shapes.Count - 1);                             // doesn't matter if top half of fraction is negative
                    break;

                case Codes.SpreadVertical:
                    shape.ApplyTransformation(new TransformMove(0, spread - minimalBounds.Top));
                    spread += minimalBounds.Height + (bounds.Height - size.Height) / (shapes.Count - 1);
                    break;
                }
                shape.Status = Shape.StatusValues.Moved;
                Geometry.Extend(ref refreshBoundary, shape.RefreshBounds());
            }
            pnlView.InvalidateData(refreshBoundary, StaticView.InvalidationBuffer.All);
        }
コード例 #16
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {        // not an error condition?
 }
コード例 #17
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Editor.ChangeTool(Tool);
 }
コード例 #18
0
ファイル: IRM.cs プロジェクト: stuart2w/SAW
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            if (!g_WarningDone)
            {
                MessageBox.Show(Strings.Item("SAW_IRM_SaveNote"));
            }
            g_WarningDone = true;

            string file = FileDialog.ShowSave(FileDialog.Context.OtherUserDoc, ImportIRM.Filter);

            if (string.IsNullOrEmpty(file))
            {
                return;
            }

            using (var operation = new Globals.Operation(this.DescriptionWithoutAccelerator()))
            {
                Document          doc         = Globals.Root.CurrentDocument;
                StringBuilder     output      = new StringBuilder();
                int               mainHeight  = 0;
                List <Scriptable> pageButtons =
                    (from shape in doc.Page(0).Shapes
                     where shape is Scriptable s && s.SelectScript.CommandList.Any(c => c is CmdGotoPage)
                     orderby PageButtonDestination(shape as Scriptable)
                     select shape as Scriptable).ToList();
                for (int pageIndex = 0; pageIndex < doc.Pages.Count(); pageIndex++)
                {
                    Page page = doc.Page(pageIndex);
                    // buttons used to change pages:
                    if (pageIndex == 0)
                    {
                        m_YOffset  = (int)page.Size.Height;
                        mainHeight = WritePageButtons(output, pageButtons, page);
                    }
                    m_YOffset = (int)page.Size.Height - mainHeight;

                    string name = pageButtons.Count() > pageIndex ? pageButtons[pageIndex].Element.LabelText : "page" + (pageIndex + 1);
                    output.Append('[').Append(name).AppendLine("]");
                    foreach (Shape s in page.Shapes)
                    {
                        if (s.ShapeCode == Shape.Shapes.Scriptable)
                        {
                            Scriptable scriptable = (Scriptable)s;
                            if (scriptable.SelectScript.CommandList.Any(c => c is CmdGotoPage))
                            {
                                continue;                                 // these are the copies of the page buttons
                            }
                            WriteElement(output, "POS", s.Bounds.Location);
                            WriteElement(output, "SIZE", s.Bounds.Size.ToPointF());
                            WriteElement(output, "TEXT", scriptable.Element.LabelText);
                            string remote  = ExtractCommentValue(scriptable.SelectScript, "REMOTE");
                            string command = ExtractCommentValue(scriptable.SelectScript, "COMMAND");
                            WriteElement(output, "REMOTE", remote ?? "?");
                            WriteElement(output, "COMMAND", command ?? "?", true);
                        }
                        else if (s.ShapeCode == Shape.Shapes.TextLine)
                        {
                            WriteElement(output, "LBL", s.Bounds.Location);
                            WriteElement(output, "SIZE", s.Bounds.Size.ToPointF());
                            WriteElement(output, "TEXT", s.LabelText, true);
                        }
                        // all other types ignored.  In particular separator line which was sort of generated automatically from button bounds
                    }
                    output.AppendLine("[END]");
                    output.AppendLine();
                }
                if (operation.ConfirmSuccess())
                {
                    File.WriteAllText(file, output.ToString());
                }
            }
        }
コード例 #19
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Editor.ChangeGridVisible(Value == 1, true);             // nominally value 1 is on, 0 off, although function will actually ignore the value
 }
コード例 #20
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     pnlView.ChangeZoom(Multiplier != 0 ? pnlView.Zoom * Multiplier : Value);
     Globals.NotifyVerbApplicabilityChanged();
 }
コード例 #21
0
 public abstract void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction);
コード例 #22
0
 public void PerformAction(Functions.Action action, EditableView.ClickPosition.Sources source = EditableView.ClickPosition.Sources.Irrelevant)
 {
     Editor.PerformAction(action, source);
 }
コード例 #23
0
ファイル: Verb.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     m_Function.Invoke(source, pnlView, transaction);
 }
コード例 #24
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     frmGridWizard.Display((CurrentPage.SelectedShapes.FirstOrDefault() as IShapeContainer) ?? CurrentPage, CurrentDocument);
 }
コード例 #25
0
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            if (CurrentPage == null || CurrentPage.SelectedCount == 0)
            {
                return;
            }
            // the items to put on the clipboard
            //  even if cutting, we put a different copy of the objects on the clipboard with different IDs (in case the user undoes which would restore the original objects)
            DatumList hash = Datum.CloneList(CurrentPage.SelectedShapes, Globals.Root.CurrentDocument);             // copies shapes, changes IDs, updates links and includes references as needed

            // write the list of shapes into a byte buffer; this way we control the serialisation
            DataObject data = new DataObject();

            using (MemoryStream buffer = new MemoryStream(1000))
            {
                using (DataWriter writer = new DataWriter(buffer, FileMarkers.ShapeList))
                {
                    List <Datum> list = new List <Datum>();
                    list.AddRange(hash.Values);
                    writer.WriteDatumList(list);
                    data.SetData("Splash data", false, buffer.GetBuffer());
                }
            }

            Bitmap bitmap = (Bitmap)pnlView.CreateImage(false, false, true);

            // will have white background - transparent gets lost somewhere within clipboard
            data.SetImage(bitmap);
            //objBitmap.Dispose()' doesn't work if this done

            // http://stackoverflow.com/questions/35045500/copy-svg-image-stored-as-xml-string-to-windows-clipboard-as-image-svgxml-mime-t
            try
            {
                var          strSVG    = pnlView.CreateSVG(false, true);     // True)
                byte[]       aBytes    = Encoding.UTF8.GetBytes(strSVG);
                MemoryStream objStream = new MemoryStream(aBytes);
                data.SetData("image/svg+xml", objStream);
                //System.IO.File.WriteAllText("g:\temp\svg.svg", strSVG)
            }
            catch (UserException u)
            {
                Globals.Root.Log.WriteLine("Cannot copy as SVG: " + u.Message);
            }
            catch (Exception ex)
            {
                Utilities.LogSubError(ex);
            }

            //check for text
            string text = "";

            foreach (Shape shape in CurrentPage.SelectedShapes)
            {
                if (shape.HasText(true) && !String.IsNullOrEmpty(shape.LabelText))
                {
                    text = shape.LabelText;
                    break;                     // stop once valid text found
                }
            }
            if (!string.IsNullOrEmpty(text))
            {
                data.SetText(text);
            }

            // store result on clipboard
            Clipboard.SetDataObject(data);

            if (IsCut)
            {
                CurrentPage.DeleteSelected(transaction);
            }
        }
コード例 #26
0
 public static void PerformAction(Action action, EditableView.ClickPosition.Sources source)
 {
     Editor?.PerformAction(action, source);
 }
コード例 #27
0
ファイル: Drawing.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     pnlView.TriggerVerb(Code, source);
 }
コード例 #28
0
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     Editor.SimulateKey(Key);
 }
コード例 #29
0
ファイル: Pages.cs プロジェクト: stuart2w/SAW
 public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
 {
     pnlView.StartCustomShape(new SAW.SetOrigin());
 }
コード例 #30
0
ファイル: ObjectEdits.cs プロジェクト: stuart2w/SAW
        public override void Trigger(EditableView.ClickPosition.Sources source, EditableView pnlView, Transaction transaction)
        {
            if (CurrentPage == null || CurrentPage.SelectedCount < 1)
            {
                return;
            }
            bool reflect = false;
            bool rotate  = false;

            switch (Code)
            {
            case Codes.FlipHorizontal:
            case Codes.FlipVertical:
                reflect = true;
                break;

            case Codes.RotateLeft:
            case Codes.RotateRight:
                rotate = true;
                break;

            default:
                Utilities.LogSubError("Unexpected verb in DoQuickTransform");
                return;
            }
            RectangleF bounds    = RectangleF.Empty;          // not using Page.SelectedBounds as I think it might be better to use the minimal bounds
            SizeF      totalSize = SizeF.Empty;

            foreach (Shape shape in CurrentPage.SelectedShapes)
            {
                RectangleF shapeBounds = shape.MinimalBounds;
                Geometry.Extend(ref bounds, shapeBounds);
                if (reflect && (shape.Allows & (Shape.AllowedActions.TransformMirror | Shape.AllowedActions.MirrorFlipOnly)) == 0)
                {
                    MessageBox.Show(Strings.Item("Cannot_Reflect"));
                    return;
                }
                if (rotate && (shape.Allows & Shape.AllowedActions.TransformRotate) == 0)
                {
                    MessageBox.Show(Strings.Item("Cannot_Rotate"));
                    return;
                }
                totalSize += shapeBounds.Size;
            }
            Transformation transformation;
            PointF         middle = new PointF((bounds.Left + bounds.Right) / 2, (bounds.Top + bounds.Bottom) / 2);

            switch (Code)
            {
            case Codes.FlipHorizontal:
                transformation = new TransformMirror(middle, middle + new SizeF(0, 100));
                break;

            // second point is arbitrary as long as it is on the necessary vertical line
            case Codes.FlipVertical:
                transformation = new TransformMirror(middle, middle + new SizeF(100, 0));
                break;

            case Codes.RotateLeft:
                transformation = new TransformRotate(middle, -Geometry.ANGLE90);
                break;

            case Codes.RotateRight:
                transformation = new TransformRotate(middle, Geometry.ANGLE90);
                break;

            default:
                Utilities.LogSubError("Unexpected verb in DoQuickTransform");
                return;
            }
            DoTransformForVerb(transformation, transaction);
        }