diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9a04aa22..4d1a14d4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -187,6 +187,7 @@ import { DefaultConsoleService } from './services/settings/default-console.servi import { NodeCreatedLabelStylesFixer } from './components/project-map/helpers/node-created-label-styles-fixer'; import { NonNegativeValidator } from './validators/non-negative-validator'; import { RotationValidator } from './validators/rotation-validator'; +import { MapSettingService } from './services/mapsettings.service'; import { HelpComponent } from './components/help/help.component'; if (environment.production) { @@ -382,7 +383,8 @@ if (environment.production) { DefaultConsoleService, NodeCreatedLabelStylesFixer, NonNegativeValidator, - RotationValidator + RotationValidator, + MapSettingService ], entryComponents: [ AddServerDialogComponent, diff --git a/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts b/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts index 53193ad5..fb362b7e 100644 --- a/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts +++ b/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts @@ -21,6 +21,7 @@ import { MapLabel } from '../../models/map/map-label'; import { MapLinkNode } from '../../models/map/map-link-node'; import { select } from 'd3-selection'; import { MapLink } from '../../models/map/map-link'; +import { MapSettingService } from '../../../services/mapsettings.service'; describe('DraggableSelectionComponent', () => { let component: DraggableSelectionComponent; @@ -121,7 +122,8 @@ describe('DraggableSelectionComponent', () => { { provide: NodesEventSource, useValue: nodesEventSourceStub }, { provide: DrawingsEventSource, useValue: drawingsEventSourceStub }, { provide: GraphDataManager, useValue: mockedGraphDataManager }, - { provide: LinksEventSource, useValue: linksEventSourceStub } + { provide: LinksEventSource, useValue: linksEventSourceStub }, + { provide: MapSettingService, useClass: MapSettingService } ], declarations: [DraggableSelectionComponent] }).compileComponents(); diff --git a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts index 37dce99b..5c284725 100644 --- a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts +++ b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts @@ -17,6 +17,7 @@ import { LabelWidget } from '../../widgets/label'; import { InterfaceLabelWidget } from '../../widgets/interface-label'; import { MapLinkNode } from '../../models/map/map-link-node'; import { LinksEventSource } from '../../events/links-event-source'; +import { MapSettingService } from '../../../services/mapsettings.service'; @Component({ selector: 'app-draggable-selection', @@ -27,6 +28,8 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { private start: Subscription; private drag: Subscription; private end: Subscription; + private mapSettingsSubscription: Subscription; + private isMapLocked: boolean = false; @Input('svg') svg: SVGSVGElement; @@ -40,12 +43,17 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { private nodesEventSource: NodesEventSource, private drawingsEventSource: DrawingsEventSource, private graphDataManager: GraphDataManager, - private linksEventSource: LinksEventSource + private linksEventSource: LinksEventSource, + private mapSettingsService: MapSettingService ) {} ngOnInit() { const svg = select(this.svg); + this.mapSettingsService.isMapLocked.subscribe((value) => { + this.isMapLocked = value; + }); + this.start = merge( this.nodesWidget.draggable.start, this.drawingsWidget.draggable.start, @@ -84,75 +92,77 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.labelWidget.draggable.drag, this.interfaceWidget.draggable.drag ).subscribe((evt: DraggableDrag) => { - const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); - // update nodes - selectedNodes.forEach((node: MapNode) => { - node.x += evt.dx; - node.y += evt.dy; + if (!this.isMapLocked) { + const selected = this.selectionManager.getSelected(); + const selectedNodes = selected.filter(item => item instanceof MapNode); + // update nodes + selectedNodes.forEach((node: MapNode) => { + node.x += evt.dx; + node.y += evt.dy; - this.nodesWidget.redrawNode(svg, node); + this.nodesWidget.redrawNode(svg, node); - const links = this.graphDataManager - .getLinks() - .filter( - link => - (link.target !== undefined && link.target.id === node.id) || - (link.source !== undefined && link.source.id === node.id) - ); - - links.forEach(link => { - this.linksWidget.redrawLink(svg, link); - }); - }); - - // update drawings - selected - .filter(item => item instanceof MapDrawing) - .forEach((drawing: MapDrawing) => { - drawing.x += evt.dx; - drawing.y += evt.dy; - this.drawingsWidget.redrawDrawing(svg, drawing); - }); - - // update labels - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - - const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; - node.label.x += evt.dx; - node.label.y += evt.dy; - this.labelWidget.redrawLabel(svg, label); - }); - - // update interface labels - selected - .filter(item => item instanceof MapLinkNode) - .forEach((interfaceLabel: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - - const link = this.graphDataManager + const links = this.graphDataManager .getLinks() - .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; - if (link.nodes[0].id === interfaceLabel.id) { - link.nodes[0].label.x += evt.dx; - link.nodes[0].label.y += evt.dy; - } - if (link.nodes[1].id === interfaceLabel.id) { - link.nodes[1].label.x += evt.dx; - link.nodes[1].label.y += evt.dy; - } + .filter( + link => + (link.target !== undefined && link.target.id === node.id) || + (link.source !== undefined && link.source.id === node.id) + ); - this.linksWidget.redrawLink(svg, link); + links.forEach(link => { + this.linksWidget.redrawLink(svg, link); + }); }); + + // update drawings + selected + .filter(item => item instanceof MapDrawing) + .forEach((drawing: MapDrawing) => { + drawing.x += evt.dx; + drawing.y += evt.dy; + this.drawingsWidget.redrawDrawing(svg, drawing); + }); + + // update labels + selected + .filter(item => item instanceof MapLabel) + .forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + + const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; + node.label.x += evt.dx; + node.label.y += evt.dy; + this.labelWidget.redrawLabel(svg, label); + }); + + // update interface labels + selected + .filter(item => item instanceof MapLinkNode) + .forEach((interfaceLabel: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + + const link = this.graphDataManager + .getLinks() + .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; + if (link.nodes[0].id === interfaceLabel.id) { + link.nodes[0].label.x += evt.dx; + link.nodes[0].label.y += evt.dy; + } + if (link.nodes[1].id === interfaceLabel.id) { + link.nodes[1].label.x += evt.dx; + link.nodes[1].label.y += evt.dy; + } + + this.linksWidget.redrawLink(svg, link); + }); + } }); this.end = merge( @@ -161,39 +171,41 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.labelWidget.draggable.end, this.interfaceWidget.draggable.end ).subscribe((evt: DraggableEnd) => { - const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); + if (!this.isMapLocked) { + const selected = this.selectionManager.getSelected(); + const selectedNodes = selected.filter(item => item instanceof MapNode); - selectedNodes.forEach((item: MapNode) => { - this.nodesEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); - }); - - selected - .filter(item => item instanceof MapDrawing) - .forEach((item: MapDrawing) => { - this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); + selectedNodes.forEach((item: MapNode) => { + this.nodesEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); }); - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } + selected + .filter(item => item instanceof MapDrawing) + .forEach((item: MapDrawing) => { + this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); + }); - this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); + selected + .filter(item => item instanceof MapLabel) + .forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } - selected - .filter(item => item instanceof MapLinkNode) - .forEach((label: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); + this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); + + selected + .filter(item => item instanceof MapLinkNode) + .forEach((label: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); + } }); } @@ -201,5 +213,6 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.start.unsubscribe(); this.drag.unsubscribe(); this.end.unsubscribe(); + this.mapSettingsSubscription.unsubscribe(); } } diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 0ed30a68..745d9fb3 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -148,6 +148,16 @@ /> + + diff --git a/src/app/components/project-map/project-map.component.scss b/src/app/components/project-map/project-map.component.scss index 0f9a00fe..aef6907e 100644 --- a/src/app/components/project-map/project-map.component.scss +++ b/src/app/components/project-map/project-map.component.scss @@ -80,7 +80,7 @@ g.node:hover { } .extended { - width: 640px !important; + width: 720px !important; height: 100%; overflow: hidden; } diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index b55dc225..0e3eedee 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -45,6 +45,7 @@ import { CapturingSettings } from '../../models/capturingSettings'; import { LinkWidget } from '../../cartography/widgets/link'; import { MapScaleService } from '../../services/mapScale.service'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; +import { MapSettingService } from '../../services/mapsettings.service'; export class MockedProgressService { public activate() {} @@ -186,6 +187,7 @@ describe('ProjectMapComponent', () => { let nodesDataSource = new MockedNodesDataSource(); let linksDataSource = new MockedLinksDataSource(); let nodeCreatedLabelStylesFixer; + let mapSettingService = new MapSettingService(); beforeEach(async(() => { nodeCreatedLabelStylesFixer = { @@ -223,6 +225,8 @@ describe('ProjectMapComponent', () => { provide: RecentlyOpenedProjectService, useClass: RecentlyOpenedProjectService }, + { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, + { provide: MapSettingService, useValue: mapSettingService }, { provide: MapScaleService }, { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer} ], @@ -263,4 +267,22 @@ describe('ProjectMapComponent', () => { expect(component.resetDrawToolChoice).toHaveBeenCalled(); }); + + it('should call map settings service when lock value was changed', () => { + spyOn(mapSettingService, 'changeMapLockValue'); + + component.changeLockValue(); + + expect(mapSettingService.changeMapLockValue).toHaveBeenCalled(); + }); + + it('should call map settings service with proper value', () => { + spyOn(mapSettingService, 'changeMapLockValue'); + + component.changeLockValue(); + expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(true); + + component.changeLockValue(); + expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(false); + }); }); diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index a25e5472..652dab67 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -46,6 +46,7 @@ import { MovingEventSource } from '../../cartography/events/moving-event-source' import { LinkWidget } from '../../cartography/widgets/link'; import { MapScaleService } from '../../services/mapScale.service'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; +import { MapSettingService } from '../../services/mapsettings.service'; @Component({ @@ -80,6 +81,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { isTextChosen: false, visibility: false }; + protected isLocked: boolean = false; private inReadOnlyMode = false; @@ -112,6 +114,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private selectionManager: SelectionManager, private selectionTool: SelectionTool, private recentlyOpenedProjectService: RecentlyOpenedProjectService, + private mapSettingsService: MapSettingService, private movingEventSource: MovingEventSource, private mapScaleService: MapScaleService, private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer @@ -428,6 +431,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { imageToUpload.src = window.URL.createObjectURL(file); } + public changeLockValue() { + this.isLocked = !this.isLocked; + this.mapSettingsService.changeMapLockValue(this.isLocked); + } + public ngOnDestroy() { this.drawingsDataSource.clear(); this.nodesDataSource.clear(); diff --git a/src/app/services/mapsettings.service.ts b/src/app/services/mapsettings.service.ts new file mode 100644 index 00000000..640c5671 --- /dev/null +++ b/src/app/services/mapsettings.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from "@angular/core"; +import { Subject } from 'rxjs'; + +@Injectable() +export class MapSettingService { + public isMapLocked = new Subject(); + + constructor() {} + + changeMapLockValue(value: boolean) { + this.isMapLocked.next(value); + } +}