Commit 02e0b003 authored by Patrik Meijer's avatar Patrik Meijer
Browse files

Initial work on pointer-creation

parent 035fdb89
......@@ -12,12 +12,12 @@ import MenuItem from '@material-ui/core/MenuItem';
export default class ContextMenu extends Component {
static propTypes = {
gmeClient: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
nodeId: PropTypes.string.isRequired, // FIXME: should be nodeIds
eventX: PropTypes.number.isRequired,
eventY: PropTypes.number.isRequired,
onClose: PropTypes.func.isRequired,
setActiveNode: PropTypes.func.isRequired,
createPointer: PropTypes.func.isRequired,
readOnly: PropTypes.bool,
};
......@@ -41,6 +41,13 @@ export default class ContextMenu extends Component {
onClose();
}
getCreatePointerFunc = (ptrName) => {
const {createPointer, nodeId} = this.props;
return () => {
createPointer(nodeId, ptrName);
};
}
render() {
const {
gmeClient,
......@@ -48,63 +55,32 @@ export default class ContextMenu extends Component {
eventX,
eventY,
readOnly,
data,
onClose,
} = this.props;
// console.log(data);
const menuItems = [];
const nodeObj = gmeClient.getNode(nodeId);
if (false) {
const metaNodeObj = gmeClient.getNode(nodeObj.getMetaTypeId());
const metaNodeObj = gmeClient.getNode(nodeObj.getMetaTypeId());
menuItems.push((
<MenuItem key="meta-type" onClick={this.props.onClose} disabled={readOnly}>
{`<<${metaNodeObj.getAttribute('name')}>>`}
</MenuItem>));
const pointerNames = metaNodeObj.getValidPointerNames();
if (metaNodeObj.getAttribute('name') === 'System') {
menuItems.push((
<MenuItem key="open-sub-system" onClick={this.setActiveNode} >
Open Subsystem ...
</MenuItem>));
}
} else if (data.memberAttrs && data.memberAttrs.length > 0) {
let args;
data.memberAttrs.forEach((memAttr) => {
if (memAttr.name === 'args') {
args = memAttr.value ? `(${memAttr.value})` : '()';
}
});
if (args) {
menuItems.push((
<MenuItem key="invocation-args" onClick={this.props.onClose}>
Invocation arguments: {args}
</MenuItem>));
}
} else if (data.pointerName === 'post' || data.pointerName === 'pre') {
const methodNode = gmeClient.getNode(data.source);
const modeNode = gmeClient.getNode(data.target);
if (methodNode && modeNode) {
// const parentName = gmeClient.getNode(modeNode.getParentId()).getAttribute('name');
// const modeName = modeNode.getAttribute('name');
const methodName = methodNode.getAttribute('name');
menuItems.push((
<MenuItem key="pre-post-condition" onClick={this.props.onClose}>
{`${data.pointerName}-condition for ${methodName}`}
</MenuItem>));
}
} else if (data.setName === 'network-nodes') {
menuItems.push((
<MenuItem key="meta-type" onClick={onClose} disabled={readOnly}>
{`<<${metaNodeObj.getAttribute('name')}>>`}
</MenuItem>));
menuItems.push((
<MenuItem key="open-sub-system" onClick={this.setActiveNode} >
Open Subsystem ...
</MenuItem>));
pointerNames.forEach((ptr) => {
menuItems.push((
<MenuItem key="cell-network-nodes" onClick={this.props.onClose}>
{`${data.setName}`}
<MenuItem key={`create-pointer-${ptr}`} onClick={this.getCreatePointerFunc(ptr)} >
{`Create pointer ${ptr} from here ...`}
</MenuItem>));
}
if (menuItems.length === 0) {
this.props.onClose();
return <div/>;
}
});
return (
<Menu
......@@ -116,7 +92,7 @@ export default class ContextMenu extends Component {
left: eventX,
}}
open
onClose={this.props.onClose}
onClose={onClose}
>
{menuItems}
</Menu>
......
......@@ -34,73 +34,79 @@ export default class FilterSelector extends Component {
static defaultProps = {
open: true,
}
};
render() {
const {validItems, activeItems} = this.props;
const {
validItems,
activeItems,
handleToggle,
open,
onClose,
} = this.props;
const lists = [];
Object.keys(validItems).forEach((groupName) => {
const items = [];
validItems[groupName].forEach((item) => {
if (item.hidden) {
return;
}
Object.keys(validItems)
.forEach((groupName) => {
const items = [];
validItems[groupName].forEach((item) => {
if (item.hidden) {
return;
}
const itemId = `${groupName}$${item.name}`;
let icon;
const itemId = `${groupName}$${item.name}`;
let icon;
switch (groupName) {
case 'pointers':
icon = <TrendingFlatIcon style={{color: 'rgb(85, 123, 139)'}}/>;
break;
case 'sets':
icon = (item.name === 'network-nodes') ?
<MoreHorizIcon/> :
<CallSplitIcon className="rotate-90"/>;
break;
case 'nodes':
icon = item.isConnection ? <RemoveIcon/> : <FiberManualRecordIcon/>;
break;
default:
icon = <FiberManualRecordIcon/>;
break;
}
switch (groupName) {
case 'pointers':
icon = <TrendingFlatIcon style={{color: 'rgb(85, 123, 139)'}}/>;
break;
case 'sets':
icon = (item.name === 'network-nodes')
? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>;
break;
case 'nodes':
icon = item.isConnection ? <RemoveIcon/> : <FiberManualRecordIcon/>;
break;
default:
icon = <FiberManualRecordIcon/>;
break;
}
items.push((
<ListItem key={itemId}>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText primary={item.name} style={{marginRight: 24}}/>
<ListItemSecondaryAction>
<Switch
onChange={() => {
this.props.handleToggle(itemId);
}}
checked={activeItems[itemId]}
/>
</ListItemSecondaryAction>
</ListItem>));
});
items.push((
<ListItem key={itemId}>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText primary={item.name} style={{marginRight: 24}}/>
<ListItemSecondaryAction>
<Switch
onChange={() => {
handleToggle(itemId);
}}
checked={activeItems[itemId]}
/>
</ListItemSecondaryAction>
</ListItem>));
});
if (items.length > 0) {
lists.push((
<div key={groupName} >
<Typography variant="subheading">
{groupName.toUpperCase()}
</Typography>
<List>
{items}
</List>
<Divider/>
</div>
));
}
});
if (items.length > 0) {
lists.push((
<div key={groupName}>
<Typography variant="subheading">
{groupName.toUpperCase()}
</Typography>
<List>
{items}
</List>
<Divider/>
</div>
));
}
});
return (
<Dialog open={this.props.open} onClose={this.props.onClose}>
<Dialog open={open} onClose={onClose}>
<DialogTitle id="filter-dialog-title">Apply Filters</DialogTitle>
<DialogContent>
{lists}
......
......@@ -85,6 +85,12 @@ const DEFAULT_STYLES = [
'background-color': 'rgba(82, 168, 236, 0.6)',
},
},
{
selector: 'node.valid-pointer-target',
style: {
'background-color': 'rgb(0, 0, 255)',
},
},
// {
// selector: 'edge.in-active-selection',
// style: {
......@@ -186,11 +192,12 @@ export default class GraphEditor extends Component {
const {validFilters} = this.props;
const res = {};
Object.keys(validFilters).forEach((filterType) => {
validFilters[filterType].forEach((f) => {
res[`${filterType}$${f.name}`] = f.active;
Object.keys(validFilters)
.forEach((filterType) => {
validFilters[filterType].forEach((f) => {
res[`${filterType}$${f.name}`] = f.active;
});
});
});
// {
// 'sets$members': true,
......@@ -200,25 +207,27 @@ export default class GraphEditor extends Component {
return res;
})(),
showFilterSelector: false,
createPointer: null,
};
componentWillReceiveProps(newProps) {
const {activeNode} = newProps;
if (activeNode !== this.props.activeNode) {
componentWillReceiveProps({activeNode}) {
const {activeNode: preActiveNode} = this.props;
if (activeNode !== preActiveNode) {
this.reposition = {};
}
}
onListItemClick = id => (
(/* e */) => {
this.props.setActiveSelection([id]);
})
const {setActiveSelection} = this.props;
setActiveSelection([id]);
});
onListItemDoubleClick = id => (
(/* e */) => {
this.props.setActiveNode(id);
})
const {setActiveNode} = this.props;
setActiveNode(id);
});
getCytoscapeElements() {
const {
......@@ -227,7 +236,7 @@ export default class GraphEditor extends Component {
activeSelection,
nodes,
} = this.props;
const {activeFilters} = this.state;
const {activeFilters, createPointer} = this.state;
// https://github.com/ybarukh/react-cytoscape/blob/master/sample/app/Graph.js
// http://js.cytoscape.org/#notation/elements-json
const result = {
......@@ -250,115 +259,125 @@ export default class GraphEditor extends Component {
// parentNode = children[parentNode.parent];
// }
return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY] ||
parentNode.registries.position));
return JSON.parse(JSON.stringify(parentNode.registries[CONSTANTS.CYTOSCAPE_POS_REG_KEY]
|| parentNode.registries.position));
};
Object.keys(nodes).forEach((id) => {
if (id === activeNode) {
return;
}
const childData = nodes[id];
if (activeFilters[`nodes$${childData.metaType}`]) {
return;
}
if (typeof childData.pointers.src === 'string' && typeof childData.pointers.dst === 'string') {
result.elements.edges.push({
data: {
id,
source: childData.pointers.src,
target: childData.pointers.dst,
label: childData.attributes.name,
},
classes: `${activeSelection.includes(id) ? 'in-active-selection ' : ''}gme-connection`,
});
} else {
const cytoData = {
data: {
id,
name: childData.attributes.name,
label: childData.attributes.label ?
`${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name,
parent: childData.parent,
metaType: childData.metaType,
},
position: getPosition(id),
grabbable: !readOnly,
classes: `${activeSelection.includes(id) ? 'in-active-selection' : ''}`,
};
Object.keys(nodes)
.forEach((id) => {
if (id === activeNode) {
return;
}
result.elements.nodes.push(cytoData);
const childData = nodes[id];
// Keep track of the containers s.t. they cannot be grabbed.
nodeMap[id] = result.elements.nodes[result.elements.nodes.length - 1];
if (childData.parent !== activeNode) {
nodeIdsWithChildren[childData.parent] = true;
if (activeFilters[`nodes$${childData.metaType}`]) {
return;
}
Object.keys(childData.sets).forEach((setName) => {
if (!childData.sets[setName] || activeFilters[`sets$${setName}`]) {
return;
}
childData.sets[setName].forEach((setMemberData) => {
const edgeId = `${id}$${setName}$${setMemberData.id}`;
const edgeData = {
data: {
id: edgeId,
source: id,
target: setMemberData.id,
label: setName,
memberAttrs: setMemberData.memberAttrs,
},
classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`,
};
if (typeof childData.pointers.src === 'string' && typeof childData.pointers.dst === 'string') {
result.elements.edges.push({
data: {
id,
source: childData.pointers.src,
target: childData.pointers.dst,
label: childData.attributes.name,
},
classes: `${activeSelection.includes(id) ? 'in-active-selection ' : ''}gme-connection`,
});
} else {
const cytoData = {
data: {
id,
name: childData.attributes.name,
label: childData.attributes.label
? `${childData.attributes.name}::${childData.attributes.label}`
: childData.attributes.name,
parent: childData.parent,
metaType: childData.metaType,
},
position: getPosition(id),
grabbable: !readOnly,
classes: `${activeSelection.includes(id) ? 'in-active-selection' : ''}`,
};
if (setMemberData.label !== null) {
edgeData.data.label = setMemberData.label;
}
if (createPointer && createPointer.target === id) {
cytoData.classes += ' valid-pointer-target';
}
result.elements.edges.push(edgeData);
});
});
result.elements.nodes.push(cytoData);
Object.keys(childData.pointers).forEach((pName) => {
if (!childData.pointers[pName] || activeFilters[`pointers$${pName}`]) {
return;
// Keep track of the containers s.t. they cannot be grabbed.
nodeMap[id] = result.elements.nodes[result.elements.nodes.length - 1];
if (childData.parent !== activeNode) {
nodeIdsWithChildren[childData.parent] = true;
}
const edgeId = `${id}$${pName}$${childData.pointers[pName]}`;
const edgeData = {
data: {
id: edgeId,
source: id,
target: childData.pointers[pName],
label: pName,
},
classes: `${pName === 'base' ? 'base-pointer' : 'pointer'}\
Object.keys(childData.sets)
.forEach((setName) => {
if (!childData.sets[setName] || activeFilters[`sets$${setName}`]) {
return;
}
childData.sets[setName].forEach((setMemberData) => {
const edgeId = `${id}$${setName}$${setMemberData.id}`;
const edgeData = {
data: {
id: edgeId,
source: id,
target: setMemberData.id,
label: setName,
memberAttrs: setMemberData.memberAttrs,
},
classes: `set-member ${activeSelection.includes(edgeId)
? 'in-active-selection' : ''}`,
};
if (setMemberData.label !== null) {
edgeData.data.label = setMemberData.label;
}
result.elements.edges.push(edgeData);
});
});
Object.keys(childData.pointers)
.forEach((pName) => {
if (!childData.pointers[pName] || activeFilters[`pointers$${pName}`]) {
return;
}
const edgeId = `${id}$${pName}$${childData.pointers[pName]}`;
const edgeData = {
data: {
id: edgeId,
source: id,
target: childData.pointers[pName],
label: pName,
},
classes: `${pName === 'base' ? 'base-pointer' : 'pointer'}\
${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
};
};
result.elements.edges.push(edgeData);
});
result.elements.edges.push(edgeData);
});
// Use the images defined for the node.
if (childData.registries.SVGIcon && childData.registries.SVGIcon.indexOf('<') === -1) {
result.style.push({
selector: `node[id = "${id}"]`,
style: {
'background-image': `url(/assets/DecoratorSVG/${childData.registries.SVGIcon})`,
},
});
// Use the images defined for the node.
if (childData.registries.SVGIcon && childData.registries.SVGIcon.indexOf('<') === -1) {
result.style.push({
selector: `node[id = "${id}"]`,
style: {
'background-image': `url(/assets/DecoratorSVG/${childData.registries.SVGIcon})`,
},
});
}
}
}
});
});
Object.keys(nodeIdsWithChildren).forEach((id) => {
nodeMap[id].data.hasChildren = true;
});
Object.keys(nodeIdsWithChildren)
.forEach((id) => {
nodeMap[id].data.hasChildren = true;
});
return result;
}
......@@ -368,15 +387,16 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
if (Object.keys(this.reposition).length > 0) {
gmeClient.startTransaction();
Object.keys(this.reposition).forEach((id) => {
if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) {
gmeClient.setRegistry(
id,
CONSTANTS.CYTOSCAPE_POS_REG_KEY,
this.reposition[id].position,
);
}
});
Object.keys(this.reposition)
.forEach((id) => {
if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) {
gmeClient.setRegistry(
id,
CONSTANTS.CYTOSCAPE_POS_REG_KEY,
this.reposition[id].position,
);
}
});
gmeClient.completeTransaction();
}
......@@ -389,69 +409,106 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
};
toggleFilter = (filterId) => {
this.setState({
this.setState(({activeFilters}) => ({
activeFilters: update(
this.state.activeFilters,
{[filterId]: {$set: !this.state.activeFilters[filterId]}},
activeFilters,
{[filterId]: {$set: activeFilters[filterId]}},
),