diff --git a/blocks/procedures.js b/blocks/procedures.js index f6b25bcd6..d43130141 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -59,16 +59,6 @@ Blockly.Blocks['procedures_defnoreturn'] = { this.setStatements_(true); this.statementConnection_ = null; }, - /** - * Initialization of the block has completed, clean up anything that may be - * inconsistent as a result of the XML loading. - * @this Blockly.Block - */ - validate: function() { - var name = Blockly.Procedures.findLegalName( - this.getFieldValue('NAME'), this); - this.setFieldValue(name, 'NAME'); - }, /** * Add or remove the statement block from this function definition. * @param {boolean} hasStatements True if a statement block is needed. @@ -244,16 +234,6 @@ Blockly.Blocks['procedures_defnoreturn'] = { } } }, - /** - * Dispose of any callers. - * @this Blockly.Block - */ - dispose: function() { - var name = this.getFieldValue('NAME'); - Blockly.Procedures.disposeCallers(name, this.workspace); - // Call parent's destructor. - this.constructor.prototype.dispose.apply(this, arguments); - }, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: @@ -371,13 +351,11 @@ Blockly.Blocks['procedures_defreturn'] = { this.statementConnection_ = null; }, setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_, - validate: Blockly.Blocks['procedures_defnoreturn'].validate, updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_, mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom, domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation, decompose: Blockly.Blocks['procedures_defnoreturn'].decompose, compose: Blockly.Blocks['procedures_defnoreturn'].compose, - dispose: Blockly.Blocks['procedures_defnoreturn'].dispose, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: @@ -668,6 +646,72 @@ Blockly.Blocks['procedures_callnoreturn'] = { } } }, + /** + * Procedure calls cannot exist without the corresponding procedure + * definition. Enforce this link whenever an event is fired. + * @this Blockly.Block + */ + onchange: function(event) { + if (!this.workspace || this.workspace.isFlyout) { + // Block is deleted or is in a flyout. + return; + } + if (event.type == Blockly.Events.CREATE && + event.ids.indexOf(this.id) != -1) { + // Look for the case where a procedure call was created (usually through + // paste) and there is no matching definition. In this case, create + // an empty definition block with the correct signature. + var name = this.getProcedureCall(); + var def = Blockly.Procedures.getDefinition(name, this.workspace); + if (def && (def.type != this.defType_ || + JSON.stringify(def.arguments_) != JSON.stringify(this.arguments_))) { + // The signatures don't match. + def = null; + } + if (!def) { + Blockly.Events.setGroup(event.group); + /** + * Create matching definition block. + * + * + * + * + * + * test + * + * + */ + var xml = goog.dom.createDom('xml'); + var block = goog.dom.createDom('block'); + block.setAttribute('type', this.defType_); + var xy = this.getRelativeToSurfaceXY(); + var x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); + var y = xy.y + Blockly.SNAP_RADIUS * 2; + block.setAttribute('x', x); + block.setAttribute('y', y); + var mutation = this.mutationToDom(); + block.appendChild(mutation); + var field = goog.dom.createDom('field'); + field.setAttribute('name', 'NAME'); + field.appendChild(document.createTextNode(this.getProcedureCall())); + block.appendChild(field); + xml.appendChild(block); + Blockly.Xml.domToWorkspace(xml, this.workspace); + Blockly.Events.setGroup(false); + } + } else if (event.type == Blockly.Events.DELETE) { + // Look for the case where a procedure definition has been deleted, + // leaving this block (a procedure call) orphaned. In this case, delete + // the orphan. + var name = this.getProcedureCall(); + var def = Blockly.Procedures.getDefinition(name, this.workspace); + if (!def) { + Blockly.Events.setGroup(event.group); + this.dispose(true, false); + Blockly.Events.setGroup(false); + } + } + }, /** * Add menu option to find the definition block for this call. * @param {!Array} options List of menu options to add to. @@ -683,7 +727,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { def && def.select(); }; options.push(option); - } + }, + defType_: 'procedures_defnoreturn' }; Blockly.Blocks['procedures_callreturn'] = { @@ -710,7 +755,10 @@ Blockly.Blocks['procedures_callreturn'] = { mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom, domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation, renameVar: Blockly.Blocks['procedures_callnoreturn'].renameVar, - customContextMenu: Blockly.Blocks['procedures_callnoreturn'].customContextMenu + onchange: Blockly.Blocks['procedures_callnoreturn'].onchange, + customContextMenu: + Blockly.Blocks['procedures_callnoreturn'].customContextMenu, + defType_: 'procedures_defreturn' }; Blockly.Blocks['procedures_ifreturn'] = { diff --git a/core/blockly.js b/core/blockly.js index cda45992e..029f83d2e 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -277,7 +277,9 @@ Blockly.onKeyDown_ = function(e) { if (e.keyCode == 86) { // 'v' for paste. if (Blockly.clipboardXml_) { + Blockly.Events.setGroup(true); Blockly.clipboardSource_.paste(Blockly.clipboardXml_); + Blockly.Events.setGroup(false); } } else if (e.keyCode == 90) { // 'z' for undo 'Z' is for redo. diff --git a/core/procedures.js b/core/procedures.js index 34419fb54..4bef0cf31 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -234,19 +234,6 @@ Blockly.Procedures.getCallers = function(name, workspace) { return callers; }; -/** - * When a procedure definition is disposed of, find and dispose of all its - * callers. - * @param {string} name Name of deleted procedure definition. - * @param {!Blockly.Workspace} workspace The workspace to delete callers from. - */ -Blockly.Procedures.disposeCallers = function(name, workspace) { - var callers = Blockly.Procedures.getCallers(name, workspace); - for (var i = 0; i < callers.length; i++) { - callers[i].dispose(true, false); - } -}; - /** * When a procedure definition changes its parameters, find and edit all its * callers. @@ -282,7 +269,8 @@ Blockly.Procedures.mutateCallers = function(defBlock) { * @return {Blockly.Block} The procedure definition block, or null not found. */ Blockly.Procedures.getDefinition = function(name, workspace) { - var blocks = workspace.getAllBlocks(); + // Assume that a procedure definition is a top block. + var blocks = workspace.getTopBlocks(false); for (var i = 0; i < blocks.length; i++) { if (blocks[i].getProcedureDef) { var tuple = blocks[i].getProcedureDef(); diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 07e43a22d..7d137f9b8 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -740,7 +740,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) { * containing the blocks on the workspace. */ Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() { - var topBlocks = this.getTopBlocks(); + var topBlocks = this.getTopBlocks(false); // There are no blocks, return empty rectangle. if (!topBlocks.length) { return {x: 0, y: 0, width: 0, height: 0}; diff --git a/core/xml.js b/core/xml.js index 70fd915a5..efdd45811 100644 --- a/core/xml.js +++ b/core/xml.js @@ -532,10 +532,6 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } block.setShadow(true); } - // Give the block a chance to clean up any initial inputs. - if (block.validate) { - block.validate(); - } return block; };