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