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 taskEditor.setActiveTest(name); 414 } 415 String logName = StringUtils.defaultString(name, SpecialCommands.STARTUP); 416 taskEditor.edit(logName); 417 addTaskPanel(taskEditor, logName); 418 return taskEditor; 419 } 420 421 private void addTaskPanel(TaskEditor taskEditor, String name) { 422 taskEditor.showPanel(tabbedPanel, name); 423 leftMenu.add(new TaskMenuItem(name, taskEditor)); 424 } 425 426 public void refreshTaskPath() throws IOException { 427 getWorkspace().refreshTaskPath(); 428 getRecipesPanel().refreshTaskPathTable(); 429 } 430 431 public String inputValue(String name, String description, String value, String type, boolean notifyMe) { 432 description = StringUtils.defaultString(description, name); 433 434 if (value == null) { 435 value = AEWorkspace.getInstance().getDefaultUserConfiguration(".inputValue." + description, value); 436 } 437 438 pushOnTop(); 439 if (notifyMe) { 440 soundManager.play(); 441 } 442 443 type = (String) ObjectUtils.defaultIfNull(type, StringUtils.EMPTY); 444 445 switch (type) { 446 case "password": 447 value = inputPassword(description, value); 448 break; 449 case "text": 450 value = simpleInput(description, description, value, null, true, null); 451 break; 452 case "map": 453 value = inputMap(description, value); 454 break; 455 456 default: 457 value = simpleInput(name, description, value, null, false, null); 458 } 459 460 if (notifyMe) { 461 SoundManager.stop(); 462 } 463 464 if (!"text".equals(type)) { 465 AEWorkspace.getInstance().setDefaultUserConfiguration(".inputValue." + description, value); 466 } 467 return value; 468 } 469 470 public void errorInformation(Processor processor, Throwable exception, Node command, boolean notifyMe) 471 throws Throwable { 472 int showConfirmDialog = 0; 473 474 String name = "Error Trace"; 475 if (!workspace.isConsoleDefaultInput(name, null)) { 476 TracePanel tracePanel = new TracePanel(this, processor, exception, command); 477 CheckPointBox vip = new CheckPointBox(name, null, InputDialogType.TASKS); 478 tracePanel.add(vip, BorderLayout.SOUTH); 479 480 pushOnTop(); 481 if (notifyMe) { 482 soundManager.play(); 483 } 484 showConfirmDialog = OptionPane.showConfirmDialog(JOptionPane.getRootFrame(), tracePanel, name); 485 vip.saveCheckpointFlag(); 486 } else { 487 throw exception; 488 } 489 490 if (showConfirmDialog == -1) { 491 processor.stop(); 492 493 } else if (showConfirmDialog != 2) { 494 CommandException commandException = new CommandException(exception, processor, command); 495 commandException.reviewed(); 496 throw commandException; 497 } 498 } 499 500 @SuppressWarnings("unchecked") 501 private String simpleInput(String name, String description, Object values, Object defaultValue, boolean bigText, 502 String type) { 503 String result = null; 504 505 int questionMessage = JOptionPane.QUESTION_MESSAGE; 506 507 JPanel simplInputPanel = new JPanel(new BorderLayout()); 508 JLabel label = new JLabel(name); 509 label.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); 510 511 simplInputPanel.add(label, BorderLayout.NORTH); 512 if (description != null) { 513 String message = description; 514 if (!StringUtils.startsWith(description, "<html>")) { 515 message = "<html><body>" + description.replace("\n", "<br/>") + "</body></html>"; 516 } 517 simplInputPanel.add(new JLabel(message), BorderLayout.NORTH); 518 } 519 520 JComponent inputField; 521 if (values instanceof String || values == null) { 522 JComponent jTextField; 523 if (bigText) { 524 jTextField = new JEditorPane(); 525 jTextField.setMinimumSize(new Dimension(400, 300)); 526 jTextField.setDropTarget(new DropTarget() { 527 private static final long serialVersionUID = 1L; 528 529 @Override 530 public synchronized void drop(DropTargetDropEvent evt) { 531 try { 532 evt.acceptDrop(DnDConstants.ACTION_COPY); 533 List<File> droppedFiles = (List<File>) evt.getTransferable() 534 .getTransferData(DataFlavor.javaFileListFlavor); 535 for (File file : droppedFiles) { 536 String text = IOUtils.toString(new FileInputStream(file)); 537 JEditorPane jEditorPane = (JEditorPane) jTextField; 538 jEditorPane.setText(jEditorPane.getText() + text); 539 } 540 } catch (Exception ex) { 541 ex.printStackTrace(); 542 } 543 } 544 }); 545 546 UIUtils.addJPopupMenuTo((JEditorPane) jTextField); 547 } else { 548 jTextField = new JTextField(20); 549 } 550 551 inputField = jTextField; 552 String text = (String) values; 553 554 if (inputField instanceof JTextField) { 555 ((JTextField) inputField).setText(text); 556 simplInputPanel.add(inputField, BorderLayout.CENTER); 557 } 558 if (inputField instanceof JEditorPane) { 559 ((JEditorPane) inputField).setText(text); 560 simplInputPanel.add(new JScrollPane(inputField), BorderLayout.CENTER); 561 } 562 563 } else { 564 if (StringUtils.contains(type, "list")) { 565 JList<String> jComboBox = new JList<String>((String[]) values); 566 jComboBox.setSelectedValue(defaultValue, true); 567 inputField = new JScrollPane(jComboBox); 568 jComboBox.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 569 jComboBox.setVisibleRowCount(5); 570 571 } else { 572 JComboBox<Object> jComboBox = new JComboBox<>((Object[]) values); 573 jComboBox.setEditable(true); 574 jComboBox.setSelectedItem(defaultValue); 575 inputField = jComboBox; 576 } 577 simplInputPanel.add(inputField, BorderLayout.CENTER); 578 } 579 580 CheckPointBox vip = new CheckPointBox(name, description, InputDialogType.VAR); 581 582 if (!vip.isSelected() && (LOOK_AND_FEEL.equals(name) || workspace.isConsoleDefaultInput(name, description))) { 583 vip.saveCheckpointFlag(); 584 String value = (String) defaultValue; 585 if (defaultValue == null) { 586 if (values instanceof String) { 587 value = (String) values; 588 } 589 if (values instanceof String[] && ((String[]) values).length > 1) { 590 value = ((String[]) values)[0]; 591 } 592 } 593 return value; 594 } 595 596 if (SwingUtilities.getRoot(this).isVisible() || LOOK_AND_FEEL.equals(name)) { 597 simplInputPanel.add(vip, BorderLayout.SOUTH); 598 } 599 600 Frame rootFrame = JOptionPane.getRootFrame(); 601 int option = OptionPane.showOptionDialog(rootFrame, simplInputPanel, "Dialog Box", questionMessage); 602 603 vip.saveCheckpointFlag(); 604 605 if (option == JOptionPane.OK_OPTION) { 606 if (values instanceof String || values == null) { 607 608 if (inputField instanceof JTextField) { 609 result = ((JTextField) inputField).getText(); 610 } 611 if (inputField instanceof JEditorPane) { 612 result = ((JEditorPane) inputField).getText(); 613 } 614 if (inputField instanceof JList) { 615 result = ((JList<String>) inputField).getSelectedValue(); 616 } 617 } else { 618 if (inputField instanceof JScrollPane) { 619 inputField = (JComponent) ((JScrollPane) inputField).getViewport().getView(); 620 621 } 622 if (inputField instanceof JComboBox) { 623 result = (String) ((JComboBox<?>) inputField).getSelectedItem(); 624 } 625 if (inputField instanceof JList<?>) { 626 result = (String) ((JList<?>) inputField).getSelectedValue(); 627 } 628 } 629 } 630 631 if (option == JOptionPane.CLOSED_OPTION) { 632 throw new TaskCancelingException(); 633 } 634 635 return result; 636 } 637 638 private String inputMap(String description, String aValue) { 639 final Map<String, String> map = AEUtils.convertStringToMap(aValue); 640 641 Set<Entry<String, String>> entrySet = map.entrySet(); 642 Object[][] data = new Object[map.size()][]; 643 int i = 0; 644 for (Entry<String, String> entry : entrySet) { 645 Object[] row = new Object[2]; 646 row[0] = entry.getKey(); 647 row[1] = entry.getValue(); 648 data[i++] = row; 649 } 650 651 Object[] columnNames = { "Name", "Value" }; 652 final JTable table = new JTable(data, columnNames) { 653 private static final long serialVersionUID = 1L; 654 655 @Override 656 public Dimension getPreferredScrollableViewportSize() { 657 Dimension d = getPreferredSize(); 658 int n = getRowHeight(); 659 return new Dimension(d.width, (n * map.size())); 660 } 661 }; 662 table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 663 table.setCellSelectionEnabled(true); 664 665 JPanel jPanel = new JPanel(); 666 jPanel.setLayout(new BorderLayout()); 667 JScrollPane sp = new JScrollPane(table); 668 jPanel.add(sp, BorderLayout.CENTER); 669 670 CheckPointBox vip = new CheckPointBox(description, null, InputDialogType.MAP); 671 jPanel.add(vip, BorderLayout.SOUTH); 672 673 jPanel.add(new JLabel(description), BorderLayout.NORTH); 674 Frame rootFrame = JOptionPane.getRootFrame(); 675 676 int option = OptionPane.showOptionDialog(rootFrame, jPanel, "Input the map property", 677 JOptionPane.QUESTION_MESSAGE); 678 vip.saveCheckpointFlag(); 679 680 String result = null; 681 if (option == -1) { 682 throw new TaskCancelingException(); 683 } 684 if (option == 0) { 685 Map<String, String> resultMap = new HashMap<>(map); 686 for (int j = 0; j < map.size(); j++) { 687 resultMap.put((String) data[j][0], (String) data[j][1]); 688 } 689 result = resultMap.toString(); 690 } 691 692 return result; 693 } 694 695 private String inputPassword(String name, String value) { 696 String result; 697 CheckPointBox vip = new CheckPointBox(name, null, InputDialogType.PWD); 698 if (vip.isSelected() || !workspace.isConsoleDefaultInput(name, null)) { 699 result = OptionPane.showInputPasswordDialog(name, value, vip, JOptionPane.QUESTION_MESSAGE, 700 OptionPane.OPTIONS); 701 } else { 702 result = value; 703 } 704 vip.saveCheckpointFlag(); 705 706 return result; 707 } 708 709 public void message(String string) { 710 JOptionPane.showMessageDialog(this, string); 711 } 712 713 public String getTestPath(String name) { 714 String property = getWorkspace().getTestsDescList().getProperty(name); 715 return property == null ? name : property; 716 } 717 718 private JTabbedPane createConfirPanel() { 719 720 JScrollPane theScrollPane = new JScrollPane(varTable); 721 722 JPanel theButtonsPanel = new JPanel(); 723 JButton theButton = new Button("save.png"); 724 theButton.setToolTipText("Save custom configuration"); 725 theButton.addActionListener(e -> SwingUtilities.invokeLater(this::saveEnvProperties)); 726 theButtonsPanel.add(theButton); 727 728 theButton = new Button("default.png"); 729 theButton.setToolTipText("Reset custom configuration"); 730 theButton.addActionListener(e -> SwingUtilities.invokeLater(() -> { 731 String customConfPropFileName = getWorkspace() 732 .getCustomConfPropFileName(getWorkspace().getConfigurationName()); 733 File theConfFile = new File(customConfPropFileName); 734 boolean result = theConfFile.delete(); 735 736 try { 737 workspace.loadConfProperties(); 738 } catch (IOException e1) { 739 e1.printStackTrace(); 740 } 741 742 varTable.refresh(); 743 744 if (result) { 745 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), 746 "The user configuration has been deleted.\nPlease restart the application."); 747 } 748 749 String lookAndFeel = StringUtils.trim((String) workspace.getSystemVariables().get(LOOK_AND_FEEL)); 750 if (StringUtils.isNotBlank(lookAndFeel)) { 751 lookAndFeel(lookAndFeel); 752 } 753 })); 754 theButtonsPanel.add(theButton); 755 756 theButton = new Button("sandbox.png"); 757 theButton.setToolTipText("Open user folder"); 758 theButton.addActionListener(e -> { 759 String absolutePath = AEWorkspace.getInstance().getHomeWorkingDir().getAbsolutePath(); 760 try { 761 Desktop.getDesktop().open(new File(absolutePath)); 762 } catch (IOException e1) { 763 e1.printStackTrace(); 764 } 765 }); 766 theButtonsPanel.add(theButton); 767 768 JPanel envToolPanel = new JPanel(new BorderLayout(4, 4)); 769 770 theButton = new Button("plus.png"); 771 theButton.setToolTipText("Add new custom variable"); 772 theButton.addActionListener(e -> SwingUtilities.invokeLater(this::addNewUserVariable)); 773 theButtonsPanel.add(theButton); 774 envToolPanel.add(theButton, BorderLayout.WEST); 775 776 theButton = new Button("hierarchy.png"); 777 theButton.setToolTipText(ConfigurationView.CONFIGURATION_TITLE); 778 theButton.setEnabled(true); 779 theButton.addActionListener(e -> configurationDialod.setVisible(true)); 780 781 envToolPanel.add(theButton, BorderLayout.EAST); 782 783 JPanel theSysvarButtonsPanel = new JPanel(new BorderLayout(4, 4)); 784 theSysvarButtonsPanel.add(theScrollPane, BorderLayout.CENTER); 785 theSysvarButtonsPanel.add(envToolPanel, BorderLayout.NORTH); 786 theSysvarButtonsPanel.add(theButtonsPanel, BorderLayout.SOUTH); 787 788 JPanel recPanel = new JPanel(); 789 recPanel.setLayout(new BorderLayout()); 790 recPanel.add(getRecipesPanel(), BorderLayout.CENTER); 791 792 JPanel comp = new JPanel(); 793 794 JButton theCreate = new Button("create.png"); 795 theCreate.setToolTipText("Create a new recipe"); 796 theCreate.addActionListener(e -> { 797 try { 798 799 String taskName = JOptionPane.showInputDialog(JOptionPane.getRootFrame(), "Task name"); 800 if (taskName != null) { 801 TaskEditor taskEditor = new TaskEditor(AEFrame.this); 802 addTaskPanel(taskEditor, taskName); 803 taskEditor.create(taskName); 804 } 805 806 } catch (Exception e1) { 807 e1.printStackTrace(); 808 } 809 }); 810 comp.add(theCreate); 811 812 JButton theRefresh = new Button("refresh.png"); 813 theRefresh.setToolTipText("Refresh recipe list"); 814 theRefresh.addActionListener(arg0 -> { 815 try { 816 workspace.refreshTaskPath(); 817 } catch (IOException e) { 818 e.printStackTrace(); 819 } 820 }); 821 comp.add(theRefresh); 822 823 recPanel.add(comp, BorderLayout.SOUTH); 824 825 JTabbedPane tablePane = new JTabbedPane(); 826 tablePane.add("Recipes", recPanel); 827 tablePane.add("Environment", theSysvarButtonsPanel); 828 829 refreshButton.addActionListener(e -> refreshTaskMap()); 830 831 return tablePane; 832 } 833 834 public Long getLastUserActionTime() { 835 return lastUserActionTime; 836 } 837 838 public String loadConfiguration() { 839 return loadConfiguration(null); 840 } 841 842 protected String loadConfiguration(String confName) { 843 String currentConfName = getWorkspace().getConfigurationName(); 844 String loadConfiguration = getWorkspace().loadConfiguration(confName, true); 845 if (loadConfiguration != null) { 846 createFrame(loadConfiguration); 847 848 ConfigurationView configurationEditor = new ConfigurationView(this); 849 configurationEditor.refreshActiveConfig(); 850 851 configurationDialod = new DialogFrame(this, ConfigurationView.CONFIGURATION_TITLE, configurationEditor); 852 UIUtils.applyPreferedView(configurationDialod, ConfigurationView.CONFIGURATION_TITLE); 853 854 configurationDialod.setIconImage(AEFrame.getIcon("hierarchy.png").getImage()); 855 856 Node configNode = workspace.getConfigNode(); 857 String menuName = configNode.getAttribute("menu"); 858 Node[] theToolBarNodes = configNode.getNodes(MENU_TAG_NAME); 859 if (StringUtils.isNotBlank(menuName)) { 860 Node findNode = configNode.findNode(MENU_TAG_NAME, "name", menuName); 861 if (findNode != null) { 862 theToolBarNodes = new Node[] { findNode }; 863 } 864 } 865 866 addEditor("Variables", VariableViewPanel.class); 867 868 if (theToolBarNodes != null && theToolBarNodes.length > 0) { 869 menuDialog.createToolBar(theToolBarNodes); 870 } 871 872 if (!StringUtils.equals(currentConfName, loadConfiguration)) { 873 tabbedPanel.removeAll(); 874 } 875 876 varTable.refresh(); 877 getRecipesPanel().refreshTaskPathTable(); 878 879 pack(); 880 setVisible(true); 881 882 log.debug("Application started."); 883 } 884 885 return loadConfiguration; 886 } 887 888 private void addEditor(String name, Class<VariableViewPanel> editorClass) { 889 Node configNode = workspace.getConfigNode(); 890 891 Node variables = new Node("Editor"); 892 variables.setAttribute("name", name); 893 variables.setAttribute("class", editorClass.getName()); 894 configNode.add(variables); 895 } 896 897 void lookAndFeel(String lookAndFeel) { 898 lookAndFeel = StringUtils.trimToNull(lookAndFeel); 899 if (StringUtils.isNotBlank(lookAndFeel)) { 900 try { 901 UIManager.setLookAndFeel(lookAndFeel); 902 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 903 | UnsupportedLookAndFeelException e) { 904 e.printStackTrace(); 905 message(e.getClass().getSimpleName() + ": " + e.getLocalizedMessage()); 906 } 907 } 908 } 909 910 public void removeTab(TaskEditor editor) { 911 tabbedPanel.remove(editor.getMainPanel()); 912 for (int i = 0; i < leftMenu.getComponentCount(); i++) { 913 Component component = leftMenu.getComponent(i); 914 if (component instanceof TaskMenuItem) { 915 TaskMenuItem taskMenuItem = (TaskMenuItem) component; 916 if (taskMenuItem.getTaskEditor() == editor) { 917 leftMenu.remove(taskMenuItem); 918 } 919 } 920 } 921 } 922 923 public void runTask(String name) { 924 if (name != null) { 925 TaskEditor taskEditor = null; 926 try { 927 taskEditor = new TaskEditor(this); 928 addTaskPanel(taskEditor, name); 929 taskEditor.start(name); 930 } catch (ConfigurationException | CommandException e) { 931 refreshRecipeMap(taskEditor); 932 } 933 } 934 } 935 936 public void refreshRecipeMap(TaskEditor taskEditor) { 937 removeTab(taskEditor); 938 int showConfirmDialog = JOptionPane.showConfirmDialog(this, "Recipe file is not found. Refresh recipe map?"); 939 if (showConfirmDialog == JOptionPane.OK_OPTION) { 940 refreshTaskMap(); 941 } 942 } 943 944 @Override 945 public void pack() { 946 int screenDevicesCount = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length; 947 948 try { 949 int theX = (int) Float.parseFloat(AEWorkspace.getInstance() 950 .getDefaultUserConfiguration(".win_position_x:" + screenDevicesCount, "0")); 951 int theY = (int) Float.parseFloat(AEWorkspace.getInstance() 952 .getDefaultUserConfiguration(".win_position_y:" + screenDevicesCount, "0")); 953 int theW = Integer.parseInt(AEWorkspace.getInstance() 954 .getDefaultUserConfiguration(".win_position_w:" + screenDevicesCount, "680")); 955 int theH = Integer.parseInt(AEWorkspace.getInstance() 956 .getDefaultUserConfiguration(".win_position_h:" + screenDevicesCount, "500")); 957 958 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 959 GraphicsEnvironment localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); 960 961 if (localGraphicsEnvironment.getScreenDevices().length > 1 962 || (theX < screenSize.getWidth() - 20 && theY < screenSize.getHeight() - 20)) { 963 Dimension size = new Dimension(theW, theH); 964 setSize(size); 965 setLocation(theX, theY); 966 checkLayoutStyle(); 967 968 FrameStyle frameStyle = getFrameStyle(); 969 setAlwaysOnTop(frameStyle != FrameStyle.FULL); 970 } 971 } catch (Exception e) { 972 super.pack(); 973 } 974 } 975 976 void refreshTaskMap() { 977 Thread thread = new Thread(() -> { 978 refreshButton.setEnabled(false); 979 try { 980 refreshTaskPath(); 981 982 } catch (IOException e1) { 983 JOptionPane.showMessageDialog(AEFrame.this, 984 "<html>Incorrect TESTDIR value.<br/><br/><i>" + e1.getMessage() + "</i></html>"); 985 e1.printStackTrace(); 986 } 987 refreshButton.setEnabled(true); 988 }); 989 thread.start(); 990 } 991 992 public void checkFailure(TaskEditor editor) { 993 mainButton.setIcon(getIcon(LOGO_ERROR_JPG)); 994 if (editor != null) { 995 failedEditor = editor; 996 } 997 } 998 999 public void criticalError(TaskEditor editor) { 1000 mainButton.setIcon(getIcon(LOGO_FATAL_JPG)); 1001 progress.setForeground(Color.RED); 1002 if (editor != null) { 1003 failedEditor = editor; 1004 } 1005 } 1006 1007 public void setConsoleDefaultInput(boolean defaultMode) { 1008 defaultInputBox.setSelected(defaultMode); 1009 mainButton.setIcon(getIcon(getLogo())); 1010 } 1011 1012 public UIChoiceTaskRunner getChoiceTaskRunner(MultiTaskRunDialog nameDialog, Processor taskProcessor, 1013 Object setup) { 1014 boolean useHistory = setup == null; 1015 String name = nameDialog.getName(); 1016 return new UIChoiceTaskRunner(name, taskProcessor, useHistory); 1017 } 1018 1019 public void startTaskNotify(TaskEditor editor) { 1020 if (failedEditor == editor) 1021 mainButton.setIcon(getIcon(getLogo())); 1022 } 1023 1024 protected void createAboutPanel() { 1025 JToolBar theToolBar = new JToolBar(); 1026 theToolBar.setFloatable(false); 1027 getContentPane().add(theToolBar, BorderLayout.SOUTH); 1028 } 1029 1030 public void progressValue(int i, int length, boolean success) { 1031 progress.setMinimum(0); 1032 if (length > 0) { 1033 progress.setValue(i); 1034 progress.setMaximum(length); 1035 progress.setForeground(success ? Color.green.darker() : Color.red.darker()); 1036 } else { 1037 progress.setValue(0); 1038 progress.setMaximum(100); 1039 progress.setForeground(null); 1040 } 1041 } 1042 1043 public String inputSelectChoice(String name, String description, String[] values, String defaultValue, 1044 boolean notifyMe, String type) { 1045 if (defaultValue == null && values.length > 0) { 1046 defaultValue = AEWorkspace.getInstance().getDefaultUserConfiguration(".choice." + name, values[0]); 1047 } 1048 1049 if (!workspace.isConsoleDefaultInput(name, null)) { 1050 pushOnTop(); 1051 if (notifyMe) { 1052 soundManager.play(); 1053 } 1054 1055 defaultValue = simpleInput(name, description, values, defaultValue, false, type); 1056 SoundManager.stop(); 1057 AEWorkspace.getInstance().setDefaultUserConfiguration(".choice." + name, defaultValue); 1058 } 1059 1060 return defaultValue; 1061 } 1062 1063 public void pushOnTop() { 1064 if (!isAlwaysOnTop()) { 1065 setAlwaysOnTop(true); 1066 setAlwaysOnTop(false); 1067 } 1068 } 1069 1070 public boolean isConsoleDefaultInput() { 1071 return defaultInputBox != null ? defaultInputBox.isSelected() : true; 1072 } 1073 1074 public void resetConfiguration() { 1075 tabbedPanel.removeAll(); 1076 1077 } 1078 1079 public void resetUserAction() { 1080 lastUserActionTime = null; 1081 } 1082 1083 public void fireBuzzAction() { 1084 fireBuzzAction(null); 1085 } 1086 1087 public void fireBuzzAction(Runnable runnable) { 1088 if (isUserInactive()) { 1089 buzzAction(runnable); 1090 } else { 1091 if (runnable != null) { 1092 runnable.run(); 1093 } 1094 } 1095 } 1096 1097 private void buzzAction(Runnable runnable) { 1098 1099 soundManager.play(); 1100 1101 boolean alwaysOnTop = isAlwaysOnTop(); 1102 if (!alwaysOnTop) { 1103 setAlwaysOnTop(true); 1104 } 1105 1106 if (getState() == Frame.ICONIFIED) { 1107 setState(Frame.NORMAL); 1108 } 1109 1110 Thread buzzThread = new Thread(new Runnable() { 1111 private Color color = Color.MAGENTA; 1112 1113 private int borderSize = 6; 1114 private int period = 200; 1115 1116 @Override 1117 public void run() { 1118 boolean bordered = true; 1119 while (!Thread.currentThread().isInterrupted()) { 1120 setBorder(bordered); 1121 bordered = !bordered; 1122 try { 1123 Thread.sleep(period); 1124 } catch (InterruptedException e) { 1125 Thread.currentThread().interrupt(); 1126 break; 1127 } 1128 } 1129 setBorder(false); 1130 } 1131 1132 private void setBorder(boolean bordered) { 1133 JComponent glassPane = (JComponent) getContentPane(); 1134 if (bordered) { 1135 glassPane.setBorder( 1136 BorderFactory.createMatteBorder(borderSize, borderSize, borderSize, borderSize, color)); 1137 } else { 1138 glassPane.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 0, Color.LIGHT_GRAY)); 1139 } 1140 glassPane.repaint(); 1141 } 1142 }); 1143 buzzThread.start(); 1144 1145 if (runnable == null) { 1146 JOptionPane.showMessageDialog(getContentPane(), "Process finished!", "Notification", 1147 JOptionPane.INFORMATION_MESSAGE); 1148 } else { 1149 runnable.run(); 1150 } 1151 1152 SoundManager.stop(); 1153 1154 ((JPanel) getContentPane()).setBorder(BorderFactory.createMatteBorder(0, 0, 0, 0, Color.LIGHT_GRAY)); 1155 buzzThread.interrupt(); 1156 setAlwaysOnTop(alwaysOnTop); 1157 } 1158 1159 public String choiceValue(String name, String description, Object[] values, boolean notifyMe) { 1160 1161 String defaultValue = null; 1162 if (values != null && values.length > 0) { 1163 defaultValue = (String) values[0]; 1164 } 1165 String result = AEWorkspace.getInstance().getDefaultUserConfiguration(".choiceValue." + name, defaultValue); 1166 1167 if (!workspace.isConsoleDefaultInput(name, description)) { 1168 pushOnTop(); 1169 if (notifyMe) { 1170 soundManager.play(); 1171 } 1172 result = simpleInput(name, description, values, result, false, null); 1173 AEWorkspace.getInstance().setDefaultUserConfiguration(".choiceValue." + name, result); 1174 SoundManager.stop(); 1175 } 1176 1177 return result; 1178 } 1179 1180 public void saveEnvProperties() { 1181 String customConfPropFileName = getWorkspace().getCustomConfPropFileName(getWorkspace().getConfigurationName()); 1182 1183 File theConfDir = getWorkspace().getHomeConfigurationsDir(); 1184 if (theConfDir.exists() && theConfDir.isDirectory()) { 1185 theConfDir.mkdirs(); 1186 } 1187 1188 Properties theConfProp = new Properties(); 1189 1190 Set<Entry<String, Object>> entrySet = getWorkspace().getSystemVariables().entrySet(); 1191 for (Entry<String, Object> entry : entrySet) { 1192 Object value = entry.getValue(); 1193 if (value instanceof String) { 1194 theConfProp.setProperty(entry.getKey(), (String) value); 1195 } else if (value instanceof List) { 1196 theConfProp.setProperty(entry.getKey(), (String) ((List<?>) value).get(0)); 1197 } 1198 } 1199 1200 try (FileOutputStream theFile = new FileOutputStream(customConfPropFileName)) { 1201 theConfProp.store(theFile, null); 1202 } catch (IOException e1) { 1203 e1.printStackTrace(); 1204 } 1205 1206 try { 1207 refreshTaskPath(); 1208 } catch (Exception e) { 1209 e.printStackTrace(); 1210 } 1211 } 1212 1213 private String getLogo() { 1214 return defaultInputBox.isSelected() ? TAKE_IT_EASY_JPG : LOGO_JPG; 1215 } 1216 1217 public boolean isUserInactive() { 1218 return lastUserActionTime == null 1219 || (lastUserActionTime + USER_ACTION_NOTIFY_TIMEOUT) < System.currentTimeMillis(); 1220 } 1221 1222 public SoundManager getSoundManager() { 1223 return soundManager; 1224 } 1225 1226 public void setSoundManager(SoundManager soundManager) { 1227 this.soundManager = soundManager; 1228 } 1229 1230 public AEWorkspace getWorkspace() { 1231 return workspace; 1232 } 1233 1234 public void endTask(RecipeRunner testRunner) { 1235 checkRunningTasks(testRunner); 1236 if (testRunner instanceof TaskEditor && ((TaskEditor) testRunner).isNotifyMe()) { 1237 fireBuzzAction(); 1238 } 1239 } 1240 1241 private void checkRunningTasks(RecipeRunner testRunner) { 1242 boolean hasRunningRecipes = false; 1243 1244 for (int i = 0; i < tabbedPanel.getTabCount() && !hasRunningRecipes; i++) { 1245 Component comp = tabbedPanel.getComponentAt(i); 1246 if (comp instanceof TaskPanel) { 1247 TaskPanel taskPanel = (TaskPanel) comp; 1248 TaskEditor taskEditor = taskPanel.getTaskEditor(); 1249 if (taskEditor.isRunning() && taskEditor != testRunner) { 1250 hasRunningRecipes = true; 1251 } 1252 } 1253 } 1254 } 1255 1256 private void addNewUserVariable() { 1257 new Thread(() -> { 1258 try { 1259 String name = inputValue("New Custom Variable", null, null, null, false); 1260 varTable.addVariable(name, ""); 1261 } catch (TaskCancelingException e) { 1262 // do nothing 1263 } 1264 }).start(); 1265 } 1266 1267 public RecipesPanel getRecipesPanel() { 1268 return recipesPanel; 1269 } 1270}