private void PasteSplashData(Transaction transaction, EditableView pnlView) { DataObject data = (DataObject)Clipboard.GetDataObject(); byte[] buffer = (byte[])data.GetData("Splash data", false); // will be nothing if deserialisation failed if (buffer == null) { Debug.Fail("Deserialisation failed"); return; } using (MemoryStream stream = new MemoryStream(buffer, false)) using (DataReader reader = new DataReader(stream, FileMarkers.ShapeList)) { List <Datum> list = reader.ReadDataList(); // cannot insert objects directly as that would mean pasting twice inserted 2 objects with same ID Mapping hashIDChanges = new Mapping(); DatumList newList = new DatumList(); RectangleF bounds = RectangleF.Empty; // work out existing bounds of list bool includesShapes = false; foreach (Datum datum in list) { if (datum is Shape) { // assumed that all shapes need to be created into page Shape create = (Shape)datum.Clone(hashIDChanges); transaction.Create(create); newList.Add(create); Geometry.Extend(ref bounds, create.MinimalBounds); // minimal makes, for example, the snapping of axes better includesShapes = true; } else if (datum is SharedBase resource) { // no need to clone these as they are effectively invariant if (CurrentDocument.FindExistingSharedResource <Datum>(resource.ID) == null) { transaction.Edit(CurrentDocument); CurrentDocument.AddSharedResource(resource); // if ID exists in this document it is assumed to be same object } } else if (datum is ButtonStyle buttonStyle) { // as Shape, but don't add to page // if IsShared it is not cloned. Means styles in multi docs will share ID, which is good as further pasting in will reuse existing style // does need to be added to document share ButtonStyle create; if (buttonStyle.IsShared) { create = buttonStyle; if (CurrentDocument.GetButtonStyle(create.ID) == null) { CurrentDocument.AddButtonStyle(create); // is new to this doc - either by cloning, or transaction.Create(create); } } else { create = (ButtonStyle)buttonStyle.Clone(hashIDChanges); transaction.Create(create); } newList.Add(create); } else { Debug.Fail("Datum not processed by by Paste: " + datum.TypeByte); } } // want to centre shapes within current area, rather than pasting at original position (which leaves them on top of each other and largely invisible) IShapeContainer container = CurrentPage; if (includesShapes) { if (CurrentPage.SelectedCount == 1 && CurrentPage.SelectedShapes.First().AsContainer != null) { container = CurrentPage.SelectedShapes.First().AsContainer; // but in SAW don't paste automatically into an empty container - chances are it's the item which was just copied! // instead paste into that container's parent if (!container.Any()) { container = (container as Shape).Container.AsParentContainer; } } PointF target = pnlView.ViewableArea().Centre(); // Or use cursor position if better PointF cursor = pnlView.CursorPositionLocal; // Automatically selects the drawing cursor if split cursors are used // or the container if (((Datum)container).TypeByte == (byte)Shape.Shapes.Container) { target = ((Shape)container).Bounds.Centre(); } else if (cursor.X > 0 && cursor.Y > 0 && cursor.X < pnlView.Width && cursor.Y < pnlView.Height) { cursor = pnlView.MouseToData(cursor.ToPoint()); // And also need to check the cursor is within the page. the above just checked it was within the control (there may be some dead area displayed) if (cursor.X < pnlView.ViewableArea().Width&& cursor.Y < pnlView.ViewableArea().Height) { target = cursor; } } target.X -= bounds.Width / 2; target.Y -= bounds.Height / 2; // where we want the top left to be if (pnlView.SnapMode == Shape.SnapModes.Grid) { target = CurrentPage.Paper.SnapPoint2(target); } var transform = new TransformMove(target.X - bounds.X, target.Y - bounds.Y); transaction.Edit((Datum)container); foreach (Shape shape in newList.Values.OfType <Shape>()) // of type needed in case other stuff was mixed in with it { container.Contents.Add(shape); shape.Parent = container; shape.ApplyTransformation(transform); } container.FinishedModifyingContents(transaction); } foreach (Datum shape in newList.Values) { try { // order changed shape.UpdateReferencesObjectsCreated(CurrentDocument, reader); shape.UpdateReferencesIDsChanged(hashIDChanges, Globals.Root.CurrentDocument); } catch (Exception ex) { Utilities.LogSubError(ex); } } List <Shape> newShapes = newList.Values.OfType <Shape>().ToList(); if (newShapes.Count > 0 && container == CurrentPage) // don't select the shapes when pasting into a container - leaves the container selected, which allows for more pastes { CurrentPage.SelectOnly(newShapes); } pnlView.InvalidateData(CurrentPage.SelectedRefreshBoundary(), StaticView.InvalidationBuffer.All); } }