OSDN Git Service

750c9faaef5c711451ce901190d71798f2051356
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / AccountPanel.java
1 /*
2  * Account panel
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sourceforge.jindolf;
9
10 import java.awt.BorderLayout;
11 import java.awt.Container;
12 import java.awt.Frame;
13 import java.awt.GridBagConstraints;
14 import java.awt.GridBagLayout;
15 import java.awt.Insets;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.event.ItemEvent;
19 import java.awt.event.ItemListener;
20 import java.io.IOException;
21 import java.util.HashMap;
22 import java.util.Map;
23 import javax.swing.BorderFactory;
24 import javax.swing.JButton;
25 import javax.swing.JComboBox;
26 import javax.swing.JComponent;
27 import javax.swing.JDialog;
28 import javax.swing.JLabel;
29 import javax.swing.JOptionPane;
30 import javax.swing.JPanel;
31 import javax.swing.JPasswordField;
32 import javax.swing.JSeparator;
33 import javax.swing.JTextArea;
34 import javax.swing.JTextField;
35 import javax.swing.border.Border;
36 import jp.sourceforge.jindolf.corelib.LandState;
37
38 /**
39  * ログインパネル。
40  */
41 @SuppressWarnings("serial")
42 public class AccountPanel
43         extends JDialog
44         implements ActionListener, ItemListener{
45
46     private static final String FRAMETITLE =
47             "アカウント管理 - " + Jindolf.TITLE;
48
49     private final Map<Land, String> landUserIDMap =
50             new HashMap<Land, String>();
51     private final Map<Land, char[]> landPasswordMap =
52             new HashMap<Land, char[]>();
53
54     private final JComboBox landBox = new JComboBox();
55     private final JTextField idField = new JTextField(15);
56     private final JPasswordField pwField = new JPasswordField(15);
57     private final JButton loginButton = new JButton("ログイン");
58     private final JButton logoutButton = new JButton("ログアウト");
59     private final JButton closeButton = new JButton("閉じる");
60     private final JTextArea status = new JTextArea();
61
62     /**
63      * アカウントパネルを生成。
64      * @param owner フレームオーナー
65      * @param landsModel 国モデル
66      * @throws java.lang.NullPointerException 引数がnull
67      */
68     public AccountPanel(Frame owner, LandsModel landsModel)
69             throws NullPointerException{
70         super(owner, FRAMETITLE, true);
71
72         if(landsModel == null) throw new NullPointerException();
73         for(Land land : landsModel.getLandList()){
74             String userID = "";
75             char[] password = {};
76             this.landUserIDMap.put(land, userID);
77             this.landPasswordMap.put(land, password);
78             this.landBox.addItem(land);
79         }
80
81         GUIUtils.modifyWindowAttributes(this, true, false, true);
82
83         this.landBox.setToolTipText("アカウント管理する国を選ぶ");
84         this.idField.setToolTipText("IDを入力してください");
85         this.pwField.setToolTipText("パスワードを入力してください");
86
87         Monodizer.monodize(this.idField);
88         Monodizer.monodize(this.pwField);
89
90         this.idField.setMargin(new Insets(1, 4, 1, 4));
91         this.pwField.setMargin(new Insets(1, 4, 1, 4));
92
93         this.idField.setComponentPopupMenu(new TextPopup());
94
95         this.landBox.setEditable(false);
96         this.landBox.addItemListener(this);
97
98         this.status.setEditable(false);
99         this.status.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
100         this.status.setRows(2);
101         this.status.setLineWrap(true);
102
103         this.loginButton.addActionListener(this);
104         this.logoutButton.addActionListener(this);
105         this.closeButton.addActionListener(this);
106
107         getRootPane().setDefaultButton(this.loginButton);
108
109         Container content = getContentPane();
110         GridBagLayout layout = new GridBagLayout();
111         GridBagConstraints constraints = new GridBagConstraints();
112         content.setLayout(layout);
113
114         constraints.gridwidth = GridBagConstraints.REMAINDER;
115         constraints.weightx = 1.0;
116         constraints.insets = new Insets(5, 5, 5, 5);
117
118         JComponent accountPanel = createCredential();
119         JComponent buttonPanel = createButtonPanel();
120
121         constraints.weighty = 0.0;
122         constraints.fill = GridBagConstraints.HORIZONTAL;
123         content.add(accountPanel, constraints);
124
125         Border border = BorderFactory.createTitledBorder("ログインステータス");
126         JPanel panel = new JPanel();
127         panel.setLayout(new BorderLayout());
128         panel.add(this.status, BorderLayout.CENTER);
129         panel.setBorder(border);
130
131         constraints.weighty = 1.0;
132         constraints.fill = GridBagConstraints.BOTH;
133         content.add(panel, constraints);
134
135         constraints.weighty = 0.0;
136         constraints.fill = GridBagConstraints.HORIZONTAL;
137         content.add(new JSeparator(), constraints);
138
139         content.add(buttonPanel, constraints);
140
141         preSelectActiveLand();
142
143         updateGUI();
144
145         return;
146     }
147
148     /**
149      * 認証パネルを生成する。
150      * @return 認証パネル
151      */
152     private JComponent createCredential(){
153         JPanel credential = new JPanel();
154
155         GridBagLayout layout = new GridBagLayout();
156         GridBagConstraints constraints = new GridBagConstraints();
157
158         credential.setLayout(layout);
159
160         constraints.insets = new Insets(5, 5, 5, 5);
161         constraints.fill = GridBagConstraints.NONE;
162
163         constraints.anchor = GridBagConstraints.EAST;
164         credential.add(new JLabel("国名 :"), constraints);
165         constraints.anchor = GridBagConstraints.WEST;
166         credential.add(this.landBox, constraints);
167
168         constraints.gridy = 1;
169         constraints.anchor = GridBagConstraints.EAST;
170         constraints.weightx = 0.0;
171         constraints.fill = GridBagConstraints.NONE;
172         credential.add(new JLabel("ID :"), constraints);
173         constraints.anchor = GridBagConstraints.WEST;
174         constraints.weightx = 1.0;
175         constraints.fill = GridBagConstraints.HORIZONTAL;
176         credential.add(this.idField, constraints);
177
178         constraints.gridy = 2;
179         constraints.anchor = GridBagConstraints.EAST;
180         constraints.weightx = 0.0;
181         constraints.fill = GridBagConstraints.NONE;
182         credential.add(new JLabel("パスワード :"), constraints);
183         constraints.anchor = GridBagConstraints.WEST;
184         constraints.weightx = 1.0;
185         constraints.fill = GridBagConstraints.HORIZONTAL;
186         credential.add(this.pwField, constraints);
187
188         return credential;
189     }
190
191     /**
192      * ボタンパネルの作成。
193      * @return ボタンパネル
194      */
195     private JComponent createButtonPanel(){
196         JPanel buttonPanel = new JPanel();
197
198         GridBagLayout layout = new GridBagLayout();
199         GridBagConstraints constraints = new GridBagConstraints();
200
201         buttonPanel.setLayout(layout);
202
203         constraints.fill = GridBagConstraints.NONE;
204         constraints.anchor = GridBagConstraints.WEST;
205         constraints.weightx = 0.0;
206         constraints.weighty = 0.0;
207
208         buttonPanel.add(this.loginButton, constraints);
209
210         constraints.insets = new Insets(0, 5, 0, 0);
211         buttonPanel.add(this.logoutButton, constraints);
212
213         constraints.anchor = GridBagConstraints.EAST;
214         constraints.weightx = 1.0;
215         constraints.insets = new Insets(0, 15, 0, 0);
216         buttonPanel.add(this.closeButton, constraints);
217
218         return buttonPanel;
219     }
220
221     /**
222      * 現在コンボボックスで選択中の国を返す。
223      * @return 現在選択中のLand
224      */
225     private Land getSelectedLand(){
226         Land land = (Land)( this.landBox.getSelectedItem() );
227         return land;
228     }
229
230     /**
231      * ACTIVEな最初の国がコンボボックスで既に選択されている状態にする。
232      */
233     private void preSelectActiveLand(){
234         for(int index = 0; index < this.landBox.getItemCount(); index++){
235             Object item = this.landBox.getItemAt(index);
236             Land land = (Land) item;
237             LandState state = land.getLandDef().getLandState();
238             if(state == LandState.ACTIVE){
239                 this.landBox.setSelectedItem(land);
240                 return;
241             }
242         }
243         return;
244     }
245
246     /**
247      * 指定された国のユーザIDを返す。
248      * @param land 国
249      * @return ユーザID
250      */
251     private String getUserID(Land land){
252         return this.landUserIDMap.get(land);
253     }
254
255     /**
256      * 指定された国のパスワードを返す。
257      * @param land 国
258      * @return パスワード
259      */
260     private char[] getPassword(Land land){
261         return this.landPasswordMap.get(land);
262     }
263
264     /**
265      * ネットワークエラーを通知するモーダルダイアログを表示する。
266      * OKボタンを押すまでこのメソッドは戻ってこない。
267      * @param e ネットワークエラー
268      */
269     protected void showNetworkError(IOException e){
270         Jindolf.logger().warn(
271                 "アカウント処理中にネットワークのトラブルが発生しました", e);
272
273         Land land = getSelectedLand();
274         ServerAccess server = land.getServerAccess();
275         String message =
276                 land.getLandDef().getLandName()
277                 +"を運営するサーバとの間の通信で"
278                 +"何らかのトラブルが発生しました。\n"
279                 +"相手サーバのURLは [ " + server.getBaseURL() + " ] だよ。\n"
280                 +"Webブラウザでも遊べないか確認してみてね!\n";
281
282         JOptionPane pane = new JOptionPane(message,
283                                            JOptionPane.WARNING_MESSAGE,
284                                            JOptionPane.DEFAULT_OPTION );
285
286         JDialog dialog = pane.createDialog(this,
287                                            "通信異常発生 - " + Jindolf.TITLE);
288
289         dialog.pack();
290         dialog.setVisible(true);
291         dialog.dispose();
292
293         return;
294     }
295
296     /**
297      * アカウントエラーを通知するモーダルダイアログを表示する。
298      * OKボタンを押すまでこのメソッドは戻ってこない。
299      */
300     protected void showIllegalAccountDialog(){
301         Land land = getSelectedLand();
302         String message =
303                 land.getLandDef().getLandName()
304                 +"へのログインに失敗しました。\n"
305                 +"ユーザ名とパスワードは本当に正しいかな?\n"
306                 +"あなたは本当に [ " + getUserID(land) + " ] さんかな?\n"
307                 +"WebブラウザによるID登録手続きは本当に完了してるかな?\n"
308                 +"Webブラウザでもログインできないか試してみて!\n"
309                 +"…ユーザ名やパスワードにある種の特殊文字を使っている人は"
310                 +"問題があるかも。";
311
312         JOptionPane pane = new JOptionPane(message,
313                                            JOptionPane.WARNING_MESSAGE,
314                                            JOptionPane.DEFAULT_OPTION );
315
316         JDialog dialog =
317                 pane.createDialog(this, "ログイン認証失敗 - " + Jindolf.TITLE);
318
319         dialog.pack();
320         dialog.setVisible(true);
321         dialog.dispose();
322
323         return;
324     }
325
326     /**
327      * 入力されたアカウント情報を基に現在選択中の国へログインする。
328      * @return ログインに成功すればtrueを返す。
329      */
330     protected boolean login(){
331         Land land = getSelectedLand();
332         ServerAccess server = land.getServerAccess();
333
334         String id = this.idField.getText();
335         char[] password = this.pwField.getPassword();
336         this.landUserIDMap.put(land, id);
337         this.landPasswordMap.put(land, password);
338
339         boolean result = false;
340         try{
341             result = server.login(id, password);
342         }catch(IOException e){
343             showNetworkError(e);
344             return false;
345         }
346
347         if( ! result ){
348             showIllegalAccountDialog();
349         }
350
351         return result;
352     }
353
354     /**
355      * 現在選択中の国からログアウトする。
356      */
357     protected void logout(){
358         try{
359             logoutInternal();
360         }catch(IOException e){
361             showNetworkError(e);
362         }
363         return;
364     }
365
366     /**
367      * 現在選択中の国からログアウトする。
368      * @throws java.io.IOException ネットワークエラー
369      */
370     protected void logoutInternal() throws IOException{
371         Land land = getSelectedLand();
372         ServerAccess server = land.getServerAccess();
373         server.logout();
374         return;
375     }
376
377     /**
378      * 現在選択中の国のログイン状態に合わせてGUIを更新する。
379      */
380     private void updateGUI(){
381         Land land = getSelectedLand();
382         LandState state = land.getLandDef().getLandState();
383         ServerAccess server = land.getServerAccess();
384         boolean hasLoggedIn = server.hasLoggedIn();
385
386         if(state != LandState.ACTIVE){
387             this.status.setText(
388                      "この国は既に募集を停止しました。\n"
389                     +"ログインは無意味です" );
390             this.idField.setEnabled(false);
391             this.pwField.setEnabled(false);
392             this.loginButton.setEnabled(false);
393             this.logoutButton.setEnabled(false);
394         }else if(hasLoggedIn){
395             this.status.setText("ユーザ [ " + getUserID(land) + " ] として\n"
396                           +"現在ログイン中です");
397             this.idField.setEnabled(false);
398             this.pwField.setEnabled(false);
399             this.loginButton.setEnabled(false);
400             this.logoutButton.setEnabled(true);
401         }else{
402             this.status.setText("現在ログインしていません");
403             this.idField.setEnabled(true);
404             this.pwField.setEnabled(true);
405             this.loginButton.setEnabled(true);
406             this.logoutButton.setEnabled(false);
407         }
408
409         return;
410     }
411
412     /**
413      * {@inheritDoc}
414      * ボタン操作のリスナ。
415      * @param event イベント {@inheritDoc}
416      */
417     // TODO Return キー押下によるログインもサポートしたい
418     @Override
419     public void actionPerformed(ActionEvent event){
420         Object source = event.getSource();
421
422         if(source == this.closeButton){
423             setVisible(false);
424             dispose();
425             return;
426         }
427
428         if(source == this.loginButton){
429             login();
430         }else if(source == this.logoutButton){
431             logout();
432         }
433
434         updateGUI();
435
436         return;
437     }
438
439     /**
440      * {@inheritDoc}
441      * コンボボックス操作のリスナ。
442      * @param event イベント {@inheritDoc}
443      */
444     @Override
445     public void itemStateChanged(ItemEvent event){
446         Object source = event.getSource();
447         if(source != this.landBox) return;
448
449         Land land = (Land) event.getItem();
450         String id;
451         char[] password;
452
453         switch(event.getStateChange()){
454         case ItemEvent.SELECTED:
455             id = getUserID(land);
456             password = getPassword(land);
457             this.idField.setText(id);
458             this.pwField.setText(new String(password));
459             updateGUI();
460             break;
461         case ItemEvent.DESELECTED:
462             id = this.idField.getText();
463             password = this.pwField.getPassword();
464             this.landUserIDMap.put(land, id);
465             this.landPasswordMap.put(land, password);
466             break;
467         default:
468             assert false;
469             return;
470         }
471
472         return;
473     }
474
475     // TODO IDかパスワードが空の場合はログインボタンを無効にしたい
476 }