This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
import bibliothek.gui.dock.common.intern.DefaultCommonDockable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import org.opentcs.guing.common.components.dockable.DockableTitleComparator;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingViewScrollPane;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
|
||||
/**
|
||||
* Manages the mapping of dockables to drawing views, transport order views and
|
||||
* order sequence views.
|
||||
*/
|
||||
public abstract class AbstractViewManager
|
||||
implements
|
||||
ViewManager {
|
||||
|
||||
/**
|
||||
* Where we register event listeners.
|
||||
*/
|
||||
private final EventSource eventSource;
|
||||
/**
|
||||
* Map for Dockable -> DrawingView + Rulers.
|
||||
*/
|
||||
private final Map<DefaultSingleCDockable, DrawingViewScrollPane> drawingViewMap;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param eventSource Where this instance registers event listeners.
|
||||
*/
|
||||
public AbstractViewManager(EventSource eventSource) {
|
||||
this.eventSource = requireNonNull(eventSource, "eventSource");
|
||||
drawingViewMap = new TreeMap<>(new DockableTitleComparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<DefaultSingleCDockable, DrawingViewScrollPane> getDrawingViewMap() {
|
||||
return drawingViewMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title texts of all drawing views.
|
||||
*
|
||||
* @return List of strings containing the names.
|
||||
*/
|
||||
@Override
|
||||
public List<String> getDrawingViewNames() {
|
||||
return drawingViewMap.keySet().stream()
|
||||
.map(dock -> dock.getTitleText())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forgets the given dockable.
|
||||
*
|
||||
* @param dockable The dockable.
|
||||
*/
|
||||
@Override
|
||||
public void removeDockable(DefaultSingleCDockable dockable) {
|
||||
DrawingViewScrollPane scrollPane = drawingViewMap.remove(dockable);
|
||||
if (scrollPane != null) {
|
||||
eventSource.unsubscribe(scrollPane.getDrawingView());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all components.
|
||||
*/
|
||||
public void reset() {
|
||||
drawingViewMap.clear();
|
||||
}
|
||||
|
||||
public int getNextDrawingViewIndex() {
|
||||
return nextAvailableIndex(drawingViewMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a scroll pane with a key dockable into the drawing view map.
|
||||
* The scroll pane has to contain the drawing view and both rulers.
|
||||
*
|
||||
* @param dockable The dockable the scrollPane is wrapped into. Used as the key.
|
||||
* @param scrollPane The scroll pane containing the drawing view and rulers.
|
||||
*/
|
||||
public void addDrawingView(
|
||||
DefaultSingleCDockable dockable,
|
||||
DrawingViewScrollPane scrollPane
|
||||
) {
|
||||
requireNonNull(dockable, "dockable");
|
||||
requireNonNull(scrollPane, "scrollPane");
|
||||
|
||||
eventSource.subscribe(scrollPane.getDrawingView());
|
||||
drawingViewMap.put(dockable, scrollPane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates which dockable should be the front dockable.
|
||||
*
|
||||
* @return The dockable that should be the front dockable. <code>null</code>
|
||||
* if no dockables exist.
|
||||
*/
|
||||
public DefaultCommonDockable evaluateFrontDockable() {
|
||||
if (!drawingViewMap.isEmpty()) {
|
||||
return drawingViewMap.keySet().iterator().next().intern();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available index of a set of dockables.
|
||||
* E.g. if "Dock 0" and "Dock 2" are being used, 1 would be returned.
|
||||
*
|
||||
* @param setToIterate The set to iterate.
|
||||
* @return The next available index.
|
||||
*/
|
||||
protected int nextAvailableIndex(Set<DefaultSingleCDockable> setToIterate) {
|
||||
// Name
|
||||
Pattern p = Pattern.compile("\\d");
|
||||
Matcher m;
|
||||
int biggestIndex = 0;
|
||||
|
||||
for (DefaultSingleCDockable dock : setToIterate) {
|
||||
m = p.matcher(dock.getTitleText());
|
||||
|
||||
if (m.find()) {
|
||||
int index = Integer.parseInt(m.group(0));
|
||||
|
||||
if (index > biggestIndex) {
|
||||
biggestIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return biggestIndex + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* Keeps and provides information about the current state of the application as
|
||||
* a whole.
|
||||
*/
|
||||
public class ApplicationState {
|
||||
|
||||
/**
|
||||
* The application's current mode of operation.
|
||||
*/
|
||||
private OperationMode operationMode = OperationMode.UNDEFINED;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
@Inject
|
||||
public ApplicationState() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application's current mode of operation.
|
||||
*
|
||||
* @return The application's current mode of operation.
|
||||
*/
|
||||
public OperationMode getOperationMode() {
|
||||
return operationMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the application is currently in the given mode of operation.
|
||||
*
|
||||
* @param mode The mode to check for.
|
||||
* @return <code>true</code> if, and only if, the application is currently in
|
||||
* the given mode.
|
||||
*/
|
||||
public boolean hasOperationMode(OperationMode mode) {
|
||||
return operationMode == mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the application's current mode of operation.
|
||||
*
|
||||
* @param operationMode The application's new mode of operation.
|
||||
*/
|
||||
public void setOperationMode(OperationMode operationMode) {
|
||||
this.operationMode = requireNonNull(operationMode, "operationMode");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import java.util.List;
|
||||
import org.opentcs.guing.common.components.tree.elements.UserObject;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface ComponentsManager {
|
||||
|
||||
/**
|
||||
* Adds the given model components to the data model. (e.g. when pasting)
|
||||
*
|
||||
* @param userObjects The user objects to restore.
|
||||
* @return The restored user objects.
|
||||
*/
|
||||
List<UserObject> restoreModelComponents(List<UserObject> userObjects);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import org.opentcs.components.plantoverview.PlantModelExporter;
|
||||
import org.opentcs.components.plantoverview.PlantModelImporter;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
|
||||
/**
|
||||
* Provides some central services for various parts of the plant overview application.
|
||||
*/
|
||||
public interface GuiManager {
|
||||
|
||||
/**
|
||||
* Called when an object was selected in the tree view.
|
||||
*
|
||||
* @param modelComponent The selected object.
|
||||
*/
|
||||
void selectModelComponent(ModelComponent modelComponent);
|
||||
|
||||
/**
|
||||
* Called when an additional object was selected in the tree view.
|
||||
*
|
||||
* @param modelComponent The selected object.
|
||||
*/
|
||||
void addSelectedModelComponent(ModelComponent modelComponent);
|
||||
|
||||
/**
|
||||
* Called when an object was removed from the tree view (by user interaction).
|
||||
*
|
||||
* @param fDataObject The object to be removed.
|
||||
* @return Indicates whether the object was really removed from the model.
|
||||
*/
|
||||
boolean treeComponentRemoved(ModelComponent fDataObject);
|
||||
|
||||
/**
|
||||
* Notifies about a figure object being selected.
|
||||
*
|
||||
* @param modelComponent The selected object.
|
||||
*/
|
||||
void figureSelected(ModelComponent modelComponent);
|
||||
|
||||
/**
|
||||
* Creates a new, empty model and initializes it.
|
||||
*/
|
||||
void createEmptyModel();
|
||||
|
||||
/**
|
||||
* Loads a plant model.
|
||||
*/
|
||||
void loadModel();
|
||||
|
||||
/**
|
||||
* Imports a plant model using the given importer.
|
||||
*
|
||||
* @param importer The importer.
|
||||
*/
|
||||
void importModel(PlantModelImporter importer);
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
boolean saveModel();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean saveModelAs();
|
||||
|
||||
/**
|
||||
* Exports a plant model using the given exporter.
|
||||
*
|
||||
* @param exporter The exporter.
|
||||
*/
|
||||
void exportModel(PlantModelExporter exporter);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.opentcs.guing.base.model.elements.BlockModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationTypeModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
|
||||
/**
|
||||
* Provides services concerning model editing.
|
||||
*/
|
||||
public interface GuiManagerModeling
|
||||
extends
|
||||
GuiManager {
|
||||
|
||||
/**
|
||||
* Creates a new vehicle model.
|
||||
*
|
||||
* @return The created vehicle model.
|
||||
*/
|
||||
VehicleModel createVehicleModel();
|
||||
|
||||
/**
|
||||
* Creates a new location type model.
|
||||
*
|
||||
* @return The created location type model.
|
||||
*/
|
||||
LocationTypeModel createLocationTypeModel();
|
||||
|
||||
/**
|
||||
* Creates a new block model.
|
||||
*
|
||||
* @return The created block model.
|
||||
*/
|
||||
BlockModel createBlockModel();
|
||||
|
||||
/**
|
||||
* Removes a block model.
|
||||
*
|
||||
* <p>
|
||||
* This method is primarily provided for use in plugin panels.
|
||||
* </p>
|
||||
*
|
||||
* @param blockModel The block model to be removed.
|
||||
*/
|
||||
void removeBlockModel(
|
||||
@Nonnull
|
||||
BlockModel blockModel
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import org.opentcs.access.Kernel;
|
||||
|
||||
/**
|
||||
* Listener interface implemented by classes interested in changes of a
|
||||
* connected kernel's state.
|
||||
*/
|
||||
public interface KernelStateHandler {
|
||||
|
||||
/**
|
||||
* Informs the handler that the kernel is now in the given state.
|
||||
*
|
||||
* @param state The new state.
|
||||
*/
|
||||
void enterKernelState(Kernel.State state);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
|
||||
/**
|
||||
* Progress status for the process of loading a model.
|
||||
*/
|
||||
public enum ModelRestorationProgressStatus
|
||||
implements
|
||||
ProgressStatus {
|
||||
|
||||
/**
|
||||
* Cleanup phase.
|
||||
*/
|
||||
CLEANUP(0, "modelRestorationProgressStatus.description.cleanup"),
|
||||
/**
|
||||
* Starting to load model.
|
||||
*/
|
||||
START_LOADING_MODEL(10, "modelRestorationProgressStatus.description.startLoadingModel"),
|
||||
/**
|
||||
* Loading points.
|
||||
*/
|
||||
LOADING_POINTS(20, "modelRestorationProgressStatus.description.startLoadingPoints"),
|
||||
/**
|
||||
* Loading paths.
|
||||
*/
|
||||
LOADING_PATHS(30, "modelRestorationProgressStatus.description.startLoadingPaths"),
|
||||
/**
|
||||
* Loading locations.
|
||||
*/
|
||||
LOADING_LOCATIONS(40, "modelRestorationProgressStatus.description.startLoadingLocations"),
|
||||
/**
|
||||
* Loading vehicles.
|
||||
*/
|
||||
LOADING_VEHICLES(50, "modelRestorationProgressStatus.description.startLoadingVehicles"),
|
||||
/**
|
||||
* Loading blocks.
|
||||
*/
|
||||
LOADING_BLOCKS(60, "modelRestorationProgressStatus.description.startLoadingBlocks"),
|
||||
/**
|
||||
* Setting up model view.
|
||||
*/
|
||||
SET_UP_MODEL_VIEW(70, "modelRestorationProgressStatus.description.setUpModelView"),
|
||||
/**
|
||||
* Setting up directory tree.
|
||||
*/
|
||||
SET_UP_DIRECTORY_TREE(80, "modelRestorationProgressStatus.description.setUpDirectoryTree"),
|
||||
/**
|
||||
* Setting up working area.
|
||||
*/
|
||||
SET_UP_WORKING_AREA(90, "modelRestorationProgressStatus.description.setUpWorkingArea");
|
||||
|
||||
private final int percentage;
|
||||
|
||||
private final String description;
|
||||
|
||||
ModelRestorationProgressStatus(int percentage, String description) {
|
||||
this.percentage = percentage;
|
||||
this.description = ResourceBundle.getBundle(I18nPlantOverview.MISC_PATH).getString(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.opentcs.access.Kernel;
|
||||
|
||||
/**
|
||||
* Defines the plant overview's potential modes of operation.
|
||||
*/
|
||||
public enum OperationMode {
|
||||
|
||||
/**
|
||||
* For cases in which the mode of operation has not been defined, yet.
|
||||
*/
|
||||
UNDEFINED,
|
||||
/**
|
||||
* Used when modelling a driving course.
|
||||
*/
|
||||
MODELLING,
|
||||
/**
|
||||
* Used when operating a plant/system.
|
||||
*/
|
||||
OPERATING;
|
||||
|
||||
/**
|
||||
* Returns the equivalent operation mode to the given kernel state.
|
||||
*
|
||||
* @param state The kernel state.
|
||||
* @return The equivalent operation mode to the given kernel state.
|
||||
*/
|
||||
public static OperationMode equivalent(Kernel.State state) {
|
||||
if (Objects.equals(state, Kernel.State.MODELLING)) {
|
||||
return MODELLING;
|
||||
}
|
||||
else if (Objects.equals(state, Kernel.State.OPERATING)) {
|
||||
return OPERATING;
|
||||
}
|
||||
else {
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
public static Kernel.State equivalent(OperationMode mode) {
|
||||
if (Objects.equals(mode, MODELLING)) {
|
||||
return Kernel.State.MODELLING;
|
||||
}
|
||||
else if (Objects.equals(mode, OPERATING)) {
|
||||
return Kernel.State.OPERATING;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import org.opentcs.components.plantoverview.PluggablePanelFactory;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface PluginPanelManager {
|
||||
|
||||
/**
|
||||
* Shows or hides the specific {@code PanelFactory}.
|
||||
*
|
||||
* @param factory The factory resp. panel that shall be shown / hidden.
|
||||
* @param visible True to set it visible, false otherwise.
|
||||
*/
|
||||
void showPluginPanel(PluggablePanelFactory factory, boolean visible);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
/**
|
||||
* Makes progress information available in some way.
|
||||
*/
|
||||
public interface ProgressIndicator {
|
||||
|
||||
/**
|
||||
* Initializes the progress indicator, possibly resetting the percentage and
|
||||
* message.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Sets/publishes the current progress status.
|
||||
*
|
||||
* @param progressStatus The progress status.
|
||||
*/
|
||||
void setProgress(ProgressStatus progressStatus);
|
||||
|
||||
/**
|
||||
* Terminates the progress indicator, indicating that no further progress is
|
||||
* going to be published.
|
||||
* The progress indicator may be reused after a call to {@code initialize()}.
|
||||
*/
|
||||
void terminate();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
/**
|
||||
* A state of progress, to be used with {@link ProgressIndicator}.
|
||||
*/
|
||||
public interface ProgressStatus {
|
||||
|
||||
/**
|
||||
* Returns a percentage value.
|
||||
*
|
||||
* @return A percentage value, greater than or equal to 0 and less than or equal to 100.
|
||||
*/
|
||||
int getPercentage();
|
||||
|
||||
/**
|
||||
* Returns a (possibly localized) description of the current status to be displayed.
|
||||
*
|
||||
* @return A description of the current status to be displayed.
|
||||
*/
|
||||
String getStatusDescription();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="splashFrame.title.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="iconImages" type="java.util.List" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="Icons.getOpenTCSIcons()" type="code"/>
|
||||
</Property>
|
||||
<Property name="undecorated" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSize" 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,-70,0,0,1,60"/>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="0"/>
|
||||
<SyntheticProperty name="generateSize" type="boolean" value="true"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="true"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_formBundle" type="java.lang.String" value="i18n/org/opentcs/plantoverview/system"/>
|
||||
<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"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="panel">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="labelImage">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/opentcs/guing/res/symbols/openTCS/splash.320x152.gif"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="23" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="labelMessage">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Arial" size="12" style="1"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="splashFrame.label_message.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="15" weightX="0.5" weightY="0.5"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JProgressBar" name="progressBar">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="99" red="99" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="15" weightX="0.0" weightY="0.5"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.opentcs.util.gui.Icons;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A frame for displaying the progress of longer-running processes.
|
||||
*/
|
||||
public class SplashFrame
|
||||
extends
|
||||
JFrame
|
||||
implements
|
||||
ProgressIndicator {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SplashFrame.class);
|
||||
|
||||
/**
|
||||
* Creates new form SplashFrame
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public SplashFrame() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// Ensure this method is called on the event dispatcher thread.
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initialize();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InterruptedException | InvocationTargetException exc) {
|
||||
LOG.warn("Unexpected exception", exc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
setVisible(true);
|
||||
setProgress(0, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(ProgressStatus progressStatus) {
|
||||
requireNonNull(progressStatus, "progressStatus");
|
||||
|
||||
setProgress(progressStatus.getPercentage(), progressStatus.getStatusDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
// Ensure this method is called on the event dispatcher thread.
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
terminate();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InterruptedException | InvocationTargetException exc) {
|
||||
LOG.warn("Unexpected exception", exc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void setProgress(final int percent, final String message) {
|
||||
// Ensure this method is called on the event dispatcher thread.
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setProgress(percent, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InterruptedException | InvocationTargetException exc) {
|
||||
LOG.warn("Unexpected exception", exc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
labelMessage.setText(message);
|
||||
progressBar.setValue(percent);
|
||||
update(getGraphics());
|
||||
toFront();
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
panel = new javax.swing.JPanel();
|
||||
labelImage = new javax.swing.JLabel();
|
||||
labelMessage = new javax.swing.JLabel();
|
||||
progressBar = new javax.swing.JProgressBar();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/system"); // NOI18N
|
||||
setTitle(bundle.getString("splashFrame.title.text")); // NOI18N
|
||||
setBackground(new java.awt.Color(255, 255, 255));
|
||||
setIconImages(Icons.getOpenTCSIcons());
|
||||
setUndecorated(true);
|
||||
getContentPane().setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
panel.setBackground(new java.awt.Color(255, 255, 255));
|
||||
panel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
labelImage.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/opentcs/guing/res/symbols/openTCS/splash.320x152.gif"))); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
|
||||
panel.add(labelImage, gridBagConstraints);
|
||||
|
||||
labelMessage.setBackground(new java.awt.Color(255, 255, 255));
|
||||
labelMessage.setFont(new java.awt.Font("Arial", 1, 12)); // NOI18N
|
||||
labelMessage.setText(bundle.getString("splashFrame.label_message.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
|
||||
gridBagConstraints.weightx = 0.5;
|
||||
gridBagConstraints.weighty = 0.5;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
|
||||
panel.add(labelMessage, gridBagConstraints);
|
||||
|
||||
progressBar.setBackground(new java.awt.Color(255, 255, 255));
|
||||
progressBar.setForeground(new java.awt.Color(153, 153, 255));
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
|
||||
gridBagConstraints.weighty = 0.5;
|
||||
panel.add(progressBar, gridBagConstraints);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
getContentPane().add(panel, gridBagConstraints);
|
||||
|
||||
setSize(new java.awt.Dimension(316, 186));
|
||||
setLocationRelativeTo(null);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel labelImage;
|
||||
private javax.swing.JLabel labelMessage;
|
||||
private javax.swing.JPanel panel;
|
||||
private javax.swing.JProgressBar progressBar;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
|
||||
/**
|
||||
* Progress status for the process of starting the application.
|
||||
*/
|
||||
public enum StartupProgressStatus
|
||||
implements
|
||||
ProgressStatus {
|
||||
|
||||
/**
|
||||
* Starting the plant overview.
|
||||
*/
|
||||
START_PLANT_OVERVIEW(0, "startupProgressStatus.description.startPlantOverview"),
|
||||
/**
|
||||
* Showing the plant overview.
|
||||
*/
|
||||
SHOW_PLANT_OVERVIEW(5, "startupProgressStatus.description.showPlantOverview"),
|
||||
/**
|
||||
* Application initialized.
|
||||
*/
|
||||
INITIALIZED(10, "startupProgressStatus.description.initialized"),
|
||||
/**
|
||||
* Initializing model.
|
||||
*/
|
||||
INITIALIZE_MODEL(15, "startupProgressStatus.description.initializeModel");
|
||||
|
||||
private final int percentage;
|
||||
|
||||
private final String description;
|
||||
|
||||
StartupProgressStatus(int percentage, String description) {
|
||||
this.percentage = percentage;
|
||||
this.description = ResourceBundle.getBundle(I18nPlantOverview.MISC_PATH).getString(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A panel at the bottom of the view, showing the mouse position and status.
|
||||
*/
|
||||
public class StatusPanel
|
||||
extends
|
||||
JPanel {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StatusPanel.class);
|
||||
/**
|
||||
* A text field for status messages.
|
||||
*/
|
||||
private final JTextField textFieldStatus = new JTextField();
|
||||
/**
|
||||
* A text field for cursor positions/coordinates.
|
||||
*/
|
||||
private final JTextField textFieldPosition = new JTextField();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public StatusPanel() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
textFieldStatus.setText(null);
|
||||
textFieldPosition.setText(null);
|
||||
|
||||
removeAll();
|
||||
setLayout(new GridBagLayout());
|
||||
|
||||
textFieldPosition.setEditable(false);
|
||||
GridBagConstraints gridBagConstraints = new GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = GridBagConstraints.BOTH;
|
||||
add(textFieldPosition, gridBagConstraints);
|
||||
|
||||
textFieldStatus.setEditable(false);
|
||||
gridBagConstraints = new GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.weightx = 0.8;
|
||||
add(textFieldStatus, gridBagConstraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the status textfield, removing any logged messages.
|
||||
*/
|
||||
public void clear() {
|
||||
textFieldStatus.setForeground(Color.black);
|
||||
textFieldStatus.setText("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Text display in the status bar (at the bottom).
|
||||
*
|
||||
* @param level Log-Level, determines the text color.
|
||||
* @param text Text to display.
|
||||
*/
|
||||
public void setLogMessage(Level level, String text) {
|
||||
if (level == Level.SEVERE) {
|
||||
showOptionPane(text);
|
||||
textFieldStatus.setForeground(Color.magenta);
|
||||
LOG.error(text);
|
||||
}
|
||||
else if (level == Level.WARNING) {
|
||||
showOptionPane(text);
|
||||
textFieldStatus.setForeground(Color.red);
|
||||
LOG.warn(text);
|
||||
}
|
||||
else if (level == Level.INFO) {
|
||||
textFieldStatus.setForeground(Color.blue);
|
||||
LOG.info(text);
|
||||
}
|
||||
else {
|
||||
textFieldStatus.setForeground(Color.black);
|
||||
LOG.info(text);
|
||||
}
|
||||
|
||||
textFieldStatus.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given text to the position text field.
|
||||
*
|
||||
* @param text The text to set.
|
||||
*/
|
||||
public void setPositionText(String text) {
|
||||
requireNonNull(text, "text");
|
||||
|
||||
// Add a space in front of the position text to avoid that a part of the lefthand side gets cut
|
||||
// off with some graphical environments (observed on Windows 10).
|
||||
textFieldPosition.setText(" " + text);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private void showOptionPane(String text) {
|
||||
JOptionPane.showMessageDialog(this.getParent(), text, "", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application;
|
||||
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingViewScrollPane;
|
||||
|
||||
/**
|
||||
* Manages the mapping of dockables to drawing views, transport order views and
|
||||
* order sequence views.
|
||||
*/
|
||||
public interface ViewManager {
|
||||
|
||||
Map<DefaultSingleCDockable, DrawingViewScrollPane> getDrawingViewMap();
|
||||
|
||||
/**
|
||||
* Returns the title texts of all drawing views.
|
||||
*
|
||||
* @return List of strings containing the names.
|
||||
*/
|
||||
List<String> getDrawingViewNames();
|
||||
|
||||
/**
|
||||
* Forgets the given dockable.
|
||||
*
|
||||
* @param dockable The dockable.
|
||||
*/
|
||||
void removeDockable(DefaultSingleCDockable dockable);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.action;
|
||||
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.Objects;
|
||||
import org.jhotdraw.draw.DrawingEditor;
|
||||
import org.jhotdraw.draw.tool.Tool;
|
||||
|
||||
/**
|
||||
* A listener if a tool was (de)selected.
|
||||
*/
|
||||
public final class ToolButtonListener
|
||||
implements
|
||||
ItemListener {
|
||||
|
||||
private final Tool tool;
|
||||
private final DrawingEditor editor;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param tool The tool
|
||||
* @param editor The drawing editor
|
||||
*/
|
||||
public ToolButtonListener(Tool tool, DrawingEditor editor) {
|
||||
this.tool = Objects.requireNonNull(tool, "tool is null");
|
||||
this.editor = Objects.requireNonNull(editor, "editor is null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent evt) {
|
||||
if (evt.getStateChange() == ItemEvent.SELECTED) {
|
||||
editor.setTool(tool);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.action.file;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.guing.common.util.I18nPlantOverview.MENU_PATH;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Objects;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.opentcs.customizations.plantoverview.ApplicationFrame;
|
||||
import org.opentcs.data.ObjectPropConstants;
|
||||
import org.opentcs.guing.common.persistence.ModelManager;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
|
||||
|
||||
/**
|
||||
* Shows a message window with some of the currently loaded model's properties.
|
||||
*/
|
||||
public class ModelPropertiesAction
|
||||
extends
|
||||
AbstractAction {
|
||||
|
||||
/**
|
||||
* This action's ID.
|
||||
*/
|
||||
public static final String ID = "file.modelProperties";
|
||||
|
||||
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
|
||||
/**
|
||||
* The parent component for dialogs shown by this action.
|
||||
*/
|
||||
private final Component dialogParent;
|
||||
/**
|
||||
* Provides the current system model.
|
||||
*/
|
||||
private final ModelManager modelManager;
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public ModelPropertiesAction(
|
||||
@ApplicationFrame
|
||||
Component dialogParent,
|
||||
ModelManager modelManager
|
||||
) {
|
||||
this.dialogParent = requireNonNull(dialogParent, "dialogParent");
|
||||
this.modelManager = requireNonNull(modelManager, "modelManager");
|
||||
|
||||
putValue(NAME, BUNDLE.getString("modelPropertiesAction.name"));
|
||||
putValue(SHORT_DESCRIPTION, BUNDLE.getString("modelPropertiesAction.shortDescription"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ResourceBundleUtil bundle
|
||||
= ResourceBundleUtil.getBundle(I18nPlantOverview.MODELPROPERTIES_PATH);
|
||||
|
||||
JOptionPane.showMessageDialog(
|
||||
dialogParent,
|
||||
"<html><p><b>" + modelManager.getModel().getName() + "</b><br>"
|
||||
+ bundle.getString("modelPropertiesAction.optionPane_properties.message.numberOfPoints")
|
||||
+ numberOfPoints()
|
||||
+ "<br>"
|
||||
+ bundle.getString("modelPropertiesAction.optionPane_properties.message.numberOfPaths")
|
||||
+ numberOfPaths()
|
||||
+ "<br>"
|
||||
+ bundle.getString(
|
||||
"modelPropertiesAction.optionPane_properties.message.numberOfLocations"
|
||||
)
|
||||
+ numberOfLocations()
|
||||
+ "<br>"
|
||||
+ bundle.getString(
|
||||
"modelPropertiesAction.optionPane_properties.message.numberOfLocationTypes"
|
||||
)
|
||||
+ numberOfLocationTypes()
|
||||
+ "<br>"
|
||||
+ bundle.getString("modelPropertiesAction.optionPane_properties.message.numberOfBlocks")
|
||||
+ numberOfBlocks()
|
||||
+ "<br>"
|
||||
+ bundle.getString(
|
||||
"modelPropertiesAction.optionPane_properties.message.numberOfVehicles"
|
||||
)
|
||||
+ numberOfVehicles()
|
||||
+ "<br>"
|
||||
+ "<br>"
|
||||
+ bundle.getString("modelPropertiesAction.optionPane_properties.message.lastModified")
|
||||
+ lastModified()
|
||||
+ "</p></html>"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private String lastModified() {
|
||||
return modelManager.getModel().getPropertyMiscellaneous().getItems().stream()
|
||||
.filter(kvp -> Objects.equals(kvp.getKey(), ObjectPropConstants.MODEL_FILE_LAST_MODIFIED))
|
||||
.findAny()
|
||||
.map(kvp -> kvp.getValue())
|
||||
.orElse("?");
|
||||
}
|
||||
|
||||
private int numberOfPoints() {
|
||||
return modelManager.getModel().getPointModels().size();
|
||||
}
|
||||
|
||||
private int numberOfPaths() {
|
||||
return modelManager.getModel().getPathModels().size();
|
||||
}
|
||||
|
||||
private int numberOfLocations() {
|
||||
return modelManager.getModel().getLocationModels().size();
|
||||
}
|
||||
|
||||
private int numberOfLocationTypes() {
|
||||
return modelManager.getModel().getLocationTypeModels().size();
|
||||
}
|
||||
|
||||
private int numberOfBlocks() {
|
||||
return modelManager.getModel().getBlockModels().size();
|
||||
}
|
||||
|
||||
private int numberOfVehicles() {
|
||||
return modelManager.getModel().getVehicleModels().size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.action.file;
|
||||
|
||||
import static org.opentcs.guing.common.util.I18nPlantOverview.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 SaveModelAction
|
||||
extends
|
||||
AbstractAction {
|
||||
|
||||
/**
|
||||
* This action's ID.
|
||||
*/
|
||||
public static final String ID = "file.saveModel";
|
||||
|
||||
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 SaveModelAction(GuiManager view) {
|
||||
this.view = view;
|
||||
|
||||
putValue(NAME, BUNDLE.getString("saveModelAction.name"));
|
||||
putValue(SHORT_DESCRIPTION, BUNDLE.getString("saveModelAction.shortDescription"));
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl S"));
|
||||
putValue(MNEMONIC_KEY, Integer.valueOf('S'));
|
||||
|
||||
ImageIcon icon = ImageDirectory.getImageIcon("/menu/document-save.png");
|
||||
putValue(SMALL_ICON, icon);
|
||||
putValue(LARGE_ICON_KEY, icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
view.saveModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.action.file;
|
||||
|
||||
import static org.opentcs.guing.common.util.I18nPlantOverview.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 SaveModelAsAction
|
||||
extends
|
||||
AbstractAction {
|
||||
|
||||
/**
|
||||
* This action's ID.
|
||||
*/
|
||||
public static final String ID = "file.saveModelAs";
|
||||
|
||||
private static final ResourceBundleUtil BUNDLE = ResourceBundleUtil.getBundle(MENU_PATH);
|
||||
/**
|
||||
* The manager this instance is working with.
|
||||
*/
|
||||
private final GuiManager guiManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param manager The gui manager
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public SaveModelAsAction(final GuiManager manager) {
|
||||
this.guiManager = manager;
|
||||
|
||||
putValue(NAME, BUNDLE.getString("saveModelAsAction.name"));
|
||||
putValue(SHORT_DESCRIPTION, BUNDLE.getString("saveModelAsAction.shortDescription"));
|
||||
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("shift ctrl S"));
|
||||
putValue(MNEMONIC_KEY, Integer.valueOf('A'));
|
||||
|
||||
ImageIcon icon = ImageDirectory.getImageIcon("/menu/document-save-as.png");
|
||||
putValue(SMALL_ICON, icon);
|
||||
putValue(LARGE_ICON_KEY, icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
guiManager.saveModelAs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.action.view;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import org.opentcs.components.plantoverview.PluggablePanelFactory;
|
||||
import org.opentcs.guing.common.application.PluginPanelManager;
|
||||
|
||||
/**
|
||||
* An action to add a plugin panel.
|
||||
*/
|
||||
public class AddPluginPanelAction
|
||||
extends
|
||||
AbstractAction {
|
||||
|
||||
/**
|
||||
* This action's ID.
|
||||
*/
|
||||
public static final String ID = "view.addPluginPanel";
|
||||
private final PluggablePanelFactory factory;
|
||||
private final PluginPanelManager pluginPanelManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param pluginPanelManager The openTCS view
|
||||
* @param factory The pluggable panel factory
|
||||
*/
|
||||
public AddPluginPanelAction(
|
||||
PluginPanelManager pluginPanelManager,
|
||||
PluggablePanelFactory factory
|
||||
) {
|
||||
this.pluginPanelManager = requireNonNull(pluginPanelManager, "pluginPanelManager");
|
||||
this.factory = requireNonNull(factory, "factory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JCheckBoxMenuItem item = (JCheckBoxMenuItem) e.getSource();
|
||||
pluginPanelManager.showPluginPanel(factory, item.isSelected());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.menus.menubar;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import org.opentcs.guing.common.components.dockable.AbstractDockingManager;
|
||||
|
||||
/**
|
||||
* Handles changes of a plugin panel's properties.
|
||||
*/
|
||||
class PluginPanelPropertyHandler
|
||||
implements
|
||||
PropertyChangeListener {
|
||||
|
||||
/**
|
||||
* The menu item corresponding to the plugin panel.
|
||||
*/
|
||||
private final JCheckBoxMenuItem utilMenuItem;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param utilMenuItem The menu item corresponding to the plugin panel.
|
||||
*/
|
||||
PluginPanelPropertyHandler(JCheckBoxMenuItem utilMenuItem) {
|
||||
this.utilMenuItem = Objects.requireNonNull(utilMenuItem, "utilMenuItem is null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(AbstractDockingManager.DOCKABLE_CLOSED)) {
|
||||
utilMenuItem.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.menus.menubar;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.opentcs.access.Kernel;
|
||||
import org.opentcs.components.plantoverview.PluggablePanelFactory;
|
||||
import org.opentcs.guing.common.application.OperationMode;
|
||||
import org.opentcs.guing.common.application.PluginPanelManager;
|
||||
import org.opentcs.guing.common.application.action.view.AddPluginPanelAction;
|
||||
import org.opentcs.guing.common.components.dockable.DockingManager;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
import org.opentcs.guing.common.util.PanelRegistry;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class ViewPluginPanelsMenu
|
||||
extends
|
||||
JMenu {
|
||||
|
||||
private static final ResourceBundleUtil BUNDLE
|
||||
= ResourceBundleUtil.getBundle(I18nPlantOverview.MENU_PATH);
|
||||
|
||||
/**
|
||||
* The plugin panel manager.
|
||||
*/
|
||||
private final PluginPanelManager pluginPanelManager;
|
||||
/**
|
||||
* Provides the registered plugin panel factories.
|
||||
*/
|
||||
private final PanelRegistry panelRegistry;
|
||||
/**
|
||||
* Manages docking frames.
|
||||
*/
|
||||
private final DockingManager dockingManager;
|
||||
|
||||
@Inject
|
||||
public ViewPluginPanelsMenu(
|
||||
PluginPanelManager pluginPanelManager,
|
||||
PanelRegistry panelRegistry,
|
||||
DockingManager dockingManager
|
||||
) {
|
||||
super(BUNDLE.getString("viewPluginPanelsMenu.text"));
|
||||
|
||||
this.pluginPanelManager = requireNonNull(pluginPanelManager, "pluginPanelManager");
|
||||
this.panelRegistry = requireNonNull(panelRegistry, "panelRegistry");
|
||||
this.dockingManager = requireNonNull(dockingManager, "dockingManager");
|
||||
}
|
||||
|
||||
public void setOperationMode(OperationMode mode) {
|
||||
requireNonNull(mode, "mode");
|
||||
|
||||
evaluatePluginPanels(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes/adds plugin panels depending on the <code>OperationMode</code>.
|
||||
*
|
||||
* @param operationMode The operation mode.
|
||||
*/
|
||||
private void evaluatePluginPanels(OperationMode operationMode) {
|
||||
Kernel.State kernelState = OperationMode.equivalent(operationMode);
|
||||
if (kernelState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeAll();
|
||||
|
||||
SortedSet<PluggablePanelFactory> factories = new TreeSet<>((factory1, factory2) -> {
|
||||
return factory1.getPanelDescription().compareTo(factory2.getPanelDescription());
|
||||
});
|
||||
factories.addAll(panelRegistry.getFactories());
|
||||
|
||||
for (final PluggablePanelFactory factory : factories) {
|
||||
if (factory.providesPanel(kernelState)) {
|
||||
String title = factory.getPanelDescription();
|
||||
final JCheckBoxMenuItem utilMenuItem = new JCheckBoxMenuItem();
|
||||
utilMenuItem.setAction(new AddPluginPanelAction(pluginPanelManager, factory));
|
||||
utilMenuItem.setText(title);
|
||||
dockingManager.addPropertyChangeListener(new PluginPanelPropertyHandler(utilMenuItem));
|
||||
add(utilMenuItem);
|
||||
}
|
||||
}
|
||||
// If the menu is empty, add a single disabled menu item to it that explains
|
||||
// to the user that no plugin panels are available.
|
||||
if (getMenuComponentCount() == 0) {
|
||||
JMenuItem dummyItem
|
||||
= new JMenuItem(BUNDLE.getString("viewPluginPanelsMenu.menuItem_none.text"));
|
||||
dummyItem.setEnabled(false);
|
||||
add(dummyItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.application.toolbar;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import org.jhotdraw.draw.tool.AbstractTool;
|
||||
|
||||
/**
|
||||
* The tool to drag the drawing.
|
||||
*/
|
||||
public class DragTool
|
||||
extends
|
||||
AbstractTool {
|
||||
|
||||
public DragTool() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
// Do nada.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface EditableComponent
|
||||
extends
|
||||
org.jhotdraw.gui.EditableComponent {
|
||||
|
||||
/**
|
||||
* Delete the components that are currently selected in the tree and save
|
||||
* them to allow restoring by a Paste operation.
|
||||
*/
|
||||
void cutSelectedItems();
|
||||
|
||||
/**
|
||||
* Save the components that are currently selected in the tree
|
||||
* to allow creating a clone by a Paste operation.
|
||||
*/
|
||||
void copySelectedItems();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void pasteBufferedItems();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
/**
|
||||
* Cancel Button which closes a dialog by pressing ESC.
|
||||
*/
|
||||
public class CancelButton
|
||||
extends
|
||||
JButton {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public CancelButton() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param text Label of this button.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public CancelButton(String text) {
|
||||
super(text);
|
||||
|
||||
ActionListener al = new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
String cmd = event.getActionCommand();
|
||||
|
||||
if (cmd.equals("PressedESCAPE")) {
|
||||
doClick();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
registerKeyboardAction(
|
||||
al, "PressedESCAPE",
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
|
||||
JButton.WHEN_IN_FOCUSED_WINDOW
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<Events>
|
||||
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
|
||||
</Events>
|
||||
<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_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,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="panelButton">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="buttonClose">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="buttonClose" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="closableDialog.button_close.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="buttonCloseActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new CancelButton()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Insets;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
/**
|
||||
* A dialog that has a close button.
|
||||
*/
|
||||
public class ClosableDialog
|
||||
extends
|
||||
JDialog {
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
*
|
||||
* @param parent The dialog's parent.
|
||||
* @param modal Whether the dialog is modal or not.
|
||||
* @param content The dialog's actual content.
|
||||
* @param title The dialog's title.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public ClosableDialog(Component parent, boolean modal, JComponent content, String title) {
|
||||
super(JOptionPane.getFrameForComponent(parent), title, modal);
|
||||
initComponents();
|
||||
getContentPane().add(content, java.awt.BorderLayout.CENTER);
|
||||
content.setBorder(new EmptyBorder(new Insets(4, 4, 4, 4)));
|
||||
getRootPane().setDefaultButton(buttonClose);
|
||||
pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*/
|
||||
private void doClose() {
|
||||
setVisible(false);
|
||||
dispose();
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
panelButton = new javax.swing.JPanel();
|
||||
buttonClose = new CancelButton();
|
||||
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
closeDialog(evt);
|
||||
}
|
||||
});
|
||||
|
||||
buttonClose.setFont(buttonClose.getFont().deriveFont(buttonClose.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/system"); // NOI18N
|
||||
buttonClose.setText(bundle.getString("closableDialog.button_close.text")); // NOI18N
|
||||
buttonClose.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
buttonCloseActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
panelButton.add(buttonClose);
|
||||
|
||||
getContentPane().add(panelButton, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void buttonCloseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCloseActionPerformed
|
||||
doClose();
|
||||
}//GEN-LAST:event_buttonCloseActionPerformed
|
||||
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
|
||||
doClose();
|
||||
}//GEN-LAST:event_closeDialog
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton buttonClose;
|
||||
private javax.swing.JPanel panelButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
/**
|
||||
* Defines a dialog that allows for easy editing of a property.
|
||||
* The actual editing of the properties is implemented in a {@link DetailsDialogContent}.
|
||||
*/
|
||||
public interface DetailsDialog {
|
||||
|
||||
/**
|
||||
* Returns the {@link DetailsDialogContent} that is used to edit the property.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
DetailsDialogContent getDialogContent();
|
||||
|
||||
/**
|
||||
* Activates the dialog.
|
||||
*/
|
||||
void activate();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import org.opentcs.guing.base.components.properties.type.Property;
|
||||
|
||||
/**
|
||||
* Interface for components to edit properties.
|
||||
* Classes that implement this interface are generally embedded in a dialog.
|
||||
* The dialog then calls these methods.
|
||||
*/
|
||||
public interface DetailsDialogContent {
|
||||
|
||||
/**
|
||||
* Writes the values of the dialog back to the attribute object.
|
||||
* This should happen when the user clicked "OK".
|
||||
*/
|
||||
void updateValues();
|
||||
|
||||
/**
|
||||
* Returns the title of the dialog.
|
||||
*
|
||||
* @return The title.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Sets the property.
|
||||
*
|
||||
* @param property The property.
|
||||
*/
|
||||
void setProperty(Property property);
|
||||
|
||||
/**
|
||||
* Returns the property.
|
||||
*
|
||||
* @return The property.
|
||||
*/
|
||||
Property getProperty();
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* Base implementation for a dialog and tab content.
|
||||
*/
|
||||
public abstract class DialogContent
|
||||
extends
|
||||
JPanel {
|
||||
|
||||
/**
|
||||
* Title of the component in a dialog.
|
||||
*/
|
||||
protected String fDialogTitle;
|
||||
/**
|
||||
* Title of the component in a tab.
|
||||
*/
|
||||
protected String fTabTitle;
|
||||
/**
|
||||
* Indicates that the update of the value has failed.
|
||||
*/
|
||||
protected boolean updateFailed;
|
||||
/**
|
||||
* Whether or not this dialog is modal.
|
||||
*/
|
||||
protected boolean fModal;
|
||||
/**
|
||||
* Parent dialog where this content is added to.
|
||||
*/
|
||||
protected StandardContentDialog fDialog;
|
||||
|
||||
/**
|
||||
* Creates a new instance of AbstractDialogContent.
|
||||
*/
|
||||
public DialogContent() {
|
||||
setModal(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component.
|
||||
*
|
||||
* @return The component of this dialog.
|
||||
*/
|
||||
public JComponent getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dialog to be modal.
|
||||
*
|
||||
* @param modal true if the dialog should be modal.
|
||||
*/
|
||||
public final void setModal(boolean modal) {
|
||||
fModal = modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the component is modal.
|
||||
*
|
||||
* @return Whether or not the component is modal.
|
||||
*/
|
||||
public boolean getModal() {
|
||||
return fModal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for a tab.
|
||||
*
|
||||
* @return The title for a tab.
|
||||
*/
|
||||
public String getTabTitle() {
|
||||
return fTabTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for a dialog.
|
||||
*
|
||||
* @return The title for a dialog.
|
||||
*/
|
||||
public String getDialogTitle() {
|
||||
return fDialogTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title for a dialog.
|
||||
*
|
||||
* @param title The new title for a dialog.
|
||||
*/
|
||||
protected void setDialogTitle(String title) {
|
||||
fDialogTitle = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title for a tab.
|
||||
*
|
||||
* @param title The new title for a tab.
|
||||
*/
|
||||
protected void setTabTitle(String title) {
|
||||
fTabTitle = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the registered listeners that the dialog would like to close.
|
||||
*/
|
||||
protected void notifyRequestClose() {
|
||||
fDialog.requestClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the update of the UI elements failed.
|
||||
*
|
||||
* @return true if the update failed.
|
||||
*/
|
||||
public boolean updateFailed() {
|
||||
return updateFailed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the dialog elements.
|
||||
*/
|
||||
public abstract void initFields();
|
||||
|
||||
/**
|
||||
* Updates the values from the dialog elements.
|
||||
*/
|
||||
public abstract void update();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
/**
|
||||
* A Listener Interface, instances can handle validation of the user input.
|
||||
*/
|
||||
public interface InputValidationListener {
|
||||
|
||||
/**
|
||||
* Notifies about the validity of an input.
|
||||
*
|
||||
* @param success true if input is valid, false otherwise.
|
||||
*/
|
||||
void inputValidationSuccessful(boolean success);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Insets;
|
||||
|
||||
/**
|
||||
* A modified version of FlowLayout that allows containers using this
|
||||
* Layout to behave in a reasonable manner when placed inside a
|
||||
* JScrollPane.
|
||||
*/
|
||||
public class ModifiedFlowLayout
|
||||
extends
|
||||
FlowLayout {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public ModifiedFlowLayout() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param align The alignment value.
|
||||
*/
|
||||
public ModifiedFlowLayout(int align) {
|
||||
super(align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param align the alignment value
|
||||
* @param hgap the horizontal gap between components and between the
|
||||
* components and the borders of the container
|
||||
* @param vgap the vertical gap between components and between the components
|
||||
* and the borders of the container
|
||||
*/
|
||||
public ModifiedFlowLayout(int align, int hgap, int vgap) {
|
||||
super(align, hgap, vgap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension minimumLayoutSize(Container target) {
|
||||
// Size of largest component, so we can resize it in
|
||||
// either direction with something like a split-pane.
|
||||
return computeMinSize(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container target) {
|
||||
return computeSize(target);
|
||||
}
|
||||
|
||||
private Dimension computeSize(Container target) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
int hgap = getHgap();
|
||||
int vgap = getVgap();
|
||||
int w = target.getWidth();
|
||||
|
||||
// Let this behave like a regular FlowLayout (single row)
|
||||
// if the container hasn't been assigned any size yet
|
||||
if (w == 0) {
|
||||
w = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
Insets insets = target.getInsets();
|
||||
|
||||
if (insets == null) {
|
||||
insets = new Insets(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int reqdWidth = 0;
|
||||
int maxwidth = w - (insets.left + insets.right + hgap * 2);
|
||||
int n = target.getComponentCount();
|
||||
int x = 0;
|
||||
int y = insets.top + vgap; // FlowLayout starts by adding vgap, so do that here too.
|
||||
int rowHeight = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = target.getComponent(i);
|
||||
|
||||
if (c.isVisible()) {
|
||||
Dimension d = c.getPreferredSize();
|
||||
|
||||
if ((x == 0) || ((x + d.width) <= maxwidth)) {
|
||||
// fits in current row.
|
||||
if (x > 0) {
|
||||
x += hgap;
|
||||
}
|
||||
|
||||
x += d.width;
|
||||
rowHeight = Math.max(rowHeight, d.height);
|
||||
}
|
||||
else {
|
||||
// Start of new row
|
||||
x = d.width;
|
||||
y += vgap + rowHeight;
|
||||
rowHeight = d.height;
|
||||
}
|
||||
|
||||
reqdWidth = Math.max(reqdWidth, x);
|
||||
}
|
||||
}
|
||||
|
||||
y += rowHeight;
|
||||
y += insets.bottom;
|
||||
|
||||
return new Dimension(reqdWidth + insets.left + insets.right, y);
|
||||
}
|
||||
}
|
||||
|
||||
private Dimension computeMinSize(Container target) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
int minx = Integer.MAX_VALUE;
|
||||
int miny = Integer.MIN_VALUE;
|
||||
boolean foundOne = false;
|
||||
int n = target.getComponentCount();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Component c = target.getComponent(i);
|
||||
|
||||
if (c.isVisible()) {
|
||||
foundOne = true;
|
||||
Dimension d = c.getPreferredSize();
|
||||
minx = Math.min(minx, d.width);
|
||||
miny = Math.min(miny, d.height);
|
||||
}
|
||||
}
|
||||
|
||||
if (foundOne) {
|
||||
return new Dimension(minx, miny);
|
||||
}
|
||||
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<Events>
|
||||
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
|
||||
</Events>
|
||||
<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_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,1,44,0,0,1,-118"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="buttonPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||
<EmptyBorder bottom="0" left="0" right="5" top="0"/>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout">
|
||||
<Property name="horizontalGap" type="int" value="10"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="okButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardContentDialog.button_ok.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="cancelButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardContentDialog.button_cancel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new CancelButton()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="applyButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="applyButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardContentDialog.button_apply.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="applyButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="closeButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="closeButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardContentDialog.button_close.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,305 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
/**
|
||||
* A standard dialog with an OK and a cancel button.
|
||||
*/
|
||||
public class StandardContentDialog
|
||||
extends
|
||||
javax.swing.JDialog
|
||||
implements
|
||||
InputValidationListener {
|
||||
|
||||
/**
|
||||
* A return status code - returned if Cancel button has been pressed.
|
||||
*/
|
||||
public static final int RET_CANCEL = 0;
|
||||
/**
|
||||
* A return status code - returned if OK button has been pressed.
|
||||
*/
|
||||
public static final int RET_OK = 1;
|
||||
/**
|
||||
* Button configuration for an OK and a cancel button.
|
||||
*/
|
||||
public static final int OK_CANCEL = 10;
|
||||
/**
|
||||
* Button configuration for an OK, cancel and apply button.
|
||||
*/
|
||||
public static final int OK_CANCEL_APPLY = 11;
|
||||
/**
|
||||
* Button configuration for a close button.
|
||||
*/
|
||||
public static final int CLOSE = 12;
|
||||
/**
|
||||
* Button configuration user-defined.
|
||||
*/
|
||||
public static final int USER_DEFINED = 13;
|
||||
/**
|
||||
* Content for this dialog.
|
||||
*/
|
||||
protected DialogContent fContent;
|
||||
/**
|
||||
* The return status.
|
||||
*/
|
||||
private int returnStatus = RET_CANCEL;
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
*/
|
||||
public StandardContentDialog(Component parent, DialogContent content) {
|
||||
this(parent, content, true, OK_CANCEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new form StandardDialog.
|
||||
*
|
||||
* @param parent The parent component on which this dialog is centered.
|
||||
* @param content The content.
|
||||
* @param modal whether or not this dialog is modal.
|
||||
* @param options Which user interface options to use.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public StandardContentDialog(
|
||||
Component parent,
|
||||
DialogContent content,
|
||||
boolean modal,
|
||||
int options
|
||||
) {
|
||||
super(JOptionPane.getFrameForComponent(parent), modal);
|
||||
|
||||
initComponents();
|
||||
initButtons(options);
|
||||
|
||||
JComponent component = content.getComponent();
|
||||
|
||||
if (component.getBorder() == null) {
|
||||
component.setBorder(new EmptyBorder(4, 4, 4, 4));
|
||||
}
|
||||
|
||||
getContentPane().add(component, BorderLayout.CENTER);
|
||||
setTitle(content.getDialogTitle());
|
||||
content.initFields();
|
||||
pack();
|
||||
setLocationRelativeTo(parent);
|
||||
fContent = content;
|
||||
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputValidationSuccessful(boolean success) {
|
||||
this.okButton.setEnabled(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the returns status code.
|
||||
*
|
||||
* @return the return status of this dialog - one of RET_OK or RET_CANCEL
|
||||
*/
|
||||
public int getReturnStatus() {
|
||||
return returnStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user-defined button.
|
||||
*
|
||||
* @param text The text for the button.
|
||||
* @param returnStatus The return value when the button is pressed.
|
||||
*/
|
||||
public void addUserDefinedButton(String text, final int returnStatus) {
|
||||
JButton button = new JButton(text);
|
||||
button.addActionListener(new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
doClose(returnStatus);
|
||||
}
|
||||
});
|
||||
|
||||
buttonPanel.add(button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event of the dialog content that the dialog can be closed.
|
||||
*/
|
||||
public void requestClose() {
|
||||
doClose(RET_CANCEL);
|
||||
}
|
||||
|
||||
protected final void initButtons(int options) {
|
||||
switch (options) {
|
||||
case OK_CANCEL:
|
||||
applyButton.setVisible(false);
|
||||
closeButton.setVisible(false);
|
||||
break;
|
||||
|
||||
case OK_CANCEL_APPLY:
|
||||
closeButton.setVisible(false);
|
||||
break;
|
||||
|
||||
case CLOSE:
|
||||
okButton.setVisible(false);
|
||||
cancelButton.setVisible(false);
|
||||
applyButton.setVisible(false);
|
||||
break;
|
||||
|
||||
case USER_DEFINED:
|
||||
okButton.setVisible(false);
|
||||
cancelButton.setVisible(false);
|
||||
applyButton.setVisible(false);
|
||||
closeButton.setVisible(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
buttonPanel = new javax.swing.JPanel();
|
||||
okButton = new javax.swing.JButton();
|
||||
cancelButton = new CancelButton();
|
||||
applyButton = new javax.swing.JButton();
|
||||
closeButton = new javax.swing.JButton();
|
||||
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
closeDialog(evt);
|
||||
}
|
||||
});
|
||||
|
||||
buttonPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 5));
|
||||
buttonPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 10, 5));
|
||||
|
||||
okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/system"); // NOI18N
|
||||
okButton.setText(bundle.getString("standardContentDialog.button_ok.text")); // NOI18N
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
cancelButton.setFont(cancelButton.getFont());
|
||||
cancelButton.setText(bundle.getString("standardContentDialog.button_cancel.text")); // NOI18N
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
applyButton.setFont(applyButton.getFont());
|
||||
applyButton.setText(bundle.getString("standardContentDialog.button_apply.text")); // NOI18N
|
||||
applyButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
applyButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(applyButton);
|
||||
|
||||
closeButton.setFont(closeButton.getFont());
|
||||
closeButton.setText(bundle.getString("standardContentDialog.button_close.text")); // NOI18N
|
||||
closeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
closeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(closeButton);
|
||||
|
||||
getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
/**
|
||||
* Button "close" pressed.
|
||||
*
|
||||
* @param evt The action event.
|
||||
*/
|
||||
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_closeButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Button "apply" pressed.
|
||||
*
|
||||
* @param evt The action event.
|
||||
*/
|
||||
private void applyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyButtonActionPerformed
|
||||
fContent.update();
|
||||
}//GEN-LAST:event_applyButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Button "Ok" pressed.
|
||||
*
|
||||
* @param evt The action event.
|
||||
*/
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
fContent.update();
|
||||
|
||||
if (!fContent.updateFailed()) {
|
||||
doClose(RET_OK);
|
||||
}
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Button "cancel" pressed.
|
||||
*
|
||||
* @param evt The action event.
|
||||
*/
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_closeDialog
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*
|
||||
* @param retStatus The return status code.
|
||||
*/
|
||||
private void doClose(int retStatus) {
|
||||
returnStatus = retStatus;
|
||||
setVisible(false);
|
||||
dispose();
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton applyButton;
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JButton closeButton;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="iconImage" type="java.awt.Image" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="null"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<Events>
|
||||
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
|
||||
</Events>
|
||||
<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,119,0,0,1,72"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="controlPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="buttonPanel">
|
||||
<Properties>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="okButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardDetailsDialog.button_ok.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="cancelButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardDetailsDialog.button_cancel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new CancelButton()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,201 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.opentcs.guing.base.components.properties.type.ModelAttribute;
|
||||
import org.opentcs.guing.base.components.properties.type.Property;
|
||||
|
||||
/**
|
||||
* A dialog in which a {@link DialogContent} can be added.
|
||||
* The dialog has an OK and a cancel button.
|
||||
*/
|
||||
public class StandardDetailsDialog
|
||||
extends
|
||||
javax.swing.JDialog
|
||||
implements
|
||||
DetailsDialog {
|
||||
|
||||
/**
|
||||
* A return status code - returned if Cancel button has been pressed
|
||||
*/
|
||||
public static final int RET_CANCEL = 0;
|
||||
/**
|
||||
* A return status code - returned if OK button has been pressed
|
||||
*/
|
||||
public static final int RET_OK = 1;
|
||||
private int returnStatus = RET_CANCEL;
|
||||
/**
|
||||
* The details dialog content to change a property.
|
||||
*/
|
||||
private final DetailsDialogContent fContent;
|
||||
private final Component fParentComponent;
|
||||
|
||||
/**
|
||||
* Creates new form JDialog
|
||||
*
|
||||
* @param parent The parent component.
|
||||
* @param content Details dialog content.
|
||||
* @param modal Whether or not the dialog is modal.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public StandardDetailsDialog(JPanel parent, boolean modal, DetailsDialogContent content) {
|
||||
super(JOptionPane.getFrameForComponent(parent), modal);
|
||||
fContent = content;
|
||||
fParentComponent = parent;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dialog.
|
||||
*
|
||||
* @param parent The parent dialog.
|
||||
* @param modal Whether or not the dialog is modal.
|
||||
* @param content Details dialog content.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public StandardDetailsDialog(JDialog parent, boolean modal, DetailsDialogContent content) {
|
||||
super(parent, modal);
|
||||
fContent = content;
|
||||
fParentComponent = parent;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialises the dialog.
|
||||
*/
|
||||
protected final void initialize() {
|
||||
JComponent component = (JComponent) fContent;
|
||||
component.setBorder(new EmptyBorder(new java.awt.Insets(5, 5, 5, 5)));
|
||||
getContentPane().add(component, java.awt.BorderLayout.CENTER);
|
||||
initComponents();
|
||||
setTitle(fContent.getTitle());
|
||||
activate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
pack();
|
||||
setLocationRelativeTo(fParentComponent);
|
||||
}
|
||||
|
||||
public Component getParentComponent() {
|
||||
return fParentComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return status.
|
||||
*
|
||||
* @return the return status of this dialog - one of RET_OK or RET_CANCEL
|
||||
*/
|
||||
public int getReturnStatus() {
|
||||
return returnStatus;
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
controlPanel = new javax.swing.JPanel();
|
||||
buttonPanel = new javax.swing.JPanel();
|
||||
okButton = new javax.swing.JButton();
|
||||
cancelButton = new CancelButton();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setIconImage(null);
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
closeDialog(evt);
|
||||
}
|
||||
});
|
||||
|
||||
controlPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/system"); // NOI18N
|
||||
okButton.setText(bundle.getString("standardDetailsDialog.button_ok.text")); // NOI18N
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
cancelButton.setFont(cancelButton.getFont());
|
||||
cancelButton.setText(bundle.getString("standardDetailsDialog.button_cancel.text")); // NOI18N
|
||||
cancelButton.setOpaque(false);
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
controlPanel.add(buttonPanel, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
getContentPane().add(controlPanel, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
fContent.updateValues();
|
||||
|
||||
Property property = fContent.getProperty();
|
||||
|
||||
if (property != null) {
|
||||
property.setChangeState(ModelAttribute.ChangeState.DETAIL_CHANGED);
|
||||
}
|
||||
|
||||
doClose(RET_OK);
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*/
|
||||
private void doClose(int retStatus) {
|
||||
returnStatus = retStatus;
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_closeDialog
|
||||
|
||||
@Override
|
||||
public DetailsDialogContent getDialogContent() {
|
||||
return fContent;
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JPanel controlPanel;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<Events>
|
||||
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
|
||||
</Events>
|
||||
<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_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,-106,0,0,0,-43"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="buttonPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="okButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardDialog.button_ok.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="cancelButton" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/plantoverview/system.properties" key="standardDialog.button_cancel.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new CancelButton()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dialogs;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Insets;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.opentcs.util.gui.Icons;
|
||||
|
||||
/**
|
||||
* A dialog with an ok and a cancel button.
|
||||
*/
|
||||
public class StandardDialog
|
||||
extends
|
||||
JDialog {
|
||||
|
||||
/**
|
||||
* A return status code - returned if Cancel button has been pressed
|
||||
*/
|
||||
public static final int RET_CANCEL = 0;
|
||||
/**
|
||||
* A return status code - returned if OK button has been pressed
|
||||
*/
|
||||
public static final int RET_OK = 1;
|
||||
/**
|
||||
* The content of this dialog.
|
||||
*/
|
||||
protected JComponent fContent;
|
||||
private int returnStatus = RET_CANCEL;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param parent The parent component.
|
||||
* @param modal Whether or not the dialog is modal.
|
||||
* @param content The content component.
|
||||
* @param title The title of the dialog.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public StandardDialog(Component parent, boolean modal, JComponent content, String title) {
|
||||
super(JOptionPane.getFrameForComponent(parent), title, modal);
|
||||
initComponents();
|
||||
initSize(content);
|
||||
setTitle(title);
|
||||
setIconImages(Icons.getOpenTCSIcons());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the size of the dialog based on the content.
|
||||
*
|
||||
* @param content the dialog to base the size on.
|
||||
*/
|
||||
protected final void initSize(JComponent content) {
|
||||
fContent = content;
|
||||
getContentPane().add(content, BorderLayout.CENTER);
|
||||
content.setBorder(new EmptyBorder(new Insets(3, 3, 3, 3)));
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of this dialog.
|
||||
*
|
||||
* @return the content of this dialog.
|
||||
*/
|
||||
public JComponent getContent() {
|
||||
return fContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the return status of this dialog - one of RET_OK or RET_CANCEL.
|
||||
*
|
||||
* @return the return status of this dialog - one of RET_OK or RET_CANCEL.
|
||||
*/
|
||||
public int getReturnStatus() {
|
||||
return returnStatus;
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
buttonPanel = new javax.swing.JPanel();
|
||||
okButton = new javax.swing.JButton();
|
||||
cancelButton = new CancelButton();
|
||||
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
closeDialog(evt);
|
||||
}
|
||||
});
|
||||
|
||||
okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/plantoverview/system"); // NOI18N
|
||||
okButton.setText(bundle.getString("standardDialog.button_ok.text")); // NOI18N
|
||||
okButton.setOpaque(false);
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(okButton);
|
||||
|
||||
cancelButton.setFont(cancelButton.getFont());
|
||||
cancelButton.setText(bundle.getString("standardDialog.button_cancel.text")); // NOI18N
|
||||
cancelButton.setOpaque(false);
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(cancelButton);
|
||||
|
||||
getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
doClose(RET_OK);
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
|
||||
doClose(RET_CANCEL);
|
||||
}//GEN-LAST:event_closeDialog
|
||||
|
||||
/**
|
||||
* Closes the dialog.
|
||||
*/
|
||||
private void doClose(int retStatus) {
|
||||
returnStatus = retStatus;
|
||||
setVisible(false);
|
||||
dispose();
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import bibliothek.gui.dock.common.CContentArea;
|
||||
import bibliothek.gui.dock.common.CControl;
|
||||
import bibliothek.gui.dock.common.CLocation;
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
import bibliothek.gui.dock.common.SingleCDockable;
|
||||
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
|
||||
import bibliothek.gui.dock.common.event.CVetoClosingListener;
|
||||
import bibliothek.gui.dock.common.mode.ExtendedMode;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* Utility class for working with dockables.
|
||||
*/
|
||||
public abstract class AbstractDockingManager
|
||||
implements
|
||||
DockingManager {
|
||||
|
||||
/**
|
||||
* PropertyChangeEvent when a floating dockable closes.
|
||||
*/
|
||||
public static final String DOCKABLE_CLOSED = "DOCKABLE_CLOSED";
|
||||
/**
|
||||
* Map that contains all tab panes. They are stored by their id.
|
||||
*/
|
||||
private final Map<String, CStack> tabPanes = new HashMap<>();
|
||||
/**
|
||||
* Control for the dockable panels.
|
||||
*/
|
||||
private final CControl control;
|
||||
/**
|
||||
* The listeners for closing events.
|
||||
*/
|
||||
private final List<PropertyChangeListener> listeners = new ArrayList<>();
|
||||
|
||||
public AbstractDockingManager(CControl control) {
|
||||
this.control = requireNonNull(control, "control");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dockable.
|
||||
*
|
||||
* @param id The unique id for this dockable.
|
||||
* @param title The title text of this new dockable.
|
||||
* @param comp The JComponent wrapped by the new dockable.
|
||||
* @param closeable If the dockable can be closeable or not.
|
||||
* @return The newly created dockable.
|
||||
*/
|
||||
public DefaultSingleCDockable createDockable(
|
||||
String id,
|
||||
String title,
|
||||
JComponent comp,
|
||||
boolean closeable
|
||||
) {
|
||||
Objects.requireNonNull(id, "id is null");
|
||||
Objects.requireNonNull(title, "title is null");
|
||||
Objects.requireNonNull(comp, "comp is null");
|
||||
if (control == null) {
|
||||
return null;
|
||||
}
|
||||
DefaultSingleCDockable dockable = new DefaultSingleCDockable(id, title);
|
||||
dockable.setCloseable(closeable);
|
||||
dockable.add(comp);
|
||||
return dockable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new floating dockable.
|
||||
*
|
||||
* @param id The unique id for this dockable.
|
||||
* @param title The title text of this new dockable.
|
||||
* @param comp The JComponent wrapped by the new dockable.
|
||||
* @return The newly created dockable.
|
||||
*/
|
||||
public DefaultSingleCDockable createFloatingDockable(
|
||||
String id,
|
||||
String title,
|
||||
JComponent comp
|
||||
) {
|
||||
if (control == null) {
|
||||
return null;
|
||||
}
|
||||
final DefaultSingleCDockable dockable = new DefaultSingleCDockable(id, title);
|
||||
dockable.setCloseable(true);
|
||||
dockable.setFocusComponent(comp);
|
||||
dockable.add(comp);
|
||||
dockable.addVetoClosingListener(new CVetoClosingListener() {
|
||||
|
||||
@Override
|
||||
public void closing(CVetoClosingEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closed(CVetoClosingEvent event) {
|
||||
fireFloatingDockableClosed(dockable);
|
||||
}
|
||||
});
|
||||
control.addDockable(dockable);
|
||||
dockable.setExtendedMode(ExtendedMode.EXTERNALIZED);
|
||||
Rectangle centerRectangle = control.getContentArea().getCenter().getBounds();
|
||||
dockable.setLocation(
|
||||
CLocation.external(
|
||||
(centerRectangle.width - comp.getWidth()) / 2,
|
||||
(centerRectangle.height - comp.getHeight()) / 2,
|
||||
comp.getWidth(),
|
||||
comp.getHeight()
|
||||
)
|
||||
);
|
||||
return dockable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dockable as tab to the tab pane identified by the given id.
|
||||
*
|
||||
* @param newTab The new dockable that shall be added.
|
||||
* @param id The ID of the tab pane.
|
||||
* @param index Index where to insert the dockable in the tab pane.
|
||||
*/
|
||||
public void addTabTo(DefaultSingleCDockable newTab, String id, int index) {
|
||||
Objects.requireNonNull(newTab, "newTab is null.");
|
||||
Objects.requireNonNull(id, "id is null");
|
||||
CStack tabPane = tabPanes.get(id);
|
||||
if (tabPane != null) {
|
||||
control.addDockable(newTab);
|
||||
newTab.setWorkingArea(tabPane);
|
||||
tabPane.getStation().add(newTab.intern(), index);
|
||||
tabPane.getStation().setFrontDockable(newTab.intern());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDockable(SingleCDockable dockable) {
|
||||
Objects.requireNonNull(dockable, "dockable is null");
|
||||
if (control != null) {
|
||||
control.removeDockable(dockable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDockable(String id) {
|
||||
Objects.requireNonNull(id);
|
||||
SingleCDockable dock = control.getSingleDockable(id);
|
||||
if (dock != null) {
|
||||
removeDockable(dock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CControl.
|
||||
*
|
||||
* @return The CControl.
|
||||
*/
|
||||
public CControl getCControl() {
|
||||
return control;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whole component with all dockables, tab panes etc.
|
||||
*
|
||||
* @return The CContentArea of the CControl.
|
||||
*/
|
||||
public CContentArea getContentArea() {
|
||||
if (control != null) {
|
||||
return control.getContentArea();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tab pane with the given id.
|
||||
*
|
||||
* @param id ID of the tab pane.
|
||||
* @return The tab pane or null if there is no tab pane with this id.
|
||||
*/
|
||||
public CStack getTabPane(String id) {
|
||||
if (control != null) {
|
||||
return tabPanes.get(id);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Wraps all given JComponents into a dockable and deploys them on the CControl.
|
||||
*/
|
||||
public abstract void initializeDockables();
|
||||
|
||||
protected void addTabPane(String id, CStack tabPane) {
|
||||
tabPanes.put(id, tabPane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides a dockable (by actually removing it from its station).
|
||||
*
|
||||
* @param station The CStackDockStation the dockable belongs to.
|
||||
* @param dockable The dockable to hide.
|
||||
*/
|
||||
public void hideDockable(CStackDockStation station, DefaultSingleCDockable dockable) {
|
||||
int index = station.indexOf(dockable.intern());
|
||||
|
||||
if (index <= -1) {
|
||||
station.add(dockable.intern(), station.getDockableCount());
|
||||
index = station.indexOf(dockable.intern());
|
||||
}
|
||||
station.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dockable (by actually adding it to its station).
|
||||
*
|
||||
* @param station The CStackDockStation the dockable belongs to.
|
||||
* @param dockable The dockable to show.
|
||||
* @param index Where to add the dockable.
|
||||
*/
|
||||
public void showDockable(
|
||||
CStackDockStation station,
|
||||
DefaultSingleCDockable dockable,
|
||||
int index
|
||||
) {
|
||||
if (station.indexOf(dockable.intern()) <= -1) {
|
||||
station.add(dockable.intern(), index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility status of a dockable with the given id.
|
||||
*
|
||||
* @param id The id of the dockable.
|
||||
* @param visible If it shall be visible or not.
|
||||
*/
|
||||
public void setDockableVisibility(String id, boolean visible) {
|
||||
if (control != null) {
|
||||
SingleCDockable dockable = control.getSingleDockable(id);
|
||||
if (dockable != null) {
|
||||
dockable.setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given dockable is docked to its CStackDockStation.
|
||||
*
|
||||
* @param station The station the dockable should be docked in.
|
||||
* @param dockable The dockable to check.
|
||||
* @return True if it is docked, false otherwise.
|
||||
*/
|
||||
public boolean isDockableDocked(CStackDockStation station, DefaultSingleCDockable dockable) {
|
||||
return station.indexOf(dockable.intern()) <= -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a <code>PropertyChangeEvent</code> when a floatable dockable is closed
|
||||
* (eg a plugin panel).
|
||||
*
|
||||
* @param dockable The dockable that was closed.
|
||||
*/
|
||||
private void fireFloatingDockableClosed(DefaultSingleCDockable dockable) {
|
||||
for (PropertyChangeListener listener : listeners) {
|
||||
listener.propertyChange(
|
||||
new PropertyChangeEvent(this, DOCKABLE_CLOSED, dockable, dockable)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import bibliothek.gui.DockController;
|
||||
import bibliothek.gui.Dockable;
|
||||
import bibliothek.gui.dock.action.DockActionSource;
|
||||
import bibliothek.gui.dock.common.CLocation;
|
||||
import bibliothek.gui.dock.common.intern.AbstractDockableCStation;
|
||||
import bibliothek.gui.dock.common.intern.CControlAccess;
|
||||
import bibliothek.gui.dock.common.mode.CNormalModeArea;
|
||||
import bibliothek.gui.dock.common.mode.ExtendedMode;
|
||||
import bibliothek.gui.dock.common.perspective.CStationPerspective;
|
||||
import bibliothek.gui.dock.facile.mode.Location;
|
||||
import bibliothek.gui.dock.facile.mode.LocationMode;
|
||||
import bibliothek.gui.dock.facile.mode.ModeAreaListener;
|
||||
import bibliothek.gui.dock.layout.DockableProperty;
|
||||
import bibliothek.gui.dock.support.mode.AffectedSet;
|
||||
import bibliothek.gui.dock.util.DockUtilities;
|
||||
import bibliothek.util.Path;
|
||||
|
||||
/**
|
||||
* A tab dockable copied from the dockingframes examples.
|
||||
*/
|
||||
public class CStack
|
||||
extends
|
||||
AbstractDockableCStation<CStackDockStation>
|
||||
implements
|
||||
CNormalModeArea {
|
||||
|
||||
@SuppressWarnings("this-escape")
|
||||
public CStack(String id) {
|
||||
CStackDockStation delegate = new CStackDockStation(this);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
CLocation stationLocation = new CLocation() {
|
||||
|
||||
@Override
|
||||
public CLocation getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String findRoot() {
|
||||
return getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockableProperty findProperty(DockableProperty successor) {
|
||||
return successor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtendedMode findMode() {
|
||||
return ExtendedMode.NORMALIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CLocation aside() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
init(delegate, id, stationLocation, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void install(CControlAccess access) {
|
||||
access.getLocationManager().getNormalMode().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstall(CControlAccess access) {
|
||||
access.getLocationManager().getNormalMode().remove(getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CStationPerspective createPerspective() {
|
||||
throw new IllegalStateException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNormalModeChild(Dockable dockable) {
|
||||
return isChild(dockable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockableProperty getLocation(Dockable child) {
|
||||
return DockUtilities.getPropertyChain(getStation(), child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLocation(Dockable dockable, DockableProperty location, AffectedSet set) {
|
||||
set.add(dockable);
|
||||
|
||||
if (isChild(dockable)) {
|
||||
getStation().move(dockable, location);
|
||||
}
|
||||
else {
|
||||
boolean acceptable = DockUtilities.acceptable(getStation(), dockable);
|
||||
if (!acceptable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getStation().drop(dockable, location)) {
|
||||
getStation().drop(dockable);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModeAreaListener(ModeAreaListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoDefaultArea() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocationRoot() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChild(Dockable dockable) {
|
||||
return dockable.getDockParent() == getStation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeModeAreaListener(ModeAreaListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setController(DockController controller) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMode(LocationMode mode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CLocation getCLocation(Dockable dockable) {
|
||||
DockableProperty property = DockUtilities.getPropertyChain(getStation(), dockable);
|
||||
return getStationLocation().expandProperty(getStation().getController(), property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CLocation getCLocation(Dockable dockable, Location location) {
|
||||
DockableProperty property = location.getLocation();
|
||||
if (property == null) {
|
||||
return getStationLocation();
|
||||
}
|
||||
|
||||
return getStationLocation().expandProperty(getStation().getController(), property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean respectWorkingAreas() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloseable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternalizable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinimizable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStackable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWorkingArea() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public DockActionSource[] getSources() {
|
||||
return new DockActionSource[]{getClose()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaximizable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getTypeId() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import bibliothek.gui.dock.StackDockStation;
|
||||
import bibliothek.gui.dock.action.DockActionSource;
|
||||
import bibliothek.gui.dock.common.CStation;
|
||||
import bibliothek.gui.dock.common.intern.CDockable;
|
||||
import bibliothek.gui.dock.common.intern.CommonDockable;
|
||||
import bibliothek.gui.dock.common.intern.station.CommonDockStation;
|
||||
import bibliothek.gui.dock.common.intern.station.CommonDockStationFactory;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class CStackDockStation
|
||||
extends
|
||||
StackDockStation
|
||||
implements
|
||||
CommonDockStation<StackDockStation, CStackDockStation>,
|
||||
CommonDockable {
|
||||
|
||||
private final CStack delegate;
|
||||
|
||||
public CStackDockStation(CStack stack) {
|
||||
this.delegate = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFactoryID() {
|
||||
return CommonDockStationFactory.FACTORY_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConverterID() {
|
||||
return super.getFactoryID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CDockable getDockable() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockActionSource[] getSources() {
|
||||
return delegate.getSources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CStation<CStackDockStation> getStation() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackDockStation getDockStation() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CStackDockStation asDockStation() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonDockable asDockable() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
|
||||
import bibliothek.gui.dock.common.event.CVetoClosingListener;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import org.opentcs.guing.common.application.ViewManager;
|
||||
|
||||
/**
|
||||
* Handles closing of a dockable.
|
||||
*/
|
||||
public class DockableClosingHandler
|
||||
implements
|
||||
CVetoClosingListener {
|
||||
|
||||
/**
|
||||
* The dockable.
|
||||
*/
|
||||
private final DefaultSingleCDockable dockable;
|
||||
/**
|
||||
* Manages the application's dockables.
|
||||
*/
|
||||
private final DockingManager dockingManager;
|
||||
/**
|
||||
* Manages the application's views.
|
||||
*/
|
||||
private final ViewManager viewManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param dockable The dockable.
|
||||
* @param viewManager Manages the application's views.
|
||||
* @param dockingManager Manages the application's dockables.
|
||||
*/
|
||||
@Inject
|
||||
public DockableClosingHandler(
|
||||
@Assisted
|
||||
DefaultSingleCDockable dockable,
|
||||
ViewManager viewManager,
|
||||
DockingManager dockingManager
|
||||
) {
|
||||
this.dockable = requireNonNull(dockable, "dockable");
|
||||
this.viewManager = requireNonNull(viewManager, "viewManager");
|
||||
this.dockingManager = requireNonNull(dockingManager, "dockingManager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing(CVetoClosingEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closed(CVetoClosingEvent event) {
|
||||
if (event.isExpected()) {
|
||||
dockingManager.removeDockable(dockable);
|
||||
viewManager.removeDockable(dockable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
|
||||
/**
|
||||
* A factory for handlers related to dockables.
|
||||
*/
|
||||
public interface DockableHandlerFactory {
|
||||
|
||||
/**
|
||||
* Creates a new handler for closing the given dockable.
|
||||
*
|
||||
* @param dockable The dockable.
|
||||
* @return A new handler for closing the given dockable.
|
||||
*/
|
||||
DockableClosingHandler createDockableClosingHandler(
|
||||
DefaultSingleCDockable dockable
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import bibliothek.gui.dock.common.DefaultSingleCDockable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Compares two <code>DefaultSingleCDockable</code> instances by their titles.
|
||||
*/
|
||||
public class DockableTitleComparator
|
||||
implements
|
||||
Comparator<DefaultSingleCDockable> {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public DockableTitleComparator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(DefaultSingleCDockable o1, DefaultSingleCDockable o2) {
|
||||
return o1.getTitleText().compareTo(o2.getTitleText());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import bibliothek.gui.dock.common.SingleCDockable;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
/**
|
||||
* Utility class for working with dockables.
|
||||
*/
|
||||
public interface DockingManager {
|
||||
|
||||
/**
|
||||
* PropertyChangeEvent when a floating dockable closes.
|
||||
*/
|
||||
String DOCKABLE_CLOSED = "DOCKABLE_CLOSED";
|
||||
|
||||
/**
|
||||
* Adds a PropertyChangeListener.
|
||||
*
|
||||
* @param listener The new listener.
|
||||
*/
|
||||
void addPropertyChangeListener(PropertyChangeListener listener);
|
||||
|
||||
/**
|
||||
* Removes a dockable from the CControl.
|
||||
*
|
||||
* @param dockable The dockable that shall be removed.
|
||||
*/
|
||||
void removeDockable(SingleCDockable dockable);
|
||||
|
||||
/**
|
||||
* Removes a dockable with the given id.
|
||||
*
|
||||
* @param id The id of the dockable to remove.
|
||||
*/
|
||||
void removeDockable(String id);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.dockable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import bibliothek.gui.dock.common.event.CFocusListener;
|
||||
import bibliothek.gui.dock.common.intern.CDockable;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.event.FocusEvent;
|
||||
import org.jhotdraw.draw.DrawingEditor;
|
||||
import org.opentcs.guing.common.application.ViewManager;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingViewScrollPane;
|
||||
import org.opentcs.guing.common.components.drawing.OpenTCSDrawingView;
|
||||
|
||||
/**
|
||||
* Handles focussing of dockable drawing views.
|
||||
*/
|
||||
public class DrawingViewFocusHandler
|
||||
implements
|
||||
CFocusListener {
|
||||
|
||||
/**
|
||||
* Manages the application's views.
|
||||
*/
|
||||
private final ViewManager viewManager;
|
||||
/**
|
||||
* The drawing editor.
|
||||
*/
|
||||
private final DrawingEditor drawingEditor;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param viewManager Manages the application's views.
|
||||
* @param drawingEditor The drawing editor.
|
||||
*/
|
||||
@Inject
|
||||
public DrawingViewFocusHandler(
|
||||
ViewManager viewManager,
|
||||
DrawingEditor drawingEditor
|
||||
) {
|
||||
this.viewManager = requireNonNull(viewManager, "viewManager");
|
||||
this.drawingEditor = requireNonNull(drawingEditor, "drawingEditor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusGained(CDockable dockable) {
|
||||
DrawingViewScrollPane scrollPane = viewManager.getDrawingViewMap().get(dockable);
|
||||
if (scrollPane == null) {
|
||||
return;
|
||||
}
|
||||
OpenTCSDrawingView drawView = scrollPane.getDrawingView();
|
||||
drawingEditor.setActiveView(drawView);
|
||||
// XXX Looks suspicious: Why are the same values set again here?
|
||||
drawView.setConstrainerVisible(drawView.isConstrainerVisible());
|
||||
drawView.setLabelsVisible(drawView.isLabelsVisible());
|
||||
scrollPane.setRulersVisible(scrollPane.isRulersVisible());
|
||||
drawView.getComponent().dispatchEvent(new FocusEvent(scrollPane, FocusEvent.FOCUS_GAINED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(CDockable dockable) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import javax.swing.event.UndoableEditEvent;
|
||||
import javax.swing.event.UndoableEditListener;
|
||||
import org.jhotdraw.draw.BezierFigure;
|
||||
import org.opentcs.guing.base.model.elements.PathModel;
|
||||
import org.opentcs.guing.common.components.drawing.figures.PathConnection;
|
||||
import org.opentcs.guing.common.components.drawing.figures.liner.BezierLinerEdit;
|
||||
|
||||
/**
|
||||
* Updates a bezier-style PathConnection's control points on edits.
|
||||
*/
|
||||
public class BezierLinerEditHandler
|
||||
implements
|
||||
UndoableEditListener {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public BezierLinerEditHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoableEditHappened(UndoableEditEvent evt) {
|
||||
if (!(evt.getEdit() instanceof BezierLinerEdit)) {
|
||||
return;
|
||||
}
|
||||
BezierFigure owner = ((BezierLinerEdit) evt.getEdit()).getOwner();
|
||||
if (!(owner instanceof PathConnection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PathConnection path = (PathConnection) owner;
|
||||
path.updateControlPoints();
|
||||
PathModel pathModel = path.getModel();
|
||||
pathModel.getPropertyPathControlPoints().markChanged();
|
||||
pathModel.propertiesChanged(path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
/**
|
||||
* Allows the configuration of drawing options.
|
||||
*/
|
||||
public class DrawingOptions {
|
||||
|
||||
/**
|
||||
* Indicates whether blocks should be drawn or not.
|
||||
*/
|
||||
private boolean blocksVisible = true;
|
||||
|
||||
public DrawingOptions() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether blocks should be drawn or not.
|
||||
*
|
||||
* @return {@code true}, if blocks should be drawn, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isBlocksVisible() {
|
||||
return blocksVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether blocks should be drawn or not.
|
||||
*
|
||||
* @param blocksVisible The new value.
|
||||
*/
|
||||
public void setBlocksVisible(boolean blocksVisible) {
|
||||
this.blocksVisible = blocksVisible;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JToggleButton;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
import org.opentcs.guing.common.util.ImageDirectory;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
|
||||
|
||||
/**
|
||||
* A placard panel for drawing views.
|
||||
*/
|
||||
public class DrawingViewPlacardPanel
|
||||
extends
|
||||
JPanel {
|
||||
|
||||
/**
|
||||
* This instance's resource bundle.
|
||||
*/
|
||||
private final ResourceBundleUtil labels
|
||||
= ResourceBundleUtil.getBundle(I18nPlantOverview.MODELVIEW_PATH);
|
||||
/**
|
||||
* A combo box for selecting the zoom level.
|
||||
*/
|
||||
private final JComboBox<ZoomItem> zoomComboBox;
|
||||
/**
|
||||
* A toggle button for turning rulers on/off.
|
||||
*/
|
||||
private final JToggleButton toggleRulersButton;
|
||||
/**
|
||||
* The drawing options.
|
||||
*/
|
||||
private final DrawingOptions drawingOptions;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingView The drawing view.
|
||||
* @param drawingOptions The drawing options.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public DrawingViewPlacardPanel(
|
||||
OpenTCSDrawingView drawingView,
|
||||
DrawingOptions drawingOptions
|
||||
) {
|
||||
requireNonNull(drawingView, "drawingView");
|
||||
this.drawingOptions = requireNonNull(drawingOptions, "drawingOptions");
|
||||
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
|
||||
// Create an extra panel so that the contents can be centered vertically.
|
||||
JPanel vCenteringPanel = new JPanel();
|
||||
vCenteringPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
|
||||
this.add(Box.createVerticalGlue());
|
||||
this.add(vCenteringPanel);
|
||||
|
||||
this.zoomComboBox = zoomComboBox(drawingView);
|
||||
vCenteringPanel.add(zoomComboBox);
|
||||
|
||||
vCenteringPanel.add(zoomViewToWindowButton(drawingView));
|
||||
|
||||
// Show/hide grid
|
||||
JToggleButton toggleConstrainerButton = toggleConstrainerButton(drawingView);
|
||||
toggleConstrainerButton.setSelected(drawingView.isConstrainerVisible());
|
||||
vCenteringPanel.add(toggleConstrainerButton);
|
||||
|
||||
// Show/hide rulers
|
||||
toggleRulersButton = toggleRulersButton();
|
||||
vCenteringPanel.add(toggleRulersButton);
|
||||
|
||||
// Show/hide leabels
|
||||
JToggleButton toggleLabelsButton = toggleLabelsButton(drawingView);
|
||||
toggleLabelsButton.setSelected(drawingView.isLabelsVisible());
|
||||
vCenteringPanel.add(toggleLabelsButton);
|
||||
|
||||
// Show/hide blocks
|
||||
JToggleButton toggleBlocksButton = toggleBlocksButton(drawingView);
|
||||
toggleBlocksButton.setSelected(drawingOptions.isBlocksVisible());
|
||||
vCenteringPanel.add(toggleBlocksButton);
|
||||
}
|
||||
|
||||
public JComboBox<ZoomItem> getZoomComboBox() {
|
||||
return zoomComboBox;
|
||||
}
|
||||
|
||||
public JToggleButton getToggleRulersButton() {
|
||||
return toggleRulersButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the combo box with different zoom factors.
|
||||
*
|
||||
* @param drawingView The DrawingView this combo box will belong to.
|
||||
* @return The created combo box.
|
||||
*/
|
||||
private JComboBox<ZoomItem> zoomComboBox(final OpenTCSDrawingView drawingView) {
|
||||
final JComboBox<ZoomItem> comboBox = new JComboBox<>();
|
||||
comboBox.setEditable(true);
|
||||
comboBox.setFocusable(true);
|
||||
|
||||
final double[] scaleFactors = {
|
||||
5.00, 4.00, 3.00, 2.00, 1.50, 1.25, 1.00, 0.75, 0.50, 0.25, 0.10
|
||||
};
|
||||
for (int i = 0; i < scaleFactors.length; i++) {
|
||||
comboBox.addItem(new ZoomItem(scaleFactors[i]));
|
||||
|
||||
if (scaleFactors[i] == 1.0) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
comboBox.addActionListener((ActionEvent e) -> {
|
||||
final double scaleFactor;
|
||||
|
||||
if (comboBox.getSelectedItem() instanceof ZoomItem) {
|
||||
// A zoom step of the array scaleFactors[]
|
||||
ZoomItem item = (ZoomItem) comboBox.getSelectedItem();
|
||||
scaleFactor = item.getScaleFactor();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// Text input in the combo box
|
||||
String text = (String) comboBox.getSelectedItem();
|
||||
double factor = Double.parseDouble(text.split(" ")[0]);
|
||||
scaleFactor = factor * 0.01; // Eingabe in %
|
||||
comboBox.setSelectedItem((int) (factor + 0.5) + " %");
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
comboBox.setSelectedIndex(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
drawingView.setScaleFactor(scaleFactor);
|
||||
});
|
||||
|
||||
drawingView.addPropertyChangeListener((PropertyChangeEvent evt) -> {
|
||||
// String constants are interned
|
||||
if ("scaleFactor".equals(evt.getPropertyName())) {
|
||||
double scaleFactor = (double) evt.getNewValue();
|
||||
|
||||
for (int i = 0; i < comboBox.getItemCount(); i++) {
|
||||
// One of the predefined scale factors was selected
|
||||
if (scaleFactor == comboBox.getItemAt(i).getScaleFactor()) {
|
||||
comboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + 1 < comboBox.getItemCount()
|
||||
&& scaleFactor < comboBox.getItemAt(i).getScaleFactor()
|
||||
&& scaleFactor > comboBox.getItemAt(i + 1).getScaleFactor()) {
|
||||
// Insert the new scale factor between the next smaller / larger entries
|
||||
ZoomItem newItem = new ZoomItem(scaleFactor);
|
||||
comboBox.insertItemAt(newItem, i + 1);
|
||||
comboBox.setSelectedItem(newItem);
|
||||
}
|
||||
else if (scaleFactor > comboBox.getItemAt(0).getScaleFactor()) {
|
||||
// Insert new item for scale factor larger than the largest predefined factor
|
||||
ZoomItem newItem = new ZoomItem(scaleFactor);
|
||||
comboBox.insertItemAt(newItem, 0);
|
||||
comboBox.setSelectedItem(newItem);
|
||||
}
|
||||
else if (scaleFactor < comboBox.getItemAt(comboBox.getItemCount() - 1).getScaleFactor()) {
|
||||
// Insert new item for scale factor larger than the largest predefined factor
|
||||
ZoomItem newItem = new ZoomItem(scaleFactor);
|
||||
comboBox.insertItemAt(newItem, comboBox.getItemCount());
|
||||
comboBox.setSelectedItem(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button that zooms the drawing to a scale factor so that
|
||||
* it fits the window size.
|
||||
*
|
||||
* @return The created button.
|
||||
*/
|
||||
private JButton zoomViewToWindowButton(final OpenTCSDrawingView drawingView) {
|
||||
final JButton button = new JButton();
|
||||
|
||||
button.setToolTipText(
|
||||
labels.getString("drawingViewPlacardPanel.button_zoomViewToWindow.tooltipText")
|
||||
);
|
||||
|
||||
button.setIcon(ImageDirectory.getImageIcon("/menu/zoom-fit-best-4.png"));
|
||||
|
||||
button.setMargin(new Insets(0, 0, 0, 0));
|
||||
button.setFocusable(false);
|
||||
|
||||
button.addActionListener((ActionEvent e) -> drawingView.zoomViewToWindow());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button to toggle the grid in the drawing.
|
||||
*
|
||||
* @param view The DrawingView the button will belong to.
|
||||
* @return The created button.
|
||||
*/
|
||||
private JToggleButton toggleConstrainerButton(final OpenTCSDrawingView drawingView) {
|
||||
final JToggleButton toggleButton = new JToggleButton();
|
||||
|
||||
toggleButton.setToolTipText(
|
||||
labels.getString("drawingViewPlacardPanel.button_toggleGrid.tooltipText")
|
||||
);
|
||||
|
||||
toggleButton.setIcon(ImageDirectory.getImageIcon("/menu/view-split.png"));
|
||||
|
||||
toggleButton.setMargin(new Insets(0, 0, 0, 0));
|
||||
toggleButton.setFocusable(false);
|
||||
|
||||
toggleButton.addItemListener(
|
||||
(ItemEvent event) -> drawingView.setConstrainerVisible(toggleButton.isSelected())
|
||||
);
|
||||
|
||||
return toggleButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button to toggle the rulers in the drawing.
|
||||
*
|
||||
* @return The created button.
|
||||
*/
|
||||
private JToggleButton toggleRulersButton() {
|
||||
final JToggleButton toggleButton = new JToggleButton();
|
||||
|
||||
toggleButton.setToolTipText(
|
||||
labels.getString("drawingViewPlacardPanel.button_toggleRulers.tooltipText")
|
||||
);
|
||||
|
||||
toggleButton.setIcon(ImageDirectory.getImageIcon("/toolbar/document-page-setup.16x16.png"));
|
||||
|
||||
toggleButton.setMargin(new Insets(0, 0, 0, 0));
|
||||
toggleButton.setFocusable(false);
|
||||
|
||||
return toggleButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button to toglle the labels.
|
||||
*
|
||||
* @param view The DrawingView the button will belong to.
|
||||
* @return The created button.
|
||||
*/
|
||||
private JToggleButton toggleLabelsButton(final OpenTCSDrawingView drawingView) {
|
||||
final JToggleButton toggleButton = new JToggleButton();
|
||||
|
||||
toggleButton.setToolTipText(
|
||||
labels.getString("drawingViewPlacardPanel.button_toggleLabels.tooltipText")
|
||||
);
|
||||
|
||||
toggleButton.setIcon(ImageDirectory.getImageIcon("/menu/comment-add.16.png"));
|
||||
|
||||
toggleButton.setMargin(new Insets(0, 0, 0, 0));
|
||||
toggleButton.setFocusable(false);
|
||||
|
||||
toggleButton.addItemListener(
|
||||
(ItemEvent event) -> drawingView.setLabelsVisible(toggleButton.isSelected())
|
||||
);
|
||||
|
||||
return toggleButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button to toggle the blocks in the drawing.
|
||||
*
|
||||
* @param view The DrawingView the button will belong to.
|
||||
* @return The created button.
|
||||
*/
|
||||
private JToggleButton toggleBlocksButton(final OpenTCSDrawingView drawingView) {
|
||||
final JToggleButton toggleButton = new JToggleButton();
|
||||
|
||||
toggleButton.setToolTipText(
|
||||
labels.getString("drawingViewPlacardPanel.button_toggleBlocks.tooltipText")
|
||||
);
|
||||
|
||||
toggleButton.setIcon(ImageDirectory.getImageIcon("/tree/block.18x18.png"));
|
||||
|
||||
toggleButton.setMargin(new Insets(0, 0, 0, 0));
|
||||
toggleButton.setFocusable(false);
|
||||
|
||||
toggleButton.addItemListener(itemEvent -> {
|
||||
drawingOptions.setBlocksVisible(toggleButton.isSelected());
|
||||
drawingView.drawingOptionsChanged();
|
||||
});
|
||||
|
||||
return toggleButton;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.EventObject;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JViewport;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.jhotdraw.gui.PlacardScrollPaneLayout;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
import org.opentcs.guing.common.components.drawing.course.OriginChangeListener;
|
||||
|
||||
/**
|
||||
* A custom scroll pane to wrap an <code>OpenTCSDrawingView</code>.
|
||||
*/
|
||||
public class DrawingViewScrollPane
|
||||
extends
|
||||
JScrollPane
|
||||
implements
|
||||
OriginChangeListener {
|
||||
|
||||
/**
|
||||
* The drawing view.
|
||||
*/
|
||||
private final OpenTCSDrawingView drawingView;
|
||||
/**
|
||||
* The view's placard panel.
|
||||
*/
|
||||
private final DrawingViewPlacardPanel placardPanel;
|
||||
/**
|
||||
* Whether the rulers are currently visible or not.
|
||||
*/
|
||||
private boolean rulersVisible = true;
|
||||
private Origin origin = new Origin();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingView The drawing view.
|
||||
* @param placardPanel The view's placard panel.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public DrawingViewScrollPane(
|
||||
OpenTCSDrawingView drawingView,
|
||||
DrawingViewPlacardPanel placardPanel
|
||||
) {
|
||||
this.drawingView = requireNonNull(drawingView, "drawingView");
|
||||
this.placardPanel = requireNonNull(placardPanel, "placardPanel");
|
||||
|
||||
setViewport(new JViewport());
|
||||
getViewport().setView(drawingView.getComponent());
|
||||
setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||
setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
setViewportBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
|
||||
setLayout(new PlacardScrollPaneLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
|
||||
// Horizontal and vertical rulers
|
||||
Ruler.Horizontal newHorizontalRuler = new Ruler.Horizontal(drawingView);
|
||||
drawingView.addPropertyChangeListener(newHorizontalRuler);
|
||||
newHorizontalRuler.setPreferredWidth(drawingView.getComponent().getWidth());
|
||||
Ruler.Vertical newVerticalRuler = new Ruler.Vertical(drawingView);
|
||||
drawingView.addPropertyChangeListener(newVerticalRuler);
|
||||
newVerticalRuler.setPreferredHeight(drawingView.getComponent().getHeight());
|
||||
setColumnHeaderView(newHorizontalRuler);
|
||||
setRowHeaderView(newVerticalRuler);
|
||||
|
||||
this.add(placardPanel, JScrollPane.LOWER_LEFT_CORNER);
|
||||
|
||||
// Increase the preferred height of the horizontal scroll bar, which also affects the height of
|
||||
// the corner in which the DrawingViewPlacardPanel is located. This ensures there is enough
|
||||
// vertical space for all components in all graphical environments. (Without this, the vertical
|
||||
// space is not sufficient for some components e.g. on Ubuntu 20.04.)
|
||||
getHorizontalScrollBar().setPreferredSize(new Dimension(100, 34));
|
||||
|
||||
// Register handler for rulers toggle button.
|
||||
placardPanel.getToggleRulersButton().addItemListener(
|
||||
new RulersToggleListener(placardPanel.getToggleRulersButton())
|
||||
);
|
||||
placardPanel.getToggleRulersButton().setSelected(rulersVisible);
|
||||
}
|
||||
|
||||
public OpenTCSDrawingView getDrawingView() {
|
||||
return drawingView;
|
||||
}
|
||||
|
||||
public DrawingViewPlacardPanel getPlacardPanel() {
|
||||
return placardPanel;
|
||||
}
|
||||
|
||||
public Ruler.Horizontal getHorizontalRuler() {
|
||||
return (Ruler.Horizontal) getColumnHeader().getView();
|
||||
}
|
||||
|
||||
public Ruler.Vertical getVerticalRuler() {
|
||||
return (Ruler.Vertical) getRowHeader().getView();
|
||||
}
|
||||
|
||||
public boolean isRulersVisible() {
|
||||
return rulersVisible;
|
||||
}
|
||||
|
||||
public void setRulersVisible(boolean visible) {
|
||||
this.rulersVisible = visible;
|
||||
if (visible) {
|
||||
getHorizontalRuler().setVisible(true);
|
||||
getHorizontalRuler().setPreferredWidth(getWidth());
|
||||
getVerticalRuler().setVisible(true);
|
||||
getVerticalRuler().setPreferredHeight(getHeight());
|
||||
getPlacardPanel().getToggleRulersButton().setSelected(true);
|
||||
}
|
||||
else {
|
||||
getHorizontalRuler().setVisible(false);
|
||||
getHorizontalRuler().setPreferredSize(new Dimension(0, 0));
|
||||
getVerticalRuler().setVisible(false);
|
||||
getVerticalRuler().setPreferredSize(new Dimension(0, 0));
|
||||
getPlacardPanel().getToggleRulersButton().setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void originChanged(
|
||||
@Nonnull
|
||||
Origin origin
|
||||
) {
|
||||
requireNonNull(origin, "origin");
|
||||
if (origin == this.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.origin.removeListener(getHorizontalRuler());
|
||||
this.origin.removeListener(getVerticalRuler());
|
||||
this.origin.removeListener(this);
|
||||
this.origin = origin;
|
||||
|
||||
origin.addListener(getHorizontalRuler());
|
||||
origin.addListener(getVerticalRuler());
|
||||
origin.addListener(this);
|
||||
|
||||
// Notify the rulers directly. This is necessary to initialize/update the rulers scale when a
|
||||
// model is created or loaded.
|
||||
// Calling origin.notifyScaleChanged() would lead to all model elements being notified (loading
|
||||
// times for bigger models would suffer).
|
||||
getHorizontalRuler().originScaleChanged(new EventObject(origin));
|
||||
getVerticalRuler().originScaleChanged(new EventObject(origin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originLocationChanged(EventObject evt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originScaleChanged(EventObject evt) {
|
||||
drawingView.getComponent().revalidate();
|
||||
}
|
||||
|
||||
private class RulersToggleListener
|
||||
implements
|
||||
ItemListener {
|
||||
|
||||
private final JToggleButton rulersButton;
|
||||
|
||||
RulersToggleListener(JToggleButton rulersButton) {
|
||||
this.rulersButton = requireNonNull(rulersButton, "rulersButton");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
setRulersVisible(rulersButton.isSelected());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
|
||||
/**
|
||||
* Triggers a (re-)initialization of the view's offset figures when notified
|
||||
* about resize events.
|
||||
*/
|
||||
public class OffsetListener
|
||||
implements
|
||||
ComponentListener {
|
||||
|
||||
/**
|
||||
* The drawing view we're working with.
|
||||
*/
|
||||
private final OpenTCSDrawingEditor drawingEditor;
|
||||
/**
|
||||
* Initiales the offset figures once the view is resized (normally
|
||||
* done when the window becomes visible). But the listener shouldn't
|
||||
* listen to further resizing events.
|
||||
* XXX get rid of this listener?
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingEditor The drawing editor to call for (re-)initialization of its
|
||||
* offset figures.
|
||||
*/
|
||||
public OffsetListener(OpenTCSDrawingEditor drawingEditor) {
|
||||
this.drawingEditor = drawingEditor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
if (!initialized) {
|
||||
drawingEditor.initializeViewport();
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.KeyStroke;
|
||||
import org.jhotdraw.draw.DefaultDrawingEditor;
|
||||
import org.jhotdraw.draw.Drawing;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.jhotdraw.draw.action.IncreaseHandleDetailLevelAction;
|
||||
import org.jhotdraw.draw.event.CompositeFigureEvent;
|
||||
import org.jhotdraw.draw.event.CompositeFigureListener;
|
||||
import org.opentcs.guing.common.components.drawing.figures.LabeledFigure;
|
||||
import org.opentcs.guing.common.components.drawing.figures.OffsetFigure;
|
||||
import org.opentcs.guing.common.components.drawing.figures.TCSLabelFigure;
|
||||
import org.opentcs.guing.common.event.DrawingEditorEvent;
|
||||
import org.opentcs.guing.common.event.DrawingEditorListener;
|
||||
import org.opentcs.guing.common.model.SystemModel;
|
||||
import org.opentcs.guing.common.util.CourseObjectFactory;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.draw.MoveAction;
|
||||
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.util.event.EventHandler;
|
||||
|
||||
/**
|
||||
* The <code>DrawingEditor</code> coordinates <code>DrawingViews</code>
|
||||
* and the <code>Drawing</code>.
|
||||
* It also offers methods to add specific unique figures to the
|
||||
* <code>Drawing</code>.
|
||||
*/
|
||||
public class OpenTCSDrawingEditor
|
||||
extends
|
||||
DefaultDrawingEditor
|
||||
implements
|
||||
EventHandler {
|
||||
|
||||
/**
|
||||
* Width on the screen edge.
|
||||
*/
|
||||
private static final int MARGIN = 20;
|
||||
/**
|
||||
* A factory for course objects.
|
||||
*/
|
||||
private final CourseObjectFactory crsObjectFactory;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private final CompositeFigureEventHandler cmpFigureEvtHandler = new CompositeFigureEventHandler();
|
||||
/**
|
||||
* Listens for figure selection, addition or removal events.
|
||||
*/
|
||||
private final List<DrawingEditorListener> fDrawingEditorListeners = new ArrayList<>();
|
||||
/**
|
||||
* The drawing that contains all figures.
|
||||
*/
|
||||
private Drawing fDrawing;
|
||||
// These invisible figures are moved automatically to enlarge the drawing.
|
||||
private OffsetFigure topOffsetFigure;
|
||||
private OffsetFigure bottomOffsetFigure;
|
||||
private OffsetFigure rightOffsetFigure;
|
||||
private OffsetFigure leftOffsetFigure;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param crsObjFactory A factory for course objects.
|
||||
*/
|
||||
@Inject
|
||||
public OpenTCSDrawingEditor(CourseObjectFactory crsObjFactory) {
|
||||
this.crsObjectFactory = requireNonNull(crsObjFactory, "crsObjectFactory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the offset figures, sets their position to the current bounds of the
|
||||
* view and repaints the ruler to fit the current grid.
|
||||
*/
|
||||
public void initializeViewport() {
|
||||
initializeRuler();
|
||||
removeOffsetFigures();
|
||||
topOffsetFigure = crsObjectFactory.createOffsetFigure();
|
||||
bottomOffsetFigure = crsObjectFactory.createOffsetFigure();
|
||||
leftOffsetFigure = crsObjectFactory.createOffsetFigure();
|
||||
rightOffsetFigure = crsObjectFactory.createOffsetFigure();
|
||||
|
||||
OpenTCSDrawingView activeView = getActiveView();
|
||||
if (activeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rectangle that contains all figures
|
||||
Rectangle2D.Double drawingArea = getDrawing().getDrawingArea();
|
||||
// The visible rectangle
|
||||
Rectangle visibleRect = activeView.getComponent().getVisibleRect();
|
||||
// The size of the invisible offset figures
|
||||
double wFigure = topOffsetFigure.getBounds().width;
|
||||
double hFigure = topOffsetFigure.getBounds().height;
|
||||
|
||||
// When the drawing already contains figures
|
||||
double xLeft = drawingArea.x;
|
||||
double xRight = drawingArea.x + drawingArea.width;
|
||||
double yTop = drawingArea.y;
|
||||
double yBottom = drawingArea.y + drawingArea.height;
|
||||
|
||||
// An empty drawing only contains the origin figure, which shall be on the bottom left.
|
||||
if (visibleRect.width > drawingArea.width && visibleRect.height > drawingArea.height) {
|
||||
xLeft = -drawingArea.width / 2 - MARGIN;
|
||||
xRight = visibleRect.width + xLeft - (MARGIN + wFigure / 2);
|
||||
yBottom = -(-drawingArea.height / 2 - MARGIN);
|
||||
yTop = -(visibleRect.height - yBottom - (MARGIN + hFigure / 2));
|
||||
}
|
||||
|
||||
double xCenter = (xLeft + xRight) / 2;
|
||||
double yCenter = (yBottom + yTop) / 2;
|
||||
|
||||
topOffsetFigure.setBounds(new Point2D.Double(xCenter, yTop), null);
|
||||
bottomOffsetFigure.setBounds(new Point2D.Double(xCenter, yBottom), null);
|
||||
leftOffsetFigure.setBounds(new Point2D.Double(xLeft, yCenter), null);
|
||||
rightOffsetFigure.setBounds(new Point2D.Double(xRight, yCenter), null);
|
||||
|
||||
getDrawing().add(topOffsetFigure);
|
||||
getDrawing().add(bottomOffsetFigure);
|
||||
getDrawing().add(leftOffsetFigure);
|
||||
getDrawing().add(rightOffsetFigure);
|
||||
|
||||
// validateViewTranslation();
|
||||
}
|
||||
|
||||
protected CourseObjectFactory getCourseObjectFactory() {
|
||||
return crsObjectFactory;
|
||||
}
|
||||
|
||||
private void initializeRuler() {
|
||||
OpenTCSDrawingView activeView = getActiveView();
|
||||
DrawingViewScrollPane scrollPane
|
||||
= (DrawingViewScrollPane) activeView.getComponent().getParent().getParent();
|
||||
Rectangle2D.Double drawingArea
|
||||
= activeView.getDrawing().getDrawingArea();
|
||||
scrollPane.getHorizontalRuler().setPreferredWidth((int) drawingArea.width);
|
||||
scrollPane.getVerticalRuler().setPreferredHeight((int) drawingArea.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the <code>OffsetFigure</code>s off the drawing.
|
||||
*/
|
||||
private void removeOffsetFigures() {
|
||||
if (getDrawing() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getDrawing().remove(topOffsetFigure);
|
||||
getDrawing().remove(bottomOffsetFigure);
|
||||
getDrawing().remove(leftOffsetFigure);
|
||||
getDrawing().remove(rightOffsetFigure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void addDrawingEditorListener(DrawingEditorListener listener) {
|
||||
requireNonNull(listener, "listener");
|
||||
fDrawingEditorListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void removeDrawingEditorListener(DrawingEditorListener listener) {
|
||||
requireNonNull(listener, "listener");
|
||||
fDrawingEditorListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the system model.
|
||||
*
|
||||
* @param systemModel The model of the course.
|
||||
*/
|
||||
public void setSystemModel(SystemModel systemModel) {
|
||||
setDrawing(systemModel.getDrawing());
|
||||
for (DrawingView drawView : getDrawingViews()) {
|
||||
((OpenTCSDrawingView) drawView).setBlocks(
|
||||
systemModel.getMainFolder(SystemModel.FolderKey.BLOCKS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Drawing getDrawing() {
|
||||
return fDrawing;
|
||||
}
|
||||
|
||||
public void setDrawing(Drawing drawing) {
|
||||
requireNonNull(drawing, "drawing");
|
||||
|
||||
if (fDrawing != null) {
|
||||
fDrawing.removeCompositeFigureListener(cmpFigureEvtHandler);
|
||||
}
|
||||
fDrawing = drawing;
|
||||
fDrawing.addCompositeFigureListener(cmpFigureEvtHandler);
|
||||
|
||||
// Also let the drawing views know about the new drawing.
|
||||
for (DrawingView view : getDrawingViews()) {
|
||||
view.setDrawing(drawing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(DrawingView view) {
|
||||
super.add(view);
|
||||
view.setDrawing(fDrawing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenTCSDrawingView getActiveView() {
|
||||
return (OpenTCSDrawingView) super.getActiveView();
|
||||
}
|
||||
|
||||
public Collection<OpenTCSDrawingView> getAllViews() {
|
||||
Collection<OpenTCSDrawingView> result = new ArrayList<>();
|
||||
for (DrawingView view : getDrawingViews()) {
|
||||
result.add((OpenTCSDrawingView) view);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification of the <code>DrawingView</code> that a figure was added.
|
||||
*
|
||||
* @param figure The added figure.
|
||||
*/
|
||||
public void figureAdded(Figure figure) {
|
||||
// Create the data model to a new point or location figure and show
|
||||
// the name in the label
|
||||
if (figure instanceof LabeledFigure) {
|
||||
LabeledFigure labeledFigure = (LabeledFigure) figure;
|
||||
|
||||
if (labeledFigure.getLabel() == null) {
|
||||
// Create the label and add the figure to the data model
|
||||
TCSLabelFigure label = new TCSLabelFigure();
|
||||
Point2D.Double pos = labeledFigure.getStartPoint();
|
||||
pos.x += label.getOffset().x;
|
||||
pos.y += label.getOffset().y;
|
||||
label.setBounds(pos, pos);
|
||||
labeledFigure.setLabel(label);
|
||||
}
|
||||
}
|
||||
|
||||
for (DrawingEditorListener listener : fDrawingEditorListeners) {
|
||||
listener.figureAdded(new DrawingEditorEvent(this, figure));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification of the <code>DrawingView</code> that a figure was removed.
|
||||
*
|
||||
* @param figure The figure that was removed.
|
||||
*/
|
||||
public void figureRemoved(Figure figure) {
|
||||
for (DrawingEditorListener listener : fDrawingEditorListeners) {
|
||||
listener.figureRemoved(new DrawingEditorEvent(this, figure));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification of the <code>DrawingView</code> that figures were selected.
|
||||
*
|
||||
* @param figures The selected figures.
|
||||
*/
|
||||
public void figuresSelected(List<Figure> figures) {
|
||||
for (DrawingEditorListener listener : fDrawingEditorListeners) {
|
||||
listener.figureSelected(new DrawingEditorEvent(this, figures));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the method from DefaultDrawingEditor to create a tool-specific
|
||||
* input map.
|
||||
* The implementation of this class creates an input map for the following
|
||||
* action ID's:
|
||||
* - DeleteAction
|
||||
* - MoveAction.West, .East, .North, .South
|
||||
*
|
||||
* SelectAll, Cut, Copy, Paste are handled by SelectAllAction etc.
|
||||
*
|
||||
* @return The input map.
|
||||
*/
|
||||
@Override // DefaultDrawingEditor
|
||||
protected InputMap createInputMap() {
|
||||
InputMap m = new InputMap();
|
||||
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), DeleteAction.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), DeleteAction.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), MoveAction.West.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), MoveAction.East.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), MoveAction.North.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), MoveAction.South.ID);
|
||||
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.ALT_DOWN_MASK), MoveAction.West.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.ALT_DOWN_MASK), MoveAction.East.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK), MoveAction.North.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK), MoveAction.South.ID);
|
||||
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.SHIFT_DOWN_MASK), MoveAction.West.ID);
|
||||
m.put(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.SHIFT_DOWN_MASK),
|
||||
MoveAction.East.ID
|
||||
);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.SHIFT_DOWN_MASK), MoveAction.North.ID);
|
||||
m.put(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.SHIFT_DOWN_MASK),
|
||||
MoveAction.South.ID
|
||||
);
|
||||
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK), MoveAction.West.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK), MoveAction.East.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK), MoveAction.North.ID);
|
||||
m.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK), MoveAction.South.ID);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override // DefaultDrawingEditor
|
||||
protected ActionMap createActionMap() {
|
||||
ActionMap m = new ActionMap();
|
||||
|
||||
m.put(DeleteAction.ID, new DeleteAction());
|
||||
m.put(SelectAllAction.ID, new SelectAllAction());
|
||||
m.put(IncreaseHandleDetailLevelAction.ID, new IncreaseHandleDetailLevelAction(this));
|
||||
|
||||
m.put(MoveAction.East.ID, new MoveAction.East(this));
|
||||
m.put(MoveAction.West.ID, new MoveAction.West(this));
|
||||
m.put(MoveAction.North.ID, new MoveAction.North(this));
|
||||
m.put(MoveAction.South.ID, new MoveAction.South(this));
|
||||
|
||||
// m.put(CutAction.ID, new CutAction());
|
||||
// m.put(CopyAction.ID, new CopyAction());
|
||||
// m.put(PasteAction.ID, new PasteAction());
|
||||
return m;
|
||||
}
|
||||
|
||||
private class CompositeFigureEventHandler
|
||||
implements
|
||||
CompositeFigureListener {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
CompositeFigureEventHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void figureAdded(CompositeFigureEvent e) {
|
||||
OpenTCSDrawingEditor.this.figureAdded(e.getChildFigure());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void figureRemoved(CompositeFigureEvent e) {
|
||||
OpenTCSDrawingEditor.this.figureRemoved(e.getChildFigure());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.awt.Point;
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.base.model.elements.BlockModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
import org.opentcs.guing.common.components.drawing.figures.BitmapFigure;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface OpenTCSDrawingView
|
||||
extends
|
||||
DrawingView,
|
||||
EventHandler {
|
||||
|
||||
boolean isLabelsVisible();
|
||||
|
||||
void setLabelsVisible(boolean newValue);
|
||||
|
||||
/**
|
||||
* Called when the drawing options have changed.
|
||||
*/
|
||||
void drawingOptionsChanged();
|
||||
|
||||
/**
|
||||
* Returns if a given point on the screen is contained in this drawing view.
|
||||
*
|
||||
* @param p The reference point on the screen.
|
||||
* @return Boolean if this point is contained.
|
||||
*/
|
||||
boolean containsPointOnScreen(Point p);
|
||||
|
||||
/**
|
||||
* Adds a background image to this drawing view.
|
||||
*
|
||||
* @param file The file with the image.
|
||||
*/
|
||||
void addBackgroundBitmap(File file);
|
||||
|
||||
/**
|
||||
* Adds a background image to this drawing view.
|
||||
*
|
||||
* @param bitmapFigure The figure containing the image.
|
||||
*/
|
||||
void addBackgroundBitmap(BitmapFigure bitmapFigure);
|
||||
|
||||
/**
|
||||
* Scales the view to a value so the whole model fits.
|
||||
*/
|
||||
void zoomViewToWindow();
|
||||
|
||||
/**
|
||||
* Sets the elements of the blocks.
|
||||
*
|
||||
* @param blocks A <code>ModelComponent</code> which childs must be <code>BlockModels</code>.
|
||||
*/
|
||||
void setBlocks(ModelComponent blocks);
|
||||
|
||||
/**
|
||||
* Shows or hides the current route of a vehicle.
|
||||
*
|
||||
* @param vehicle The vehicle
|
||||
* @param visible <code>true</code> to set it to visible, <code>false</code> otherwise.
|
||||
*/
|
||||
void displayDriveOrders(VehicleModel vehicle, boolean visible);
|
||||
|
||||
/**
|
||||
* Updates the figures of a block.
|
||||
*
|
||||
* @param block The block.
|
||||
*/
|
||||
void updateBlock(BlockModel block);
|
||||
|
||||
/**
|
||||
* Scrolls to the given figure. Normally called when the user clicks on a model component in the
|
||||
* TreeView and wants to see the corresponding figure.
|
||||
*
|
||||
* @param figure The figure to be scrolled to.
|
||||
*/
|
||||
void scrollTo(Figure figure);
|
||||
|
||||
/**
|
||||
* Fixes the view on the vehicle and marks it and its destination with a colored circle.
|
||||
*
|
||||
* @param model The vehicle model.
|
||||
*/
|
||||
void followVehicle(
|
||||
@Nonnull
|
||||
VehicleModel model
|
||||
);
|
||||
|
||||
/**
|
||||
* Releases the view and stops following the current vehicle.
|
||||
*/
|
||||
void stopFollowVehicle();
|
||||
|
||||
/**
|
||||
* Deletes the given model components from the drawing view.
|
||||
*
|
||||
* @param components The components to delete.
|
||||
*/
|
||||
void delete(Set<ModelComponent> components);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.EventObject;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
import org.opentcs.guing.common.components.drawing.course.OriginChangeListener;
|
||||
|
||||
/**
|
||||
* A ruler.
|
||||
*/
|
||||
public abstract class Ruler
|
||||
extends
|
||||
JComponent
|
||||
implements
|
||||
PropertyChangeListener,
|
||||
OriginChangeListener {
|
||||
|
||||
/**
|
||||
* Size of the rulers (height of the horizontal ruler, width of the vertical).
|
||||
*/
|
||||
private static final int SIZE = 25;
|
||||
/**
|
||||
* The standard translation of the drawing view. Not sure though why
|
||||
* it is -12.
|
||||
*/
|
||||
private static final int STANDARD_TRANSLATION = -12;
|
||||
/**
|
||||
* The DrawingView.
|
||||
*/
|
||||
protected final DrawingView drawingView;
|
||||
/**
|
||||
* The current scale factor.
|
||||
*/
|
||||
protected double scaleFactor = 1.0;
|
||||
/**
|
||||
* The scale factor for the horizontal ruler.
|
||||
*/
|
||||
protected double horizontalRulerScale = Origin.DEFAULT_SCALE;
|
||||
/**
|
||||
* The scale factor for the vertical ruler.
|
||||
*/
|
||||
protected double verticalRulerScale = Origin.DEFAULT_SCALE;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingView The drawing view.
|
||||
*/
|
||||
private Ruler(DrawingView drawingView) {
|
||||
this.drawingView = requireNonNull(drawingView, "drawingView");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A horizontal ruler.
|
||||
*/
|
||||
public static class Horizontal
|
||||
extends
|
||||
Ruler {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingView The drawing view.
|
||||
*/
|
||||
public Horizontal(DrawingView drawingView) {
|
||||
super(drawingView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new width of the ruler and repaints it.
|
||||
*
|
||||
* @param preferredWidth The new width.
|
||||
*/
|
||||
public void setPreferredWidth(int preferredWidth) {
|
||||
setPreferredSize(new Dimension(preferredWidth, SIZE));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
Rectangle drawHere = g.getClipBounds();
|
||||
Point translation = new Point(
|
||||
(int) -drawingView.getDrawingToViewTransform().getTranslateX(),
|
||||
(int) -drawingView.getDrawingToViewTransform().getTranslateY()
|
||||
);
|
||||
// If we scroll right, the translation isn't incremented by default.
|
||||
// We use the translation of the visible rectangle instead.
|
||||
int visibleRectX = drawingView.getComponent().getVisibleRect().x + STANDARD_TRANSLATION;
|
||||
if (STANDARD_TRANSLATION == translation.x) {
|
||||
translation.x = visibleRectX;
|
||||
}
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
// i translated
|
||||
int translated;
|
||||
// i normalized to decimal
|
||||
int draw;
|
||||
int drawOld = 0;
|
||||
// draw translated
|
||||
int drawTranslated;
|
||||
String translatedAsString;
|
||||
String lastIndex;
|
||||
|
||||
// base line
|
||||
g2d.drawLine(
|
||||
0, SIZE - 1,
|
||||
getWidth(), SIZE - 1
|
||||
);
|
||||
|
||||
for (int i = drawHere.x; i < getWidth(); i += 10) {
|
||||
translated = translateValue(i, translation);
|
||||
translatedAsString = Integer.toString(translated);
|
||||
lastIndex = translatedAsString.substring(translatedAsString.length() - 1);
|
||||
|
||||
int decimal = Integer.parseInt(lastIndex);
|
||||
{
|
||||
// These steps are neccessary to guarantee lines are drawn
|
||||
// at every pixel. It always rounds i to a decimal, so the modulo
|
||||
// operators work
|
||||
draw = i;
|
||||
|
||||
if (translated < 0) {
|
||||
draw += decimal;
|
||||
}
|
||||
else {
|
||||
draw -= decimal;
|
||||
}
|
||||
|
||||
drawTranslated = translateValue(draw, translation);
|
||||
// draw has to be incremented by 1, otherwise the drawn lines
|
||||
// are wrong by 1 pixel
|
||||
draw++;
|
||||
}
|
||||
|
||||
if (drawTranslated % (10 * scaleFactor) == 0) {
|
||||
g2d.drawLine(draw, SIZE - 1, draw, SIZE - 4);
|
||||
}
|
||||
|
||||
if (drawTranslated % (50 * scaleFactor) == 0) {
|
||||
g2d.drawLine(draw, SIZE - 1, draw, SIZE - 7);
|
||||
}
|
||||
|
||||
if (drawTranslated % (100 * scaleFactor) == 0) {
|
||||
g2d.drawLine(draw, SIZE - 1, draw, SIZE - 11);
|
||||
int value = (int) (drawTranslated / scaleFactor) * (int) horizontalRulerScale;
|
||||
String textValue = Integer.toString(value);
|
||||
if (scaleFactor < 0.06) {
|
||||
if (value % 5000 == 0) {
|
||||
g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
|
||||
}
|
||||
}
|
||||
else if ((draw - drawOld) < 31) {
|
||||
if (value % 500 == 0) {
|
||||
g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
|
||||
}
|
||||
}
|
||||
else {
|
||||
g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
|
||||
}
|
||||
|
||||
drawOld = draw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated value, considering current translation of the view.
|
||||
*
|
||||
* @param i The value.
|
||||
* @return The translated value.
|
||||
*/
|
||||
private int translateValue(int i, Point translation) {
|
||||
if (translation.x < 0) {
|
||||
return i + translation.x;
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals("scaleFactor")) {
|
||||
scaleFactor = (double) evt.getNewValue();
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
setPreferredWidth(drawingView.getComponent().getWidth());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originLocationChanged(EventObject evt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originScaleChanged(EventObject evt) {
|
||||
if (evt.getSource() instanceof Origin) {
|
||||
Origin origin = (Origin) evt.getSource();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
horizontalRulerScale = origin.getScaleX();
|
||||
repaint();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A vertical ruler.
|
||||
*/
|
||||
public static class Vertical
|
||||
extends
|
||||
Ruler {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param drawingView The drawing view.
|
||||
*/
|
||||
public Vertical(DrawingView drawingView) {
|
||||
super(drawingView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new height of the ruler and repaints it.
|
||||
*
|
||||
* @param preferredHeight The new height.
|
||||
*/
|
||||
public void setPreferredHeight(int preferredHeight) {
|
||||
setPreferredSize(new Dimension(SIZE, preferredHeight));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
Rectangle drawHere = g.getClipBounds();
|
||||
Point translation = new Point(
|
||||
(int) -drawingView.getDrawingToViewTransform().getTranslateX(),
|
||||
(int) -drawingView.getDrawingToViewTransform().getTranslateY()
|
||||
);
|
||||
// If we scroll downwards, the translation isn't incremented by default.
|
||||
// We use the translation of the visible rectangle instead.
|
||||
if (translation.y == STANDARD_TRANSLATION) {
|
||||
translation.y = drawingView.getComponent().getVisibleRect().y + STANDARD_TRANSLATION;
|
||||
}
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
// i translated
|
||||
int translated;
|
||||
// i normalized to decimal
|
||||
int draw;
|
||||
int drawOld = 0;
|
||||
// draw translated
|
||||
int drawTranslated;
|
||||
String translatedAsString;
|
||||
String lastIndex;
|
||||
|
||||
// base line
|
||||
g2d.drawLine(
|
||||
SIZE - 1, 0,
|
||||
SIZE - 1, getHeight()
|
||||
);
|
||||
|
||||
// Rotate the font for vertical axis
|
||||
AffineTransform fontAT = new AffineTransform();
|
||||
fontAT.rotate(270 * java.lang.Math.PI / 180);
|
||||
Font font = g2d.getFont().deriveFont(fontAT);
|
||||
g2d.setFont(font);
|
||||
|
||||
for (int i = drawHere.y; i < getHeight(); i += 10) {
|
||||
translated = translateValue(i, translation);
|
||||
translatedAsString = Integer.toString(translated);
|
||||
lastIndex = translatedAsString.substring(translatedAsString.length() - 1);
|
||||
int decimal = Integer.parseInt(lastIndex);
|
||||
|
||||
{
|
||||
// These steps are neccessary to guarantee lines are drawn
|
||||
// at every pixel. It always rounds i to a decimal, so the modulo
|
||||
// operators work
|
||||
draw = i;
|
||||
|
||||
if (translated < 0) {
|
||||
draw += decimal;
|
||||
}
|
||||
else {
|
||||
draw -= decimal;
|
||||
}
|
||||
|
||||
drawTranslated = translateValue(draw, translation);
|
||||
draw++;
|
||||
}
|
||||
|
||||
if (drawTranslated % (10 * scaleFactor) == 0) {
|
||||
g2d.drawLine(SIZE - 1, draw, SIZE - 4, draw);
|
||||
}
|
||||
|
||||
if (drawTranslated % (50 * scaleFactor) == 0) {
|
||||
g2d.drawLine(SIZE - 1, draw, SIZE - 7, draw);
|
||||
}
|
||||
|
||||
if (drawTranslated % (100 * scaleFactor) == 0) {
|
||||
g2d.drawLine(SIZE - 1, draw, SIZE - 11, draw);
|
||||
int value = -(int) (drawTranslated / scaleFactor) * (int) verticalRulerScale;
|
||||
String textValue = Integer.toString(value);
|
||||
|
||||
if (scaleFactor < 0.06) {
|
||||
if (value % 5000 == 0) {
|
||||
g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
|
||||
}
|
||||
}
|
||||
else if ((draw - drawOld) < 31) {
|
||||
if (value % 500 == 0) {
|
||||
g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
|
||||
}
|
||||
}
|
||||
else {
|
||||
g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
|
||||
}
|
||||
|
||||
drawOld = draw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated value, considering current translation of the view.
|
||||
*
|
||||
* @param i The value.
|
||||
* @return The translated value.
|
||||
*/
|
||||
private int translateValue(int i, Point translation) {
|
||||
if (translation.y < 0) {
|
||||
return i + translation.y;
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals("scaleFactor")) {
|
||||
scaleFactor = (double) evt.getNewValue();
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
setPreferredHeight(drawingView.getComponent().getHeight());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originLocationChanged(EventObject evt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originScaleChanged(EventObject evt) {
|
||||
if (evt.getSource() instanceof Origin) {
|
||||
Origin origin = (Origin) evt.getSource();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
verticalRulerScale = origin.getScaleY();
|
||||
repaint();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Stroke;
|
||||
|
||||
/**
|
||||
* Strokes used in the drawing.
|
||||
*/
|
||||
public class Strokes {
|
||||
|
||||
/**
|
||||
* Decoration of paths, points and locations that are part of a block.
|
||||
*/
|
||||
public static final Stroke BLOCK_ELEMENT = new BasicStroke(4.0f);
|
||||
/**
|
||||
* Decoration of paths that are part of a transport order.
|
||||
*/
|
||||
public static final Stroke PATH_ON_ROUTE
|
||||
= new BasicStroke(
|
||||
6.0f,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER,
|
||||
10.0f,
|
||||
new float[]{10.0f, 5.0f},
|
||||
0.0f
|
||||
);
|
||||
/**
|
||||
* Decoration of paths that are part of a withdrawn transport order.
|
||||
*/
|
||||
public static final Stroke PATH_ON_WITHDRAWN_ROUTE
|
||||
= new BasicStroke(
|
||||
6.0f,
|
||||
BasicStroke.CAP_BUTT,
|
||||
BasicStroke.JOIN_MITER,
|
||||
10.0f,
|
||||
new float[]{8.0f, 4.0f, 2.0f, 4.0f},
|
||||
0.0f
|
||||
);
|
||||
|
||||
/**
|
||||
* Prevents instantiation of this utility class.
|
||||
*/
|
||||
private Strokes() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
/**
|
||||
* An item to show in a combo box.
|
||||
*/
|
||||
public class ZoomItem {
|
||||
|
||||
private final double scaleFactor;
|
||||
|
||||
public ZoomItem(double scaleFactor) {
|
||||
this.scaleFactor = scaleFactor;
|
||||
}
|
||||
|
||||
public double getScaleFactor() {
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d %%", (int) (scaleFactor * 100));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Represents an exact point that won't change when zooming the model.
|
||||
*/
|
||||
public class ZoomPoint
|
||||
implements
|
||||
Serializable {
|
||||
|
||||
/**
|
||||
* The x position with a scale of 1.
|
||||
*/
|
||||
protected double fX;
|
||||
/**
|
||||
* The y position with a scale of 1.
|
||||
*/
|
||||
protected double fY;
|
||||
/**
|
||||
* The current scale.
|
||||
*/
|
||||
protected double fScale;
|
||||
|
||||
/**
|
||||
* Creates a new instance of ZoomPoint
|
||||
*/
|
||||
public ZoomPoint() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param x The x position for this point.
|
||||
* @param y The y position for this point.
|
||||
*/
|
||||
public ZoomPoint(double x, double y) {
|
||||
this(x, y, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param x The x position for this point.
|
||||
* @param y The y position for this point.
|
||||
* @param scale The current scale.
|
||||
*/
|
||||
public ZoomPoint(double x, double y, double scale) {
|
||||
fX = x / scale;
|
||||
fY = y / scale;
|
||||
fScale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current scale.
|
||||
*/
|
||||
public double scale() {
|
||||
return fScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x position.
|
||||
*
|
||||
* @param x the x position.
|
||||
*/
|
||||
public void setX(double x) {
|
||||
fX = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the y position.
|
||||
*
|
||||
* @param y the y position.
|
||||
*/
|
||||
public void setY(double y) {
|
||||
fY = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x position.
|
||||
*
|
||||
* @return the x position.
|
||||
*/
|
||||
public double getX() {
|
||||
return fX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y position.
|
||||
*
|
||||
* @return the y position.
|
||||
*/
|
||||
public double getY() {
|
||||
return fY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a point with the position.
|
||||
*
|
||||
* @return a point with the position.
|
||||
*/
|
||||
public Point getPixelLocation() {
|
||||
int x = (int) (getX() * scale());
|
||||
int y = (int) (getY() * scale());
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact position of the point in pixels with the current zoom level.
|
||||
*
|
||||
* @return the exact position.
|
||||
*/
|
||||
public Point2D getPixelLocationExactly() {
|
||||
double x = getX() * scale();
|
||||
double y = getY() * scale();
|
||||
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that the scale has changed.
|
||||
*
|
||||
* @param scale The new scale factor.
|
||||
*/
|
||||
public void scaleChanged(double scale) {
|
||||
fScale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that the point has been moved by the user.
|
||||
*
|
||||
* @param x The x-coordinate in Pixel.
|
||||
* @param y The y-coordinate in Pixel.
|
||||
*/
|
||||
public void movedByMouse(int x, int y) {
|
||||
fX = x / scale();
|
||||
fY = y / scale();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
/**
|
||||
* A drawing method where the position of a figure and the real position are in relation
|
||||
* to each other.
|
||||
*/
|
||||
public class CoordinateBasedDrawingMethod
|
||||
implements
|
||||
DrawingMethod {
|
||||
|
||||
/**
|
||||
* The origin point.
|
||||
*/
|
||||
protected Origin fOrigin;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public CoordinateBasedDrawingMethod() {
|
||||
fOrigin = new Origin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Origin getOrigin() {
|
||||
return fOrigin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A strategy that can translate pixel coordinates to real coordinates.
|
||||
*/
|
||||
public interface CoordinateSystem
|
||||
extends
|
||||
Serializable {
|
||||
|
||||
/**
|
||||
* Translates the real coordinate into a pixel coordinate.
|
||||
*
|
||||
* @param refPointLocation The current position of the reference point.
|
||||
* @param realValue The real position to translate.
|
||||
* @param relationX The amount of mm for one pixel in the x axis.
|
||||
* @param relationY The amount of mm for one pixel in the y axis.
|
||||
* @return A point with the pixel coordinates.
|
||||
*/
|
||||
Point2D toPixel(Point refPointLocation, Point2D realValue, double relationX, double relationY);
|
||||
|
||||
/**
|
||||
* Translates the pixel coordinate into a real coordinate.
|
||||
*
|
||||
* @param refPointLocation The current position of the reference point.
|
||||
* @param pixelValue The pixel coordinate position to translate.
|
||||
* @param relationX The amount of mm for one pixel in the x axis.
|
||||
* @param relationY The amount of mm for one pixel in the y axis.
|
||||
* @return A point with the real position.
|
||||
*/
|
||||
Point2D toReal(Point refPointLocation, Point pixelValue, double relationX, double relationY);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
/**
|
||||
* An interface for drawing methods. Possible drawing methods are:
|
||||
* <p>
|
||||
* <ul> <li> symbolic: No relation between the real position and the position of the figure.
|
||||
* <li> coordinate based: The position of the figure is the exact real position. </ul>
|
||||
*/
|
||||
public interface DrawingMethod {
|
||||
|
||||
/**
|
||||
* Returns the origin point.
|
||||
*
|
||||
* @return the origin point.
|
||||
*/
|
||||
Origin getOrigin();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
/**
|
||||
* A coordinate system strategy that converts pixel coordinates into real coordinates.
|
||||
*/
|
||||
public class NormalCoordinateSystem
|
||||
implements
|
||||
CoordinateSystem {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public NormalCoordinateSystem() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D toPixel(Point refPointLocation, Point2D realValue, double scaleX, double scaleY) {
|
||||
double xPixel = realValue.getX() / scaleX;
|
||||
double yPixel = realValue.getY() / scaleY;
|
||||
return new Point2D.Double(refPointLocation.x + xPixel, -(refPointLocation.y + yPixel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D toReal(Point refPointLocation, Point pixelValue, double scaleX, double scaleY) {
|
||||
int xDiff = pixelValue.x - refPointLocation.x;
|
||||
int yDiff = pixelValue.y - refPointLocation.y;
|
||||
return new Point2D.Double(scaleX * xDiff, -scaleY * yDiff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.EventObject;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.opentcs.guing.base.components.properties.type.LengthProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.StringProperty;
|
||||
import org.opentcs.guing.common.components.drawing.figures.OriginFigure;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The origin of the coordinate system. Represents the current scale, coordinate system and
|
||||
* position of the origin on the screen.
|
||||
*/
|
||||
public final class Origin {
|
||||
|
||||
/**
|
||||
* Scale (in mm per pixel) of the layout.
|
||||
*/
|
||||
public static final double DEFAULT_SCALE = 50.0;
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Origin.class);
|
||||
/**
|
||||
* Amount of mm to equal one pixel on screen in horizontal direction.
|
||||
*/
|
||||
private double fScaleX = DEFAULT_SCALE;
|
||||
/**
|
||||
* Amount of mm to equal one pixel on screen in horizontal direction.
|
||||
*/
|
||||
private double fScaleY = DEFAULT_SCALE;
|
||||
/**
|
||||
* Current position in pixels.
|
||||
*/
|
||||
private Point fPosition;
|
||||
/**
|
||||
* The coordinate system.
|
||||
*/
|
||||
private CoordinateSystem fCoordinateSystem;
|
||||
/**
|
||||
* List of {@link OriginChangeListener}.
|
||||
*/
|
||||
private final Set<OriginChangeListener> fListeners = new HashSet<>();
|
||||
/**
|
||||
* Graphical figure to represent the origin.
|
||||
*/
|
||||
private final OriginFigure fFigure = new OriginFigure();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public Origin() {
|
||||
setCoordinateSystem(new NormalCoordinateSystem());
|
||||
fFigure.setModel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scale in millimeter per pixel.
|
||||
*/
|
||||
public void setScale(double scaleX, double scaleY) {
|
||||
if (fScaleX == scaleX && fScaleY == scaleY) {
|
||||
return;
|
||||
}
|
||||
fScaleX = scaleX;
|
||||
fScaleY = scaleY;
|
||||
notifyScaleChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the millimeter per pixel in horizontal direction.
|
||||
*
|
||||
* @return the millimeter per pixel in horizontal direction.
|
||||
*/
|
||||
public double getScaleX() {
|
||||
return fScaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the millimeter per pixel in vertical direction.
|
||||
*
|
||||
* @return the millimeter per pixel in vertical direction.
|
||||
*/
|
||||
public double getScaleY() {
|
||||
return fScaleY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the coordinate system.
|
||||
*/
|
||||
public void setCoordinateSystem(CoordinateSystem coordinateSystem) {
|
||||
fCoordinateSystem = coordinateSystem;
|
||||
notifyLocationChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the origin.
|
||||
*
|
||||
* @param position the position of the origin.
|
||||
*/
|
||||
public void setPosition(Point position) {
|
||||
fPosition = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current position of the origin.
|
||||
*
|
||||
* @return the current position of the origin.
|
||||
*/
|
||||
public Point getPosition() {
|
||||
return fPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the real coordinate into pixel coordinates.
|
||||
*
|
||||
* @param xReal The real x position.
|
||||
* @param yReal The real y position.
|
||||
* @return A point with the pixel position.
|
||||
*/
|
||||
public Point calculatePixelPosition(LengthProperty xReal, LengthProperty yReal) {
|
||||
Point2D pixelExact = calculatePixelPositionExactly(xReal, yReal);
|
||||
|
||||
return new Point((int) pixelExact.getX(), (int) pixelExact.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the real coordinate into pixel coordinates with double precision.
|
||||
*
|
||||
* @param xReal The real x position.
|
||||
* @param yReal The real y position.
|
||||
* @return A point with the pixel position with double precision.
|
||||
*/
|
||||
public Point2D calculatePixelPositionExactly(LengthProperty xReal, LengthProperty yReal) {
|
||||
Point2D realPosition = new Point2D.Double(
|
||||
xReal.getValueByUnit(LengthProperty.Unit.MM),
|
||||
yReal.getValueByUnit(LengthProperty.Unit.MM)
|
||||
);
|
||||
|
||||
Point2D pixelPosition = fCoordinateSystem.toPixel(fPosition, realPosition, fScaleX, fScaleY);
|
||||
|
||||
return pixelPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the real coordinate into pixel coordinates with double precision from
|
||||
* string properties.
|
||||
*
|
||||
* @param xReal The real x position.
|
||||
* @param yReal The real y position.
|
||||
* @return A point with the pixel position with double precision.
|
||||
*/
|
||||
public Point2D calculatePixelPositionExactly(StringProperty xReal, StringProperty yReal) {
|
||||
try {
|
||||
double xPos = Double.parseDouble(xReal.getText());
|
||||
double yPos = Double.parseDouble(yReal.getText());
|
||||
Point2D realPosition = new Point2D.Double(xPos, yPos);
|
||||
Point2D pixelPosition = fCoordinateSystem.toPixel(fPosition, realPosition, fScaleX, fScaleY);
|
||||
|
||||
return pixelPosition;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
LOG.info("Couldn't parse layout coordinates", e);
|
||||
return new Point2D.Double();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a pixel position into a real position and write to the length properties.
|
||||
*
|
||||
*
|
||||
* @param pixelPosition The pixel position to convert.
|
||||
* @param xReal The length property to write the x position to.
|
||||
* @param yReal The length property to write the y position to.
|
||||
* @return A point with the pixel position with double precision.
|
||||
*/
|
||||
public Point2D calculateRealPosition(
|
||||
Point pixelPosition, LengthProperty xReal,
|
||||
LengthProperty yReal
|
||||
) {
|
||||
Point2D realPosition = fCoordinateSystem.toReal(fPosition, pixelPosition, fScaleX, fScaleY);
|
||||
|
||||
LengthProperty.Unit unitX = xReal.getUnit();
|
||||
LengthProperty.Unit unitY = yReal.getUnit();
|
||||
|
||||
xReal.setValueAndUnit((int) realPosition.getX(), LengthProperty.Unit.MM);
|
||||
yReal.setValueAndUnit((int) realPosition.getY(), LengthProperty.Unit.MM);
|
||||
xReal.convertTo(unitX);
|
||||
yReal.convertTo(unitY);
|
||||
|
||||
return realPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a pixel position onto a real position.
|
||||
*
|
||||
* @param pixelPosition The pixel position to convert.
|
||||
* @return A point with the pixel position with double precision.
|
||||
*/
|
||||
public Point2D calculateRealPosition(Point pixelPosition) {
|
||||
Point2D realPosition = fCoordinateSystem.toReal(fPosition, pixelPosition, fScaleX, fScaleY);
|
||||
|
||||
return realPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an origin change listener.
|
||||
*
|
||||
* @param l The origin change listener to add.
|
||||
*/
|
||||
public void addListener(OriginChangeListener l) {
|
||||
fListeners.add(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an origin change listener.
|
||||
*
|
||||
* @param l The origin change listener to remove.
|
||||
*/
|
||||
public void removeListener(OriginChangeListener l) {
|
||||
fListeners.remove(l);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Tests whether a specific origin change listener is registerd.
|
||||
*
|
||||
* @param l The origin change listener to test for.
|
||||
* @return <code> true </code>, if the listener is registerd.
|
||||
*/
|
||||
public boolean containsListener(OriginChangeListener l) {
|
||||
return fListeners.contains(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered listeners that the position of the origin has changed.
|
||||
*/
|
||||
public void notifyLocationChanged() {
|
||||
for (OriginChangeListener l : fListeners) {
|
||||
l.originLocationChanged(new EventObject(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered listeners that the scale has changed.
|
||||
*/
|
||||
public void notifyScaleChanged() {
|
||||
for (OriginChangeListener l : fListeners) {
|
||||
l.originScaleChanged(new EventObject(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the graphical representation of the origin.
|
||||
*
|
||||
* @return The graphical representation of the origin.
|
||||
*/
|
||||
public OriginFigure getFigure() {
|
||||
return fFigure;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.course;
|
||||
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
* Interface for classes that want to be notified about origin position changes
|
||||
* and origin scale changes.
|
||||
*/
|
||||
public interface OriginChangeListener {
|
||||
|
||||
/**
|
||||
* Event that the position of the origin has changed.
|
||||
*
|
||||
* @param evt event that the position has changed.
|
||||
*/
|
||||
void originLocationChanged(EventObject evt);
|
||||
|
||||
/**
|
||||
* Event that the scale of the origin has changed.
|
||||
*
|
||||
* @param evt event that the scale of the origin has changed.
|
||||
*/
|
||||
void originScaleChanged(EventObject evt);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.awt.image.ImageObserver.ABORT;
|
||||
import static java.awt.image.ImageObserver.ALLBITS;
|
||||
import static java.awt.image.ImageObserver.FRAMEBITS;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.jhotdraw.draw.AbstractAttributedDecoratedFigure;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A Figure displaying a bitmap.
|
||||
*/
|
||||
public class BitmapFigure
|
||||
extends
|
||||
AbstractAttributedDecoratedFigure
|
||||
implements
|
||||
ImageObserver {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BitmapFigure.class);
|
||||
/**
|
||||
* The image to be displayed.
|
||||
*/
|
||||
private BufferedImage image;
|
||||
/**
|
||||
* The enclosing rectangle.
|
||||
*/
|
||||
private Rectangle fDisplayBox;
|
||||
/**
|
||||
* The exact position of the figure's center.
|
||||
*/
|
||||
private ZoomPoint fZoomPoint;
|
||||
/**
|
||||
* Flag, if this figures has been removed from the drawing due to
|
||||
* an other view is active or if it visible.
|
||||
*/
|
||||
private boolean temporarilyRemoved = false;
|
||||
/**
|
||||
* Path of the image.
|
||||
*/
|
||||
private String imagePath;
|
||||
|
||||
@SuppressWarnings("this-escape")
|
||||
public BitmapFigure(File file) {
|
||||
try {
|
||||
image = ImageIO.read(file);
|
||||
imagePath = file.getPath();
|
||||
if (image == null) {
|
||||
LOG.error("Couldn't open image file at" + file.getPath());
|
||||
fDisplayBox = new Rectangle(0, 0, 0, 0);
|
||||
fZoomPoint = new ZoomPoint(0, 0);
|
||||
requestRemove();
|
||||
return;
|
||||
}
|
||||
fDisplayBox = new Rectangle(image.getWidth(), image.getHeight());
|
||||
fZoomPoint = new ZoomPoint(0.5 * image.getWidth(), 0.5 * image.getHeight());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
LOG.error("", ex);
|
||||
requestRemove();
|
||||
}
|
||||
}
|
||||
|
||||
public String getImagePath() {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
public boolean isTemporarilyRemoved() {
|
||||
return temporarilyRemoved;
|
||||
}
|
||||
|
||||
public void setTemporarilyRemoved(boolean temporarilyRemoved) {
|
||||
this.temporarilyRemoved = temporarilyRemoved;
|
||||
}
|
||||
|
||||
public Rectangle displayBox() {
|
||||
return new Rectangle(
|
||||
fDisplayBox.x, fDisplayBox.y,
|
||||
fDisplayBox.width, fDisplayBox.height
|
||||
);
|
||||
}
|
||||
|
||||
public void setDisplayBox(Rectangle displayBox) {
|
||||
fDisplayBox = displayBox;
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
|
||||
//resize
|
||||
if (lead != null) {
|
||||
//anchor is upper left, lead lower right
|
||||
fDisplayBox.width = (int) (lead.x - anchor.x);
|
||||
fDisplayBox.height = (int) (lead.y - anchor.y);
|
||||
}
|
||||
else {
|
||||
fZoomPoint.setX(anchor.x);
|
||||
fZoomPoint.setY(anchor.y);
|
||||
fDisplayBox.x = (int) (anchor.x - 0.5 * fDisplayBox.width);
|
||||
fDisplayBox.y = (int) (anchor.y - 0.5 * fDisplayBox.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // ImageObserver
|
||||
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||
if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
return (infoflags & (ALLBITS | ABORT)) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean figureContains(Point2D.Double p) {
|
||||
return fDisplayBox.contains(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawFill(Graphics2D g) {
|
||||
if (image != null) {
|
||||
Rectangle r = displayBox();
|
||||
g.drawImage(image, r.x, r.y, r.width, r.height, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawStroke(Graphics2D g) {
|
||||
Rectangle r = displayBox();
|
||||
g.drawRect(r.x, r.y, r.width - 1, r.height - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D.Double getBounds() {
|
||||
Rectangle2D r2 = fDisplayBox.getBounds2D();
|
||||
Rectangle2D.Double r2d = new Rectangle2D.Double();
|
||||
r2d.setRect(r2);
|
||||
|
||||
return r2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransformRestoreData() {
|
||||
return fDisplayBox.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreTransformTo(Object restoreData) {
|
||||
Rectangle r = (Rectangle) restoreData;
|
||||
fDisplayBox.x = r.x;
|
||||
fDisplayBox.y = r.y;
|
||||
fDisplayBox.width = r.width;
|
||||
fDisplayBox.height = r.height;
|
||||
fZoomPoint.setX(r.x + 0.5 * r.width);
|
||||
fZoomPoint.setY(r.y + 0.5 * r.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(AffineTransform tx) {
|
||||
Point2D center = fZoomPoint.getPixelLocationExactly();
|
||||
setBounds((Point2D.Double) tx.transform(center, center), null);
|
||||
}
|
||||
|
||||
public void setScaleFactor(double oldValue, double newValue) {
|
||||
fDisplayBox.width = (int) (fDisplayBox.width / oldValue * newValue);
|
||||
fDisplayBox.height = (int) (fDisplayBox.height / oldValue * newValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import org.jhotdraw.draw.AttributeKey;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
|
||||
/**
|
||||
* Constants that are relevant to figures.
|
||||
*/
|
||||
public interface FigureConstants {
|
||||
|
||||
/**
|
||||
* Key for figures to access their models.
|
||||
*/
|
||||
AttributeKey<ModelComponent> MODEL = new AttributeKey<>("Model", ModelComponent.class);
|
||||
/**
|
||||
* Key for figures to access the origin.
|
||||
*/
|
||||
AttributeKey<Origin> ORIGIN = new AttributeKey<>("Origin", Origin.class);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import org.opentcs.guing.base.model.elements.LinkModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationModel;
|
||||
import org.opentcs.guing.base.model.elements.PathModel;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
|
||||
/**
|
||||
*/
|
||||
public interface FigureFactory {
|
||||
|
||||
PointFigure createPointFigure(PointModel model);
|
||||
|
||||
LabeledPointFigure createLabeledPointFigure(PointFigure figure);
|
||||
|
||||
LocationFigure createLocationFigure(LocationModel model);
|
||||
|
||||
LabeledLocationFigure createLabeledLocationFigure(LocationFigure figure);
|
||||
|
||||
PathConnection createPathConnection(PathModel model);
|
||||
|
||||
LinkConnection createLinkConnection(LinkModel model);
|
||||
|
||||
OffsetFigure createOffsetFigure();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
/**
|
||||
* Defines fixed ordinals for some figures.
|
||||
*/
|
||||
public interface FigureOrdinals {
|
||||
|
||||
/**
|
||||
* The layer ordinal to be used for the origin figure.
|
||||
* Note: Be cautious with this value. The ordinal for the default layer is 0 . So a value
|
||||
* of -1 for the origin figure should be enough. We can't really use e.g. Integer.MIN_VALUE as
|
||||
* this leads to unexpected behavior and exceptions when moving layers in the Model Editor
|
||||
* application (probably caused by FigureLayerComparator).
|
||||
*/
|
||||
int ORIGIN_FIGURE_ORDINAL = -1;
|
||||
/**
|
||||
* The layer ordinal to be used for vehicle figures.
|
||||
* Note: Be cautious with this value. We want vehicles to be on the uppermost layer. We can't
|
||||
* really use e.g. Integer.MAX_VALUE as this leads to unexpected behavior when showing/hiding
|
||||
* layers in the Operations Desk application (probably caused by FigureLayerComparator).
|
||||
*/
|
||||
int VEHICLE_FIGURE_ORDINAL = 1000000;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import org.jhotdraw.draw.AttributeKey;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.jhotdraw.draw.GraphicalCompositeFigure;
|
||||
import org.jhotdraw.draw.handle.BoundsOutlineHandle;
|
||||
import org.jhotdraw.draw.handle.DragHandle;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.handle.MoveHandle;
|
||||
import org.jhotdraw.draw.handle.ResizeHandleKit;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeListener;
|
||||
import org.opentcs.guing.common.components.drawing.course.OriginChangeListener;
|
||||
|
||||
/**
|
||||
* A figure that is labeled by another figure.
|
||||
*/
|
||||
public abstract class LabeledFigure
|
||||
extends
|
||||
GraphicalCompositeFigure
|
||||
implements
|
||||
AttributesChangeListener,
|
||||
OriginChangeListener {
|
||||
|
||||
/**
|
||||
* The figure of the label of this labeled figure.
|
||||
*/
|
||||
private TCSLabelFigure fLabel;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public LabeledFigure() {
|
||||
}
|
||||
|
||||
public void setLabel(TCSLabelFigure label) {
|
||||
add(0, label); // Allow only one label for each figure
|
||||
addFigureListener(label);
|
||||
label.setParent(this);
|
||||
fLabel = label;
|
||||
}
|
||||
|
||||
public TCSLabelFigure getLabel() {
|
||||
return fLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of the label.
|
||||
*
|
||||
* @param visible Indicates whether the label should be visible or not.
|
||||
*/
|
||||
public void setLabelVisible(boolean visible) {
|
||||
fLabel.setLabelVisible(visible);
|
||||
}
|
||||
|
||||
public abstract Shape getShape();
|
||||
|
||||
@Override
|
||||
public TCSFigure getPresentationFigure() {
|
||||
return (TCSFigure) super.getPresentationFigure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMouseClick(Point2D.Double p, MouseEvent evt, DrawingView view) {
|
||||
boolean ret = getPresentationFigure().handleMouseClick(p, evt, view);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed() {
|
||||
super.changed();
|
||||
updateModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
Collection<Handle> handles = new ArrayList<>();
|
||||
|
||||
switch (detailLevel) {
|
||||
case -1: // Mouse Moved
|
||||
handles.add(new BoundsOutlineHandle(getPresentationFigure(), false, true));
|
||||
break;
|
||||
|
||||
case 0: // Mouse clicked
|
||||
MoveHandle.addMoveHandles(this, handles);
|
||||
for (Figure child : getChildren()) {
|
||||
MoveHandle.addMoveHandles(child, handles);
|
||||
handles.add(new DragHandle(child));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1: // Double-Click
|
||||
ResizeHandleKit.addResizeHandles(this, handles);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(AttributeKey<T> key, T newValue) {
|
||||
super.set(key, newValue);
|
||||
|
||||
if (fLabel != null) {
|
||||
fLabel.set(key, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
|
||||
basicSetPresentationFigureBounds(anchor, anchor);
|
||||
|
||||
if (fLabel != null) {
|
||||
Point2D.Double p = getStartPoint();
|
||||
p.x += fLabel.getOffset().x;
|
||||
p.y += fLabel.getOffset().y;
|
||||
fLabel.setBounds(p, p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originLocationChanged(EventObject event) {
|
||||
updateModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void originScaleChanged(EventObject event) {
|
||||
scaleModel(event);
|
||||
}
|
||||
|
||||
public abstract void updateModel();
|
||||
|
||||
/**
|
||||
* Scales the model coodinates accodring to changes to the layout scale.
|
||||
*
|
||||
* @param event The event containing the layout scale change.
|
||||
*/
|
||||
public abstract void scaleModel(EventObject event);
|
||||
|
||||
@Override
|
||||
public LabeledFigure clone() {
|
||||
LabeledFigure clone = (LabeledFigure) super.clone();
|
||||
clone.fLabel = null;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import javax.swing.Action;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
|
||||
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.StringProperty;
|
||||
import org.opentcs.guing.base.model.elements.LocationModel;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
|
||||
/**
|
||||
* {@link LocationFigure} with a label.
|
||||
*/
|
||||
public class LabeledLocationFigure
|
||||
extends
|
||||
LabeledFigure {
|
||||
|
||||
/**
|
||||
* The tool tip text generator.
|
||||
*/
|
||||
private final ToolTipTextGenerator textGenerator;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param figure The presentation figure.
|
||||
* @param textGenerator The tool tip text generator.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public LabeledLocationFigure(
|
||||
@Assisted
|
||||
LocationFigure figure,
|
||||
ToolTipTextGenerator textGenerator
|
||||
) {
|
||||
requireNonNull(figure, "figure");
|
||||
this.textGenerator = requireNonNull(textGenerator, "textGenerator");
|
||||
|
||||
setPresentationFigure(figure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationFigure getPresentationFigure() {
|
||||
return (LocationFigure) super.getPresentationFigure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
return getPresentationFigure().getDrawingArea();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(Point2D.Double p) {
|
||||
return textGenerator.getToolTipText(getPresentationFigure().getModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabeledLocationFigure clone() {
|
||||
// Do NOT clone the label here.
|
||||
LabeledLocationFigure that = (LabeledLocationFigure) super.clone();
|
||||
|
||||
if (that.getChildCount() > 0) {
|
||||
that.removeChild(0);
|
||||
}
|
||||
|
||||
LocationFigure thatPresentationFigure = that.getPresentationFigure();
|
||||
thatPresentationFigure.addFigureListener(that.eventHandler);
|
||||
// Force loading of the symbol bitmap
|
||||
thatPresentationFigure.propertiesChanged(null);
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Action> getActions(Point2D.Double p) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
if (!isVisible()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return super.createHandles(detailLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return getPresentationFigure().getLayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return getPresentationFigure().isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertiesChanged(AttributesChangeEvent event) {
|
||||
if (event.getInitiator().equals(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the figure if the model coordinates have been changed in the
|
||||
// Properties panel
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
|
||||
if (origin != null) {
|
||||
LocationFigure lf = getPresentationFigure();
|
||||
|
||||
if (lf.getModel().getPropertyLayoutPositionX().hasChanged()
|
||||
|| lf.getModel().getPropertyLayoutPositionY().hasChanged()) {
|
||||
getLabel().willChange();
|
||||
Point2D exact
|
||||
= origin.calculatePixelPositionExactly(
|
||||
lf.getModel().getPropertyLayoutPositionX(),
|
||||
lf.getModel().getPropertyLayoutPositionY()
|
||||
);
|
||||
double scale = lf.getZoomPoint().scale();
|
||||
double xNew = exact.getX() / scale;
|
||||
double yNew = exact.getY() / scale;
|
||||
Point2D.Double anchor = new Point2D.Double(xNew, yNew);
|
||||
setBounds(anchor, anchor);
|
||||
getLabel().changed();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the image of the actual Location type
|
||||
getPresentationFigure().propertiesChanged(event);
|
||||
|
||||
invalidate();
|
||||
// also update the label.
|
||||
fireFigureChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleModel(EventObject event) {
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
|
||||
if (origin != null) {
|
||||
LocationFigure lf = getPresentationFigure();
|
||||
|
||||
Point2D exact
|
||||
= origin.calculatePixelPositionExactly(
|
||||
lf.getModel().getPropertyLayoutPositionX(),
|
||||
lf.getModel().getPropertyLayoutPositionY()
|
||||
);
|
||||
Point2D.Double anchor = new Point2D.Double(exact.getX(), exact.getY());
|
||||
setBounds(anchor, anchor);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
// also update the label.
|
||||
fireFigureChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModel() {
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
LocationFigure lf = getPresentationFigure();
|
||||
LocationModel model = lf.getModel();
|
||||
CoordinateProperty cpx = model.getPropertyModelPositionX();
|
||||
CoordinateProperty cpy = model.getPropertyModelPositionY();
|
||||
if ((double) cpx.getValue() == 0.0 && (double) cpy.getValue() == 0.0) {
|
||||
origin.calculateRealPosition(lf.center(), cpx, cpy);
|
||||
cpx.markChanged();
|
||||
cpy.markChanged();
|
||||
}
|
||||
ZoomPoint zoomPoint = lf.getZoomPoint();
|
||||
if (zoomPoint != null && origin != null) {
|
||||
StringProperty lpx = model.getPropertyLayoutPositionX();
|
||||
int oldX = 0;
|
||||
|
||||
if (!Strings.isNullOrEmpty(lpx.getText())) {
|
||||
oldX = (int) Double.parseDouble(lpx.getText());
|
||||
}
|
||||
int newX = (int) (zoomPoint.getX() * origin.getScaleX());
|
||||
|
||||
if (newX != oldX || newX == 0) {
|
||||
lpx.setText(String.format("%d", newX));
|
||||
lpx.markChanged();
|
||||
}
|
||||
|
||||
StringProperty lpy = model.getPropertyLayoutPositionY();
|
||||
|
||||
int oldY = 0;
|
||||
if (!Strings.isNullOrEmpty(lpy.getText())) {
|
||||
oldY = (int) Double.parseDouble(lpy.getText());
|
||||
}
|
||||
|
||||
int newY = (int) (-zoomPoint.getY() * origin.getScaleY()); // Vorzeichen!
|
||||
|
||||
if (newY != oldY || newY == 0) {
|
||||
lpy.setText(String.format("%d", newY));
|
||||
lpy.markChanged();
|
||||
}
|
||||
|
||||
// Offset of the labels of the location will be updated here.
|
||||
StringProperty propOffsetX = model.getPropertyLabelOffsetX();
|
||||
if (Strings.isNullOrEmpty(propOffsetX.getText())) {
|
||||
propOffsetX.setText(String.format("%d", TCSLabelFigure.DEFAULT_LABEL_OFFSET_X));
|
||||
}
|
||||
|
||||
StringProperty propOffsetY = model.getPropertyLabelOffsetY();
|
||||
if (Strings.isNullOrEmpty(propOffsetY.getText())) {
|
||||
propOffsetY.setText(String.format("%d", TCSLabelFigure.DEFAULT_LABEL_OFFSET_Y));
|
||||
}
|
||||
|
||||
}
|
||||
// update the type.
|
||||
model.getPropertyType().markChanged();
|
||||
|
||||
model.propertiesChanged(this);
|
||||
// also update the label.
|
||||
fireFigureChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import javax.swing.Action;
|
||||
import org.jhotdraw.draw.ConnectionFigure;
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.jhotdraw.draw.connector.ChopEllipseConnector;
|
||||
import org.jhotdraw.draw.connector.Connector;
|
||||
import org.jhotdraw.draw.handle.DragHandle;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.handle.MoveHandle;
|
||||
import org.jhotdraw.draw.handle.ResizeHandleKit;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
|
||||
import org.opentcs.guing.base.components.properties.type.CoordinateProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.StringProperty;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
import org.opentcs.guing.common.components.drawing.figures.decoration.PointOutlineHandle;
|
||||
|
||||
/**
|
||||
* {@link PointFigure} with a label.
|
||||
*/
|
||||
public class LabeledPointFigure
|
||||
extends
|
||||
LabeledFigure {
|
||||
|
||||
/**
|
||||
* The tool tip text generator.
|
||||
*/
|
||||
private final ToolTipTextGenerator textGenerator;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param figure The presentation figure.
|
||||
* @param textGenerator The tool tip text generator.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public LabeledPointFigure(
|
||||
@Assisted
|
||||
PointFigure figure,
|
||||
ToolTipTextGenerator textGenerator
|
||||
) {
|
||||
requireNonNull(figure, "figure");
|
||||
this.textGenerator = requireNonNull(textGenerator, "textGenerator");
|
||||
|
||||
setPresentationFigure(figure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PointFigure getPresentationFigure() {
|
||||
return (PointFigure) super.getPresentationFigure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getShape() {
|
||||
return getPresentationFigure().getShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connector findConnector(Point2D.Double p, ConnectionFigure prototype) {
|
||||
return new ChopEllipseConnector(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(Point2D.Double p) {
|
||||
return textGenerator.getToolTipText(getPresentationFigure().getModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabeledPointFigure clone() {
|
||||
// Do NOT clone the label here.
|
||||
LabeledPointFigure that = (LabeledPointFigure) super.clone();
|
||||
|
||||
if (that.getChildCount() > 0) {
|
||||
that.basicRemoveAllChildren();
|
||||
}
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Action> getActions(Point2D.Double p) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return getPresentationFigure().getLayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return getPresentationFigure().isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertiesChanged(AttributesChangeEvent event) {
|
||||
if (event.getInitiator().equals(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the figure if the model coordinates have been changed in the
|
||||
// Properties panel
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
|
||||
if (origin != null) {
|
||||
PointFigure pf = getPresentationFigure();
|
||||
|
||||
StringProperty xLayout = pf.getModel().getPropertyLayoutPosX();
|
||||
StringProperty yLayout = pf.getModel().getPropertyLayoutPosY();
|
||||
|
||||
if (xLayout.hasChanged() || yLayout.hasChanged()) {
|
||||
getLabel().willChange();
|
||||
Point2D exact = origin.calculatePixelPositionExactly(xLayout, yLayout);
|
||||
double scale = pf.getZoomPoint().scale();
|
||||
double xNew = exact.getX() / scale;
|
||||
double yNew = exact.getY() / scale;
|
||||
Point2D.Double anchor = new Point2D.Double(xNew, yNew);
|
||||
setBounds(anchor, anchor);
|
||||
getLabel().changed();
|
||||
}
|
||||
}
|
||||
|
||||
invalidate();
|
||||
fireFigureChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleModel(EventObject event) {
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
|
||||
if (origin != null) {
|
||||
PointFigure pf = getPresentationFigure();
|
||||
|
||||
Point2D exact = origin.calculatePixelPositionExactly(
|
||||
pf.getModel().getPropertyLayoutPosX(),
|
||||
pf.getModel().getPropertyLayoutPosY()
|
||||
);
|
||||
Point2D.Double anchor = new Point2D.Double(exact.getX(), exact.getY());
|
||||
setBounds(anchor, anchor);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
fireFigureChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModel() {
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
PointFigure pf = getPresentationFigure();
|
||||
PointModel model = pf.getModel();
|
||||
CoordinateProperty cpx = model.getPropertyModelPositionX();
|
||||
CoordinateProperty cpy = model.getPropertyModelPositionY();
|
||||
// Write current model position to properties once when creating the layout.
|
||||
if ((double) cpx.getValue() == 0.0 && (double) cpy.getValue() == 0.0) {
|
||||
origin.calculateRealPosition(pf.center(), cpx, cpy);
|
||||
cpx.markChanged();
|
||||
cpy.markChanged();
|
||||
}
|
||||
ZoomPoint zoomPoint = pf.getZoomPoint();
|
||||
if (zoomPoint != null && origin != null) {
|
||||
StringProperty lpx = model.getPropertyLayoutPosX();
|
||||
|
||||
int oldX = 0;
|
||||
if (!Strings.isNullOrEmpty(lpx.getText())) {
|
||||
oldX = (int) Double.parseDouble(lpx.getText());
|
||||
}
|
||||
|
||||
int newX = (int) (zoomPoint.getX() * origin.getScaleX());
|
||||
if (newX != oldX) {
|
||||
lpx.setText(String.format("%d", newX));
|
||||
lpx.markChanged();
|
||||
}
|
||||
|
||||
StringProperty lpy = model.getPropertyLayoutPosY();
|
||||
|
||||
int oldY = 0;
|
||||
if (!Strings.isNullOrEmpty(lpy.getText())) {
|
||||
oldY = (int) Double.parseDouble(lpy.getText());
|
||||
}
|
||||
|
||||
int newY = (int) (-zoomPoint.getY() * origin.getScaleY()); // Vorzeichen!
|
||||
if (newY != oldY) {
|
||||
lpy.setText(String.format("%d", newY));
|
||||
lpy.markChanged();
|
||||
}
|
||||
|
||||
// Offset of the labels of the location will be updated here.
|
||||
StringProperty propOffsetX = model.getPropertyPointLabelOffsetX();
|
||||
if (Strings.isNullOrEmpty(propOffsetX.getText())) {
|
||||
propOffsetX.setText(String.format("%d", TCSLabelFigure.DEFAULT_LABEL_OFFSET_X));
|
||||
}
|
||||
|
||||
StringProperty propOffsetY = model.getPropertyPointLabelOffsetY();
|
||||
if (Strings.isNullOrEmpty(propOffsetY.getText())) {
|
||||
propOffsetY.setText(String.format("%d", TCSLabelFigure.DEFAULT_LABEL_OFFSET_Y));
|
||||
}
|
||||
|
||||
}
|
||||
model.getPropertyType().markChanged();
|
||||
|
||||
model.propertiesChanged(this);
|
||||
fireFigureChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
Collection<Handle> handles = new ArrayList<>();
|
||||
|
||||
if (!isVisible()) {
|
||||
return handles;
|
||||
}
|
||||
|
||||
switch (detailLevel) {
|
||||
case -1: // Mouse Moved
|
||||
handles.add(new PointOutlineHandle(getPresentationFigure()));
|
||||
break;
|
||||
|
||||
case 0: // Mouse clicked
|
||||
MoveHandle.addMoveHandles(this, handles);
|
||||
for (Figure child : getChildren()) {
|
||||
MoveHandle.addMoveHandles(child, handles);
|
||||
handles.add(new DragHandle(child));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1: // Double-Click
|
||||
ResizeHandleKit.addResizeHandles(this, handles);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.jhotdraw.draw.connector.ChopEllipseConnector;
|
||||
import org.jhotdraw.draw.connector.Connector;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.base.model.elements.LinkModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationModel;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
|
||||
/**
|
||||
* A dashed line that connects a decision point with a location.
|
||||
*/
|
||||
public class LinkConnection
|
||||
extends
|
||||
SimpleLineConnection {
|
||||
|
||||
/**
|
||||
* The tool tip text generator.
|
||||
*/
|
||||
private final ToolTipTextGenerator textGenerator;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param model The model corresponding to this graphical object.
|
||||
* @param textGenerator The tool tip text generator.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public LinkConnection(
|
||||
@Assisted
|
||||
LinkModel model,
|
||||
ToolTipTextGenerator textGenerator
|
||||
) {
|
||||
super(model);
|
||||
this.textGenerator = requireNonNull(textGenerator, "textGenerator");
|
||||
|
||||
double[] dash = {5.0, 5.0};
|
||||
set(AttributeKeys.START_DECORATION, null);
|
||||
set(AttributeKeys.END_DECORATION, null);
|
||||
set(AttributeKeys.STROKE_WIDTH, 1.0);
|
||||
set(AttributeKeys.STROKE_CAP, BasicStroke.CAP_BUTT);
|
||||
set(AttributeKeys.STROKE_JOIN, BasicStroke.JOIN_MITER);
|
||||
set(AttributeKeys.STROKE_MITER_LIMIT, 1.0);
|
||||
set(AttributeKeys.STROKE_DASHES, dash);
|
||||
set(AttributeKeys.STROKE_DASH_PHASE, 0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkModel getModel() {
|
||||
return (LinkModel) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects two figures.
|
||||
*
|
||||
* @param point The point figure to connect.
|
||||
* @param location The location figure to connect.
|
||||
*/
|
||||
public void connect(LabeledPointFigure point, LabeledLocationFigure location) {
|
||||
Connector compConnector = new ChopEllipseConnector();
|
||||
Connector startConnector = point.findCompatibleConnector(compConnector, true);
|
||||
Connector endConnector = location.findCompatibleConnector(compConnector, true);
|
||||
|
||||
if (!canConnect(startConnector, endConnector)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setStartConnector(startConnector);
|
||||
setEndConnector(endConnector);
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public String getToolTipText(Point2D.Double p) {
|
||||
return textGenerator.getToolTipText(getModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConnect(Connector start) {
|
||||
return start.getOwner().get(FigureConstants.MODEL) instanceof LocationModel;
|
||||
}
|
||||
|
||||
@Override // SimpleLineConnection
|
||||
public boolean canConnect(Connector start, Connector end) {
|
||||
ModelComponent modelStart = start.getOwner().get(FigureConstants.MODEL);
|
||||
ModelComponent modelEnd = end.getOwner().get(FigureConstants.MODEL);
|
||||
|
||||
if (modelStart == null || modelEnd == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even though new links can now only be created starting from a location, we need this to
|
||||
// ensure backward campatibility for older models that may still have links with a point
|
||||
// as the start component. Otherwise those links would not be connected/drawn properly.
|
||||
if ((modelStart instanceof PointModel) && modelEnd instanceof LocationModel) {
|
||||
LocationModel location = (LocationModel) modelEnd;
|
||||
PointModel point = (PointModel) modelStart;
|
||||
|
||||
return !location.hasConnectionTo(point);
|
||||
}
|
||||
|
||||
if (modelStart instanceof LocationModel && (modelEnd instanceof PointModel)) {
|
||||
LocationModel location = (LocationModel) modelStart;
|
||||
PointModel point = (PointModel) modelEnd;
|
||||
|
||||
return !point.hasConnectionTo(location);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
if (!isVisible()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return super.createHandles(detailLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return getModel().getPropertyLayerWrapper().getValue().getLayer().getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return super.isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayer().isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayerGroup().isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleModel(EventObject event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkConnection clone() {
|
||||
LinkConnection clone = (LinkConnection) super.clone();
|
||||
clone.initConnectionFigure();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.guing.base.AllocationState.ALLOCATED;
|
||||
import static org.opentcs.guing.base.AllocationState.CLAIMED;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.jhotdraw.draw.ConnectionFigure;
|
||||
import org.jhotdraw.draw.connector.ChopEllipseConnector;
|
||||
import org.jhotdraw.draw.connector.Connector;
|
||||
import org.jhotdraw.geom.Geom;
|
||||
import org.opentcs.components.plantoverview.LocationTheme;
|
||||
import org.opentcs.data.model.TCSResourceReference;
|
||||
import org.opentcs.data.model.visualization.LocationRepresentation;
|
||||
import org.opentcs.guing.base.AllocationState;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
|
||||
import org.opentcs.guing.base.components.properties.type.SymbolProperty;
|
||||
import org.opentcs.guing.base.model.elements.BlockModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationTypeModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingOptions;
|
||||
import org.opentcs.guing.common.components.drawing.Strokes;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
|
||||
/**
|
||||
* A figure for locations.
|
||||
*/
|
||||
public class LocationFigure
|
||||
extends
|
||||
TCSFigure
|
||||
implements
|
||||
ImageObserver {
|
||||
|
||||
/**
|
||||
* The fill color for locked locations.
|
||||
*/
|
||||
private static final Color LOCKED_COLOR = new Color(255, 50, 50);
|
||||
/**
|
||||
* The image representing the location.
|
||||
*/
|
||||
private transient Image fImage;
|
||||
private int fWidth;
|
||||
private int fHeight;
|
||||
private final LocationTheme locationTheme;
|
||||
private final DrawingOptions drawingOptions;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param locationTheme The location theme to be used.
|
||||
* @param model The model corresponding to this graphical object.
|
||||
* @param drawingOptions The drawing options.
|
||||
*/
|
||||
@Inject
|
||||
public LocationFigure(
|
||||
LocationTheme locationTheme,
|
||||
@Assisted
|
||||
LocationModel model,
|
||||
DrawingOptions drawingOptions
|
||||
) {
|
||||
super(model);
|
||||
this.locationTheme = requireNonNull(locationTheme, "locationTheme");
|
||||
this.drawingOptions = requireNonNull(drawingOptions, "drawingOptions");
|
||||
|
||||
fWidth = 30;
|
||||
fHeight = 30;
|
||||
fDisplayBox = new Rectangle(fWidth, fHeight);
|
||||
fZoomPoint = new ZoomPoint(0.5 * fWidth, 0.5 * fHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationModel getModel() {
|
||||
return (LocationModel) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
public Point center() {
|
||||
return Geom.center(fDisplayBox);
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public Rectangle2D.Double getBounds() {
|
||||
Rectangle2D r2 = fDisplayBox.getBounds2D();
|
||||
Rectangle2D.Double r2d = new Rectangle2D.Double();
|
||||
r2d.setRect(r2);
|
||||
|
||||
return r2d;
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public Object getTransformRestoreData() {
|
||||
return fDisplayBox.clone();
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public void restoreTransformTo(Object restoreData) {
|
||||
Rectangle r = (Rectangle) restoreData;
|
||||
fDisplayBox.x = r.x;
|
||||
fDisplayBox.y = r.y;
|
||||
fDisplayBox.width = r.width;
|
||||
fDisplayBox.height = r.height;
|
||||
fZoomPoint.setX(r.x + 0.5 * r.width);
|
||||
fZoomPoint.setY(r.y + 0.5 * r.height);
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public void transform(AffineTransform tx) {
|
||||
Point2D center = getZoomPoint().getPixelLocationExactly();
|
||||
setBounds((Point2D.Double) tx.transform(center, center), null);
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
|
||||
fZoomPoint.setX(anchor.x);
|
||||
fZoomPoint.setY(anchor.y);
|
||||
fDisplayBox.x = (int) (anchor.x - 0.5 * fDisplayBox.width);
|
||||
fDisplayBox.y = (int) (anchor.y - 0.5 * fDisplayBox.height);
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public Connector findConnector(Point2D.Double p, ConnectionFigure prototype) {
|
||||
return new ChopEllipseConnector(this);
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public Connector findCompatibleConnector(Connector c, boolean isStartConnector) {
|
||||
return new ChopEllipseConnector(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return getModel().getPropertyLayerWrapper().getValue().getLayer().getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return super.isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayer().isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayerGroup().isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawFigure(Graphics2D g) {
|
||||
if (drawingOptions.isBlocksVisible()) {
|
||||
drawBlockDecoration(g);
|
||||
}
|
||||
drawRouteDecoration(g);
|
||||
|
||||
super.drawFigure(g);
|
||||
}
|
||||
|
||||
private void drawRouteDecoration(Graphics2D g) {
|
||||
for (Map.Entry<VehicleModel, AllocationState> entry : getModel().getAllocationStates()
|
||||
.entrySet()) {
|
||||
VehicleModel vehicleModel = entry.getKey();
|
||||
switch (entry.getValue()) {
|
||||
case CLAIMED:
|
||||
drawDecoration(
|
||||
g,
|
||||
Strokes.PATH_ON_ROUTE,
|
||||
transparentColor(vehicleModel.getDriveOrderColor(), 70)
|
||||
);
|
||||
break;
|
||||
case ALLOCATED:
|
||||
drawDecoration(g, Strokes.PATH_ON_ROUTE, vehicleModel.getDriveOrderColor());
|
||||
break;
|
||||
case ALLOCATED_WITHDRAWN:
|
||||
drawDecoration(g, Strokes.PATH_ON_WITHDRAWN_ROUTE, Color.GRAY);
|
||||
break;
|
||||
default:
|
||||
// Don't draw any decoration.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBlockDecoration(Graphics2D g) {
|
||||
for (BlockModel blockModel : getModel().getBlockModels()) {
|
||||
drawDecoration(g, Strokes.BLOCK_ELEMENT, transparentColor(blockModel.getColor(), 192));
|
||||
}
|
||||
}
|
||||
|
||||
private Color transparentColor(Color color, int alpha) {
|
||||
return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
|
||||
}
|
||||
|
||||
private void drawDecoration(Graphics2D g, Stroke stroke, Color color) {
|
||||
g.setStroke(stroke);
|
||||
g.setColor(color);
|
||||
g.draw(this.getDrawingArea());
|
||||
}
|
||||
|
||||
@Override // AbstractAttributedFigure
|
||||
protected void drawFill(Graphics2D g) {
|
||||
int dx;
|
||||
int dy;
|
||||
Rectangle r = displayBox();
|
||||
g.fillRect(r.x, r.y, r.width, r.height);
|
||||
|
||||
if (fImage != null) {
|
||||
dx = (r.width - fImage.getWidth(this)) / 2;
|
||||
dy = (r.height - fImage.getHeight(this)) / 2;
|
||||
g.drawImage(fImage, r.x + dx, r.y + dy, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // AbstractAttributedFigure
|
||||
protected void drawStroke(Graphics2D g) {
|
||||
Rectangle r = displayBox();
|
||||
g.drawRect(r.x, r.y, r.width - 1, r.height - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationFigure clone() {
|
||||
LocationFigure thatFigure = (LocationFigure) super.clone();
|
||||
thatFigure.setZoomPoint(new ZoomPoint(fZoomPoint.getX(), fZoomPoint.getY()));
|
||||
|
||||
return thatFigure;
|
||||
}
|
||||
|
||||
@Override // ImageObserver
|
||||
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||
if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
return (infoflags & (ALLBITS | ABORT)) == 0;
|
||||
}
|
||||
|
||||
public void propertiesChanged(AttributesChangeEvent e) {
|
||||
handleLocationTypeChanged();
|
||||
handleLocationLockChanged();
|
||||
}
|
||||
|
||||
private void handleLocationTypeChanged() {
|
||||
LocationTypeModel locationType = getModel().getLocationType();
|
||||
|
||||
if (locationType == null) {
|
||||
return;
|
||||
}
|
||||
if (getModel().getLocation() != null && locationType.getLocationType() != null) {
|
||||
fImage = locationTheme.getImageFor(getModel().getLocation(), locationType.getLocationType());
|
||||
}
|
||||
else {
|
||||
SymbolProperty pSymbol = getModel().getPropertyDefaultRepresentation();
|
||||
LocationRepresentation locationRepresentation = pSymbol.getLocationRepresentation();
|
||||
if (locationRepresentation == null
|
||||
|| locationRepresentation == LocationRepresentation.DEFAULT) {
|
||||
pSymbol = locationType.getPropertyDefaultRepresentation();
|
||||
locationRepresentation = pSymbol.getLocationRepresentation();
|
||||
fImage = locationTheme.getImageFor(locationRepresentation);
|
||||
}
|
||||
else {
|
||||
fImage = locationTheme.getImageFor(locationRepresentation);
|
||||
}
|
||||
}
|
||||
fWidth = Math.max(fImage.getWidth(this) + 10, 30);
|
||||
fHeight = Math.max(fImage.getHeight(this) + 10, 30);
|
||||
fDisplayBox.setSize(fWidth, fHeight);
|
||||
}
|
||||
|
||||
private void handleLocationLockChanged() {
|
||||
set(
|
||||
AttributeKeys.FILL_COLOR,
|
||||
(Boolean) getModel().getPropertyLocked().getValue() ? LOCKED_COLOR : Color.WHITE
|
||||
);
|
||||
}
|
||||
|
||||
private List<Set<TCSResourceReference<?>>> getCurrentDriveOrderClaim(VehicleModel vehicle) {
|
||||
List<Set<TCSResourceReference<?>>> result = new ArrayList<>();
|
||||
|
||||
boolean driveOrderEndFound = false;
|
||||
for (Set<TCSResourceReference<?>> res : vehicle.getClaimedResources().getItems()) {
|
||||
result.add(res);
|
||||
|
||||
if (containsDriveOrderDestination(res, vehicle)) {
|
||||
driveOrderEndFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driveOrderEndFound) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// With the end of the drive order not found, there is nothing from the current drive order in
|
||||
// the claimed resources.
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsDriveOrderDestination(
|
||||
Set<TCSResourceReference<?>> resources,
|
||||
VehicleModel vehicle
|
||||
) {
|
||||
if (vehicle.getDriveOrderDestination() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return resources.stream()
|
||||
.anyMatch(
|
||||
resource -> Objects.equals(
|
||||
resource.getName(),
|
||||
vehicle.getDriveOrderDestination().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.opentcs.guing.base.model.DrawnModelComponent;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
|
||||
/**
|
||||
* A figure that is based on/is a graphical representation for a {@link ModelComponent}.
|
||||
*/
|
||||
public interface ModelBasedFigure
|
||||
extends
|
||||
Figure {
|
||||
|
||||
/**
|
||||
* Returns the model component for this figure.
|
||||
*
|
||||
* @return The model component for this figure.
|
||||
*/
|
||||
DrawnModelComponent getModel();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
|
||||
/**
|
||||
* An OffsetFigure is an (invisible) figure that moves as the user drags the view
|
||||
* beyond its current bounds, so the view becomes larger resp is repainted
|
||||
* larger.
|
||||
*/
|
||||
public class OffsetFigure
|
||||
extends
|
||||
OriginFigure {
|
||||
|
||||
@SuppressWarnings("this-escape")
|
||||
public OffsetFigure() {
|
||||
super();
|
||||
setModel(new Origin()); // The figure needs a model to work
|
||||
set(AttributeKeys.STROKE_COLOR, Color.darkGray);
|
||||
setVisible(false); // only visible for test
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawStroke(Graphics2D g) {
|
||||
// Shape: "Crosshair" with square
|
||||
Rectangle r = (Rectangle) fDisplayBox.clone();
|
||||
|
||||
if (r.width > 0 && r.height > 0) {
|
||||
g.drawLine(r.x + r.width / 2, r.y, r.x + r.width / 2, r.y + r.height);
|
||||
g.drawLine(r.x, r.y + r.height / 2, r.x + r.width, r.y + r.height / 2);
|
||||
r.grow(-4, -4);
|
||||
g.drawRect(r.x, r.y, r.width, r.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import org.jhotdraw.draw.AbstractAttributedFigure;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.geom.Geom;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
|
||||
/**
|
||||
* A Figure for the coordinate system's origin.
|
||||
*/
|
||||
public class OriginFigure
|
||||
extends
|
||||
AbstractAttributedFigure {
|
||||
|
||||
/**
|
||||
* The enclosing rectangle.
|
||||
*/
|
||||
protected final Rectangle fDisplayBox;
|
||||
/**
|
||||
* The width and height.
|
||||
*/
|
||||
private final int fSideLength;
|
||||
/**
|
||||
* The origin's model.
|
||||
*/
|
||||
private Origin fModel;
|
||||
/**
|
||||
* The exact position of the figure.
|
||||
*/
|
||||
private final ZoomPoint fZoomPoint;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public OriginFigure() {
|
||||
super();
|
||||
fSideLength = 20;
|
||||
fZoomPoint = new ZoomPoint(0.0, 0.0);
|
||||
fDisplayBox = new Rectangle(
|
||||
-fSideLength / 2, -fSideLength / 2,
|
||||
fSideLength, fSideLength
|
||||
);
|
||||
set(AttributeKeys.STROKE_COLOR, Color.blue);
|
||||
setSelectable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the origin's model.
|
||||
*
|
||||
* @param model The model.
|
||||
*/
|
||||
public void setModel(Origin model) {
|
||||
fModel = model;
|
||||
getModel().setPosition(Geom.center(fDisplayBox));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the origin's model.
|
||||
*
|
||||
* @return The model.
|
||||
*/
|
||||
public Origin getModel() {
|
||||
return fModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact position of the origin.
|
||||
*
|
||||
* @return The exact position of the origin.
|
||||
*/
|
||||
public ZoomPoint getZoomPoint() {
|
||||
return fZoomPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D.Double getBounds() {
|
||||
Rectangle2D.Double r2d = new Rectangle2D.Double();
|
||||
r2d.setRect(fDisplayBox.getBounds2D());
|
||||
|
||||
return r2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Point2D.Double p) {
|
||||
Rectangle r = (Rectangle) fDisplayBox.clone();
|
||||
double grow = AttributeKeys.getPerpendicularHitGrowth(this);
|
||||
r.x = (int) (r.x - grow);
|
||||
r.y = (int) (r.y - grow);
|
||||
r.width = (int) (r.width + grow * 2);
|
||||
r.height = (int) (r.height + grow * 2);
|
||||
|
||||
return r.contains(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransformRestoreData() {
|
||||
return fDisplayBox.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreTransformTo(Object restoreData) {
|
||||
Rectangle r = (Rectangle) restoreData;
|
||||
fDisplayBox.x = r.x;
|
||||
fDisplayBox.y = r.y;
|
||||
fDisplayBox.width = r.width;
|
||||
fDisplayBox.height = r.height;
|
||||
fZoomPoint.setX(r.x + 0.5 * r.width);
|
||||
fZoomPoint.setY(r.y + 0.5 * r.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(AffineTransform tx) {
|
||||
Point2D center = getZoomPoint().getPixelLocationExactly();
|
||||
Point2D lead = new Point2D.Double(); // not used
|
||||
setBounds(
|
||||
(Point2D.Double) tx.transform(center, center),
|
||||
(Point2D.Double) tx.transform(lead, lead)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed() {
|
||||
super.changed();
|
||||
getModel().setPosition(Geom.center(fDisplayBox));
|
||||
getModel().notifyLocationChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
|
||||
// Only change the position here, NOT the size!
|
||||
// Draw the center of the figure at the mouse cursor's position.
|
||||
fZoomPoint.setX(anchor.x);
|
||||
fZoomPoint.setY(anchor.y);
|
||||
fDisplayBox.x = (int) (anchor.x - 0.5 * fSideLength);
|
||||
fDisplayBox.y = (int) (anchor.y - 0.5 * fSideLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
// No handles for the origin figure.
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawFill(Graphics2D g) {
|
||||
// No filling for the origin's figure.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawStroke(Graphics2D g) {
|
||||
// Outline: "Crosshair" with circle
|
||||
Rectangle r = (Rectangle) fDisplayBox.clone();
|
||||
|
||||
if (r.width > 0 && r.height > 0) {
|
||||
g.drawLine(r.x + r.width / 2, r.y, r.x + r.width / 2, r.y + r.height);
|
||||
g.drawLine(r.x, r.y + r.height / 2, r.x + r.width, r.y + r.height / 2);
|
||||
r.grow(-4, -4);
|
||||
g.drawOval(r.x, r.y, r.width, r.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return FigureOrdinals.ORIGIN_FIGURE_ORDINAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,945 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Point2D.Double;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import org.jhotdraw.draw.AttributeKey;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.jhotdraw.draw.connector.ChopEllipseConnector;
|
||||
import org.jhotdraw.draw.connector.Connector;
|
||||
import org.jhotdraw.draw.handle.BezierOutlineHandle;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.liner.ElbowLiner;
|
||||
import org.jhotdraw.draw.liner.Liner;
|
||||
import org.jhotdraw.draw.liner.SlantedLiner;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
import org.opentcs.data.model.TCSResourceReference;
|
||||
import org.opentcs.guing.base.AllocationState;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
|
||||
import org.opentcs.guing.base.components.properties.type.AbstractProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.LengthProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.SpeedProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.StringProperty;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.base.model.elements.BlockModel;
|
||||
import org.opentcs.guing.base.model.elements.PathModel;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingOptions;
|
||||
import org.opentcs.guing.common.components.drawing.Strokes;
|
||||
import org.opentcs.guing.common.components.drawing.course.Origin;
|
||||
import org.opentcs.guing.common.components.drawing.figures.liner.BezierLinerControlPointHandle;
|
||||
import org.opentcs.guing.common.components.drawing.figures.liner.PolyPathLiner;
|
||||
import org.opentcs.guing.common.components.drawing.figures.liner.TripleBezierLiner;
|
||||
import org.opentcs.guing.common.components.drawing.figures.liner.TupelBezierLiner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A connection between two points.
|
||||
*/
|
||||
public class PathConnection
|
||||
extends
|
||||
SimpleLineConnection {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PathConnection.class);
|
||||
/**
|
||||
* The dash pattern for locked paths.
|
||||
*/
|
||||
private static final double[] LOCKED_DASH = {6.0, 4.0};
|
||||
/**
|
||||
* The dash pattern for unlocked paths.
|
||||
*/
|
||||
private static final double[] UNLOCKED_DASH = {10.0, 0.0};
|
||||
/**
|
||||
* The tool tip text generator.
|
||||
*/
|
||||
private final ToolTipTextGenerator textGenerator;
|
||||
/**
|
||||
* The drawing options.
|
||||
*/
|
||||
private final DrawingOptions drawingOptions;
|
||||
/**
|
||||
* Control point 1.
|
||||
*/
|
||||
private Point2D.Double cp1;
|
||||
/**
|
||||
* Control point 2.
|
||||
*/
|
||||
private Point2D.Double cp2;
|
||||
/**
|
||||
* Control point 3.
|
||||
*/
|
||||
private Point2D.Double cp3;
|
||||
/**
|
||||
* Control point 4.
|
||||
*/
|
||||
private Point2D.Double cp4;
|
||||
/**
|
||||
* Control point 5.
|
||||
*/
|
||||
private Point2D.Double cp5;
|
||||
|
||||
private Origin previousOrigin;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param model The model corresponding to this graphical object.
|
||||
* @param textGenerator The tool tip text generator.
|
||||
* @param drawingOptions The drawing options.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public PathConnection(
|
||||
@Assisted
|
||||
PathModel model,
|
||||
ToolTipTextGenerator textGenerator,
|
||||
DrawingOptions drawingOptions
|
||||
) {
|
||||
super(model);
|
||||
this.textGenerator = requireNonNull(textGenerator, "textGenerator");
|
||||
this.drawingOptions = requireNonNull(drawingOptions, "drawingOptions");
|
||||
resetPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathModel getModel() {
|
||||
return (PathModel) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConnection() {
|
||||
super.updateConnection();
|
||||
initializePreviousOrigin();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets control points and connects start and end point with a straight line.
|
||||
*/
|
||||
private void resetPath() {
|
||||
Point2D.Double sp = path.get(0, BezierPath.C0_MASK);
|
||||
Point2D.Double ep = path.get(path.size() - 1, BezierPath.C0_MASK);
|
||||
|
||||
path.clear();
|
||||
path.add(new BezierPath.Node(sp));
|
||||
path.add(new BezierPath.Node(ep));
|
||||
cp1 = null;
|
||||
cp2 = null;
|
||||
cp3 = null;
|
||||
cp4 = null;
|
||||
cp5 = null;
|
||||
getModel().getPropertyPathControlPoints().markChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the control points when converting into BEZIER curve.
|
||||
*
|
||||
* @param type the type of the curve
|
||||
*/
|
||||
private void initControlPoints(PathModel.Type type) {
|
||||
Point2D.Double sp = path.get(0, BezierPath.C0_MASK);
|
||||
Point2D.Double ep = path.get(path.size() - 1, BezierPath.C0_MASK);
|
||||
|
||||
if (sp.x != ep.x || sp.y != ep.y) {
|
||||
path.clear();
|
||||
if (type == PathModel.Type.BEZIER_3) { //BEZIER curve with 3 control points);
|
||||
//Add the scaled vector between start and endpoint to the startpoint
|
||||
cp1 = new Point2D.Double(sp.x + (ep.x - sp.x) * 1 / 6, sp.y + (ep.y - sp.y) * 1 / 6);
|
||||
cp2 = new Point2D.Double(sp.x + (ep.x - sp.x) * 2 / 6, sp.y + (ep.y - sp.y) * 2 / 6);
|
||||
cp3 = new Point2D.Double(sp.x + (ep.x - sp.x) * 3 / 6, sp.y + (ep.y - sp.y) * 3 / 6);
|
||||
cp4 = new Point2D.Double(sp.x + (ep.x - sp.x) * 4 / 6, sp.y + (ep.y - sp.y) * 4 / 6);
|
||||
cp5 = new Point2D.Double(sp.x + (ep.x - sp.x) * 5 / 6, sp.y + (ep.y - sp.y) * 5 / 6);
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C2_MASK,
|
||||
sp.x, sp.y, //Current point
|
||||
sp.x, sp.y, //Previous point - not in use because of C2_MASK
|
||||
cp1.x, cp1.y
|
||||
)
|
||||
); //Next point
|
||||
//Use cp1 and cp2 to draw between sp and cp3
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1C2_MASK,
|
||||
cp3.x, cp3.y, //Current point
|
||||
cp2.x, cp2.y, //Previous point
|
||||
cp4.x, cp4.y
|
||||
)
|
||||
); //Next point
|
||||
//Use cp4 and cp5 to draw between cp3 and ep
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1_MASK,
|
||||
ep.x, ep.y, //Current point
|
||||
cp5.x, cp5.y, //Previous point
|
||||
ep.x, ep.y
|
||||
)
|
||||
); //Next point - not in use because of C1_MASK
|
||||
}
|
||||
else {
|
||||
cp1 = new Point2D.Double(sp.x + (ep.x - sp.x) / 3, sp.y + (ep.y - sp.y) / 3); //point at 1/3
|
||||
cp2 = new Point2D.Double(ep.x - (ep.x - sp.x) / 3, ep.y - (ep.y - sp.y) / 3); //point at 2/3
|
||||
cp3 = null;
|
||||
cp4 = null;
|
||||
cp5 = null;
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C2_MASK,
|
||||
sp.x, sp.y, //Current point
|
||||
sp.x, sp.y, //Previous point - not in use because of C2_MASK
|
||||
cp1.x, cp1.y
|
||||
)
|
||||
); //Next point
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1_MASK,
|
||||
ep.x, ep.y, //Current point
|
||||
cp2.x, cp2.y, //Previous point
|
||||
ep.x, ep.y
|
||||
)
|
||||
); //Next point - not in use because of C1_MASK
|
||||
}
|
||||
|
||||
getModel().getPropertyPathControlPoints().markChanged();
|
||||
path.invalidatePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add control points.
|
||||
*
|
||||
* @param cp1 First control point.
|
||||
* @param cp2 Identical with cp1 for quadratic curves.
|
||||
*/
|
||||
public void addControlPoints(Point2D.Double cp1, Point2D.Double cp2) {
|
||||
this.cp1 = cp1;
|
||||
this.cp2 = cp2;
|
||||
this.cp3 = null;
|
||||
Point2D.Double sp = path.get(0, BezierPath.C0_MASK);
|
||||
Point2D.Double ep = path.get(1, BezierPath.C0_MASK);
|
||||
path.clear();
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C2_MASK,
|
||||
sp.x, sp.y, //Current point
|
||||
sp.x, sp.y, //Previous point
|
||||
cp1.x, cp1.y
|
||||
)
|
||||
); //Next point
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1_MASK,
|
||||
ep.x, ep.y, //Current point
|
||||
cp2.x, cp2.y, //Previous point
|
||||
ep.x, ep.y
|
||||
)
|
||||
); //Next point
|
||||
}
|
||||
|
||||
/**
|
||||
* A bezier curve with three control points.
|
||||
*
|
||||
* @param cp1 Control point 1
|
||||
* @param cp2 Control point 2
|
||||
* @param cp3 Control point 3
|
||||
* @param cp4 Control point 4
|
||||
* @param cp5 Control point 5
|
||||
*/
|
||||
public void addControlPoints(
|
||||
Point2D.Double cp1,
|
||||
Point2D.Double cp2,
|
||||
Point2D.Double cp3,
|
||||
Point2D.Double cp4,
|
||||
Point2D.Double cp5
|
||||
) {
|
||||
this.cp1 = cp1;
|
||||
this.cp2 = cp2;
|
||||
this.cp3 = cp3;
|
||||
this.cp4 = cp4;
|
||||
this.cp5 = cp5;
|
||||
Point2D.Double sp = path.get(0, BezierPath.C0_MASK);
|
||||
Point2D.Double ep = path.get(path.size() - 1, BezierPath.C0_MASK);
|
||||
path.clear();
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C2_MASK,
|
||||
sp.x, sp.y, //Current point
|
||||
sp.x, sp.y, //Previous point
|
||||
cp1.x, cp1.y
|
||||
)
|
||||
); //Next point
|
||||
//Use cp1 and cp2 to draw between sp and cp3
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1C2_MASK,
|
||||
cp3.x, cp3.y, //Current point
|
||||
cp2.x, cp2.y, //Previous point
|
||||
cp4.x, cp4.y
|
||||
)
|
||||
); //Next point
|
||||
//Use cp4 and cp5 to draw between cp3 and ep
|
||||
path.add(
|
||||
new BezierPath.Node(
|
||||
BezierPath.C1_MASK,
|
||||
ep.x, ep.y, //Current point
|
||||
cp5.x, cp5.y, //Previous point
|
||||
cp4.x, cp4.y
|
||||
)
|
||||
); //Next point
|
||||
StringProperty sProp = getModel().getPropertyPathControlPoints();
|
||||
sProp.setText(
|
||||
String.format(
|
||||
"%d,%d;%d,%d;%d,%d;%d,%d;%d,%d;",
|
||||
(int) cp1.x, (int) cp1.y,
|
||||
(int) cp2.x, (int) cp2.y,
|
||||
(int) cp3.x, (int) cp3.y,
|
||||
(int) cp4.x, (int) cp4.y,
|
||||
(int) cp5.x, (int) cp5.y
|
||||
)
|
||||
);
|
||||
sProp.markChanged();
|
||||
getModel().propertiesChanged(this);
|
||||
}
|
||||
|
||||
public Point2D.Double getCp1() {
|
||||
return cp1;
|
||||
}
|
||||
|
||||
public Point2D.Double getCp2() {
|
||||
return cp2;
|
||||
}
|
||||
|
||||
public Point2D.Double getCp3() {
|
||||
return cp3;
|
||||
}
|
||||
|
||||
public Point2D.Double getCp4() {
|
||||
return cp4;
|
||||
}
|
||||
|
||||
public Point2D.Double getCp5() {
|
||||
return cp5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D.Double getCenter() {
|
||||
// Computes the center of the curve.
|
||||
// Approximation: Center of the control points.
|
||||
Point2D.Double p1;
|
||||
Point2D.Double p2;
|
||||
Point2D.Double pc;
|
||||
|
||||
p1 = (cp1 == null ? path.get(0, BezierPath.C0_MASK) : cp1);
|
||||
p2 = (cp2 == null ? path.get(1, BezierPath.C0_MASK) : cp2);
|
||||
if (cp3 == null) {
|
||||
pc = new Point2D.Double((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
|
||||
}
|
||||
else {
|
||||
//Use cp3 for 3-bezier as center because the curve goes through it at 50%
|
||||
pc = (cp3 == null ? path.get(3, BezierPath.C0_MASK) : cp3);
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return getModel().getPropertyLayerWrapper().getValue().getLayer().getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return super.isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayer().isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayerGroup().isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the previous origin which is used to scale the control points of this path.
|
||||
*/
|
||||
private void initializePreviousOrigin() {
|
||||
if (previousOrigin == null) {
|
||||
Origin origin = get(FigureConstants.ORIGIN);
|
||||
previousOrigin = new Origin();
|
||||
previousOrigin.setScale(origin.getScaleX(), origin.getScaleY());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update bezier and polypath control points.
|
||||
*/
|
||||
public void updateControlPoints() {
|
||||
String sControlPoints = "";
|
||||
if (getLinerType() == PathModel.Type.POLYPATH) {
|
||||
sControlPoints = updatePolyPathControlPoints();
|
||||
}
|
||||
else if (getLinerType() == PathModel.Type.BEZIER || getLinerType() == PathModel.Type.BEZIER_3) {
|
||||
sControlPoints = updateBezierControlPoints();
|
||||
}
|
||||
|
||||
StringProperty sProp = getModel().getPropertyPathControlPoints();
|
||||
sProp.setText(sControlPoints);
|
||||
invalidate();
|
||||
sProp.markChanged();
|
||||
getModel().propertiesChanged(this);
|
||||
}
|
||||
|
||||
private String updatePolyPathControlPoints() {
|
||||
StringJoiner rtn = new StringJoiner(";");
|
||||
for (int i = 1; i < path.size() - 1; i++) {
|
||||
Point2D.Double p = path.get(i, BezierPath.C0_MASK);
|
||||
rtn.add(String.format("%d,%d", (int) p.x, (int) p.y));
|
||||
}
|
||||
return rtn.toString();
|
||||
}
|
||||
|
||||
private String updateBezierControlPoints() {
|
||||
if (cp1 != null && cp2 != null) {
|
||||
if (cp3 != null) {
|
||||
cp1 = path.get(0, BezierPath.C2_MASK);
|
||||
cp2 = path.get(1, BezierPath.C1_MASK);
|
||||
cp3 = path.get(1, BezierPath.C0_MASK);
|
||||
cp4 = path.get(1, BezierPath.C2_MASK);
|
||||
cp5 = path.get(2, BezierPath.C1_MASK);
|
||||
}
|
||||
else {
|
||||
cp1 = path.get(0, BezierPath.C2_MASK);
|
||||
cp2 = path.get(1, BezierPath.C1_MASK);
|
||||
}
|
||||
}
|
||||
String sControlPoints = "";
|
||||
if (cp1 != null) {
|
||||
if (cp2 != null) {
|
||||
if (cp3 != null) {
|
||||
// Format: x1,y1;x2,y2;x3,y3;x4,y4;x5,y5
|
||||
sControlPoints = String.format(
|
||||
"%d,%d;%d,%d;%d,%d;%d,%d;%d,%d",
|
||||
(int) (cp1.x),
|
||||
(int) (cp1.y),
|
||||
(int) (cp2.x),
|
||||
(int) (cp2.y),
|
||||
(int) (cp3.x),
|
||||
(int) (cp3.y),
|
||||
(int) (cp4.x),
|
||||
(int) (cp4.y),
|
||||
(int) (cp5.x),
|
||||
(int) (cp5.y)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Format: x1,y1;x2,y2
|
||||
sControlPoints = String.format(
|
||||
"%d,%d;%d,%d", (int) (cp1.x),
|
||||
(int) (cp1.y), (int) (cp2.x),
|
||||
(int) (cp2.y)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Format: x1,y1
|
||||
sControlPoints = String.format("%d,%d", (int) (cp1.x), (int) (cp1.y));
|
||||
}
|
||||
}
|
||||
return sControlPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects two figures with this connection.
|
||||
*
|
||||
* @param start The first figure.
|
||||
* @param end The second figure.
|
||||
*/
|
||||
public void connect(LabeledPointFigure start, LabeledPointFigure end) {
|
||||
Connector compConnector = new ChopEllipseConnector();
|
||||
Connector startConnector = start.findCompatibleConnector(compConnector, true);
|
||||
Connector endConnector = end.findCompatibleConnector(compConnector, true);
|
||||
|
||||
if (!canConnect(startConnector, endConnector)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setStartConnector(startConnector);
|
||||
setEndConnector(endConnector);
|
||||
|
||||
getModel().setConnectedComponents(
|
||||
start.get(FigureConstants.MODEL),
|
||||
end.get(FigureConstants.MODEL)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this path.
|
||||
*
|
||||
* @return The type of this path
|
||||
*/
|
||||
public PathModel.Type getLinerType() {
|
||||
return (PathModel.Type) getModel().getPropertyPathConnType().getValue();
|
||||
}
|
||||
|
||||
public void setLinerByType(PathModel.Type type) {
|
||||
switch (type) {
|
||||
case DIRECT:
|
||||
resetPath();
|
||||
updateLiner(null);
|
||||
break;
|
||||
|
||||
case ELBOW:
|
||||
if (!(getLiner() instanceof ElbowLiner)) {
|
||||
resetPath();
|
||||
updateLiner(new ElbowLiner());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SLANTED:
|
||||
if (!(getLiner() instanceof SlantedLiner)) {
|
||||
resetPath();
|
||||
updateLiner(new SlantedLiner());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BEZIER:
|
||||
if (!(getLiner() instanceof TupelBezierLiner)) {
|
||||
initControlPoints(type);
|
||||
updateLiner(new TupelBezierLiner());
|
||||
}
|
||||
break;
|
||||
case BEZIER_3:
|
||||
if (!(getLiner() instanceof TripleBezierLiner)) {
|
||||
initControlPoints(type);
|
||||
updateLiner(new TripleBezierLiner());
|
||||
}
|
||||
break;
|
||||
case POLYPATH:
|
||||
if (!(getLiner() instanceof PolyPathLiner)) {
|
||||
initPolyPath();
|
||||
updateLiner(new PolyPathLiner());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setLiner(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLiner(Liner newLiner) {
|
||||
setLiner(newLiner);
|
||||
fireFigureHandlesChanged();
|
||||
fireAreaInvalidated();
|
||||
updateControlPoints();
|
||||
invalidate();
|
||||
getModel().propertiesChanged(this);
|
||||
}
|
||||
|
||||
private LengthProperty calculateLength() {
|
||||
try {
|
||||
LengthProperty property = getModel().getPropertyLength();
|
||||
|
||||
if (property != null) {
|
||||
double length = (double) property.getValue();
|
||||
if (length <= 0.0) {
|
||||
PointFigure start = ((LabeledPointFigure) getStartFigure()).getPresentationFigure();
|
||||
PointFigure end = ((LabeledPointFigure) getEndFigure()).getPresentationFigure();
|
||||
double startPosX
|
||||
= start.getModel().getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM);
|
||||
double startPosY
|
||||
= start.getModel().getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM);
|
||||
double endPosX
|
||||
= end.getModel().getPropertyModelPositionX().getValueByUnit(LengthProperty.Unit.MM);
|
||||
double endPosY
|
||||
= end.getModel().getPropertyModelPositionY().getValueByUnit(LengthProperty.Unit.MM);
|
||||
length = distance(startPosX, startPosY, endPosX, endPosY);
|
||||
property.setValueAndUnit(length, LengthProperty.Unit.MM);
|
||||
property.markChanged();
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
LOG.error("calculateLength()", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(Point2D.Double p) {
|
||||
return textGenerator.getToolTipText(getModel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two points can be connected with each other.
|
||||
*
|
||||
* @param start The start connector.
|
||||
* @param end The end connector.
|
||||
* @return {@code true}, if the two points can be connected, otherwise {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean canConnect(Connector start, Connector end) {
|
||||
ModelComponent modelStart = start.getOwner().get(FigureConstants.MODEL);
|
||||
ModelComponent modelEnd = end.getOwner().get(FigureConstants.MODEL);
|
||||
|
||||
return modelStart instanceof PointModel
|
||||
&& modelEnd instanceof PointModel
|
||||
&& modelStart != modelEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(int detailLevel) {
|
||||
Collection<Handle> handles = new ArrayList<>();
|
||||
|
||||
if (!isVisible()) {
|
||||
return handles;
|
||||
}
|
||||
|
||||
// see BezierFigure
|
||||
switch (detailLevel % 2) {
|
||||
case -1: // Mouse hover handles
|
||||
handles.add(new BezierOutlineHandle(this, true));
|
||||
break;
|
||||
|
||||
case 0: // Mouse clicked
|
||||
if (getLinerType() == PathModel.Type.POLYPATH) {
|
||||
for (int i = 1; i < path.size() - 1; i++) {
|
||||
handles.add(new BezierLinerControlPointHandle(this, i, BezierPath.C0_MASK));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (cp1 != null) {
|
||||
// Start point: handle to CP2
|
||||
handles.add(new BezierLinerControlPointHandle(this, 0, BezierPath.C2_MASK));
|
||||
if (cp2 != null) {
|
||||
// End point: handle to CP3
|
||||
handles.add(new BezierLinerControlPointHandle(this, 1, BezierPath.C1_MASK));
|
||||
if (cp3 != null) {
|
||||
// End point: handle to EP
|
||||
handles.add(new BezierLinerControlPointHandle(this, 2, BezierPath.C1_MASK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1: // double click
|
||||
handles.add(new BezierOutlineHandle(this));
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lineout() {
|
||||
if (getLiner() == null) {
|
||||
path.invalidatePath();
|
||||
}
|
||||
else {
|
||||
getLiner().lineout(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertiesChanged(AttributesChangeEvent e) {
|
||||
if (!e.getInitiator().equals(this)) {
|
||||
setLinerByType((PathModel.Type) getModel().getPropertyPathConnType().getValue());
|
||||
calculateLength();
|
||||
lineout();
|
||||
}
|
||||
|
||||
super.propertiesChanged(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g) {
|
||||
if (drawingOptions.isBlocksVisible()) {
|
||||
drawBlockDecoration(g);
|
||||
}
|
||||
drawRouteDecoration(g);
|
||||
|
||||
super.draw(g);
|
||||
}
|
||||
|
||||
private void drawRouteDecoration(Graphics2D g) {
|
||||
for (Map.Entry<VehicleModel, AllocationState> entry : getModel().getAllocationStates()
|
||||
.entrySet()) {
|
||||
VehicleModel vehicleModel = entry.getKey();
|
||||
switch (entry.getValue()) {
|
||||
case CLAIMED:
|
||||
drawDecoration(
|
||||
g,
|
||||
Strokes.PATH_ON_ROUTE,
|
||||
transparentColor(vehicleModel.getDriveOrderColor(), 70)
|
||||
);
|
||||
break;
|
||||
case ALLOCATED:
|
||||
drawDecoration(g, Strokes.PATH_ON_ROUTE, vehicleModel.getDriveOrderColor());
|
||||
break;
|
||||
case ALLOCATED_WITHDRAWN:
|
||||
drawDecoration(g, Strokes.PATH_ON_WITHDRAWN_ROUTE, Color.GRAY);
|
||||
break;
|
||||
default:
|
||||
// Don't draw any decoration.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBlockDecoration(Graphics2D g) {
|
||||
for (BlockModel blockModel : getModel().getBlockModels()) {
|
||||
drawDecoration(g, Strokes.BLOCK_ELEMENT, transparentColor(blockModel.getColor(), 192));
|
||||
}
|
||||
}
|
||||
|
||||
private Color transparentColor(Color color, int alpha) {
|
||||
return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
|
||||
}
|
||||
|
||||
private void drawDecoration(Graphics2D g, Stroke stroke, Color color) {
|
||||
g.setStroke(stroke);
|
||||
g.setColor(color);
|
||||
g.draw(this.getShape());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDecorations() {
|
||||
if (getModel() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(AttributeKeys.START_DECORATION, navigableBackward() ? ARROW_BACKWARD : null);
|
||||
set(AttributeKeys.END_DECORATION, navigableForward() ? ARROW_FORWARD : null);
|
||||
|
||||
// Mark locked path.
|
||||
if (Boolean.TRUE.equals(getModel().getPropertyLocked().getValue())) {
|
||||
set(AttributeKeys.STROKE_COLOR, Color.red);
|
||||
set(AttributeKeys.STROKE_DASHES, LOCKED_DASH);
|
||||
}
|
||||
else {
|
||||
set(AttributeKeys.STROKE_COLOR, Color.black);
|
||||
set(AttributeKeys.STROKE_DASHES, UNLOCKED_DASH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(AttributeKey<T> key, T newValue) {
|
||||
super.set(key, newValue);
|
||||
// if the ModelComponent is set we update the decorations, because
|
||||
// properties like maxReverseVelocity could have changed
|
||||
if (key.equals(FigureConstants.MODEL)) {
|
||||
updateDecorations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModel() {
|
||||
if (calculateLength() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getModel().getPropertyMaxVelocity().markChanged();
|
||||
getModel().getPropertyMaxReverseVelocity().markChanged();
|
||||
|
||||
getModel().propertiesChanged(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleModel(EventObject event) {
|
||||
if (!(event.getSource() instanceof Origin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Origin origin = (Origin) event.getSource();
|
||||
if (previousOrigin.getScaleX() == origin.getScaleX()
|
||||
&& previousOrigin.getScaleY() == origin.getScaleY()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTupelBezier()) { // BEZIER
|
||||
Point2D.Double scaledControlPoint = scaleControlPoint(cp1, origin);
|
||||
path.set(0, BezierPath.C2_MASK, scaledControlPoint);
|
||||
scaledControlPoint = scaleControlPoint(cp2, origin);
|
||||
path.set(1, BezierPath.C1_MASK, scaledControlPoint);
|
||||
}
|
||||
else if (isTripleBezier()) { // BEZIER_3
|
||||
Point2D.Double scaledControlPoint = scaleControlPoint(cp1, origin);
|
||||
path.set(0, BezierPath.C2_MASK, scaledControlPoint);
|
||||
scaledControlPoint = scaleControlPoint(cp2, origin);
|
||||
path.set(1, BezierPath.C1_MASK, scaledControlPoint);
|
||||
scaledControlPoint = scaleControlPoint(cp3, origin);
|
||||
path.set(1, BezierPath.C0_MASK, scaledControlPoint);
|
||||
scaledControlPoint = scaleControlPoint(cp4, origin);
|
||||
path.set(1, BezierPath.C2_MASK, scaledControlPoint);
|
||||
path.set(2, BezierPath.C2_MASK, scaledControlPoint);
|
||||
scaledControlPoint = scaleControlPoint(cp5, origin);
|
||||
path.set(2, BezierPath.C1_MASK, scaledControlPoint);
|
||||
}
|
||||
|
||||
// Remember the new scale
|
||||
previousOrigin.setScale(origin.getScaleX(), origin.getScaleY());
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
private boolean navigableForward() {
|
||||
return getModel().getPropertyMaxVelocity().getValueByUnit(SpeedProperty.Unit.MM_S) > 0.0;
|
||||
}
|
||||
|
||||
private boolean navigableBackward() {
|
||||
return getModel().getPropertyMaxReverseVelocity().getValueByUnit(SpeedProperty.Unit.MM_S) > 0.0;
|
||||
}
|
||||
|
||||
private Point2D.Double scaleControlPoint(Point2D.Double p, Origin newScale) {
|
||||
return new Double(
|
||||
(p.x * previousOrigin.getScaleX()) / newScale.getScaleX(),
|
||||
(p.y * previousOrigin.getScaleY()) / newScale.getScaleY()
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isTupelBezier() {
|
||||
return cp1 != null && cp2 != null && cp3 == null && cp4 == null && cp5 == null;
|
||||
}
|
||||
|
||||
private boolean isTripleBezier() {
|
||||
return cp1 != null && cp2 != null && cp3 != null && cp4 != null && cp5 != null;
|
||||
}
|
||||
|
||||
@Override // LineConnectionFigure
|
||||
public PathConnection clone() {
|
||||
PathConnection clone = (PathConnection) super.clone();
|
||||
|
||||
AbstractProperty pConnType = (AbstractProperty) clone.getModel().getPropertyPathConnType();
|
||||
if (getLiner() instanceof TupelBezierLiner) {
|
||||
pConnType.setValue(PathModel.Type.BEZIER);
|
||||
}
|
||||
else if (getLiner() instanceof TripleBezierLiner) {
|
||||
pConnType.setValue(PathModel.Type.BEZIER_3);
|
||||
}
|
||||
else if (getLiner() instanceof ElbowLiner) {
|
||||
pConnType.setValue(PathModel.Type.ELBOW);
|
||||
}
|
||||
else if (getLiner() instanceof SlantedLiner) {
|
||||
pConnType.setValue(PathModel.Type.SLANTED);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private void initPolyPath() {
|
||||
Point2D.Double sp = path.get(0, BezierPath.C0_MASK);
|
||||
Point2D.Double ep = path.get(path.size() - 1, BezierPath.C0_MASK);
|
||||
|
||||
if (sp.x == ep.x && sp.y == ep.y) {
|
||||
path.clear();
|
||||
path.add(sp);
|
||||
String[] coords = getModel().getPropertyPathControlPoints().getText().split("[;]");
|
||||
for (String coordinates : coords) {
|
||||
String[] c = coordinates.split("[,]");
|
||||
int x = (int) java.lang.Double.parseDouble(c[0]);
|
||||
int y = (int) java.lang.Double.parseDouble(c[1]);
|
||||
path.add(x, y);
|
||||
}
|
||||
|
||||
path.add(ep);
|
||||
}
|
||||
else {
|
||||
|
||||
path.clear();
|
||||
path.add(new BezierPath.Node(sp));
|
||||
path.add(new BezierPath.Node((sp.x + ep.x) / 2.0, (sp.y + ep.y) / 2.0));
|
||||
path.add(new BezierPath.Node(ep));
|
||||
}
|
||||
}
|
||||
|
||||
@Override // SimpleLineConnection
|
||||
public boolean handleMouseClick(Point2D.Double p, MouseEvent evt, DrawingView drawingView) {
|
||||
if (getLinerType() == PathModel.Type.POLYPATH) {
|
||||
int addPointMask = MouseEvent.CTRL_DOWN_MASK;
|
||||
int deletePointMask = MouseEvent.ALT_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK;
|
||||
if ((evt.getModifiersEx() & (addPointMask | deletePointMask)) == addPointMask) {
|
||||
int index = path.findSegment(p, 10);
|
||||
if (index != -1) {
|
||||
path.add(index + 1, new BezierPath.Node(p));
|
||||
updateConnection();
|
||||
}
|
||||
}
|
||||
else if ((evt.getModifiersEx() & (deletePointMask | addPointMask)) == deletePointMask) {
|
||||
int index = path.findSegment(p, 10);
|
||||
if (index != -1) {
|
||||
Point2D.Double[] points = path.toPolygonArray();
|
||||
path.clear();
|
||||
for (int i = 0; i < points.length; i++) {
|
||||
if (i != index) {
|
||||
path.add(new BezierPath.Node(points[i]));
|
||||
}
|
||||
}
|
||||
updateConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Set<TCSResourceReference<?>>> getCurrentDriveOrderClaim(VehicleModel vehicle) {
|
||||
List<Set<TCSResourceReference<?>>> result = new ArrayList<>();
|
||||
|
||||
boolean driveOrderEndFound = false;
|
||||
for (Set<TCSResourceReference<?>> res : vehicle.getClaimedResources().getItems()) {
|
||||
result.add(res);
|
||||
|
||||
if (containsDriveOrderDestination(res, vehicle)) {
|
||||
driveOrderEndFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driveOrderEndFound) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// With the end of the drive order not found, there is nothing from the current drive order in
|
||||
// the claimed resources.
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsDriveOrderDestination(
|
||||
Set<TCSResourceReference<?>> resources,
|
||||
VehicleModel vehicle
|
||||
) {
|
||||
if (vehicle.getDriveOrderDestination() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return resources.stream()
|
||||
.anyMatch(
|
||||
resource -> Objects.equals(
|
||||
resource.getName(),
|
||||
vehicle.getDriveOrderDestination().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.jhotdraw.geom.Geom;
|
||||
import org.opentcs.data.model.TCSResourceReference;
|
||||
import org.opentcs.guing.base.AllocationState;
|
||||
import org.opentcs.guing.base.model.elements.BlockModel;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
import org.opentcs.guing.common.components.drawing.DrawingOptions;
|
||||
import org.opentcs.guing.common.components.drawing.Strokes;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
|
||||
/**
|
||||
* A figure that represents a decision point.
|
||||
*/
|
||||
public class PointFigure
|
||||
extends
|
||||
TCSFigure {
|
||||
|
||||
/**
|
||||
* A color for parking positions.
|
||||
*/
|
||||
private static final Color C_PARK = Color.BLUE;
|
||||
/**
|
||||
* A color for halt positions.
|
||||
*/
|
||||
private static final Color C_HALT = Color.LIGHT_GRAY;
|
||||
/**
|
||||
* The figure's diameter in drawing units (pixels at 100% zoom).
|
||||
*/
|
||||
private final int fDiameter;
|
||||
/**
|
||||
* The drawing options.
|
||||
*/
|
||||
private final DrawingOptions drawingOptions;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param model The model corresponding to this graphical object.
|
||||
* @param drawingOptions The drawing options.
|
||||
*/
|
||||
@Inject
|
||||
public PointFigure(
|
||||
@Assisted
|
||||
PointModel model,
|
||||
DrawingOptions drawingOptions
|
||||
) {
|
||||
super(model);
|
||||
this.drawingOptions = requireNonNull(drawingOptions, "drawingOptions");
|
||||
|
||||
fDiameter = 10;
|
||||
fDisplayBox = new Rectangle(fDiameter, fDiameter);
|
||||
fZoomPoint = new ZoomPoint(0.5 * fDiameter, 0.5 * fDiameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PointModel getModel() {
|
||||
return (PointModel) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
public Point center() {
|
||||
return Geom.center(fDisplayBox);
|
||||
}
|
||||
|
||||
public Ellipse2D.Double getShape() {
|
||||
Rectangle2D r2 = fDisplayBox.getBounds2D();
|
||||
Ellipse2D.Double shape
|
||||
= new Ellipse2D.Double(r2.getX(), r2.getY(), fDiameter - 1, fDiameter - 1);
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public Rectangle2D.Double getBounds() {
|
||||
Rectangle2D r2 = fDisplayBox.getBounds2D();
|
||||
Rectangle2D.Double r2d = new Rectangle2D.Double();
|
||||
r2d.setRect(r2);
|
||||
|
||||
return r2d;
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public Object getTransformRestoreData() {
|
||||
// Never used?
|
||||
return fDisplayBox.clone();
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public void restoreTransformTo(Object restoreData) {
|
||||
// Never used?
|
||||
Rectangle r = (Rectangle) restoreData;
|
||||
fDisplayBox.x = r.x;
|
||||
fDisplayBox.y = r.y;
|
||||
fDisplayBox.width = r.width;
|
||||
fDisplayBox.height = r.height;
|
||||
fZoomPoint.setX(r.x + 0.5 * r.width);
|
||||
fZoomPoint.setY(r.y + 0.5 * r.height);
|
||||
}
|
||||
|
||||
@Override // Figure
|
||||
public void transform(AffineTransform tx) {
|
||||
Point2D center = getZoomPoint().getPixelLocationExactly();
|
||||
Point2D lead = new Point2D.Double(); // not used
|
||||
setBounds(
|
||||
(Point2D.Double) tx.transform(center, center),
|
||||
(Point2D.Double) tx.transform(lead, lead)
|
||||
);
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
|
||||
fZoomPoint.setX(anchor.x);
|
||||
fZoomPoint.setY(anchor.y);
|
||||
fDisplayBox.x = (int) (anchor.x - 0.5 * fDiameter);
|
||||
fDisplayBox.y = (int) (anchor.y - 0.5 * fDiameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawFigure(Graphics2D g) {
|
||||
if (drawingOptions.isBlocksVisible()) {
|
||||
drawBlockDecoration(g);
|
||||
}
|
||||
drawRouteDecoration(g);
|
||||
|
||||
super.drawFigure(g);
|
||||
}
|
||||
|
||||
private void drawRouteDecoration(Graphics2D g) {
|
||||
for (Map.Entry<VehicleModel, AllocationState> entry : getModel().getAllocationStates()
|
||||
.entrySet()) {
|
||||
VehicleModel vehicleModel = entry.getKey();
|
||||
switch (entry.getValue()) {
|
||||
case CLAIMED:
|
||||
drawDecoration(
|
||||
g,
|
||||
Strokes.PATH_ON_ROUTE,
|
||||
transparentColor(vehicleModel.getDriveOrderColor(), 70)
|
||||
);
|
||||
break;
|
||||
case ALLOCATED:
|
||||
drawDecoration(g, Strokes.PATH_ON_ROUTE, vehicleModel.getDriveOrderColor());
|
||||
break;
|
||||
case ALLOCATED_WITHDRAWN:
|
||||
drawDecoration(g, Strokes.PATH_ON_WITHDRAWN_ROUTE, Color.GRAY);
|
||||
break;
|
||||
default:
|
||||
// Don't draw any decoration.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBlockDecoration(Graphics2D g) {
|
||||
for (BlockModel blockModel : getModel().getBlockModels()) {
|
||||
drawDecoration(g, Strokes.BLOCK_ELEMENT, transparentColor(blockModel.getColor(), 192));
|
||||
}
|
||||
}
|
||||
|
||||
private Color transparentColor(Color color, int alpha) {
|
||||
return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
|
||||
}
|
||||
|
||||
private void drawDecoration(Graphics2D g, Stroke stroke, Color color) {
|
||||
g.setStroke(stroke);
|
||||
g.setColor(color);
|
||||
g.draw(this.getShape());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawFill(Graphics2D g) {
|
||||
Rectangle rect = fDisplayBox;
|
||||
|
||||
if (getModel().getPropertyType().getValue() == PointModel.Type.PARK) {
|
||||
g.setColor(C_PARK);
|
||||
}
|
||||
else {
|
||||
g.setColor(C_HALT);
|
||||
}
|
||||
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
g.fillOval(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
if (getModel().getPropertyType().getValue() == PointModel.Type.PARK) {
|
||||
g.setColor(Color.white);
|
||||
Font oldFont = g.getFont();
|
||||
Font newFont = new Font(Font.DIALOG, Font.BOLD, 7);
|
||||
g.setFont(newFont);
|
||||
g.drawString("P", rect.x + 3, rect.y + rect.height - 3);
|
||||
g.setFont(oldFont);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // AbstractAttributedFigure
|
||||
protected void drawStroke(Graphics2D g) {
|
||||
Rectangle r = fDisplayBox;
|
||||
|
||||
if (r.width > 0 && r.height > 0) {
|
||||
g.drawOval(r.x, r.y, r.width - 1, r.height - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // AbstractAttributedDecoratedFigure
|
||||
public PointFigure clone() {
|
||||
PointFigure thatFigure = (PointFigure) super.clone();
|
||||
thatFigure.setZoomPoint(new ZoomPoint(fZoomPoint.getX(), fZoomPoint.getY()));
|
||||
|
||||
return thatFigure;
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public int getLayer() {
|
||||
return getModel().getPropertyLayerWrapper().getValue().getLayer().getOrdinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return super.isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayer().isVisible()
|
||||
&& getModel().getPropertyLayerWrapper().getValue().getLayerGroup().isVisible();
|
||||
}
|
||||
|
||||
private List<Set<TCSResourceReference<?>>> getCurrentDriveOrderClaim(VehicleModel vehicle) {
|
||||
List<Set<TCSResourceReference<?>>> result = new ArrayList<>();
|
||||
|
||||
boolean driveOrderEndFound = false;
|
||||
for (Set<TCSResourceReference<?>> res : vehicle.getClaimedResources().getItems()) {
|
||||
result.add(res);
|
||||
|
||||
if (containsDriveOrderDestination(res, vehicle)) {
|
||||
driveOrderEndFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driveOrderEndFound) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// With the end of the drive order not found, there is nothing from the current drive order in
|
||||
// the claimed resources.
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsDriveOrderDestination(
|
||||
Set<TCSResourceReference<?>> resources,
|
||||
VehicleModel vehicle
|
||||
) {
|
||||
if (vehicle.getDriveOrderDestination() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return resources.stream()
|
||||
.anyMatch(
|
||||
resource -> Objects.equals(
|
||||
resource.getName(),
|
||||
vehicle.getDriveOrderDestination().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Shape;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.EventObject;
|
||||
import org.jhotdraw.draw.AttributeKey;
|
||||
import org.jhotdraw.draw.DrawingView;
|
||||
import org.jhotdraw.draw.LineConnectionFigure;
|
||||
import org.jhotdraw.draw.connector.Connector;
|
||||
import org.jhotdraw.draw.decoration.ArrowTip;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeEvent;
|
||||
import org.opentcs.guing.base.components.properties.event.AttributesChangeListener;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.base.model.elements.AbstractConnection;
|
||||
import org.opentcs.guing.common.components.drawing.course.OriginChangeListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*/
|
||||
public abstract class SimpleLineConnection
|
||||
extends
|
||||
LineConnectionFigure
|
||||
implements
|
||||
ModelBasedFigure,
|
||||
AttributesChangeListener,
|
||||
OriginChangeListener {
|
||||
|
||||
protected static final AttributeKey<Color> FILL_COLOR
|
||||
= new AttributeKey<>("FillColor", Color.class);
|
||||
protected static final AttributeKey<Color> STROKE_COLOR
|
||||
= new AttributeKey<>("StrokeColor", Color.class);
|
||||
protected static final ArrowTip ARROW_FORWARD
|
||||
= new ArrowTip(0.35, 12.0, 11.3, true, true, true);
|
||||
protected static final ArrowTip ARROW_BACKWARD
|
||||
= new ArrowTip(0.35, 12.0, 11.3, true, true, false);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SimpleLineConnection.class);
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param model The model corresponding to this graphical object.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public SimpleLineConnection(AbstractConnection model) {
|
||||
set(FigureConstants.MODEL, model);
|
||||
initConnectionFigure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise this figure.
|
||||
*/
|
||||
protected final void initConnectionFigure() {
|
||||
updateDecorations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractConnection getModel() {
|
||||
return (AbstractConnection) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the shape.
|
||||
*
|
||||
* @return the shape.
|
||||
*/
|
||||
public Shape getShape() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override // BezierFigure
|
||||
protected BezierPath getCappedPath() {
|
||||
// Workaround for NullPointerException in BezierFigure.getCappedPath()
|
||||
try {
|
||||
return super.getCappedPath();
|
||||
}
|
||||
catch (NullPointerException ex) {
|
||||
LOG.warn("", ex);
|
||||
return path.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the properties of the model.
|
||||
*/
|
||||
public abstract void updateModel();
|
||||
|
||||
/**
|
||||
* Scales the model coodinates accodring to changes to the layout scale.
|
||||
*
|
||||
* @param event The event containing the layout scale change.
|
||||
*/
|
||||
public abstract void scaleModel(EventObject event);
|
||||
|
||||
/**
|
||||
* Calculates the euclid distance between the start position and the end position.
|
||||
*
|
||||
* @param startPosX The x coordiante of the start position.
|
||||
* @param startPosY The y coordiante of the start position.
|
||||
* @param endPosX The x coordinate of the end position.
|
||||
* @param endPosY The y coordinate of the end position.
|
||||
* @return the euclid distance between start and end point rounded to the next integer.
|
||||
*/
|
||||
protected double distance(double startPosX, double startPosY, double endPosX, double endPosY) {
|
||||
double dX = startPosX - endPosX;
|
||||
double dY = startPosY - endPosY;
|
||||
double dist = Math.sqrt(dX * dX + dY * dY);
|
||||
dist = Math.floor(dist + 0.5); // round to an integer value.
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
public void updateDecorations() {
|
||||
}
|
||||
|
||||
@Override // LineConnectionFigure
|
||||
protected void handleConnect(Connector start, Connector end) {
|
||||
if (start != null && end != null) {
|
||||
ModelComponent startModel = start.getOwner().get(FigureConstants.MODEL);
|
||||
ModelComponent endModel = end.getOwner().get(FigureConstants.MODEL);
|
||||
getModel().setConnectedComponents(startModel, endModel);
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // LineConnectionFigure
|
||||
protected void handleDisconnect(Connector start, Connector end) {
|
||||
super.handleDisconnect(start, end);
|
||||
|
||||
getModel().removingConnection();
|
||||
}
|
||||
|
||||
@Override // LineConnectionFigure
|
||||
public boolean handleMouseClick(Point2D.Double p, MouseEvent evt, DrawingView drawingView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // AttributesChangeListener
|
||||
public void propertiesChanged(AttributesChangeEvent e) {
|
||||
if (!e.getInitiator().equals(this)) {
|
||||
updateDecorations();
|
||||
fireFigureChanged(getDrawingArea());
|
||||
}
|
||||
}
|
||||
|
||||
@Override // OriginChangeListener
|
||||
public void originLocationChanged(EventObject event) {
|
||||
}
|
||||
|
||||
@Override // OriginChangeListener
|
||||
public void originScaleChanged(EventObject event) {
|
||||
scaleModel(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleLineConnection clone() {
|
||||
try {
|
||||
SimpleLineConnection clone = (SimpleLineConnection) super.clone();
|
||||
clone.set(FigureConstants.MODEL, getModel().clone());
|
||||
|
||||
return clone;
|
||||
}
|
||||
catch (CloneNotSupportedException exc) {
|
||||
throw new IllegalStateException("Unexpected exception encountered", exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import org.jhotdraw.draw.AbstractAttributedDecoratedFigure;
|
||||
import org.jhotdraw.geom.Geom;
|
||||
import org.opentcs.guing.base.model.DrawnModelComponent;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.guing.common.components.drawing.ZoomPoint;
|
||||
|
||||
/**
|
||||
* Base implementation for figures.
|
||||
*/
|
||||
public abstract class TCSFigure
|
||||
extends
|
||||
AbstractAttributedDecoratedFigure
|
||||
implements
|
||||
ModelBasedFigure {
|
||||
|
||||
/**
|
||||
* The enclosing rectangle.
|
||||
*/
|
||||
protected Rectangle fDisplayBox;
|
||||
/**
|
||||
* The exact position for the middle of the figure.
|
||||
*/
|
||||
protected ZoomPoint fZoomPoint;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param modelComponent The model corresponding to this graphical object.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public TCSFigure(ModelComponent modelComponent) {
|
||||
super();
|
||||
set(FigureConstants.MODEL, modelComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact point at the middle of the figure.
|
||||
*
|
||||
* @return the exact point at the middle of the figure.
|
||||
*/
|
||||
public ZoomPoint getZoomPoint() {
|
||||
return fZoomPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the zoom point.
|
||||
*
|
||||
* @param zoomPoint The point at the middle of the figure.
|
||||
*/
|
||||
public void setZoomPoint(ZoomPoint zoomPoint) {
|
||||
fZoomPoint = zoomPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones this figure, also clones the associated model component.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override // AbstractAttributedDecoratedFigure
|
||||
public TCSFigure clone() {
|
||||
try {
|
||||
TCSFigure that = (TCSFigure) super.clone();
|
||||
that.fDisplayBox = new Rectangle(fDisplayBox);
|
||||
that.setModel(getModel().clone());
|
||||
|
||||
return that;
|
||||
}
|
||||
catch (CloneNotSupportedException ex) {
|
||||
throw new Error("Cannot clone() unexpectedly", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rectangle2D.Double getFigureDrawingArea() {
|
||||
// Add some margin to the drawing area of the figure, so the
|
||||
// drawing area scrolls a little earlier
|
||||
Rectangle2D.Double drawingArea = super.getFigureDrawingArea();
|
||||
// if we add these two lines the Drawing becomes grey, if we start
|
||||
// the application in operating mode..
|
||||
// drawingArea.height += 50;
|
||||
// drawingArea.width += 100;
|
||||
|
||||
return drawingArea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawnModelComponent getModel() {
|
||||
return (DrawnModelComponent) get(FigureConstants.MODEL);
|
||||
}
|
||||
|
||||
public void setModel(ModelComponent model) {
|
||||
set(FigureConstants.MODEL, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enclosing rectangle.
|
||||
*
|
||||
* @return The enclosing rectangle.
|
||||
*/
|
||||
public Rectangle displayBox() {
|
||||
return new Rectangle(fDisplayBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean figureContains(Point2D.Double p) {
|
||||
Rectangle2D.Double r2d = getBounds();
|
||||
// Grow for connectors
|
||||
Geom.grow(r2d, 10d, 10d);
|
||||
|
||||
return (r2d.contains(p));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Point2D.Double;
|
||||
import org.jhotdraw.draw.LabelFigure;
|
||||
import org.jhotdraw.draw.event.FigureEvent;
|
||||
import org.opentcs.data.model.visualization.ElementPropKeys;
|
||||
import org.opentcs.guing.base.components.properties.type.StringProperty;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
|
||||
/**
|
||||
* A label belonging to another {@code Figure} that shows the name of the affiliated object in the
|
||||
* kernel model.
|
||||
*/
|
||||
public class TCSLabelFigure
|
||||
extends
|
||||
LabelFigure {
|
||||
|
||||
/**
|
||||
* The default x label offset for label figures.
|
||||
*/
|
||||
public static final int DEFAULT_LABEL_OFFSET_X = -10;
|
||||
|
||||
/**
|
||||
* The default y label offset for label figures.
|
||||
*/
|
||||
public static final int DEFAULT_LABEL_OFFSET_Y = -20;
|
||||
|
||||
private Point2D.Double fOffset;
|
||||
private LabeledFigure fParent;
|
||||
private boolean isLabelVisible = true;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public TCSLabelFigure() {
|
||||
this("?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param text The text of the label
|
||||
*/
|
||||
public TCSLabelFigure(String text) {
|
||||
super(text);
|
||||
fOffset = new Point2D.Double(DEFAULT_LABEL_OFFSET_X, DEFAULT_LABEL_OFFSET_Y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility flag of the label.
|
||||
*
|
||||
* @param visible The visibility flag.
|
||||
*/
|
||||
public void setLabelVisible(boolean visible) {
|
||||
isLabelVisible = visible;
|
||||
|
||||
if (visible) {
|
||||
setText(fParent.getPresentationFigure().getModel().getName());
|
||||
}
|
||||
else {
|
||||
setText("");
|
||||
}
|
||||
|
||||
invalidate();
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position relative to the {@code Figure}.
|
||||
*
|
||||
* @param posX The X-Offset of the label.
|
||||
* @param posY The Y-Offset of the label.
|
||||
*/
|
||||
public void setOffset(int posX, int posY) {
|
||||
fOffset = new Point2D.Double(posX, posY);
|
||||
}
|
||||
|
||||
public Double getOffset() {
|
||||
return fOffset;
|
||||
}
|
||||
|
||||
void setParent(LabeledFigure parent) {
|
||||
fParent = parent;
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public void changed() {
|
||||
// Called when the figure has changed - movement with MouseDragger.
|
||||
super.changed();
|
||||
|
||||
if (fParent != null) {
|
||||
TCSFigure figure = fParent.getPresentationFigure();
|
||||
ModelComponent model = figure.getModel();
|
||||
|
||||
Point2D.Double newOffset = new Point2D.Double(
|
||||
getBounds().getX() - figure.getBounds().x,
|
||||
getBounds().getY() - figure.getBounds().y
|
||||
);
|
||||
|
||||
if (newOffset.x != fOffset.x || newOffset.y != fOffset.y) {
|
||||
fOffset = newOffset;
|
||||
StringProperty sp
|
||||
= (StringProperty) model.getProperty(ElementPropKeys.POINT_LABEL_OFFSET_X);
|
||||
|
||||
if (sp != null) {
|
||||
sp.setText(String.format("%d", (long) newOffset.x));
|
||||
sp.markChanged();
|
||||
}
|
||||
|
||||
sp = (StringProperty) model.getProperty(ElementPropKeys.POINT_LABEL_OFFSET_Y);
|
||||
|
||||
if (sp != null) {
|
||||
sp.setText(String.format("%d", (long) newOffset.y));
|
||||
sp.markChanged();
|
||||
}
|
||||
model.propertiesChanged(fParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // AbstractFigure
|
||||
public int getLayer() {
|
||||
return 1; // stay above other figures ?
|
||||
}
|
||||
|
||||
@Override // LabelFigure
|
||||
public void figureChanged(FigureEvent event) {
|
||||
if (event.getFigure() instanceof LabeledFigure) {
|
||||
LabeledFigure lf = (LabeledFigure) event.getFigure();
|
||||
TCSFigure figure = lf.getPresentationFigure();
|
||||
ModelComponent model = figure.getModel();
|
||||
String name = model.getName();
|
||||
setText(name);
|
||||
|
||||
if (isLabelVisible) {
|
||||
invalidate();
|
||||
validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.guing.base.model.ModelComponent.MISCELLANEOUS;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.opentcs.guing.base.components.properties.type.KeyValueProperty;
|
||||
import org.opentcs.guing.base.components.properties.type.KeyValueSetProperty;
|
||||
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.base.model.elements.LinkModel;
|
||||
import org.opentcs.guing.base.model.elements.LocationModel;
|
||||
import org.opentcs.guing.base.model.elements.PathModel;
|
||||
import org.opentcs.guing.base.model.elements.PointModel;
|
||||
import org.opentcs.guing.base.model.elements.VehicleModel;
|
||||
import org.opentcs.guing.common.persistence.ModelManager;
|
||||
|
||||
/**
|
||||
* Generates tooltip texts for various model elements.
|
||||
*/
|
||||
public class ToolTipTextGenerator {
|
||||
|
||||
/**
|
||||
* The model manager.
|
||||
*/
|
||||
private final ModelManager modelManager;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param modelManager The model manager to use.
|
||||
*/
|
||||
@Inject
|
||||
public ToolTipTextGenerator(ModelManager modelManager) {
|
||||
this.modelManager = requireNonNull(modelManager, "modelManager");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tooltip text for a vehicle model.
|
||||
*
|
||||
* @param model The vehicle model.
|
||||
* @return A tooltip text for the model element.
|
||||
*/
|
||||
public String getToolTipText(VehicleModel model) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tooltip text for a point model.
|
||||
*
|
||||
* @param model The point model.
|
||||
* @return A tooltip text for the model element.
|
||||
*/
|
||||
public String getToolTipText(PointModel model) {
|
||||
String pointDesc = model.getDescription();
|
||||
StringBuilder sb = new StringBuilder("<html>");
|
||||
sb.append(pointDesc).append(" ").append("<b>").append(model.getName()).append("</b>");
|
||||
|
||||
appendBlockInfo(sb, model);
|
||||
appendMiscProps(sb, model);
|
||||
appendAllocatingVehicle(sb, model);
|
||||
|
||||
sb.append("</html>");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tooltip text for a location model.
|
||||
*
|
||||
* @param model The location model.
|
||||
* @return A tooltip text for the model element.
|
||||
*/
|
||||
public String getToolTipText(LocationModel model) {
|
||||
String locationDesc = model.getDescription();
|
||||
StringBuilder sb = new StringBuilder("<html>");
|
||||
sb.append(locationDesc).append(" ").append("<b>").append(model.getName()).append("</b>");
|
||||
|
||||
appendBlockInfo(sb, model);
|
||||
appendPeripheralInformation(sb, model);
|
||||
appendMiscProps(sb, model);
|
||||
appendAllocatingVehicle(sb, model);
|
||||
|
||||
sb.append("</html>");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tooltip text for a path model.
|
||||
*
|
||||
* @param model The path model.
|
||||
* @return A tooltip text for the model element.
|
||||
*/
|
||||
public String getToolTipText(PathModel model) {
|
||||
String pathDesc = model.getDescription();
|
||||
StringBuilder sb = new StringBuilder("<html>");
|
||||
sb.append(pathDesc).append(" ").append("<b>").append(model.getName()).append("</b>");
|
||||
|
||||
appendBlockInfo(sb, model);
|
||||
appendMiscProps(sb, model);
|
||||
appendAllocatingVehicle(sb, model);
|
||||
|
||||
sb.append("</html>");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tooltip text for a link model.
|
||||
*
|
||||
* @param model The link model.
|
||||
* @return A tooltip text for the model element.
|
||||
*/
|
||||
public String getToolTipText(LinkModel model) {
|
||||
return new StringBuilder("<html>")
|
||||
.append(model.getDescription()).append(" ")
|
||||
.append("<b>").append(model.getName()).append("</b>")
|
||||
.append("</html>").toString();
|
||||
}
|
||||
|
||||
private void appendBlockInfo(StringBuilder sb, ModelComponent component) {
|
||||
sb.append(blocksToToolTipContent(getBlocksWith(component)));
|
||||
}
|
||||
|
||||
private void appendBlockInfo(StringBuilder sb, LocationModel location) {
|
||||
List<LinkModel> links = modelManager.getModel().getLinkModels();
|
||||
links = links.stream()
|
||||
.filter(link -> link.getLocation().getName().equals(location.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<BlockModel> partOfBlocks = new ArrayList<>();
|
||||
for (LinkModel link : links) {
|
||||
partOfBlocks.addAll(getBlocksWith(link.getPoint()));
|
||||
}
|
||||
|
||||
sb.append(blocksToToolTipContent(partOfBlocks));
|
||||
}
|
||||
|
||||
protected void appendMiscProps(StringBuilder sb, ModelComponent component) {
|
||||
KeyValueSetProperty miscProps = (KeyValueSetProperty) component.getProperty(MISCELLANEOUS);
|
||||
|
||||
if (miscProps.getItems().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sb.append("<hr>\n");
|
||||
sb.append(miscProps.getDescription()).append(": \n");
|
||||
sb.append("<ul>\n");
|
||||
miscProps.getItems().stream()
|
||||
.sorted(Comparator.comparing(KeyValueProperty::getKey))
|
||||
.forEach(kvp -> {
|
||||
sb.append("<li>")
|
||||
.append(kvp.getKey()).append(": ").append(kvp.getValue())
|
||||
.append("</li>\n");
|
||||
});
|
||||
sb.append("</ul>\n");
|
||||
}
|
||||
|
||||
protected void appendAllocatingVehicle(StringBuilder sb, FigureDecorationDetails figure) {
|
||||
// Displaying information about allocating vehicles is only relevant in the Operations Desk
|
||||
// application, which is why this method is empty here.
|
||||
}
|
||||
|
||||
private void appendPeripheralInformation(StringBuilder sb, LocationModel model) {
|
||||
sb.append("<hr>");
|
||||
sb.append("<br>").append(model.getPropertyPeripheralReservationToken().getDescription())
|
||||
.append(": ")
|
||||
.append(model.getPropertyPeripheralReservationToken().getText());
|
||||
sb.append("<br>").append(model.getPropertyPeripheralState().getDescription())
|
||||
.append(": ")
|
||||
.append(model.getPropertyPeripheralState().getText());
|
||||
sb.append("<br>").append(model.getPropertyPeripheralProcState().getDescription())
|
||||
.append(": ")
|
||||
.append(model.getPropertyPeripheralProcState().getText());
|
||||
sb.append("<br>").append(model.getPropertyPeripheralJob().getDescription())
|
||||
.append(": ")
|
||||
.append(model.getPropertyPeripheralJob().getText());
|
||||
}
|
||||
|
||||
private List<BlockModel> getBlocksWith(ModelComponent component) {
|
||||
List<BlockModel> result = new ArrayList<>();
|
||||
List<BlockModel> blocks = modelManager.getModel().getBlockModels();
|
||||
for (BlockModel block : blocks) {
|
||||
if (block.contains(component)) {
|
||||
result.add(block);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String blocksToToolTipContent(List<BlockModel> blocks) {
|
||||
if (blocks.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
blocks.sort((b1, b2) -> b1.getName().compareTo(b2.getName()));
|
||||
|
||||
String desc = blocks.get(0).getDescription();
|
||||
StringBuilder sb = new StringBuilder("<hr>")
|
||||
.append(desc).append(": ");
|
||||
for (BlockModel block : blocks) {
|
||||
sb.append(block.getName()).append(", ");
|
||||
}
|
||||
sb.delete(sb.lastIndexOf(", "), sb.length());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.decoration;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Paint;
|
||||
import java.awt.RadialGradientPaint;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import org.jhotdraw.draw.AttributeKeys;
|
||||
import org.jhotdraw.draw.Figure;
|
||||
import org.jhotdraw.draw.handle.BoundsOutlineHandle;
|
||||
import org.opentcs.guing.common.components.drawing.figures.PointFigure;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class PointOutlineHandle
|
||||
extends
|
||||
BoundsOutlineHandle {
|
||||
|
||||
public PointOutlineHandle(Figure owner) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g) {
|
||||
PointFigure pf = (PointFigure) getOwner();
|
||||
Shape bounds = pf.getShape();
|
||||
|
||||
if (getOwner().get(AttributeKeys.TRANSFORM) != null) {
|
||||
bounds = getOwner().get(AttributeKeys.TRANSFORM).createTransformedShape(bounds);
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
bounds = view.getDrawingToViewTransform().createTransformedShape(bounds);
|
||||
Rectangle2D bounds2D = bounds.getBounds2D();
|
||||
float centerX = (float) bounds2D.getCenterX();
|
||||
float centerY = (float) bounds2D.getCenterY();
|
||||
Point2D center = new Point2D.Float(centerX, centerY);
|
||||
float radius = 10.0f;
|
||||
float[] dist = {0.1f, 0.9f};
|
||||
Color[] colors = {Color.CYAN, Color.BLUE};
|
||||
|
||||
RadialGradientPaint radialGradientPaint
|
||||
= new RadialGradientPaint(center, radius, dist, colors);
|
||||
Paint oldPaint = g.getPaint();
|
||||
g.setPaint(radialGradientPaint);
|
||||
|
||||
g.fill(bounds);
|
||||
g.setPaint(oldPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.liner;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.Objects;
|
||||
import org.jhotdraw.draw.BezierFigure;
|
||||
import org.jhotdraw.draw.Drawing;
|
||||
|
||||
/**
|
||||
* A Handle which allows to interactively change a control point
|
||||
*/
|
||||
public class BezierLinerControlPointHandle
|
||||
extends
|
||||
org.jhotdraw.draw.handle.BezierControlPointHandle {
|
||||
|
||||
public BezierLinerControlPointHandle(BezierFigure owner, int index, int coord) {
|
||||
super(owner, index, coord);
|
||||
}
|
||||
|
||||
@Override // BezierControlPointHandle
|
||||
public void trackEnd(Point anchor, Point lead, int modifiersEx) {
|
||||
super.trackEnd(anchor, lead, modifiersEx);
|
||||
// Fire edit event to update the control points of the Path figure
|
||||
Drawing drawing = Objects.requireNonNull(view.getDrawing());
|
||||
drawing.fireUndoableEditHappened(new BezierLinerEdit(getBezierFigure()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.liner;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.undo.CannotRedoException;
|
||||
import javax.swing.undo.CannotUndoException;
|
||||
import org.jhotdraw.draw.BezierFigure;
|
||||
import org.jhotdraw.draw.event.BezierNodeEdit;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
import org.opentcs.guing.common.components.drawing.figures.PathConnection;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.util.ResourceBundleUtil;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class BezierLinerEdit
|
||||
extends
|
||||
javax.swing.undo.AbstractUndoableEdit {
|
||||
|
||||
private final BezierFigure fOwner;
|
||||
private final BezierNodeEdit fNodeEdit;
|
||||
|
||||
/**
|
||||
* @param owner A path
|
||||
*/
|
||||
public BezierLinerEdit(BezierFigure owner) {
|
||||
fOwner = owner;
|
||||
BezierPath.Node node = owner.getNode(0);
|
||||
fNodeEdit = new BezierNodeEdit(owner, 0, node, node);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The associated PathConnection
|
||||
*/
|
||||
public BezierFigure getOwner() {
|
||||
return fOwner;
|
||||
}
|
||||
|
||||
@Override // AbstractUndoableEdit
|
||||
public boolean isSignificant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // AbstractUndoableEdit
|
||||
public String getPresentationName() {
|
||||
return ResourceBundleUtil.getBundle(I18nPlantOverview.MISC_PATH)
|
||||
.getString("bezierLinerEdit.presentationName");
|
||||
}
|
||||
|
||||
@Override // AbstractUndoableEdit
|
||||
public void redo()
|
||||
throws CannotRedoException {
|
||||
fNodeEdit.redo();
|
||||
updateProperties();
|
||||
}
|
||||
|
||||
@Override // AbstractUndoableEdit
|
||||
public void undo()
|
||||
throws CannotUndoException {
|
||||
fNodeEdit.undo();
|
||||
updateProperties();
|
||||
}
|
||||
|
||||
private void updateProperties() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
PathConnection path = (PathConnection) fOwner;
|
||||
path.updateControlPoints();
|
||||
path.getModel().getPropertyPathControlPoints().markChanged();
|
||||
path.getModel().propertiesChanged(path);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.liner;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.jhotdraw.draw.ConnectionFigure;
|
||||
import org.jhotdraw.draw.LineConnectionFigure;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.liner.Liner;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class PolyPathLiner
|
||||
implements
|
||||
org.jhotdraw.draw.liner.Liner {
|
||||
|
||||
public PolyPathLiner() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lineout(ConnectionFigure figure) {
|
||||
BezierPath path = ((LineConnectionFigure) figure).getBezierPath();
|
||||
|
||||
if (path != null) {
|
||||
path.invalidatePath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Handle> createHandles(BezierPath path) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override // Object
|
||||
public Liner clone() {
|
||||
try {
|
||||
return (Liner) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException ex) {
|
||||
InternalError error = new InternalError(ex.getMessage());
|
||||
error.initCause(ex);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.liner;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.jhotdraw.draw.ConnectionFigure;
|
||||
import org.jhotdraw.draw.LineConnectionFigure;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.liner.Liner;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
|
||||
/**
|
||||
* A {@link Liner} that constrains a connection to a fourth-order curved
|
||||
* line.
|
||||
*/
|
||||
public class TripleBezierLiner
|
||||
implements
|
||||
org.jhotdraw.draw.liner.Liner {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public TripleBezierLiner() {
|
||||
}
|
||||
|
||||
@Override // Liner
|
||||
public Collection<Handle> createHandles(BezierPath path) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override // Liner
|
||||
public void lineout(ConnectionFigure figure) {
|
||||
BezierPath path = ((LineConnectionFigure) figure).getBezierPath();
|
||||
|
||||
if (path != null) {
|
||||
path.invalidatePath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Object
|
||||
public Liner clone() {
|
||||
try {
|
||||
return (Liner) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException ex) {
|
||||
InternalError error = new InternalError(ex.getMessage());
|
||||
error.initCause(ex);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.drawing.figures.liner;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.jhotdraw.draw.ConnectionFigure;
|
||||
import org.jhotdraw.draw.LineConnectionFigure;
|
||||
import org.jhotdraw.draw.handle.Handle;
|
||||
import org.jhotdraw.draw.liner.Liner;
|
||||
import org.jhotdraw.geom.BezierPath;
|
||||
|
||||
/**
|
||||
* A {@link Liner} that constrains a connection to a quadratic or cubic curved
|
||||
* line.
|
||||
*/
|
||||
public class TupelBezierLiner
|
||||
implements
|
||||
org.jhotdraw.draw.liner.Liner {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public TupelBezierLiner() {
|
||||
}
|
||||
|
||||
@Override // Liner
|
||||
public Collection<Handle> createHandles(BezierPath path) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override // Liner
|
||||
public void lineout(ConnectionFigure figure) {
|
||||
BezierPath path = ((LineConnectionFigure) figure).getBezierPath();
|
||||
|
||||
if (path != null) {
|
||||
path.invalidatePath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Object
|
||||
public Liner clone() {
|
||||
try {
|
||||
return (Liner) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException ex) {
|
||||
InternalError error = new InternalError(ex.getMessage());
|
||||
error.initCause(ex);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.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.table.AbstractTableModel;
|
||||
import org.opentcs.data.model.visualization.LayerGroup;
|
||||
import org.opentcs.guing.common.persistence.ModelManager;
|
||||
import org.opentcs.guing.common.util.I18nPlantOverview;
|
||||
|
||||
/**
|
||||
* A table model for layer groups.
|
||||
*/
|
||||
public abstract class AbstractLayerGroupsTableModel
|
||||
extends
|
||||
AbstractTableModel
|
||||
implements
|
||||
LayerGroupChangeListener {
|
||||
|
||||
/**
|
||||
* The number of the "ID" column.
|
||||
*/
|
||||
public static final int COLUMN_ID = 0;
|
||||
/**
|
||||
* The number of the "Name" column.
|
||||
*/
|
||||
public static final int COLUMN_NAME = 1;
|
||||
/**
|
||||
* The number of the "Visible" column.
|
||||
*/
|
||||
public static final int COLUMN_VISIBLE = 2;
|
||||
/**
|
||||
* The resource bundle to use.
|
||||
*/
|
||||
private static final ResourceBundle BUNDLE
|
||||
= ResourceBundle.getBundle(I18nPlantOverview.LAYERS_PATH);
|
||||
/**
|
||||
* The column names.
|
||||
*/
|
||||
private static final String[] COLUMN_NAMES
|
||||
= new String[]{
|
||||
BUNDLE.getString("abstractLayerGroupsTableModel.column_id.headerText"),
|
||||
BUNDLE.getString("abstractLayerGroupsTableModel.column_name.headerText"),
|
||||
BUNDLE.getString("abstractLayerGroupsTableModel.column_visible.headerText")
|
||||
};
|
||||
/**
|
||||
* The column classes.
|
||||
*/
|
||||
private static final Class<?>[] COLUMN_CLASSES
|
||||
= new Class<?>[]{
|
||||
Integer.class,
|
||||
String.class,
|
||||
Boolean.class
|
||||
};
|
||||
/**
|
||||
* The model manager.
|
||||
*/
|
||||
private final ModelManager modelManager;
|
||||
/**
|
||||
* The layer group editor.
|
||||
*/
|
||||
private final LayerGroupEditor layerGroupEditor;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param modelManager The model manager.
|
||||
* @param layerGroupEditor The layer group editor.
|
||||
*/
|
||||
public AbstractLayerGroupsTableModel(
|
||||
ModelManager modelManager,
|
||||
LayerGroupEditor layerGroupEditor
|
||||
) {
|
||||
this.modelManager = requireNonNull(modelManager, "modelManager");
|
||||
this.layerGroupEditor = requireNonNull(layerGroupEditor, "layerGroupEditor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return layerGroups().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex < 0 || rowIndex >= getRowCount()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LayerGroup entry = layerGroups().get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case COLUMN_ID:
|
||||
return entry.getId();
|
||||
case COLUMN_NAME:
|
||||
return entry.getName();
|
||||
case COLUMN_VISIBLE:
|
||||
return entry.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_ID:
|
||||
return false;
|
||||
case COLUMN_NAME:
|
||||
return isNameColumnEditable();
|
||||
case COLUMN_VISIBLE:
|
||||
return isVisibleColumnEditable();
|
||||
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;
|
||||
}
|
||||
|
||||
LayerGroup entry = layerGroups().get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case COLUMN_ID:
|
||||
// Do nothing.
|
||||
break;
|
||||
case COLUMN_NAME:
|
||||
layerGroupEditor.setGroupName(entry.getId(), aValue.toString());
|
||||
break;
|
||||
case COLUMN_VISIBLE:
|
||||
layerGroupEditor.setGroupVisible(entry.getId(), (boolean) aValue);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public LayerGroup getDataAt(int index) {
|
||||
return layerGroups().get(index);
|
||||
}
|
||||
|
||||
protected abstract boolean isNameColumnEditable();
|
||||
|
||||
protected abstract boolean isVisibleColumnEditable();
|
||||
|
||||
private List<LayerGroup> layerGroups() {
|
||||
return getLayerGroups().values().stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<Integer, LayerGroup> getLayerGroups() {
|
||||
return modelManager.getModel().getLayoutModel().getPropertyLayerGroups().getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.util.Assertions.checkArgument;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.SwingUtilities;
|
||||
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.common.application.ViewManager;
|
||||
import org.opentcs.guing.common.event.SystemModelTransitionEvent;
|
||||
import org.opentcs.guing.common.model.SystemModel;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link LayerManager}.
|
||||
*/
|
||||
public class DefaultLayerManager
|
||||
implements
|
||||
LayerManager,
|
||||
LayerGroupManager {
|
||||
|
||||
/**
|
||||
* The sets of model components mapped to the IDs of the layers the model components are drawn on.
|
||||
*/
|
||||
private final Map<Integer, Set<DrawnModelComponent>> components = new HashMap<>();
|
||||
/**
|
||||
* The view manager.
|
||||
*/
|
||||
private final ViewManager viewManager;
|
||||
/**
|
||||
* The application's event bus.
|
||||
*/
|
||||
private final EventBus eventBus;
|
||||
/**
|
||||
* The listeners for layer group data changes.
|
||||
*/
|
||||
private final Set<LayerGroupChangeListener> layerGroupChangeListeners = new HashSet<>();
|
||||
/**
|
||||
* The listener for layer data changes.
|
||||
*/
|
||||
private LayerChangeListener layerChangeListener;
|
||||
/**
|
||||
* The system model we're working with.
|
||||
*/
|
||||
private SystemModel systemModel;
|
||||
/**
|
||||
* Whether this instance is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
@Inject
|
||||
public DefaultLayerManager(
|
||||
ViewManager viewManager,
|
||||
@ApplicationEventBus
|
||||
EventBus eventBus
|
||||
) {
|
||||
this.viewManager = requireNonNull(viewManager, "viewManager");
|
||||
this.eventBus = requireNonNull(eventBus, "eventBus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventBus.subscribe(this);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventBus.unsubscribe(this);
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayerChangeListener(LayerChangeListener listener) {
|
||||
layerChangeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayerVisible(int layerId, boolean visible) {
|
||||
checkArgument(
|
||||
getLayerWrapper(layerId) != null,
|
||||
"A layer with layer ID '%d' doesn't exist.",
|
||||
layerId
|
||||
);
|
||||
|
||||
LayerWrapper wrapper = getLayerWrapper(layerId);
|
||||
// We want to manipulate the drawing (by adding or removing figures) only if the visible state
|
||||
// of the layer has actually changed. This prevents figures from being added multiple times and
|
||||
// figures from being removed although they are no longer present in the drawing.
|
||||
if (wrapper.getLayer().isVisible() == visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean visibleBefore = wrapper.getLayer().isVisible() && wrapper.getLayerGroup().isVisible();
|
||||
Layer oldLayer = wrapper.getLayer();
|
||||
Layer newLayer = oldLayer.withVisible(visible);
|
||||
wrapper.setLayer(newLayer);
|
||||
boolean visibleAfter = wrapper.getLayer().isVisible() && wrapper.getLayerGroup().isVisible();
|
||||
|
||||
if (visibleBefore != visibleAfter) {
|
||||
layerVisibilityChanged(newLayer, visibleAfter);
|
||||
}
|
||||
|
||||
layerChangeListener.layersChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayerName(int layerId, String name) {
|
||||
checkArgument(
|
||||
getLayerWrapper(layerId) != null,
|
||||
"A layer with layer ID '%d' doesn't exist.",
|
||||
layerId
|
||||
);
|
||||
|
||||
LayerWrapper wrapper = getLayerWrapper(layerId);
|
||||
Layer oldLayer = wrapper.getLayer();
|
||||
Layer newLayer = oldLayer.withName(name);
|
||||
wrapper.setLayer(newLayer);
|
||||
|
||||
layerChangeListener.layersChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (!(event instanceof SystemModelTransitionEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SystemModelTransitionEvent evt = (SystemModelTransitionEvent) event;
|
||||
switch (evt.getStage()) {
|
||||
case UNLOADED:
|
||||
reset();
|
||||
break;
|
||||
case LOADED:
|
||||
systemModel = evt.getModel();
|
||||
restoreLayers();
|
||||
break;
|
||||
default: // Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLayerGroupChangeListener(LayerGroupChangeListener listener) {
|
||||
layerGroupChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsComponents(int layerId) {
|
||||
return !components.getOrDefault(layerId, new HashSet<>()).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupVisible(int groupId, boolean visible) {
|
||||
checkArgument(
|
||||
getLayerGroup(groupId) != null,
|
||||
"A layer group with layer group ID '%d' doesn't exist.",
|
||||
groupId
|
||||
);
|
||||
|
||||
LayerGroup oldGroup = getLayerGroup(groupId);
|
||||
if (oldGroup.isVisible() == visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Boolean, List<LayerWrapper>> wrappersByLayerVisibility
|
||||
= getLayerWrappers().values().stream()
|
||||
.filter(wrapper -> wrapper.getLayer().getGroupId() == groupId)
|
||||
.collect(Collectors.partitioningBy(wrapper -> wrapper.getLayer().isVisible()));
|
||||
|
||||
// We only need to take care of "visible" layers. Non-visible layers are not affected by
|
||||
// any changes to a group's visibility.
|
||||
for (LayerWrapper wrapper : wrappersByLayerVisibility.get(Boolean.TRUE)) {
|
||||
if (wrapper.getLayerGroup().isVisible() != visible) {
|
||||
layerVisibilityChanged(wrapper.getLayer(), visible);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the group for all layer wrappers that are assigned to it.
|
||||
LayerGroup newGroup = oldGroup.withVisible(visible);
|
||||
getLayerWrappers().values().stream()
|
||||
.filter(wrapper -> wrapper.getLayer().getGroupId() == groupId)
|
||||
.forEach(wrapper -> wrapper.setLayerGroup(newGroup));
|
||||
|
||||
// Update the group in the system model's layout.
|
||||
getLayerGroups().put(groupId, newGroup);
|
||||
|
||||
notifyGroupsChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupName(int groupId, String name) {
|
||||
checkArgument(
|
||||
getLayerGroup(groupId) != null,
|
||||
"A layer group with layer group ID '%d' doesn't exist.",
|
||||
groupId
|
||||
);
|
||||
|
||||
LayerGroup oldGroup = getLayerGroup(groupId);
|
||||
LayerGroup newGroup = oldGroup.withName(name);
|
||||
|
||||
// Update the group for all layer wrappers that are assigned to it.
|
||||
getLayerWrappers().values().stream()
|
||||
.filter(wrapper -> wrapper.getLayer().getGroupId() == groupId)
|
||||
.forEach(wrapper -> wrapper.setLayerGroup(newGroup));
|
||||
|
||||
// Update the group in the system model's layout.
|
||||
getLayerGroups().put(groupId, newGroup);
|
||||
|
||||
notifyGroupsChanged();
|
||||
layerChangeListener.layersChanged();
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
components.clear();
|
||||
}
|
||||
|
||||
protected void restoreLayers() {
|
||||
restoreComponentsMap();
|
||||
|
||||
notifyGroupsInitialized();
|
||||
layerChangeListener.layersInitialized();
|
||||
}
|
||||
|
||||
protected LayerChangeListener getLayerChangeListener() {
|
||||
return layerChangeListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system model the layer manager is working with.
|
||||
*
|
||||
* @return The system model.
|
||||
*/
|
||||
protected SystemModel getSystemModel() {
|
||||
return systemModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model components mapped to the IDs of the layers they are drawn on.
|
||||
*
|
||||
* @return The model components.
|
||||
*/
|
||||
protected Map<Integer, Set<DrawnModelComponent>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view manager the layer manager is working with.
|
||||
*
|
||||
* @return The view manager.
|
||||
*/
|
||||
protected ViewManager getViewManager() {
|
||||
return viewManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of drawings the layer manager is working with.
|
||||
*
|
||||
* @return The set of drawings the layer manager is working with.
|
||||
*/
|
||||
protected Set<Drawing> getDrawings() {
|
||||
return viewManager.getDrawingViewMap().values().stream()
|
||||
.map(scrollPane -> scrollPane.getDrawingView().getDrawing())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the given model component to the given layer ID.
|
||||
*
|
||||
* @param modelComponent The model component to add.
|
||||
* @param layerId The ID of the layer to map the model component to.
|
||||
* @see #getComponents()
|
||||
*/
|
||||
protected void addComponent(DrawnModelComponent modelComponent, int layerId) {
|
||||
components.get(layerId).add(modelComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the mapping for the given model component.
|
||||
*
|
||||
* @param modelComponent The model component to remove.
|
||||
* @see #getComponents()
|
||||
*/
|
||||
protected void removeComponent(DrawnModelComponent modelComponent) {
|
||||
LayerWrapper layerWrapper = modelComponent.getPropertyLayerWrapper().getValue();
|
||||
components.get(layerWrapper.getLayer().getId()).remove(modelComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer wrappers in the system model's layout.
|
||||
*
|
||||
* @return The layer wrappers in the system model's layout.
|
||||
*/
|
||||
protected Map<Integer, LayerWrapper> getLayerWrappers() {
|
||||
return systemModel.getLayoutModel().getPropertyLayerWrappers().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer wrapper for the given layer ID.
|
||||
*
|
||||
* @param layerId The layer ID.
|
||||
* @return The layer wrapper.
|
||||
*/
|
||||
protected LayerWrapper getLayerWrapper(int layerId) {
|
||||
return getLayerWrappers().get(layerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer groups in the system model's layout.
|
||||
*
|
||||
* @return The layer groups in the system model's layout.
|
||||
*/
|
||||
protected Map<Integer, LayerGroup> getLayerGroups() {
|
||||
return systemModel.getLayoutModel().getPropertyLayerGroups().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer group for the given layer ID.
|
||||
*
|
||||
* @param groupId The layer group ID.
|
||||
* @return The layer group.
|
||||
*/
|
||||
protected LayerGroup getLayerGroup(int groupId) {
|
||||
return getLayerGroups().get(groupId);
|
||||
}
|
||||
|
||||
protected void notifyGroupsInitialized() {
|
||||
for (LayerGroupChangeListener listener : layerGroupChangeListeners) {
|
||||
listener.groupsInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyGroupsChanged() {
|
||||
for (LayerGroupChangeListener listener : layerGroupChangeListeners) {
|
||||
listener.groupsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyGroupAdded() {
|
||||
for (LayerGroupChangeListener listener : layerGroupChangeListeners) {
|
||||
listener.groupAdded();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyGroupRemoved() {
|
||||
for (LayerGroupChangeListener listener : layerGroupChangeListeners) {
|
||||
listener.groupRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreComponentsMap() {
|
||||
// Prepare an entry in the components map for every registered layer.
|
||||
for (LayerWrapper wrapper : getLayerWrappers().values()) {
|
||||
components.put(wrapper.getLayer().getId(), new HashSet<>());
|
||||
}
|
||||
|
||||
List<DrawnModelComponent> drawnModelComponents = new ArrayList<>();
|
||||
drawnModelComponents.addAll(systemModel.getPointModels());
|
||||
drawnModelComponents.addAll(systemModel.getPathModels());
|
||||
drawnModelComponents.addAll(systemModel.getLocationModels());
|
||||
drawnModelComponents.addAll(systemModel.getLinkModels());
|
||||
|
||||
// Add all model components to their respective layer.
|
||||
for (DrawnModelComponent modelComponent : drawnModelComponents) {
|
||||
addComponent(
|
||||
modelComponent,
|
||||
modelComponent.getPropertyLayerWrapper().getValue().getLayer().getId()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void layerVisibilityChanged(Layer layer, boolean visible) {
|
||||
Set<Drawing> drawings = getDrawings();
|
||||
SwingUtilities.invokeLater(() -> drawings.forEach(drawing -> drawing.willChange()));
|
||||
SwingUtilities.invokeLater(() -> drawings.forEach(drawing -> drawing.changed()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
/**
|
||||
* A table cell renderer for a disabled check box.
|
||||
*/
|
||||
public class DisabledCheckBoxCellRenderer
|
||||
implements
|
||||
TableCellRenderer {
|
||||
|
||||
private final JCheckBox checkBox;
|
||||
|
||||
public DisabledCheckBoxCellRenderer() {
|
||||
checkBox = new JCheckBox();
|
||||
checkBox.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table, Object value, boolean isSelected,
|
||||
boolean hasFocus, int row, int column
|
||||
) {
|
||||
Color bg = isSelected ? table.getSelectionBackground() : table.getBackground();
|
||||
checkBox.setBackground(bg);
|
||||
checkBox.setEnabled(false);
|
||||
checkBox.setSelected(value == Boolean.TRUE);
|
||||
return checkBox;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
/**
|
||||
* Listens for changes to/updates on layer data.
|
||||
*/
|
||||
public interface LayerChangeListener {
|
||||
|
||||
/**
|
||||
* Notifies the listener that the layer data has been initialized.
|
||||
*/
|
||||
void layersInitialized();
|
||||
|
||||
/**
|
||||
* Notifies the listener that some layer data has changed.
|
||||
*/
|
||||
void layersChanged();
|
||||
|
||||
/**
|
||||
* Notifies the listener that a layer has been added.
|
||||
*/
|
||||
void layerAdded();
|
||||
|
||||
/**
|
||||
* Notifies the listener that a layer has been removed.
|
||||
*/
|
||||
void layerRemoved();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
/**
|
||||
* Provides methods to edit layers.
|
||||
*/
|
||||
public interface LayerEditor {
|
||||
|
||||
/**
|
||||
* Sets a layer's visible state.
|
||||
*
|
||||
* @param layerId The ID of the layer.
|
||||
* @param visible The layer's new visible state.
|
||||
*/
|
||||
void setLayerVisible(int layerId, boolean visible);
|
||||
|
||||
/**
|
||||
* Sets a layer's name.
|
||||
*
|
||||
* @param layerId The ID of the layer.
|
||||
* @param name The layer's new name.
|
||||
*/
|
||||
void setLayerName(int layerId, String name);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import org.opentcs.data.model.visualization.LayerGroup;
|
||||
|
||||
/**
|
||||
* A table cell renderer for {@link LayerGroup}s.
|
||||
*/
|
||||
public class LayerGroupCellRenderer
|
||||
extends
|
||||
DefaultTableCellRenderer {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public LayerGroupCellRenderer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValue(Object value) {
|
||||
setText(((LayerGroup) value).getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
/**
|
||||
* Listens for changes to/updates on layer group data.
|
||||
*/
|
||||
public interface LayerGroupChangeListener {
|
||||
|
||||
/**
|
||||
* Notifies the listener that the layer group data has been initialized.
|
||||
*/
|
||||
void groupsInitialized();
|
||||
|
||||
/**
|
||||
* Notifies the listener that some layer group data has changed.
|
||||
*/
|
||||
void groupsChanged();
|
||||
|
||||
/**
|
||||
* Notifies the listener that a layer group has been added.
|
||||
*/
|
||||
void groupAdded();
|
||||
|
||||
/**
|
||||
* Notifies the listener that a layer group has been removed.
|
||||
*/
|
||||
void groupRemoved();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
/**
|
||||
* Provides methods to edit layer groups.
|
||||
*/
|
||||
public interface LayerGroupEditor {
|
||||
|
||||
/**
|
||||
* Sets a layer group's visible state.
|
||||
*
|
||||
* @param groupId The ID of the layer group.
|
||||
* @param visible The layer group's new visible state.
|
||||
*/
|
||||
void setGroupVisible(int groupId, boolean visible);
|
||||
|
||||
/**
|
||||
* Sets a layer group's name.
|
||||
*
|
||||
* @param groupId The ID of the layer group.
|
||||
* @param name The layer group's new name.
|
||||
*/
|
||||
void setGroupName(int groupId, String name);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
/**
|
||||
* Organizes the layer groups in a plant model.
|
||||
*/
|
||||
public interface LayerGroupManager
|
||||
extends
|
||||
LayerGroupEditor {
|
||||
|
||||
/**
|
||||
* Add a listener to the set that's notified each time a change to group data occurs.
|
||||
*
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
void addLayerGroupChangeListener(LayerGroupChangeListener listener);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.layer;
|
||||
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
|
||||
/**
|
||||
* Organizes the layers in a plant model.
|
||||
*/
|
||||
public interface LayerManager
|
||||
extends
|
||||
Lifecycle,
|
||||
LayerEditor,
|
||||
EventHandler {
|
||||
|
||||
/**
|
||||
* Sets the listener for layer data changes.
|
||||
*
|
||||
* @param listener The listener for layer data changes.
|
||||
*/
|
||||
void setLayerChangeListener(LayerChangeListener listener);
|
||||
|
||||
/**
|
||||
* Returns whether the layer with the given layer ID contains any components.
|
||||
*
|
||||
* @param layerId The ID of the layer.
|
||||
* @return Whether the layer contains any components.
|
||||
*/
|
||||
boolean containsComponents(int layerId);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.properties;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import org.opentcs.guing.base.model.ModelComponent;
|
||||
import org.opentcs.thirdparty.guing.common.jhotdraw.application.action.edit.UndoRedoManager;
|
||||
|
||||
/**
|
||||
* Base implementation for visualisation of model component properties.
|
||||
*/
|
||||
public abstract class AbstractAttributesContent
|
||||
implements
|
||||
AttributesContent {
|
||||
|
||||
/**
|
||||
* The model component to show the properties of.
|
||||
*/
|
||||
protected ModelComponent fModel;
|
||||
/**
|
||||
* The undo manager.
|
||||
*/
|
||||
protected UndoRedoManager fUndoRedoManager;
|
||||
/**
|
||||
* The swing component.
|
||||
*/
|
||||
protected JComponent fComponent;
|
||||
|
||||
/**
|
||||
* Creates a new instance of AbstractAttributesContent
|
||||
*/
|
||||
public AbstractAttributesContent() {
|
||||
}
|
||||
|
||||
@Override // AttributesContent
|
||||
public void setModel(ModelComponent model) {
|
||||
fModel = model;
|
||||
}
|
||||
|
||||
@Override // AttributesContent
|
||||
public abstract void reset();
|
||||
|
||||
@Override // AttributesContent
|
||||
public JComponent getComponent() {
|
||||
return fComponent;
|
||||
}
|
||||
|
||||
@Override // AttributesContent
|
||||
public String getDescription() {
|
||||
return fModel.getDescription();
|
||||
}
|
||||
|
||||
@Override // AttributesContent
|
||||
public void setup(UndoRedoManager undoManager) {
|
||||
fUndoRedoManager = undoManager;
|
||||
fComponent = createComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the component.
|
||||
*
|
||||
* @return The created component.
|
||||
*/
|
||||
protected abstract JComponent createComponent();
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.guing.common.components.properties;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Provider;
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import javax.swing.table.TableModel;
|
||||
import org.opentcs.guing.base.components.properties.type.Property;
|
||||
import org.opentcs.guing.common.components.properties.event.TableChangeListener;
|
||||
import org.opentcs.guing.common.components.properties.event.TableSelectionChangeEvent;
|
||||
import org.opentcs.guing.common.components.properties.table.AttributesTable;
|
||||
|
||||
/**
|
||||
* Base implementation for content that displays model properties in a table.
|
||||
*/
|
||||
public abstract class AbstractTableContent
|
||||
extends
|
||||
AbstractAttributesContent
|
||||
implements
|
||||
TableChangeListener {
|
||||
|
||||
/**
|
||||
* The table that shows the properties.
|
||||
*/
|
||||
protected AttributesTable fTable;
|
||||
/**
|
||||
* The cell editors.
|
||||
*/
|
||||
protected List<TableCellEditor> fCellEditors = new ArrayList<>();
|
||||
/**
|
||||
* Indicates that changes to the tabel model should also update the model component.
|
||||
*/
|
||||
protected boolean fEvaluateTableChanges = true;
|
||||
/**
|
||||
* Provides attribute tables.
|
||||
*/
|
||||
private final Provider<AttributesTable> tableProvider;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param tableProvider Provides attribute tables.
|
||||
*/
|
||||
public AbstractTableContent(Provider<AttributesTable> tableProvider) {
|
||||
this.tableProvider = requireNonNull(tableProvider, "tableProvider");
|
||||
}
|
||||
|
||||
@Override // AbstractAttributesContent
|
||||
protected JComponent createComponent() {
|
||||
JPanel component = new JPanel();
|
||||
|
||||
initTable();
|
||||
JScrollPane scrollPane = new JScrollPane(fTable);
|
||||
|
||||
component.setLayout(new BorderLayout());
|
||||
component.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override // TableChangeListener
|
||||
public void tableSelectionChanged(TableSelectionChangeEvent e) {
|
||||
}
|
||||
|
||||
@Override // TableChangeListener
|
||||
public void tableModelChanged() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the table.
|
||||
*/
|
||||
protected void initTable() {
|
||||
fTable = tableProvider.get();
|
||||
setTableCellRenderers();
|
||||
setTableCellEditors();
|
||||
fTable.addTableChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table cell renderers.
|
||||
*/
|
||||
protected void setTableCellRenderers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table cell editors.
|
||||
*/
|
||||
protected void setTableCellEditors() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new table content.
|
||||
*
|
||||
* @param content A map from property name to property.
|
||||
*/
|
||||
protected void setTableContent(Map<String, Property> content) {
|
||||
fEvaluateTableChanges = false;
|
||||
|
||||
TableColumnModel columnModel = fTable.getColumnModel();
|
||||
int[] widths = new int[columnModel.getColumnCount()];
|
||||
|
||||
for (int i = 0; i < widths.length; i++) {
|
||||
widths[i] = columnModel.getColumn(i).getWidth();
|
||||
}
|
||||
|
||||
fTable.setModel(createTableModel(content));
|
||||
|
||||
for (int i = 0; i < widths.length; i++) {
|
||||
columnModel.getColumn(i).setPreferredWidth(widths[i]);
|
||||
}
|
||||
|
||||
fEvaluateTableChanges = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new table model from the content.
|
||||
*
|
||||
* @param content Map from property name to property.
|
||||
* @return A table model that represents the content.
|
||||
*/
|
||||
protected abstract TableModel createTableModel(Map<String, Property> content);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user