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