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}