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