Initial commit
Some checks failed
Gradle Build / build (push) Has been cancelled

This commit is contained in:
CaiXiang
2024-11-30 18:36:13 +08:00
commit aa56926258
2134 changed files with 232943 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
apply from: "${rootDir}/gradle/java-project.gradle"
apply from: "${rootDir}/gradle/java-codequality.gradle"
apply from: "${rootDir}/gradle/guice-application.gradle"
apply from: "${rootDir}/gradle/publishing-java.gradle"
if (!hasProperty('mainClass')) {
ext.mainClass = 'org.opentcs.modeleditor.RunModelEditor'
}
application.mainClass = ext.mainClass
ext.collectableDistDir = new File(buildDir, 'install')
dependencies {
api project(':opentcs-common')
api project(':opentcs-impl-configuration-gestalt')
api project(':opentcs-plantoverview-common')
api project(':opentcs-plantoverview-themes-default')
runtimeOnly group: 'org.slf4j', name: 'slf4j-jdk14', version: '2.0.16'
}
compileJava {
options.compilerArgs << "-Xlint:-rawtypes"
}
distributions {
main {
contents {
from "${sourceSets.main.resources.srcDirs[0]}/org/opentcs/modeleditor/distribution"
}
}
}
// For now, we're using hand-crafted start scripts, so disable the application
// plugin's start script generation.
startScripts.enabled = false
distTar.enabled = false
task release {
dependsOn build
dependsOn installDist
}
run {
systemProperties(['java.util.logging.config.file':'./config/logging.config',\
'sun.java2d.d3d':'false',\
'opentcs.base':'.',\
'opentcs.home':'.',\
'opentcs.configuration.reload.interval':'10000',\
'opentcs.configuration.provider':'gestalt'])
jvmArgs('-XX:-OmitStackTraceInFastThrow',\
'-splash:bin/splash-image.gif')
}

View File

@@ -0,0 +1,40 @@
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAnnotationArgs=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineMethodParams=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAfterDotInChainedMethodCalls=false
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineDisjunctiveCatchTypes=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineFor=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineImplements=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapFor=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.sortMembersByVisibility=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.visibilityOrder=PUBLIC;PROTECTED;DEFAULT;PRIVATE
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeFinallyOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapMethodParams=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.enable-indent=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineArrayInit=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineCallArgs=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapDisjunctiveCatchTypes=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.keepGettersAndSettersTogether=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsList=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsKeyword=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsKeyword=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classMembersOrder=STATIC FIELD;FIELD;STATIC_INIT;CONSTRUCTOR;INSTANCE_INIT;STATIC METHOD;METHOD;STATIC CLASS;CLASS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapEnumConstants=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapCommentText=false
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsList=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAssert=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder=*
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.continuationIndentSize=4
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeElseOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeCatchOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineAnnotationArgs=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineTryResources=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.preserveNewLinesInComments=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineParenthesized=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineThrows=true
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=100
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true
netbeans.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0

View File

@@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
#handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = ./log/opentcs-modeleditor.%g.log
java.util.logging.FileHandler.limit = 500000
java.util.logging.FileHandler.count = 10
#java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.formatter = org.opentcs.util.logging.SingleLineFormatter
java.util.logging.FileHandler.append = true
java.util.logging.FileHandler.level = FINE
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
#java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.formatter = org.opentcs.util.logging.SingleLineFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
#com.xyz.foo.level = SEVERE
# Logging configuration for single classes. Remember that you might also have to
# adjust handler levels!
#org.opentcs.guing.*.level = FINE
#org.opentcs.access.rmi.ProxyInvocationHandler.level = FINE

View File

@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0

View File

@@ -0,0 +1,810 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model version="6.0.0" name="Demo-01">
<point name="Point-0001" positionX="31000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0001 --- Point-0002"/>
<outgoingPath name="Point-0001 --- Point-0003"/>
<pointLayout positionX="31000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0002" positionX="25000" positionY="9000" positionZ="0" vehicleOrientationAngle="NaN" type="PARK_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0002 --- Point-0004"/>
<outgoingPath name="Point-0002 --- Point-0014"/>
<property name="tcs:parkingPositionPriority" value="4"/>
<pointLayout positionX="25000" positionY="9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0003" positionX="31000" positionY="5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0003 --- Point-0004"/>
<outgoingPath name="Point-0003 --- Point-0005"/>
<pointLayout positionX="31000" positionY="5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0004" positionX="25000" positionY="3000" positionZ="0" vehicleOrientationAngle="NaN" type="PARK_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0004 --- Point-0006"/>
<outgoingPath name="Point-0004 --- Point-0008"/>
<property name="tcs:parkingPositionPriority" value="3"/>
<pointLayout positionX="25000" positionY="3000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0005" positionX="31000" positionY="-1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0005 --- Point-0006"/>
<outgoingPath name="Point-0005 --- Point-0007"/>
<pointLayout positionX="31000" positionY="-1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0006" positionX="25000" positionY="-3000" positionZ="0" vehicleOrientationAngle="NaN" type="PARK_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0006 --- Point-0009"/>
<outgoingPath name="Point-0006 --- Point-0010"/>
<property name="tcs:parkingPositionPriority" value="2"/>
<pointLayout positionX="25000" positionY="-3000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0007" positionX="31000" positionY="-7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0007 --- Point-0010"/>
<pointLayout positionX="31000" positionY="-7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0008" positionX="22000" positionY="1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0008 --- Point-0009"/>
<pointLayout positionX="22000" positionY="1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0009" positionX="22000" positionY="-5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0009 --- Point-0011"/>
<pointLayout positionX="22000" positionY="-5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0010" positionX="25000" positionY="-9000" positionZ="0" vehicleOrientationAngle="NaN" type="PARK_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0010 --- Point-0011"/>
<property name="tcs:parkingPositionPriority" value="1"/>
<pointLayout positionX="25000" positionY="-9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0011" positionX="22000" positionY="-11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0011 --- Point-0013"/>
<pointLayout positionX="22000" positionY="-11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0013" positionX="18000" positionY="-15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0013 --- Point-0015"/>
<outgoingPath name="Point-0013 --- Point-0018"/>
<outgoingPath name="Point-0013 --- Point-0057"/>
<pointLayout positionX="18000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0014" positionX="22000" positionY="7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0014 --- Point-0008"/>
<pointLayout positionX="22000" positionY="7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0015" positionX="7850" positionY="-11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0015 --- Point-0016"/>
<outgoingPath name="Point-0015 --- Point-0050"/>
<pointLayout positionX="7850" positionY="-11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0016" positionX="-2000" positionY="-11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0016 --- Point-0017"/>
<outgoingPath name="Point-0016 --- Point-0022"/>
<outgoingPath name="Point-0016 --- Point-0046"/>
<pointLayout positionX="-2000" positionY="-11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0017" positionX="-12000" positionY="-15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0017 --- Point-0020"/>
<pointLayout positionX="-12000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0018" positionX="8000" positionY="-15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0018 --- Point-0019"/>
<pointLayout positionX="8000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0019" positionX="-2000" positionY="-15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0019 --- Point-0017"/>
<outgoingPath name="Point-0019 --- Point-0022"/>
<pointLayout positionX="-2000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0020" positionX="-20000" positionY="-12000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0020 --- Point-0021"/>
<pointLayout positionX="-20000" positionY="-12000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0021" positionX="-24000" positionY="-8000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0021 --- Point-0023"/>
<pointLayout positionX="-24000" positionY="-8000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0022" positionX="-12000" positionY="-11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0022 --- Point-0056"/>
<pointLayout positionX="-12000" positionY="-11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0023" positionX="-24000" positionY="-4000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0023 --- Point-0024"/>
<pointLayout positionX="-24000" positionY="-4000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0024" positionX="-24000" positionY="1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0024 --- Point-0025"/>
<pointLayout positionX="-24000" positionY="1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0025" positionX="-24000" positionY="6000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0025 --- Point-0026"/>
<outgoingPath name="Point-0025 --- Point-0030"/>
<outgoingPath name="Point-0025 --- Point-0042"/>
<pointLayout positionX="-24000" positionY="6000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0026" positionX="-24000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0026 --- Point-0027"/>
<pointLayout positionX="-24000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0027" positionX="-20000" positionY="15000" positionZ="0" vehicleOrientationAngle="NaN" type="PARK_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0027 --- Point-0032"/>
<property name="tcs:parkingPositionPriority" value="4"/>
<pointLayout positionX="-20000" positionY="15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0028" positionX="-8000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0028 --- Point-0029"/>
<pointLayout positionX="-8000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0029" positionX="-2000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0029 --- Point-0035"/>
<pointLayout positionX="-2000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0030" positionX="-20000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0030 --- Point-0032"/>
<pointLayout positionX="-20000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0032" positionX="-13000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0032 --- Point-0028"/>
<pointLayout positionX="-13000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0033" positionX="24000" positionY="15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0033 --- Point-0001"/>
<pointLayout positionX="24000" positionY="15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0034" positionX="16000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0034 --- Point-0014"/>
<outgoingPath name="Point-0034 --- Point-0033"/>
<pointLayout positionX="16000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0035" positionX="3000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0035 --- Point-0036"/>
<pointLayout positionX="3000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0036" positionX="9000" positionY="11000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0036 --- Point-0034"/>
<pointLayout positionX="9000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0037" positionX="-8000" positionY="7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0037 --- Point-0028"/>
<pointLayout positionX="-8000" positionY="7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0038" positionX="-2000" positionY="7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0038 --- Point-0037"/>
<pointLayout positionX="-2000" positionY="7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0039" positionX="-13000" positionY="9000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0039 --- Point-0040"/>
<pointLayout positionX="-13000" positionY="9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0040" positionX="-8000" positionY="9000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0040 --- Point-0041"/>
<pointLayout positionX="-8000" positionY="9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0041" positionX="-2000" positionY="9000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0041 --- Point-0035"/>
<pointLayout positionX="-2000" positionY="9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0042" positionX="-20000" positionY="9000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0042 --- Point-0039"/>
<pointLayout positionX="-20000" positionY="9000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0043" positionX="-11000" positionY="5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0043 --- Point-0052"/>
<pointLayout positionX="-11000" positionY="5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0044" positionX="7000" positionY="5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0044 --- Point-0047"/>
<pointLayout positionX="7000" positionY="5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0045" positionX="7000" positionY="-7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0045 --- Point-0016"/>
<outgoingPath name="Point-0045 --- Point-0055"/>
<pointLayout positionX="7000" positionY="-7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0046" positionX="-11000" positionY="-7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0046 --- Point-0054"/>
<pointLayout positionX="-11000" positionY="-7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0047" positionX="11000" positionY="1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0047 --- Point-0053"/>
<pointLayout positionX="11000" positionY="1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0048" positionX="-15000" positionY="1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0048 --- Point-0043"/>
<pointLayout positionX="-15000" positionY="1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0049" positionX="2000" positionY="3000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0049 --- Point-0038"/>
<pointLayout positionX="2000" positionY="3000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0050" positionX="2000" positionY="-5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0050 --- Point-0051"/>
<pointLayout positionX="2000" positionY="-5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0051" positionX="2000" positionY="-1000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0051 --- Point-0049"/>
<pointLayout positionX="2000" positionY="-1000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0052" positionX="-2000" positionY="5000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0052 --- Point-0044"/>
<pointLayout positionX="-2000" positionY="5000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0053" positionX="11000" positionY="-3000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0053 --- Point-0045"/>
<pointLayout positionX="11000" positionY="-3000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0054" positionX="-15000" positionY="-3000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0054 --- Point-0048"/>
<pointLayout positionX="-15000" positionY="-3000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0055" positionX="-2000" positionY="-7000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0055 --- Point-0046"/>
<pointLayout positionX="-2000" positionY="-7000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0056" positionX="-20000" positionY="-8000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0056 --- Point-0023"/>
<pointLayout positionX="-20000" positionY="-8000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0057" positionX="8000" positionY="-21000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0057 --- Point-0058"/>
<pointLayout positionX="8000" positionY="-21000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0058" positionX="-2000" positionY="-21000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0058 --- Point-0059"/>
<pointLayout positionX="-2000" positionY="-21000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0059" positionX="-12000" positionY="-21000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0059 --- Point-0060"/>
<pointLayout positionX="-12000" positionY="-21000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0060" positionX="-20000" positionY="-21000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0060 --- Point-0061"/>
<pointLayout positionX="-20000" positionY="-21000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<point name="Point-0061" positionX="-24000" positionY="-15000" positionZ="0" vehicleOrientationAngle="NaN" type="HALT_POSITION">
<maxVehicleBoundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<outgoingPath name="Point-0061 --- Point-0021"/>
<pointLayout positionX="-24000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" layerId="0"/>
</point>
<path name="Point-0001 --- Point-0002" sourcePoint="Point-0001" destinationPoint="Point-0002" length="4383" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="610" y="-180"/>
<controlPoint x="610" y="-180"/>
</pathLayout>
</path>
<path name="Point-0001 --- Point-0003" sourcePoint="Point-0001" destinationPoint="Point-0003" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0002 --- Point-0004" sourcePoint="Point-0002" destinationPoint="Point-0004" length="6000" maxVelocity="500" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0002 --- Point-0014" sourcePoint="Point-0002" destinationPoint="Point-0014" length="4267" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="450" y="-174"/>
<controlPoint x="450" y="-174"/>
</pathLayout>
</path>
<path name="Point-0003 --- Point-0004" sourcePoint="Point-0003" destinationPoint="Point-0004" length="4336" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="610" y="-60"/>
<controlPoint x="610" y="-60"/>
</pathLayout>
</path>
<path name="Point-0003 --- Point-0005" sourcePoint="Point-0003" destinationPoint="Point-0005" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0004 --- Point-0006" sourcePoint="Point-0004" destinationPoint="Point-0006" length="6000" maxVelocity="500" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0004 --- Point-0008" sourcePoint="Point-0004" destinationPoint="Point-0008" length="4302" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="449" y="-54"/>
<controlPoint x="449" y="-54"/>
</pathLayout>
</path>
<path name="Point-0005 --- Point-0006" sourcePoint="Point-0005" destinationPoint="Point-0006" length="4423" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="610" y="60"/>
<controlPoint x="610" y="60"/>
</pathLayout>
</path>
<path name="Point-0005 --- Point-0007" sourcePoint="Point-0005" destinationPoint="Point-0007" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0006 --- Point-0009" sourcePoint="Point-0006" destinationPoint="Point-0009" length="4302" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="449" y="65"/>
<controlPoint x="449" y="65"/>
</pathLayout>
</path>
<path name="Point-0006 --- Point-0010" sourcePoint="Point-0006" destinationPoint="Point-0010" length="6000" maxVelocity="500" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0007 --- Point-0010" sourcePoint="Point-0007" destinationPoint="Point-0010" length="4379" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="610" y="180"/>
<controlPoint x="610" y="180"/>
</pathLayout>
</path>
<path name="Point-0008 --- Point-0009" sourcePoint="Point-0008" destinationPoint="Point-0009" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0009 --- Point-0011" sourcePoint="Point-0009" destinationPoint="Point-0011" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0010 --- Point-0011" sourcePoint="Point-0010" destinationPoint="Point-0011" length="4302" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="449" y="185"/>
<controlPoint x="449" y="185"/>
</pathLayout>
</path>
<path name="Point-0011 --- Point-0013" sourcePoint="Point-0011" destinationPoint="Point-0013" length="6881" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="429" y="289"/>
<controlPoint x="429" y="289"/>
</pathLayout>
</path>
<path name="Point-0013 --- Point-0015" sourcePoint="Point-0013" destinationPoint="Point-0015" length="11449" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="253" y="287"/>
<controlPoint x="213" y="233"/>
</pathLayout>
</path>
<path name="Point-0013 --- Point-0018" sourcePoint="Point-0013" destinationPoint="Point-0018" length="9381" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0013 --- Point-0057" sourcePoint="Point-0013" destinationPoint="Point-0057" length="11638" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="280" y="320"/>
<controlPoint x="250" y="410"/>
</pathLayout>
</path>
<path name="Point-0014 --- Point-0008" sourcePoint="Point-0014" destinationPoint="Point-0008" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0015 --- Point-0016" sourcePoint="Point-0015" destinationPoint="Point-0016" length="9241" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0015 --- Point-0050" sourcePoint="Point-0015" destinationPoint="Point-0050" length="9861" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="60" y="199"/>
<controlPoint x="60" y="199"/>
</pathLayout>
</path>
<path name="Point-0016 --- Point-0017" sourcePoint="Point-0016" destinationPoint="Point-0017" length="11166" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-126" y="233"/>
<controlPoint x="-175" y="288"/>
</pathLayout>
</path>
<path name="Point-0016 --- Point-0022" sourcePoint="Point-0016" destinationPoint="Point-0022" length="9381" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0016 --- Point-0046" sourcePoint="Point-0016" destinationPoint="Point-0046" length="10093" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-101" y="209"/>
<controlPoint x="-159" y="153"/>
</pathLayout>
</path>
<path name="Point-0017 --- Point-0020" sourcePoint="Point-0017" destinationPoint="Point-0020" length="8705" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-293" y="292"/>
<controlPoint x="-343" y="250"/>
</pathLayout>
</path>
<path name="Point-0018 --- Point-0019" sourcePoint="Point-0018" destinationPoint="Point-0019" length="9381" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0019 --- Point-0017" sourcePoint="Point-0019" destinationPoint="Point-0017" length="9381" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0019 --- Point-0022" sourcePoint="Point-0019" destinationPoint="Point-0022" length="11062" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-113" y="288"/>
<controlPoint x="-169" y="233"/>
</pathLayout>
</path>
<path name="Point-0020 --- Point-0021" sourcePoint="Point-0020" destinationPoint="Point-0021" length="6802" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-465" y="230"/>
<controlPoint x="-465" y="230"/>
</pathLayout>
</path>
<path name="Point-0021 --- Point-0023" sourcePoint="Point-0021" destinationPoint="Point-0023" length="3752" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0022 --- Point-0056" sourcePoint="Point-0022" destinationPoint="Point-0056" length="8680" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-296" y="209"/>
<controlPoint x="-344" y="169"/>
</pathLayout>
</path>
<path name="Point-0023 --- Point-0024" sourcePoint="Point-0023" destinationPoint="Point-0024" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0024 --- Point-0025" sourcePoint="Point-0024" destinationPoint="Point-0025" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0025 --- Point-0026" sourcePoint="Point-0025" destinationPoint="Point-0026" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0025 --- Point-0030" sourcePoint="Point-0025" destinationPoint="Point-0030" length="7695" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-467" y="-205"/>
<controlPoint x="-467" y="-205"/>
</pathLayout>
</path>
<path name="Point-0025 --- Point-0042" sourcePoint="Point-0025" destinationPoint="Point-0042" length="5992" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-469" y="-169"/>
<controlPoint x="-469" y="-169"/>
</pathLayout>
</path>
<path name="Point-0026 --- Point-0027" sourcePoint="Point-0026" destinationPoint="Point-0027" length="6841" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-468" y="-287"/>
<controlPoint x="-468" y="-287"/>
</pathLayout>
</path>
<path name="Point-0027 --- Point-0032" sourcePoint="Point-0027" destinationPoint="Point-0032" length="8657" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-345" y="-287"/>
<controlPoint x="-317" y="-233"/>
</pathLayout>
</path>
<path name="Point-0028 --- Point-0029" sourcePoint="Point-0028" destinationPoint="Point-0029" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0029 --- Point-0035" sourcePoint="Point-0029" destinationPoint="Point-0035" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0030 --- Point-0032" sourcePoint="Point-0030" destinationPoint="Point-0032" length="6567" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0032 --- Point-0028" sourcePoint="Point-0032" destinationPoint="Point-0028" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0033 --- Point-0001" sourcePoint="Point-0033" destinationPoint="Point-0001" length="6922" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="600" y="-300"/>
<controlPoint x="600" y="-300"/>
</pathLayout>
</path>
<path name="Point-0034 --- Point-0014" sourcePoint="Point-0034" destinationPoint="Point-0014" length="8731" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="423" y="-211"/>
<controlPoint x="423" y="-211"/>
</pathLayout>
</path>
<path name="Point-0034 --- Point-0033" sourcePoint="Point-0034" destinationPoint="Point-0033" length="9481" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="374" y="-231"/>
<controlPoint x="408" y="-285"/>
</pathLayout>
</path>
<path name="Point-0035 --- Point-0036" sourcePoint="Point-0035" destinationPoint="Point-0036" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0036 --- Point-0034" sourcePoint="Point-0036" destinationPoint="Point-0034" length="6567" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0037 --- Point-0028" sourcePoint="Point-0037" destinationPoint="Point-0028" length="7582" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-209" y="-154"/>
<controlPoint x="-210" y="-202"/>
</pathLayout>
</path>
<path name="Point-0038 --- Point-0037" sourcePoint="Point-0038" destinationPoint="Point-0037" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0039 --- Point-0040" sourcePoint="Point-0039" destinationPoint="Point-0040" length="4690" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0040 --- Point-0041" sourcePoint="Point-0040" destinationPoint="Point-0041" length="5629" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0041 --- Point-0035" sourcePoint="Point-0041" destinationPoint="Point-0035" length="5734" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="15" y="-186"/>
<controlPoint x="31" y="-213"/>
</pathLayout>
</path>
<path name="Point-0042 --- Point-0039" sourcePoint="Point-0042" destinationPoint="Point-0039" length="6567" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0043 --- Point-0052" sourcePoint="Point-0043" destinationPoint="Point-0052" length="8443" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0044 --- Point-0047" sourcePoint="Point-0044" destinationPoint="Point-0047" length="6881" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="209" y="-88"/>
<controlPoint x="209" y="-88"/>
</pathLayout>
</path>
<path name="Point-0045 --- Point-0016" sourcePoint="Point-0045" destinationPoint="Point-0016" length="10170" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="79" y="154"/>
<controlPoint x="30" y="208"/>
</pathLayout>
</path>
<path name="Point-0045 --- Point-0055" sourcePoint="Point-0045" destinationPoint="Point-0055" length="8443" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0046 --- Point-0054" sourcePoint="Point-0046" destinationPoint="Point-0054" length="6800" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-288" y="127"/>
<controlPoint x="-288" y="127"/>
</pathLayout>
</path>
<path name="Point-0047 --- Point-0053" sourcePoint="Point-0047" destinationPoint="Point-0053" length="3752" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0048 --- Point-0043" sourcePoint="Point-0048" destinationPoint="Point-0043" length="6722" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-287" y="-85"/>
<controlPoint x="-287" y="-85"/>
</pathLayout>
</path>
<path name="Point-0049 --- Point-0038" sourcePoint="Point-0049" destinationPoint="Point-0038" length="6882" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="30" y="-127"/>
<controlPoint x="30" y="-127"/>
</pathLayout>
</path>
<path name="Point-0050 --- Point-0051" sourcePoint="Point-0050" destinationPoint="Point-0051" length="3752" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0051 --- Point-0049" sourcePoint="Point-0051" destinationPoint="Point-0049" length="3752" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0052 --- Point-0044" sourcePoint="Point-0052" destinationPoint="Point-0044" length="8443" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0053 --- Point-0045" sourcePoint="Point-0053" destinationPoint="Point-0045" length="6922" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="209" y="130"/>
<controlPoint x="209" y="130"/>
</pathLayout>
</path>
<path name="Point-0054 --- Point-0048" sourcePoint="Point-0054" destinationPoint="Point-0048" length="3752" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0055 --- Point-0046" sourcePoint="Point-0055" destinationPoint="Point-0046" length="8443" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0056 --- Point-0023" sourcePoint="Point-0056" destinationPoint="Point-0023" length="6882" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-467" y="150"/>
<controlPoint x="-467" y="150"/>
</pathLayout>
</path>
<path name="Point-0057 --- Point-0058" sourcePoint="Point-0057" destinationPoint="Point-0058" length="10000" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<peripheralOperation completionRequired="true" executionTrigger="AFTER_ALLOCATION" locationName="Fire door 01" name="Open"/>
<peripheralOperation completionRequired="false" executionTrigger="AFTER_MOVEMENT" locationName="Fire door 01" name="Close"/>
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0058 --- Point-0059" sourcePoint="Point-0058" destinationPoint="Point-0059" length="10000" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0059 --- Point-0060" sourcePoint="Point-0059" destinationPoint="Point-0060" length="8000" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<path name="Point-0060 --- Point-0061" sourcePoint="Point-0060" destinationPoint="Point-0061" length="7211" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="BEZIER" layerId="0">
<controlPoint x="-470" y="410"/>
<controlPoint x="-470" y="370"/>
</pathLayout>
</path>
<path name="Point-0061 --- Point-0021" sourcePoint="Point-0061" destinationPoint="Point-0021" length="8000" maxVelocity="1000" maxReverseVelocity="0" locked="false">
<pathLayout connectionType="DIRECT" layerId="0"/>
</path>
<vehicle name="Vehicle-01" energyLevelCritical="30" energyLevelGood="90" energyLevelFullyRecharged="95" energyLevelSufficientlyRecharged="45" maxVelocity="1000" maxReverseVelocity="1000">
<boundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<property name="loopback:loadOperation" value="Load cargo"/>
<property name="loopback:unloadOperation" value="Unload cargo"/>
<vehicleLayout color="#FF0000"/>
</vehicle>
<vehicle name="Vehicle-02" energyLevelCritical="30" energyLevelGood="90" energyLevelFullyRecharged="95" energyLevelSufficientlyRecharged="45" maxVelocity="1000" maxReverseVelocity="1000">
<boundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<property name="loopback:loadOperation" value="Load cargo"/>
<property name="loopback:unloadOperation" value="Unload cargo"/>
<vehicleLayout color="#33FF00"/>
</vehicle>
<vehicle name="Vehicle-03" energyLevelCritical="30" energyLevelGood="90" energyLevelFullyRecharged="95" energyLevelSufficientlyRecharged="45" maxVelocity="1000" maxReverseVelocity="1000">
<boundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<property name="loopback:loadOperation" value="Load cargo"/>
<property name="loopback:unloadOperation" value="Unload cargo"/>
<vehicleLayout color="#00FFFF"/>
</vehicle>
<vehicle name="Vehicle-04" energyLevelCritical="30" energyLevelGood="90" energyLevelFullyRecharged="95" energyLevelSufficientlyRecharged="45" maxVelocity="1000" maxReverseVelocity="1000">
<boundingBox length="1000" width="1000" height="1000" referenceOffsetX="0" referenceOffsetY="0"/>
<property name="loopback:loadOperation" value="Load cargo"/>
<property name="loopback:unloadOperation" value="Unload cargo"/>
<vehicleLayout color="#FF33FF"/>
</vehicle>
<locationType name="Fire door">
<allowedPeripheralOperation name="Close"/>
<allowedPeripheralOperation name="Open"/>
<property name="tcs:defaultLocationTypeSymbol" value="NONE"/>
<locationTypeLayout locationRepresentation="NONE"/>
</locationType>
<locationType name="Recharge station">
<allowedOperation name="CHARGE"/>
<allowedOperation name="NOP"/>
<property name="tcs:defaultLocationSymbol" value="RECHARGE_GENERIC"/>
<property name="tcs:defaultLocationTypeSymbol" value="RECHARGE_GENERIC"/>
<locationTypeLayout locationRepresentation="RECHARGE_GENERIC"/>
</locationType>
<locationType name="Transfer station">
<allowedOperation name="Load cargo"/>
<allowedOperation name="NOP"/>
<allowedOperation name="Unload cargo"/>
<property name="tcs:defaultLocationSymbol" value="LOAD_TRANSFER_GENERIC"/>
<property name="tcs:defaultLocationTypeSymbol" value="LOAD_TRANSFER_GENERIC"/>
<locationTypeLayout locationRepresentation="LOAD_TRANSFER_GENERIC"/>
</locationType>
<locationType name="Working station">
<allowedOperation name="Cut"/>
<allowedOperation name="Drill"/>
<allowedOperation name="NOP"/>
<property name="tcs:defaultLocationSymbol" value="WORKING_GENERIC"/>
<property name="tcs:defaultLocationTypeSymbol" value="WORKING_GENERIC"/>
<locationTypeLayout locationRepresentation="WORKING_GENERIC"/>
</locationType>
<location name="Fire door 01" positionX="3500" positionY="-21000" positionZ="0" locked="false" type="Fire door">
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<property name="tcs:loopbackPeripheral" value="Value unknown"/>
<locationLayout positionX="3500" positionY="-21000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods in north 01" positionX="-28000" positionY="11000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0026"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-28000" positionY="11000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods in north 02" positionX="-28000" positionY="6000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0025"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-28000" positionY="6000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods in south 01" positionX="-2000" positionY="-18000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0019"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-2000" positionY="-18000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods in south 02" positionX="-12000" positionY="-24000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0059"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-12000" positionY="-24000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods out 01" positionX="-20000" positionY="-15000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0020"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-20000" positionY="-15000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Goods out 02" positionX="-28000" positionY="-8000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0021"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-28000" positionY="-8000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Recharge 01" positionX="27750" positionY="6750" positionZ="0" locked="false" type="Recharge station">
<link point="Point-0002"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="27750" positionY="6250" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Recharge 02" positionX="27750" positionY="750" positionZ="0" locked="false" type="Recharge station">
<link point="Point-0004"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="27750" positionY="250" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Recharge 03" positionX="27750" positionY="-5250" positionZ="0" locked="false" type="Recharge station">
<link point="Point-0006"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="27750" positionY="-5750" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Recharge 04" positionX="27750" positionY="-11250" positionZ="0" locked="false" type="Recharge station">
<link point="Point-0010"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="27750" positionY="-11750" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Storage 01" positionX="-8000" positionY="14000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0028"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-8000" positionY="14000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Storage 02" positionX="-2000" positionY="14000" positionZ="0" locked="false" type="Transfer station">
<link point="Point-0029"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-2000" positionY="14000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Working station 01" positionX="-11000" positionY="-3000" positionZ="0" locked="false" type="Working station">
<link point="Point-0054"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="-11000" positionY="-3000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Working station 02" positionX="15000" positionY="1000" positionZ="0" locked="false" type="Working station">
<link point="Point-0047"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="15000" positionY="1000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<location name="Working station 03" positionX="15000" positionY="-3000" positionZ="0" locked="false" type="Working station">
<link point="Point-0053"/>
<property name="tcs:defaultLocationSymbol" value="DEFAULT"/>
<locationLayout positionX="15000" positionY="-3000" labelOffsetX="-10" labelOffsetY="-20" locationRepresentation="DEFAULT" layerId="0"/>
</location>
<block name="Block-0001" type="SINGLE_VEHICLE_ONLY">
<member name="Point-0016 --- Point-0017"/>
<member name="Point-0019 --- Point-0022"/>
<blockLayout color="#FF0000"/>
</block>
<block name="Block-0002" type="SINGLE_VEHICLE_ONLY">
<member name="Point-0037 --- Point-0028"/>
<member name="Point-0039 --- Point-0040"/>
<blockLayout color="#0000FF"/>
</block>
<block name="Block-0003" type="SINGLE_VEHICLE_ONLY">
<member name="Point-0049 --- Point-0038"/>
<member name="Point-0052 --- Point-0044"/>
<blockLayout color="#00CC00"/>
</block>
<block name="Block-0004" type="SINGLE_VEHICLE_ONLY">
<member name="Point-0015 --- Point-0050"/>
<member name="Point-0045 --- Point-0016"/>
<member name="Point-0045 --- Point-0055"/>
<blockLayout color="#0099FF"/>
</block>
<visualLayout name="VLayout-01" scaleX="50.0" scaleY="50.0">
<layer id="0" ordinal="0" visible="true" name="Default layer" groupId="0"/>
<layerGroup id="0" name="Default layer group" visible="true"/>
</visualLayout>
<property name="tcs:modelFileLastModified" value="2024-09-12T12:01:37Z"/>
</model>

