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 pointerNames = metaNodeObj.getValidPointerNames();
menuItems.push((
<MenuItem key="meta-type" onClick={this.props.onClose} disabled={readOnly}>
<MenuItem key="meta-type" onClick={onClose} disabled={readOnly}>
{`<<${metaNodeObj.getAttribute('name')}>>`}
</MenuItem>));
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) {
pointerNames.forEach((ptr) => {
menuItems.push((
<MenuItem key="invocation-args" onClick={this.props.onClose}>
Invocation arguments: {args}
<MenuItem key={`create-pointer-${ptr}`} onClick={this.getCreatePointerFunc(ptr)} >
{`Create pointer ${ptr} from here ...`}
</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="cell-network-nodes" onClick={this.props.onClose}>
{`${data.setName}`}
</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,13 +34,20 @@ 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) => {
Object.keys(validItems)
.forEach((groupName) => {
const items = [];
validItems[groupName].forEach((item) => {
if (item.hidden) {
......@@ -55,9 +62,8 @@ export default class FilterSelector extends Component {
icon = <TrendingFlatIcon style={{color: 'rgb(85, 123, 139)'}}/>;
break;
case 'sets':
icon = (item.name === 'network-nodes') ?
<MoreHorizIcon/> :
<CallSplitIcon className="rotate-90"/>;
icon = (item.name === 'network-nodes')
? <MoreHorizIcon/> : <CallSplitIcon className="rotate-90"/>;
break;
case 'nodes':
icon = item.isConnection ? <RemoveIcon/> : <FiberManualRecordIcon/>;
......@@ -76,7 +82,7 @@ export default class FilterSelector extends Component {
<ListItemSecondaryAction>
<Switch
onChange={() => {
this.props.handleToggle(itemId);
handleToggle(itemId);
}}
checked={activeItems[itemId]}
/>
......@@ -86,7 +92,7 @@ export default class FilterSelector extends Component {
if (items.length > 0) {
lists.push((
<div key={groupName} >
<div key={groupName}>
<Typography variant="subheading">
{groupName.toUpperCase()}
</Typography>
......@@ -100,7 +106,7 @@ export default class FilterSelector extends Component {
});
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,7 +192,8 @@ export default class GraphEditor extends Component {
const {validFilters} = this.props;
const res = {};
Object.keys(validFilters).forEach((filterType) => {
Object.keys(validFilters)
.forEach((filterType) => {
validFilters[filterType].forEach((f) => {
res[`${filterType}$${f.name}`] = f.active;
});
......@@ -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,11 +259,12 @@ 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) => {
Object.keys(nodes)
.forEach((id) => {
if (id === activeNode) {
return;
}
......@@ -280,8 +290,9 @@ export default class GraphEditor extends Component {
data: {
id,
name: childData.attributes.name,
label: childData.attributes.label ?
`${childData.attributes.name}::${childData.attributes.label}` : childData.attributes.name,
label: childData.attributes.label
? `${childData.attributes.name}::${childData.attributes.label}`
: childData.attributes.name,
parent: childData.parent,
metaType: childData.metaType,
},
......@@ -290,6 +301,10 @@ export default class GraphEditor extends Component {
classes: `${activeSelection.includes(id) ? 'in-active-selection' : ''}`,
};
if (createPointer && createPointer.target === id) {
cytoData.classes += ' valid-pointer-target';
}
result.elements.nodes.push(cytoData);
// Keep track of the containers s.t. they cannot be grabbed.
......@@ -298,7 +313,8 @@ export default class GraphEditor extends Component {
nodeIdsWithChildren[childData.parent] = true;
}
Object.keys(childData.sets).forEach((setName) => {
Object.keys(childData.sets)
.forEach((setName) => {
if (!childData.sets[setName] || activeFilters[`sets$${setName}`]) {
return;
}
......@@ -313,7 +329,8 @@ export default class GraphEditor extends Component {
label: setName,
memberAttrs: setMemberData.memberAttrs,
},
classes: `set-member ${activeSelection.includes(edgeId) ? 'in-active-selection' : ''}`,
classes: `set-member ${activeSelection.includes(edgeId)
? 'in-active-selection' : ''}`,
};
if (setMemberData.label !== null) {
......@@ -324,7 +341,8 @@ export default class GraphEditor extends Component {
});
});
Object.keys(childData.pointers).forEach((pName) => {
Object.keys(childData.pointers)
.forEach((pName) => {
if (!childData.pointers[pName] || activeFilters[`pointers$${pName}`]) {
return;
}
......@@ -356,7 +374,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
}
});
Object.keys(nodeIdsWithChildren).forEach((id) => {
Object.keys(nodeIdsWithChildren)
.forEach((id) => {
nodeMap[id].data.hasChildren = true;
});
......@@ -368,7 +387,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
if (Object.keys(this.reposition).length > 0) {
gmeClient.startTransaction();
Object.keys(this.reposition).forEach((id) => {
Object.keys(this.reposition)
.forEach((id) => {
if (id.indexOf(nodeId) === 0 || nodeId.indexOf(id) === 0) {
gmeClient.setRegistry(
id,
......@@ -389,52 +409,69 @@ ${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]}},
),
}));
};
startNewPointer = (nodeId, ptrName) => {
this.setState({
showNodeMenu: false,
createPointer: {
nodeId,
ptrName,
target: null,
},
});
};
attachCytoscapeHandlers() {
this.cy.on('position', (e) => {
const cyNode = e.target;
const childNodeDesc = this.props.nodes[cyNode.id()];
this.cy.on('position', ({target}) => {
const {nodes} = this.props;
const childNodeDesc = nodes[target.id()];
const floorPos = {
x: Math.floor(cyNode.position().x),
y: Math.floor(cyNode.position().y),
x: Math.floor(target.position().x),
y: Math.floor(target.position().y),
};
if (childNodeDesc &&
(!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY] ||
childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x ||
childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) {
this.reposition[cyNode.id()] = {
id: cyNode.id(),
if (childNodeDesc
&& (!childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY]
|| childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].x !== floorPos.x
|| childNodeDesc[CONSTANTS.CYTOSCAPE_POS_REG_KEY].y !== floorPos.y)) {
this.reposition[target.id()] = {
id: target.id(),
position: floorPos,
};
}
});
this.cy.on('free', (e) => {
const cyNode = e.target;
if (typeof cyNode.id === 'function') {
this.cy.on('free', ({target}) => {
const {setActiveSelection} = this.props;
if (typeof target.id === 'function') {
// console.log('free', cyNode.id(), JSON.stringify(this.reposition));
this.storePosition(cyNode.id());
this.storePosition(target.id());
setTimeout(() => {
this.props.setActiveSelection([e.target.id()]);
setActiveSelection([target.id()]);
});
} else {
// Free outside of canvas
this.reposition = {};
this.props.setActiveSelection([]);
setActiveSelection([]);
}
});
this.cy.on('vclick', (e) => {
this.reposition = {};
const {setActiveSelection, gmeClient} = this.props;
const {createPointer} = this.state;
if (typeof e.target.id === 'function') {
if (createPointer && createPointer.target === e.target.id()) {
this.setState({createPointer: null});
gmeClient.setPointer(createPointer.nodeId, createPointer.ptrName, createPointer.target);
} else {
// console.log('vclick', e.target.id(), JSON.stringify(this.reposition));
this.setState({
showNodeMenu: {
......@@ -445,13 +482,33 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
y: e.originalEvent.clientY,
},
},
createPointer: null,
});
}
setTimeout(() => {
this.props.setActiveSelection([e.target.id()]);
setActiveSelection([e.target.id()]);
});
} else {
this.props.setActiveSelection([]);
setActiveSelection([]);
}
});
this.cy.on('mouseover', 'node', (e) => {
const {createPointer} = this.state;
const {gmeClient} = this.props;
if (createPointer && typeof e.target.id === 'function') {
const gmeNode = gmeClient.getNode(e.target.id());
if (gmeNode && gmeNode.isValidTargetOf(createPointer.nodeId, createPointer.ptrName)
&& createPointer.target !== e.target.id()) {
this.setState({
createPointer: {
nodeId: createPointer.nodeId,
prtName: createPointer.prtName,
target: e.target.id(),
},
});
}
}
});
}
......@@ -460,6 +517,7 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
const {
showNodeMenu,
showFilterSelector,
activeFilters,
} = this.state;
const {
readOnly,
......@@ -470,6 +528,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
activeNode,
activeSelection,
nodes,
setActiveNode,
validFilters,
} = this.props;
const cytoData = this.getCytoscapeElements();
......@@ -477,7 +537,11 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
const activeNodeName = nodes[activeNode] ? nodes[activeNode].attributes.name : '';
return (
<div style={{width: '100%', height: '100%'}}>
<div style={{
width: '100%',
height: '100%',
}}
>
<div style={{
position: 'absolute',
top: 5,
......@@ -487,7 +551,10 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
>
{readOnly ? <LockIcon/> : null}
<span
style={{fontSize: 24, color: isActivePanel ? null : 'grey'}}
style={{
fontSize: 24,
color: isActivePanel ? null : 'grey',
}}
>
{activeNodeName}
<Tooltip id="tooltip-auto-layout" title="Run auto-layout. Warning this will move everything!">
......@@ -551,7 +618,8 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
layout={{name: 'preset'/* preset dagre */}}
style={DEFAULT_STYLES.concat(cytoData.style)}
/>
{showNodeMenu && showNodeMenu.id === activeSelection[0] ?
{showNodeMenu && showNodeMenu.id === activeSelection[0]
? (
<ContextMenu
gmeClient={gmeClient}
nodeId={showNodeMenu.id}
......@@ -561,13 +629,13 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
onClose={() => {
this.setState({showNodeMenu: null});
}}
setActiveNode={this.props.setActiveNode}
/> : null}
setActiveNode={setActiveNode}
/>) : null}
<FilterSelector
open={showFilterSelector}
validItems={this.props.validFilters}
activeItems={this.state.activeFilters}
validItems={validFilters}
activeItems={activeFilters}
handleToggle={this.toggleFilter}
onClose={(() => this.setState({showFilterSelector: false}))}
/>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment