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