1 | // License: GPL. For details, see LICENSE file. |
---|
2 | package org.openstreetmap.josm.gui.io; |
---|
3 | |
---|
4 | import static org.openstreetmap.josm.tools.I18n.tr; |
---|
5 | |
---|
6 | import java.awt.BorderLayout; |
---|
7 | import java.awt.Dimension; |
---|
8 | import java.awt.FlowLayout; |
---|
9 | import java.awt.Font; |
---|
10 | import java.awt.GridBagConstraints; |
---|
11 | import java.awt.GridBagLayout; |
---|
12 | import java.awt.Insets; |
---|
13 | import java.awt.event.ActionEvent; |
---|
14 | import java.awt.event.FocusAdapter; |
---|
15 | import java.awt.event.FocusEvent; |
---|
16 | import java.awt.event.KeyEvent; |
---|
17 | import java.awt.event.KeyListener; |
---|
18 | import java.awt.event.WindowAdapter; |
---|
19 | import java.awt.event.WindowEvent; |
---|
20 | |
---|
21 | import javax.swing.AbstractAction; |
---|
22 | import javax.swing.BorderFactory; |
---|
23 | import javax.swing.JCheckBox; |
---|
24 | import javax.swing.JComponent; |
---|
25 | import javax.swing.JDialog; |
---|
26 | import javax.swing.JLabel; |
---|
27 | import javax.swing.JPanel; |
---|
28 | import javax.swing.JTextField; |
---|
29 | import javax.swing.KeyStroke; |
---|
30 | |
---|
31 | import org.openstreetmap.josm.Main; |
---|
32 | import org.openstreetmap.josm.gui.JMultilineLabel; |
---|
33 | import org.openstreetmap.josm.gui.SideButton; |
---|
34 | import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; |
---|
35 | import org.openstreetmap.josm.gui.help.HelpUtil; |
---|
36 | import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel; |
---|
37 | import org.openstreetmap.josm.gui.widgets.JosmPasswordField; |
---|
38 | import org.openstreetmap.josm.io.OsmApi; |
---|
39 | import org.openstreetmap.josm.tools.ImageProvider; |
---|
40 | import org.openstreetmap.josm.tools.Utils; |
---|
41 | import org.openstreetmap.josm.tools.WindowGeometry; |
---|
42 | |
---|
43 | public class CredentialDialog extends JDialog { |
---|
44 | |
---|
45 | static public CredentialDialog getOsmApiCredentialDialog(String username, String password, String host, String saveUsernameAndPasswordCheckboxText) { |
---|
46 | CredentialDialog dialog = new CredentialDialog(saveUsernameAndPasswordCheckboxText); |
---|
47 | if (Utils.equal(OsmApi.getOsmApi().getHost(), host)) { |
---|
48 | dialog.prepareForOsmApiCredentials(username, password); |
---|
49 | } else { |
---|
50 | dialog.prepareForOtherHostCredentials(username, password, host); |
---|
51 | } |
---|
52 | dialog.pack(); |
---|
53 | return dialog; |
---|
54 | } |
---|
55 | |
---|
56 | static public CredentialDialog getHttpProxyCredentialDialog(String username, String password, String host, String saveUsernameAndPasswordCheckboxText) { |
---|
57 | CredentialDialog dialog = new CredentialDialog(saveUsernameAndPasswordCheckboxText); |
---|
58 | dialog.prepareForProxyCredentials(username, password); |
---|
59 | dialog.pack(); |
---|
60 | return dialog; |
---|
61 | } |
---|
62 | |
---|
63 | private boolean canceled; |
---|
64 | protected CredentialPanel pnlCredentials; |
---|
65 | String saveUsernameAndPasswordCheckboxText; |
---|
66 | |
---|
67 | public boolean isCanceled() { |
---|
68 | return canceled; |
---|
69 | } |
---|
70 | |
---|
71 | protected void setCanceled(boolean canceled) { |
---|
72 | this.canceled = canceled; |
---|
73 | } |
---|
74 | |
---|
75 | @Override |
---|
76 | public void setVisible(boolean visible) { |
---|
77 | if (visible) { |
---|
78 | WindowGeometry.centerInWindow(Main.parent, new Dimension(350,300)).applySafe(this); |
---|
79 | } |
---|
80 | super.setVisible(visible); |
---|
81 | } |
---|
82 | |
---|
83 | protected JPanel createButtonPanel() { |
---|
84 | JPanel pnl = new JPanel(new FlowLayout()); |
---|
85 | pnl.add(new SideButton(new OKAction())); |
---|
86 | pnl.add(new SideButton(new CancelAction())); |
---|
87 | pnl.add(new SideButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/Password")))); |
---|
88 | return pnl; |
---|
89 | } |
---|
90 | |
---|
91 | protected void build() { |
---|
92 | getContentPane().setLayout(new BorderLayout()); |
---|
93 | getContentPane().add(createButtonPanel(), BorderLayout.SOUTH); |
---|
94 | |
---|
95 | addWindowListener(new WindowEventHander()); |
---|
96 | getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape"); |
---|
97 | getRootPane().getActionMap().put("escape", new CancelAction()); |
---|
98 | |
---|
99 | getRootPane().setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); |
---|
100 | } |
---|
101 | |
---|
102 | public CredentialDialog(String saveUsernameAndPasswordCheckboxText) { |
---|
103 | this.saveUsernameAndPasswordCheckboxText = saveUsernameAndPasswordCheckboxText; |
---|
104 | setModalityType(ModalityType.DOCUMENT_MODAL); |
---|
105 | try { |
---|
106 | setAlwaysOnTop(true); |
---|
107 | } catch(SecurityException e) { |
---|
108 | Main.warn(tr("Failed to put Credential Dialog always on top. Caught security exception.")); |
---|
109 | } |
---|
110 | build(); |
---|
111 | } |
---|
112 | |
---|
113 | public void prepareForOsmApiCredentials(String username, String password) { |
---|
114 | setTitle(tr("Enter credentials for OSM API")); |
---|
115 | getContentPane().add(pnlCredentials = new OsmApiCredentialsPanel(this), BorderLayout.CENTER); |
---|
116 | pnlCredentials.init(username, password); |
---|
117 | validate(); |
---|
118 | } |
---|
119 | |
---|
120 | public void prepareForOtherHostCredentials(String username, String password, String host) { |
---|
121 | setTitle(tr("Enter credentials for host")); |
---|
122 | getContentPane().add(pnlCredentials = new OtherHostCredentialsPanel(this, host), BorderLayout.CENTER); |
---|
123 | pnlCredentials.init(username, password); |
---|
124 | validate(); |
---|
125 | } |
---|
126 | |
---|
127 | public void prepareForProxyCredentials(String username, String password) { |
---|
128 | setTitle(tr("Enter credentials for HTTP proxy")); |
---|
129 | getContentPane().add(pnlCredentials = new HttpProxyCredentialsPanel(this), BorderLayout.CENTER); |
---|
130 | pnlCredentials.init(username, password); |
---|
131 | validate(); |
---|
132 | } |
---|
133 | |
---|
134 | public String getUsername() { |
---|
135 | if (pnlCredentials== null) return null; |
---|
136 | return pnlCredentials.getUserName(); |
---|
137 | } |
---|
138 | |
---|
139 | public char[] getPassword() { |
---|
140 | if (pnlCredentials== null) return null; |
---|
141 | return pnlCredentials.getPassword(); |
---|
142 | } |
---|
143 | |
---|
144 | public boolean isSaveCredentials() { |
---|
145 | if (pnlCredentials== null) return false; |
---|
146 | return pnlCredentials.isSaveCredentials(); |
---|
147 | } |
---|
148 | |
---|
149 | protected static class CredentialPanel extends JPanel { |
---|
150 | protected JTextField tfUserName; |
---|
151 | protected JosmPasswordField tfPassword; |
---|
152 | protected JCheckBox cbSaveCredentials; |
---|
153 | protected JMultilineLabel lblHeading; |
---|
154 | protected JMultilineLabel lblWarning; |
---|
155 | protected CredentialDialog owner; // owner Dependency Injection to use Key listeners for username and password text fields |
---|
156 | |
---|
157 | protected void build() { |
---|
158 | tfUserName = new JTextField(20); |
---|
159 | tfPassword = new JosmPasswordField(20); |
---|
160 | tfUserName.addFocusListener(new SelectAllOnFocusHandler()); |
---|
161 | tfPassword.addFocusListener(new SelectAllOnFocusHandler()); |
---|
162 | tfUserName.addKeyListener(new TFKeyListener(owner, tfUserName, tfPassword)); |
---|
163 | tfPassword.addKeyListener(new TFKeyListener(owner, tfPassword, tfUserName)); |
---|
164 | cbSaveCredentials = new JCheckBox(owner.saveUsernameAndPasswordCheckboxText); |
---|
165 | |
---|
166 | setLayout(new GridBagLayout()); |
---|
167 | GridBagConstraints gc = new GridBagConstraints(); |
---|
168 | gc.gridwidth = 2; |
---|
169 | gc.gridheight = 1; |
---|
170 | gc.fill = GridBagConstraints.HORIZONTAL; |
---|
171 | gc.weightx = 1.0; |
---|
172 | gc.weighty = 0.0; |
---|
173 | gc.insets = new Insets(0,0,10,0); |
---|
174 | add(lblHeading = new JMultilineLabel(""), gc); |
---|
175 | |
---|
176 | gc.gridx = 0; |
---|
177 | gc.gridy = 1; |
---|
178 | gc.gridwidth = 1; |
---|
179 | gc.gridheight = 1; |
---|
180 | gc.fill = GridBagConstraints.HORIZONTAL; |
---|
181 | gc.weightx = 0.0; |
---|
182 | gc.weighty = 0.0; |
---|
183 | gc.insets = new Insets(0,0,10,10); |
---|
184 | add(new JLabel(tr("Username")), gc); |
---|
185 | gc.gridx = 1; |
---|
186 | gc.gridy = 1; |
---|
187 | gc.weightx = 1.0; |
---|
188 | add(tfUserName, gc); |
---|
189 | gc.gridx = 0; |
---|
190 | gc.gridy = 2; |
---|
191 | gc.weightx = 0.0; |
---|
192 | add(new JLabel(tr("Password")), gc); |
---|
193 | |
---|
194 | gc.gridx = 1; |
---|
195 | gc.gridy = 2; |
---|
196 | gc.weightx = 0.0; |
---|
197 | add(tfPassword, gc); |
---|
198 | |
---|
199 | gc.gridx = 0; |
---|
200 | gc.gridy = 3; |
---|
201 | gc.gridwidth = 2; |
---|
202 | gc.gridheight = 1; |
---|
203 | gc.fill = GridBagConstraints.BOTH; |
---|
204 | gc.weightx = 1.0; |
---|
205 | gc.weighty = 0.0; |
---|
206 | lblWarning = new JMultilineLabel(""); |
---|
207 | lblWarning.setFont(lblWarning.getFont().deriveFont(Font.ITALIC)); |
---|
208 | add(lblWarning, gc); |
---|
209 | |
---|
210 | gc.gridx = 0; |
---|
211 | gc.gridy = 4; |
---|
212 | gc.weighty = 0.0; |
---|
213 | add(cbSaveCredentials, gc); |
---|
214 | |
---|
215 | // consume the remaining space |
---|
216 | gc.gridx = 0; |
---|
217 | gc.gridy = 5; |
---|
218 | gc.weighty = 1.0; |
---|
219 | add(new JPanel(),gc); |
---|
220 | |
---|
221 | } |
---|
222 | |
---|
223 | public CredentialPanel(CredentialDialog owner) { |
---|
224 | this.owner = owner; |
---|
225 | } |
---|
226 | |
---|
227 | public void init(String username, String password) { |
---|
228 | username = username == null ? "" : username; |
---|
229 | password = password == null ? "" : password; |
---|
230 | tfUserName.setText(username); |
---|
231 | tfPassword.setText(password); |
---|
232 | cbSaveCredentials.setSelected(!username.isEmpty() && !password.isEmpty()); |
---|
233 | } |
---|
234 | |
---|
235 | public void startUserInput() { |
---|
236 | tfUserName.requestFocusInWindow(); |
---|
237 | } |
---|
238 | |
---|
239 | public String getUserName() { |
---|
240 | return tfUserName.getText(); |
---|
241 | } |
---|
242 | |
---|
243 | public char[] getPassword() { |
---|
244 | return tfPassword.getPassword(); |
---|
245 | } |
---|
246 | |
---|
247 | public boolean isSaveCredentials() { |
---|
248 | return cbSaveCredentials.isSelected(); |
---|
249 | } |
---|
250 | } |
---|
251 | |
---|
252 | private static class OsmApiCredentialsPanel extends CredentialPanel { |
---|
253 | |
---|
254 | @Override |
---|
255 | protected void build() { |
---|
256 | super.build(); |
---|
257 | tfUserName.setToolTipText(tr("Please enter the user name of your OSM account")); |
---|
258 | tfPassword.setToolTipText(tr("Please enter the password of your OSM account")); |
---|
259 | lblHeading.setText( |
---|
260 | "<html>" + tr("Authenticating at the OSM API ''{0}'' failed. Please enter a valid username and a valid password.", |
---|
261 | OsmApi.getOsmApi().getBaseUrl()) + "</html>"); |
---|
262 | lblWarning.setText(tr("Warning: The password is transferred unencrypted.")); |
---|
263 | } |
---|
264 | |
---|
265 | public OsmApiCredentialsPanel(CredentialDialog owner) { |
---|
266 | super(owner); |
---|
267 | build(); |
---|
268 | } |
---|
269 | } |
---|
270 | |
---|
271 | private static class OtherHostCredentialsPanel extends CredentialPanel { |
---|
272 | |
---|
273 | String host; |
---|
274 | |
---|
275 | @Override |
---|
276 | protected void build() { |
---|
277 | super.build(); |
---|
278 | tfUserName.setToolTipText(tr("Please enter the user name of your account")); |
---|
279 | tfPassword.setToolTipText(tr("Please enter the password of your account")); |
---|
280 | lblHeading.setText( |
---|
281 | "<html>" + tr("Authenticating at the host ''{0}'' failed. Please enter a valid username and a valid password.", |
---|
282 | host) + "</html>"); |
---|
283 | lblWarning.setText(tr("Warning: The password is transferred unencrypted.")); |
---|
284 | } |
---|
285 | |
---|
286 | public OtherHostCredentialsPanel(CredentialDialog owner, String host) { |
---|
287 | super(owner); |
---|
288 | this.host = host; |
---|
289 | build(); |
---|
290 | } |
---|
291 | } |
---|
292 | |
---|
293 | private static class HttpProxyCredentialsPanel extends CredentialPanel { |
---|
294 | @Override |
---|
295 | protected void build() { |
---|
296 | super.build(); |
---|
297 | tfUserName.setToolTipText(tr("Please enter the user name for authenticating at your proxy server")); |
---|
298 | tfPassword.setToolTipText(tr("Please enter the password for authenticating at your proxy server")); |
---|
299 | lblHeading.setText( |
---|
300 | "<html>" + tr("Authenticating at the HTTP proxy ''{0}'' failed. Please enter a valid username and a valid password.", |
---|
301 | Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_HOST) + ":" + Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_PORT)) + "</html>"); |
---|
302 | lblWarning.setText("<html>" + tr("Warning: depending on the authentication method the proxy server uses the password may be transferred unencrypted.") + "</html>"); |
---|
303 | } |
---|
304 | |
---|
305 | public HttpProxyCredentialsPanel(CredentialDialog owner) { |
---|
306 | super(owner); |
---|
307 | build(); |
---|
308 | } |
---|
309 | } |
---|
310 | |
---|
311 | static private class SelectAllOnFocusHandler extends FocusAdapter { |
---|
312 | @Override |
---|
313 | public void focusGained(FocusEvent e) { |
---|
314 | if (e.getSource() instanceof JTextField) { |
---|
315 | JTextField tf = (JTextField)e.getSource(); |
---|
316 | tf.selectAll(); |
---|
317 | } |
---|
318 | } |
---|
319 | } |
---|
320 | |
---|
321 | /** |
---|
322 | * Listener for username and password text fields key events. |
---|
323 | * When user presses Enter: |
---|
324 | * If current text field is empty (or just contains a sequence of spaces), nothing happens (or all spaces become selected). |
---|
325 | * If current text field is not empty, but the next one is (or just contains a sequence of spaces), focuses the next text field. |
---|
326 | * If both text fields contain characters, submits the form by calling owner's {@link OKAction}. |
---|
327 | */ |
---|
328 | static private class TFKeyListener implements KeyListener{ |
---|
329 | protected CredentialDialog owner; // owner Dependency Injection to call OKAction |
---|
330 | protected JTextField currentTF; |
---|
331 | protected JTextField nextTF; |
---|
332 | |
---|
333 | public TFKeyListener (CredentialDialog owner, JTextField currentTF, JTextField nextTF) |
---|
334 | { |
---|
335 | this.owner = owner; |
---|
336 | this.currentTF = currentTF; |
---|
337 | this.nextTF = nextTF; |
---|
338 | } |
---|
339 | |
---|
340 | @Override |
---|
341 | public void keyPressed(KeyEvent e) { |
---|
342 | if(e.getKeyChar() == KeyEvent.VK_ENTER) { |
---|
343 | if (currentTF.getText().trim().isEmpty()) { |
---|
344 | currentTF.selectAll(); |
---|
345 | return; |
---|
346 | } else if (nextTF.getText().trim().isEmpty()) { |
---|
347 | nextTF.requestFocusInWindow(); |
---|
348 | nextTF.selectAll(); |
---|
349 | return; |
---|
350 | } else { |
---|
351 | OKAction okAction = owner.new OKAction(); |
---|
352 | okAction.actionPerformed(null); |
---|
353 | } |
---|
354 | } |
---|
355 | } |
---|
356 | |
---|
357 | @Override |
---|
358 | public void keyReleased ( KeyEvent e ){ |
---|
359 | } |
---|
360 | |
---|
361 | @Override |
---|
362 | public void keyTyped ( KeyEvent e ){ |
---|
363 | } |
---|
364 | } |
---|
365 | |
---|
366 | class OKAction extends AbstractAction { |
---|
367 | public OKAction() { |
---|
368 | putValue(NAME, tr("Authenticate")); |
---|
369 | putValue(SHORT_DESCRIPTION, tr("Authenticate with the supplied username and password")); |
---|
370 | putValue(SMALL_ICON, ImageProvider.get("ok")); |
---|
371 | } |
---|
372 | |
---|
373 | @Override |
---|
374 | public void actionPerformed(ActionEvent arg0) { |
---|
375 | setCanceled(false); |
---|
376 | setVisible(false); |
---|
377 | } |
---|
378 | } |
---|
379 | |
---|
380 | class CancelAction extends AbstractAction { |
---|
381 | public CancelAction() { |
---|
382 | putValue(NAME, tr("Cancel")); |
---|
383 | putValue(SHORT_DESCRIPTION, tr("Cancel authentication")); |
---|
384 | putValue(SMALL_ICON, ImageProvider.get("cancel")); |
---|
385 | } |
---|
386 | |
---|
387 | public void cancel() { |
---|
388 | setCanceled(true); |
---|
389 | setVisible(false); |
---|
390 | } |
---|
391 | |
---|
392 | @Override |
---|
393 | public void actionPerformed(ActionEvent arg0) { |
---|
394 | cancel(); |
---|
395 | } |
---|
396 | } |
---|
397 | |
---|
398 | class WindowEventHander extends WindowAdapter { |
---|
399 | |
---|
400 | @Override |
---|
401 | public void windowActivated(WindowEvent e) { |
---|
402 | if (pnlCredentials != null) { |
---|
403 | pnlCredentials.startUserInput(); |
---|
404 | } |
---|
405 | } |
---|
406 | |
---|
407 | @Override |
---|
408 | public void windowClosing(WindowEvent e) { |
---|
409 | new CancelAction().cancel(); |
---|
410 | } |
---|
411 | } |
---|
412 | } |
---|