View File

@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0

View File

View File

@@ -0,0 +1,38 @@
@echo off
rem SPDX-FileCopyrightText: The openTCS Authors
rem SPDX-License-Identifier: MIT
rem
rem Start the openTCS Model Editor application.
rem
rem Set window title
title Model Editor (openTCS)
rem Don't export variables to the parent shell
setlocal
rem Set base directory names.
set OPENTCS_BASE=.
set OPENTCS_HOME=.
set OPENTCS_CONFIGDIR=%OPENTCS_HOME%\config
set OPENTCS_LIBDIR=%OPENTCS_BASE%\lib
rem Set the class path
set OPENTCS_CP=%OPENTCS_LIBDIR%\*;
set OPENTCS_CP=%OPENTCS_CP%;%OPENTCS_LIBDIR%\openTCS-extensions\*;
rem XXX Be a bit more clever to find out the name of the JVM runtime.
set JAVA=javaw
rem Start plant overview
start /b %JAVA% -enableassertions ^
-Dopentcs.base="%OPENTCS_BASE%" ^
-Dopentcs.home="%OPENTCS_HOME%" ^
-Dopentcs.configuration.provider=gestalt ^
-Dopentcs.configuration.reload.interval=10000 ^
-Djava.util.logging.config.file="%OPENTCS_CONFIGDIR%\logging.config" ^
-Dsun.java2d.d3d=false ^
-XX:-OmitStackTraceInFastThrow ^
-classpath "%OPENTCS_CP%" ^
-splash:bin/splash-image.gif ^
org.opentcs.modeleditor.RunModelEditor

View File

@@ -0,0 +1,35 @@
#!/bin/sh
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: MIT
#
# Start the openTCS Model Editor application.
#
# Set base directory names.
export OPENTCS_BASE=.
export OPENTCS_HOME=.
export OPENTCS_CONFIGDIR="${OPENTCS_HOME}/config"
export OPENTCS_LIBDIR="${OPENTCS_BASE}/lib"
# Set the class path
export OPENTCS_CP="${OPENTCS_LIBDIR}/*"
export OPENTCS_CP="${OPENTCS_CP}:${OPENTCS_LIBDIR}/openTCS-extensions/*"
if [ -n "${OPENTCS_JAVAVM}" ]; then
export JAVA="${OPENTCS_JAVAVM}"
else
# XXX Be a bit more clever to find out the name of the JVM runtime.
export JAVA="java"
fi
# Start plant overview
${JAVA} -enableassertions \
-Dopentcs.base="${OPENTCS_BASE}" \
-Dopentcs.home="${OPENTCS_HOME}" \
-Dopentcs.configuration.provider=gestalt \
-Dopentcs.configuration.reload.interval=10000 \
-Djava.util.logging.config.file=${OPENTCS_CONFIGDIR}/logging.config \
-XX:-OmitStackTraceInFastThrow \
-classpath "${OPENTCS_CP}" \
-splash:bin/splash-image.gif \
org.opentcs.modeleditor.RunModelEditor

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
/**
* Configures/binds the default importers and exporters of the openTCS plant overview.
*/
public class DefaultImportersExportersModule
extends
PlantOverviewInjectionModule {
/**
* Creates a new instance.
*/
public DefaultImportersExportersModule() {
}
@Override
protected void configure() {
plantModelImporterBinder();
plantModelExporterBinder();
}
}

View File

@@ -0,0 +1,170 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor;
import com.google.inject.TypeLiteral;
import jakarta.inject.Singleton;
import java.io.File;
import java.util.List;
import java.util.Locale;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.opentcs.access.KernelServicePortal;
import org.opentcs.access.SslParameterSet;
import org.opentcs.access.rmi.KernelServicePortalBuilder;
import org.opentcs.access.rmi.factories.NullSocketFactoryProvider;
import org.opentcs.access.rmi.factories.SecureSocketFactoryProvider;
import org.opentcs.access.rmi.factories.SocketFactoryProvider;
import org.opentcs.common.GuestUserCredentials;
import org.opentcs.components.plantoverview.LocationTheme;
import org.opentcs.customizations.ApplicationHome;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
import org.opentcs.drivers.LowLevelCommunicationEvent;
import org.opentcs.guing.common.exchange.ApplicationPortalProviderConfiguration;
import org.opentcs.guing.common.exchange.SslConfiguration;
import org.opentcs.modeleditor.application.ApplicationInjectionModule;
import org.opentcs.modeleditor.components.ComponentsInjectionModule;
import org.opentcs.modeleditor.exchange.ExchangeInjectionModule;
import org.opentcs.modeleditor.math.path.PathLengthFunctionInjectionModule;
import org.opentcs.modeleditor.model.ModelInjectionModule;
import org.opentcs.modeleditor.persistence.DefaultPersistenceInjectionModule;
import org.opentcs.modeleditor.transport.TransportInjectionModule;
import org.opentcs.modeleditor.util.ElementNamingSchemeConfiguration;
import org.opentcs.modeleditor.util.ModelEditorConfiguration;
import org.opentcs.modeleditor.util.UtilInjectionModule;
import org.opentcs.util.ClassMatcher;
import org.opentcs.util.gui.dialog.ConnectionParamSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Guice module for the openTCS plant overview application.
*/
public class DefaultPlantOverviewInjectionModule
extends
PlantOverviewInjectionModule {
/**
* This class's logger.
*/
private static final Logger LOG
= LoggerFactory.getLogger(DefaultPlantOverviewInjectionModule.class);
/**
* Creates a new instance.
*/
public DefaultPlantOverviewInjectionModule() {
}
@Override
protected void configure() {
File applicationHome = new File(System.getProperty("opentcs.home", "."));
bind(File.class)
.annotatedWith(ApplicationHome.class)
.toInstance(applicationHome);
configurePlantOverviewDependencies();
install(new ApplicationInjectionModule());
install(new ComponentsInjectionModule());
install(new ExchangeInjectionModule());
install(new PathLengthFunctionInjectionModule());
install(new ModelInjectionModule());
install(new DefaultPersistenceInjectionModule());
install(new TransportInjectionModule());
install(new UtilInjectionModule());
// Ensure there is at least an empty binder for pluggable panels.
pluggablePanelFactoryBinder();
// Ensure there is at least an empty binder for history entry formatters.
objectHistoryEntryFormatterBinder();
}
private void configurePlantOverviewDependencies() {
ModelEditorConfiguration configuration
= getConfigBindingProvider().get(
ModelEditorConfiguration.PREFIX,
ModelEditorConfiguration.class
);
bind(ApplicationPortalProviderConfiguration.class)
.toInstance(configuration);
bind(ModelEditorConfiguration.class)
.toInstance(configuration);
configurePlantOverview(configuration);
configureThemes(configuration);
configureSocketConnections();
configureNamingConfiguration();
bind(new TypeLiteral<List<ConnectionParamSet>>() {
})
.toInstance(configuration.connectionBookmarks());
}
private void configureNamingConfiguration() {
ElementNamingSchemeConfiguration configuration
= getConfigBindingProvider().get(
ElementNamingSchemeConfiguration.PREFIX,
ElementNamingSchemeConfiguration.class
);
bind(ElementNamingSchemeConfiguration.class)
.toInstance(configuration);
}
private void configureSocketConnections() {
SslConfiguration sslConfiguration = getConfigBindingProvider().get(
SslConfiguration.PREFIX,
SslConfiguration.class
);
//Create the data object for the ssl configuration
SslParameterSet sslParamSet = new SslParameterSet(
SslParameterSet.DEFAULT_KEYSTORE_TYPE,
null,
null,
new File(sslConfiguration.truststoreFile()),
sslConfiguration.truststorePassword()
);
bind(SslParameterSet.class).toInstance(sslParamSet);
SocketFactoryProvider socketFactoryProvider;
if (sslConfiguration.enable()) {
socketFactoryProvider = new SecureSocketFactoryProvider(sslParamSet);
}
else {
LOG.warn("SSL encryption disabled, connections will not be secured!");
socketFactoryProvider = new NullSocketFactoryProvider();
}
//Bind socket provider to the kernel portal
bind(KernelServicePortal.class)
.toInstance(
new KernelServicePortalBuilder(
GuestUserCredentials.USER,
GuestUserCredentials.PASSWORD
)
.setSocketFactoryProvider(socketFactoryProvider)
.setEventFilter(new ClassMatcher(LowLevelCommunicationEvent.class).negate())
.build()
);
}
private void configureThemes(ModelEditorConfiguration configuration) {
bind(LocationTheme.class)
.to(configuration.locationThemeClass())
.in(Singleton.class);
}
private void configurePlantOverview(ModelEditorConfiguration configuration) {
Locale.setDefault(Locale.forLanguageTag(configuration.locale()));
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException ex) {
LOG.warn("Could not set look-and-feel", ex);
}
// Show tooltips for 30 seconds (Default: 4 sec)
ToolTipManager.sharedInstance().setDismissDelay(30 * 1000);
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor;
import java.util.HashSet;
import java.util.Set;
import org.opentcs.common.LoopbackAdapterConstants;
import org.opentcs.components.kernel.Dispatcher;
import org.opentcs.components.kernel.Router;
import org.opentcs.components.kernel.Scheduler;
import org.opentcs.components.plantoverview.PropertySuggestions;
import org.opentcs.data.ObjectPropConstants;
/**
* The default property suggestions of the baseline plant overview.
*/
public class DefaultPropertySuggestions
implements
PropertySuggestions {
private final Set<String> keySuggestions = new HashSet<>();
private final Set<String> valueSuggestions = new HashSet<>();
/**
* Creates a new instance.
*/
public DefaultPropertySuggestions() {
keySuggestions.add(Scheduler.PROPKEY_BLOCK_ENTRY_DIRECTION);
keySuggestions.add(Router.PROPKEY_ROUTING_GROUP);
keySuggestions.add(Dispatcher.PROPKEY_PARKING_POSITION_PRIORITY);
keySuggestions.add(Dispatcher.PROPKEY_ASSIGNED_PARKING_POSITION);
keySuggestions.add(Dispatcher.PROPKEY_PREFERRED_PARKING_POSITION);
keySuggestions.add(Dispatcher.PROPKEY_ASSIGNED_RECHARGE_LOCATION);
keySuggestions.add(Dispatcher.PROPKEY_PREFERRED_RECHARGE_LOCATION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_INITIAL_POSITION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_OPERATING_TIME);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_LOAD_OPERATION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_UNLOAD_OPERATION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_ACCELERATION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_DECELERATION);
keySuggestions.add(ObjectPropConstants.VEHICLE_DATA_TRANSFORMER);
}
@Override
public Set<String> getKeySuggestions() {
return keySuggestions;
}
@Override
public Set<String> getValueSuggestions() {
return valueSuggestions;
}
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor;
import jakarta.inject.Singleton;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
/**
* This module configures the multibinder used to suggest key value properties in the editor.
*/
public class PropertySuggestionsModule
extends
PlantOverviewInjectionModule {
/**
* Creates a new instance.
*/
public PropertySuggestionsModule() {
}
@Override
protected void configure() {
propertySuggestionsBinder().addBinding()
.to(DefaultPropertySuggestions.class)
.in(Singleton.class);
}
}

View File

@@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.util.Modules;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import org.opentcs.configuration.ConfigurationBindingProvider;
import org.opentcs.configuration.gestalt.GestaltConfigurationBindingProvider;
import org.opentcs.customizations.ConfigurableInjectionModule;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
import org.opentcs.guing.common.util.CompatibilityChecker;
import org.opentcs.modeleditor.application.PlantOverviewStarter;
import org.opentcs.util.Environment;
import org.opentcs.util.logging.UncaughtExceptionLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The plant overview process's default entry point.
*/
public class RunModelEditor {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(RunModelEditor.class);
/**
* Prevents external instantiation.
*/
private RunModelEditor() {
}
/**
* The plant overview client's main entry point.
*
* @param args the command line arguments
*/
public static void main(final String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(false));
Environment.logSystemInfo();
ensureVersionCompatibility();
Injector injector = Guice.createInjector(customConfigurationModule());
injector.getInstance(PlantOverviewStarter.class).startPlantOverview();
}
private static void ensureVersionCompatibility() {
String version = System.getProperty("java.version");
if (!CompatibilityChecker.versionCompatibleWithDockingFrames(version)) {
LOG.error("Version incompatible with Docking Frames: '{}'", version);
CompatibilityChecker.showVersionIncompatibleWithDockingFramesMessage();
System.exit(1);
}
}
/**
* Builds and returns a Guice module containing the custom configuration for the plant overview
* application, including additions and overrides by the user.
*
* @return The custom configuration module.
*/
private static Module customConfigurationModule() {
ConfigurationBindingProvider bindingProvider = configurationBindingProvider();
ConfigurableInjectionModule plantOverviewInjectionModule
= new DefaultPlantOverviewInjectionModule();
plantOverviewInjectionModule.setConfigBindingProvider(bindingProvider);
return Modules.override(plantOverviewInjectionModule)
.with(findRegisteredModules(bindingProvider));
}
/**
* Finds and returns all Guice modules registered via ServiceLoader.
*
* @return The registered/found modules.
*/
private static List<PlantOverviewInjectionModule> findRegisteredModules(
ConfigurationBindingProvider bindingProvider
) {
List<PlantOverviewInjectionModule> registeredModules = new ArrayList<>();
for (PlantOverviewInjectionModule module : ServiceLoader.load(
PlantOverviewInjectionModule.class
)) {
LOG.info(
"Integrating injection module {} (source: {})",
module.getClass().getName(),
module.getClass().getProtectionDomain().getCodeSource()
);
module.setConfigBindingProvider(bindingProvider);
registeredModules.add(module);
}
return registeredModules;
}
private static ConfigurationBindingProvider configurationBindingProvider() {
String chosenProvider = System.getProperty("opentcs.configuration.provider", "gestalt");
switch (chosenProvider) {
case "gestalt":
default:
LOG.info("Using gestalt as the configuration provider.");
return gestaltConfigurationBindingProvider();
}
}
private static ConfigurationBindingProvider gestaltConfigurationBindingProvider() {
return new GestaltConfigurationBindingProvider(
Paths.get(
System.getProperty("opentcs.base", "."),
"config",
"opentcs-modeleditor-defaults-baseline.properties"
)
.toAbsolutePath(),
Paths.get(
System.getProperty("opentcs.base", "."),
"config",
"opentcs-modeleditor-defaults-custom.properties"
)
.toAbsolutePath(),
Paths.get(
System.getProperty("opentcs.home", "."),
"config",
"opentcs-modeleditor.properties"
)
.toAbsolutePath()
);
}
}

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application;
import com.google.inject.AbstractModule;
import jakarta.inject.Singleton;
import java.awt.Component;
import javax.swing.JFrame;
import org.jhotdraw.app.Application;
import org.opentcs.customizations.plantoverview.ApplicationFrame;
import org.opentcs.guing.common.application.ApplicationState;
import org.opentcs.guing.common.application.ComponentsManager;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.guing.common.application.PluginPanelManager;
import org.opentcs.guing.common.application.ProgressIndicator;
import org.opentcs.guing.common.application.SplashFrame;
import org.opentcs.guing.common.application.StatusPanel;
import org.opentcs.guing.common.application.ViewManager;
import org.opentcs.modeleditor.application.action.ActionInjectionModule;
import org.opentcs.modeleditor.application.menus.MenusInjectionModule;
import org.opentcs.modeleditor.application.toolbar.ToolBarInjectionModule;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.OpenTCSSDIApplication;
/**
* An injection module for this package.
*/
public class ApplicationInjectionModule
extends
AbstractModule {
/**
* The application's main frame.
*/
private final JFrame applicationFrame = new JFrame();
/**
* Creates a new instance.
*/
public ApplicationInjectionModule() {
}
@Override
protected void configure() {
install(new ActionInjectionModule());
install(new MenusInjectionModule());
install(new ToolBarInjectionModule());
bind(ApplicationState.class).in(Singleton.class);
bind(UndoRedoManager.class).in(Singleton.class);
bind(ProgressIndicator.class)
.to(SplashFrame.class)
.in(Singleton.class);
bind(StatusPanel.class).in(Singleton.class);
bind(JFrame.class)
.annotatedWith(ApplicationFrame.class)
.toInstance(applicationFrame);
bind(Component.class)
.annotatedWith(ApplicationFrame.class)
.toInstance(applicationFrame);
bind(ViewManagerModeling.class)
.in(Singleton.class);
bind(ViewManager.class).to(ViewManagerModeling.class);
bind(Application.class)
.to(OpenTCSSDIApplication.class)
.in(Singleton.class);
bind(OpenTCSView.class).in(Singleton.class);
bind(GuiManager.class).to(OpenTCSView.class);
bind(ComponentsManager.class).to(OpenTCSView.class);
bind(PluginPanelManager.class).to(OpenTCSView.class);
}
}

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action;
import com.google.inject.AbstractModule;
import jakarta.inject.Singleton;
/**
* An injection module for this package.
*/
public class ActionInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public ActionInjectionModule() {
}
@Override
protected void configure() {
bind(ViewActionMap.class).in(Singleton.class);
}
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
/**
*/
public class MenusInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public MenusInjectionModule() {
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(MenuFactory.class));
}
}

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.toolbar;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import jakarta.inject.Singleton;
import org.jhotdraw.draw.tool.DragTracker;
import org.jhotdraw.draw.tool.SelectAreaTracker;
import org.opentcs.modeleditor.application.action.ToolBarManager;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.toolbar.OpenTCSDragTracker;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.toolbar.OpenTCSSelectAreaTracker;
/**
*/
public class ToolBarInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public ToolBarInjectionModule() {
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(SelectionToolFactory.class));
install(new FactoryModuleBuilder().build(CreationToolFactory.class));
bind(ToolBarManager.class).in(Singleton.class);
bind(SelectAreaTracker.class).to(OpenTCSSelectAreaTracker.class);
bind(DragTracker.class).to(OpenTCSDragTracker.class);
}
}

View File

@@ -0,0 +1,116 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components;
import com.google.inject.AbstractModule;
import com.google.inject.PrivateModule;
import jakarta.inject.Singleton;
import java.awt.event.MouseListener;
import org.opentcs.guing.common.components.tree.AbstractTreeViewPanel;
import org.opentcs.guing.common.components.tree.BlockMouseListener;
import org.opentcs.guing.common.components.tree.BlocksTreeViewManager;
import org.opentcs.guing.common.components.tree.BlocksTreeViewPanel;
import org.opentcs.guing.common.components.tree.ComponentsTreeViewManager;
import org.opentcs.guing.common.components.tree.ComponentsTreeViewPanel;
import org.opentcs.guing.common.components.tree.TreeMouseAdapter;
import org.opentcs.guing.common.components.tree.TreeView;
import org.opentcs.modeleditor.components.dialogs.DialogsInjectionModule;
import org.opentcs.modeleditor.components.dockable.DockableInjectionModule;
import org.opentcs.modeleditor.components.drawing.DrawingInjectionModule;
import org.opentcs.modeleditor.components.layer.LayersInjectionModule;
import org.opentcs.modeleditor.components.properties.PropertiesInjectionModule;
import org.opentcs.modeleditor.components.tree.elements.TreeElementsInjectionModule;
/**
* A Guice module for this package.
*/
public class ComponentsInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public ComponentsInjectionModule() {
}
@Override
protected void configure() {
install(new DialogsInjectionModule());
install(new DockableInjectionModule());
install(new DrawingInjectionModule());
install(new PropertiesInjectionModule());
install(new TreeElementsInjectionModule());
install(new ComponentsTreeViewModule());
install(new BlocksTreeViewModule());
install(new LayersInjectionModule());
}
private static class ComponentsTreeViewModule
extends
PrivateModule {
ComponentsTreeViewModule() {
}
@Override
protected void configure() {
// Within this (private) module, there should only be a single tree panel.
bind(ComponentsTreeViewPanel.class)
.in(Singleton.class);
// Bind the tree panel annotated with the given annotation to our single
// instance and expose only this annotated version.
bind(AbstractTreeViewPanel.class)
.to(ComponentsTreeViewPanel.class);
expose(ComponentsTreeViewPanel.class);
// Bind TreeView to the single tree panel, too.
bind(TreeView.class)
.to(ComponentsTreeViewPanel.class);
// Bind and expose a single manager for the single tree view/panel.
bind(ComponentsTreeViewManager.class)
.in(Singleton.class);
expose(ComponentsTreeViewManager.class);
bind(MouseListener.class)
.to(TreeMouseAdapter.class);
}
}
private static class BlocksTreeViewModule
extends
PrivateModule {
BlocksTreeViewModule() {
}
@Override
protected void configure() {
// Within this (private) module, there should only be a single tree panel.
bind(BlocksTreeViewPanel.class)
.in(Singleton.class);
// Bind the tree panel annotated with the given annotation to our single
// instance and expose only this annotated version.
bind(AbstractTreeViewPanel.class)
.to(BlocksTreeViewPanel.class);
expose(BlocksTreeViewPanel.class);
// Bind TreeView to the single tree panel, too.
bind(TreeView.class)
.to(BlocksTreeViewPanel.class);
// Bind and expose a single manager for the single tree view/panel.
bind(BlocksTreeViewManager.class)
.in(Singleton.class);
expose(BlocksTreeViewManager.class);
bind(MouseListener.class)
.to(BlockMouseListener.class);
}
}
}

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.dialogs;
import com.google.inject.AbstractModule;
/**
* A Guice module for this package.
*/
public class DialogsInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public DialogsInjectionModule() {
}
@Override
protected void configure() {
}
}

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.dockable;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import jakarta.inject.Singleton;
import org.opentcs.guing.common.components.dockable.DockableHandlerFactory;
import org.opentcs.guing.common.components.dockable.DockingManager;
/**
* A Guice module for this package.
*/
public class DockableInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public DockableInjectionModule() {
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(DockableHandlerFactory.class));
bind(DockingManagerModeling.class).in(Singleton.class);
bind(DockingManager.class).to(DockingManagerModeling.class);
}
}

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import jakarta.inject.Singleton;
import org.jhotdraw.draw.DrawingEditor;
import org.opentcs.guing.common.components.drawing.DrawingOptions;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingView;
import org.opentcs.guing.common.components.drawing.figures.FigureFactory;
import org.opentcs.thirdparty.modeleditor.jhotdraw.components.drawing.OpenTCSDrawingViewModeling;
/**
* A Guice module for this package.
*/
public class DrawingInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public DrawingInjectionModule() {
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(FigureFactory.class));
bind(OpenTCSDrawingEditor.class).in(Singleton.class);
bind(DrawingEditor.class).to(OpenTCSDrawingEditor.class);
bind(OpenTCSDrawingView.class).to(OpenTCSDrawingViewModeling.class);
bind(DrawingOptions.class).in(Singleton.class);
}
}

View File

@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import jakarta.inject.Singleton;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
import org.opentcs.guing.common.components.layer.LayerGroupManager;
import org.opentcs.guing.common.components.layer.LayerManager;
/**
* A Guice module for this package.
*/
public class LayersInjectionModule
extends
PlantOverviewInjectionModule {
/**
* Creates a new instance.
*/
public LayersInjectionModule() {
}
@Override
protected void configure() {
bind(LayerManagerModeling.class).in(Singleton.class);
bind(LayerManager.class).to(LayerManagerModeling.class);
bind(LayerEditorModeling.class).to(LayerManagerModeling.class);
bind(ActiveLayerProvider.class).to(LayerManagerModeling.class);
bind(LayersPanel.class).in(Singleton.class);
bind(LayerGroupManager.class).to(LayerManagerModeling.class);
bind(LayerGroupEditorModeling.class).to(LayerManagerModeling.class);
bind(LayerGroupsPanel.class).in(Singleton.class);
}
}

View File

