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