`.
- this.$el.addClass('selected');
+ // The root element of the selection switches `position` to `static` when `selected`. This
+ // is neccessary in order for the `selection-box` coordinates to be relative to the
+ // `paper` element, not the `selection` `
`.
+ this.$el.addClass('selected');
- } else {
+ } else {
- // Hide the selection box if there was no element found in the area covered by the
- // selection box.
- this.$el.hide();
- }
+ // Hide the selection box if there was no element found in the area covered by the
+ // selection box.
+ this.$el.hide();
+ }
- this.model.reset(_.pluck(elementViews, 'model'));
- break;
+ this.model.reset(_.pluck(elementViews, 'model'));
+ break;
- case 'translating':
+ case 'translating':
- this.options.graph.trigger('batch:stop');
- // Everything else is done during the translation.
- break;
+ this.options.graph.trigger('batch:stop');
+ // Everything else is done during the translation.
+ break;
- case 'cherry-picking':
- // noop; All is done in the `createSelectionBox()` function.
- // This is here to avoid removing selection boxes as a reaction on mouseup event and
- // propagating to the `default` branch in this switch.
- break;
+ case 'cherry-picking':
+ // noop; All is done in the `createSelectionBox()` function.
+ // This is here to avoid removing selection boxes as a reaction on mouseup event and
+ // propagating to the `default` branch in this switch.
+ break;
- default:
- // Hide selection if the user clicked somehwere else in the document.
- // this.$el.hide().empty();
- // this.model.reset([]);
- break;
- }
+ default:
+ // Hide selection if the user clicked somehwere else in the document.
+ // this.$el.hide().empty();
+ // this.model.reset([]);
+ break;
+ }
- delete this._action;
- },
+ delete this._action;
+ },
- cancelSelection: function() {
+ cancelSelection: function() {
- this.$('.selection-box').remove();
- this.$el.hide().removeClass('selected');
- this.model.reset([]);
- },
+ this.$('.selection-box').remove();
+ this.$el.hide().removeClass('selected');
+ this.model.reset([]);
+ },
- destroySelectionBox: function(elementView) {
+ destroySelectionBox: function(elementView) {
- this.$('[data-model="' + elementView.model.get('id') + '"]').remove();
- if (this.$('.selection-box').length === 0) {
+ this.$('[data-model="' + elementView.model.get('id') + '"]').remove();
+ if (this.$('.selection-box').length === 0) {
- this.$el.hide().removeClass('selected');
- }
- },
+ this.$el.hide().removeClass('selected');
+ }
+ },
- createSelectionBox: function(elementView) {
+ createSelectionBox: function(elementView) {
- var element = elementView.model;
+ var element = elementView.model;
- if (!element.isLink()) {
+ if (!element.isLink()) {
- var $selectionBox = $('
', {
- 'class': 'selection-box',
- 'data-model': element.get('id')
- });
- this.$el.append($selectionBox);
+ var $selectionBox = $('
', {
+ 'class': 'selection-box',
+ 'data-model': element.get('id')
+ });
+ this.$el.append($selectionBox);
- this.updateBox(element);
+ this.updateBox(element);
- this.$el.addClass('selected').show();
+ this.$el.addClass('selected').show();
- this._action = 'cherry-picking';
- }
- },
+ this._action = 'cherry-picking';
+ }
+ },
- updateBox: function(element) {
+ updateBox: function(element) {
- var border = 12;
+ var margin = 4;
- var bbox = element.getBBox();
- var state = this.options.state;
+ var bbox = element.getBBox();
+ var state = this.options.state;
- $("div[data-model='" + element.get('id') + "']").css({
- left: bbox.x * state.zoom + state.pan.x +
- bbox.width / 2.0 * (state.zoom - 1) - border / 2,
- top: bbox.y * state.zoom + state.pan.y +
- bbox.height / 2.0 * (state.zoom - 1) - border / 2,
- width: bbox.width + border,
- height: bbox.height + border,
- transform: 'scale(' + state.zoom + ')'
- });
- }
+ $('div[data-model=\'' + element.get('id') + '\']').css({
+ left: bbox.x * state.zoom + state.pan.x +
+ bbox.width / 2.0 * (state.zoom - 1) - margin,
+ top: bbox.y * state.zoom + state.pan.y +
+ bbox.height / 2.0 * (state.zoom - 1) - margin,
+ width: bbox.width + 2 * margin,
+ height: bbox.height + 2 * margin,
+ transform: 'scale(' + state.zoom + ')'
+ });
+ }
});
diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js
new file mode 100644
index 000000000..f24fb33a0
--- /dev/null
+++ b/app/scripts/services/blocks.service.js
@@ -0,0 +1,755 @@
+'use strict';
+
+angular.module('icestudio')
+ .service('blocks', function(joint,
+ boards,
+ utils,
+ gettextCatalog) {
+ var gridsize = 8;
+
+ this.newBasic = newBasic;
+ this.newGeneric = newGeneric;
+
+ this.loadBasic = loadBasic;
+ this.loadGeneric = loadGeneric;
+ this.loadWire = loadWire;
+
+ this.editBasic = editBasic;
+
+
+ //-- New
+
+ function newBasic(type, addCellCallback) {
+ switch(type) {
+ case 'basic.input':
+ case 'basic.output':
+ newBasicIO(type, addCellCallback);
+ break;
+ case 'basic.constant':
+ newBasicConstant(addCellCallback);
+ break;
+ case 'basic.code':
+ newBasicCode(addCellCallback);
+ break;
+ case 'basic.info':
+ newBasicInfo(addCellCallback);
+ break;
+ default:
+ break;
+ }
+ }
+
+ function newBasicIO(type, addCellCallback) {
+ var config;
+ if (type === 'basic.input') {
+ config = { type: type, _default: 'in', x: 4 };
+ }
+ else { // 'basic.output'
+ config = { type: type, _default: 'out', x: 95 };
+ }
+ var blockInstance = {
+ id: null,
+ data: {},
+ type: config.type,
+ position: { x: config.x * gridsize, y: 4 * gridsize }
+ };
+ utils.inputcheckboxprompt([
+ gettextCatalog.getString('Enter the ports'),
+ gettextCatalog.getString('FPGA pin')
+ ], [
+ config._default,
+ false
+ ],
+ function(evt, values) {
+ var labels = values[0].replace(/ /g, '').split(',');
+ var virtual = !values[1];
+ // Validate values
+ var portInfo, portInfos = [];
+ for (var l in labels) {
+ if (labels[l]) {
+ portInfo = utils.parsePortLabel(labels[l]);
+ if (portInfo) {
+ evt.cancel = false;
+ portInfos.push(portInfo);
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Wrong port name {{name}}', { name: labels[l] }), 'warning', 3);
+ return;
+ }
+ }
+ else {
+ evt.cancel = true;
+ //return;
+ }
+ }
+ // Create blocks
+ for (var p in portInfos) {
+ portInfo = portInfos[p];
+ var pins = getPins(portInfo);
+ blockInstance.data = {
+ name: portInfo.name,
+ range: portInfo.rangestr,
+ pins: pins,
+ virtual: virtual
+ };
+ if (addCellCallback) {
+ addCellCallback(loadBasic(blockInstance));
+ }
+ // Next block position
+ blockInstance.position.y += (virtual ? 10 : (6 + 4 * pins.length)) * gridsize;
+ }
+ });
+ }
+
+ function getPins(portInfo) {
+ var pins = [];
+ if (portInfo.range) {
+ for (var r in portInfo.range) {
+ pins.push({ index: portInfo.range[r].toString(), name: '', value: '0' });
+ }
+ }
+ else {
+ pins.push({ index: '0', name: '', value: '0' });
+ }
+ return pins;
+ }
+
+ function newBasicConstant(addCellCallback) {
+ var blockInstance = {
+ id: null,
+ data: {},
+ type: 'basic.constant',
+ position: { x: 20 * gridsize, y: 4 * gridsize }
+ };
+ utils.inputcheckboxprompt([
+ gettextCatalog.getString('Enter the constant blocks'),
+ gettextCatalog.getString('Local parameter')
+ ], [
+ 'C',
+ false
+ ],
+ function(evt, values) {
+ var labels = values[0].replace(/ /g, '').split(',');
+ var local = values[1];
+ // Validate values
+ var paramInfo, paramInfos = [];
+ for (var l in labels) {
+ if (labels[l]) {
+ paramInfo = utils.parseParamLabel(labels[l]);
+ if (paramInfo) {
+ evt.cancel = false;
+ paramInfos.push(paramInfo);
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Wrong parameter name {{name}}', { name: labels[l] }), 'warning', 3);
+ return;
+ }
+ }
+ else {
+ evt.cancel = true;
+ //return;
+ }
+ }
+ // Create blocks
+ for (var p in paramInfos) {
+ paramInfo = paramInfos[p];
+ blockInstance.data = {
+ name: paramInfo.name,
+ value: '',
+ local: local
+ };
+ if (addCellCallback) {
+ addCellCallback(loadBasicConstant(blockInstance));
+ }
+ blockInstance.position.x += 15 * gridsize;
+ }
+ });
+ }
+
+ function newBasicCode(addCellCallback, block) {
+ var blockInstance = {
+ id: null,
+ data: {
+ code: '',
+ params: [],
+ ports: { in: [], out: [] }
+ },
+ type: 'basic.code',
+ position: { x: 31 * gridsize, y: 24 * gridsize }
+ };
+ var defaultValues = [
+ 'a , b',
+ 'c , d',
+ ''
+ ];
+ if (block) {
+ blockInstance = block;
+ var index, port;
+ if (block.data.ports) {
+ var inPorts = [];
+ for (index in block.data.ports.in) {
+ port = block.data.ports.in[index];
+ inPorts.push(port.name + (port.range || ''));
+ }
+ defaultValues[0] = inPorts.join(' , ');
+ var outPorts = [];
+ for (index in block.data.ports.out) {
+ port = block.data.ports.out[index];
+ outPorts.push(port.name + (port.range || ''));
+ }
+ defaultValues[1] = outPorts.join(' , ');
+ }
+ if (block.data.params) {
+ var params = [];
+ for (index in block.data.params) {
+ params.push(block.data.params[index].name);
+ }
+ defaultValues[2] = params.join(' , ');
+ }
+ }
+ utils.multiprompt(
+ [ gettextCatalog.getString('Enter the input ports'),
+ gettextCatalog.getString('Enter the output ports'),
+ gettextCatalog.getString('Enter the parameters') ],
+ defaultValues,
+ function(evt, values) {
+ var inPorts = values[0].replace(/ /g, '').split(',');
+ var outPorts = values[1].replace(/ /g, '').split(',');
+ var params = values[2].replace(/ /g, '').split(',');
+ var allNames = [];
+ // Validate values
+ var i, inPortInfo, inPortInfos = [];
+ for (i in inPorts) {
+ if (inPorts[i]) {
+ inPortInfo = utils.parsePortLabel(inPorts[i]);
+ if (inPortInfo && inPortInfo.name) {
+ evt.cancel = false;
+ inPortInfos.push(inPortInfo);
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Wrong port name {{name}}', { name: inPorts[i] }), 'warning', 3);
+ return;
+ }
+ }
+ }
+ var o, outPortInfo, outPortInfos = [];
+ for (o in outPorts) {
+ if (outPorts[o]) {
+ outPortInfo = utils.parsePortLabel(outPorts[o]);
+ if (outPortInfo && outPortInfo.name) {
+ evt.cancel = false;
+ outPortInfos.push(outPortInfo);
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Wrong port name {{name}}', { name: outPorts[o] }), 'warning', 3);
+ return;
+ }
+ }
+ }
+ var p, paramInfo, paramInfos = [];
+ for (p in params) {
+ if (params[p]) {
+ paramInfo = utils.parseParamLabel(params[p]);
+ if (paramInfo) {
+ evt.cancel = false;
+ paramInfos.push(paramInfo);
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Wrong parameter name {{name}}', { name: params[p] }), 'warning', 3);
+ return;
+ }
+ }
+ }
+ // Create ports
+ var pins;
+ blockInstance.data.ports.in = [];
+ for (i in inPortInfos) {
+ if (inPortInfos[i]) {
+ pins = getPins(inPortInfos[i]);
+ blockInstance.data.ports.in.push({
+ name: inPortInfos[i].name,
+ range: inPortInfos[i].rangestr,
+ size: (pins.length > 1) ? pins.length : undefined
+ });
+ allNames.push(inPortInfos[i].name);
+ }
+ }
+ blockInstance.data.ports.out = [];
+ for (o in outPortInfos) {
+ if (outPortInfos[o]) {
+ pins = getPins(outPortInfos[o]);
+ blockInstance.data.ports.out.push({
+ name: outPortInfos[o].name,
+ range: outPortInfos[o].rangestr,
+ size: (pins.length > 1) ? pins.length : undefined
+ });
+ allNames.push(outPortInfos[o].name);
+ }
+ }
+ blockInstance.data.params = [];
+ for (p in paramInfos) {
+ if (paramInfos[p]) {
+ blockInstance.data.params.push({
+ name: paramInfos[p].name
+ });
+ allNames.push(paramInfos[p].name);
+ }
+ }
+ // Check duplicated attributes
+ var numNames = allNames.length;
+ if (numNames === $.unique(allNames).length) {
+ evt.cancel = false;
+ // Create block
+ if (addCellCallback) {
+ addCellCallback(loadBasicCode(blockInstance));
+ }
+ }
+ else {
+ evt.cancel = true;
+ alertify.notify(gettextCatalog.getString('Duplicated block attributes'), 'warning', 3);
+ }
+ });
+ }
+
+ function newBasicInfo(addCellCallback) {
+ var blockInstance = {
+ id: null,
+ data: { info: '' },
+ type: 'basic.info',
+ position: { x: 4 * gridsize, y: 30 * gridsize }
+ };
+ if (addCellCallback) {
+ addCellCallback(loadBasicInfo(blockInstance));
+ }
+ }
+
+ function newGeneric(type, block, addCellCallback) {
+ var blockInstance = {
+ id: null,
+ type: type,
+ position: { x: 6 * gridsize, y: 16 * gridsize }
+ };
+ if (block &&
+ block.design &&
+ block.design.graph &&
+ block.design.graph.blocks &&
+ block.design.graph.wires) {
+ if (addCellCallback) {
+ addCellCallback(loadGeneric(blockInstance, block));
+ }
+ }
+ else {
+ alertify.notify(gettextCatalog.getString('Wrong block format: {{type}}', { type: type }), 'error', 30);
+ }
+ }
+
+
+ //-- Load
+
+ function loadBasic(instance, disabled) {
+ switch(instance.type) {
+ case 'basic.input':
+ return loadBasicInput(instance, disabled);
+ case 'basic.output':
+ return loadBasicOutput(instance, disabled);
+ case 'basic.constant':
+ return loadBasicConstant(instance, disabled);
+ case 'basic.code':
+ return loadBasicCode(instance, disabled);
+ case 'basic.info':
+ return loadBasicInfo(instance, disabled);
+ default:
+ break;
+ }
+ }
+
+ function loadBasicInput(instance, disabled) {
+ var data = instance.data;
+ var rightPorts = [{
+ id: 'out',
+ label: '',
+ size: data.pins ? data.pins.length : (data.size || 1),
+ gridUnits: 8
+ }];
+
+ var cell = new joint.shapes.ice.Input({
+ id: instance.id,
+ blockType: instance.type,
+ data: instance.data,
+ position: instance.position,
+ disabled: disabled,
+ rightPorts: rightPorts,
+ choices: boards.getPinoutHTML()
+ });
+ return cell;
+ }
+
+ function loadBasicOutput(instance, disabled) {
+ var data = instance.data;
+ var leftPorts = [{
+ id: 'in',
+ label: '',
+ size: data.pins ? data.pins.length : (data.size || 1),
+ gridUnits: 8
+ }];
+ var cell = new joint.shapes.ice.Output({
+ id: instance.id,
+ blockType: instance.type,
+ data: instance.data,
+ position: instance.position,
+ disabled: disabled,
+ leftPorts: leftPorts,
+ choices: boards.getPinoutHTML()
+ });
+ return cell;
+ }
+
+ function loadBasicConstant(instance, disabled) {
+ var bottomPorts = [{
+ id: 'constant-out',
+ label: '',
+ gridUnits: 8
+ }];
+ var cell = new joint.shapes.ice.Constant({
+ id: instance.id,
+ blockType: instance.type,
+ data: instance.data,
+ position: instance.position,
+ disabled: disabled,
+ bottomPorts: bottomPorts
+ });
+ return cell;
+ }
+
+ function loadBasicCode(instance, disabled) {
+ var port;
+ var leftPorts = [];
+ var rightPorts = [];
+ var topPorts = [];
+
+ for (var i in instance.data.ports.in) {
+ port = instance.data.ports.in[i];
+ leftPorts.push({
+ id: port.name,
+ label: port.name + (port.range || ''),
+ size: port.size || 1,
+ gridUnits: 32
+ });
+ }
+
+ for (var o in instance.data.ports.out) {
+ port = instance.data.ports.out[o];
+ rightPorts.push({
+ id: port.name,
+ label: port.name + (port.range || ''),
+ size: port.size || 1,
+ gridUnits: 32
+ });
+ }
+
+ for (var p in instance.data.params) {
+ port = instance.data.params[p];
+ topPorts.push({
+ id: port.name,
+ label: port.name,
+ gridUnits: 48
+ });
+ }
+
+ var cell = new joint.shapes.ice.Code({
+ id: instance.id,
+ blockType: instance.type,
+ data: instance.data,
+ position: instance.position,
+ disabled: disabled,
+ leftPorts: leftPorts,
+ rightPorts: rightPorts,
+ topPorts: topPorts
+ });
+ return cell;
+ }
+
+ function loadBasicInfo(instance, disabled) {
+ var cell = new joint.shapes.ice.Info({
+ id: instance.id,
+ blockType: instance.type,
+ data: instance.data,
+ position: instance.position,
+ disabled: disabled
+ });
+ return cell;
+ }
+
+ function loadGeneric(instance, block) {
+ var i;
+ var leftPorts = [];
+ var rightPorts = [];
+ var topPorts = [];
+ var bottomPorts = [];
+ var gridsize = 8;
+
+ for (i in block.design.graph.blocks) {
+ var data;
+ var item = block.design.graph.blocks[i];
+ if (item.type === 'basic.input') {
+ data = block.design.graph.blocks[i].data;
+ leftPorts.push({
+ id: item.id,
+ label: item.data.name + (item.data.range || ''),
+ size: data.pins ? data.pins.length : (data.size || 1)
+ });
+ }
+ else if (item.type === 'basic.output') {
+ data = block.design.graph.blocks[i].data;
+ rightPorts.push({
+ id: item.id,
+ label: item.data.name + (item.data.range || ''),
+ size: data.pins ? data.pins.length : (data.size || 1)
+ });
+ }
+ else if (item.type === 'basic.constant') {
+ if (!item.data.local) {
+ topPorts.push({
+ id: item.id,
+ label: item.data.name
+ });
+ }
+ }
+ }
+
+ var numPortsHeight = Math.max(leftPorts.length, rightPorts.length);
+ var numPortsWidth = Math.max(topPorts.length, bottomPorts.length);
+
+ var height = 8 * gridsize;
+ height = Math.max(4 * gridsize * numPortsHeight, height);
+ var blockLabel = block.package.name;
+ var width = 12 * gridsize;
+
+ width = Math.max(4 * gridsize * numPortsWidth, width);
+
+ var gridUnitsHeight = height / gridsize;
+ var gridUnitsWidth = width / gridsize;
+
+ for (i in leftPorts) {
+ leftPorts[i].gridUnits = gridUnitsHeight;
+ }
+ for (i in rightPorts) {
+ rightPorts[i].gridUnits = gridUnitsHeight;
+ }
+ for (i in topPorts) {
+ topPorts[i].gridUnits = gridUnitsWidth;
+ }
+ for (i in bottomPorts) {
+ bottomPorts[i].gridUnits = gridUnitsWidth;
+ }
+
+ var blockImage = '';
+ if (block.package.image) {
+ width = 12 * gridsize;
+ if (block.package.image.startsWith('%3Csvg')) {
+ blockImage = block.package.image;
+ }
+ else if (block.package.image.startsWith('