@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.properties;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.multibindings.MapBinder;
import jakarta.inject.Singleton;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
import org.opentcs.guing.base.components.properties.type.AbstractComplexProperty;
import org.opentcs.guing.base.components.properties.type.BoundingBoxProperty;
import org.opentcs.guing.base.components.properties.type.EnergyLevelThresholdSetProperty;
import org.opentcs.guing.base.components.properties.type.EnvelopesProperty;
import org.opentcs.guing.base.components.properties.type.KeyValueProperty;
import org.opentcs.guing.base.components.properties.type.KeyValueSetProperty;
import org.opentcs.guing.base.components.properties.type.LinkActionsProperty;
import org.opentcs.guing.base.components.properties.type.LocationTypeActionsProperty;
import org.opentcs.guing.base.components.properties.type.OrderTypesProperty;
import org.opentcs.guing.base.components.properties.type.PeripheralOperationsProperty;
import org.opentcs.guing.base.components.properties.type.SymbolProperty;
import org.opentcs.guing.common.components.dialogs.DetailsDialogContent;
import org.opentcs.guing.common.components.properties.PropertiesComponentsFactory;
import org.opentcs.guing.common.components.properties.SelectionPropertiesComponent;
import org.opentcs.guing.common.components.properties.panel.BoundingBoxPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.EnergyLevelThresholdSetPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.EnvelopesPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.KeyValuePropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.KeyValueSetPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.LinkActionsEditorPanel;
import org.opentcs.guing.common.components.properties.panel.LocationTypeActionsEditorPanel;
import org.opentcs.guing.common.components.properties.panel.OrderTypesPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.PeripheralOperationsPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.panel.PropertiesPanelFactory;
import org.opentcs.guing.common.components.properties.panel.SymbolPropertyEditorPanel;
import org.opentcs.guing.common.components.properties.table.CellEditorFactory;
import org.opentcs.modeleditor.application.menus.MenuItemComponentsFactory;
/**
* A Guice module for this package.
*/
public class PropertiesInjectionModule
extends
PlantOverviewInjectionModule {
/**
* Creates a new instance.
*/
public PropertiesInjectionModule() {
}
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(PropertiesPanelFactory.class));
install(new FactoryModuleBuilder().build(CellEditorFactory.class));
install(new FactoryModuleBuilder().build(PropertiesComponentsFactory.class));
install(new FactoryModuleBuilder().build(MenuItemComponentsFactory.class));
MapBinder<Class<? extends AbstractComplexProperty>, DetailsDialogContent> dialogContentMapBinder
= MapBinder.newMapBinder(
binder(),
new TypeLiteral<Class<? extends AbstractComplexProperty>>() {
},
new TypeLiteral<DetailsDialogContent>() {
}
);
dialogContentMapBinder
.addBinding(KeyValueProperty.class)
.to(KeyValuePropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(KeyValueSetProperty.class)
.to(KeyValueSetPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(LocationTypeActionsProperty.class)
.to(LocationTypeActionsEditorPanel.class);
dialogContentMapBinder
.addBinding(LinkActionsProperty.class)
.to(LinkActionsEditorPanel.class);
dialogContentMapBinder
.addBinding(SymbolProperty.class)
.to(SymbolPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(OrderTypesProperty.class)
.to(OrderTypesPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(PeripheralOperationsProperty.class)
.to(PeripheralOperationsPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(EnvelopesProperty.class)
.to(EnvelopesPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(BoundingBoxProperty.class)
.to(BoundingBoxPropertyEditorPanel.class);
dialogContentMapBinder
.addBinding(EnergyLevelThresholdSetProperty.class)
.to(EnergyLevelThresholdSetPropertyEditorPanel.class);
bind(SelectionPropertiesComponent.class)
.in(Singleton.class);
}
}

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.tree.elements;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import org.opentcs.guing.common.components.tree.elements.UserObjectFactory;
import org.opentcs.guing.common.components.tree.elements.VehicleUserObject;
/**
* A Guice module for this package.
*/
public class TreeElementsInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public TreeElementsInjectionModule() {
}
@Override
protected void configure() {
install(
new FactoryModuleBuilder()
.implement(VehicleUserObject.class, VehicleUserObjectModeling.class)
.build(UserObjectFactory.class)
);
}
}

View File

@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.exchange;
import com.google.inject.AbstractModule;
import jakarta.inject.Singleton;
import org.opentcs.access.SharedKernelServicePortalProvider;
import org.opentcs.common.DefaultPortalManager;
import org.opentcs.common.PortalManager;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.guing.common.exchange.ApplicationPortalProvider;
import org.opentcs.util.event.EventBus;
import org.opentcs.util.event.EventHandler;
import org.opentcs.util.event.EventSource;
import org.opentcs.util.event.SimpleEventBus;
/**
* A Guice configuration module for this package.
*/
public class ExchangeInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public ExchangeInjectionModule() {
}
@Override
protected void configure() {
bind(PortalManager.class)
.to(DefaultPortalManager.class)
.in(Singleton.class);
EventBus eventBus = new SimpleEventBus();
bind(EventSource.class)
.annotatedWith(ApplicationEventBus.class)
.toInstance(eventBus);
bind(EventHandler.class)
.annotatedWith(ApplicationEventBus.class)
.toInstance(eventBus);
bind(EventBus.class)
.annotatedWith(ApplicationEventBus.class)
.toInstance(eventBus);
bind(SharedKernelServicePortalProvider.class)
.to(ApplicationPortalProvider.class)
.in(Singleton.class);
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapBinder;
import org.opentcs.guing.base.model.elements.PathModel;
/**
* A Guice configuration module for this package.
*/
public class PathLengthFunctionInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public PathLengthFunctionInjectionModule() {
}
@Override
protected void configure() {
MapBinder<PathModel.Type, PathLengthFunction> pathLengthFunctionBinder
= MapBinder.newMapBinder(
binder(),
new TypeLiteral<PathModel.Type>() {
},
new TypeLiteral<PathLengthFunction>() {
}
);
pathLengthFunctionBinder
.addBinding(PathModel.Type.DIRECT)
.to(EuclideanDistance.class);
pathLengthFunctionBinder
.addBinding(PathModel.Type.ELBOW)
.to(EuclideanDistance.class);
pathLengthFunctionBinder
.addBinding(PathModel.Type.SLANTED)
.to(EuclideanDistance.class);
pathLengthFunctionBinder
.addBinding(PathModel.Type.POLYPATH)
.to(PolyPathLength.class);
pathLengthFunctionBinder
.addBinding(PathModel.Type.BEZIER)
.to(BezierLength.class);
pathLengthFunctionBinder
.addBinding(PathModel.Type.BEZIER_3)
.to(BezierThreeLength.class);
bind(PathLengthFunction.class)
.to(CompositePathLengthFunction.class);
}
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.model;
import com.google.inject.AbstractModule;
import org.opentcs.guing.common.model.StandardSystemModel;
import org.opentcs.guing.common.model.SystemModel;
/**
* A Guice module for the model package.
*/
public class ModelInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public ModelInjectionModule() {
}
@Override
protected void configure() {
bind(SystemModel.class).to(StandardSystemModel.class);
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.persistence;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import org.opentcs.guing.common.persistence.ModelFilePersistor;
import org.opentcs.guing.common.persistence.ModelFileReader;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.guing.common.persistence.unified.UnifiedModelPersistor;
import org.opentcs.modeleditor.persistence.unified.UnifiedModelReader;
/**
* Default bindings for model readers and persistors.
*/
public class DefaultPersistenceInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public DefaultPersistenceInjectionModule() {
}
@Override
protected void configure() {
bind(OpenTCSModelManagerModeling.class).in(Singleton.class);
bind(ModelManager.class).to(OpenTCSModelManagerModeling.class);
bind(ModelManagerModeling.class).to(OpenTCSModelManagerModeling.class);
bind(ModelFileReader.class).to(UnifiedModelReader.class);
bind(ModelFilePersistor.class).to(UnifiedModelPersistor.class);
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.transport;
import java.util.HashSet;
import java.util.Set;
import org.opentcs.components.plantoverview.OrderTypeSuggestions;
import org.opentcs.data.order.OrderConstants;
/**
* The default suggestions for transport order types.
*/
public class DefaultOrderTypeSuggestions
implements
OrderTypeSuggestions {
/**
* The transport order type suggestions.
*/
private final Set<String> typeSuggestions = new HashSet<>();
/**
* Creates a new instance.
*/
public DefaultOrderTypeSuggestions() {
typeSuggestions.add(OrderConstants.TYPE_NONE);
typeSuggestions.add(OrderConstants.TYPE_CHARGE);
typeSuggestions.add(OrderConstants.TYPE_PARK);
typeSuggestions.add(OrderConstants.TYPE_TRANSPORT);
}
@Override
public Set<String> getTypeSuggestions() {
return typeSuggestions;
}
}

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.transport;
import jakarta.inject.Singleton;
import org.opentcs.customizations.plantoverview.PlantOverviewInjectionModule;
import org.opentcs.guing.common.transport.OrderTypeSuggestionsPool;
/**
* A Guice module for the transport order type suggestions.
*/
public class OrderTypeSuggestionsModule
extends
PlantOverviewInjectionModule {
/**
* Creates a new instance.
*/
public OrderTypeSuggestionsModule() {
}
@Override
protected void configure() {
orderTypeSuggestionsBinder().addBinding()
.to(DefaultOrderTypeSuggestions.class)
.in(Singleton.class);
bind(OrderTypeSuggestionsPool.class).in(Singleton.class);
}
}

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.transport;
import com.google.inject.AbstractModule;
/**
* A Guice module for this package.
*/
public class TransportInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public TransportInjectionModule() {
}
@Override
protected void configure() {
}
}

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.util;
import com.google.inject.AbstractModule;
import jakarta.inject.Singleton;
import org.opentcs.guing.common.util.PanelRegistry;
/**
* A default Guice module for this package.
*/
public class UtilInjectionModule
extends
AbstractModule {
/**
* Creates a new instance.
*/
public UtilInjectionModule() {
}
@Override
protected void configure() {
bind(PanelRegistry.class).in(Singleton.class);
}
}

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: MIT
org.opentcs.modeleditor.DefaultImportersExportersModule
org.opentcs.modeleditor.PropertySuggestionsModule
org.opentcs.modeleditor.transport.OrderTypeSuggestionsModule

View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.jhotdraw.app.Application;
import org.opentcs.guing.common.application.ProgressIndicator;
import org.opentcs.guing.common.application.StartupProgressStatus;
import org.opentcs.guing.common.event.EventLogger;
/**
* The plant overview application's entry point.
*/
public class PlantOverviewStarter {
/**
* Our startup progress indicator.
*/
private final ProgressIndicator progressIndicator;
/**
* The enclosing application.
*/
private final Application application;
/**
* The actual document view.
*/
private final OpenTCSView opentcsView;
/**
* Provides logging for events published on the application event bus.
*/
private final EventLogger eventLogger;
/**
* Creates a new instance.
*
* @param progressIndicator The progress indicator to be used.
* @param application The application to be used.
* @param opentcsView The view to be used.
* @param eventLogger Provides logging for events published on the application event bus.
*/
@Inject
public PlantOverviewStarter(
ProgressIndicator progressIndicator,
Application application,
OpenTCSView opentcsView,
EventLogger eventLogger
) {
this.progressIndicator = requireNonNull(progressIndicator, "progressIndicator");
this.application = requireNonNull(application, "application");
this.opentcsView = requireNonNull(opentcsView, "opentcsView");
this.eventLogger = requireNonNull(eventLogger, "eventLogger");
}
public void startPlantOverview() {
eventLogger.initialize();
opentcsView.init();
progressIndicator.initialize();
progressIndicator.setProgress(StartupProgressStatus.START_PLANT_OVERVIEW);
progressIndicator.setProgress(StartupProgressStatus.SHOW_PLANT_OVERVIEW);
opentcsView.setApplication(application);
// Start the view.
application.show(opentcsView);
progressIndicator.terminate();
}
}

View File

@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application;
import static java.util.Objects.requireNonNull;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import jakarta.inject.Inject;
import java.awt.event.FocusEvent;
import java.io.File;
import java.util.ArrayList;
import org.jhotdraw.draw.DrawingView;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.guing.common.application.AbstractViewManager;
import org.opentcs.guing.common.components.dockable.CStackDockStation;
import org.opentcs.modeleditor.components.dockable.DockingManagerModeling;
import org.opentcs.util.event.EventSource;
/**
* Manages the mapping of dockables to drawing views, transport order views and
* order sequence views.
*/
public class ViewManagerModeling
extends
AbstractViewManager {
/**
* Manages the application's docking frames.
*/
private final DockingManagerModeling dockingManager;
/**
* The default modelling dockable.
*/
private DefaultSingleCDockable drawingViewModellingDockable;
/**
* Creates a new instance.
*
* @param dockingManager Manages the application's docking frames.
* @param eventSource Where this instance registers event listeners.
*/
@Inject
public ViewManagerModeling(
DockingManagerModeling dockingManager,
@ApplicationEventBus
EventSource eventSource
) {
super(eventSource);
this.dockingManager = requireNonNull(dockingManager, "dockingManager");
}
public void init() {
setPlantOverviewStateModelling();
}
/**
* Resets all components.
*/
@Override
public void reset() {
super.reset();
drawingViewModellingDockable = null;
}
/**
* Initializes the unique modelling dockable.
*
* @param dockable The dockable that will be the modelling dockable.
* @param title The title of this dockable.
*/
public void initModellingDockable(DefaultSingleCDockable dockable, String title) {
drawingViewModellingDockable = requireNonNull(dockable, "dockable");
drawingViewModellingDockable.setTitleText(requireNonNull(title, "title"));
drawingViewModellingDockable.setCloseable(false);
}
public void setBitmapToModellingView(File file) {
getDrawingViewMap().get(drawingViewModellingDockable)
.getDrawingView()
.addBackgroundBitmap(file);
}
/**
* Sets visibility states of all dockables to modelling.
*/
private void setPlantOverviewStateModelling() {
CStackDockStation station
= dockingManager.getTabPane(DockingManagerModeling.COURSE_TAB_PANE_ID).getStation();
for (DefaultSingleCDockable dock : new ArrayList<>(getDrawingViewMap().keySet())) {
if (dock != drawingViewModellingDockable) {
// Setting it to closeable = false, so the ClosingListener
// doesn't remove the dockable when it's closed
dock.setCloseable(false);
dockingManager.setDockableVisibility(dock.getUniqueId(), false);
}
}
dockingManager.showDockable(station, drawingViewModellingDockable, 0);
DrawingView view = getDrawingViewMap().get(drawingViewModellingDockable).getDrawingView();
view.getComponent().dispatchEvent(new FocusEvent(view.getComponent(), FocusEvent.FOCUS_GAINED));
}
}

View File

@@ -0,0 +1,471 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.awt.Rectangle;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import org.jhotdraw.draw.DrawingEditor;
import org.jhotdraw.draw.event.ToolAdapter;
import org.jhotdraw.draw.event.ToolEvent;
import org.jhotdraw.draw.event.ToolListener;
import org.jhotdraw.draw.tool.ConnectionTool;
import org.jhotdraw.draw.tool.CreationTool;
import org.jhotdraw.draw.tool.Tool;
import org.jhotdraw.gui.JPopupButton;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.application.action.ToolButtonListener;
import org.opentcs.guing.common.application.toolbar.DragTool;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.figures.LabeledLocationFigure;
import org.opentcs.guing.common.components.drawing.figures.LabeledPointFigure;
import org.opentcs.guing.common.components.drawing.figures.LinkConnection;
import org.opentcs.guing.common.components.drawing.figures.PathConnection;
import org.opentcs.guing.common.event.ResetInteractionToolCommand;
import org.opentcs.guing.common.util.CourseObjectFactory;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.modeleditor.application.action.actions.CreateBlockAction;
import org.opentcs.modeleditor.application.action.actions.CreateLocationTypeAction;
import org.opentcs.modeleditor.application.action.actions.CreateVehicleAction;
import org.opentcs.modeleditor.application.action.draw.DefaultPointSelectedAction;
import org.opentcs.modeleditor.application.toolbar.CreationToolFactory;
import org.opentcs.modeleditor.application.toolbar.MultipleSelectionTool;
import org.opentcs.modeleditor.application.toolbar.SelectionToolFactory;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.draw.SelectSameAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.ButtonFactory;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.draw.DefaultPathSelectedAction;
import org.opentcs.util.event.EventHandler;
/**
* Sets up and manages a list of tool bars in the graphical user interface.
*/
public class ToolBarManager
implements
EventHandler {
/**
* A factory for selectiont tools.
*/
private final SelectionToolFactory selectionToolFactory;
/**
* A list of all toolbars.
*/
private final List<JToolBar> toolBarList = Collections.synchronizedList(new ArrayList<>());
/**
* A tool bar for actions creating new items.
*/
private final JToolBar toolBarCreation = new JToolBar();
/**
* A tool bar for actions regarding alignment.
*/
private final JToolBar toolBarAlignment = new JToolBar();
/**
* A toggle button for the selection tool.
*/
private final JToggleButton selectionToolButton;
/**
* A toggle button for the drag tool.
*/
private final JToggleButton dragToolButton;
/**
* The actual drag tool.
*/
private DragTool dragTool;
/**
* A button for creating points.
*/
private final JPopupButton buttonCreatePoint;
/**
* A button for creating locations.
*/
private final JToggleButton buttonCreateLocation;
/**
* A button for creating paths.
*/
private final JPopupButton buttonCreatePath;
/**
* A button for creating location links.
*/
private final JToggleButton buttonCreateLink;
/**
* A button for creating location types.
*/
private final JButton buttonCreateLocationType;
/**
* A button for creating vehicles.
*/
private final JButton buttonCreateVehicle;
/**
* A button for creating blocks.
*/
private final JButton buttonCreateBlock;
/**
* Creates a new instance.
*
* @param actionMap The action map to be used
* @param crsObjFactory A factory for course objects
* @param editor The drawing editor
* @param creationToolFactory The creation tool factory.
* @param selectionToolFactory The selection tool factory
*/
@Inject
public ToolBarManager(
ViewActionMap actionMap,
CourseObjectFactory crsObjFactory,
OpenTCSDrawingEditor editor,
CreationToolFactory creationToolFactory,
SelectionToolFactory selectionToolFactory
) {
requireNonNull(actionMap, "actionMap");
requireNonNull(crsObjFactory, "crsObjFactory");
requireNonNull(editor, "editor");
requireNonNull(creationToolFactory, "creationToolFactory");
this.selectionToolFactory = requireNonNull(selectionToolFactory, "selectionToolFactory");
ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.TOOLBAR_PATH);
// --- 1. ToolBar: Creation ---
// Selection, Drag | Create Point, Location, Path, Link |
// Create Location Type, Vehicle, Block, Static Route |
// Create Transport Order | Find, Show Vehicles
toolBarCreation.setActionMap(actionMap);
// --- Selection Tool ---
selectionToolButton = addSelectionToolButton(toolBarCreation, editor);
// --- Drag Tool ---
dragToolButton = addDragToolButton(toolBarCreation, editor);
toolBarCreation.addSeparator();
// --- Create Point Figure ---
LabeledPointFigure lpf = crsObjFactory.createPointFigure();
CreationTool creationTool = creationToolFactory.createCreationTool(lpf);
buttonCreatePoint = pointToolButton(toolBarCreation, editor, creationTool);
creationTool.setToolDoneAfterCreation(false);
// --- Create Location Figure ---
LabeledLocationFigure llf = crsObjFactory.createLocationFigure();
creationTool = creationToolFactory.createCreationTool(llf);
buttonCreateLocation
= addToolButton(
toolBarCreation,
editor,
creationTool,
labels.getString("toolBarManager.button_createLocation.tooltipText"),
ImageDirectory.getImageIcon("/toolbar/location.22.png")
);
creationTool.setToolDoneAfterCreation(false);
// --- Create Path Figure ---
PathConnection pc = crsObjFactory.createPathConnection();
ConnectionTool connectionTool = creationToolFactory.createConnectionTool(pc);
buttonCreatePath = pathToolButton(toolBarCreation, editor, connectionTool);
connectionTool.setToolDoneAfterCreation(false);
// --- Create Link ---
LinkConnection lc = crsObjFactory.createLinkConnection();
connectionTool = creationToolFactory.createConnectionTool(lc);
buttonCreateLink
= addToolButton(
toolBarCreation,
editor,
connectionTool,
labels.getString("toolBarManager.button_createLink.tooltipText"),
ImageDirectory.getImageIcon("/toolbar/link.22.png")
);
connectionTool.setToolDoneAfterCreation(false);
toolBarCreation.addSeparator();
// --- Location Type: No Figure, just creates a tree entry ---
buttonCreateLocationType = new JButton(actionMap.get(CreateLocationTypeAction.ID));
buttonCreateLocationType.setText(null);
toolBarCreation.add(buttonCreateLocationType);
// --- Create Vehicle Figure ---
buttonCreateVehicle = new JButton(actionMap.get(CreateVehicleAction.ID));
buttonCreateVehicle.setText(null);
toolBarCreation.add(buttonCreateVehicle);
// --- Create Block ---
buttonCreateBlock = new JButton(actionMap.get(CreateBlockAction.ID));
buttonCreateBlock.setText(null);
toolBarCreation.add(buttonCreateBlock);
toolBarCreation.addSeparator();
toolBarCreation.setName(labels.getString("toolBarManager.toolbar_drawing.title"));
toolBarList.add(toolBarCreation);
// --- 3. ToolBar: Alignment ---
// Align: West, East, Horizontal; North, South, Vertical
// Move: West, East, North, South
// Bring to front, Send to back
ButtonFactory.addAlignmentButtonsTo(toolBarAlignment, editor);
toolBarAlignment.setName(labels.getString("toolBarManager.toolbar_alignment.title"));
toolBarList.add(toolBarAlignment);
}
public List<JToolBar> getToolBars() {
return toolBarList;
}
public JToolBar getToolBarCreation() {
return toolBarCreation;
}
public JToggleButton getSelectionToolButton() {
return selectionToolButton;
}
public JToggleButton getDragToolButton() {
return dragToolButton;
}
public JPopupButton getButtonCreatePath() {
return buttonCreatePath;
}
public JToggleButton getButtonCreateLink() {
return buttonCreateLink;
}
@Override
public void onEvent(Object event) {
if (event instanceof ResetInteractionToolCommand) {
handleToolReset((ResetInteractionToolCommand) event);
}
}
private void handleToolReset(ResetInteractionToolCommand evt) {
selectionToolButton.setSelected(true);
}
/**
* Adds the selection tool to the given toolbar.
*
* @param toolBar The toolbar to add to.
* @param editor The DrawingEditor.
*/
private JToggleButton addSelectionToolButton(
JToolBar toolBar,
DrawingEditor editor
) {
List<Action> drawingActions = new ArrayList<>();
// Drawing Actions
drawingActions.add(new SelectSameAction(editor));
MultipleSelectionTool selectionTool
= selectionToolFactory.createMultipleSelectionTool(drawingActions, new ArrayList<>());
ButtonGroup buttonGroup;
if (toolBar.getClientProperty("toolButtonGroup") instanceof ButtonGroup) {
buttonGroup = (ButtonGroup) toolBar.getClientProperty("toolButtonGroup");
}
else {
buttonGroup = new ButtonGroup();
toolBar.putClientProperty("toolButtonGroup", buttonGroup);
}
// Selection tool
editor.setTool(selectionTool);
final JToggleButton toggleButton = new JToggleButton();
if (!(toolBar.getClientProperty("toolHandler") instanceof ToolListener)) {
ToolListener toolHandler = new ToolAdapter() {
@Override
public void toolDone(ToolEvent event) {
toggleButton.setSelected(true);
}
};
toolBar.putClientProperty("toolHandler", toolHandler);
}
toggleButton.setIcon(ImageDirectory.getImageIcon("/toolbar/select-2.png"));
toggleButton.setText(null);
toggleButton.setToolTipText(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.TOOLBAR_PATH)
.getString("toolBarManager.button_selectionTool.tooltipText")
);
toggleButton.setSelected(true);
toggleButton.addItemListener(new ToolButtonListener(selectionTool, editor));
buttonGroup.add(toggleButton);
toolBar.add(toggleButton);
return toggleButton;
}
/**
*
* @param toolBar
* @param editor
*/
private JToggleButton addDragToolButton(JToolBar toolBar, DrawingEditor editor) {
final JToggleButton button = new JToggleButton();
dragTool = new DragTool();
editor.setTool(dragTool);
if (!(toolBar.getClientProperty("toolHandler") instanceof ToolListener)) {
ToolListener toolHandler = new ToolAdapter() {
@Override
public void toolDone(ToolEvent event) {
button.setSelected(true);
}
};
toolBar.putClientProperty("toolHandler", toolHandler);
}
URL url = getClass().getResource(ImageDirectory.DIR + "/toolbar/cursor-opened-hand.png");
button.setIcon(new ImageIcon(url));
button.setText(null);
button.setToolTipText(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.TOOLBAR_PATH)
.getString("toolBarManager.button_dragTool.tooltipText")
);
button.setSelected(false);
button.addItemListener(new ToolButtonListener(dragTool, editor));
ButtonGroup group = (ButtonGroup) toolBar.getClientProperty("toolButtonGroup");
group.add(button);
toolBar.add(button);
return button;
}
/**
* Configures a JPopupButton with all available Point types.
*
* @param toolBar
* @param editor OpenTCSDrawingEditor
* @param tool CreationTool
* @param labelKey
* @param labels
* @return
*/
private JPopupButton pointToolButton(
JToolBar toolBar,
DrawingEditor editor,
Tool tool
) {
JPopupButton popupButton = new JPopupButton();
ButtonGroup group = (ButtonGroup) toolBar.getClientProperty("toolButtonGroup");
popupButton.setAction(
new DefaultPointSelectedAction(editor, tool, popupButton, group),
new Rectangle(0, 0, 16, 16)
);
ToolListener toolHandler = (ToolListener) toolBar.getClientProperty("toolHandler");
tool.addToolListener(toolHandler);
for (PointModel.Type type : PointModel.Type.values()) {
DefaultPointSelectedAction action
= new DefaultPointSelectedAction(editor, tool, type, popupButton, group);
popupButton.add(action);
action.setEnabled(true);
}
popupButton.setText(null);
popupButton.setToolTipText(PointModel.Type.values()[0].getHelptext());
popupButton.setIcon(ImageDirectory.getImageIcon("/toolbar/point-halt-arrow.22.png"));
popupButton.setFocusable(true);
group.add(popupButton);
toolBar.add(popupButton);
return popupButton;
}
/**
* Method addSelectionToolButton must have been invoked prior to this on the
* JToolBar.
*
* @param toolBar
* @param editor
* @param tool
* @param toolTipText
* @param labels
* @return
*/
private JToggleButton addToolButton(
JToolBar toolBar,
DrawingEditor editor,
Tool tool,
String toolTipText,
ImageIcon iconBase
) {
JToggleButton toggleButton = new JToggleButton();
toggleButton.setIcon(iconBase);
toggleButton.setText(null);
toggleButton.setToolTipText(toolTipText);
toggleButton.addItemListener(new ToolButtonListener(tool, editor));
ToolListener toolHandler = (ToolListener) toolBar.getClientProperty("toolHandler");
tool.addToolListener(toolHandler);
ButtonGroup group = (ButtonGroup) toolBar.getClientProperty("toolButtonGroup");
group.add(toggleButton);
toolBar.add(toggleButton);
return toggleButton;
}
/**
* Configures a JPopupButton with all available path types.
*
* @param toolBar
* @param editor
* @param tool
* @param labels
* @param types
* @return
*/
private JPopupButton pathToolButton(
JToolBar toolBar,
DrawingEditor editor,
Tool tool
) {
JPopupButton popupButton = new JPopupButton();
ButtonGroup group = (ButtonGroup) toolBar.getClientProperty("toolButtonGroup");
popupButton.setAction(
new DefaultPathSelectedAction(editor, tool, popupButton, group),
new Rectangle(0, 0, 16, 16)
);
ToolListener toolHandler = (ToolListener) toolBar.getClientProperty("toolHandler");
tool.addToolListener(toolHandler);
for (PathModel.Type type : PathModel.Type.values()) {
DefaultPathSelectedAction action
= new DefaultPathSelectedAction(editor, tool, type, popupButton, group);
popupButton.add(action);
action.setEnabled(true);
}
popupButton.setText(null);
popupButton.setToolTipText(PathModel.Type.values()[0].getHelptext());
popupButton.setIcon(ImageDirectory.getImageIcon("/toolbar/path-direct-arrow.22.png"));
popupButton.setFocusable(true);
group.add(popupButton);
toolBar.add(popupButton);
return popupButton;
}
}

View File

@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.ActionMap;
import org.opentcs.guing.common.application.action.file.ModelPropertiesAction;
import org.opentcs.guing.common.application.action.file.SaveModelAction;
import org.opentcs.guing.common.application.action.file.SaveModelAsAction;
import org.opentcs.modeleditor.application.OpenTCSView;
import org.opentcs.modeleditor.application.action.actions.CreateBlockAction;
import org.opentcs.modeleditor.application.action.actions.CreateLocationTypeAction;
import org.opentcs.modeleditor.application.action.actions.CreateVehicleAction;
import org.opentcs.modeleditor.application.action.app.AboutAction;
import org.opentcs.modeleditor.application.action.file.DownloadModelFromKernelAction;
import org.opentcs.modeleditor.application.action.file.LoadModelAction;
import org.opentcs.modeleditor.application.action.file.NewModelAction;
import org.opentcs.modeleditor.application.action.file.UploadModelToKernelAction;
import org.opentcs.modeleditor.application.action.view.AddBitmapAction;
import org.opentcs.modeleditor.application.action.view.RestoreDockingLayoutAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.DeleteAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.SelectAllAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.ClearSelectionAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.CopyAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.CutAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.DuplicateAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.PasteAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.file.CloseFileAction;
/**
* A custom ActionMap for the plant overview application.
*/
public class ViewActionMap
extends
ActionMap {
/**
* Creates a new instance.
*
* @param view The openTCS view
* @param undoRedoManager The undo redo manager
* @param aboutAction The action to show the about window
* @param modelPropertiesAction The action to show some model properties.
* @param loadModelFromKernelAction The action to load the current kernel model.
*/
@Inject
@SuppressWarnings("this-escape")
public ViewActionMap(
OpenTCSView view,
UndoRedoManager undoRedoManager,
AboutAction aboutAction,
ModelPropertiesAction modelPropertiesAction,
DownloadModelFromKernelAction loadModelFromKernelAction
) {
requireNonNull(view, "view");
requireNonNull(undoRedoManager, "undoRedoManager");
requireNonNull(aboutAction, "aboutAction");
// --- Menu File ---
put(NewModelAction.ID, new NewModelAction(view));
put(LoadModelAction.ID, new LoadModelAction(view));
put(SaveModelAction.ID, new SaveModelAction(view));
put(SaveModelAsAction.ID, new SaveModelAsAction(view));
put(ModelPropertiesAction.ID, modelPropertiesAction);
put(CloseFileAction.ID, new CloseFileAction(view));
// --- Menu Synchronize ---
put(UploadModelToKernelAction.ID, new UploadModelToKernelAction(view));
put(DownloadModelFromKernelAction.ID, loadModelFromKernelAction);
// --- Menu Edit ---
// Undo, Redo
put(UndoRedoManager.UNDO_ACTION_ID, undoRedoManager.getUndoAction());
put(UndoRedoManager.REDO_ACTION_ID, undoRedoManager.getRedoAction());
// Cut, Copy, Paste, Duplicate, Delete
put(CutAction.ID, new CutAction());
put(CopyAction.ID, new CopyAction());
put(PasteAction.ID, new PasteAction());
put(DuplicateAction.ID, new DuplicateAction());
put(DeleteAction.ID, new DeleteAction());
// Select all, Clear selection
put(SelectAllAction.ID, new SelectAllAction());
put(ClearSelectionAction.ID, new ClearSelectionAction());
// --- Menu Actions ---
// Menu item Actions -> Create ...
put(CreateLocationTypeAction.ID, new CreateLocationTypeAction(view));
put(CreateVehicleAction.ID, new CreateVehicleAction(view));
put(CreateBlockAction.ID, new CreateBlockAction(view));
// --- Menu View ---
put(AddBitmapAction.ID, new AddBitmapAction(view));
put(RestoreDockingLayoutAction.ID, new RestoreDockingLayoutAction(view));
// --- Menu Help ---
put(AboutAction.ID, aboutAction);
}
}

View File

@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.actions;
import static javax.swing.Action.LARGE_ICON_KEY;
import static javax.swing.Action.SMALL_ICON;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.TOOLBAR_PATH;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import org.opentcs.guing.common.application.GuiManagerModeling;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* An action to trigger the creation of a block.
*/
public class CreateBlockAction
extends
AbstractAction {
/**
* This action class's ID.
*/
public static final String ID = "openTCS.createBlock";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(TOOLBAR_PATH);
/**
* The GUI manager instance we're working with.
*/
private final GuiManagerModeling guiManager;
/**
* Creates a new instance.
*
* @param guiManager The GUI manager instance we're working with.
*/
@SuppressWarnings("this-escape")
public CreateBlockAction(GuiManagerModeling guiManager) {
this.guiManager = guiManager;
putValue(NAME, BUNDLE.getString("createBlockAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("createBlockAction.shortDescription"));
ImageIcon iconSmall = ImageDirectory.getImageIcon("/toolbar/blockdevice-3.16.png");
ImageIcon iconLarge = ImageDirectory.getImageIcon("/toolbar/blockdevice-3.22.png");
putValue(SMALL_ICON, iconSmall);
putValue(LARGE_ICON_KEY, iconLarge);
}
@Override
public void actionPerformed(ActionEvent evt) {
guiManager.createBlockModel();
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.actions;
import static javax.swing.Action.LARGE_ICON_KEY;
import static javax.swing.Action.SMALL_ICON;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.TOOLBAR_PATH;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import org.opentcs.guing.common.application.GuiManagerModeling;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* An action to trigger the creation of a location type.
*/
public class CreateLocationTypeAction
extends
AbstractAction {
/**
* This action class's ID.
*/
public static final String ID = "openTCS.createLocationType";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(TOOLBAR_PATH);
/**
* The GUI manager instance we're working with.
*/
private final GuiManagerModeling guiManager;
/**
* Creates a new instance.
*
* @param guiManager The GUI manager instance we're working with.
*/
@SuppressWarnings("this-escape")
public CreateLocationTypeAction(GuiManagerModeling guiManager) {
this.guiManager = guiManager;
putValue(NAME, BUNDLE.getString("createLocationTypeAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("createLocationTypeAction.shortDescription"));
ImageIcon icon = ImageDirectory.getImageIcon("/toolbar/locationType.22.png");
putValue(SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
}
@Override
public void actionPerformed(ActionEvent evt) {
guiManager.createLocationTypeModel();
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.actions;
import static javax.swing.Action.LARGE_ICON_KEY;
import static javax.swing.Action.SMALL_ICON;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.TOOLBAR_PATH;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import org.opentcs.guing.common.application.GuiManagerModeling;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* An action to trigger the creation of a vehicle.
*/
public class CreateVehicleAction
extends
AbstractAction {
/**
* This action class's ID.
*/
public static final String ID = "openTCS.createVehicle";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(TOOLBAR_PATH);
/**
* The GUI manager instance we're working with.
*/
private final GuiManagerModeling guiManager;
/**
* Creates a new instance.
*
* @param guiManager The GUI manager instance we're working with.
*/
@SuppressWarnings("this-escape")
public CreateVehicleAction(GuiManagerModeling guiManager) {
this.guiManager = guiManager;
putValue(NAME, BUNDLE.getString("createVehicleAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("createVehicleAction.shortDescription"));
ImageIcon icon = ImageDirectory.getImageIcon("/toolbar/car.png");
putValue(SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
}
@Override
public void actionPerformed(ActionEvent evt) {
guiManager.createVehicleModel();
}
}

View File

@@ -0,0 +1,136 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.app;
import static java.util.Objects.requireNonNull;
import static javax.swing.Action.LARGE_ICON_KEY;
import static javax.swing.Action.MNEMONIC_KEY;
import static javax.swing.Action.SMALL_ICON;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import jakarta.inject.Inject;
import java.awt.Component;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import org.opentcs.access.SharedKernelServicePortalProvider;
import org.opentcs.customizations.plantoverview.ApplicationFrame;
import org.opentcs.guing.common.application.ApplicationState;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.modeleditor.application.OpenTCSView;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.util.Environment;
/**
* Displays a dialog showing information about the application.
*/
public class AboutAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "application.about";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
/**
* Stores the application's current state.
*/
private final ApplicationState appState;
/**
* Provides access to a portal.
*/
private final SharedKernelServicePortalProvider portalProvider;
/**
* The parent component for dialogs shown by this action.
*/
private final Component dialogParent;
/**
* Creates a new instance.
*
* @param appState Stores the application's current state.
* @param portalProvider Provides access to a portal.
* @param dialogParent The parent component for dialogs shown by this action.
*/
@Inject
@SuppressWarnings("this-escape")
public AboutAction(
ApplicationState appState,
SharedKernelServicePortalProvider portalProvider,
@ApplicationFrame
Component dialogParent
) {
this.appState = requireNonNull(appState, "appState");
this.portalProvider = requireNonNull(portalProvider, "portalProvider");
this.dialogParent = requireNonNull(dialogParent, "dialogParent");
putValue(NAME, BUNDLE.getString("aboutAction.name"));
putValue(MNEMONIC_KEY, Integer.valueOf('A'));
ImageIcon icon = ImageDirectory.getImageIcon("/menu/help-contents.png");
putValue(SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
}
@Override
public void actionPerformed(ActionEvent evt) {
JOptionPane.showMessageDialog(
dialogParent,
"<html><p><b>" + OpenTCSView.NAME + "</b><br> "
+ BUNDLE.getFormatted(
"aboutAction.optionPane_applicationInformation.message.baselineVersion",
Environment.getBaselineVersion()
)
+ "<br>"
+ BUNDLE.getFormatted(
"aboutAction.optionPane_applicationInformation.message.customization",
Environment.getCustomizationName(),
Environment.getCustomizationVersion()
)
+ "<br>"
+ BUNDLE.getString(
"aboutAction.optionPane_applicationInformation.message.copyright"
)
+ "<br>"
+ BUNDLE.getString(
"aboutAction.optionPane_applicationInformation.message.runningOn"
)
+ "<br>"
+ "Java: "
+ System.getProperty("java.version")
+ ", "
+ System.getProperty("java.vendor")
+ "<br>"
+ "JVM: "
+ System.getProperty("java.vm.version")
+ ", "
+ System.getProperty("java.vm.vendor")
+ "<br>"
+ "OS: "
+ System.getProperty("os.name")
+ " "
+ System.getProperty("os.version")
+ ", "
+ System.getProperty("os.arch")
+ "<br>"
+ "<b>Kernel</b><br>"
+ portalProvider.getPortalDescription()
+ "<br>"
+ BUNDLE.getFormatted(
"aboutAction.optionPane_applicationInformation.message.mode",
appState.getOperationMode()
)
+ "</p></html>",
BUNDLE.getString(
"aboutAction.optionPane_applicationInformation.title"
),
JOptionPane.PLAIN_MESSAGE,
new ImageIcon(
getClass().getResource("/org/opentcs/guing/res/symbols/openTCS/openTCS.300x132.gif")
)
);
}
}

View File

@@ -0,0 +1,146 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.draw;
import static java.util.Objects.requireNonNull;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import org.jhotdraw.draw.DrawingEditor;
import org.jhotdraw.draw.action.AbstractSelectedAction;
import org.jhotdraw.draw.tool.CreationTool;
import org.jhotdraw.draw.tool.Tool;
import org.jhotdraw.gui.JPopupButton;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.components.drawing.figures.LabeledPointFigure;
import org.opentcs.guing.common.components.drawing.figures.PointFigure;
import org.opentcs.guing.common.util.ImageDirectory;
/**
* This action manages the behaviour when the user selects the point button.
*/
public class DefaultPointSelectedAction
extends
AbstractSelectedAction {
/**
* The SelectionProperty contains all point types in the model.
*/
private final PointModel.Type pointType;
private final Tool tool;
/**
* The button this action belongs to.
*/
private final JPopupButton popupButton;
/**
* The Icon the popup button uses when this action is selected.
*/
private final ImageIcon largeIcon;
/**
* The ButtonGroup the popupButton belongs to. It is necessary to know it,
* because
* <code>DrawingEditor.setTool()</code> doesn't select or deselect the
* popupButton, so we have to do it manually.
*/
private final ButtonGroup group;
/**
* Constructor for an action of a button in the toolbar.
*
* @param editor The drawing editor
* @param tool The tool
* @param popupButton The popup button
* @param group The button group
*/
public DefaultPointSelectedAction(
DrawingEditor editor,
Tool tool,
JPopupButton popupButton,
ButtonGroup group
) {
super(editor);
this.tool = requireNonNull(tool);
this.popupButton = requireNonNull(popupButton);
this.group = requireNonNull(group);
this.pointType = null;
this.largeIcon = null;
}
/**
* Constructor for a button inside a drop down menu of another button.
*
* @param editor The drawing editor
* @param tool The tool
* @param pointType The point type
* @param popupButton The popup button
* @param group The button group
*/
@SuppressWarnings("this-escape")
public DefaultPointSelectedAction(
DrawingEditor editor,
Tool tool,
PointModel.Type pointType,
JPopupButton popupButton,
ButtonGroup group
) {
super(editor);
this.pointType = requireNonNull(pointType);
this.tool = requireNonNull(tool);
this.popupButton = requireNonNull(popupButton);
this.group = requireNonNull(group);
this.largeIcon = getLargeImageIconByType(pointType);
putValue(AbstractAction.NAME, pointType.getDescription());
putValue(AbstractAction.SHORT_DESCRIPTION, pointType.getHelptext());
putValue(AbstractAction.SMALL_ICON, getImageIconByType(pointType));
}
@Override
public void actionPerformed(ActionEvent e) {
if (pointType != null) {
CreationTool creationTool = (CreationTool) tool;
LabeledPointFigure lpf = (LabeledPointFigure) creationTool.getPrototype();
PointFigure pointFigure = lpf.getPresentationFigure();
pointFigure.getModel().getPropertyType().setValue(pointType);
popupButton.setText(null);
popupButton.setToolTipText(pointType.getHelptext());
popupButton.setIcon(largeIcon);
}
getEditor().setTool(tool);
group.setSelected(popupButton.getModel(), true);
}
@Override
protected void updateEnabledState() {
setEnabled(getView() != null && getView().isEnabled());
}
private ImageIcon getImageIconByType(PointModel.Type pointType) {
switch (pointType) {
case HALT:
return ImageDirectory.getImageIcon("/toolbar/point-halt.22.png");
case PARK:
return ImageDirectory.getImageIcon("/toolbar/point-park.22.png");
default:
return null;
}
}
private ImageIcon getLargeImageIconByType(PointModel.Type pointType) {
switch (pointType) {
case HALT:
return ImageDirectory.getImageIcon("/toolbar/point-halt-arrow.22.png");
case PARK:
return ImageDirectory.getImageIcon("/toolbar/point-park-arrow.22.png");
default:
return null;
}
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static java.util.Objects.requireNonNull;
import static javax.swing.Action.ACCELERATOR_KEY;
import static javax.swing.Action.MNEMONIC_KEY;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import jakarta.inject.Inject;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import org.opentcs.modeleditor.application.OpenTCSView;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* An action to load the current kernel model in the plant overview.
*/
public class DownloadModelFromKernelAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "file.downloadModelFromKernel";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
/**
* The OpenTCS view.
*/
private final OpenTCSView openTCSView;
/**
* Creates a new instance.
*
* @param openTCSView The openTCS view.
*/
@Inject
@SuppressWarnings("this-escape")
public DownloadModelFromKernelAction(OpenTCSView openTCSView) {
this.openTCSView = requireNonNull(openTCSView);
putValue(NAME, BUNDLE.getString("downloadModelFromKernelAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("downloadModelFromKernelAction.shortDescription"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt K"));
putValue(MNEMONIC_KEY, Integer.valueOf('K'));
}
@Override
public void actionPerformed(ActionEvent e) {
openTCSView.downloadModelFromKernel();
}
}

View File

@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static java.util.Objects.requireNonNull;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.opentcs.components.plantoverview.PlantModelExporter;
import org.opentcs.guing.common.application.GuiManager;
/**
*/
public class ExportPlantModelAction
extends
AbstractAction {
private final PlantModelExporter exporter;
private final GuiManager guiManager;
/**
* Creates a new instance.
*
* @param exporter The importer.
* @param guiManager The gui manager
*/
@SuppressWarnings("this-escape")
public ExportPlantModelAction(PlantModelExporter exporter, GuiManager guiManager) {
this.exporter = requireNonNull(exporter, "exporter");
this.guiManager = requireNonNull(guiManager, "guiManager");
this.putValue(NAME, exporter.getDescription());
}
@Override
public void actionPerformed(ActionEvent evt) {
guiManager.exportModel(exporter);
}
}

View File

@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static java.util.Objects.requireNonNull;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.opentcs.components.plantoverview.PlantModelImporter;
import org.opentcs.guing.common.application.GuiManager;
/**
*/
public class ImportPlantModelAction
extends
AbstractAction {
private final PlantModelImporter importer;
private final GuiManager guiManager;
/**
* Creates a new instance.
*
* @param importer The importer.
* @param guiManager The gui manager
*/
@SuppressWarnings("this-escape")
public ImportPlantModelAction(PlantModelImporter importer, GuiManager guiManager) {
this.importer = requireNonNull(importer, "importer");
this.guiManager = requireNonNull(guiManager, "guiManager");
this.putValue(NAME, importer.getDescription());
}
@Override
public void actionPerformed(ActionEvent evt) {
guiManager.importModel(importer);
}
}

View File

@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class LoadModelAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "file.loadModel";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
private final GuiManager view;
/**
* Creates a new instance.
*
* @param view The gui manager
*/
@SuppressWarnings("this-escape")
public LoadModelAction(GuiManager view) {
this.view = view;
putValue(NAME, BUNDLE.getString("loadModelAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("loadModelAction.shortDescription"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl L"));
putValue(MNEMONIC_KEY, Integer.valueOf('L'));
ImageIcon icon = ImageDirectory.getImageIcon("/menu/document-import-2.png");
putValue(SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
}
@Override
public void actionPerformed(ActionEvent evt) {
view.loadModel();
}
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.guing.common.util.ImageDirectory;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class NewModelAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "file.newModel";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
private final GuiManager view;
/**
* Creates a new instance.
*
* @param view The gui manager
*/
@SuppressWarnings("this-escape")
public NewModelAction(GuiManager view) {
this.view = view;
putValue(NAME, BUNDLE.getString("newModelAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("newModelAction.shortDescription"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl N"));
putValue(MNEMONIC_KEY, Integer.valueOf('N'));
ImageIcon icon = ImageDirectory.getImageIcon("/menu/document-new.png");
putValue(SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
}
@Override
public void actionPerformed(ActionEvent evt) {
view.createEmptyModel();
}
}

View File

@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.file;
import static javax.swing.Action.ACCELERATOR_KEY;
import static javax.swing.Action.MNEMONIC_KEY;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import java.awt.event.ActionEvent;
import java.util.Objects;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import org.opentcs.modeleditor.application.OpenTCSView;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* An action to upload the (local) model to the kernel.
*/
public class UploadModelToKernelAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "file.uploadModelToKernel";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
private final OpenTCSView openTCSView;
/**
* Creates a new instance.
*
* @param openTCSView The openTCS view
*/
@SuppressWarnings("this-escape")
public UploadModelToKernelAction(OpenTCSView openTCSView) {
this.openTCSView = Objects.requireNonNull(openTCSView);
putValue(NAME, BUNDLE.getString("uploadModelToKernelAction.name"));
putValue(SHORT_DESCRIPTION, BUNDLE.getString("uploadModelToKernelAction.shortDescription"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt P"));
putValue(MNEMONIC_KEY, Integer.valueOf('P'));
}
@Override
public void actionPerformed(ActionEvent e) {
openTCSView.uploadModelToKernel();
}
}

View File

@@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.view;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.MENU_PATH;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Objects;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.opentcs.modeleditor.application.OpenTCSView;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* Actions for adding background bitmaps to the drawing view.
*/
public class AddBitmapAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "view.addBitmap";
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
private final JFileChooser fc;
private final OpenTCSView view;
/**
* Creates a new instance.
*
* @param view The openTCS view
*/
@SuppressWarnings("this-escape")
public AddBitmapAction(OpenTCSView view) {
this.view = Objects.requireNonNull(view, "view");
this.fc = new JFileChooser(System.getProperty("opentcs.home"));
this.fc.setFileFilter(
new FileNameExtensionFilter(
"Bitmaps (PNG, JPG, BMP, GIF)",
"png",
"jpg",
"bmp",
"gif"
)
);
this.fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
putValue(NAME, BUNDLE.getString("addBitmapAction.name"));
}
@Override
public void actionPerformed(ActionEvent e) {
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
view.addBackgroundBitmap(file);
}
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.action.view;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import org.opentcs.modeleditor.application.OpenTCSView;
/**
* Action for resetting the docking layout.
*/
public class RestoreDockingLayoutAction
extends
AbstractAction {
/**
* This action's ID.
*/
public static final String ID = "openTCS.restoreDockingLayout";
private final OpenTCSView view;
/**
* Creates a new instance.
*
* @param view The openTCS view
*/
public RestoreDockingLayoutAction(OpenTCSView view) {
this.view = view;
}
@Override
public void actionPerformed(ActionEvent e) {
view.resetWindowArrangement();
}
}

View File

@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.awt.event.ActionEvent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import org.jhotdraw.draw.DrawingEditor;
import org.opentcs.guing.base.components.properties.event.NullAttributesChangeListener;
import org.opentcs.guing.base.components.properties.type.LengthProperty;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.common.components.dialogs.StandardContentDialog;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.figures.FigureConstants;
import org.opentcs.guing.common.components.properties.PropertyUndoActivity;
import org.opentcs.modeleditor.components.dialog.PathTypeSelectionPanel;
import org.opentcs.modeleditor.math.path.PathLengthFunction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.util.gui.Icons;
/**
* A menu item to calculate and update the lengths of paths.
*/
public class CalculatePathLengthMenuItem
extends
JMenuItem {
/**
* The <code>DrawingEditor</code> instance.
*/
private final DrawingEditor drawingEditor;
/**
* The UndoRedoManager instance to be used.
*/
private final UndoRedoManager undoRedoManager;
/**
* The calculator to use to calculate the path length.
*/
private final Provider<PathLengthFunction> pathLengthFunctionProvider;
@Inject
@SuppressWarnings("this-escape")
public CalculatePathLengthMenuItem(
OpenTCSDrawingEditor drawingEditor,
UndoRedoManager undoRedoManager,
Provider<PathLengthFunction> pathLengthFunctionProvider
) {
super(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH)
.getString("calculatePathLengthMenuItem.text")
);
this.drawingEditor = requireNonNull(drawingEditor, "drawingEditor");
this.undoRedoManager = requireNonNull(undoRedoManager, "undoRedoManager");
this.pathLengthFunctionProvider
= requireNonNull(pathLengthFunctionProvider, "pathLengthFunctionProvider");
addActionListener(this::calculatePathLength);
}
private void calculatePathLength(ActionEvent e) {
PathTypeSelectionPanel content = new PathTypeSelectionPanel();
StandardContentDialog dialog
= new StandardContentDialog(JOptionPane.getFrameForComponent(this), content);
dialog.setIconImages(Icons.getOpenTCSIcons());
dialog.setVisible(true);
if (dialog.getReturnStatus() != StandardContentDialog.RET_OK) {
return;
}
PathLengthFunction pathLengthFunction = pathLengthFunctionProvider.get();
drawingEditor.getActiveView().getDrawing().getFiguresFrontToBack().stream()
.map(figure -> figure.get(FigureConstants.MODEL))
.filter(model -> model instanceof PathModel)
.map(model -> (PathModel) model)
.filter(path -> content.isPathTypeSelected(connectionType(path)))
.forEach(path -> updatePath(path, pathLengthFunction));
}
private PathModel.Type connectionType(PathModel path) {
return (PathModel.Type) path.getPropertyPathConnType().getValue();
}
private void updatePath(PathModel path, PathLengthFunction pathLengthFunction) {
updatePathLength(path, Math.round(pathLengthFunction.applyAsDouble(path)));
path.propertiesChanged(new NullAttributesChangeListener());
}
private void updatePathLength(PathModel path, double length) {
LengthProperty pathLengthProperty = path.getPropertyLength();
PropertyUndoActivity pua = new PropertyUndoActivity(pathLengthProperty);
pua.snapShotBeforeModification();
pathLengthProperty.setValueAndUnit(length, LengthProperty.Unit.MM);
pathLengthProperty.markChanged();
pua.snapShotAfterModification();
undoRedoManager.addEdit(pua);
}
}

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
import org.opentcs.guing.common.components.properties.CoordinateUndoActivity;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class LayoutToModelCoordinateUndoActivity
extends
CoordinateUndoActivity {
@Inject
public LayoutToModelCoordinateUndoActivity(
@Assisted
CoordinateProperty property,
ModelManager modelManager
) {
super(property, modelManager);
}
@Override
public String getPresentationName() {
return ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MISC_PATH)
.getString("layoutToModelCoordinateUndoActivity.presentationName");
}
@Override
protected void saveTransformBeforeModification() {
}
@Override
protected void saveTransformForUndo() {
}
@Override
protected void saveTransformForRedo() {
}
}

View File

@@ -0,0 +1,185 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import static java.util.Objects.requireNonNull;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import org.jhotdraw.draw.DrawingEditor;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.data.model.visualization.ElementPropKeys;
import org.opentcs.guing.base.components.properties.event.NullAttributesChangeListener;
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
import org.opentcs.guing.base.components.properties.type.StringProperty;
import org.opentcs.guing.base.model.ModelComponent;
import org.opentcs.guing.base.model.elements.LocationModel;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.figures.FigureConstants;
import org.opentcs.guing.common.components.properties.CoordinateUndoActivity;
import org.opentcs.guing.common.event.ResetInteractionToolCommand;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.util.event.EventHandler;
/**
* A menu item for copying the value of the layout properties of selected points
* or locations to the corresponding model properties.
*/
public class LayoutToModelMenuItem
extends
JMenuItem {
/**
* The <code>DrawingEditor</code> instance.
*/
private final DrawingEditor drawingEditor;
/**
* The UndoRedoManager instance to be used.
*/
private final UndoRedoManager undoRedoManager;
/**
* Where we send events.
*/
private final EventHandler eventHandler;
/**
* The components factory.
*/
private final MenuItemComponentsFactory componentsFactory;
/**
* A flag if the values of ALL points and location shall be copied when
* the menu item is clicked. If false only the selected figures will be
* considered.
*/
private final boolean copyAll;
/**
* Creates a new instance.
*
* @param drawingEditor A <code>DrawingEditor</code> instance.
* @param undoRedoManager The application's undo/redo manager.
* @param eventHandler Where this instance sends events.
* @param componentsFactory The components factory.
* @param copyAll Indicates whether the values of ALL points and locations
* shall be copied when the menu item is clicked. If false only the selected
* figures will be considered.
*/
@Inject
@SuppressWarnings("this-escape")
public LayoutToModelMenuItem(
OpenTCSDrawingEditor drawingEditor,
UndoRedoManager undoRedoManager,
@ApplicationEventBus
EventHandler eventHandler,
MenuItemComponentsFactory componentsFactory,
@Assisted
boolean copyAll
) {
super(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH)
.getString("layoutToModelMenuItem.text")
);
this.drawingEditor = requireNonNull(drawingEditor, "drawingEditor");
this.undoRedoManager = requireNonNull(undoRedoManager, "undoRedoManager");
this.eventHandler = requireNonNull(eventHandler, "eventHandler");
this.componentsFactory = requireNonNull(componentsFactory, "componentsFactory");
this.copyAll = copyAll;
setIcon(
new ImageIcon(
getClass().getClassLoader()
.getResource("org/opentcs/guing/res/symbols/menu/arrow-up-3.png")
)
);
setMargin(new Insets(0, 2, 0, 2));
addActionListener();
}
private void addActionListener() {
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Figure figure : copyAll
? getView().getDrawing().getFiguresFrontToBack()
: getView().getSelectedFigures()) {
ModelComponent model = figure.get(FigureConstants.MODEL);
if (model instanceof PointModel || model instanceof LocationModel) {
updateModelX(model);
updateModelY(model);
model.propertiesChanged(new NullAttributesChangeListener());
eventHandler.onEvent(new ResetInteractionToolCommand(this));
}
}
}
});
}
private DrawingView getView() {
return drawingEditor.getActiveView();
}
private void updateModelY(ModelComponent model)
throws IllegalArgumentException {
CoordinateProperty modelProperty;
if (model instanceof PointModel) {
modelProperty = (CoordinateProperty) model.getProperty(PointModel.MODEL_Y_POSITION);
}
else {
modelProperty = (CoordinateProperty) model.getProperty(LocationModel.MODEL_Y_POSITION);
}
CoordinateUndoActivity cua
= componentsFactory.createLayoutToModelCoordinateUndoActivity(modelProperty);
cua.snapShotBeforeModification();
StringProperty spy;
if (model instanceof PointModel) {
spy = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_Y);
}
else {
spy = (StringProperty) model.getProperty(ElementPropKeys.LOC_POS_Y);
}
if (!spy.getText().isEmpty()) {
modelProperty.setValueAndUnit(Double.parseDouble(spy.getText()), modelProperty.getUnit());
modelProperty.markChanged();
}
cua.snapShotAfterModification();
undoRedoManager.addEdit(cua);
}
private void updateModelX(ModelComponent model)
throws IllegalArgumentException {
CoordinateProperty modelProperty;
if (model instanceof PointModel) {
modelProperty = (CoordinateProperty) model.getProperty(PointModel.MODEL_X_POSITION);
}
else {
modelProperty = (CoordinateProperty) model.getProperty(LocationModel.MODEL_X_POSITION);
}
CoordinateUndoActivity cua
= componentsFactory.createLayoutToModelCoordinateUndoActivity(modelProperty);
cua.snapShotBeforeModification();
StringProperty spx;
if (model instanceof PointModel) {
spx = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_X);
}
else {
spx = (StringProperty) model.getProperty(ElementPropKeys.LOC_POS_X);
}
if (!spx.getText().isEmpty()) {
modelProperty.setValueAndUnit(Double.parseDouble(spx.getText()), modelProperty.getUnit());
modelProperty.markChanged();
}
cua.snapShotAfterModification();
undoRedoManager.addEdit(cua);
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
/**
* A factory for various menus and menu items.
*/
public interface MenuFactory {
/**
* Creates a menu item for copying the value of the layout properties of
* selected elements to the corresponding model properties.
*
* @param copyAll Indicates whether the values of ALL points and locations
* shall be copied when the menu item is clicked. If false only the selected
* figures will be considered.
* @return The created menu item.
*/
LayoutToModelMenuItem createLayoutToModelMenuItem(boolean copyAll);
/**
*
* @param copyAll Indicates whether the values of ALL points and locations
* shall be copied when the menu item is clicked. If false only the selected
* figures will be considered.
* @return The created menu item.
*/
ModelToLayoutMenuItem createModelToLayoutMenuItem(boolean copyAll);
/**
* Creates a menu item for calculating the length of paths.
*
* @return The created menu item.
*/
CalculatePathLengthMenuItem createCalculatePathLengthMenuItem();
}

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
/**
* A factory for creating instances in relation to menu items.
*/
public interface MenuItemComponentsFactory {
/**
* Creates a {@link LayoutToModelCoordinateUndoActivity} for the given coordinate property.
*
* @param property The property.
* @return The {@link LayoutToModelCoordinateUndoActivity}.
*/
LayoutToModelCoordinateUndoActivity createLayoutToModelCoordinateUndoActivity(
CoordinateProperty property
);
/**
* Creates a {@link ModelToLayoutCoordinateUndoActivity} for the given coordinate property.
*
* @param property The property.
* @return The {@link ModelToLayoutCoordinateUndoActivity}.
*/
ModelToLayoutCoordinateUndoActivity createModelToLayoutCoordinateUndoActivity(
CoordinateProperty property
);
}

View File

@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import org.opentcs.data.model.visualization.ElementPropKeys;
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
import org.opentcs.guing.base.components.properties.type.StringProperty;
import org.opentcs.guing.base.model.AbstractConnectableModelComponent;
import org.opentcs.guing.common.components.drawing.course.Origin;
import org.opentcs.guing.common.components.drawing.figures.FigureConstants;
import org.opentcs.guing.common.components.drawing.figures.TCSFigure;
import org.opentcs.guing.common.components.properties.CoordinateUndoActivity;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*/
public class ModelToLayoutCoordinateUndoActivity
extends
CoordinateUndoActivity {
/**
* This class's logger.
*/
private static final Logger LOG
= LoggerFactory.getLogger(ModelToLayoutCoordinateUndoActivity.class);
@Inject
public ModelToLayoutCoordinateUndoActivity(
@Assisted
CoordinateProperty property,
ModelManager modelManager
) {
super(property, modelManager);
}
@Override
public String getPresentationName() {
return ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MISC_PATH)
.getString("modelToLayoutCoordinateUndoActivity.presentationName");
}
@Override
protected void saveTransformBeforeModification() {
AbstractConnectableModelComponent model
= (AbstractConnectableModelComponent) property.getModel();
StringProperty pxLayout = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_X);
StringProperty pyLayout = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_Y);
Origin origin = bufferedFigure.get(FigureConstants.ORIGIN);
TCSFigure pf = bufferedFigure.getPresentationFigure();
double zoomScale = pf.getZoomPoint().scale();
double xModel
= pxModel.getValueByUnit(CoordinateProperty.Unit.MM) / (zoomScale * origin.getScaleX());
double yModel
= pyModel.getValueByUnit(CoordinateProperty.Unit.MM) / (-zoomScale * origin.getScaleY());
String sx = (String) pxLayout.getComparableValue();
double xLayout = Double.parseDouble(sx) / (zoomScale * origin.getScaleX());
String sy = (String) pyLayout.getComparableValue();
double yLayout = Double.parseDouble(sy) / (-zoomScale * origin.getScaleY());
bufferedTransform.translate(xModel - xLayout, yModel - yLayout);
}
@Override
protected void saveTransformForUndo() {
try {
AffineTransform inverse = bufferedTransform.createInverse();
bufferedFigure.willChange();
bufferedFigure.transform(inverse);
bufferedFigure.changed();
}
catch (NoninvertibleTransformException e) {
LOG.warn("Exception inverting transform.", e);
}
}
@Override
protected void saveTransformForRedo() {
bufferedFigure.willChange();
bufferedFigure.transform(bufferedTransform);
bufferedFigure.changed();
}
}

View File

@@ -0,0 +1,192 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus;
import static java.util.Objects.requireNonNull;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import org.jhotdraw.draw.DrawingEditor;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.data.model.visualization.ElementPropKeys;
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
import org.opentcs.guing.base.components.properties.event.NullAttributesChangeListener;
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
import org.opentcs.guing.base.components.properties.type.ModelAttribute;
import org.opentcs.guing.base.components.properties.type.StringProperty;
import org.opentcs.guing.base.model.ModelComponent;
import org.opentcs.guing.base.model.elements.LocationModel;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.figures.FigureConstants;
import org.opentcs.guing.common.components.drawing.figures.LabeledFigure;
import org.opentcs.guing.common.components.properties.CoordinateUndoActivity;
import org.opentcs.guing.common.event.ResetInteractionToolCommand;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.util.event.EventHandler;
/**
* A menu item for copying the value of the model properties of selected points
* or locations to the corresponding layout properties.
*/
public class ModelToLayoutMenuItem
extends
JMenuItem {
/**
* The <code>DrawingEditor</code> instance.
*/
private final DrawingEditor drawingEditor;
/**
* The UndoRedoManager instance to be used.
*/
private final UndoRedoManager undoRedoManager;
/**
* Where we send events.
*/
private final EventHandler eventBus;
/**
* The components factory.
*/
private final MenuItemComponentsFactory componentsFactory;
/**
* A flag if the values of ALL points and location shall be copied when
* the menu item is clicked. If false only the selected figures will be
* considered.
*/
private final boolean copyAll;
/**
* Creates a new instance.
*
* @param drawingEditor A <code>DrawingEditor</code> instance.
* @param undoRedoManager The application's undo/redo manager.
* @param eventHandler Where this instance sends events.
* @param componentsFactory The components factory.
* @param copyAll Indicates whether the values of ALL points and locations
* shall be copied when the menu item is clicked. If false only the selected
* figures will be considered.
*/
@Inject
@SuppressWarnings("this-escape")
public ModelToLayoutMenuItem(
OpenTCSDrawingEditor drawingEditor,
UndoRedoManager undoRedoManager,
@ApplicationEventBus
EventHandler eventHandler,
MenuItemComponentsFactory componentsFactory,
@Assisted
boolean copyAll
) {
super(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH)
.getString("modelToLayoutMenuItem.text")
);
this.drawingEditor = requireNonNull(drawingEditor, "drawingEditor");
this.undoRedoManager = requireNonNull(undoRedoManager, "undoRedoManager");
this.eventBus = requireNonNull(eventHandler, "eventHandler");
this.componentsFactory = requireNonNull(componentsFactory, "componentsFactory");
this.copyAll = copyAll;
setIcon(
new ImageIcon(
getClass().getClassLoader()
.getResource("org/opentcs/guing/res/symbols/menu/arrow-down-3.png")
)
);
setMargin(new Insets(0, 2, 0, 2));
addActionListener();
}
private void addActionListener() {
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Figure figure : copyAll
? getView().getDrawing().getFiguresFrontToBack()
: getView().getSelectedFigures()) {
ModelComponent model = figure.get(FigureConstants.MODEL);
if (model instanceof PointModel || model instanceof LocationModel) {
updateLayoutX(model);
updateLayoutY(model);
// ... and move the figure
final LabeledFigure labeledFigure = (LabeledFigure) figure;
labeledFigure.propertiesChanged(
new AttributesChangeEvent(
new NullAttributesChangeListener(), model
)
);
model.propertiesChanged(new NullAttributesChangeListener());
eventBus.onEvent(new ResetInteractionToolCommand(this));
}
}
}
});
}
private DrawingView getView() {
return drawingEditor.getActiveView();
}
private void updateLayoutY(ModelComponent model) {
CoordinateProperty modelProperty;
if (model instanceof PointModel) {
modelProperty = (CoordinateProperty) model.getProperty(PointModel.MODEL_Y_POSITION);
}
else {
modelProperty = (CoordinateProperty) model.getProperty(LocationModel.MODEL_Y_POSITION);
}
CoordinateUndoActivity cua
= componentsFactory.createModelToLayoutCoordinateUndoActivity(modelProperty);
cua.snapShotBeforeModification();
modelProperty.setChangeState(ModelAttribute.ChangeState.DETAIL_CHANGED);
StringProperty spy;
if (model instanceof PointModel) {
spy = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_Y);
}
else {
spy = (StringProperty) model.getProperty(ElementPropKeys.LOC_POS_Y);
}
spy.setText(String.valueOf(((Number) modelProperty.getValue()).intValue()));
spy.markChanged();
cua.snapShotAfterModification();
undoRedoManager.addEdit(cua);
}
private void updateLayoutX(ModelComponent model) {
CoordinateProperty modelProperty;
if (model instanceof PointModel) {
modelProperty = (CoordinateProperty) model.getProperty(PointModel.MODEL_X_POSITION);
}
else {
modelProperty = (CoordinateProperty) model.getProperty(LocationModel.MODEL_X_POSITION);
}
CoordinateUndoActivity cua
= componentsFactory.createModelToLayoutCoordinateUndoActivity(modelProperty);
cua.snapShotBeforeModification();
modelProperty.setChangeState(ModelAttribute.ChangeState.DETAIL_CHANGED);
// Copy the model coordinates to the layout coordinates...
StringProperty spx;
if (model instanceof PointModel) {
spx = (StringProperty) model.getProperty(ElementPropKeys.POINT_POS_X);
}
else {
spx = (StringProperty) model.getProperty(ElementPropKeys.LOC_POS_X);
}
spx.setText(String.valueOf(((Number) modelProperty.getValue()).intValue()));
spx.markChanged();
cua.snapShotAfterModification();
undoRedoManager.addEdit(cua);
}
}

View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.modeleditor.application.action.ViewActionMap;
import org.opentcs.modeleditor.application.menus.MenuFactory;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* The application's menu for run-time actions.
*/
public class ActionsMenu
extends
JMenu {
/**
* A menu item for assuming the model coordinates from the layout coordinates.
*/
private final JMenuItem cbiAlignLayoutWithModel;
/**
* A menu item for assuming the layout coordinates from the model coordinates.
*/
private final JMenuItem cbiAlignModelWithLayout;
/**
* A menu item for calculating the euclidean distance for paths.
*/
private final JMenuItem calculatePathLength;
/**
* Creates a new instance.
*
* @param actionMap The application's action map.
* @param drawingEditor The application's drawing editor.
* @param menuFactory A factory for menu items.
*/
@Inject
@SuppressWarnings("this-escape")
public ActionsMenu(
ViewActionMap actionMap,
OpenTCSDrawingEditor drawingEditor,
MenuFactory menuFactory
) {
requireNonNull(actionMap, "actionMap");
requireNonNull(drawingEditor, "drawingEditor");
requireNonNull(menuFactory, "menuFactory");
final ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
this.setText(labels.getString("actionsMenu.text"));
this.setToolTipText(labels.getString("actionsMenu.tooltipText"));
this.setMnemonic('A');
// Menu item Actions -> Copy model to layout
cbiAlignModelWithLayout = menuFactory.createModelToLayoutMenuItem(true);
add(cbiAlignModelWithLayout);
// Menu item Actions -> Copy layout to model
cbiAlignLayoutWithModel = menuFactory.createLayoutToModelMenuItem(true);
add(cbiAlignLayoutWithModel);
calculatePathLength = menuFactory.createCalculatePathLengthMenuItem();
add(calculatePathLength);
}
}

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.JMenuBar;
/**
* The plant overview's main menu bar.
*/
public class ApplicationMenuBar
extends
JMenuBar {
private final FileMenu menuFile;
private final EditMenu menuEdit;
private final ActionsMenu menuActions;
private final ViewMenu menuView;
private final HelpMenu menuHelp;
/**
* Creates a new instance.
*
* @param menuFile The "File" menu.
* @param menuEdit The "Edit" menu.
* @param menuActions The "Actions" menu.
* @param menuView The "View" menu.
* @param menuHelp The "Help menu.
*/
@Inject
@SuppressWarnings("this-escape")
public ApplicationMenuBar(
FileMenu menuFile,
EditMenu menuEdit,
ActionsMenu menuActions,
ViewMenu menuView,
HelpMenu menuHelp
) {
requireNonNull(menuFile, "menuFile");
requireNonNull(menuEdit, "menuEdit");
requireNonNull(menuActions, "menuActions");
requireNonNull(menuView, "menuView");
requireNonNull(menuHelp, "menuHelp");
this.menuFile = menuFile;
add(menuFile);
this.menuEdit = menuEdit;
add(menuEdit);
this.menuActions = menuActions;
add(menuActions);
this.menuView = menuView;
add(menuView);
this.menuHelp = menuHelp;
add(menuHelp);
}
}

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.JMenu;
import org.opentcs.modeleditor.application.action.ViewActionMap;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.DeleteAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.SelectAllAction;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.ClearSelectionAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.CopyAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.CutAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.DuplicateAction;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.edit.PasteAction;
/**
* The application's "Edit" menu.
*/
public class EditMenu
extends
JMenu {
// private final JMenuItem menuItemCopy;
// private final JMenuItem menuItemCut;
// private final JMenuItem menuItemDuplicate;
// private final JMenuItem menuItemPaste;
/**
* Creates a new instance.
*
* @param actionMap The application's action map.
*/
@Inject
@SuppressWarnings("this-escape")
public EditMenu(ViewActionMap actionMap) {
requireNonNull(actionMap, "actionMap");
final ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
this.setText(labels.getString("editMenu.text"));
this.setToolTipText(labels.getString("editMenu.tooltipText"));
this.setMnemonic('E');
// Undo, Redo
add(actionMap.get(UndoRedoManager.UNDO_ACTION_ID));
add(actionMap.get(UndoRedoManager.REDO_ACTION_ID));
addSeparator();
// Cut, Copy, Paste, Duplicate
// menuItemCut = menuEdit.add(actionMap.get(CutAction.ID));
// menuItemCopy = menuEdit.add(actionMap.get(CopyAction.ID));
// menuItemPaste = menuEdit.add(actionMap.get(PasteAction.ID));
// menuItemDuplicate = menuEdit.add(actionMap.get(DuplicateAction.ID));
// Delete
add(actionMap.get(DeleteAction.ID));
add(actionMap.get(CopyAction.ID));
add(actionMap.get(PasteAction.ID));
add(actionMap.get(DuplicateAction.ID));
add(actionMap.get(CutAction.ID));
addSeparator();
// Select all, Clear selection
add(actionMap.get(SelectAllAction.ID));
add(actionMap.get(ClearSelectionAction.ID));
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.Set;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.opentcs.components.plantoverview.PlantModelExporter;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.modeleditor.application.action.file.ExportPlantModelAction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class FileExportMenu
extends
JMenu {
private static final ResourceBundleUtil LABELS
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
@Inject
@SuppressWarnings("this-escape")
public FileExportMenu(
Set<PlantModelExporter> exporters,
GuiManager guiManager
) {
super(LABELS.getString("fileExportMenu.text"));
requireNonNull(exporters, "exporters");
requireNonNull(guiManager, "guiManager");
for (PlantModelExporter exporter : exporters) {
add(new JMenuItem(new ExportPlantModelAction(exporter, guiManager)));
}
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.Set;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.opentcs.components.plantoverview.PlantModelImporter;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.modeleditor.application.action.file.ImportPlantModelAction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class FileImportMenu
extends
JMenu {
private static final ResourceBundleUtil LABELS
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
@Inject
@SuppressWarnings("this-escape")
public FileImportMenu(
Set<PlantModelImporter> importers,
GuiManager guiManager
) {
super(LABELS.getString("fileImportMenu.text"));
requireNonNull(importers, "importers");
requireNonNull(guiManager, "guiManager");
for (PlantModelImporter importer : importers) {
add(new JMenuItem(new ImportPlantModelAction(importer, guiManager)));
}
}
}

View File

@@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.opentcs.guing.common.application.action.file.ModelPropertiesAction;
import org.opentcs.guing.common.application.action.file.SaveModelAction;
import org.opentcs.guing.common.application.action.file.SaveModelAsAction;
import org.opentcs.modeleditor.application.action.ViewActionMap;
import org.opentcs.modeleditor.application.action.file.DownloadModelFromKernelAction;
import org.opentcs.modeleditor.application.action.file.LoadModelAction;
import org.opentcs.modeleditor.application.action.file.NewModelAction;
import org.opentcs.modeleditor.application.action.file.UploadModelToKernelAction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.action.file.CloseFileAction;
/**
* The application's "File" menu.
*/
public class FileMenu
extends
JMenu {
/**
* A menu item for creating a new, empty system model.
*/
private final JMenuItem menuItemNewModel;
/**
* A menu item for loading a mode into the kernel.
*/
private final JMenuItem menuItemLoadModel;
/**
* A menu item for saving the kernel's current model.
*/
private final JMenuItem menuItemSaveModel;
/**
* A menu item for saving the kernel's current model with a new name.
*/
private final JMenuItem menuItemSaveModelAs;
/**
* A menu item for retrieving the system model data from the kernel.
*/
private final JMenuItem menuItemDownloadModelFromKernel;
/**
* A menu item for transferring the system model data to the kernel.
*/
private final JMenuItem menuItemUploadModelToKernel;
/**
* A menu item for showing the current model's properties.
*/
private final JMenuItem menuItemModelProperties;
/**
* A menu item for closing the application.
*/
private final JMenuItem menuItemClose;
/**
* Creates a new instance.
*
* @param actionMap The application's action map.
* @param menuImport The sub-menu for the selectable plant model importers.
* @param menuExport The sub-menu for the selectable plant model exporters.
*/
@Inject
@SuppressWarnings("this-escape")
public FileMenu(
ViewActionMap actionMap,
FileImportMenu menuImport,
FileExportMenu menuExport
) {
requireNonNull(actionMap, "actionMap");
requireNonNull(menuImport, "menuImport");
requireNonNull(menuExport, "menuExport");
final ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
this.setText(labels.getString("fileMenu.text"));
this.setToolTipText(labels.getString("fileMenu.tooltipText"));
this.setMnemonic('F');
// Menu item File -> New Model
menuItemNewModel = new JMenuItem(actionMap.get(NewModelAction.ID));
add(menuItemNewModel);
// Menu item File -> Load Model
menuItemLoadModel = new JMenuItem(actionMap.get(LoadModelAction.ID));
add(menuItemLoadModel);
// Menu item File -> Save Model
menuItemSaveModel = new JMenuItem(actionMap.get(SaveModelAction.ID));
add(menuItemSaveModel);
// Menu item File -> Save Model As
menuItemSaveModelAs = new JMenuItem(actionMap.get(SaveModelAsAction.ID));
add(menuItemSaveModelAs);
addSeparator();
add(menuImport);
add(menuExport);
addSeparator();
// Load model from kernel
menuItemDownloadModelFromKernel
= new JMenuItem(actionMap.get(DownloadModelFromKernelAction.ID));
add(menuItemDownloadModelFromKernel);
// Persist model in kernel
menuItemUploadModelToKernel = new JMenuItem(actionMap.get(UploadModelToKernelAction.ID));
add(menuItemUploadModelToKernel);
addSeparator();
menuItemModelProperties = new JMenuItem(actionMap.get(ModelPropertiesAction.ID));
add(menuItemModelProperties);
addSeparator();
// Menu item File -> Close
menuItemClose = new JMenuItem(actionMap.get(CloseFileAction.ID));
add(menuItemClose);
}
}

View File

@@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.opentcs.modeleditor.application.action.ViewActionMap;
import org.opentcs.modeleditor.application.action.app.AboutAction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* The application's "Help" menu.
*/
public class HelpMenu
extends
JMenu {
/**
* A menu item for showing the application's "about" panel.
*/
private final JMenuItem menuItemAbout;
/**
* Creates a new instance.
*
* @param actionMap The application's action map.
*/
@Inject
@SuppressWarnings("this-escape")
public HelpMenu(ViewActionMap actionMap) {
requireNonNull(actionMap, "actionMap");
final ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
this.setText(labels.getString("helpMenu.text"));
this.setToolTipText(labels.getString("helpMenu.tooltipText"));
this.setMnemonic('?');
menuItemAbout = add(actionMap.get(AboutAction.ID));
}
}

View File

@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import org.jhotdraw.app.action.window.ToggleVisibleAction;
import org.opentcs.guing.common.application.OperationMode;
import org.opentcs.guing.common.application.menus.menubar.ViewPluginPanelsMenu;
import org.opentcs.modeleditor.application.action.ToolBarManager;
import org.opentcs.modeleditor.application.action.ViewActionMap;
import org.opentcs.modeleditor.application.action.view.AddBitmapAction;
import org.opentcs.modeleditor.application.action.view.RestoreDockingLayoutAction;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* The application's menu for view-related operations.
*/
public class ViewMenu
extends
JMenu {
/**
* The toolbar manager.
*/
private final ToolBarManager toolBarManager;
/**
* A menu item for setting a bitmap for the current drawing view.
*/
private final JMenuItem menuAddBitmap;
/**
* A menu for manipulating the application's tool bars.
*/
private final ViewToolBarsMenu menuViewToolBars;
/**
* A menu for showing/hiding plugin panels.
*/
private final ViewPluginPanelsMenu menuPluginPanels;
/**
* A menu item for restoring the default GUI layout.
*/
private final JMenuItem menuItemRestoreDockingLayout;
/**
* Creates a new instance.
*
* @param actionMap The application's action map.
* @param toolBarManager The toolbar manager.
* @param menuPluginPanels A menu for showing/hiding plugin panels.
*/
@Inject
@SuppressWarnings("this-escape")
public ViewMenu(
ViewActionMap actionMap,
ToolBarManager toolBarManager,
ViewPluginPanelsMenu menuPluginPanels
) {
requireNonNull(actionMap, "actionMap");
this.toolBarManager = requireNonNull(toolBarManager, "toolBarManager");
requireNonNull(menuPluginPanels, "menuPluginPanels");
final ResourceBundleUtil labels
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
this.setText(labels.getString("viewMenu.text"));
this.setToolTipText(labels.getString("viewMenu.tooltipText"));
this.setMnemonic('V');
// Menu item View -> Add Background Image
menuAddBitmap = new JMenuItem(actionMap.get(AddBitmapAction.ID));
add(menuAddBitmap);
addSeparator();
List<Action> viewActions = createToolBarActions();
if (!viewActions.isEmpty()) {
menuViewToolBars = new ViewToolBarsMenu(viewActions);
add(menuViewToolBars);
}
else {
menuViewToolBars = null;
}
// Menu item View -> Plugins
this.menuPluginPanels = menuPluginPanels;
menuPluginPanels.setOperationMode(OperationMode.MODELLING);
add(menuPluginPanels);
// Menu item View -> Restore docking layout
menuItemRestoreDockingLayout = new JMenuItem(actionMap.get(RestoreDockingLayoutAction.ID));
menuItemRestoreDockingLayout.setText(
labels.getString("viewMenu.menuItem_restoreWindowArrangement.text")
);
add(menuItemRestoreDockingLayout);
}
private List<Action> createToolBarActions() {
List<Action> toolBarActions = new ArrayList<>();
for (JToolBar curToolBar : toolBarManager.getToolBars()) {
toolBarActions.add(new ToggleVisibleAction(curToolBar, curToolBar.getName()));
}
return toolBarActions;
}
}

View File

@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.menus.menubar;
import static java.util.Objects.requireNonNull;
import java.util.Collection;
import javax.swing.Action;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
*/
public class ViewToolBarsMenu
extends
JMenu {
private static final ResourceBundleUtil LABELS_MENU
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH);
private static final ResourceBundleUtil LABELS_TOOLBAR
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.TOOLBAR_PATH);
@SuppressWarnings("this-escape")
public ViewToolBarsMenu(Collection<Action> viewActions) {
super(LABELS_MENU.getString("viewToolBarsMenu.text"));
requireNonNull(viewActions, "viewActions");
JCheckBoxMenuItem checkBoxMenuItem;
for (Action a : viewActions) {
checkBoxMenuItem = new JCheckBoxMenuItem(a);
add(checkBoxMenuItem);
if (checkBoxMenuItem.getText().equals(
LABELS_TOOLBAR.getString("toolBarManager.toolbar_drawing.title")
)) {
checkBoxMenuItem.setEnabled(false); // "Draw"-Toolbar musn't be disabled.
}
}
}
}

View File

@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.toolbar;
import org.jhotdraw.draw.ConnectionFigure;
import org.jhotdraw.draw.Figure;
import org.opentcs.thirdparty.modeleditor.jhotdraw.application.toolbar.OpenTCSConnectionTool;
/**
* A factory for tools concerned with the creation of figures/model elements.
*/
public interface CreationToolFactory {
OpenTCSCreationTool createCreationTool(Figure prototype);
OpenTCSConnectionTool createConnectionTool(ConnectionFigure prototype);
}

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.toolbar;
import static java.util.Objects.requireNonNull;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.Action;
import javax.swing.JMenuItem;
import org.jhotdraw.draw.tool.DragTracker;
import org.jhotdraw.draw.tool.SelectAreaTracker;
import org.opentcs.guing.common.application.ApplicationState;
import org.opentcs.modeleditor.application.menus.MenuFactory;
import org.opentcs.thirdparty.guing.common.jhotdraw.application.toolbar.AbstractMultipleSelectionTool;
/**
* The default selection tool.
*/
public class MultipleSelectionTool
extends
AbstractMultipleSelectionTool {
/**
* A factory for menu items.
*/
private final MenuFactory menuFactory;
/**
* Creates a new instance.
*
* @param appState Stores the application's current state.
* @param menuFactory A factory for menu items in popup menus created by this tool.
* @param selectAreaTracker The tracker to be used for area selections in the drawing.
* @param dragTracker The tracker to be used for dragging figures.
* @param drawingActions Drawing-related actions for the popup menus created by this tool.
* @param selectionActions Selection-related actions for the popup menus created by this tool.
*/
@Inject
public MultipleSelectionTool(
ApplicationState appState,
MenuFactory menuFactory,
SelectAreaTracker selectAreaTracker,
DragTracker dragTracker,
@Assisted("drawingActions")
Collection<Action> drawingActions,
@Assisted("selectionActions")
Collection<Action> selectionActions
) {
super(appState, selectAreaTracker, dragTracker, drawingActions, selectionActions);
this.menuFactory = requireNonNull(menuFactory, "menuFactory");
}
@Override
public List<JMenuItem> customPopupMenuItems() {
// Points and Locations get two additional entries
List<JMenuItem> actions = new ArrayList<>();
actions.add(menuFactory.createModelToLayoutMenuItem(false));
actions.add(menuFactory.createLayoutToModelMenuItem(false));
return actions;
}
}

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.toolbar;
import static java.util.Objects.requireNonNull;
import static org.opentcs.modeleditor.util.I18nPlantOverviewModeling.TOOLBAR_PATH;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.awt.event.MouseEvent;
import javax.swing.JOptionPane;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.tool.CreationTool;
import org.opentcs.guing.common.components.drawing.figures.LabeledFigure;
import org.opentcs.guing.common.components.drawing.figures.ModelBasedFigure;
import org.opentcs.modeleditor.components.layer.ActiveLayerProvider;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* A custom tool used to create {@code PointFigure}s and {@code LocationFigure}s.
*/
public class OpenTCSCreationTool
extends
CreationTool {
/**
* The resource bundle to use.
*/
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(TOOLBAR_PATH);
/**
* Provides the currently active layer.
*/
private final ActiveLayerProvider activeLayerProvider;
/**
* Creates a new instance.
*
* @param activeLayerProvider Provides the currently active layer.
* @param prototype The figure to be used as a prototype.
*/
@Inject
public OpenTCSCreationTool(
ActiveLayerProvider activeLayerProvider,
@Assisted
Figure prototype
) {
super(prototype);
this.activeLayerProvider = requireNonNull(activeLayerProvider, "activeLayerProvider");
}
@Override
public void mousePressed(MouseEvent evt) {
if (!activeLayerProvider.getActiveLayer().getLayer().isVisible()
|| !activeLayerProvider.getActiveLayer().getLayerGroup().isVisible()) {
JOptionPane.showMessageDialog(
evt.getComponent(),
BUNDLE.getString("openTcsCreationTool.optionPane_activeLayerNotVisible.message"),
BUNDLE.getString("openTcsCreationTool.optionPane_activeLayerNotVisible.title"),
JOptionPane.INFORMATION_MESSAGE
);
return;
}
super.mousePressed(evt);
}
@Override
protected Figure createFigure() {
Figure figure = super.createFigure();
if (figure instanceof ModelBasedFigure) {
((ModelBasedFigure) figure).getModel()
.getPropertyLayerWrapper().setValue(activeLayerProvider.getActiveLayer());
}
else if (figure instanceof LabeledFigure) {
((LabeledFigure) figure).getPresentationFigure().getModel()
.getPropertyLayerWrapper().setValue(activeLayerProvider.getActiveLayer());
}
return figure;
}
}

View File

@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.application.toolbar;
import com.google.inject.assistedinject.Assisted;
import java.util.Collection;
import javax.swing.Action;
/**
*/
public interface SelectionToolFactory {
MultipleSelectionTool createMultipleSelectionTool(
@Assisted("drawingActions")
Collection<Action> drawingActions,
@Assisted("selectionActions")
Collection<Action> selectionActions
);
}

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-93,0,0,0,-102"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="allTypesCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="i18n/org/opentcs/plantoverview/modeling/miscellaneous.properties" key="pathTypeSelection.allTypes_label.text" replaceFormat="java.util.ResourceBundle.getBundle(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allTypesCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="directCheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Direct:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="directCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="elbowCheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Elbow:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="elbowCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="slantedCheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Slanted:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="slantedCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="polypathCheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Polypath:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="polypathCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="bezierCheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Bezier:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bezierCheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JCheckBox" name="bezier3CheckBox">
<Properties>
<Property name="text" type="java.lang.String" value="Bezier 3:"/>
<Property name="horizontalAlignment" type="int" value="11"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bezier3CheckBoxActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@@ -0,0 +1,264 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.dialog;
import static org.opentcs.guing.base.model.elements.PathModel.Type.BEZIER;
import static org.opentcs.guing.base.model.elements.PathModel.Type.BEZIER_3;
import static org.opentcs.guing.base.model.elements.PathModel.Type.DIRECT;
import static org.opentcs.guing.base.model.elements.PathModel.Type.ELBOW;
import static org.opentcs.guing.base.model.elements.PathModel.Type.POLYPATH;
import static org.opentcs.guing.base.model.elements.PathModel.Type.SLANTED;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.common.components.dialogs.DialogContent;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* A dialog content to select a set of path types.
*/
public class PathTypeSelectionPanel
extends
DialogContent {
/**
* Creates new form PathTypeSelection.
*/
@SuppressWarnings("this-escape")
public PathTypeSelectionPanel() {
setDialogTitle(
ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MISC_PATH)
.getString("pathTypeSelection.title")
);
initComponents();
}
@Override
public void initFields() {
}
@Override
public void update() {
}
/**
* Test if the given path type is selected.
*
* @param type The path type to test.
* @return True if the path type is selected.
*/
public boolean isPathTypeSelected(PathModel.Type type) {
switch (type) {
case DIRECT:
return directCheckBox.isSelected();
case ELBOW:
return elbowCheckBox.isSelected();
case SLANTED:
return slantedCheckBox.isSelected();
case POLYPATH:
return polypathCheckBox.isSelected();
case BEZIER:
return bezierCheckBox.isSelected();
case BEZIER_3:
return bezier3CheckBox.isSelected();
default:
return false;
}
}
private void updateAllTypesCheckBox() {
allTypesCheckBox.setSelected(
directCheckBox.isSelected()
&& elbowCheckBox.isSelected()
&& slantedCheckBox.isSelected()
&& polypathCheckBox.isSelected()
&& bezierCheckBox.isSelected()
&& bezier3CheckBox.isSelected()
);
}
// FORMATTER:OFF
// CHECKSTYLE:OFF
/**
* This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
allTypesCheckBox = new javax.swing.JCheckBox();
directCheckBox = new javax.swing.JCheckBox();
elbowCheckBox = new javax.swing.JCheckBox();
slantedCheckBox = new javax.swing.JCheckBox();
polypathCheckBox = new javax.swing.JCheckBox();
bezierCheckBox = new javax.swing.JCheckBox();
bezier3CheckBox = new javax.swing.JCheckBox();
setLayout(new java.awt.GridBagLayout());
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/modeling/miscellaneous"); // NOI18N
allTypesCheckBox.setText(bundle.getString("pathTypeSelection.allTypes_label.text")); // NOI18N
allTypesCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
allTypesCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
allTypesCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allTypesCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
add(allTypesCheckBox, gridBagConstraints);
directCheckBox.setText("Direct:");
directCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
directCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
directCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
directCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(directCheckBox, gridBagConstraints);
elbowCheckBox.setText("Elbow:");
elbowCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
elbowCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
elbowCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
elbowCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(elbowCheckBox, gridBagConstraints);
slantedCheckBox.setText("Slanted:");
slantedCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
slantedCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
slantedCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
slantedCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(slantedCheckBox, gridBagConstraints);
polypathCheckBox.setText("Polypath:");
polypathCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
polypathCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
polypathCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
polypathCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(polypathCheckBox, gridBagConstraints);
bezierCheckBox.setText("Bezier:");
bezierCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
bezierCheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
bezierCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bezierCheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(bezierCheckBox, gridBagConstraints);
bezier3CheckBox.setText("Bezier 3:");
bezier3CheckBox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
bezier3CheckBox.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
bezier3CheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bezier3CheckBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
add(bezier3CheckBox, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// CHECKSTYLE:ON
// FORMATTER:ON
private void allTypesCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allTypesCheckBoxActionPerformed
directCheckBox.setSelected(allTypesCheckBox.isSelected());
elbowCheckBox.setSelected(allTypesCheckBox.isSelected());
slantedCheckBox.setSelected(allTypesCheckBox.isSelected());
polypathCheckBox.setSelected(allTypesCheckBox.isSelected());
bezierCheckBox.setSelected(allTypesCheckBox.isSelected());
bezier3CheckBox.setSelected(allTypesCheckBox.isSelected());
}//GEN-LAST:event_allTypesCheckBoxActionPerformed
private void directCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_directCheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_directCheckBoxActionPerformed
private void elbowCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_elbowCheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_elbowCheckBoxActionPerformed
private void slantedCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_slantedCheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_slantedCheckBoxActionPerformed
private void polypathCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_polypathCheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_polypathCheckBoxActionPerformed
private void bezierCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bezierCheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_bezierCheckBoxActionPerformed
private void bezier3CheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bezier3CheckBoxActionPerformed
updateAllTypesCheckBox();
}//GEN-LAST:event_bezier3CheckBoxActionPerformed
// FORMATTER:OFF
// CHECKSTYLE:OFF
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox allTypesCheckBox;
private javax.swing.JCheckBox bezier3CheckBox;
private javax.swing.JCheckBox bezierCheckBox;
private javax.swing.JCheckBox directCheckBox;
private javax.swing.JCheckBox elbowCheckBox;
private javax.swing.JCheckBox polypathCheckBox;
private javax.swing.JCheckBox slantedCheckBox;
// End of variables declaration//GEN-END:variables
// CHECKSTYLE:ON
// FORMATTER:ON
}

View File

@@ -0,0 +1,196 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.dockable;
import static java.util.Objects.requireNonNull;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.group.CGroupBehavior;
import jakarta.inject.Inject;
import javax.swing.JComponent;
import javax.swing.JFrame;
import org.opentcs.customizations.plantoverview.ApplicationFrame;
import org.opentcs.guing.common.components.dockable.AbstractDockingManager;
import org.opentcs.guing.common.components.dockable.CStack;
import org.opentcs.guing.common.components.properties.SelectionPropertiesComponent;
import org.opentcs.guing.common.components.tree.BlocksTreeViewManager;
import org.opentcs.guing.common.components.tree.ComponentsTreeViewManager;
import org.opentcs.modeleditor.components.layer.LayerGroupsPanel;
import org.opentcs.modeleditor.components.layer.LayersPanel;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* Utility class for working with dockables.
*/
public class DockingManagerModeling
extends
AbstractDockingManager {
/**
* ID of the tab pane that contains the course, transport orders and order sequences.
*/
public static final String COURSE_TAB_PANE_ID = "course_tab_pane";
/**
* ID of the tab pane that contains the components, blocks and groups.
*/
private static final String TREE_TAB_PANE_ID = "tree_tab_pane";
private static final String LAYER_TAB_PANE_ID = "layer_tab_pane";
private static final String COMPONENTS_ID = "comp_dock";
private static final String BLOCKS_ID = "block_dock";
private static final String PROPERTIES_ID = "properties_id";
private static final String LAYERS_ID = "layers_id";
private static final String LAYER_GROUPS_ID = "layer_groups_id";
/**
* The tree view manager for components.
*/
private final ComponentsTreeViewManager componentsTreeViewManager;
/**
* The tree view manager for blocks.
*/
private final BlocksTreeViewManager blocksTreeViewManager;
/**
* The panel displaying the properties of the currently selected driving course components.
*/
private final SelectionPropertiesComponent selectionPropertiesComponent;
/**
* The panel displaying the layers in the plant model.
*/
private final LayersPanel layersPanel;
/**
* The panel displaying the layer groups in the plant model.
*/
private final LayerGroupsPanel layerGroupsPanel;
/**
* Tab pane that contains the components, blocks and groups.
*/
private CStack treeTabPane;
/**
* Tab pane that contains the course, transport orders and order sequences.
*/
private CStack courseTabPane;
/**
* Tab pane that contains layers and layer groups.
*/
private CStack layerTabPane;
/**
* Creates a new instance.
*
* @param applicationFrame The application's main frame.
* @param componentsTreeViewManager The tree view manager for components.
* @param blocksTreeViewManager The tree view manager for blocks.
* @param selectionPropertiesComponent The panel displaying the properties of the currently
* selected driving course components.
* @param layersPanel The panel displaying the layers in the plant model.
* @param layerGroupsPanel The panel displaying the layer groups in the plant model.
*/
@Inject
public DockingManagerModeling(@ApplicationFrame
JFrame applicationFrame,
ComponentsTreeViewManager componentsTreeViewManager,
BlocksTreeViewManager blocksTreeViewManager,
SelectionPropertiesComponent selectionPropertiesComponent,
LayersPanel layersPanel,
LayerGroupsPanel layerGroupsPanel
) {
super(new CControl(applicationFrame));
this.componentsTreeViewManager = requireNonNull(
componentsTreeViewManager,
"componentsTreeViewManager"
);
this.blocksTreeViewManager = requireNonNull(blocksTreeViewManager, "blocksTreeViewManager");
this.selectionPropertiesComponent = requireNonNull(
selectionPropertiesComponent,
"selectionPropertiesComponent"
);
this.layersPanel = requireNonNull(layersPanel, "layersPanel");
this.layerGroupsPanel = requireNonNull(layerGroupsPanel, "layerGroupsPanel");
}
@Override
public void reset() {
removeDockable(BLOCKS_ID);
removeDockable(COMPONENTS_ID);
removeDockable(PROPERTIES_ID);
removeDockable(LAYERS_ID);
removeDockable(LAYER_GROUPS_ID);
getCControl().removeStation(getTabPane(COURSE_TAB_PANE_ID));
getCControl().removeStation(getTabPane(TREE_TAB_PANE_ID));
getCControl().removeStation(getTabPane(LAYER_TAB_PANE_ID));
}
@Override
public void initializeDockables() {
getCControl().setGroupBehavior(CGroupBehavior.TOPMOST);
// Disable keyboard shortcuts to avoid collisions.
getCControl().putProperty(CControl.KEY_GOTO_NORMALIZED, null);
getCControl().putProperty(CControl.KEY_GOTO_EXTERNALIZED, null);
getCControl().putProperty(CControl.KEY_GOTO_MAXIMIZED, null);
getCControl().putProperty(CControl.KEY_MAXIMIZE_CHANGE, null);
ResourceBundleUtil bundle
= ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.DOCKABLE_PATH);
CGrid grid = new CGrid(getCControl());
courseTabPane = new CStack(COURSE_TAB_PANE_ID);
addTabPane(COURSE_TAB_PANE_ID, courseTabPane);
treeTabPane = new CStack(TREE_TAB_PANE_ID);
addTabPane(TREE_TAB_PANE_ID, treeTabPane);
layerTabPane = new CStack(LAYER_TAB_PANE_ID);
addTabPane(LAYER_TAB_PANE_ID, layerTabPane);
DefaultSingleCDockable treeViewDock
= createDockable(
COMPONENTS_ID,
bundle.getString("dockingManagerModeling.panel_components.title"),
(JComponent) componentsTreeViewManager.getTreeView(),
false
);
DefaultSingleCDockable treeBlocks
= createDockable(
BLOCKS_ID,
bundle.getString("dockingManagerModeling.panel_blocks.title"),
(JComponent) blocksTreeViewManager.getTreeView(),
false
);
grid.add(0, 0, 250, 400, treeTabPane);
grid.add(
0, 400, 250, 300,
createDockable(
PROPERTIES_ID,
bundle.getString("dockingManagerModeling.panel_properties.title"),
selectionPropertiesComponent,
false
)
);
DefaultSingleCDockable layersDock
= createDockable(
LAYERS_ID,
bundle.getString("dockingManagerModeling.panel_layers.title"),
layersPanel,
false
);
DefaultSingleCDockable layerGroupsDock
= createDockable(
LAYER_GROUPS_ID,
bundle.getString("dockingManagerModeling.panel_layerGroups.title"),
layerGroupsPanel,
false
);
grid.add(0, 700, 250, 300, layerTabPane);
grid.add(400, 0, 1000, 500, courseTabPane);
getCControl().getContentArea().deploy(grid);
// init tab panes
addTabTo(treeViewDock, TREE_TAB_PANE_ID, 0);
addTabTo(treeBlocks, TREE_TAB_PANE_ID, 1);
addTabTo(layersDock, LAYER_TAB_PANE_ID, 0);
addTabTo(layerGroupsDock, LAYER_TAB_PANE_ID, 1);
treeTabPane.getStation().setFrontDockable(treeViewDock.intern());
layerTabPane.getStation().setFrontDockable(layersDock.intern());
}
}

View File

@@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.jhotdraw.draw.AbstractFigure;
import org.jhotdraw.draw.Figure;
import org.opentcs.guing.base.event.BlockChangeEvent;
import org.opentcs.guing.base.event.BlockChangeListener;
import org.opentcs.guing.base.model.FigureDecorationDetails;
import org.opentcs.guing.base.model.ModelComponent;
import org.opentcs.guing.base.model.elements.BlockModel;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.guing.common.util.ModelComponentUtil;
/**
*/
public class BlockChangeHandler
implements
BlockChangeListener {
/**
* The members/elements of a block mapped to the block model.
*/
private final Map<BlockModel, Set<FigureDecorationDetails>> blockElementsHistory
= new HashMap<>();
/**
* The manager keeping/providing the currently loaded model.
*/
private final ModelManager modelManager;
/**
* Creates a new instance.
*
* @param modelManager The model manager.
*/
@Inject
public BlockChangeHandler(ModelManager modelManager) {
this.modelManager = requireNonNull(modelManager, "modelManager");
}
@Override // BlockChangeListener
public void courseElementsChanged(BlockChangeEvent e) {
BlockModel block = (BlockModel) e.getSource();
// Let the block's elements know the block they are now part of.
Set<FigureDecorationDetails> blockElements = block.getPropertyElements().getItems().stream()
.map(elementName -> modelManager.getModel().getModelComponent(elementName))
.filter(modelComponent -> modelComponent instanceof FigureDecorationDetails)
.map(modelComponent -> (FigureDecorationDetails) modelComponent)
.collect(Collectors.toSet());
for (FigureDecorationDetails component : blockElements) {
component.addBlockModel(block);
}
// The elements that are no longer part of the block should also know this.
Set<FigureDecorationDetails> removedBlockElements = updateBlockElementHistory(
block,
blockElements
);
for (FigureDecorationDetails component : removedBlockElements) {
component.removeBlockModel(block);
// Update the figure so that it no longer appears as being part of the block.
Figure figure = modelManager.getModel().getFigure(((ModelComponent) component));
((AbstractFigure) figure).fireFigureChanged();
}
updateBlock(block);
}
@Override // BlockChangeListener
public void colorChanged(BlockChangeEvent e) {
updateBlock((BlockModel) e.getSource());
}
@Override // BlockChangeListener
public void blockRemoved(BlockChangeEvent e) {
BlockModel block = (BlockModel) e.getSource();
// Let the block's elements know they are no longer part of the block.
Set<FigureDecorationDetails> removedBlockElements
= updateBlockElementHistory(block, new HashSet<>());
for (FigureDecorationDetails component : removedBlockElements) {
component.removeBlockModel(block);
}
block.removeBlockChangeListener(this);
updateBlock(block);
}
/**
* Remembers the given set of components as the new block elements for the given
* block model and returns the set difference of the old and the new block elements
* (e.g. the elements that are no longer part of the block).
*
* @param block The block model.
* @param newBlockElements The new block elements.
* @return The set difference of the old and the new block elements.
*/
private Set<FigureDecorationDetails> updateBlockElementHistory(
BlockModel block,
Set<FigureDecorationDetails> newBlockElements
) {
Set<FigureDecorationDetails> oldBlockElements = getBlockElements(block);
Set<FigureDecorationDetails> removedBlockElements = new HashSet<>(oldBlockElements);
removedBlockElements.removeAll(newBlockElements);
oldBlockElements.clear();
oldBlockElements.addAll(newBlockElements);
return removedBlockElements;
}
private Set<FigureDecorationDetails> getBlockElements(BlockModel block) {
return blockElementsHistory.computeIfAbsent(block, b -> new HashSet<>());
}
private void updateBlock(BlockModel block) {
for (Figure figure : ModelComponentUtil.getChildFigures(block, modelManager.getModel())) {
((AbstractFigure) figure).fireFigureChanged();
}
}
}

View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.List;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* Undoes or redoes the "delete" action.
*/
public class DeleteEdit
extends
AbstractUndoableEdit {
/**
* The drawing view we're working with.
*/
private final DrawingView drawingView;
/**
* The deleted figures.
*/
private final ArrayList<Figure> figures = new ArrayList<>();
/**
* Creates a new instance.
*
* @param drawingView The drawing view the delete happened in.
* @param figures The deleted figures.
*/
public DeleteEdit(DrawingView drawingView, List<Figure> figures) {
this.drawingView = requireNonNull(drawingView, "drawingView");
this.figures.addAll(figures);
}
@Override
public String getPresentationName() {
return ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH)
.getString("deleteEdit.presentationName");
}
@Override
public void undo()
throws CannotUndoException {
super.undo();
drawingView.clearSelection();
for (Figure figure : figures) {
drawingView.getDrawing().add(figure);
}
}
@Override
public void redo()
throws CannotRedoException {
super.redo();
for (Figure figure : figures) {
drawingView.getDrawing().remove(figure);
}
}
}

View File

@@ -0,0 +1,115 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import javax.swing.JToggleButton;
import org.jhotdraw.gui.JPopupButton;
import org.opentcs.guing.base.model.elements.VehicleModel;
import org.opentcs.guing.common.application.StatusPanel;
import org.opentcs.guing.common.components.drawing.DrawingOptions;
import org.opentcs.guing.common.components.drawing.DrawingViewPlacardPanel;
import org.opentcs.guing.common.components.drawing.DrawingViewScrollPane;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingEditor;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingView;
import org.opentcs.guing.common.model.SystemModel;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* A factory for drawing views.
*/
public class DrawingViewFactory {
/**
* A provider for drawing views.
*/
private final Provider<OpenTCSDrawingView> drawingViewProvider;
/**
* The drawing editor.
*/
private final OpenTCSDrawingEditor drawingEditor;
/**
* The status panel to display the current mouse position in.
*/
private final StatusPanel statusPanel;
/**
* The manager keeping/providing the currently loaded model.
*/
private final ModelManager modelManager;
/**
* The drawing options.
*/
private final DrawingOptions drawingOptions;
@Inject
public DrawingViewFactory(
Provider<OpenTCSDrawingView> drawingViewProvider,
OpenTCSDrawingEditor drawingEditor,
StatusPanel statusPanel,
ModelManager modelManager,
DrawingOptions drawingOptions
) {
this.drawingViewProvider = requireNonNull(drawingViewProvider, "drawingViewProvider");
this.drawingEditor = requireNonNull(drawingEditor, "drawingEditor");
this.statusPanel = requireNonNull(statusPanel, "statusPanel");
this.modelManager = requireNonNull(modelManager, "modelManager");
this.drawingOptions = requireNonNull(drawingOptions, "drawingOptions");
}
/**
* Creates and returns a new drawing view along with its placard panel, both
* wrapped in a scroll pane.
*
* @param systemModel The system model.
* @param selectionToolButton The selection tool button in the tool bar.
* @param dragToolButton The drag tool button in the tool bar.
* @param linkCreationToolButton The link creation tool button in the tool bar.
* @param pathCreationToolButton The path creation tool button in the tool bar.
* @return A new drawing view, wrapped in a scroll pane.
*/
public DrawingViewScrollPane createDrawingView(
SystemModel systemModel,
JToggleButton selectionToolButton,
JToggleButton dragToolButton,
JToggleButton linkCreationToolButton,
JPopupButton pathCreationToolButton
) {
requireNonNull(systemModel, "systemModel");
requireNonNull(selectionToolButton, "selectionToolButton");
requireNonNull(dragToolButton, "dragToolButton");
OpenTCSDrawingView drawingView = drawingViewProvider.get();
drawingEditor.add(drawingView);
drawingEditor.setActiveView(drawingView);
for (VehicleModel vehicle : systemModel.getVehicleModels()) {
drawingView.displayDriveOrders(vehicle, vehicle.getDisplayDriveOrders());
}
drawingView.setBlocks(systemModel.getMainFolder(SystemModel.FolderKey.BLOCKS));
DrawingViewPlacardPanel placardPanel = new DrawingViewPlacardPanel(drawingView, drawingOptions);
DrawingViewScrollPane scrollPane = new DrawingViewScrollPane(drawingView, placardPanel);
scrollPane.originChanged(systemModel.getDrawingMethod().getOrigin());
// --- Listens to draggings in the drawing ---
ViewDragScrollListener dragScrollListener
= new ViewDragScrollListener(
scrollPane,
placardPanel.getZoomComboBox(),
selectionToolButton,
dragToolButton,
linkCreationToolButton,
pathCreationToolButton,
statusPanel,
modelManager
);
drawingView.addMouseListener(dragScrollListener);
drawingView.addMouseMotionListener(dragScrollListener);
drawingView.getComponent().addMouseWheelListener(dragScrollListener);
return scrollPane;
}
}

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.List;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
/**
* Undoes or redoes a "paste" action.
*/
public class PasteEdit
extends
AbstractUndoableEdit {
/**
* The drawing view we're working with.
*/
private final DrawingView drawingView;
/**
* The pasted figures.
*/
private final ArrayList<Figure> figures = new ArrayList<>();
/**
* Creates a new instance.
*
* @param drawingView The drawing view the paste happened in.
* @param figures The pasted figures.
*/
public PasteEdit(DrawingView drawingView, List<Figure> figures) {
this.drawingView = requireNonNull(drawingView, "drawingView");
this.figures.addAll(figures);
}
@Override
public String getPresentationName() {
return ResourceBundleUtil.getBundle(I18nPlantOverviewModeling.MENU_PATH)
.getString("pasteEdit.presentationName");
}
@Override
public void undo()
throws CannotUndoException {
super.undo();
drawingView.getDrawing().removeAll(figures);
}
@Override
public void redo()
throws CannotRedoException {
super.redo();
drawingView.getDrawing().addAll(figures);
}
}

View File

@@ -0,0 +1,346 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.drawing;
import static java.util.Objects.requireNonNull;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComboBox;
import javax.swing.JToggleButton;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.gui.JPopupButton;
import org.opentcs.guing.base.model.elements.LayoutModel;
import org.opentcs.guing.common.application.StatusPanel;
import org.opentcs.guing.common.components.drawing.DrawingViewScrollPane;
import org.opentcs.guing.common.components.drawing.ZoomItem;
import org.opentcs.guing.common.components.drawing.figures.LabeledLocationFigure;
import org.opentcs.guing.common.components.drawing.figures.LabeledPointFigure;
import org.opentcs.guing.common.components.drawing.figures.PathConnection;
import org.opentcs.guing.common.components.drawing.figures.liner.TripleBezierLiner;
import org.opentcs.guing.common.components.drawing.figures.liner.TupelBezierLiner;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* A listener for dragging of the drawing view and single objects inside the
* view.
*/
public class ViewDragScrollListener
extends
MouseAdapter {
/**
* The scroll pane enclosing the drawing view.
*/
private final DrawingViewScrollPane scrollPane;
/**
* The combo box for selecting the zoom level.
*/
private final JComboBox<ZoomItem> zoomComboBox;
/**
* The button for enabling object selection.
*/
private final JToggleButton selectionTool;
/**
* The button for enabling dragging.
*/
private final JToggleButton dragTool;
/**
* The button for creating a link.
*/
private final JToggleButton linkCreationTool;
/**
* The button for creating a path.
*/
private final JPopupButton pathCreationTool;
/**
* The status panel to display the current mouse position in.
*/
private final StatusPanel statusPanel;
/**
* The manager keeping/providing the currently loaded model.
*/
private final ModelManager modelManager;
/**
* A default cursor for the drawing view.
*/
private final Cursor defaultCursor;
/**
* The start position of drag movements.
*/
private final Point startPoint = new Point();
/**
* Start coordinate for measuring.
* XXX Is this redundant, or is it used for something different than startPoint?
*/
private final Point2D.Double fMouseStartPoint = new Point2D.Double();
/**
* Current coordinate for measuring.
*/
private final Point2D.Double fMouseCurrentPoint = new Point2D.Double();
/**
* End coordinate for measuring.
*/
private final Point2D.Double fMouseEndPoint = new Point2D.Double();
/**
* The figure a user may have pressed on / want to drag.
*/
private Figure pressedFigure;
/**
* Creates a new instance.
*
* @param scrollPane The scroll pane enclosing the drawing view.
* @param zoomComboBox The combo box for selecting the zoom level.
* @param selectionTool The button for enabling object selection.
* @param dragTool The button for enabling dragging.
* @param linkCreationTool The button for creating a link.
* @param pathCreationTool The button for creating a path.
* @param statusPanel The status panel to display the current mouse position in.
* @param modelManager The manager keeping/providing the currently loaded model.
*/
public ViewDragScrollListener(
DrawingViewScrollPane scrollPane,
JComboBox<ZoomItem> zoomComboBox,
JToggleButton selectionTool,
JToggleButton dragTool,
JToggleButton linkCreationTool,
JPopupButton pathCreationTool,
StatusPanel statusPanel,
ModelManager modelManager
) {
this.scrollPane = requireNonNull(scrollPane, "scrollPane");
this.zoomComboBox = requireNonNull(zoomComboBox, "zoomComboBox");
this.selectionTool = requireNonNull(selectionTool, "selectionTool");
this.dragTool = requireNonNull(dragTool, "dragTool");
this.linkCreationTool = requireNonNull(linkCreationTool, "linkCreationTool");
this.pathCreationTool = requireNonNull(pathCreationTool, "pathCreationTool");
this.statusPanel = requireNonNull(statusPanel, "statusPanel");
this.modelManager = requireNonNull(modelManager, "modelManager");
this.defaultCursor = scrollPane.getDrawingView().getComponent().getCursor();
}
@Override
public void mouseDragged(final MouseEvent evt) {
final DrawingView drawingView = scrollPane.getDrawingView();
if (!(drawingView.getComponent().getParent() instanceof JViewport)) {
return;
}
final JViewport viewport = (JViewport) drawingView.getComponent().getParent();
Point cp = SwingUtilities.convertPoint(drawingView.getComponent(), evt.getPoint(), viewport);
if (dragTool.isSelected()) {
int dx = startPoint.x - cp.x;
int dy = startPoint.y - cp.y;
Point vp = viewport.getViewPosition();
vp.translate(dx, dy);
drawingView.getComponent().scrollRectToVisible(new Rectangle(vp, viewport.getSize()));
}
else if (linkCreationTool.isSelected() || pathCreationTool.isSelected()) {
viewport.revalidate();
// Start scrolling as soon as the mouse is hitting the view bounds.
drawingView.getComponent().scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1));
}
else { // The selection tool is selected
viewport.revalidate();
if (isMovableFigure(pressedFigure)) {
if (!isFigureCompletelyInView(pressedFigure, viewport, drawingView)) {
// If the figure exceeds the current view, start scrolling as soon as the mouse is
// hitting the view bounds.
drawingView.getComponent().scrollRectToVisible(
new Rectangle(evt.getX(), evt.getY(), 1, 1)
);
}
fMouseCurrentPoint.setLocation(drawingView.viewToDrawing(evt.getPoint()));
showPositionStatus(false);
startPoint.setLocation(cp);
}
}
SwingUtilities.invokeLater(() -> {
Rectangle2D.Double drawingArea = drawingView.getDrawing().getDrawingArea();
scrollPane.getHorizontalRuler().setPreferredWidth((int) drawingArea.width);
scrollPane.getVerticalRuler().setPreferredHeight((int) drawingArea.height);
});
}
private boolean isMovableFigure(Figure figure) {
return (figure instanceof LabeledPointFigure)
|| (figure instanceof LabeledLocationFigure)
|| ((figure instanceof PathConnection)
&& (((PathConnection) figure).getLiner() instanceof TupelBezierLiner))
|| ((figure instanceof PathConnection)
&& (((PathConnection) figure).getLiner() instanceof TripleBezierLiner));
}
private boolean isFigureCompletelyInView(
Figure figure,
JViewport viewport,
DrawingView drawingView
) {
Rectangle viewPortBounds = viewport.getViewRect();
Rectangle figureBounds = drawingView.drawingToView(figure.getDrawingArea());
return (figureBounds.getMinX() > viewPortBounds.getMinX())
&& (figureBounds.getMinY() > viewPortBounds.getMinY())
&& (figureBounds.getMaxX() < viewPortBounds.getMaxX())
&& (figureBounds.getMaxY() < viewPortBounds.getMaxY());
}
@Override
public void mousePressed(MouseEvent evt) {
final DrawingView drawingView = scrollPane.getDrawingView();
if (dragIsSelected()) {
drawingView.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
Container c = drawingView.getComponent().getParent();
if (c instanceof JViewport) {
JViewport viewPort = (JViewport) c;
Point cp = SwingUtilities.convertPoint(drawingView.getComponent(), evt.getPoint(), viewPort);
startPoint.setLocation(cp);
}
pressedFigure = drawingView.findFigure(evt.getPoint());
fMouseCurrentPoint.setLocation(drawingView.viewToDrawing(evt.getPoint()));
fMouseStartPoint.setLocation(drawingView.viewToDrawing(evt.getPoint()));
showPositionStatus(false);
}
@Override
public void mouseReleased(MouseEvent evt) {
if (dragIsSelected()) {
return;
}
final DrawingView drawingView = scrollPane.getDrawingView();
pressedFigure = null;
fMouseEndPoint.setLocation(drawingView.viewToDrawing(evt.getPoint()));
if (evt.getButton() != 2) {
showPositionStatus(true);
}
else {
showPositionStatus(false);
}
}
@Override
public void mouseExited(MouseEvent evt) {
dragIsSelected();
clearPositionStatus();
}
@Override
public void mouseEntered(MouseEvent evt) {
dragIsSelected();
}
@Override
public void mouseMoved(MouseEvent evt) {
final DrawingView drawingView = scrollPane.getDrawingView();
fMouseCurrentPoint.setLocation(drawingView.viewToDrawing(evt.getPoint()));
showPositionStatus(false);
}
@Override
public void mouseClicked(MouseEvent evt) {
if (evt.getButton() == 2) {
if (dragTool.isSelected()) {
selectionTool.setSelected(true);
}
else if (selectionTool.isSelected()) {
dragTool.setSelected(true);
}
// Sets the correct cursor
dragIsSelected();
}
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.isControlDown()) {
int zoomLevel = zoomComboBox.getSelectedIndex();
int notches = e.getWheelRotation();
if (zoomLevel != -1) {
if (notches < 0) {
if (zoomLevel > 0) {
zoomLevel--;
zoomComboBox.setSelectedIndex(zoomLevel);
}
}
else {
if (zoomLevel < zoomComboBox.getItemCount() - 1) {
zoomLevel++;
zoomComboBox.setSelectedIndex(zoomLevel);
}
}
}
}
}
/**
* Checks whether the drag tool is selected.
*
* @return true if the drag tool is selected, false otherwise.
*/
private boolean dragIsSelected() {
final DrawingView drawingView = scrollPane.getDrawingView();
if (!selectionTool.isSelected() && dragTool.isSelected()) {
drawingView.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
return true;
}
else if (selectionTool.isSelected() && !dragTool.isSelected()) {
drawingView.setCursor(defaultCursor);
return false;
}
else {
return false;
}
}
/**
* Clears the mouse position information in the status panel.
*/
private void clearPositionStatus() {
statusPanel.setPositionText("");
}
/**
* Displays the current mouse position or covered area in the status panel.
*
* @param showCoveredArea Whether to display the dimensions of the covered
* area instead of the current mouse coordinates.
*/
private void showPositionStatus(boolean showCoveredArea) {
double x = fMouseCurrentPoint.x;
double y = -fMouseCurrentPoint.y;
if (showCoveredArea) {
double w = Math.abs(fMouseEndPoint.x - fMouseStartPoint.x);
double h = Math.abs(fMouseEndPoint.y - fMouseStartPoint.y);
statusPanel.setPositionText(
String.format("X %.0f Y %.0f W %.0f H %.0f", x, y, w, h)
);
}
else {
LayoutModel layout = modelManager.getModel().getLayoutModel();
double scaleX = (double) layout.getPropertyScaleX().getValue();
double scaleY = (double) layout.getPropertyScaleY().getValue();
double xmm = x * scaleX;
double ymm = y * scaleY;
statusPanel.setPositionText(
String.format("X %.0f (%.0fmm) Y %.0f (%.0fmm)", x, xmm, y, ymm)
);
}
}
}

