/// <summary> /// Create a callback function that creates and configures a block, /// then places the new block next to the original. /// </summary> /// <param name="block">block Original block.</param> /// <param name="xml">XML representation of new block.</param> /// <returns>Function that creates a block.</returns> public static Action <goog.events.Event> callbackFactory(Block block, Element xml) { return(new Action <goog.events.Event>((e) => { Events.disable(); BlockSvg newBlock; try { newBlock = (BlockSvg)Xml.domToBlock(xml, block.workspace); // Move the new block next to the old block. var xy = block.getRelativeToSurfaceXY(); if (block.RTL) { xy.x -= Core.SNAP_RADIUS; } else { xy.x += Core.SNAP_RADIUS; } xy.y += Core.SNAP_RADIUS * 2; newBlock.moveBy(xy.x, xy.y); } finally { Events.enable(); } if (Events.isEnabled() && !newBlock.isShadow()) { Events.fire(new Events.Create(newBlock)); } newBlock.select(); })); }
/// <summary> /// Decode an XML DOM and create blocks on the workspace. /// </summary> /// <param name="xml">XML DOM.</param> /// <param name="workspace">The workspace.</param> public static void domToWorkspace(Element xml, Workspace workspace) { var width = 0.0; // Not used in LTR. if (workspace.RTL) { width = workspace.getWidth(); } Field.startCache(); // Safari 7.1.3 is known to provide node lists with extra references to // children beyond the lists" length. Trust the length, do not use the // looping pattern of checking the index for an object. var childCount = xml.ChildNodes.Length; var existingGroup = Events.getGroup(); if (existingGroup == null) { Events.setGroup(true); } ((WorkspaceSvg)workspace).setResizesEnabled(false); for (var i = 0; i < childCount; i++) { var xmlChild_ = xml.ChildNodes[i]; var name = xmlChild_.NodeName.ToLowerCase(); if (name == "block" || (name == "shadow" && !Events.recordUndo)) { var xmlChild = (Element)xmlChild_; // Allow top-level shadow blocks if recordUndo is disabled since // that means an undo is in progress. Such a block is expected // to be moved to a nested destination in the next operation. var block = Xml.domToBlock(xmlChild, workspace); var blockX = Double.TryParse(xmlChild.GetAttribute("x"), out var x) ? x : Double.NaN; var blockY = Double.TryParse(xmlChild.GetAttribute("y"), out var y) ? y : Double.NaN; if (!Double.IsNaN(blockX) && !Double.IsNaN(blockY)) { block.moveBy(workspace.RTL ? width - blockX : blockX, blockY); } } else if (name == "shadow") { goog.asserts.fail("Shadow block cannot be a top-level block."); } } if (existingGroup == null) { Events.setGroup(false); } Field.stopCache(); workspace.updateVariableList(false); ((WorkspaceSvg)workspace).setResizesEnabled(false); }
/// <summary> /// Respawn the shadow block if there was one connected to the this connection. /// </summary> protected virtual void respawnShadow_() { var parentBlock = this.getSourceBlock(); var shadow = this.getShadowDom(); if (parentBlock.workspace != null && shadow != null && Events.recordUndo) { var blockShadow = Xml.domToBlock(shadow, parentBlock.workspace); if (blockShadow.outputConnection != null) { this.connect(blockShadow.outputConnection); } else if (blockShadow.previousConnection != null) { this.connect(blockShadow.previousConnection); } else { throw new Exception("Child block does not have output or previous statement."); } } }