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