View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import org.opentcs.guing.base.components.layer.LayerWrapper;
/**
* Provides a method to get the currently active layer.
*/
public interface ActiveLayerProvider {
/**
* Returns the {@link LayerWrapper} instance that holds the currently active layer.
*
* @return The currently active layer.
*/
LayerWrapper getActiveLayer();
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.jhotdraw.draw.Figure;
import org.opentcs.guing.base.model.DrawnModelComponent;
import org.opentcs.guing.base.model.ModelComponent;
import org.opentcs.guing.common.components.drawing.figures.FigureConstants;
import org.opentcs.guing.common.event.DrawingEditorEvent;
import org.opentcs.guing.common.event.DrawingEditorListener;
/**
* Handles drawing editor events that the layer editor needs to know about.
*/
public class LayerEditorEventHandler
implements
DrawingEditorListener {
/**
* The layer editor.
*/
private final LayerEditorModeling layerEditor;
@Inject
public LayerEditorEventHandler(LayerEditorModeling layerEditor) {
this.layerEditor = requireNonNull(layerEditor, "layerEditor");
}
@Override
public void figureAdded(DrawingEditorEvent e) {
Figure figure = e.getFigure();
ModelComponent model = figure.get(FigureConstants.MODEL);
if (model instanceof DrawnModelComponent) {
layerEditor.add((DrawnModelComponent) model);
}
}
@Override
public void figureRemoved(DrawingEditorEvent e) {
Figure figure = e.getFigure();
ModelComponent model = figure.get(FigureConstants.MODEL);
if (model instanceof DrawnModelComponent) {
layerEditor.remove((DrawnModelComponent) model);
}
}
@Override
public void figureSelected(DrawingEditorEvent e) {
}
}

View File

@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import org.opentcs.guing.base.model.DrawnModelComponent;
import org.opentcs.guing.common.components.layer.LayerEditor;
/**
* Provides methods to edit layers.
*/
public interface LayerEditorModeling
extends
LayerEditor {
/**
* Creates a new layer.
*/
void createLayer();
/**
* Deletes the layer with the given layer ID.
*
* @param layerId The ID of the layer to delete.
* @throws IllegalArgumentException If a layer with the given layer ID doesn't exist.
*/
void deleteLayer(int layerId)
throws IllegalArgumentException;
/**
* Adds the given model component to the layer that is set in the component's layer wrapper
* property.
*
* @param modelComponent The model component to add.
*/
void add(DrawnModelComponent modelComponent);
/**
* Removes the given model component from its layer.
*
* @param modelComponent The model component to remove.
*/
void remove(DrawnModelComponent modelComponent);
/**
* Moves the layer with the given ID one level down.
*
* @param layerId The ID of the layer.
*/
void moveLayerDown(int layerId);
/**
* Moves the layer with the given ID one level up.
*
* @param layerId The ID of the layer.
*/
void moveLayerUp(int layerId);
/**
* Sets the layer with the given ID as the active layer.
*
* @param layerId The ID of the layer.
*/
void setLayerActive(int layerId);
/**
* Sets the group ID for the layer with the given ID.
*
* @param layerId The ID of the layer.
* @param groupId The ID of the layer group.
*/
void setLayerGroupId(int layerId, int groupId);
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import org.opentcs.guing.common.components.layer.LayerGroupEditor;
/**
* Provides methods to edit layer groups.
*/
public interface LayerGroupEditorModeling
extends
LayerGroupEditor {
/**
* Creates a new layer group.
*/
void createLayerGroup();
/**
* Deletes the layer group with the given layer group ID.
*
* @param groupId The ID of the layer group to delete.
* @throws IllegalArgumentException If a layer group with the given layer group ID doesn't exist.
*/
void deleteLayerGroup(int groupId)
throws IllegalArgumentException;
}

View File

@@ -0,0 +1,218 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.table.TableRowSorter;
import org.opentcs.data.model.visualization.Layer;
import org.opentcs.guing.common.components.layer.LayerGroupManager;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.guing.common.util.IconToolkit;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
/**
* A panel to display and edit layer groups.
*/
public class LayerGroupsPanel
extends
JPanel {
/**
* The path containing the icons.
*/
private static final String ICON_PATH = "/org/opentcs/guing/res/symbols/layer/";
/**
* The resource bundle to use.
*/
private static final ResourceBundle BUNDLE
= ResourceBundle.getBundle(I18nPlantOverviewModeling.LAYERS_PATH);
/**
* The model manager.
*/
private final ModelManager modelManager;
/**
* The layer manager.
*/
private final LayerGroupManager layerGroupManager;
/**
* The layer editor.
*/
private final LayerGroupEditorModeling layerGroupEditor;
/**
* The table to display available layers.
*/
private JTable table;
/**
* The table model.
*/
private LayerGroupsTableModel tableModel;
@Inject
@SuppressWarnings("this-escape")
public LayerGroupsPanel(
ModelManager modelManager,
LayerGroupManager layerGroupManager,
LayerGroupEditorModeling layerGroupEditor
) {
this.modelManager = requireNonNull(modelManager, "modelManager");
this.layerGroupManager = requireNonNull(layerGroupManager, "layerGroupManager");
this.layerGroupEditor = requireNonNull(layerGroupEditor, "layerGroupEditor");
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout());
tableModel = new LayerGroupsTableModel(modelManager, layerGroupEditor);
layerGroupManager.addLayerGroupChangeListener(tableModel);
table = new JTable(tableModel);
initTable();
add(createToolBar(), BorderLayout.NORTH);
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void initTable() {
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableRowSorter<LayerGroupsTableModel> sorter = new TableRowSorter<>(tableModel);
// Sort the table by the layer ordinals...
sorter.setSortKeys(
Arrays.asList(
new RowSorter.SortKey(LayerGroupsTableModel.COLUMN_ID, SortOrder.DESCENDING)
)
);
// ...but prevent manual sorting.
for (int i = 0; i < table.getColumnCount(); i++) {
sorter.setSortable(i, false);
}
sorter.setSortsOnUpdates(true);
table.setRowSorter(sorter);
// Hide the column that shows the layer group IDs.
table.removeColumn(
table.getColumnModel()
.getColumn(table.convertColumnIndexToView(LayerGroupsTableModel.COLUMN_ID))
);
}
private JToolBar createToolBar() {
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.add(createAddGroupButton());
toolBar.add(createRemoveGroupButton());
return toolBar;
}
private JButton createAddGroupButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button
= new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "create-layer-group.16.png"));
button.addActionListener(actionEvent -> {
layerGroupEditor.createLayerGroup();
table.getSelectionModel().setSelectionInterval(0, 0);
});
button.setToolTipText(BUNDLE.getString("layerGroupsPanel.button_addGroup.tooltipText"));
return button;
}
private JButton createRemoveGroupButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button
= new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "delete-layer-group.16.png"));
button.addActionListener(new RemoveGroupListener());
button.setToolTipText(BUNDLE.getString("layerGroupsPanel.button_removeGroup.tooltipText"));
// Allow the remove group button to be pressed only if there's a group selected and if there's
// more than one group in the model.
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
button.setEnabled(table.getSelectedRow() != -1 && tableModel.getRowCount() > 1);
});
tableModel.addTableModelListener(tableModelEvent -> {
button.setEnabled(table.getSelectedRow() != -1 && tableModel.getRowCount() > 1);
});
return button;
}
private class RemoveGroupListener
implements
ActionListener {
/**
* Creates a new instance.
*/
RemoveGroupListener() {
}
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
int selectedGroupId = tableModel.getDataAt(table.convertRowIndexToModel(selectedRow)).getId();
Map<Boolean, List<Layer>> layersByGroupAssignment = modelManager.getModel()
.getLayoutModel().getPropertyLayerWrappers().getValue().values().stream()
.map(wrapper -> wrapper.getLayer())
.collect(Collectors.partitioningBy(layer -> layer.getGroupId() == selectedGroupId));
List<Layer> layersAssignedToGroupToDelete = layersByGroupAssignment.get(Boolean.TRUE);
List<Layer> layersAssignedToOtherGroups = layersByGroupAssignment.get(Boolean.FALSE);
if (layersAssignedToOtherGroups.isEmpty()) {
// All layers in the model are assigned to the group the user wants to remove.
// In this case, removing the group is not allowed as that would mean that all layers would
// be removed as well and there wouldn't be any layers left.
JOptionPane.showMessageDialog(
LayerGroupsPanel.this,
BUNDLE.getString("layerGroupsPanel.optionPane_groupRemovalNotPossible.message"),
BUNDLE.getString("layerGroupsPanel.optionPane_groupRemovalNotPossible.title"),
JOptionPane.INFORMATION_MESSAGE
);
return;
}
if (!layersAssignedToGroupToDelete.isEmpty()) {
// The user is about remove a group with layers assigned to it. Removing the group results
// in the assigned layers and the model components they contain to be removed as well.
int selectedOption = JOptionPane.showConfirmDialog(
LayerGroupsPanel.this,
BUNDLE.getString(
"layerGroupsPanel.optionPane_confirmGroupAndAssignedLayersRemoval.message"
),
BUNDLE.getString(
"layerGroupsPanel.optionPane_confirmGroupAndAssignedLayersRemoval.title"
),
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
);
if (selectedOption == JOptionPane.NO_OPTION) {
return;
}
}
layerGroupEditor.deleteLayerGroup(selectedGroupId);
}
}
}

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import javax.swing.SwingUtilities;
import org.opentcs.guing.common.components.layer.AbstractLayerGroupsTableModel;
import org.opentcs.guing.common.components.layer.LayerGroupEditor;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* The table model for layer groups for the Model Editor application.
*/
class LayerGroupsTableModel
extends
AbstractLayerGroupsTableModel {
/**
* Creates a new instance.
*
* @param modelManager The model manager.
* @param layerGroupEditor The layer group editor.
*/
LayerGroupsTableModel(ModelManager modelManager, LayerGroupEditor layerGroupEditor) {
super(modelManager, layerGroupEditor);
}
@Override
protected boolean isNameColumnEditable() {
return true;
}
@Override
protected boolean isVisibleColumnEditable() {
return true;
}
@Override
public void groupsInitialized() {
// Once the layers are initialized we want to redraw the entire table to avoid any
// display errors.
executeOnEventDispatcherThread(() -> fireTableDataChanged());
}
@Override
public void groupsChanged() {
// Update the entire table but don't use fireTableDataChanged() to preserve the current
// selection.
executeOnEventDispatcherThread(() -> fireTableRowsUpdated(0, getRowCount() - 1));
}
@Override
public void groupAdded() {
// Groups are always added to the top (with regard to sorting).
executeOnEventDispatcherThread(() -> fireTableRowsInserted(0, 0));
}
@Override
public void groupRemoved() {
// At this point, there's no way for us to determine the row the removed layer was in. The
// entry has already been remove from this table model's data source which is provided by
// layersByOrdinal().
// Workaround: Since the table now contains one entry less, pretend that the last entry was
// deleted.
executeOnEventDispatcherThread(() -> fireTableRowsDeleted(getRowCount(), getRowCount()));
}
/**
* Ensures the given runnable is executed on the EDT.
* If the runnable is already being called on the EDT, the runnable is executed immediately.
* Otherwise it is scheduled for execution on the EDT.
* <p>
* Note: Deferring a runnable by scheduling it for execution on the EDT even though it would
* have already been executed on the EDT may lead to exceptions due to data inconsistency.
* </p>
*
* @param runnable The runnable.
*/
private void executeOnEventDispatcherThread(Runnable runnable) {
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
}
else {
SwingUtilities.invokeLater(runnable);
}
}
}

