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