1 package charactermanaj.ui;
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;
22 import java.util.Map.Entry;
23 import java.util.Properties;
24 import java.util.WeakHashMap;
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;
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;
56 public class SearchPartsDialog extends JDialog {
58 private static final long serialVersionUID = 1L;
61 private static final WeakHashMap<SearchPartsDialog, Object> ALL_DIALOGS
62 = new WeakHashMap<SearchPartsDialog, Object>();
65 protected static final String STRINGS_RESOURCE = "languages/searchpartsdialog";
67 private PartsSpecResolver partsSpecResolver;
69 private PartsSelectionManager partsSelectionManager;
71 private JTable searchPartsTable;
74 private SearchPartsTableModel searchPartsTableModel;
76 private JTextField txtPartsName;
78 private JComboBox cmbAuthors;
80 private JComboBox cmbCategories;
83 public static SearchPartsDialog[] getDialogs() {
84 return ALL_DIALOGS.keySet().toArray(new SearchPartsDialog[ALL_DIALOGS.size()]);
88 public SearchPartsDialog(JFrame parent,
89 PartsSpecResolver partsSpecResolver,
90 PartsSelectionManager partsSelectionManager) {
93 if (partsSpecResolver == null) {
94 throw new IllegalArgumentException();
97 this.partsSpecResolver = partsSpecResolver;
98 this.partsSelectionManager = partsSelectionManager;
100 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
101 addWindowListener(new WindowAdapter() {
103 public void windowClosing(WindowEvent e) {
109 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
110 .getLocalizedProperties(STRINGS_RESOURCE);
112 setTitle(strings.getProperty("title"));
115 searchPartsTableModel = new SearchPartsTableModel();
120 JPanel searchCondPanel = new JPanel();
121 GridBagLayout searchCondPanelLayout = new GridBagLayout();
122 searchCondPanel.setLayout(searchCondPanelLayout);
123 GridBagConstraints gbc = new GridBagConstraints();
125 searchCondPanel.setBorder(BorderFactory.createCompoundBorder(
126 BorderFactory.createEmptyBorder(5, 5, 5, 5), BorderFactory
127 .createTitledBorder(strings.getProperty("search.condition"))));
133 gbc.anchor = GridBagConstraints.EAST;
134 gbc.fill = GridBagConstraints.BOTH;
137 gbc.insets = new Insets(3, 3, 3, 3);
140 searchCondPanel.add(new JLabel(strings.getProperty("partsname"), JLabel.RIGHT), gbc);
145 txtPartsName = new JTextField();
146 searchCondPanel.add(txtPartsName, gbc);
152 searchCondPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc);
158 ArrayList<String> authors = new ArrayList<String>();
160 authors.addAll(getAuthors(partsSpecResolver));
161 cmbAuthors = new JComboBox(authors.toArray(new String[authors.size()]));
162 searchCondPanel.add(cmbAuthors, gbc);
168 searchCondPanel.add(new JLabel(strings.getProperty("partscategory"), JLabel.RIGHT), gbc);
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);
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("");
194 searchCondPanel.add(btnClear, gbc);
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() {
209 public void mouseClicked(MouseEvent e) {
210 if (e.getClickCount() == 2) {
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) {
229 JPanel btnPanel = new JPanel();
230 btnPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 10));
231 GridBagLayout btnPanelLayout = new GridBagLayout();
232 btnPanel.setLayout(btnPanelLayout);
238 gbc.anchor = GridBagConstraints.EAST;
239 gbc.fill = GridBagConstraints.BOTH;
242 gbc.insets = new Insets(3, 3, 3, 3);
245 btnPanel.add(Box.createHorizontalGlue(), gbc);
250 JButton btnSelect = new JButton(new AbstractAction(strings.getProperty("select")) {
251 private static final long serialVersionUID = 1L;
252 public void actionPerformed(ActionEvent e) {
256 btnPanel.add(btnSelect, gbc);
257 searchResult.add(btnPanel, BorderLayout.SOUTH);
260 // 検索条件の入力が変更されたことを検知するリスナの登録
262 txtPartsName.getDocument().addDocumentListener(new DocumentListener() {
263 public void removeUpdate(DocumentEvent e) {
266 public void insertUpdate(DocumentEvent e) {
269 public void changedUpdate(DocumentEvent e) {
274 ActionListener changeListener = new ActionListener() {
275 public void actionPerformed(ActionEvent e) {
279 cmbAuthors.addActionListener(changeListener);
280 cmbCategories.addActionListener(changeListener);
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) {
296 // ウィンドウがアクティブになったときに検索フィールドにフォーカスをあてる
297 addWindowFocusListener(new WindowAdapter() {
298 public void windowGainedFocus(WindowEvent e) {
299 txtPartsName.requestFocusInWindow();
304 Container contentPane = getContentPane();
305 contentPane.setLayout(new BorderLayout());
306 contentPane.add(searchCondPanel, BorderLayout.NORTH);
307 contentPane.add(searchResult, BorderLayout.CENTER);
310 setLocationRelativeTo(parent);
313 ALL_DIALOGS.put(this, null);
316 public List<String> getAuthors(PartsSpecResolver partsSpecResolver) {
317 if (partsSpecResolver == null) {
318 throw new IllegalArgumentException();
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);
331 ArrayList<String> authors = new ArrayList<String>(authorsSet);
332 Collections.sort(authors);
336 protected void onClose() {
341 * 「選択」ボタンまたはテーブルのダブルクリックのハンドラ.<br>
342 * 選択されている行のパーツ識別子をもとに、パーツにフォーカスをあてる.<br>
344 protected void onSelect() {
345 int selRow = searchPartsTable.getSelectedRow();
347 Map.Entry<PartsIdentifier, PartsSpec> entry = searchPartsTableModel.getRow(selRow);
348 PartsIdentifier partsIdentifier = entry.getKey();
349 partsSelectionManager.setSelection(partsIdentifier);
353 protected void onChangeCondition() {
354 String partsNamesRaw = txtPartsName.getText();
355 partsNamesRaw = partsNamesRaw.replace(" ", " "); // 全角空白を半角に変換
356 String[] condPartsNames = partsNamesRaw.split("\\s+");
358 PartsCategory condPartsCategory = (PartsCategory) cmbCategories.getSelectedItem();
359 String condAuthor = (String) cmbAuthors.getSelectedItem();
360 if (condAuthor != null && condAuthor.trim().length() == 0) {
364 ArrayList<Map.Entry<PartsIdentifier, PartsSpec>> partsIdentifiers = new ArrayList<Map.Entry<PartsIdentifier, PartsSpec>>();
366 for (PartsCategory partsCategory : partsSpecResolver.getPartsCategories()) {
367 if (condPartsCategory != null && !condPartsCategory.equals(partsCategory)) {
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)) {
379 String localizedPartsName = partsIdentifier.getLocalizedPartsName();
380 if (localizedPartsName != null) {
381 for (String condPartsName : condPartsNames) {
382 if (localizedPartsName.indexOf(condPartsName) >= 0) {
383 partsIdentifiers.add(entry);
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);
398 searchPartsTableModel.initModel(partsIdentifiers);
403 class SearchPartsTableModel extends AbstractTableModelWithComboBoxModel<Map.Entry<PartsIdentifier, PartsSpec>> {
405 private static final long serialVersionUID = 1L;
407 private static final String[] COLUMN_NAMES;
409 private static final int[] COLUMN_WIDTHS;
412 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
413 .getLocalizedProperties(SearchPartsDialog.STRINGS_RESOURCE);
415 COLUMN_NAMES = new String[] {
416 strings.getProperty("column.partsname"),
417 strings.getProperty("column.category"),
418 strings.getProperty("column.author"),
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")),
427 public void adjustColumnModel(TableColumnModel columnModel) {
428 for (int idx = 0; idx < COLUMN_WIDTHS.length; idx++) {
429 columnModel.getColumn(idx).setPreferredWidth(COLUMN_WIDTHS[idx]);
433 public int getColumnCount() {
434 return COLUMN_NAMES.length;
438 public String getColumnName(int column) {
439 return COLUMN_NAMES[column];
442 public Object getValueAt(int rowIndex, int columnIndex) {
443 Map.Entry<PartsIdentifier, PartsSpec> row = getRow(rowIndex);
445 PartsIdentifier partsIdentifier = row.getKey();
446 PartsSpec partsSpec = row.getValue();
448 switch (columnIndex) {
450 return partsIdentifier.getLocalizedPartsName();
452 return partsIdentifier.getPartsCategory().getLocalizedCategoryName();
454 return partsSpec.getAuthor();
459 public void initModel(List<Map.Entry<PartsIdentifier, PartsSpec>> partsIdentifiers) {
461 if (partsIdentifiers != null) {
462 for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsIdentifiers) {