View File

@@ -0,0 +1,421 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import static org.opentcs.util.Assertions.checkArgument;
import jakarta.inject.Inject;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.jhotdraw.draw.AbstractFigure;
import org.jhotdraw.draw.Drawing;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.data.model.visualization.Layer;
import org.opentcs.data.model.visualization.LayerGroup;
import org.opentcs.guing.base.components.layer.LayerWrapper;
import org.opentcs.guing.base.model.DrawnModelComponent;
import org.opentcs.guing.base.model.ModelComponent;
import org.opentcs.guing.common.application.ViewManager;
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingView;
import org.opentcs.guing.common.components.layer.DefaultLayerManager;
import org.opentcs.guing.common.components.layer.LayerManager;
import org.opentcs.util.event.EventBus;
/**
* The {@link LayerManager} implementation for the model editor application.
*/
public class LayerManagerModeling
extends
DefaultLayerManager
implements
LayerEditorModeling,
LayerGroupEditorModeling,
ActiveLayerProvider {
/**
* The currently active layer.
*/
private LayerWrapper activeLayerWrapper;
@Inject
public LayerManagerModeling(
ViewManager viewManager,
@ApplicationEventBus
EventBus eventBus
) {
super(viewManager, eventBus);
}
@Override
public void createLayer() {
Layer layer = createLayerWrapper().getLayer();
getLayerChangeListener().layerAdded();
setLayerActive(layer.getId());
}
@Override
public void deleteLayer(int layerId)
throws IllegalArgumentException {
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
deleteLayerWrapper(layerId);
getLayerChangeListener().layerRemoved();
if (layerId == activeLayerWrapper.getLayer().getId()) {
handleActiveLayerRemoved();
}
}
@Override
public void add(DrawnModelComponent modelComponent) {
int layerId = modelComponent.getPropertyLayerWrapper().getValue().getLayer().getId();
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
addComponent(modelComponent, layerId);
}
@Override
public void remove(DrawnModelComponent modelComponent) {
removeComponent(modelComponent);
}
@Override
public void moveLayerDown(int layerId) {
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
List<LayerWrapper> layerWrappersByOrdinal = getLayerWrappers().values().stream()
.sorted(Comparator.comparing(wrapper -> wrapper.getLayer().getOrdinal()))
.collect(Collectors.toList());
if (layerId == layerWrappersByOrdinal.get(0).getLayer().getId()) {
// The layer with the given layer ID is already the lowest layer.
return;
}
shiftLayerByOne(layerId, layerWrappersByOrdinal);
getLayerChangeListener().layersChanged();
}
@Override
public void moveLayerUp(int layerId) {
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
List<LayerWrapper> layerWrappersByReverseOrdinal = getLayerWrappers().values().stream()
.sorted(
Comparator.comparing(
wrapper -> wrapper.getLayer().getOrdinal(),
Comparator.reverseOrder()
)
)
.collect(Collectors.toList());
if (layerId == layerWrappersByReverseOrdinal.get(0).getLayer().getId()) {
// The layer with the given layer ID is already the heighest layer.
return;
}
shiftLayerByOne(layerId, layerWrappersByReverseOrdinal);
getLayerChangeListener().layersChanged();
}
@Override
public void setLayerActive(int layerId) {
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
activeLayerWrapper = getLayerWrapper(layerId);
getLayerChangeListener().layersChanged();
}
@Override
public void createLayerGroup() {
int groupId = getNextAvailableLayerGroupId();
// Add the created layer group to the system model's layout.
addLayerGroup(new LayerGroup(groupId, "Group " + groupId, true));
notifyGroupAdded();
}
@Override
public void deleteLayerGroup(int groupId)
throws IllegalArgumentException {
checkArgument(
getLayerGroup(groupId) != null,
"A layer group with layer group ID '%d' doesn't exist.",
groupId
);
// Delete the layers that are assigned to the group.
Set<Integer> layerAssignedToGroup = getLayerWrappers().values().stream()
.map(wrapper -> wrapper.getLayer())
.filter(layer -> layer.getGroupId() == groupId)
.map(layer -> layer.getId())
.collect(Collectors.toSet());
layerAssignedToGroup.forEach(layerId -> deleteLayer(layerId));
removeLayerGroup(groupId);
notifyGroupRemoved();
}
@Override
public void setLayerGroupId(int layerId, int groupId) {
checkArgument(
getLayerWrapper(layerId) != null,
"A layer with layer ID '%d' doesn't exist.",
layerId
);
checkArgument(
getLayerGroup(groupId) != null,
"A layer group with layer group ID '%d' doesn't exist.",
groupId
);
LayerWrapper wrapper = getLayerWrapper(layerId);
boolean visibleBefore = wrapper.getLayer().isVisible() && wrapper.getLayerGroup().isVisible();
wrapper.setLayer(wrapper.getLayer().withGroupId(groupId));
wrapper.setLayerGroup(getLayerGroup(groupId));
boolean visibleAfter = wrapper.getLayer().isVisible() && wrapper.getLayerGroup().isVisible();
if (visibleBefore != visibleAfter) {
layerVisibilityChanged(wrapper.getLayer(), visibleAfter);
}
getLayerChangeListener().layersChanged();
}
@Override
public LayerWrapper getActiveLayer() {
return activeLayerWrapper;
}
@Override
protected void reset() {
super.reset();
}
@Override
protected void restoreLayers() {
// Make sure there will be one layer set as the active layer. Do it for the highest layer.
activeLayerWrapper = getLayerWrappers().values().stream()
.sorted(
Comparator.comparing(
wrapper -> wrapper.getLayer().getOrdinal(),
Comparator.reverseOrder()
)
)
.findFirst()
.get();
super.restoreLayers();
}
private LayerWrapper createLayerWrapper() {
int layerId = getNextAvailableLayerId();
int layerOrdinal = getNextAvailableLayerOrdinal();
LayerGroup group = getLayerGroups().values().iterator().next();
Layer layer = new Layer(layerId, layerOrdinal, true, "Layer " + layerId, group.getId());
LayerWrapper wrapper = new LayerWrapper(layer, group);
getComponents().put(layerId, new HashSet<>());
// Add the created layer wrapper to the system model's layout.
addLayerWrapper(wrapper);
return wrapper;
}
private int getNextAvailableLayerId() {
return getLayerWrappers().values().stream()
.mapToInt(wrapper -> wrapper.getLayer().getId())
.max()
.getAsInt() + 1;
}
private int getNextAvailableLayerOrdinal() {
return getLayerWrappers().values().stream()
.mapToInt(wrapper -> wrapper.getLayer().getOrdinal())
.max()
.getAsInt() + 1;
}
private int getNextAvailableLayerGroupId() {
return getLayerGroups().values().stream()
.mapToInt(group -> group.getId())
.max()
.getAsInt() + 1;
}
private void deleteLayerWrapper(int layerId) {
Set<OpenTCSDrawingView> drawingViews = getDrawingViews();
drawingViews.forEach(drawingView -> drawingView.getDrawing().willChange());
Set<ModelComponent> componentsToDelete = getComponents().get(layerId).stream()
.map(component -> (ModelComponent) component)
.collect(Collectors.toSet());
drawingViews.forEach(drawingView -> drawingView.delete(componentsToDelete));
drawingViews.forEach(drawingView -> drawingView.getDrawing().changed());
getComponents().remove(layerId);
// Remove the deleted layer wrapper from the system model's layout.
removeLayerWrapper(layerId);
}
/**
* Returns the set of drawing views the layer manager is working with.
*
* @return The set of drawing views the layer manager is working with.
*/
private Set<OpenTCSDrawingView> getDrawingViews() {
return getViewManager().getDrawingViewMap().values().stream()
.map(scrollPane -> scrollPane.getDrawingView())
.collect(Collectors.toSet());
}
private void handleActiveLayerRemoved() {
List<Layer> layersByOrdinal = getLayerWrappers().values().stream()
.map(wrapper -> wrapper.getLayer())
.sorted(Comparator.comparing(layer -> layer.getOrdinal()))
.collect(Collectors.toList());
Optional<Layer> layerUnderRemovedActiveLayer = layersByOrdinal.stream()
.takeWhile(layer -> layer.getOrdinal() < activeLayerWrapper.getLayer().getOrdinal())
.reduce((layer1, layer2) -> layer2);
// If there's a layer right under the active layer that just has been removed, select that layer
// as the new active layer. Otherwise, just select the lowest layer.
if (layerUnderRemovedActiveLayer.isPresent()) {
setLayerActive(layerUnderRemovedActiveLayer.get().getId());
}
else {
setLayerActive(layersByOrdinal.get(0).getId());
}
}
private void shiftLayerByOne(int layerId, List<LayerWrapper> layers) {
LayerWrapper layerWrapper = getLayerWrapper(layerId);
int layerWrapperBeforeIndex = layers.indexOf(layerWrapper) - 1;
LayerWrapper layerWrapperBefore = layers.get(layerWrapperBeforeIndex);
swapLayerOrdinals(layerWrapper, layerWrapperBefore);
updateLayerComponentsInDrawing(layerWrapper.getLayer(), layerWrapperBefore.getLayer());
}
private void swapLayerOrdinals(LayerWrapper layerWrapper1, LayerWrapper layerWrapper2) {
int ordinal1 = layerWrapper1.getLayer().getOrdinal();
int ordinal2 = layerWrapper2.getLayer().getOrdinal();
Layer oldLayer = layerWrapper1.getLayer();
Layer newLayer = oldLayer.withOrdinal(ordinal2);
layerWrapper1.setLayer(newLayer);
oldLayer = layerWrapper2.getLayer();
newLayer = oldLayer.withOrdinal(ordinal1);
layerWrapper2.setLayer(newLayer);
}
private void updateLayerComponentsInDrawing(Layer... layers) {
Set<DrawnModelComponent> componentsToUpdate = new HashSet<>();
for (Layer layer : layers) {
if (layer.isVisible()) {
componentsToUpdate.addAll(getComponents().get(layer.getId()));
}
}
Set<Drawing> drawings = getDrawings();
drawings.forEach(drawing -> drawing.willChange());
for (DrawnModelComponent modelComponent : componentsToUpdate) {
AbstractFigure figure = (AbstractFigure) getSystemModel().getFigure(modelComponent);
drawings.forEach(drawing -> {
drawing.basicRemove(figure);
drawing.basicAdd(figure);
});
}
drawings.forEach(drawing -> drawing.changed());
}
/**
* Adds the given layer group to the system model's layout.
*
* @param layerWrapper The layer group to add.
* @throws IllegalArgumentException If a layer group with the same layer group ID already exists
* in the system model's layout.
*/
private void addLayerGroup(LayerGroup layerGroup)
throws IllegalArgumentException {
int groupId = layerGroup.getId();
checkArgument(
getLayerGroup(groupId) == null,
"A layer group for group ID '%d' already exists in the model.",
groupId
);
getLayerGroups().put(groupId, layerGroup);
}
/**
* Removes the layer group with the given group ID from the system model's layout.
*
* @param layerId The layer group ID.
*/
private void removeLayerGroup(int groupId) {
getLayerGroups().remove(groupId);
}
/**
* Adds the given layer wrapper to the system model's layout.
*
* @param layerWrapper The layer wrapper to add.
* @throws IllegalArgumentException If a layer wrapper with the same layer ID already exists
* in the system model's layout.
*/
private void addLayerWrapper(LayerWrapper layerWrapper)
throws IllegalArgumentException {
int layerId = layerWrapper.getLayer().getId();
checkArgument(
getLayerWrapper(layerId) == null,
"A layer wrapper for layer ID '%d' already exists in the model.",
layerId
);
getLayerWrappers().put(layerId, layerWrapper);
}
/**
* Removes the layer wrapper with the given layer ID from the system model's layout.
*
* @param layerId The layer ID.
*/
private void removeLayerWrapper(int layerId) {
getLayerWrappers().remove(layerId);
}
}

