1 /* 2 * Copyright (c) 2016 Vivid Solutions. 3 * 4 * All rights reserved. This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. 7 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html 8 * and the Eclipse Distribution License is available at 9 * 10 * http://www.eclipse.org/org/documents/edl-v10.php. 11 */ 12 package org.locationtech.jtstest.testbuilder; 13 14 import java.awt.BorderLayout; 15 import java.awt.Dimension; 16 import java.awt.FlowLayout; 17 import java.awt.Font; 18 import java.awt.GridLayout; 19 import java.awt.Insets; 20 import java.awt.event.ActionEvent; 21 import java.awt.event.ActionListener; 22 import java.util.Vector; 23 24 import javax.swing.BorderFactory; 25 import javax.swing.BoxLayout; 26 import javax.swing.ComboBoxModel; 27 import javax.swing.DefaultComboBoxModel; 28 import javax.swing.ImageIcon; 29 import javax.swing.JButton; 30 import javax.swing.JCheckBox; 31 import javax.swing.JComboBox; 32 import javax.swing.JComponent; 33 import javax.swing.JLabel; 34 import javax.swing.JPanel; 35 import javax.swing.JTextField; 36 import javax.swing.SwingConstants; 37 import javax.swing.border.EmptyBorder; 38 39 import org.locationtech.jts.operation.buffer.BufferParameters; 40 import org.locationtech.jts.util.Stopwatch; 41 import org.locationtech.jtstest.geomfunction.BaseGeometryFunction; 42 import org.locationtech.jtstest.geomfunction.GeometryFunction; 43 import org.locationtech.jtstest.geomfunction.GeometryFunctionRegistry; 44 import org.locationtech.jtstest.geomfunction.RepeaterGeometryFunction; 45 import org.locationtech.jtstest.geomfunction.SpreaderGeometryFunction; 46 import org.locationtech.jtstest.testbuilder.controller.JTSTestBuilderController; 47 import org.locationtech.jtstest.testbuilder.event.GeometryFunctionEvent; 48 import org.locationtech.jtstest.testbuilder.event.GeometryFunctionListener; 49 import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; 50 import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelListener; 51 import org.locationtech.jtstest.testbuilder.ui.SwingUtil; 52 import org.locationtech.jtstest.util.ClassUtil; 53 54 55 /** 56 * @version 1.7 57 */ 58 public class SpatialFunctionPanel 59 extends JPanel implements FunctionPanel 60 { 61 private static final EmptyBorder LABEL_BORDER = new EmptyBorder(3,5,3,5); 62 63 private static final String[] PARAM_DEFAULT = { "10", "0", "0", "0", "0" }; 64 65 private static String[] capStyleItems = new String[] { "Round", "Flat", "Square" }; 66 private static Object[] capStyleValues = new Object[] { 67 BufferParameters.CAP_ROUND, 68 BufferParameters.CAP_FLAT, 69 BufferParameters.CAP_SQUARE 70 }; 71 private static String[] joinStyleItems = new String[] { "Round", "Mitre", "Bevel" }; 72 private static Object[] joinStyleValues = new Object[] { 73 BufferParameters.JOIN_ROUND, 74 BufferParameters.JOIN_MITRE, 75 BufferParameters.JOIN_BEVEL 76 }; 77 78 79 80 JPanel panelExecControl = new JPanel(); 81 // GeometryFunctionListPanel geomFuncPanel = new GeometryFunctionListPanel(); 82 GeometryFunctionTreePanel geomFuncPanel = new GeometryFunctionTreePanel(); 83 GridLayout gridLayout1 = new GridLayout(); 84 GridLayout gridLayout2 = new GridLayout(); 85 86 87 BorderLayout borderLayout1 = new BorderLayout(); 88 BorderLayout borderLayout2 = new BorderLayout(); 89 90 JPanel panelParam = new JPanel(); 91 JPanel panelExec = new JPanel(); 92 JPanel panelExecMeta = new JPanel(); 93 JPanel panelExecParam = new JPanel(); 94 FlowLayout flowLayout = new FlowLayout(); 95 FlowLayout flowLayout1 = new FlowLayout(); 96 FlowLayout flowLayout2 = new FlowLayout(); 97 98 private JButton execButton = new JButton(); 99 private JButton execToNewButton = new JButton(); 100 101 private final ImageIcon clearIcon = new ImageIcon(this.getClass().getResource("clear.gif")); 102 private final ImageIcon expandDownIcon = new ImageIcon(this.getClass().getResource("Expand-Down.png")); 103 104 private transient Vector spatialFunctionPanelListeners; 105 private JPanel panelControl = new JPanel(); 106 private JCheckBox displayAAndBCheckBox = new JCheckBox(); 107 private JCheckBox cbExecEachA = new JCheckBox(); 108 private JCheckBox cbExecEachB = new JCheckBox(); 109 private JCheckBox cbExecRepeat = new JCheckBox(); 110 private final JTextField txtRepeatCount = new JTextField(); 111 private JButton btnClearResult = new JButton(); 112 113 private JLabel lblFunctionName = new JLabel(); 114 private JLabel lblFunction = new JLabel(); 115 private JLabel lblDistance = new JLabel(); 116 private JTextField txtDistance = new JTextField(); 117 private JLabel lblQuadSegs = new JLabel(); 118 private JTextField txtQuadrantSegs = new JTextField(); 119 private JLabel lblCapStyle = new JLabel(); 120 private JComboBox cbCapStyle = new JComboBox(); 121 private JLabel lblJoinStyle = new JLabel(); 122 private JComboBox cbJoinStyle = new JComboBox(); 123 private JLabel lblMitreLimit = new JLabel(); 124 private JTextField txtMitreLimit = new JTextField(); 125 126 private JComponent[] paramComp = { txtDistance, txtQuadrantSegs, cbCapStyle, cbJoinStyle, txtMitreLimit }; 127 private JLabel[] paramLabel = { lblDistance, lblQuadSegs, lblCapStyle, lblJoinStyle, lblMitreLimit }; 128 129 private GeometryFunction currentFunc = null; 130 private Stopwatch timer; 131 132 private JButton btnExecEach; 133 SpatialFunctionPanel()134 public SpatialFunctionPanel() { 135 try { 136 uiInit(); 137 } 138 catch (Exception ex) { 139 ex.printStackTrace(); 140 } 141 } 142 uiInit()143 void uiInit() throws Exception { 144 this.setLayout(borderLayout1); 145 146 // geomFuncPanel.populate(JTSTestBuilder.getFunctionRegistry().getGeometryFunctions()); 147 geomFuncPanel.populate(JTSTestBuilder.getFunctionRegistry().getCategorizedGeometryFunctions()); 148 149 panelParam.setLayout(gridLayout2); 150 gridLayout2.setRows(6); 151 gridLayout2.setColumns(2); 152 panelExec.setLayout(flowLayout); 153 panelExecMeta.setLayout(flowLayout2); 154 panelExecParam.setLayout(borderLayout2); 155 156 157 displayAAndBCheckBox.setSelected(true); 158 displayAAndBCheckBox.setToolTipText(""); 159 displayAAndBCheckBox.setText("Display Input"); 160 displayAAndBCheckBox.addActionListener(new java.awt.event.ActionListener() { 161 public void actionPerformed(ActionEvent e) { 162 displayAAndBCheckBox_actionPerformed(e); 163 } 164 }); 165 166 lblFunction.setText("Function"); 167 lblFunction.setHorizontalAlignment(SwingConstants.RIGHT); 168 lblFunction.setBorder(LABEL_BORDER);//top,left,bottom,right 169 170 lblFunctionName.setHorizontalAlignment(SwingConstants.LEFT); 171 lblFunctionName.setFont(new java.awt.Font("Dialog", Font.BOLD, 12)); 172 173 lblDistance.setText("Distance"); 174 175 txtDistance.setMaximumSize(new Dimension(25, 2147483647)); 176 txtDistance.setMinimumSize(new Dimension(25, 21)); 177 txtDistance.setPreferredSize(new Dimension(25, 17)); 178 txtDistance.setText(PARAM_DEFAULT[0]); 179 txtDistance.setHorizontalAlignment(SwingConstants.RIGHT); 180 181 lblQuadSegs.setText("Quadrant Segs"); 182 txtQuadrantSegs.setHorizontalAlignment(SwingConstants.RIGHT); 183 184 lblCapStyle.setText("Cap Style"); 185 ComboBoxModel modelCapStyle = new DefaultComboBoxModel(capStyleItems); 186 cbCapStyle.setModel(modelCapStyle); 187 188 lblJoinStyle.setText("Join Style"); 189 ComboBoxModel modelJoinStyle = new DefaultComboBoxModel(joinStyleItems); 190 cbJoinStyle.setModel(modelJoinStyle); 191 192 lblMitreLimit.setText("Mitre Limit"); 193 txtMitreLimit.setHorizontalAlignment(SwingConstants.RIGHT); 194 195 initLabels(paramLabel); 196 197 198 199 btnClearResult.setToolTipText(""); 200 btnClearResult.setMargin(new Insets(0, 10, 0, 10)); 201 btnClearResult.setSelected(true); 202 btnClearResult.setText("Clear Result"); 203 btnClearResult.addActionListener(new java.awt.event.ActionListener() { 204 205 public void actionPerformed(ActionEvent e) { 206 clearResultButton_actionPerformed(e); 207 } 208 }); 209 210 panelParam.add(lblFunction); 211 panelParam.add(lblFunctionName); 212 panelParam.add(lblDistance); 213 panelParam.add(txtDistance); 214 panelParam.add(lblQuadSegs); 215 panelParam.add(txtQuadrantSegs); 216 panelParam.add(lblCapStyle); 217 panelParam.add(cbCapStyle); 218 panelParam.add(lblJoinStyle); 219 panelParam.add(cbJoinStyle); 220 panelParam.add(lblMitreLimit); 221 panelParam.add(txtMitreLimit); 222 223 panelControl.setLayout(flowLayout1); 224 panelControl.add(displayAAndBCheckBox, null); 225 //panelControl.add(btnClearResult, null); 226 227 228 cbExecEachA.setToolTipText("Compute for each A geometry element"); 229 cbExecEachA.setText("Each A"); 230 231 cbExecEachB.setToolTipText("Compute for each B geometry element"); 232 cbExecEachB.setText("Each B"); 233 234 cbExecRepeat.setToolTipText("Repeat function a number of times, incrementing the first parameter"); 235 cbExecRepeat.setText("Repeat"); 236 237 txtRepeatCount.setMaximumSize(new Dimension(25, 2147483647)); 238 txtRepeatCount.setMinimumSize(new Dimension(30, 21)); 239 txtRepeatCount.setPreferredSize(new Dimension(30, 21)); 240 txtRepeatCount.setText("10"); 241 txtRepeatCount.setHorizontalAlignment(SwingConstants.RIGHT); 242 243 execButton = SwingUtil.createButton(AppIcons.EXECUTE, AppStrings.TIP_EXECUTE, 244 new ActionListener() { 245 public void actionPerformed(ActionEvent e) { 246 execButton_actionPerformed(e); 247 } 248 }); 249 execButton.setEnabled(false); 250 251 execToNewButton = SwingUtil.createButton("New", AppIcons.EXECUTE, "Compute function result to a new case", 252 new ActionListener() { 253 public void actionPerformed(ActionEvent e) { 254 execToNewButton_actionPerformed(e); 255 } 256 }); 257 execToNewButton.setEnabled(false); 258 259 JButton btnShowExecExt = SwingUtil.createButton(expandDownIcon, "Show extended/meta Compute tools", 260 new ActionListener() { 261 public void actionPerformed(ActionEvent e) { 262 clearExtended(); 263 panelExecMeta.setVisible(! panelExecMeta.isVisible()); 264 } 265 }); 266 btnShowExecExt.setPreferredSize(new Dimension(20, 20)); 267 btnShowExecExt.setBorder(BorderFactory.createEmptyBorder()); 268 btnShowExecExt.setContentAreaFilled(false); 269 btnShowExecExt.setFocusable(false); 270 271 panelExec.add(execButton); 272 // disabled until behaviour is worked out 273 panelExec.add(execToNewButton); 274 //panelExec.add(btnShowExecExt); 275 276 JPanel panelExecHolder = new JPanel(); 277 panelExecHolder.setLayout(new BorderLayout()); 278 panelExecHolder.add(panelExec, BorderLayout.CENTER); 279 panelExecHolder.add(btnShowExecExt, BorderLayout.EAST); 280 281 panelExecMeta.add(cbExecEachA); 282 panelExecMeta.add(cbExecEachB); 283 panelExecMeta.add(cbExecRepeat); 284 panelExecMeta.add(txtRepeatCount); 285 panelExecMeta.setVisible(false); 286 287 panelExecControl.setLayout(new BoxLayout(panelExecControl, BoxLayout.Y_AXIS)); 288 panelExecControl.add(panelExecHolder); 289 panelExecControl.add(panelExecMeta); 290 panelExecControl.add(panelControl); 291 292 panelExecParam.add(panelParam, BorderLayout.CENTER); 293 panelExecParam.add(panelExecControl, BorderLayout.SOUTH); 294 295 this.add(geomFuncPanel, BorderLayout.CENTER); 296 this.add(panelExecParam, BorderLayout.SOUTH); 297 298 GeometryFunctionListener gfListener = new GeometryFunctionListener() { 299 public void functionSelected(GeometryFunctionEvent e) { 300 functionChanged(e.getFunction()); 301 } 302 public void functionInvoked(GeometryFunctionEvent e) { 303 execFunction(e.getFunction(), false); 304 } 305 }; 306 geomFuncPanel.addGeometryFunctionListener(gfListener); 307 308 hideAllParams(paramComp, paramLabel); 309 } 310 initLabels(JLabel[] paramLabel)311 static void initLabels(JLabel[] paramLabel) 312 { 313 for (int i = 0; i < paramLabel.length; i++) { 314 JLabel lbl = paramLabel[i]; 315 lbl.setHorizontalAlignment(SwingConstants.RIGHT); 316 lbl.setBorder(LABEL_BORDER); 317 } 318 } 319 enableExecuteControl(boolean isEnabled)320 public void enableExecuteControl(boolean isEnabled) 321 { 322 execButton.setEnabled(isEnabled); 323 execToNewButton.setEnabled(isEnabled); 324 } 325 clearResultButton_actionPerformed(ActionEvent e)326 void clearResultButton_actionPerformed(ActionEvent e) { 327 clearFunction(); 328 } 329 execButton_actionPerformed(ActionEvent e)330 void execButton_actionPerformed(ActionEvent e) { 331 execFunction(getMetaFunction(), false); 332 } 333 execToNewButton_actionPerformed(ActionEvent e)334 void execToNewButton_actionPerformed(ActionEvent e) { 335 execFunction(getMetaFunction(), true); 336 } 337 getMetaFunction()338 GeometryFunction getMetaFunction() { 339 GeometryFunction funToRun = geomFuncPanel.getFunction(); 340 if (! isMetaFunctionEnabled()) return funToRun; 341 342 if (isFunctionRepeated()) { 343 int count = SwingUtil.getInteger(txtRepeatCount, 10); 344 funToRun = new RepeaterGeometryFunction(funToRun, count); 345 } 346 if (isFunctionEach()) { 347 funToRun = new SpreaderGeometryFunction(funToRun, isEachA(), isEachB()); 348 } 349 return funToRun; 350 } 351 352 isMetaFunctionEnabled()353 private boolean isMetaFunctionEnabled() { 354 return panelExecMeta.isVisible(); 355 } 356 isFunctionRepeated()357 private boolean isFunctionRepeated() { 358 return cbExecRepeat.isSelected(); 359 } isFunctionEach()360 private boolean isFunctionEach() { 361 return cbExecEachA.isSelected() || cbExecEachB.isSelected(); 362 } isEachA()363 private boolean isEachA() { 364 return cbExecEachA.isSelected(); 365 } isEachB()366 private boolean isEachB() { 367 return cbExecEachB.isSelected(); 368 } clearExtended()369 void clearExtended() { 370 cbExecRepeat.setSelected(false); 371 cbExecEachA.setSelected(false); 372 cbExecEachB.setSelected(false); 373 } 374 displayAAndBCheckBox_actionPerformed(ActionEvent e)375 void displayAAndBCheckBox_actionPerformed(ActionEvent e) { 376 JTSTestBuilderController.editPanel().setShowingInput(displayAAndBCheckBox.isSelected()); 377 } 378 setCurrentFunction(GeometryFunction func)379 private void setCurrentFunction(GeometryFunction func) { 380 currentFunc = func; 381 // fire execution event even if null, to set UI appropriately 382 fireFunctionExecuted(new SpatialFunctionPanelEvent(this)); 383 } 384 execFunction(GeometryFunction func, boolean createNew)385 public void execFunction(GeometryFunction func, boolean createNew) { 386 currentFunc = func; 387 if (currentFunc == null) 388 return; 389 fireFunctionExecuted(new SpatialFunctionPanelEvent(this, createNew)); 390 } 391 functionChanged(GeometryFunction func)392 private void functionChanged(GeometryFunction func) 393 { 394 currentFunc = func; 395 lblFunctionName.setText(func.getName()); 396 lblFunctionName.setToolTipText( GeometryFunctionRegistry.functionDescriptionHTML(func) ); 397 398 updateParameters(func, paramComp, paramLabel); 399 400 execButton.setEnabled(true); 401 execToNewButton.setEnabled(true); 402 } 403 updateParameters(GeometryFunction func, JComponent[] paramComp, JLabel[] paramLabel)404 static void updateParameters(GeometryFunction func, JComponent[] paramComp, JLabel[] paramLabel) { 405 int numNonGeomParams = numNonGeomParams(func); 406 int indexOffset = BaseGeometryFunction.firstScalarParamIndex(func); 407 for (int i = 0; i < paramComp.length; i++) { 408 boolean isUsed = numNonGeomParams > i; 409 if (isUsed) { 410 paramLabel[i].setText(func.getParameterNames()[i+indexOffset]); 411 } 412 paramComp[i].setVisible(isUsed); 413 paramLabel[i].setVisible(isUsed); 414 SpatialFunctionPanel.setToolTipText(paramComp[i], func, i); 415 } 416 } 417 hideAllParams(JComponent[] paramComp, JLabel[] paramLabel)418 static void hideAllParams(JComponent[] paramComp, JLabel[] paramLabel) { 419 for (int i = 0; i < paramComp.length; i++) { 420 paramComp[i].setVisible(false); 421 paramLabel[i].setVisible(false); 422 } 423 } 424 setToolTipText(JComponent control, GeometryFunction func, int i)425 private static void setToolTipText(JComponent control, GeometryFunction func, int i) { 426 String txt = null; 427 if (func.getParameterTypes().length > i) { 428 txt = "Enter a " + func.getParameterTypes()[i].getSimpleName(); 429 } 430 control.setToolTipText(txt); 431 } 432 numNonGeomParams(GeometryFunction func)433 private static int numNonGeomParams(GeometryFunction func) 434 { 435 int count = 0; 436 Class[] paramTypes = func.getParameterTypes(); 437 for (int i = 0; i < paramTypes.length; i++) { 438 if (! ClassUtil.isGeometry(paramTypes[i])) 439 count++; 440 } 441 return count; 442 } 443 attributeParamOffset(GeometryFunction func)444 public static int attributeParamOffset(GeometryFunction func) { 445 return func.isBinary() ? 1 : 0; 446 } 447 shouldShowGeometryA()448 public boolean shouldShowGeometryA() { 449 return displayAAndBCheckBox.isSelected(); 450 } 451 shouldShowGeometryB()452 public boolean shouldShowGeometryB() { 453 return displayAAndBCheckBox.isSelected(); 454 } 455 clearFunction()456 public void clearFunction() { 457 setCurrentFunction(null); 458 } 459 getFunctionParams()460 public Object[] getFunctionParams() 461 { 462 if (currentFunc == null) return null; 463 Class[] paramTypes = currentFunc.getParameterTypes(); 464 Object[] paramVal = new Object[paramTypes.length]; 465 466 for (int i = 0; i < paramVal.length; i++) { 467 Object valRaw = getParamValue(i); 468 paramVal[i] = SwingUtil.coerce(valRaw, paramTypes[i]); 469 } 470 return paramVal; 471 } 472 getParamValue(int index)473 private Object getParamValue(int index) { 474 if (currentFunc.isBinary() && index == 0) 475 return JTSTestBuilder.controller().getGeometryB(); 476 477 int attrIndex = index - attributeParamOffset(currentFunc); 478 479 switch (attrIndex) { 480 case 0: return valOrDefault(SwingUtil.value(txtDistance), PARAM_DEFAULT[0]); 481 case 1: return valOrDefault(SwingUtil.value(txtQuadrantSegs), PARAM_DEFAULT[1]); 482 case 2: return SwingUtil.value(cbCapStyle, capStyleValues); 483 case 3: return SwingUtil.value(cbJoinStyle, joinStyleValues); 484 case 4: return valOrDefault(SwingUtil.value(txtMitreLimit), PARAM_DEFAULT[4]); 485 } 486 return null; 487 } 488 valOrDefault(String s, String defaultVal)489 public static String valOrDefault(String s, String defaultVal) { 490 if (s.length() > 0) return s; 491 return defaultVal; 492 } 493 isFunctionSelected()494 public boolean isFunctionSelected() 495 { 496 return currentFunc != null; 497 } 498 getFunction()499 public GeometryFunction getFunction() { 500 return currentFunc; 501 } 502 503 //================================================= 504 // Events 505 //================================================= 506 removeSpatialFunctionPanelListener(SpatialFunctionPanelListener l)507 public synchronized void removeSpatialFunctionPanelListener(SpatialFunctionPanelListener l) { 508 if (spatialFunctionPanelListeners != null && spatialFunctionPanelListeners.contains(l)) { 509 Vector v = (Vector) spatialFunctionPanelListeners.clone(); 510 v.removeElement(l); 511 spatialFunctionPanelListeners = v; 512 } 513 } 514 addSpatialFunctionPanelListener(SpatialFunctionPanelListener l)515 public synchronized void addSpatialFunctionPanelListener(SpatialFunctionPanelListener l) { 516 Vector v = spatialFunctionPanelListeners == null ? new Vector(2) : (Vector) spatialFunctionPanelListeners.clone(); 517 if (!v.contains(l)) { 518 v.addElement(l); 519 spatialFunctionPanelListeners = v; 520 } 521 } 522 fireFunctionExecuted(SpatialFunctionPanelEvent e)523 protected void fireFunctionExecuted(SpatialFunctionPanelEvent e) { 524 if (spatialFunctionPanelListeners != null) { 525 Vector listeners = spatialFunctionPanelListeners; 526 int count = listeners.size(); 527 for (int i = 0; i < count; i++) { 528 ((SpatialFunctionPanelListener) listeners.elementAt(i)).functionExecuted(e); 529 } 530 } 531 } 532 533 534 } 535 536