001package com.ganteater.ae.desktop.ui; 002 003import java.awt.AWTEvent; 004import java.awt.BorderLayout; 005import java.awt.Color; 006import java.awt.Component; 007import java.awt.Desktop; 008import java.awt.Dimension; 009import java.awt.Font; 010import java.awt.Frame; 011import java.awt.GraphicsEnvironment; 012import java.awt.Toolkit; 013import java.awt.datatransfer.DataFlavor; 014import java.awt.dnd.DnDConstants; 015import java.awt.dnd.DropTarget; 016import java.awt.dnd.DropTargetDropEvent; 017import java.awt.event.ComponentAdapter; 018import java.awt.event.ComponentEvent; 019import java.awt.event.InputEvent; 020import java.awt.event.KeyEvent; 021import java.awt.event.MouseAdapter; 022import java.awt.event.MouseEvent; 023import java.awt.event.WindowAdapter; 024import java.awt.event.WindowEvent; 025import java.io.File; 026import java.io.FileInputStream; 027import java.io.FileOutputStream; 028import java.io.IOException; 029import java.net.URL; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.Properties; 035import java.util.Set; 036 037import javax.swing.BorderFactory; 038import javax.swing.ImageIcon; 039import javax.swing.JButton; 040import javax.swing.JCheckBox; 041import javax.swing.JComboBox; 042import javax.swing.JComponent; 043import javax.swing.JEditorPane; 044import javax.swing.JFrame; 045import javax.swing.JLabel; 046import javax.swing.JList; 047import javax.swing.JOptionPane; 048import javax.swing.JPanel; 049import javax.swing.JProgressBar; 050import javax.swing.JScrollPane; 051import javax.swing.JSplitPane; 052import javax.swing.JTabbedPane; 053import javax.swing.JTable; 054import javax.swing.JTextField; 055import javax.swing.JToolBar; 056import javax.swing.KeyStroke; 057import javax.swing.ListSelectionModel; 058import javax.swing.SwingUtilities; 059import javax.swing.UIManager; 060import javax.swing.UnsupportedLookAndFeelException; 061import javax.swing.border.EmptyBorder; 062 063import org.apache.commons.io.IOUtils; 064import org.apache.commons.lang.ObjectUtils; 065import org.apache.commons.lang.StringUtils; 066 067import com.ganteater.ae.AEWorkspace; 068import com.ganteater.ae.CommandException; 069import com.ganteater.ae.ConfigConstants; 070import com.ganteater.ae.ConfigurationException; 071import com.ganteater.ae.ILogger; 072import com.ganteater.ae.Logger; 073import com.ganteater.ae.MultiTaskRunDialog; 074import com.ganteater.ae.RecipeRunner; 075import com.ganteater.ae.TaskCancelingException; 076import com.ganteater.ae.desktop.DesktopWorkspace; 077import com.ganteater.ae.desktop.FrameStyle; 078import com.ganteater.ae.desktop.InputDialogType; 079import com.ganteater.ae.desktop.editor.TaskEditor; 080import com.ganteater.ae.desktop.editor.VariableViewPanel; 081import com.ganteater.ae.desktop.util.SoundManager; 082import com.ganteater.ae.desktop.util.UIUtils; 083import com.ganteater.ae.processor.Processor; 084import com.ganteater.ae.processor.SpecialCommands; 085import com.ganteater.ae.util.AEUtils; 086import com.ganteater.ae.util.xml.easyparser.Node; 087 088public class AEFrame extends JFrame { 089 090 public static final int MINIMUM_WIN_WIDTH = 240; 091 092 private static final long serialVersionUID = 1L; 093 094 public class UIChoiceTaskRunner extends MultiTaskRunDialogImpl implements MultiTaskRunDialog { 095 private static final long serialVersionUID = 1L; 096 097 public UIChoiceTaskRunner(String name, Processor taskProcessor, boolean useHistory) { 098 super(AEFrame.this, name, taskProcessor, useHistory); 099 } 100 } 101 102 private static ILogger log = new Logger("AEFrame"); 103 104 public static final String APP_TITLE = "Anteater"; 105 106 public static final String LOOK_AND_FEEL = "LOOK_AND_FEEL"; 107 public static final String TAKE_IT_EASY = "Take it Easy"; 108 private static final String MENU_TAG_NAME = "Menu"; 109 private static final int USER_ACTION_NOTIFY_TIMEOUT = 3000; 110 private static final int COMPACT_THRESHOLD = 450; 111 private static final String LOGO_ERROR_JPG = "logo-error.png"; 112 private static final String LOGO_FATAL_JPG = "logo-fatal.png"; 113 public static final String ICON16_JPG = "icon16.png"; 114 115 public static final String LOGO_JPG = "logo.png"; 116 static final String TAKE_IT_EASY_JPG = "take-it-easy.png"; 117 118 private static final String CONF_REFRESH_COMMAND = "Refresh recipes"; 119 120 private JButton mainButton; 121 private JProgressBar progress; 122 123 private transient AEWorkspace workspace; 124 private JTabbedPane tabbedPanel; 125 126 private EnvPropertiesTable varTable; 127 private transient TaskEditor failedEditor; 128 private transient SoundManager soundManager; 129 130 private JButton refreshButton; 131 private JCheckBox defaultInputBox; 132 133 private DialogPopupMenu leftMenu; 134 protected RecipesPanel recipesPanel; 135 136 private JPanel panel; 137 138 private CheckpointsDialog checkpointsDialog; 139 private MenuDialogFrame menuDialog; 140 private DialogFrame configurationDialod; 141 142 private FrameStyle frameStyle = FrameStyle.NONE; 143 private boolean isWinMaximized; 144 private Long lastUserActionTime = System.currentTimeMillis(); 145 146 public AEFrame() { 147 super(APP_TITLE); 148 setMinimumSize(new Dimension(MINIMUM_WIN_WIDTH, 195)); 149 150 workspace = createWorkspace(); 151 soundManager = new SoundManager(this); 152 153 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 154 JOptionPane.setRootFrame(this); 155 156 addComponentListener(new ComponentAdapter() { 157 @Override 158 public void componentResized(ComponentEvent e) { 159 if (isVisible()) { 160 checkLayoutStyle(); 161 } 162 } 163 }); 164 165 addWindowStateListener(e -> { 166 checkLayoutStyle(); 167 int state = e.getNewState(); 168 isWinMaximized = state == Frame.MAXIMIZED_BOTH; 169 }); 170 171 Toolkit.getDefaultToolkit().addAWTEventListener(event -> { 172 if (event instanceof MouseEvent) { 173 lastUserActionTime = System.currentTimeMillis(); 174 SoundManager.stop(); 175 } 176 if (event instanceof KeyEvent) { 177 lastUserActionTime = System.currentTimeMillis(); 178 SoundManager.stop(); 179 } 180 }, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); 181 182 getRootPane().registerKeyboardAction(e -> { 183 defaultInputBox.setSelected(!defaultInputBox.isSelected()); 184 mainButton.setIcon(getIcon(getLogo())); 185 }, KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); 186 } 187 188 protected DesktopWorkspace createWorkspace() { 189 return new DesktopWorkspace(this) { 190 @Override 191 public void refreshTaskPath() throws IOException { 192 super.refreshTaskPath(); 193 getRecipesPanel().refreshTaskPathTable(); 194 } 195 }; 196 } 197 198 public FrameStyle getFrameStyle() { 199 FrameStyle style; 200 201 Dimension size = getSize(); 202 double width = size.getWidth(); 203 204 if (width < COMPACT_THRESHOLD) { 205 Dimension minimumSize = getMinimumSize(); 206 if (size.getHeight() > minimumSize.getHeight()) { 207 style = FrameStyle.COMPACT; 208 } else { 209 style = FrameStyle.BUTTON; 210 } 211 } else { 212 style = FrameStyle.FULL; 213 } 214 215 return style; 216 217 } 218 219 private void checkLayoutStyle() { 220 FrameStyle newStyle = getFrameStyle(); 221 222 if (newStyle != frameStyle) { 223 try { 224 setStyle(newStyle); 225 226 } catch (Exception e1) { 227 e1.printStackTrace(); 228 } 229 } 230 } 231 232 private void createFrame(String loadConfiguration) { 233 setTitle(loadConfiguration + " - " + APP_TITLE); 234 235 String defaultlaf = System.getProperty("swing.defaultlaf"); 236 if (defaultlaf == null) { 237 UIManager.LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels(); 238 239 String[] lafs = new String[lafInfo.length]; 240 for (int i = 0; i < lafInfo.length; i++) { 241 lafs[i] = lafInfo[i].getClassName(); 242 } 243 244 String lookAndFeel = StringUtils.trim((String) workspace.getSystemVariables().get(LOOK_AND_FEEL)); 245 if (StringUtils.isBlank(lookAndFeel)) { 246 lookAndFeel = workspace.choiceValue(LOOK_AND_FEEL, "Look And Feel", lafs, null, false, null); 247 } 248 249 lookAndFeel(lookAndFeel); 250 } 251 252 tabbedPanel = new JTabbedPane(); 253 254 menuDialog = new MenuDialogFrame(this); 255 recipesPanel = new RecipesPanel(this); 256 varTable = new EnvPropertiesTable(this); 257 refreshButton = new JButton(CONF_REFRESH_COMMAND); 258 progress = new JProgressBar(); 259 defaultInputBox = new JCheckBox("<html>" + TAKE_IT_EASY.replace("E", "<u>E</u>") + "</html>"); 260 261 String takeItEasy = System.getProperty(ConfigConstants.TAKE_IT_EASY_MODE, "false"); 262 defaultInputBox.setSelected(Boolean.parseBoolean(takeItEasy)); 263 264 mainButton = new JButton(getIcon(getLogo())); 265 mainButton.setBorder(BorderFactory.createEmptyBorder()); 266 267 leftMenu = new DialogPopupMenu(mainButton); 268 panel = new JPanel(new BorderLayout()); 269 checkpointsDialog = CheckpointsDialog.getInstance(); 270 271 setIconImage(getIcon(ICON16_JPG).getImage()); 272 addWindowListener(new WindowAdapter() { 273 274 @Override 275 public void windowClosing(WindowEvent arg0) { 276 AEWorkspace.getInstance().close(); 277 278 int screenDevicesCount = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length; 279 if (!isWinMaximized) { 280 AEWorkspace.getInstance().setDefaultUserConfiguration(".win_position_x:" + screenDevicesCount, 281 String.valueOf(getLocation().getX())); 282 AEWorkspace.getInstance().setDefaultUserConfiguration(".win_position_y:" + screenDevicesCount, 283 String.valueOf(getLocation().getY())); 284 AEWorkspace.getInstance().setDefaultUserConfiguration(".win_position_w:" + screenDevicesCount, 285 String.valueOf(getSize().width)); 286 AEWorkspace.getInstance().setDefaultUserConfiguration(".win_position_h:" + screenDevicesCount, 287 String.valueOf(getSize().height)); 288 } 289 AEWorkspace.getInstance().setDefaultUserConfiguration(".win_maximized", isWinMaximized ? "1" : "0"); 290 291 super.windowClosing(arg0); 292 } 293 294 }); 295 296 mainButton.addActionListener(arg0 -> { 297 if (failedEditor != null) { 298 setSelectedComponent(failedEditor.getMainPanel()); 299 failedEditor = null; 300 mainButton.setIcon(getIcon(getLogo())); 301 } else { 302 menuDialog.showDialogTestSelect(getRecipesPanel().getMenuTasks()); 303 } 304 }); 305 306 mainButton.addMouseListener(new MouseAdapter() { 307 @Override 308 public void mouseClicked(MouseEvent e) { 309 if (e.getButton() == 3) { 310 int componentCount = leftMenu.getComponentCount(); 311 if (componentCount > 0) { 312 leftMenu.show(mainButton, 0, 0); 313 } else { 314 menuDialog.showDialogTestSelect(getRecipesPanel().getMenuTasks()); 315 } 316 } 317 } 318 }); 319 320 } 321 322 public static ImageIcon getIcon(String name) { 323 name = "/images/ui/" + name; 324 URL resource = AEFrame.class.getResource(name); 325 if (resource == null) { 326 throw new IllegalArgumentException("Resource is not found: " + name); 327 } 328 return new ImageIcon(resource); 329 } 330 331 private void setStyle(FrameStyle style) { 332 defaultInputBox.addActionListener(e -> mainButton.setIcon(getIcon(getLogo()))); 333 334 getContentPane().removeAll(); 335 336 panel.removeAll(); 337 progress.setStringPainted(true); 338 panel.add(mainButton, BorderLayout.NORTH); 339 JPanel comp = new JPanel(new BorderLayout()); 340 Color background = new JButton().getBackground(); 341 comp.setBackground(background); 342 defaultInputBox.setBackground(background); 343 344 comp.add(defaultInputBox, BorderLayout.WEST); 345 JButton checkpointList = new JButton(Character.toString((char) 9016)); 346 checkpointList.addActionListener(e -> checkpointsDialog.showDialog()); 347 checkpointList.setToolTipText(CheckpointsDialog.REQUIRED_DIALOGS); 348 checkpointList.setFont(new Font(checkpointList.getFont().getName(), Font.PLAIN, 16)); 349 checkpointList.setBorder(new EmptyBorder(0, 0, 0, 0)); 350 checkpointList.setBorderPainted(false); 351 comp.add(checkpointList, BorderLayout.EAST); 352 353 panel.add(comp, BorderLayout.CENTER); 354 panel.add(progress, BorderLayout.SOUTH); 355 356 switch (style) { 357 case COMPACT: 358 JPanel compact = new JPanel(new BorderLayout()); 359 compact.add(panel, BorderLayout.NORTH); 360 compact.add(tabbedPanel, BorderLayout.CENTER); 361 getContentPane().add(compact); 362 setAlwaysOnTop(true); 363 tabbedPanel.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); 364 break; 365 366 case BUTTON: 367 getContentPane().add(panel, BorderLayout.CENTER); 368 setAlwaysOnTop(true); 369 break; 370 371 default: 372 JPanel leftPanel = new JPanel(new BorderLayout()); 373 leftPanel.setBorder(BorderFactory.createBevelBorder(0)); 374 375 leftPanel.add(panel, BorderLayout.NORTH); 376 leftPanel.add(createConfirPanel(), BorderLayout.CENTER); 377 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, tabbedPanel); 378 splitPane.setDividerLocation(230); 379 380 getContentPane().add(splitPane); 381 setAlwaysOnTop(false); 382 tabbedPanel.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); 383 } 384 385 mainButton.setFocusable(true); 386 mainButton.requestFocus(); 387 388 frameStyle = style; 389 } 390 391 public void start() { 392 loadConfiguration(); 393 394 try { 395 getWorkspace().runSetupNodes(); 396 397 } catch (Exception e) { 398 log.error("Startup execution failed.", e); 399 } 400 } 401 402 public void setSelectedComponent(Component comp) { 403 try { 404 tabbedPanel.setSelectedComponent(comp); 405 } catch (IllegalArgumentException e) { 406 // do nothing. 407 } 408 } 409 410 public TaskEditor editTask(String name) throws CommandException { 411 TaskEditor taskEditor = new TaskEditor(this); 412 if (name != null) { 413 name = taskEditor.getProcessor().replaceProperties(name); 414 taskEditor.setActiveTest(name); 415 } 416 String logName = StringUtils.defaultString(name, SpecialCommands.STARTUP); 417 taskEditor.edit(logName); 418 addTaskPanel(taskEditor, logName); 419 return taskEditor; 420 } 421 422 private void addTaskPanel(TaskEditor taskEditor, String name) { 423 taskEditor.showPanel(tabbedPanel, name); 424 leftMenu.add(new TaskMenuItem(name, taskEditor)); 425 } 426 427 public void refreshTaskPath() throws IOException { 428 getWorkspace().refreshTaskPath(); 429 getRecipesPanel().refreshTaskPathTable(); 430 } 431 432 public String inputValue(String name, String description, String value, String type, boolean notifyMe) { 433 description = StringUtils.defaultString(description, name); 434 435 if (value == null) { 436 value = AEWorkspace.getInstance().getDefaultUserConfiguration(".inputValue." + description, value); 437 } 438 439 pushOnTop(); 440 if (notifyMe) { 441 soundManager.play(); 442 } 443 444 type = (String) ObjectUtils.defaultIfNull(type, StringUtils.EMPTY); 445 446 switch (type) { 447 case "password": 448 value = inputPassword(description, value); 449 break; 450 case "text": 451 value = simpleInput(description, description, value, null, true, null); 452 break; 453 case "map": 454 value = inputMap(description, value); 455 break; 456 457 default: 458 value = simpleInput(name, description, value, null, false, null); 459 } 460 461 if (notifyMe) { 462 SoundManager.stop(); 463 } 464 465 if (!"text".equals(type)) { 466 AEWorkspace.getInstance().setDefaultUserConfiguration(".inputValue." + description, value); 467 } 468 return value; 469 } 470 471 public void errorInformation(Processor processor, Throwable exception, Node command, boolean notifyMe) 472 throws Throwable { 473 int showConfirmDialog = 0; 474 475 String name = "Error Trace"; 476 if (!workspace.isConsoleDefaultInput(name, null)) { 477 TracePanel tracePanel = new TracePanel(this, processor, exception, command); 478 CheckPointBox vip = new CheckPointBox(name, null, InputDialogType.TASKS); 479 tracePanel.add(new JLabel(), BorderLayout.NORTH); 480 tracePanel.add(vip, BorderLayout.SOUTH); 481 482 pushOnTop(); 483 if (notifyMe) { 484 soundManager.play(); 485 } 486 showConfirmDialog = OptionPane.showConfirmDialog(JOptionPane.getRootFrame(), tracePanel, name); 487 vip.saveCheckpointFlag(); 488 } else { 489 throw exception; 490 } 491 492 if (showConfirmDialog == -1) { 493 processor.stop(); 494 495 } else if (showConfirmDialog != 2) { 496 CommandException commandException = new CommandException(exception, processor, command); 497 commandException.reviewed(); 498 throw commandException; 499 } 500 } 501 502 @SuppressWarnings("unchecked") 503 private String simpleInput(String name, String description, Object values, Object defaultValue, boolean bigText, 504 String type) { 505 String result = null; 506 507 int questionMessage = JOptionPane.QUESTION_MESSAGE; 508 509 JPanel simplInputPanel = new JPanel(new BorderLayout()); 510 JLabel label = new JLabel(name); 511 label.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); 512 513 simplInputPanel.add(label, BorderLayout.NORTH); 514 if (description != null) { 515 String message = description; 516 if (!StringUtils.startsWith(description, "<html>")) { 517 message = "<html><body>" + description.replace("\n", "<br/>") + "</body></html>"; 518 } 519 simplInputPanel.add(new JLabel(message), BorderLayout.NORTH); 520 } 521 522 JComponent inputField; 523 if (values instanceof String || values == null) { 524 JComponent jTextField; 525 if (bigText) { 526 jTextField = new JEditorPane(); 527 jTextField.setMinimumSize(new Dimension(400, 300)); 528 jTextField.setDropTarget(new DropTarget() { 529 private static final long serialVersionUID = 1L; 530 531 @Override 532 public synchronized void drop(DropTargetDropEvent evt) { 533 try { 534 evt.acceptDrop(DnDConstants.ACTION_COPY); 535 List<File> droppedFiles = (List<File>) evt.getTransferable() 536 .getTransferData(DataFlavor.javaFileListFlavor); 537 for (File file : droppedFiles) { 538 String text = IOUtils.toString(new FileInputStream(file)); 539 JEditorPane jEditorPane = (JEditorPane) jTextField; 540 jEditorPane.setText(jEditorPane.getText() + text); 541 } 542 } catch (Exception ex) { 543 ex.printStackTrace(); 544 } 545 } 546 }); 547 548 UIUtils.addJPopupMenuTo((JEditorPane) jTextField); 549 } else { 550 jTextField = new JTextField(40); 551 } 552 553 inputField = jTextField; 554 String text = (String) values; 555 556 if (inputField instanceof JTextField) { 557 ((JTextField) inputField).setText(text); 558 simplInputPanel.add(inputField, BorderLayout.CENTER); 559 } 560 if (inputField instanceof JEditorPane) { 561 ((JEditorPane) inputField).setText(text); 562 simplInputPanel.add(new JScrollPane(inputField), BorderLayout.CENTER); 563 } 564 565 } else { 566 if (StringUtils.contains(type, "list")) { 567 JList<String> jComboBox = new JList<String>((String[]) values); 568 jComboBox.setSelectedValue(defaultValue, true); 569 inputField = new JScrollPane(jComboBox); 570 jComboBox.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 571 jComboBox.setVisibleRowCount(5); 572 573 } else { 574 JComboBox<Object> jComboBox = new JComboBox<>((Object[]) values); 575 jComboBox.setEditable(true); 576 jComboBox.setSelectedItem(defaultValue); 577 inputField = jComboBox; 578 } 579 simplInputPanel.add(inputField, BorderLayout.CENTER); 580 } 581 582 CheckPointBox vip = new CheckPointBox(name, description, InputDialogType.VAR); 583 584 if (!vip.isSelected() && (LOOK_AND_FEEL.equals(name) || workspace.isConsoleDefaultInput(name, description))) { 585 vip.saveCheckpointFlag(); 586 String value = (String) defaultValue; 587 if (defaultValue == null) { 588 if (values instanceof String) { 589 value = (String) values; 590 } 591 if (values instanceof String[] && ((String[]) values).length > 1) { 592 value = ((String[]) values)[0]; 593 } 594 } 595 return value; 596 } 597 598 if (SwingUtilities.getRoot(this).isVisible() || LOOK_AND_FEEL.equals(name)) { 599 simplInputPanel.add(vip, BorderLayout.SOUTH); 600 } 601 602 Frame rootFrame = JOptionPane.getRootFrame(); 603 int option = OptionPane.showOptionDialog(rootFrame, simplInputPanel, "Dialog Box", questionMessage); 604 605 vip.saveCheckpointFlag(); 606 607 if (option == JOptionPane.OK_OPTION) { 608 if (values instanceof String || values == null) { 609 610 if (inputField instanceof JTextField) { 611 result = ((JTextField) inputField).getText(); 612 } 613 if (inputField instanceof JEditorPane) { 614 result = ((JEditorPane) inputField).getText(); 615 } 616 if (inputField instanceof JList) { 617 result = ((JList<String>) inputField).getSelectedValue(); 618 } 619 } else { 620 if (inputField instanceof JScrollPane) { 621 inputField = (JComponent) ((JScrollPane) inputField).getViewport().getView(); 622 623 } 624 if (inputField instanceof JComboBox) { 625 result = (String) ((JComboBox<?>) inputField).getSelectedItem(); 626 } 627 if (inputField instanceof JList<?>) { 628 result = (String) ((JList<?>) inputField).getSelectedValue(); 629 } 630 } 631 } 632 633 if (option == JOptionPane.CLOSED_OPTION) { 634 throw new TaskCancelingException(); 635 } 636 637 return result; 638 } 639 640 private String inputMap(String description, String aValue) { 641 final Map<String, String> map = AEUtils.convertStringToMap(aValue); 642 643 Set<Entry<String, String>> entrySet = map.entrySet(); 644 Object[][] data = new Object[map.size()][]; 645 int i = 0; 646 for (Entry<String, String> entry : entrySet) { 647 Object[] row = new Object[2]; 648 row[0] = entry.getKey(); 649 row[1] = entry.getValue(); 650 data[i++] = row; 651 } 652 653 Object[] columnNames = { "Name", "Value" }; 654 final JTable table = new JTable(data, columnNames) { 655 private static final long serialVersionUID = 1L; 656 657 @Override 658 public Dimension getPreferredScrollableViewportSize() { 659 Dimension d = getPreferredSize(); 660 int n = getRowHeight(); 661 return new Dimension(d.width, (n * map.size())); 662 } 663 }; 664 table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 665 table.setCellSelectionEnabled(true); 666 667 JPanel jPanel = new JPanel(); 668 jPanel.setLayout(new BorderLayout()); 669 JScrollPane sp = new JScrollPane(table); 670 jPanel.add(sp, BorderLayout.CENTER); 671 672 CheckPointBox vip = new CheckPointBox(description, null, InputDialogType.MAP); 673 jPanel.add(vip, BorderLayout.SOUTH); 674 675 jPanel.add(new JLabel(description), BorderLayout.NORTH); 676 Frame rootFrame = JOptionPane.getRootFrame(); 677 678 int option = OptionPane.showOptionDialog(rootFrame, jPanel, "Input the map property", 679 JOptionPane.QUESTION_MESSAGE); 680 vip.saveCheckpointFlag(); 681 682 String result = null; 683 if (option == -1) { 684 throw new TaskCancelingException(); 685 } 686 if (option == 0) { 687 Map<String, String> resultMap = new HashMap<>(map); 688 for (int j = 0; j < map.size(); j++) { 689 resultMap.put((String) data[j][0], (String) data[j][1]); 690 } 691 result = resultMap.toString(); 692 } 693 694 return result; 695 } 696 697 private String inputPassword(String name, String value) { 698 String result; 699 CheckPointBox vip = new CheckPointBox(name, null, InputDialogType.PWD); 700 if (vip.isSelected() || !workspace.isConsoleDefaultInput(name, null)) { 701 result = OptionPane.showInputPasswordDialog(name, value, vip, JOptionPane.QUESTION_MESSAGE, 702 OptionPane.OPTIONS); 703 } else { 704 result = value; 705 } 706 vip.saveCheckpointFlag(); 707 708 return result; 709 } 710 711 public void message(String string) { 712 JOptionPane.showMessageDialog(this, string); 713 } 714 715 public String getTestPath(String name) { 716 String property = getWorkspace().getTestsDescList().getProperty(name); 717 return property == null ? name : property; 718 } 719 720 private JTabbedPane createConfirPanel() { 721 722 JScrollPane theScrollPane = new JScrollPane(varTable); 723 724 JPanel theButtonsPanel = new JPanel(); 725 JButton theButton = new Button("save.png"); 726 theButton.setToolTipText("Save custom configuration"); 727 theButton.addActionListener(e -> SwingUtilities.invokeLater(this::saveEnvProperties)); 728 theButtonsPanel.add(theButton); 729 730 theButton = new Button("default.png"); 731 theButton.setToolTipText("Reset custom configuration"); 732 theButton.addActionListener(e -> SwingUtilities.invokeLater(() -> { 733 String customConfPropFileName = getWorkspace() 734 .getCustomConfPropFileName(getWorkspace().getConfigurationName()); 735 File theConfFile = new File(customConfPropFileName); 736 boolean result = theConfFile.delete(); 737 738 try { 739 workspace.loadConfProperties(); 740 } catch (IOException e1) { 741 e1.printStackTrace(); 742 } 743 744 varTable.refresh(); 745 746 if (result) { 747 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), 748 "The user configuration has been deleted.\nPlease restart the application."); 749 } 750 751 String lookAndFeel = StringUtils.trim((String) workspace.getSystemVariables().get(LOOK_AND_FEEL)); 752 if (StringUtils.isNotBlank(lookAndFeel)) { 753 lookAndFeel(lookAndFeel); 754 } 755 })); 756 theButtonsPanel.add(theButton); 757 758 theButton = new Button("sandbox.png"); 759 theButton.setToolTipText("Open user folder"); 760 theButton.addActionListener(e -> { 761 String absolutePath = AEWorkspace.getInstance().getHomeWorkingDir().getAbsolutePath(); 762 try { 763 Desktop.getDesktop().open(new File(absolutePath)); 764 } catch (IOException e1) { 765 e1.printStackTrace(); 766 } 767 }); 768 theButtonsPanel.add(theButton); 769 770 JPanel envToolPanel = new JPanel(new BorderLayout(4, 4)); 771 772 theButton = new Button("plus.png"); 773 theButton.setToolTipText("Add new custom variable"); 774 theButton.addActionListener(e -> SwingUtilities.invokeLater(this::addNewUserVariable)); 775 theButtonsPanel.add(theButton); 776 envToolPanel.add(theButton, BorderLayout.WEST); 777 778 theButton = new Button("hierarchy.png"); 779 theButton.setToolTipText(ConfigurationView.CONFIGURATION_TITLE); 780 theButton.setEnabled(true); 781 theButton.addActionListener(e -> configurationDialod.setVisible(true)); 782 783 envToolPanel.add(theButton, BorderLayout.EAST); 784 785 JPanel theSysvarButtonsPanel = new JPanel(new BorderLayout(4, 4)); 786 theSysvarButtonsPanel.add(theScrollPane, BorderLayout.CENTER); 787 theSysvarButtonsPanel.add(envToolPanel, BorderLayout.NORTH); 788 theSysvarButtonsPanel.add(theButtonsPanel, BorderLayout.SOUTH); 789 790 JPanel recPanel = new JPanel(); 791 recPanel.setLayout(new BorderLayout()); 792 recPanel.add(getRecipesPanel(), BorderLayout.CENTER); 793 794 JPanel comp = new JPanel(); 795 796 JButton theCreate = new Button("create.png"); 797 theCreate.setToolTipText("Create a new recipe"); 798 theCreate.addActionListener(e -> { 799 try { 800 801 String taskName = JOptionPane.showInputDialog(JOptionPane.getRootFrame(), "Task name"); 802 if (taskName != null) { 803 TaskEditor taskEditor = new TaskEditor(AEFrame.this); 804 addTaskPanel(taskEditor, taskName); 805 taskEditor.create(taskName); 806 } 807 808 } catch (Exception e1) { 809 e1.printStackTrace(); 810 } 811 }); 812 comp.add(theCreate); 813 814 JButton theRefresh = new Button("refresh.png"); 815 theRefresh.setToolTipText("Refresh recipe list"); 816 theRefresh.addActionListener(arg0 -> { 817 try { 818 workspace.refreshTaskPath(); 819 } catch (IOException e) { 820 e.printStackTrace(); 821 } 822 }); 823 comp.add(theRefresh); 824 825 recPanel.add(comp, BorderLayout.SOUTH); 826 827 JTabbedPane tablePane = new JTabbedPane(); 828 tablePane.add("Recipes", recPanel); 829 tablePane.add("Environment", theSysvarButtonsPanel); 830 831 refreshButton.addActionListener(e -> refreshTaskMap()); 832 833 return tablePane; 834 } 835 836 public Long getLastUserActionTime() { 837 return lastUserActionTime; 838 } 839 840 public String loadConfiguration() { 841 return loadConfiguration(null); 842 } 843 844 protected String loadConfiguration(String confName) { 845 String currentConfName = getWorkspace().getConfigurationName(); 846 String loadConfiguration = getWorkspace().loadConfiguration(confName, true); 847 if (loadConfiguration != null) { 848 createFrame(loadConfiguration); 849 850 ConfigurationView configurationEditor = new ConfigurationView(this); 851 configurationEditor.refreshActiveConfig(); 852 853 configurationDialod = new DialogFrame(this, ConfigurationView.CONFIGURATION_TITLE, configurationEditor); 854 UIUtils.applyPreferedView(configurationDialod, ConfigurationView.CONFIGURATION_TITLE); 855 856 configurationDialod.setIconImage(AEFrame.getIcon("hierarchy.png").getImage()); 857 858 Node configNode = workspace.getConfigNode(); 859 String menuName = configNode.getAttribute("menu"); 860 Node[] theToolBarNodes = configNode.getNodes(MENU_TAG_NAME); 861 if (StringUtils.isNotBlank(menuName)) { 862 Node findNode = configNode.findNode(MENU_TAG_NAME, "name", menuName); 863 if (findNode != null) { 864 theToolBarNodes = new Node[] { findNode }; 865 } 866 } 867 868 addEditor("Variables", VariableViewPanel.class); 869 870 if (theToolBarNodes != null && theToolBarNodes.length > 0) { 871 menuDialog.createToolBar(theToolBarNodes); 872 } 873 874 if (!StringUtils.equals(currentConfName, loadConfiguration)) { 875 tabbedPanel.removeAll(); 876 } 877 878 varTable.refresh(); 879 getRecipesPanel().refreshTaskPathTable(); 880 881 pack(); 882 setVisible(true); 883 884 log.debug("Application started."); 885 } 886 887 return loadConfiguration; 888 } 889 890 private void addEditor(String name, Class<VariableViewPanel> editorClass) { 891 Node configNode = workspace.getConfigNode(); 892 893 Node variables = new Node("Editor"); 894 variables.setAttribute("name", name); 895 variables.setAttribute("class", editorClass.getName()); 896 configNode.add(variables); 897 } 898 899 void lookAndFeel(String lookAndFeel) { 900 lookAndFeel = StringUtils.trimToNull(lookAndFeel); 901 if (StringUtils.isNotBlank(lookAndFeel)) { 902 try { 903 UIManager.setLookAndFeel(lookAndFeel); 904 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 905 | UnsupportedLookAndFeelException e) { 906 e.printStackTrace(); 907 message(e.getClass().getSimpleName() + ": " + e.getLocalizedMessage()); 908 } 909 } 910 } 911 912 public void removeTab(TaskEditor editor) { 913 tabbedPanel.remove(editor.getMainPanel()); 914 for (int i = 0; i < leftMenu.getComponentCount(); i++) { 915 Component component = leftMenu.getComponent(i); 916 if (component instanceof TaskMenuItem) { 917 TaskMenuItem taskMenuItem = (TaskMenuItem) component; 918 if (taskMenuItem.getTaskEditor() == editor) { 919 leftMenu.remove(taskMenuItem); 920 } 921 } 922 } 923 } 924 925 public void runTask(String name) { 926 if (name != null) { 927 TaskEditor taskEditor = null; 928 try { 929 taskEditor = new TaskEditor(this); 930 addTaskPanel(taskEditor, name); 931 taskEditor.start(name); 932 } catch (ConfigurationException | CommandException e) { 933 refreshRecipeMap(taskEditor); 934 } 935 } 936 } 937 938 public void refreshRecipeMap(TaskEditor taskEditor) { 939 removeTab(taskEditor); 940 int showConfirmDialog = JOptionPane.showConfirmDialog(this, "Recipe file is not found. Refresh recipe map?"); 941 if (showConfirmDialog == JOptionPane.OK_OPTION) { 942 refreshTaskMap(); 943 } 944 } 945 946 @Override 947 public void pack() { 948 int screenDevicesCount = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length; 949 950 try { 951 int theX = (int) Float.parseFloat(AEWorkspace.getInstance() 952 .getDefaultUserConfiguration(".win_position_x:" + screenDevicesCount, "0")); 953 int theY = (int) Float.parseFloat(AEWorkspace.getInstance() 954 .getDefaultUserConfiguration(".win_position_y:" + screenDevicesCount, "0")); 955 int theW = Integer.parseInt(AEWorkspace.getInstance() 956 .getDefaultUserConfiguration(".win_position_w:" + screenDevicesCount, "680")); 957 int theH = Integer.parseInt(AEWorkspace.getInstance() 958 .getDefaultUserConfiguration(".win_position_h:" + screenDevicesCount, "500")); 959 960 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 961 GraphicsEnvironment localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); 962 963 if (localGraphicsEnvironment.getScreenDevices().length > 1 964 || (theX < screenSize.getWidth() - 20 && theY < screenSize.getHeight() - 20)) { 965 Dimension size = new Dimension(theW, theH); 966 setSize(size); 967 setLocation(theX, theY); 968 checkLayoutStyle(); 969 970 FrameStyle frameStyle = getFrameStyle(); 971 setAlwaysOnTop(frameStyle != FrameStyle.FULL); 972 } 973 } catch (Exception e) { 974 super.pack(); 975 } 976 } 977 978 void refreshTaskMap() { 979 Thread thread = new Thread(() -> { 980 refreshButton.setEnabled(false); 981 try { 982 refreshTaskPath(); 983 984 } catch (IOException e1) { 985 JOptionPane.showMessageDialog(AEFrame.this, 986 "<html>Incorrect TESTDIR value.<br/><br/><i>" + e1.getMessage() + "</i></html>"); 987 e1.printStackTrace(); 988 } 989 refreshButton.setEnabled(true); 990 }); 991 thread.start(); 992 } 993 994 public void checkFailure(TaskEditor editor) { 995 mainButton.setIcon(getIcon(LOGO_ERROR_JPG)); 996 if (editor != null) { 997 failedEditor = editor; 998 } 999 } 1000 1001 public void criticalError(TaskEditor editor) { 1002 mainButton.setIcon(getIcon(LOGO_FATAL_JPG)); 1003 progress.setForeground(Color.RED); 1004 if (editor != null) { 1005 failedEditor = editor; 1006 } 1007 } 1008 1009 public void setConsoleDefaultInput(boolean defaultMode) { 1010 defaultInputBox.setSelected(defaultMode); 1011 mainButton.setIcon(getIcon(getLogo())); 1012 } 1013 1014 public UIChoiceTaskRunner getChoiceTaskRunner(MultiTaskRunDialog nameDialog, Processor taskProcessor, 1015 Object setup) { 1016 boolean useHistory = setup == null; 1017 String name = nameDialog.getName(); 1018 return new UIChoiceTaskRunner(name, taskProcessor, useHistory); 1019 } 1020 1021 public void startTaskNotify(TaskEditor editor) { 1022 if (failedEditor == editor) 1023 mainButton.setIcon(getIcon(getLogo())); 1024 } 1025 1026 protected void createAboutPanel() { 1027 JToolBar theToolBar = new JToolBar(); 1028 theToolBar.setFloatable(false); 1029 getContentPane().add(theToolBar, BorderLayout.SOUTH); 1030 } 1031 1032 public void progressValue(int i, int length, boolean success) { 1033 progress.setMinimum(0); 1034 if (length > 0) { 1035 progress.setValue(i); 1036 progress.setMaximum(length); 1037 progress.setForeground(success ? Color.green.darker() : Color.red.darker()); 1038 } else { 1039 progress.setValue(0); 1040 progress.setMaximum(100); 1041 progress.setForeground(null); 1042 } 1043 } 1044 1045 public String inputSelectChoice(String name, String description, String[] values, String defaultValue, 1046 boolean notifyMe, String type) { 1047 if (defaultValue == null && values.length > 0) { 1048 defaultValue = AEWorkspace.getInstance().getDefaultUserConfiguration(".choice." + name, values[0]); 1049 } 1050 1051 if (!workspace.isConsoleDefaultInput(name, null)) { 1052 pushOnTop(); 1053 if (notifyMe) { 1054 soundManager.play(); 1055 } 1056 1057 defaultValue = simpleInput(name, description, values, defaultValue, false, type); 1058 SoundManager.stop(); 1059 AEWorkspace.getInstance().setDefaultUserConfiguration(".choice." + name, defaultValue); 1060 } 1061 1062 return defaultValue; 1063 } 1064 1065 public void pushOnTop() { 1066 if (!isAlwaysOnTop()) { 1067 setAlwaysOnTop(true); 1068 setAlwaysOnTop(false); 1069 } 1070 } 1071 1072 public boolean isConsoleDefaultInput() { 1073 return defaultInputBox != null ? defaultInputBox.isSelected() : true; 1074 } 1075 1076 public void resetConfiguration() { 1077 tabbedPanel.removeAll(); 1078 1079 } 1080 1081 public void resetUserAction() { 1082 lastUserActionTime = null; 1083 } 1084 1085 public void fireBuzzAction() { 1086 fireBuzzAction(null); 1087 } 1088 1089 public void fireBuzzAction(Runnable runnable) { 1090 if (isUserInactive()) { 1091 buzzAction(runnable); 1092 } else { 1093 if (runnable != null) { 1094 runnable.run(); 1095 } 1096 } 1097 } 1098 1099 private void buzzAction(Runnable runnable) { 1100 1101 soundManager.play(); 1102 1103 boolean alwaysOnTop = isAlwaysOnTop(); 1104 if (!alwaysOnTop) { 1105 setAlwaysOnTop(true); 1106 } 1107 1108 if (getState() == Frame.ICONIFIED) { 1109 setState(Frame.NORMAL); 1110 } 1111 1112 Thread buzzThread = new Thread(new Runnable() { 1113 private Color color = Color.MAGENTA; 1114 1115 private int borderSize = 6; 1116 private int period = 200; 1117 1118 @Override 1119 public void run() { 1120 boolean bordered = true; 1121 while (!Thread.currentThread().isInterrupted()) { 1122 setBorder(bordered); 1123 bordered = !bordered; 1124 try { 1125 Thread.sleep(period); 1126 } catch (InterruptedException e) { 1127 Thread.currentThread().interrupt(); 1128 break; 1129 } 1130 } 1131 setBorder(false); 1132 } 1133 1134 private void setBorder(boolean bordered) { 1135 JComponent glassPane = (JComponent) getContentPane(); 1136 if (bordered) { 1137 glassPane.setBorder( 1138 BorderFactory.createMatteBorder(borderSize, borderSize, borderSize, borderSize, color)); 1139 } else { 1140 glassPane.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 0, Color.LIGHT_GRAY)); 1141 } 1142 glassPane.repaint(); 1143 } 1144 }); 1145 buzzThread.start(); 1146 1147 if (runnable == null) { 1148 JOptionPane.showMessageDialog(getContentPane(), "Process finished!", "Notification", 1149 JOptionPane.INFORMATION_MESSAGE); 1150 } else { 1151 runnable.run(); 1152 } 1153 1154 SoundManager.stop(); 1155 1156 ((JPanel) getContentPane()).setBorder(BorderFactory.createMatteBorder(0, 0, 0, 0, Color.LIGHT_GRAY)); 1157 buzzThread.interrupt(); 1158 setAlwaysOnTop(alwaysOnTop); 1159 } 1160 1161 public String choiceValue(String name, String description, Object[] values, boolean notifyMe) { 1162 1163 String defaultValue = null; 1164 if (values != null && values.length > 0) { 1165 defaultValue = (String) values[0]; 1166 } 1167 String result = AEWorkspace.getInstance().getDefaultUserConfiguration(".choiceValue." + name, defaultValue); 1168 1169 if (!workspace.isConsoleDefaultInput(name, description)) { 1170 pushOnTop(); 1171 if (notifyMe) { 1172 soundManager.play(); 1173 } 1174 result = simpleInput(name, description, values, result, false, null); 1175 AEWorkspace.getInstance().setDefaultUserConfiguration(".choiceValue." + name, result); 1176 SoundManager.stop(); 1177 } 1178 1179 return result; 1180 } 1181 1182 public void saveEnvProperties() { 1183 String customConfPropFileName = getWorkspace().getCustomConfPropFileName(getWorkspace().getConfigurationName()); 1184 1185 File theConfDir = getWorkspace().getHomeConfigurationsDir(); 1186 if (theConfDir.exists() && theConfDir.isDirectory()) { 1187 theConfDir.mkdirs(); 1188 } 1189 1190 Properties theConfProp = new Properties(); 1191 1192 Set<Entry<String, Object>> entrySet = getWorkspace().getSystemVariables().entrySet(); 1193 for (Entry<String, Object> entry : entrySet) { 1194 Object value = entry.getValue(); 1195 if (value instanceof String) { 1196 theConfProp.setProperty(entry.getKey(), (String) value); 1197 } else if (value instanceof List) { 1198 theConfProp.setProperty(entry.getKey(), (String) ((List<?>) value).get(0)); 1199 } 1200 } 1201 1202 try (FileOutputStream theFile = new FileOutputStream(customConfPropFileName)) { 1203 theConfProp.store(theFile, null); 1204 } catch (IOException e1) { 1205 e1.printStackTrace(); 1206 } 1207 1208 try { 1209 refreshTaskPath(); 1210 } catch (Exception e) { 1211 e.printStackTrace(); 1212 } 1213 } 1214 1215 private String getLogo() { 1216 return defaultInputBox.isSelected() ? TAKE_IT_EASY_JPG : LOGO_JPG; 1217 } 1218 1219 public boolean isUserInactive() { 1220 return lastUserActionTime == null 1221 || (lastUserActionTime + USER_ACTION_NOTIFY_TIMEOUT) < System.currentTimeMillis(); 1222 } 1223 1224 public SoundManager getSoundManager() { 1225 return soundManager; 1226 } 1227 1228 public void setSoundManager(SoundManager soundManager) { 1229 this.soundManager = soundManager; 1230 } 1231 1232 public AEWorkspace getWorkspace() { 1233 return workspace; 1234 } 1235 1236 public void endTask(RecipeRunner testRunner) { 1237 checkRunningTasks(testRunner); 1238 if (testRunner instanceof TaskEditor && ((TaskEditor) testRunner).isNotifyMe()) { 1239 fireBuzzAction(); 1240 } 1241 } 1242 1243 private void checkRunningTasks(RecipeRunner testRunner) { 1244 boolean hasRunningRecipes = false; 1245 1246 for (int i = 0; i < tabbedPanel.getTabCount() && !hasRunningRecipes; i++) { 1247 Component comp = tabbedPanel.getComponentAt(i); 1248 if (comp instanceof TaskPanel) { 1249 TaskPanel taskPanel = (TaskPanel) comp; 1250 TaskEditor taskEditor = taskPanel.getTaskEditor(); 1251 if (taskEditor.isRunning() && taskEditor != testRunner) { 1252 hasRunningRecipes = true; 1253 } 1254 } 1255 } 1256 } 1257 1258 private void addNewUserVariable() { 1259 new Thread(() -> { 1260 try { 1261 String name = inputValue("New Custom Variable", null, null, null, false); 1262 varTable.addVariable(name, ""); 1263 } catch (TaskCancelingException e) { 1264 // do nothing 1265 } 1266 }).start(); 1267 } 1268 1269 public RecipesPanel getRecipesPanel() { 1270 return recipesPanel; 1271 } 1272}