View File

@@ -0,0 +1,380 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import org.opentcs.data.model.visualization.LayerGroup;
import org.opentcs.guing.common.components.layer.DisabledCheckBoxCellRenderer;
import org.opentcs.guing.common.components.layer.LayerGroupCellRenderer;
import org.opentcs.guing.common.components.layer.LayerGroupManager;
import org.opentcs.guing.common.components.layer.LayerManager;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.guing.common.util.IconToolkit;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
import org.opentcs.util.gui.StringListCellRenderer;
/**
* A panel to display and edit layers.
*/
public class LayersPanel
extends
JPanel {
/**
* The path containing the icons.
*/
private static final String ICON_PATH = "/org/opentcs/guing/res/symbols/layer/";
/**
* The resource bundle to use.
*/
private static final ResourceBundle BUNDLE
= ResourceBundle.getBundle(I18nPlantOverviewModeling.LAYERS_PATH);
/**
* The model manager.
*/
private final ModelManager modelManager;
/**
* The layer manager.
*/
private final LayerManager layerManager;
/**
* The layer group manager.
*/
private final LayerGroupManager layerGroupManager;
/**
* The layer editor.
*/
private final LayerEditorModeling layerEditor;
/**
* Provides the currently active layer.
*/
private final ActiveLayerProvider activeLayerProvider;
/**
* The table to display available layers.
*/
private JTable table;
/**
* The table model.
*/
private LayersTableModel tableModel;
@Inject
@SuppressWarnings("this-escape")
public LayersPanel(
ModelManager modelManager,
LayerManager layerManager,
LayerGroupManager layerGroupManager,
LayerEditorModeling layerEditor,
ActiveLayerProvider activeLayerProvider
) {
this.modelManager = requireNonNull(modelManager, "modelManager");
this.layerManager = requireNonNull(layerManager, "layerManager");
this.layerGroupManager = requireNonNull(layerGroupManager, "layerGroupManager");
this.layerEditor = requireNonNull(layerEditor, "layerEditor");
this.activeLayerProvider = requireNonNull(activeLayerProvider, "activeLayerProvider");
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout());
tableModel = new LayersTableModel(modelManager, activeLayerProvider, layerEditor);
layerManager.setLayerChangeListener(tableModel);
layerGroupManager.addLayerGroupChangeListener(tableModel);
table = new JTable(tableModel);
initTable();
add(createToolBar(), BorderLayout.NORTH);
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void initTable() {
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableRowSorter<LayersTableModel> sorter = new TableRowSorter<>(tableModel);
// Sort the table by the layer ordinals...
sorter.setSortKeys(
Arrays.asList(
new RowSorter.SortKey(LayersTableModel.COLUMN_ORDINAL, SortOrder.DESCENDING)
)
);
// ...but prevent manual sorting.
for (int i = 0; i < table.getColumnCount(); i++) {
sorter.setSortable(i, false);
}
sorter.setSortsOnUpdates(true);
table.setRowSorter(sorter);
// Hide the column that shows the layer ordinals.
table.removeColumn(
table.getColumnModel()
.getColumn(table.convertColumnIndexToView(LayersTableModel.COLUMN_ORDINAL))
);
table.getColumnModel()
.getColumn(table.convertColumnIndexToView(LayersTableModel.COLUMN_GROUP_VISIBLE))
.setCellRenderer(new DisabledCheckBoxCellRenderer());
initActiveColumn();
initGroupColumn();
}
private JToolBar createToolBar() {
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
toolBar.add(createAddLayerButton());
toolBar.add(createRemoveLayerButton());
toolBar.add(createMoveLayerUpButton());
toolBar.add(createMoveLayerDownButton());
return toolBar;
}
private void initActiveColumn() {
// Since our concept of layers allows only one active layer at a time, a representation of that
// state as radio buttons seems to have the potential to be more intuitive for users.
// Radio buttons usually come with/in a button group which ensures that only one of the radio
// buttons in that group can be selected at a time. In a table, though, such a button group
// doesn't seem to work very well if it's used to group radio buttons across multiple rows.
// For that reason the behavior of radio buttons in a button group is "emulated" in/trough
// the LayerManager implementation.
TableColumn columnActive = table.getColumnModel()
.getColumn(table.convertColumnIndexToView(LayersTableModel.COLUMN_ACTIVE));
columnActive.setCellEditor(new RadioButtonCellEditor());
columnActive.setCellRenderer(new RadioButtonCellRenderer());
}
private void initGroupColumn() {
TableColumn columnGroup = table.getColumnModel()
.getColumn(table.convertColumnIndexToView(LayersTableModel.COLUMN_GROUP));
columnGroup.setCellRenderer(new LayerGroupCellRenderer());
columnGroup.setCellEditor(new GroupCellEditor());
}
private JButton createAddLayerButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button = new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "create-layer.16.png"));
button.addActionListener(actionEvent -> {
layerEditor.createLayer();
table.getSelectionModel().setSelectionInterval(0, 0);
});
button.setToolTipText(BUNDLE.getString("layersPanel.button_addLayer.tooltipText"));
return button;
}
private JButton createRemoveLayerButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button = new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "delete-layer.16.png"));
button.addActionListener(new RemoveLayerListener());
button.setToolTipText(BUNDLE.getString("layersPanel.button_removeLayer.tooltipText"));
// Allow the remove layer button to be pressed only if there's a layer selected and if there's
// more than one layer in the model.
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
button.setEnabled(table.getSelectedRow() != -1 && tableModel.getRowCount() > 1);
});
tableModel.addTableModelListener(tableModelEvent -> {
button.setEnabled(table.getSelectedRow() != -1 && tableModel.getRowCount() > 1);
});
return button;
}
private JButton createMoveLayerUpButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button
= new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "move-layer-up.16.png"));
button.setEnabled(false);
button.addActionListener(actionEvent -> {
int selectedRow = table.getSelectedRow();
int selectedLayerId = tableModel.getDataAt(table.convertRowIndexToModel(selectedRow)).getId();
layerEditor.moveLayerUp(selectedLayerId);
});
button.setToolTipText(BUNDLE.getString("layersPanel.button_moveLayerUp.tooltipText"));
// Allow the button to be pressed only if there's a layer selected.
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
button.setEnabled(table.getSelectedRow() != -1);
});
return button;
}
private JButton createMoveLayerDownButton() {
IconToolkit iconkit = IconToolkit.instance();
JButton button
= new JButton(iconkit.getImageIconByFullPath(ICON_PATH + "move-layer-down.16.png"));
button.setEnabled(false);
button.addActionListener(actionEvent -> {
int selectedRow = table.getSelectedRow();
int selectedLayerId = tableModel.getDataAt(table.convertRowIndexToModel(selectedRow)).getId();
layerEditor.moveLayerDown(selectedLayerId);
});
button.setToolTipText(BUNDLE.getString("layersPanel.button_moveLayerDown.tooltipText"));
// Allow the button to be pressed only if there's a layer selected.
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
button.setEnabled(table.getSelectedRow() != -1);
});
return button;
}
private class RemoveLayerListener
implements
ActionListener {
/**
* Creates a new instance.
*/
RemoveLayerListener() {
}
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = table.getSelectedRow();
int selectedLayerId = tableModel.getDataAt(table.convertRowIndexToModel(selectedRow)).getId();
if (layerManager.containsComponents(selectedLayerId)) {
int selectedOption = JOptionPane.showConfirmDialog(
LayersPanel.this,
BUNDLE.getString("layersPanel.optionPane_confirmLayerWithComponentsRemoval.message"),
BUNDLE.getString("layersPanel.optionPane_confirmLayerWithComponentsRemoval.title"),
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
);
if (selectedOption == JOptionPane.NO_OPTION) {
return;
}
}
layerEditor.deleteLayer(selectedLayerId);
}
}
private class RadioButtonCellRenderer
implements
TableCellRenderer {
private final Border unfocusedCellBorder = BorderFactory.createEmptyBorder();
private final Border focusedCellBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
private final JRadioButton radioButton;
RadioButtonCellRenderer() {
radioButton = new JRadioButton();
radioButton.setHorizontalAlignment(JRadioButton.CENTER);
radioButton.setBorderPainted(true);
}
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col
) {
radioButton.setBackground(
isSelected ? table.getSelectionBackground() : table.getBackground()
);
radioButton.setForeground(
isSelected ? table.getSelectionForeground() : table.getForeground()
);
radioButton.setBorder(hasFocus ? focusedCellBorder : unfocusedCellBorder);
radioButton.setSelected(Boolean.TRUE.equals(value));
return radioButton;
}
}
private class RadioButtonCellEditor
extends
AbstractCellEditor
implements
TableCellEditor {
private final JRadioButton radioButton;
RadioButtonCellEditor() {
radioButton = new JRadioButton();
radioButton.setHorizontalAlignment(JRadioButton.CENTER);
radioButton.addActionListener(actionEvent -> stopCellEditing());
}
@Override
public Component getTableCellEditorComponent(
JTable table, Object value,
boolean isSelected, int row, int col
) {
radioButton.setSelected(Boolean.TRUE.equals(value));
return radioButton;
}
@Override
public Object getCellEditorValue() {
return radioButton.isSelected();
}
}
private class GroupCellEditor
extends
DefaultCellEditor {
private final DefaultComboBoxModel<LayerGroup> model;
GroupCellEditor() {
super(new JComboBox<LayerGroup>());
@SuppressWarnings("unchecked")
JComboBox<LayerGroup> combobox = (JComboBox<LayerGroup>) getComponent();
combobox.setRenderer(new StringListCellRenderer<>(group -> group.getName()));
this.model = (DefaultComboBoxModel<LayerGroup>) combobox.getModel();
}
@Override
public Component getTableCellEditorComponent(
JTable table, Object value,
boolean isSelected, int row, int column
) {
model.removeAllElements();
List<LayerGroup> groups = modelManager.getModel().getLayoutModel().getPropertyLayerGroups()
.getValue().values().stream()
.sorted((o1, o2) -> Integer.compare(o1.getId(), o2.getId()))
.collect(Collectors.toList());
model.addAll(groups);
model.setSelectedItem(table.getModel().getValueAt(row, column));
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}

View File

@@ -0,0 +1,311 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.layer;
import static java.util.Objects.requireNonNull;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import org.opentcs.data.model.visualization.Layer;
import org.opentcs.data.model.visualization.LayerGroup;
import org.opentcs.guing.base.components.layer.LayerWrapper;
import org.opentcs.guing.common.components.layer.LayerChangeListener;
import org.opentcs.guing.common.components.layer.LayerGroupChangeListener;
import org.opentcs.guing.common.persistence.ModelManager;
import org.opentcs.modeleditor.util.I18nPlantOverviewModeling;
/**
* A table model for layers.
*/
class LayersTableModel
extends
AbstractTableModel
implements
LayerChangeListener,
LayerGroupChangeListener {
/**
* The number of the "Active" column.
*/
public static final int COLUMN_ACTIVE = 0;
/**
* The number of the "Ordinal" column.
*/
public static final int COLUMN_ORDINAL = 1;
/**
* The number of the "Visible" column.
*/
public static final int COLUMN_VISIBLE = 2;
/**
* The number of the "Name" column.
*/
public static final int COLUMN_NAME = 3;
/**
* The number of the "Group" column.
*/
public static final int COLUMN_GROUP = 4;
/**
* The number of the "Group visible" column.
*/
public static final int COLUMN_GROUP_VISIBLE = 5;
/**
* The resource bundle to use.
*/
private static final ResourceBundle BUNDLE
= ResourceBundle.getBundle(I18nPlantOverviewModeling.LAYERS_PATH);
/**
* The column names.
*/
private static final String[] COLUMN_NAMES
= new String[]{
BUNDLE.getString(
"layersTableModel.column_active.headerText"
),
BUNDLE.getString(
"layersTableModel.column_ordinal.headerText"
),
BUNDLE.getString(
"layersTableModel.column_visible.headerText"
),
BUNDLE.getString(
"layersTableModel.column_name.headerText"
),
BUNDLE.getString(
"layersTableModel.column_group.headerText"
),
BUNDLE.getString(
"layersTableModel.column_groupVisible.headerText"
)
};
/**
* The column classes.
*/
private static final Class<?>[] COLUMN_CLASSES
= new Class<?>[]{
Boolean.class,
Integer.class,
Boolean.class,
String.class,
LayerGroup.class,
Boolean.class
};
/**
* The model manager.
*/
private final ModelManager modelManager;
/**
* Provides the currently active layer.
*/
private final ActiveLayerProvider activeLayerProvider;
/**
* The layer editor.
*/
private final LayerEditorModeling layerEditor;
/**
* Creates a new instance.
*
* @param modelManager The model manager.
* @param activeLayerProvider Provides the currently active layer.
* @param layerEditor The layer editor.
*/
LayersTableModel(
ModelManager modelManager,
ActiveLayerProvider activeLayerProvider,
LayerEditorModeling layerEditor
) {
this.modelManager = requireNonNull(modelManager, "modelManager");
this.activeLayerProvider = requireNonNull(activeLayerProvider, "activeLayerProvider");
this.layerEditor = requireNonNull(layerEditor, "layerEditor");
}
@Override
public int getRowCount() {
return getLayers().size();
}
@Override
public int getColumnCount() {
return COLUMN_NAMES.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= getRowCount()) {
return null;
}
Layer entry = getLayers().get(rowIndex);
switch (columnIndex) {
case COLUMN_ACTIVE:
return entry.getId() == activeLayerProvider.getActiveLayer().getLayer().getId();
case COLUMN_ORDINAL:
return entry.getOrdinal();
case COLUMN_VISIBLE:
return entry.isVisible();
case COLUMN_NAME:
return entry.getName();
case COLUMN_GROUP:
return getLayerGroups().get(entry.getGroupId());
case COLUMN_GROUP_VISIBLE:
return getLayerGroups().get(entry.getGroupId()).isVisible();
default:
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
}
}
@Override
public String getColumnName(int columnIndex) {
return COLUMN_NAMES[columnIndex];
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return COLUMN_CLASSES[columnIndex];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
switch (columnIndex) {
case COLUMN_ACTIVE:
return true;
case COLUMN_ORDINAL:
return false;
case COLUMN_VISIBLE:
return true;
case COLUMN_NAME:
return true;
case COLUMN_GROUP:
return true;
case COLUMN_GROUP_VISIBLE:
return false;
default:
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
}
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= getRowCount()) {
return;
}
if (aValue == null) {
return;
}
Layer entry = getLayers().get(rowIndex);
switch (columnIndex) {
case COLUMN_ACTIVE:
layerEditor.setLayerActive(entry.getId());
break;
case COLUMN_ORDINAL:
// Do nothing.
break;
case COLUMN_VISIBLE:
layerEditor.setLayerVisible(entry.getId(), (boolean) aValue);
break;
case COLUMN_NAME:
layerEditor.setLayerName(entry.getId(), aValue.toString());
break;
case COLUMN_GROUP:
layerEditor.setLayerGroupId(entry.getId(), ((LayerGroup) aValue).getId());
break;
case COLUMN_GROUP_VISIBLE:
// Do nothing.
break;
default:
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
}
}
@Override
public void layersInitialized() {
// Once the layers are initialized we want to redraw the entire table to avoid any
// display errors.
executeOnEventDispatcherThread(() -> fireTableDataChanged());
}
@Override
public void layersChanged() {
// Update the entire table but don't use fireTableDataChanged() to preserve the current
// selection.
executeOnEventDispatcherThread(() -> fireTableRowsUpdated(0, getRowCount() - 1));
}
@Override
public void layerAdded() {
// Layers are always added to the top (with regard to sorting).
executeOnEventDispatcherThread(() -> fireTableRowsInserted(0, 0));
}
@Override
public void layerRemoved() {
// At this point, there's no way for us to determine the row the removed layer was in. The
// entry has already been remove from this table model's data source which is provided by
// layersByOrdinal().
// Workaround: Since the table now contains one entry less, pretend that the last entry was
// deleted.
executeOnEventDispatcherThread(() -> fireTableRowsDeleted(getRowCount(), getRowCount()));
}
@Override
public void groupsInitialized() {
}
@Override
public void groupsChanged() {
// The visibility of a group, which we display as well, may have changed. Update the table.
executeOnEventDispatcherThread(() -> fireTableRowsUpdated(0, getRowCount() - 1));
}
@Override
public void groupAdded() {
}
@Override
public void groupRemoved() {
}
public Layer getDataAt(int index) {
return getLayers().get(index);
}
private List<Layer> getLayers() {
return getLayerWrappers().values().stream()
.map(wrapper -> wrapper.getLayer())
.collect(Collectors.toList());
}
private Map<Integer, LayerWrapper> getLayerWrappers() {
return modelManager.getModel().getLayoutModel().getPropertyLayerWrappers().getValue();
}
private Map<Integer, LayerGroup> getLayerGroups() {
return modelManager.getModel().getLayoutModel().getPropertyLayerGroups().getValue();
}
/**
* Ensures the given runnable is executed on the EDT.
* If the runnable is already being called on the EDT, the runnable is executed immediately.
* Otherwise it is scheduled for execution on the EDT.
* <p>
* Note: Deferring a runnable by scheduling it for execution on the EDT even though it would
* have already been executed on the EDT may lead to exceptions due to data inconsistency.
* </p>
*
* @param runnable The runnable.
*/
private void executeOnEventDispatcherThread(Runnable runnable) {
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
}
else {
SwingUtilities.invokeLater(runnable);
}
}
}

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.components.tree.elements;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import javax.swing.JPopupMenu;
import org.opentcs.guing.base.model.elements.VehicleModel;
import org.opentcs.guing.common.application.GuiManager;
import org.opentcs.guing.common.components.tree.elements.VehicleUserObject;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* A Vehicle object in the tree view.
*/
public class VehicleUserObjectModeling
extends
VehicleUserObject {
/**
* Creates a new instance.
*
* @param model The corresponding vehicle object.
* @param guiManager The gui manager.
* @param modelManager Provides the current system model.
*/
@Inject
public VehicleUserObjectModeling(
@Assisted
VehicleModel model,
GuiManager guiManager,
ModelManager modelManager
) {
super(model, guiManager, modelManager);
}
@Override // AbstractUserObject
public JPopupMenu getPopupMenu() {
return null;
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import static java.util.Objects.requireNonNull;
import static org.opentcs.util.Assertions.checkArgument;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import org.opentcs.guing.base.components.properties.type.LengthProperty;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* Calculates the length of {@link PathModel.Type#BEZIER} paths.
*/
public class BezierLength
implements
PathLengthFunction {
private final double scaleX;
private final double scaleY;
private final PathLengthMath pathLengthMath;
/**
* Creates a new instance.
*
* @param manager Provides access to the current system model.
* @param pathLengthMath Provides methods to evaluate the position of a point of a Bezier curve.
*/
@Inject
public BezierLength(
@Nonnull
ModelManager manager,
@Nonnull
PathLengthMath pathLengthMath
) {
requireNonNull(manager, "manager");
this.pathLengthMath = requireNonNull(pathLengthMath, "pathLengthMath");
scaleX = manager.getModel().getLayoutModel().getPropertyScaleX()
.getValueByUnit(LengthProperty.Unit.MM);
scaleY = manager.getModel().getLayoutModel().getPropertyScaleY()
.getValueByUnit(LengthProperty.Unit.MM);
}
@Override
public double applyAsDouble(
@Nonnull
PathModel path
) {
requireNonNull(path, "path");
String[] cps = path.getPropertyPathControlPoints().getText().split(";");
checkArgument(
cps.length == 2,
String.format("Path '%s' does not have exactly two control points.", path.getName())
);
PointModel start = (PointModel) path.getStartComponent();
PointModel end = (PointModel) path.getEndComponent();
return pathLengthMath.approximateCubicBezierCurveLength(
new Coordinate(
start.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
start.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
),
new Coordinate(
Double.parseDouble(cps[0].split(",")[0]) * scaleX,
Double.parseDouble(cps[0].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
Double.parseDouble(cps[1].split(",")[0]) * scaleX,
Double.parseDouble(cps[1].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
end.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
end.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
),
1000
);
}
}

View File

@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import static java.util.Objects.requireNonNull;
import static org.opentcs.util.Assertions.checkArgument;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import org.opentcs.guing.base.components.properties.type.LengthProperty;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.base.model.elements.PointModel;
import org.opentcs.guing.common.persistence.ModelManager;
/**
* Calculates the length of {@link PathModel.Type#BEZIER_3} paths.
*/
public class BezierThreeLength
implements
PathLengthFunction {
private final double scaleX;
private final double scaleY;
private final PathLengthMath pathLengthMath;
/**
* Creates a new instance.
*
* @param manager Provides access to the current system model.
* @param pathLengthMath Provides methods to evaluate the position of a point of a Bezier curve.
*/
@Inject
public BezierThreeLength(
@Nonnull
ModelManager manager,
@Nonnull
PathLengthMath pathLengthMath
) {
requireNonNull(manager, "manager");
this.pathLengthMath = requireNonNull(pathLengthMath, "pathLengthMath");
scaleX = manager.getModel().getLayoutModel().getPropertyScaleX()
.getValueByUnit(LengthProperty.Unit.MM);
scaleY = manager.getModel().getLayoutModel().getPropertyScaleY()
.getValueByUnit(LengthProperty.Unit.MM);
}
@Override
public double applyAsDouble(
@Nonnull
PathModel path
) {
requireNonNull(path, "path");
String[] cps = path.getPropertyPathControlPoints().getText().split(";");
checkArgument(
cps.length == 5,
String.format("Path '%s' does not have exactly five control points.", path.getName())
);
PointModel start = (PointModel) path.getStartComponent();
PointModel end = (PointModel) path.getEndComponent();
return calculateBezierCurveLength(
new Coordinate(
start.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
start.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
),
new Coordinate(
Double.parseDouble(cps[0].split(",")[0]) * scaleX,
Double.parseDouble(cps[0].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
Double.parseDouble(cps[1].split(",")[0]) * scaleX,
Double.parseDouble(cps[1].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
Double.parseDouble(cps[2].split(",")[0]) * scaleX,
Double.parseDouble(cps[2].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
Double.parseDouble(cps[3].split(",")[0]) * scaleX,
Double.parseDouble(cps[3].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
Double.parseDouble(cps[4].split(",")[0]) * scaleX,
Double.parseDouble(cps[4].split(",")[1]) * scaleY * (-1)
),
new Coordinate(
end.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
end.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
)
);
}
private double calculateBezierCurveLength(
Coordinate start, Coordinate cp0, Coordinate cp1,
Coordinate cp2, Coordinate cp3, Coordinate cp4,
Coordinate end
) {
return pathLengthMath.approximateCubicBezierCurveLength(start, cp0, cp1, cp2, 1000)
+ pathLengthMath.approximateCubicBezierCurveLength(cp2, cp3, cp4, end, 1000);
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import java.util.Map;
import org.opentcs.guing.base.model.elements.PathModel;
/**
* A composite of various {@link PathLengthFunction}s for different {@link PathModel.Type}s which
* falls back to {@link EuclideanDistance}.
*/
public class CompositePathLengthFunction
implements
PathLengthFunction {
private final Map<PathModel.Type, PathLengthFunction> pathLengthFunctions;
private final EuclideanDistance euclideanDistance;
@Inject
public CompositePathLengthFunction(
@Nonnull
Map<PathModel.Type, PathLengthFunction> pathLengthFunctions,
@Nonnull
EuclideanDistance euclideanDistance
) {
this.pathLengthFunctions = requireNonNull(pathLengthFunctions, "pathLengthFunctions");
this.euclideanDistance = requireNonNull(euclideanDistance, "euclideanDistance");
}
@Override
public double applyAsDouble(PathModel path) {
return pathLengthFunctions
.getOrDefault((PathModel.Type) path.getPropertyPathConnType().getValue(), euclideanDistance)
.applyAsDouble(path);
}
}

View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
/**
* A generic 2-tuple of double values, usable for 2D coordinates and vectors, for instance.
*/
public class Coordinate {
/**
* The X coordinate.
*/
private final double x;
/**
* The Y coordinate.
*/
private final double y;
/**
* Creates a new instance.
*
* @param x The X coordinate.
* @param y The Y coordinate.
*/
public Coordinate(double x, double y) {
this.x = x;
this.y = y;
}
/**
* Returns the x coordinate.
*
* @return x
*/
public double getX() {
return x;
}
/**
* Returns the y coordinate.
*
* @return y
*/
public double getY() {
return y;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Coordinate)) {
return false;
}
Coordinate other = (Coordinate) obj;
if (this.x != other.x) {
return false;
}
if (this.y != other.y) {
return false;
}
return true;
}
@Override
public int hashCode() {
return (int) (x * y);
}
@Override
public String toString() {
return "Coordinate{" + "x=" + x + ", y=" + y + '}';
}
}

View File

@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import org.opentcs.guing.base.components.properties.type.LengthProperty;
import org.opentcs.guing.base.model.elements.PathModel;
import org.opentcs.guing.base.model.elements.PointModel;
/**
* Calculates the length of a path as the Euclidean distance between the start and end point.
*/
public class EuclideanDistance
implements
PathLengthFunction {
private final PathLengthMath pathLengthMath;
/**
* Creates a new instance.
*
* @param pathLengthMath Provides a method for the euclidean distance.
*/
@Inject
public EuclideanDistance(
@Nonnull
PathLengthMath pathLengthMath
) {
this.pathLengthMath = requireNonNull(pathLengthMath, "pathLengthMath");
}
@Override
public double applyAsDouble(
@Nonnull
PathModel path
) {
requireNonNull(path, "path");
PointModel start = (PointModel) path.getStartComponent();
PointModel end = (PointModel) path.getEndComponent();
Coordinate x = new Coordinate(
start.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
start.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
);
Coordinate y = new Coordinate(
end.getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM),
end.getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM)
);
return pathLengthMath.euclideanDistance(x, y);
}
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
import java.util.function.ToDoubleFunction;
import org.opentcs.guing.base.model.elements.PathModel;
/**
* A function that computes the length of a path.
*/
public interface PathLengthFunction
extends
ToDoubleFunction<PathModel> {
}

View File

@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.modeleditor.math.path;
/**
* Provides the euclidean distance and methods to evaluate the position of a point of a Bezier curve
* with n control points at a given parameter value.
*/
public class PathLengthMath {
public PathLengthMath() {
}
/**
* Approximates the length of a cubic Bezier curve (described via its control points) by
* discretization of the curve.
*
* @param cp0 The control point with index 0 (i.e. the start point of the Bezier curve).
* @param cp1 The control point with index 1.
* @param cp2 The control point with index 2.
* @param cp3 The control point with index 3 (i.e. the end point of the Bezier curve).
* @param granularity The granularity of the discretized curve. A higher granularity results in a
* more precise approximation.
*
* @return The approximated length of the Bezier curve.
*/
public double approximateCubicBezierCurveLength(
Coordinate cp0,
Coordinate cp1,
Coordinate cp2,
Coordinate cp3,
double granularity
) {
double length = 0.0;
for (int i = 0; i < granularity; i++) {
length += euclideanDistance(
evaluatePointOnCubicBezierCurve((double) i / granularity, cp0, cp1, cp2, cp3),
evaluatePointOnCubicBezierCurve((double) (i + 1) / granularity, cp0, cp1, cp2, cp3)
);
}
return length;
}
/**
* Calculates a point's position on a cubic Bezier curve (described via its control points) using
* Bernstein basis polynomials.
*
* @param t The parameter (with a value between 0 and 1) that defines the position of a point
* along the Bezier curve. A value of 0 corresponds to the position of the first control point and
* a value of 1 to the position of the last control point.
* @param cp0 The control point with index 0 (i.e. the start point of the Bezier curve).
* @param cp1 The control point with index 1.
* @param cp2 The control point with index 2.
* @param cp3 The control point with index 3 (i.e. the end point of the Bezier curve).
* @return A point's position on a cubic Bezier curve.
*/
public Coordinate evaluatePointOnCubicBezierCurve(
double t,
Coordinate cp0,
Coordinate cp1,
Coordinate cp2,
Coordinate cp3
) {
return new Coordinate(
bernsteinPolynomialOfDegree3(t, cp0.getX(), cp1.getX(), cp2.getX(), cp3.getX()),
bernsteinPolynomialOfDegree3(t, cp0.getY(), cp1.getY(), cp2.getY(), cp3.getY())
);
}
/**
* Calculates the distance between two given points using the euclidean algorithm.
*
* @param p1 The first point.
* @param p2 The second point.
* @return The distance between these two points.
*/
public double euclideanDistance(Coordinate p1, Coordinate p2) {
return Math.sqrt(Math.pow(p1.getX() - p2.getX(), 2) + Math.pow(p1.getY() - p2.getY(), 2));
}
private double bernsteinPolynomialOfDegree3(
double t,
double cp0,
double cp1,
double cp2,
double cp3
) {
double u = 1.0 - t;
return Math.pow(u, 3) * cp0
+ 3 * Math.pow(u, 2) * t * cp1
+ 3 * u * Math.pow(t, 2) * cp2
+ Math.pow(t, 3) * cp3;
}
}

Some files were not shown because too many files have changed in this diff Show More