1#  Copyright 2008-2015 Nokia Networks
2#  Copyright 2016-     Robot Framework Foundation
3#
4#  Licensed under the Apache License, Version 2.0 (the "License");
5#  you may not use this file except in compliance with the License.
6#  You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10#  Unless required by applicable law or agreed to in writing, software
11#  distributed under the License is distributed on an "AS IS" BASIS,
12#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#  See the License for the specific language governing permissions and
14#  limitations under the License.
15
16import textwrap
17import time
18
19from java.awt import Component
20from java.awt.event import WindowAdapter
21from javax.swing import (BoxLayout,  JLabel, JOptionPane, JPanel,
22                         JPasswordField, JTextField, JList, JScrollPane)
23from javax.swing.JOptionPane import (DEFAULT_OPTION, OK_CANCEL_OPTION,
24                                     OK_OPTION, PLAIN_MESSAGE,
25                                     UNINITIALIZED_VALUE, YES_NO_OPTION)
26
27from robot.utils import html_escape
28
29
30MAX_CHARS_PER_LINE = 120
31
32
33class _SwingDialog(object):
34
35    def __init__(self, pane):
36        self._pane = pane
37
38    def _create_panel(self, message, widget):
39        panel = JPanel()
40        panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS))
41        label = self._create_label(message)
42        label.setAlignmentX(Component.LEFT_ALIGNMENT)
43        panel.add(label)
44        widget.setAlignmentX(Component.LEFT_ALIGNMENT)
45        panel.add(widget)
46        return panel
47
48    def _create_label(self, message):
49        # JLabel doesn't support multiline text, setting size, or wrapping.
50        # Need to handle all that ourselves. Feels like 2005...
51        wrapper = textwrap.TextWrapper(MAX_CHARS_PER_LINE,
52                                       drop_whitespace=False)
53        lines = []
54        for line in html_escape(message, linkify=False).splitlines():
55            if line:
56                lines.extend(wrapper.wrap(line))
57            else:
58                lines.append('')
59        return JLabel('<html>%s</html>' % '<br>'.join(lines))
60
61    def show(self):
62        self._show_dialog(self._pane)
63        return self._get_value(self._pane)
64
65    def _show_dialog(self, pane):
66        dialog = pane.createDialog(None, 'Robot Framework')
67        dialog.setModal(False)
68        dialog.setAlwaysOnTop(True)
69        dialog.addWindowFocusListener(pane.focus_listener)
70        dialog.show()
71        while dialog.isShowing():
72            time.sleep(0.2)
73        dialog.dispose()
74
75    def _get_value(self, pane):
76        value = pane.getInputValue()
77        return value if value != UNINITIALIZED_VALUE else None
78
79
80class MessageDialog(_SwingDialog):
81
82    def __init__(self, message):
83        pane = WrappedOptionPane(message, PLAIN_MESSAGE, DEFAULT_OPTION)
84        _SwingDialog.__init__(self, pane)
85
86
87class InputDialog(_SwingDialog):
88
89    def __init__(self, message, default, hidden=False):
90        self._input_field = JPasswordField() if hidden else JTextField()
91        self._input_field.setText(default)
92        self._input_field.selectAll()
93        panel = self._create_panel(message, self._input_field)
94        pane = WrappedOptionPane(panel, PLAIN_MESSAGE, OK_CANCEL_OPTION)
95        pane.set_focus_listener(self._input_field)
96        _SwingDialog.__init__(self, pane)
97
98    def _get_value(self, pane):
99        if pane.getValue() != OK_OPTION:
100            return None
101        return self._input_field.getText()
102
103
104class SelectionDialog(_SwingDialog):
105
106    def __init__(self, message, options):
107        pane = WrappedOptionPane(message, PLAIN_MESSAGE, OK_CANCEL_OPTION)
108        pane.setWantsInput(True)
109        pane.setSelectionValues(options)
110        _SwingDialog.__init__(self, pane)
111
112
113class MultipleSelectionDialog(_SwingDialog):
114
115    def __init__(self, message, options):
116        self._selection_list = JList(options)
117        self._selection_list.setVisibleRowCount(8)
118        panel = self._create_panel(message, JScrollPane(self._selection_list))
119        pane = WrappedOptionPane(panel, PLAIN_MESSAGE, OK_CANCEL_OPTION)
120        _SwingDialog.__init__(self, pane)
121
122    def _get_value(self, pane):
123        if pane.getValue() != OK_OPTION:
124            return None
125        return list(self._selection_list.getSelectedValuesList())
126
127
128class PassFailDialog(_SwingDialog):
129
130    def __init__(self, message):
131        pane = WrappedOptionPane(message, PLAIN_MESSAGE, YES_NO_OPTION,
132                                 None, ['PASS', 'FAIL'], 'PASS')
133        _SwingDialog.__init__(self, pane)
134
135    def _get_value(self, pane):
136        value = pane.getValue()
137        return value == 'PASS' if value in ['PASS', 'FAIL'] else None
138
139
140class WrappedOptionPane(JOptionPane):
141    focus_listener = None
142
143    def getMaxCharactersPerLineCount(self):
144        return MAX_CHARS_PER_LINE
145
146    def set_focus_listener(self, component):
147        self.focus_listener = WindowFocusListener(component)
148
149
150class WindowFocusListener(WindowAdapter):
151
152    def __init__(self, component):
153        self.component = component
154
155    def windowGainedFocus(self, event):
156        self.component.requestFocusInWindow()
157