diff --git a/diylc/diylc-core/src/org/diylc/common/EventType.java b/diylc/diylc-core/src/org/diylc/common/EventType.java index c22e4858..8970a8d7 100644 --- a/diylc/diylc-core/src/org/diylc/common/EventType.java +++ b/diylc/diylc-core/src/org/diylc/common/EventType.java @@ -102,5 +102,9 @@ public enum EventType { /** * Called to update the status message. New status message is passed as a first parameter. */ - STATUS_MESSAGE_CHANGED; + STATUS_MESSAGE_CHANGED, + /** + * Signals the listeners to scroll to selection. + */ + SCROLL_TO; } diff --git a/diylc/diylc-core/src/org/diylc/common/ISelectionProcessor.java b/diylc/diylc-core/src/org/diylc/common/ISelectionProcessor.java index 56075d10..e754ccc7 100644 --- a/diylc/diylc-core/src/org/diylc/common/ISelectionProcessor.java +++ b/diylc/diylc-core/src/org/diylc/common/ISelectionProcessor.java @@ -113,5 +113,12 @@ public interface ISelectionProcessor { * @param yOffset * @param includeStuckComponents */ - void nudgeSelection(Size xOffset, Size yOffset, boolean includeStuckComponents); + void nudgeSelection(Size xOffset, Size yOffset, boolean includeStuckComponents); + + /** + * Selects components that match the criteria. + * + * @param criteria + */ + void selectMatching(String criteria); } diff --git a/diylc/diylc-core/src/org/diylc/common/Orientation.java b/diylc/diylc-core/src/org/diylc/common/Orientation.java index 48b75aa4..4e61f43a 100644 --- a/diylc/diylc-core/src/org/diylc/common/Orientation.java +++ b/diylc/diylc-core/src/org/diylc/common/Orientation.java @@ -32,5 +32,25 @@ public enum Orientation { } else { return name().replace("_", "") + " degrees clockwise"; } + } + + public double toDegrees() { + switch (this) { + case DEFAULT: + return 0; + case _180: + return 180; + case _270: + return 270; + case _90: + return 90; + default: + break; + } + return 0; + } + + public double toRadians() { + return Math.toRadians(toDegrees()); } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java index d38701bf..e3c04ded 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java @@ -1003,6 +1003,19 @@ public class Presenter implements IPlugInPort { messageDispatcher.dispatchMessage(EventType.REPAINT); drawingManager.clearContinuityArea(); } + + @Override + public void selectMatching(String criteria) { + Set> matching = new HashSet>(); + for (IDIYComponent c : currentProject.getComponents()) { + if ((c.getName() != null && c.getName().toLowerCase().matches(criteria.toLowerCase())) || + (c.getValueForDisplay() != null && c.getValueForDisplay().toLowerCase().matches(criteria.toLowerCase()))) + matching.add(c); + } + updateSelection(matching); + messageDispatcher.dispatchMessage(EventType.REPAINT); + messageDispatcher.dispatchMessage(EventType.SCROLL_TO, getSelectionBounds(true)); + } @Override public VersionNumber getCurrentVersionNumber() { @@ -1924,7 +1937,7 @@ public class Presenter implements IPlugInPort { if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.STICKY_POINTS_KEY, true)) { includeStuckComponents(controlPointMap); } - messageDispatcher.dispatchMessage(EventType.SELECTION_CHANGED, selectedComponents, controlPointMap.keySet()); + messageDispatcher.dispatchMessage(EventType.SELECTION_CHANGED, selectedComponents, controlPointMap.keySet()); } private static boolean useNetlistForExpandSelection = false; diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 3a40c7ba..ee6da5e1 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -2934,6 +2934,10 @@ 2020-04-09 00:00:00.000 CET + + NEW_FEATURE + Added "Find" functionality that locates and selects component(s) matching the search text + NEW_FEATURE Added "Tag Strip" component for point-to-point layouts diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index 855a12dc..e0d05d8a 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java index a983e70c..81108058 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java +++ b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java @@ -73,13 +73,14 @@ import org.diylc.swing.gui.DialogFactory; import org.diylc.swing.gui.editor.PropertyEditorDialog; import org.diylc.swing.plugins.config.ConfigPlugin; import org.diylc.swing.plugins.edit.ComponentTransferable; +import org.diylc.swing.plugins.edit.FindDialog; import org.diylc.swing.plugins.file.BomDialog; import org.diylc.swing.plugins.file.FileFilterEnum; import org.diylc.swingframework.ButtonDialog; import org.diylc.swingframework.CheckBoxListDialog; import org.diylc.swingframework.IDrawingProvider; -import org.diylc.swingframework.export.DrawingExporter; import org.diylc.swingframework.TextDialog; +import org.diylc.swingframework.export.DrawingExporter; import org.diylc.utils.BomEntry; import com.thoughtworks.xstream.XStream; @@ -231,6 +232,10 @@ public class ActionFactory { public NudgeAction createNudgeAction(IPlugInPort plugInPort) { return new NudgeAction(plugInPort); } + + public FindAction createFindAction(IPlugInPort plugInPort, ISwingUI swingUI) { + return new FindAction(plugInPort, swingUI); + } // Config actions. @@ -1545,6 +1550,38 @@ public class ActionFactory { plugInPort.bringSelectionToFront(); } } + + public static class FindAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + private IPlugInPort plugInPort; + private ISwingUI swingUI; + + public FindAction(IPlugInPort plugInPort, ISwingUI swingUI) { + super(); + this.plugInPort = plugInPort; + this.swingUI = swingUI; + putValue(AbstractAction.NAME, "Find"); + putValue(AbstractAction.SMALL_ICON, IconLoader.SearchBox.getIcon()); + putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, ActionEvent.CTRL_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + LOG.info("Find triggered"); + FindDialog dialog = DialogFactory.getInstance().createFindDialog(); + dialog.setVisible(true); + + if (dialog.getSelectedButtonCaption() == FindDialog.OK) { + String criteria = dialog.getCriteria(); + plugInPort.selectMatching(criteria); + if (plugInPort.getSelectedComponents().size() == 0) { + swingUI.showMessage("No matching components found.", "Find", ISwingUI.INFORMATION_MESSAGE); + } + } + } + } public static class ConfigAction extends AbstractAction { diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/DialogFactory.java b/diylc/diylc-swing/src/org/diylc/swing/gui/DialogFactory.java index d7f441ba..4c1fdd95 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/gui/DialogFactory.java +++ b/diylc/diylc-swing/src/org/diylc/swing/gui/DialogFactory.java @@ -36,6 +36,7 @@ import org.diylc.swing.plugins.cloud.view.ChangePasswordDialog; import org.diylc.swing.plugins.cloud.view.LoginDialog; import org.diylc.swing.plugins.cloud.view.UploadDialog; import org.diylc.swing.plugins.cloud.view.UserEditDialog; +import org.diylc.swing.plugins.edit.FindDialog; import org.diylc.swing.plugins.file.BomDialog; import org.diylc.swingframework.AboutDialog; import org.diylc.swingframework.IFileChooserAccessory; @@ -196,6 +197,11 @@ public class DialogFactory { LoginDialog dialog = new LoginDialog(mainFrame); return dialog; } + + public FindDialog createFindDialog() { + FindDialog dialog = new FindDialog(mainFrame); + return dialog; + } public UploadDialog createUploadDialog(JFrame ownerFrame, IPlugInPort plugInPort, String[] categories, boolean isUpdate) { diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPlugin.java index edf57b22..7c0ac26a 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPlugin.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPlugin.java @@ -706,7 +706,7 @@ public class CanvasPlugin implements IPlugIn, ClipboardOwner { @Override public EnumSet getSubscribedEventTypes() { - return EnumSet.of(EventType.PROJECT_LOADED, EventType.ZOOM_CHANGED, EventType.REPAINT); + return EnumSet.of(EventType.PROJECT_LOADED, EventType.ZOOM_CHANGED, EventType.REPAINT, EventType.SCROLL_TO); } @SuppressWarnings("incomplete-switch") @@ -759,6 +759,12 @@ public class CanvasPlugin implements IPlugIn, ClipboardOwner { } }); break; + case SCROLL_TO: + Rectangle visibleRect2 = canvasPanel.getVisibleRect(); + Rectangle2D targetRect = (Rectangle2D) params[0]; + canvasPanel.scrollRectToVisible(new Rectangle((int)(targetRect.getCenterX() - visibleRect2.width / 2), + (int)(targetRect.getCenterY() - visibleRect2.height / 2), visibleRect2.width, visibleRect2.height)); + break; } // } // }); diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/EditMenuPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/EditMenuPlugin.java index 303fd462..79050b43 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/EditMenuPlugin.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/EditMenuPlugin.java @@ -74,7 +74,8 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner { private ActionFactory.RotateSelectionAction rotateClockwiseAction; private ActionFactory.RotateSelectionAction rotateCounterClockwiseAction; private ActionFactory.MirrorSelectionAction mirrorHorizontallyAction; - private ActionFactory.MirrorSelectionAction mirrorVerticallyAction; + private ActionFactory.MirrorSelectionAction mirrorVerticallyAction; + private ActionFactory.FindAction findAction; private UndoHandler undoHandler; @@ -261,6 +262,12 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner { ActionFactory.getInstance().createMirrorSelectionAction(plugInPort, IComponentTransformer.VERTICAL); } return mirrorVerticallyAction; + } + + public ActionFactory.FindAction getFindAction() { + if (findAction == null) + findAction = ActionFactory.getInstance().createFindAction(plugInPort, swingUI); + return findAction; } @Override @@ -276,7 +283,8 @@ public class EditMenuPlugin implements IPlugIn, ClipboardOwner { swingUI.injectMenuAction(getCopyAction(), EDIT_TITLE); swingUI.injectMenuAction(getPasteAction(), EDIT_TITLE); swingUI.injectMenuAction(getDuplicateAction(), EDIT_TITLE); - swingUI.injectMenuAction(null, EDIT_TITLE); + swingUI.injectMenuAction(null, EDIT_TITLE); + swingUI.injectMenuAction(getFindAction(), EDIT_TITLE); swingUI.injectMenuAction(actionFactory.createSelectAllAction(plugInPort), EDIT_TITLE); swingUI.injectMenuAction(getEditSelectionAction(), EDIT_TITLE); swingUI.injectMenuAction(getDeleteSelectionAction(), EDIT_TITLE); diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/FindDialog.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/FindDialog.java new file mode 100644 index 00000000..e18929fb --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/edit/FindDialog.java @@ -0,0 +1,118 @@ +/* + + DIY Layout Creator (DIYLC). + Copyright (c) 2009-2018 held jointly by the individual authors. + + This file is part of DIYLC. + + DIYLC is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DIYLC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DIYLC. If not, see . + +*/ +package org.diylc.swing.plugins.edit; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.diylc.swingframework.ButtonDialog; + +public class FindDialog extends ButtonDialog { + + private static final long serialVersionUID = 1L; + + private JPanel mainPanel; + + private JTextField criteriaField; + + private String criteria; + + public FindDialog(JFrame owner) { + super(owner, "Find", new String[] {OK, CANCEL}); + setMinimumSize(new Dimension(240, 32)); + layoutGui(); + refreshState(); + } + + public String getCriteria() { + return criteria; + } + + @Override + protected JComponent getMainComponent() { + if (mainPanel == null) { + mainPanel = new JPanel(new GridBagLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.fill = GridBagConstraints.NONE; + gbc.insets = new Insets(2, 2, 2, 2); + + gbc.gridx = 0; + + gbc.gridy = 0; + mainPanel.add(new JLabel("Look for:"), gbc); + + gbc.gridx = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1; + + gbc.gridy = 0; + mainPanel.add(getCriteriaField(), gbc); + } + return mainPanel; + } + + private void refreshState() { + this.criteria = getCriteriaField().getText(); + + JButton okButton = getButton(OK); + okButton.setEnabled(this.criteria.length() > 0); + } + + private JTextField getCriteriaField() { + if (criteriaField == null) { + criteriaField = new JTextField(); + criteriaField.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void removeUpdate(DocumentEvent e) { + refreshState(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + refreshState(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + refreshState(); + } + }); + } + return criteriaField; + } +}