OSDN Git Service

ec523ce0d99fbacf983d78a4f4e7cf4b6bc14d4e
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / ui / SearchPartsDialog.java
1 package charactermanaj.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Container;
5 import java.awt.GridBagConstraints;
6 import java.awt.GridBagLayout;
7 import java.awt.Insets;
8 import java.awt.Toolkit;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.KeyEvent;
12 import java.awt.event.MouseAdapter;
13 import java.awt.event.MouseEvent;
14 import java.awt.event.WindowAdapter;
15 import java.awt.event.WindowEvent;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Properties;
24 import java.util.WeakHashMap;
25
26 import javax.swing.AbstractAction;
27 import javax.swing.ActionMap;
28 import javax.swing.BorderFactory;
29 import javax.swing.Box;
30 import javax.swing.InputMap;
31 import javax.swing.JButton;
32 import javax.swing.JComboBox;
33 import javax.swing.JComponent;
34 import javax.swing.JDialog;
35 import javax.swing.JFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JPanel;
38 import javax.swing.JRootPane;
39 import javax.swing.JScrollPane;
40 import javax.swing.JTable;
41 import javax.swing.JTextField;
42 import javax.swing.KeyStroke;
43 import javax.swing.event.DocumentEvent;
44 import javax.swing.event.DocumentListener;
45 import javax.swing.table.TableColumnModel;
46
47 import charactermanaj.model.AppConfig;
48 import charactermanaj.model.PartsCategory;
49 import charactermanaj.model.PartsIdentifier;
50 import charactermanaj.model.PartsSpec;
51 import charactermanaj.model.PartsSpecResolver;
52 import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;
53 import charactermanaj.ui.model.PartsSelectionManager;
54 import charactermanaj.util.LocalizedResourcePropertyLoader;
55
56 public class SearchPartsDialog extends JDialog {
57
58         private static final long serialVersionUID = 1L;
59         
60         
61         private static final WeakHashMap<SearchPartsDialog, Object> ALL_DIALOGS
62                 = new WeakHashMap<SearchPartsDialog, Object>();
63         
64         
65         protected static final String STRINGS_RESOURCE = "languages/searchpartsdialog";
66
67         private PartsSpecResolver partsSpecResolver;
68         
69         private PartsSelectionManager partsSelectionManager;
70         
71         private JTable searchPartsTable;
72         
73         
74         private SearchPartsTableModel searchPartsTableModel;
75         
76         private JTextField txtPartsName;
77         
78         private JComboBox cmbAuthors;
79         
80         private JComboBox cmbCategories;
81         
82         
83         public static SearchPartsDialog[] getDialogs() {
84                 return ALL_DIALOGS.keySet().toArray(new SearchPartsDialog[ALL_DIALOGS.size()]);
85         }
86         
87         
88         public SearchPartsDialog(JFrame parent,
89                         PartsSpecResolver partsSpecResolver,
90                         PartsSelectionManager partsSelectionManager) {
91                 super(parent, false);
92                 
93                 if (partsSpecResolver == null) {
94                         throw new IllegalArgumentException();
95                 }
96
97                 this.partsSpecResolver = partsSpecResolver;
98                 this.partsSelectionManager = partsSelectionManager;
99                 
100                 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
101                 addWindowListener(new WindowAdapter() {
102                         @Override
103                         public void windowClosing(WindowEvent e) {
104                                 onClose();
105                         }
106                 });
107                 
108                 
109                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
110                                 .getLocalizedProperties(STRINGS_RESOURCE);
111
112                 setTitle(strings.getProperty("title"));
113                 
114                 // モデル
115                 searchPartsTableModel = new SearchPartsTableModel();
116                 
117                 
118                 // 検索条件パネル
119                 
120                 JPanel searchCondPanel = new JPanel();
121                 GridBagLayout searchCondPanelLayout = new GridBagLayout();
122                 searchCondPanel.setLayout(searchCondPanelLayout);
123                 GridBagConstraints gbc = new GridBagConstraints();
124                 
125                 searchCondPanel.setBorder(BorderFactory.createCompoundBorder(
126                                 BorderFactory.createEmptyBorder(5, 5, 5, 5), BorderFactory
127                                                 .createTitledBorder(strings.getProperty("search.condition"))));
128                 
129                 gbc.gridx = 0;
130                 gbc.gridy = 0;
131                 gbc.gridheight = 1;
132                 gbc.gridwidth = 1;
133                 gbc.anchor = GridBagConstraints.EAST;
134                 gbc.fill = GridBagConstraints.BOTH;
135                 gbc.ipadx = 0;
136                 gbc.ipady = 0;
137                 gbc.insets = new Insets(3, 3, 3, 3);
138                 gbc.weightx = 0.;
139                 gbc.weighty = 0.;
140                 searchCondPanel.add(new JLabel(strings.getProperty("partsname"), JLabel.RIGHT), gbc);
141                 
142                 gbc.gridx = 1;
143                 gbc.gridy = 0;
144                 gbc.weightx = 1.;
145                 txtPartsName = new JTextField();
146                 searchCondPanel.add(txtPartsName, gbc);
147                 
148                 gbc.gridx = 0;
149                 gbc.gridy = 1;
150                 gbc.gridwidth = 1;
151                 gbc.weightx = 0.;
152                 searchCondPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc);
153                 
154                 gbc.gridx = 1;
155                 gbc.gridy = 1;
156                 gbc.gridwidth = 2;
157                 gbc.weightx = 1.;
158                 ArrayList<String> authors = new ArrayList<String>();
159                 authors.add("");
160                 authors.addAll(getAuthors(partsSpecResolver));
161                 cmbAuthors = new JComboBox(authors.toArray(new String[authors.size()]));
162                 searchCondPanel.add(cmbAuthors, gbc);
163                 
164                 gbc.gridx = 0;
165                 gbc.gridy = 2;
166                 gbc.gridwidth = 1;
167                 gbc.weightx = 0.;
168                 searchCondPanel.add(new JLabel(strings.getProperty("partscategory"), JLabel.RIGHT), gbc);
169                 
170                 gbc.gridx = 1;
171                 gbc.gridy = 2;
172                 gbc.gridwidth = 2;
173                 gbc.weightx = 1.;
174                 ArrayList<PartsCategory> categories = new ArrayList<PartsCategory>();
175                 categories.add(null);
176                 categories.addAll(partsSpecResolver.getPartsCategories());
177                 cmbCategories = new JComboBox(categories.toArray(new PartsCategory[categories.size()]));
178                 searchCondPanel.add(cmbCategories, gbc);
179
180                 gbc.gridx = 2;
181                 gbc.gridy = 0;
182                 gbc.gridheight = 1;
183                 gbc.gridwidth = 1;
184                 gbc.weightx = 0.;
185                 gbc.weighty = 1.;
186                 gbc.anchor = GridBagConstraints.SOUTH;
187                 gbc.fill = GridBagConstraints.HORIZONTAL;
188                 JButton btnClear = new JButton(new AbstractAction(strings.getProperty("clear")) {
189                         private static final long serialVersionUID = 1L;
190                         public void actionPerformed(ActionEvent e) {
191                                 txtPartsName.setText("");
192                         }
193                 });
194                 searchCondPanel.add(btnClear, gbc);
195                 
196                 // 検索結果
197                 JPanel searchResult = new JPanel(new BorderLayout());
198                 searchResult.setBorder(BorderFactory.createCompoundBorder(
199                                 BorderFactory.createEmptyBorder(0, 5, 0, 5), BorderFactory
200                                                 .createTitledBorder(strings.getProperty("results"))));
201                 searchPartsTable = new JTable(searchPartsTableModel);
202                 searchPartsTable.setShowGrid(true);
203                 searchPartsTable.setGridColor(AppConfig.getInstance().getGridColor());
204                 JScrollPane searchPartsTableSP = new JScrollPane(searchPartsTable);
205                 searchPartsTableModel.adjustColumnModel(searchPartsTable.getColumnModel());
206                 searchResult.add(searchPartsTableSP, BorderLayout.CENTER);
207                 searchPartsTable.addMouseListener(new MouseAdapter() {
208                         @Override
209                         public void mouseClicked(MouseEvent e) {
210                                 if (e.getClickCount() == 2) {
211                                         // ダブルクリック
212                                         // 正確に2回
213                                         onSelect();
214                                 }
215                         }
216                 });
217                 
218                 // テーブルのキーイベント
219                 ActionMap tblAm = searchPartsTable.getActionMap();
220                 InputMap tblIm = searchPartsTable.getInputMap();
221                 tblIm.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "onSelect");
222                 tblAm.put("onSelect", new AbstractAction() {
223                         private static final long serialVersionUID = 1L;
224                         public void actionPerformed(ActionEvent e) {
225                                 onSelect();
226                         }
227                 });
228                 
229                 JPanel btnPanel = new JPanel();
230                 btnPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 10));
231                 GridBagLayout btnPanelLayout = new GridBagLayout();
232                 btnPanel.setLayout(btnPanelLayout);
233
234                 gbc.gridx = 0;
235                 gbc.gridy = 0;
236                 gbc.gridheight = 1;
237                 gbc.gridwidth = 1;
238                 gbc.anchor = GridBagConstraints.EAST;
239                 gbc.fill = GridBagConstraints.BOTH;
240                 gbc.ipadx = 0;
241                 gbc.ipady = 0;
242                 gbc.insets = new Insets(3, 3, 3, 3);
243                 gbc.weightx = 1.;
244                 gbc.weighty = 0.;
245                 btnPanel.add(Box.createHorizontalGlue(), gbc);
246
247                 gbc.gridx = 1;
248                 gbc.gridy = 0;
249                 gbc.weightx = 0.;
250                 JButton btnSelect = new JButton(new AbstractAction(strings.getProperty("select")) {
251                         private static final long serialVersionUID = 1L;
252                         public void actionPerformed(ActionEvent e) {
253                                 onSelect();
254                         }
255                 });
256                 btnPanel.add(btnSelect, gbc);
257                 searchResult.add(btnPanel, BorderLayout.SOUTH);
258                 
259                 
260                 // 検索条件の入力が変更されたことを検知するリスナの登録
261                 
262                 txtPartsName.getDocument().addDocumentListener(new DocumentListener() {
263                         public void removeUpdate(DocumentEvent e) {
264                                 onChangeCondition();
265                         }
266                         public void insertUpdate(DocumentEvent e) {
267                                 onChangeCondition();
268                         }
269                         public void changedUpdate(DocumentEvent e) {
270                                 onChangeCondition();
271                         }
272                 });
273                 
274                 ActionListener changeListener = new ActionListener() {
275                         public void actionPerformed(ActionEvent e) {
276                                 onChangeCondition();
277                         }
278                 };
279                 cmbAuthors.addActionListener(changeListener);
280                 cmbCategories.addActionListener(changeListener);
281                 
282                 // ESCキーとCTRL-Wで閉じる.
283                 Toolkit tk = Toolkit.getDefaultToolkit();
284                 JRootPane rootPane = getRootPane();
285                 InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
286                 ActionMap am = rootPane.getActionMap();
287                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeSearchDialog");
288                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeSearchDialog");
289                 am.put("closeSearchDialog", new AbstractAction() {
290                         private static final long serialVersionUID = 1L;
291                         public void actionPerformed(ActionEvent e) {
292                                 onClose();
293                         }
294                 });
295                 
296                 // ウィンドウがアクティブになったときに検索フィールドにフォーカスをあてる
297                 addWindowFocusListener(new WindowAdapter() {
298                     public void windowGainedFocus(WindowEvent e) {
299                         txtPartsName.requestFocusInWindow();
300                     }
301                 });
302
303                 // 画面の設定
304                 Container contentPane = getContentPane();
305                 contentPane.setLayout(new BorderLayout());
306                 contentPane.add(searchCondPanel, BorderLayout.NORTH);
307                 contentPane.add(searchResult, BorderLayout.CENTER);
308                 
309                 setSize(250, 300);
310                 setLocationRelativeTo(parent);
311                 
312                 // ダイアログの登録
313                 ALL_DIALOGS.put(this, null);
314         }
315         
316         public List<String> getAuthors(PartsSpecResolver partsSpecResolver) {
317                 if (partsSpecResolver == null) {
318                         throw new IllegalArgumentException();
319                 }
320                 HashSet<String> authorsSet = new HashSet<String>();
321                 for (PartsCategory category : partsSpecResolver.getPartsCategories()) {
322                         for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsSpecResolver.getPartsSpecMap(category).entrySet()) {
323                                 PartsSpec partsSpec = entry.getValue();
324                                 String author = partsSpec.getAuthor();
325                                 if (author != null) {
326                                         authorsSet.add(author);
327                                 }
328                         }
329                 }
330                 
331                 ArrayList<String> authors = new ArrayList<String>(authorsSet); 
332                 Collections.sort(authors);
333                 return authors;
334         }
335         
336         protected void onClose() {
337                 dispose();
338         }
339         
340         /**
341          * 「選択」ボタンまたはテーブルのダブルクリックのハンドラ.<br>
342          * 選択されている行のパーツ識別子をもとに、パーツにフォーカスをあてる.<br>
343          */
344         protected void onSelect() {
345                 int selRow = searchPartsTable.getSelectedRow();
346                 if (selRow >= 0) {
347                         Map.Entry<PartsIdentifier, PartsSpec> entry = searchPartsTableModel.getRow(selRow);
348                         PartsIdentifier partsIdentifier = entry.getKey();
349                         partsSelectionManager.setSelection(partsIdentifier);
350                 }
351         }
352         
353         protected void onChangeCondition() {
354                 String partsNamesRaw = txtPartsName.getText();
355                 partsNamesRaw = partsNamesRaw.replace(" ", " "); // 全角空白を半角に変換
356                 String[] condPartsNames = partsNamesRaw.split("\\s+");
357                 
358                 PartsCategory condPartsCategory = (PartsCategory) cmbCategories.getSelectedItem();
359                 String condAuthor = (String) cmbAuthors.getSelectedItem();
360                 if (condAuthor != null && condAuthor.trim().length() == 0) {
361                         condAuthor = null;
362                 }
363                 
364                 ArrayList<Map.Entry<PartsIdentifier, PartsSpec>> partsIdentifiers = new ArrayList<Map.Entry<PartsIdentifier, PartsSpec>>();
365                 
366                 for (PartsCategory partsCategory : partsSpecResolver.getPartsCategories()) {
367                         if (condPartsCategory != null && !condPartsCategory.equals(partsCategory)) {
368                                 continue;
369                         }
370                         for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsSpecResolver.getPartsSpecMap(partsCategory).entrySet()) {
371                                 PartsIdentifier partsIdentifier = entry.getKey();
372                                 PartsSpec partsSpec = entry.getValue();
373                                 if (condAuthor != null) {
374                                         String author = partsSpec.getAuthor();
375                                         if (author == null || !author.equals(condAuthor)) {
376                                                 continue;
377                                         }
378                                 }
379                                 String localizedPartsName = partsIdentifier.getLocalizedPartsName();
380                                 if (localizedPartsName != null) {
381                                         for (String condPartsName : condPartsNames) {
382                                                 if (localizedPartsName.indexOf(condPartsName) >= 0) {
383                                                         partsIdentifiers.add(entry);
384                                                         continue;
385                                                 }
386                                         }
387                                 }
388                         }
389                 }
390                 Collections.sort(partsIdentifiers, new Comparator<Map.Entry<PartsIdentifier, PartsSpec>>() {
391                         public int compare(Entry<PartsIdentifier, PartsSpec> o1,
392                                         Entry<PartsIdentifier, PartsSpec> o2) {
393                                 PartsIdentifier partsIdentifier1 = o1.getKey();
394                                 PartsIdentifier partsIdentifier2 = o2.getKey();
395                                 return partsIdentifier1.compareTo(partsIdentifier2);
396                         }
397                 });
398                 searchPartsTableModel.initModel(partsIdentifiers);
399         }
400         
401 }
402
403 class SearchPartsTableModel extends AbstractTableModelWithComboBoxModel<Map.Entry<PartsIdentifier, PartsSpec>> {
404
405         private static final long serialVersionUID = 1L;
406
407         private static final String[] COLUMN_NAMES;
408         
409         private static final int[] COLUMN_WIDTHS;
410         
411         static {
412                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
413                                 .getLocalizedProperties(SearchPartsDialog.STRINGS_RESOURCE);
414                 
415                 COLUMN_NAMES = new String[] {
416                                 strings.getProperty("column.partsname"),
417                                 strings.getProperty("column.category"),
418                                 strings.getProperty("column.author"),
419                 };
420                 COLUMN_WIDTHS = new int[] {
421                                 Integer.parseInt(strings.getProperty("column.partsname.width")),
422                                 Integer.parseInt(strings.getProperty("column.category.width")),
423                                 Integer.parseInt(strings.getProperty("column.author.width")),
424                 };
425         }
426         
427         public void adjustColumnModel(TableColumnModel columnModel) {
428                 for (int idx = 0; idx < COLUMN_WIDTHS.length; idx++) {
429                         columnModel.getColumn(idx).setPreferredWidth(COLUMN_WIDTHS[idx]);
430                 }
431         }
432         
433         public int getColumnCount() {
434                 return COLUMN_NAMES.length;
435         }
436         
437         @Override
438         public String getColumnName(int column) {
439                 return COLUMN_NAMES[column];
440         }
441         
442         public Object getValueAt(int rowIndex, int columnIndex) {
443                 Map.Entry<PartsIdentifier, PartsSpec> row = getRow(rowIndex);
444                 
445                 PartsIdentifier partsIdentifier = row.getKey();
446                 PartsSpec partsSpec = row.getValue();
447                 
448                 switch (columnIndex) {
449                 case 0:
450                         return partsIdentifier.getLocalizedPartsName();
451                 case 1:
452                         return partsIdentifier.getPartsCategory().getLocalizedCategoryName();
453                 case 2:
454                         return partsSpec.getAuthor();
455                 }
456                 return "";
457         }
458         
459         public void initModel(List<Map.Entry<PartsIdentifier, PartsSpec>> partsIdentifiers) {
460                 clear();
461                 if (partsIdentifiers != null) {
462                         for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsIdentifiers) {
463                                 addRow(entry);
464                         }
465                 }
466         }
467         
468 }
469