OSDN Git Service

dmg作成バージョンの取得方法の修正
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / ui / PartsRandomChooserDialog.java
1 package charactermanaj.ui;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Component;\r
5 import java.awt.Container;\r
6 import java.awt.GridBagConstraints;\r
7 import java.awt.GridBagLayout;\r
8 import java.awt.Toolkit;\r
9 import java.awt.event.ActionEvent;\r
10 import java.awt.event.ActionListener;\r
11 import java.awt.event.KeyEvent;\r
12 import java.awt.event.WindowAdapter;\r
13 import java.awt.event.WindowEvent;\r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.Collections;\r
17 import java.util.HashMap;\r
18 import java.util.LinkedList;\r
19 import java.util.List;\r
20 import java.util.Map;\r
21 import java.util.Properties;\r
22 import java.util.Random;\r
23 import java.util.concurrent.atomic.AtomicInteger;\r
24 \r
25 import javax.swing.AbstractAction;\r
26 import javax.swing.Action;\r
27 import javax.swing.ActionMap;\r
28 import javax.swing.BorderFactory;\r
29 import javax.swing.Box;\r
30 import javax.swing.InputMap;\r
31 import javax.swing.JButton;\r
32 import javax.swing.JCheckBox;\r
33 import javax.swing.JComboBox;\r
34 import javax.swing.JComponent;\r
35 import javax.swing.JDialog;\r
36 import javax.swing.JFrame;\r
37 import javax.swing.JPanel;\r
38 import javax.swing.JRootPane;\r
39 import javax.swing.JScrollBar;\r
40 import javax.swing.JScrollPane;\r
41 import javax.swing.JToggleButton;\r
42 import javax.swing.KeyStroke;\r
43 import javax.swing.event.EventListenerList;\r
44 \r
45 import charactermanaj.model.AppConfig;\r
46 import charactermanaj.model.CharacterData;\r
47 import charactermanaj.model.PartsCategory;\r
48 import charactermanaj.model.PartsIdentifier;\r
49 import charactermanaj.model.PartsSet;\r
50 import charactermanaj.util.LocalizedResourcePropertyLoader;\r
51 \r
52 /**\r
53  * パーツのランダム選択ダイアログ.<br>\r
54  *\r
55  * @author seraphy\r
56  */\r
57 public class PartsRandomChooserDialog extends JDialog {\r
58 \r
59         private static final long serialVersionUID = -8427874726724107481L;\r
60 \r
61         protected static final String STRINGS_RESOURCE = "languages/partsrandomchooserdialog";\r
62 \r
63         /**\r
64          * メインフレームとの間でパーツの選択状態の取得・設定を行うためのインターフェイス.<br>\r
65          */\r
66         public interface PartsSetSynchronizer {\r
67 \r
68                 /**\r
69                  * 現在フレームで設定されているパーツセットを取得する.\r
70                  *\r
71                  * @return\r
72                  */\r
73                 PartsSet getCurrentPartsSet();\r
74 \r
75                 /**\r
76                  * ランダム選択パネルのパーツセットでフレームを設定する.\r
77                  *\r
78                  * @param partsSet\r
79                  */\r
80                 void setPartsSet(PartsSet partsSet);\r
81 \r
82                 /**\r
83                  * 指定されたパーツがランダム選択対象外であるか?\r
84                  *\r
85                  * @param partsIdentifier\r
86                  *            パーツ\r
87                  * @return 対象外であればtrue\r
88                  */\r
89                 boolean isExcludePartsIdentifier(PartsIdentifier partsIdentifier);\r
90 \r
91                 /**\r
92                  * 指定したパーツがランダム選択対象外であるか設定する.\r
93                  *\r
94                  * @param partsIdentifier\r
95                  *            パーツ\r
96                  * @param exclude\r
97                  *            対象外であればtrue\r
98                  */\r
99                 void setExcludePartsIdentifier(PartsIdentifier partsIdentifier,\r
100                                 boolean exclude);\r
101         }\r
102 \r
103         /**\r
104          * ランダム選択パネルを縦に並べるボックス\r
105          */\r
106         private Box centerPnl;\r
107 \r
108         /**\r
109          * キャラクターデータ\r
110          */\r
111         private CharacterData characterData;\r
112 \r
113         /**\r
114          * メインフレームとの同期用\r
115          */\r
116         private PartsSetSynchronizer partsSync;\r
117 \r
118         /**\r
119          * 一括ランダムアクション\r
120          */\r
121         private Action actRandomAll;\r
122 \r
123         /**\r
124          * 選択を戻すアクション\r
125          */\r
126         private Action actBack;\r
127 \r
128         /**\r
129          * 閉じるアクション\r
130          */\r
131         private Action actCancel;\r
132 \r
133         /**\r
134          * 履歴\r
135          */\r
136         private LinkedList<Map<RandomChooserPanel, PartsIdentifier>> history = new LinkedList<Map<RandomChooserPanel, PartsIdentifier>>();\r
137 \r
138         /**\r
139          * 最大の履歴保持数\r
140          */\r
141         private int maxHistory;\r
142 \r
143         /**\r
144          * コンストラクタ\r
145          *\r
146          * @param parent\r
147          *            メインフレーム(親)\r
148          * @param characterData\r
149          *            キャラクターデータ\r
150          * @param partsSync\r
151          *            メインフレームとの同期用\r
152          */\r
153         public PartsRandomChooserDialog(JFrame parent, CharacterData characterData,\r
154                         PartsSetSynchronizer partsSync) {\r
155                 super(parent, false);\r
156                 try {\r
157                         if (characterData == null || partsSync == null) {\r
158                                 throw new IllegalArgumentException();\r
159                         }\r
160 \r
161                         setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);\r
162                         addWindowListener(new WindowAdapter() {\r
163                                 @Override\r
164                                 public void windowClosing(WindowEvent e) {\r
165                                         onClose();\r
166                                 }\r
167                         });\r
168 \r
169                         this.characterData = characterData;\r
170                         this.partsSync = partsSync;\r
171 \r
172                         AppConfig appConfig = AppConfig.getInstance();\r
173                         this.maxHistory = appConfig.getRandomChooserMaxHistory();\r
174                         if (this.maxHistory < 0) {\r
175                                 this.maxHistory = 0;\r
176                         }\r
177 \r
178                         initLayout();\r
179 \r
180                         pack();\r
181                         setLocationRelativeTo(parent);\r
182 \r
183                 } catch (RuntimeException ex) {\r
184                         dispose();\r
185                         throw ex;\r
186                 }\r
187         }\r
188 \r
189         /**\r
190          * レイアウトを行う.\r
191          */\r
192         private void initLayout() {\r
193                 Properties strings = LocalizedResourcePropertyLoader\r
194                                 .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
195 \r
196                 setTitle(strings.getProperty("partsRandomChooser"));\r
197 \r
198                 Container contentPane = getContentPane();\r
199                 contentPane.setLayout(new BorderLayout());\r
200 \r
201                 this.centerPnl = Box.createVerticalBox();\r
202 \r
203                 ActionListener changePartsIdentifierListener = new ActionListener() {\r
204                         public void actionPerformed(ActionEvent e) {\r
205                                 if (eventLock.get() == 0) {\r
206                                         onChangePartsIdentifiers();\r
207                                 }\r
208                         }\r
209                 };\r
210 \r
211                 PartsSet partsSet = partsSync.getCurrentPartsSet();\r
212                 eventLock.incrementAndGet();\r
213                 try {\r
214                         for (PartsCategory category : characterData.getPartsCategories()) {\r
215                                 List<PartsIdentifier> partsIdentifiers = partsSet.get(category);\r
216                                 int partsLen = (partsIdentifiers != null) ? partsIdentifiers\r
217                                                 .size() : 0;\r
218                                 boolean enable = true;\r
219                                 if (partsLen < 1) {\r
220                                         partsLen = 1; // 未選択の場合でも1つは作成する.\r
221                                         enable = false; // 未選択の場合はディセーブルとする.\r
222                                 }\r
223 \r
224                                 for (int partsIdx = 0; partsIdx < partsLen; partsIdx++) {\r
225                                         PartsIdentifier partsIdentifier = null;\r
226                                         if (partsIdentifiers != null\r
227                                                         && partsIdx < partsIdentifiers.size()) {\r
228                                                 partsIdentifier = partsIdentifiers.get(partsIdx);\r
229                                         }\r
230                                         boolean lastInCategory = (partsIdx == partsLen - 1);\r
231 \r
232                                         int idx = centerPnl.getComponentCount();\r
233                                         RandomChooserPanel pnl = addPartsChooserPanel(centerPnl,\r
234                                                         idx, category, lastInCategory,\r
235                                                         changePartsIdentifierListener);\r
236 \r
237                                         // 未選択の場合、もしくは複数選択カテゴリの場合はランダムはディセーブルとする\r
238                                         pnl.setEnableRandom(enable\r
239                                                         && !category.isMultipleSelectable());\r
240 \r
241                                         if (partsIdentifier != null) {\r
242                                                 pnl.setSelectedPartsIdentifier(partsIdentifier);\r
243                                         }\r
244                                 }\r
245                         }\r
246 \r
247                 } finally {\r
248                         eventLock.decrementAndGet();\r
249                 }\r
250 \r
251                 JScrollPane scr = new JScrollPane(centerPnl) {\r
252                         private static final long serialVersionUID = 1L;\r
253 \r
254                         @Override\r
255                         public JScrollBar createVerticalScrollBar() {\r
256                                 JScrollBar sb = super.createVerticalScrollBar();\r
257                                 sb.setUnitIncrement(12);\r
258                                 return sb;\r
259                         }\r
260                 };\r
261                 scr.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\r
262                 contentPane.add(scr, BorderLayout.CENTER);\r
263 \r
264                 this.actRandomAll = new AbstractAction(strings.getProperty("randomAll")) {\r
265                         private static final long serialVersionUID = 1L;\r
266 \r
267                         public void actionPerformed(ActionEvent e) {\r
268                                 onRandomAll();\r
269                         }\r
270                 };\r
271 \r
272                 this.actBack = new AbstractAction(strings.getProperty("back")) {\r
273                         private static final long serialVersionUID = 1L;\r
274 \r
275                         public void actionPerformed(ActionEvent e) {\r
276                                 onBack();\r
277                         }\r
278                 };\r
279 \r
280                 this.actCancel = new AbstractAction(strings.getProperty("close")) {\r
281                         private static final long serialVersionUID = 1L;\r
282 \r
283                         public void actionPerformed(ActionEvent e) {\r
284                                 onClose();\r
285                         }\r
286                 };\r
287 \r
288                 JButton btnClose = new JButton(actCancel);\r
289                 JButton btnRandomAll = new JButton(actRandomAll);\r
290                 JButton btnBack = new JButton(actBack);\r
291 \r
292                 Box btnPanel = Box.createHorizontalBox();\r
293                 btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 42));\r
294 \r
295                 btnPanel.add(btnRandomAll);\r
296                 btnPanel.add(btnBack);\r
297                 btnPanel.add(Box.createHorizontalGlue());\r
298                 btnPanel.add(btnClose);\r
299 \r
300                 contentPane.add(btnPanel, BorderLayout.SOUTH);\r
301 \r
302                 JRootPane rootPane = getRootPane();\r
303                 rootPane.setDefaultButton(btnRandomAll);\r
304 \r
305                 Toolkit tk = Toolkit.getDefaultToolkit();\r
306                 InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\r
307                 ActionMap am = rootPane.getActionMap();\r
308                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeDialog");\r
309                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W,\r
310                                 tk.getMenuShortcutKeyMask()), "closeDialog");\r
311                 am.put("closeDialog", actCancel);\r
312 \r
313                 addHistory(getSelection());\r
314                 updateUIState();\r
315         }\r
316 \r
317         /**\r
318          * ボタンの状態を設定する.\r
319          */\r
320         protected void updateUIState() {\r
321                 actBack.setEnabled(history.size() > 1);\r
322         }\r
323 \r
324         /**\r
325          * ダイアログを破棄して閉じる.\r
326          */\r
327         protected void onClose() {\r
328                 dispose();\r
329         }\r
330 \r
331         /**\r
332          * パネル構築時、および一括ランダム選択時などでパーツのコンボボックスの選択が複数変更される場合に\r
333          * イベントを一度だけ処理するようにグループ化するためのロック.\r
334          */\r
335         private final AtomicInteger eventLock = new AtomicInteger(0);\r
336 \r
337         /**\r
338          * センターパネル上に配置したランダム選択パネルのリストを取得する.<br>\r
339          * (ランダム選択パネルの個数は実行時に自由に可変できるため.)\r
340          *\r
341          * @return ランダム選択パネルのリスト\r
342          */\r
343         protected List<RandomChooserPanel> getRandomChooserPanels() {\r
344                 ArrayList<RandomChooserPanel> panels = new ArrayList<RandomChooserPanel>();\r
345                 int mx = centerPnl.getComponentCount();\r
346                 for (int idx = 0; idx < mx; idx++) {\r
347                         Component comp = centerPnl.getComponent(idx);\r
348                         if (comp instanceof RandomChooserPanel) {\r
349                                 RandomChooserPanel pnl = (RandomChooserPanel) comp;\r
350                                 panels.add(pnl);\r
351                         }\r
352                 }\r
353                 return panels;\r
354         }\r
355 \r
356         /**\r
357          * 現在選択中の状態を取得する.\r
358          *\r
359          * @return\r
360          */\r
361         protected Map<RandomChooserPanel, PartsIdentifier> getSelection() {\r
362                 HashMap<RandomChooserPanel, PartsIdentifier> selection = new HashMap<RandomChooserPanel, PartsIdentifier>();\r
363 \r
364                 for (RandomChooserPanel pnl : getRandomChooserPanels()) {\r
365                         PartsIdentifier partsIdentifier = pnl.getSelectedPartsIdentifier();\r
366                         selection.put(pnl, partsIdentifier);\r
367                 }\r
368 \r
369                 return selection;\r
370         }\r
371 \r
372         /**\r
373          * 履歴に追加する.\r
374          *\r
375          * @param selection\r
376          */\r
377         protected void\r
378                         addHistory(Map<RandomChooserPanel, PartsIdentifier> selection) {\r
379                 if (selection == null || selection.isEmpty()) {\r
380                         return;\r
381                 }\r
382 \r
383                 // 履歴に追加する.\r
384                 history.addLast(selection);\r
385 \r
386                 // 最大数を越えた場合は除去する\r
387                 while (history.size() > maxHistory) {\r
388                         history.removeFirst();\r
389                 }\r
390                 updateUIState();\r
391         }\r
392 \r
393         /**\r
394          * 前回の選択状態に戻す\r
395          */\r
396         protected void onBack() {\r
397                 if (history.size() <= 1) {\r
398                         return;\r
399                 }\r
400 \r
401                 // ヒストリーの直前のものを取り出す\r
402                 // 先頭のものは現在表示中のものなので、2つ取り出す必要がある.\r
403                 history.removeLast();\r
404                 Map<RandomChooserPanel, PartsIdentifier> selection = history.getLast();\r
405 \r
406                 // すべてのランダム選択パネルに再適用する.\r
407                 eventLock.incrementAndGet();\r
408                 try {\r
409                         for (Map.Entry<RandomChooserPanel, PartsIdentifier> entry : selection\r
410                                         .entrySet()) {\r
411                                 RandomChooserPanel pnl = entry.getKey();\r
412                                 PartsIdentifier partsIdentifier = entry.getValue();\r
413                                 pnl.setSelectedPartsIdentifier(partsIdentifier);\r
414                         }\r
415 \r
416                         PartsSet partsSet = makePartsSet(selection.values());\r
417                         if (!partsSet.isEmpty()) {\r
418                                 partsSync.setPartsSet(partsSet);\r
419                         }\r
420 \r
421                 } finally {\r
422                         eventLock.decrementAndGet();\r
423                 }\r
424 \r
425                 updateUIState();\r
426         }\r
427 \r
428         /**\r
429          * 一括ランダム選択\r
430          */\r
431         protected void onRandomAll() {\r
432                 eventLock.incrementAndGet();\r
433                 try {\r
434                         for (RandomChooserPanel pnl : getRandomChooserPanels()) {\r
435                                 if (pnl.isEnableRandom()) {\r
436                                         // ランダム選択を有効としているものだけを対象とする.\r
437                                         pnl.selectRandom();\r
438                                 }\r
439                         }\r
440                         onChangePartsIdentifiers();\r
441 \r
442                 } finally {\r
443                         eventLock.decrementAndGet();\r
444                 }\r
445         }\r
446 \r
447         /**\r
448          * パーツの選択からパーツセットを生成して返す.\r
449          *\r
450          * @param selection\r
451          * @return\r
452          */\r
453         protected PartsSet makePartsSet(Collection<PartsIdentifier> selection) {\r
454                 PartsSet partsSet = new PartsSet();\r
455                 for (PartsIdentifier partsIdentifier : selection) {\r
456                         if (partsIdentifier != null) {\r
457                                 PartsCategory category = partsIdentifier.getPartsCategory();\r
458                                 partsSet.appendParts(category, partsIdentifier, null); // 色は不問とする\r
459                         }\r
460                 }\r
461                 return partsSet;\r
462         }\r
463 \r
464         /**\r
465          * パーツの選択が変更されたことを通知される.<br>\r
466          * 現在のランダム選択状態を、プレビューの状態に反映させる.<brr>\r
467          */\r
468         protected void onChangePartsIdentifiers() {\r
469 \r
470                 Map<RandomChooserPanel, PartsIdentifier> selection = getSelection();\r
471 \r
472                 PartsSet partsSet = makePartsSet(selection.values());\r
473                 if (!partsSet.isEmpty()) {\r
474                         partsSync.setPartsSet(partsSet);\r
475                         addHistory(selection);\r
476                 }\r
477         }\r
478 \r
479         /**\r
480          * アイテムごとのランダム選択パネル\r
481          *\r
482          * @author seraphy\r
483          */\r
484         protected class RandomChooserPanel extends JPanel {\r
485                 private static final long serialVersionUID = 1L;\r
486 \r
487                 private EventListenerList listeners = new EventListenerList();\r
488 \r
489                 private JCheckBox label;\r
490 \r
491                 private JComboBox partsCombo;\r
492 \r
493                 private JToggleButton btnReject;\r
494 \r
495                 public RandomChooserPanel(final PartsCategory category,\r
496                                 final boolean lastInCategory) {\r
497                         Properties strings = LocalizedResourcePropertyLoader\r
498                                         .getCachedInstance().getLocalizedProperties(\r
499                                                         STRINGS_RESOURCE);\r
500 \r
501                         setBorder(BorderFactory.createCompoundBorder(\r
502                                         BorderFactory.createEmptyBorder(3, 3, 3, 3),\r
503                                         BorderFactory.createCompoundBorder(\r
504                                                         BorderFactory.createEtchedBorder(),\r
505                                                         BorderFactory.createEmptyBorder(3, 3, 3, 3))));\r
506                         setLayout(new GridBagLayout());\r
507 \r
508                         GridBagConstraints gbc = new GridBagConstraints();\r
509                         gbc.gridx = 0;\r
510                         gbc.gridy = 0;\r
511                         gbc.gridheight = 1;\r
512                         gbc.gridwidth = 1;\r
513                         gbc.anchor = GridBagConstraints.EAST;\r
514                         gbc.fill = GridBagConstraints.BOTH;\r
515                         gbc.weightx = 1.;\r
516                         gbc.weighty = 0.;\r
517 \r
518                         String categoryName = category.getLocalizedCategoryName();\r
519                         this.label = new JCheckBox(categoryName, true);\r
520                         add(label, gbc);\r
521 \r
522                         JButton btnRandom = new JButton(new AbstractAction(\r
523                                         strings.getProperty("random")) {\r
524                                 private static final long serialVersionUID = -1;\r
525 \r
526                                 public void actionPerformed(ActionEvent e) {\r
527                                         onClickRandom(e);\r
528                                 }\r
529                         });\r
530                         gbc.gridx = 1;\r
531                         gbc.weightx = 0;\r
532                         add(btnRandom, gbc);\r
533 \r
534                         ArrayList<PartsIdentifier> partsList = new ArrayList<PartsIdentifier>();\r
535                         partsList.addAll(characterData.getPartsSpecMap(category).keySet());\r
536                         Collections.sort(partsList);\r
537                         if (category.isMultipleSelectable()) {\r
538                                 // 複数選択カテゴリは未選択状態が可能なため先頭に空行を入れる.\r
539                                 partsList.add(0, null);\r
540                         }\r
541 \r
542                         this.partsCombo = new JComboBox(\r
543                                         partsList.toArray(new PartsIdentifier[partsList.size()]));\r
544 \r
545                         partsCombo.addActionListener(new ActionListener() {\r
546                                 public void actionPerformed(ActionEvent e) {\r
547                                         onSelectChangePartsIdentifier(e);\r
548                                 }\r
549                         });\r
550 \r
551                         gbc.gridx = 0;\r
552                         gbc.gridy = 1;\r
553                         gbc.weightx = 1.;\r
554                         add(partsCombo, gbc);\r
555 \r
556                         this.btnReject = new JToggleButton(new AbstractAction(\r
557                                         strings.getProperty("reject")) {\r
558                                 private static final long serialVersionUID = -1;\r
559 \r
560                                 public void actionPerformed(ActionEvent e) {\r
561                                         onClickReject(e);\r
562                                 }\r
563                         });\r
564                         gbc.gridx = 1;\r
565                         gbc.gridy = 1;\r
566                         gbc.weightx = 0;\r
567                         add(btnReject, gbc);\r
568 \r
569                         if (category.isMultipleSelectable() && lastInCategory) {\r
570                                 JButton btnAdd = new JButton(new AbstractAction(\r
571                                                 strings.getProperty("add")) {\r
572                                         private static final long serialVersionUID = -1;\r
573 \r
574                                         public void actionPerformed(ActionEvent e) {\r
575                                                 onClickAdd(e);\r
576                                         }\r
577                                 });\r
578                                 gbc.gridx = 1;\r
579                                 gbc.gridy = 2;\r
580                                 gbc.weightx = 0;\r
581                                 add(btnAdd, gbc);\r
582                         }\r
583 \r
584                         updateButtonState();\r
585                 }\r
586 \r
587                 public void addActionListener(ActionListener l) {\r
588                         listeners.add(ActionListener.class, l);\r
589                 }\r
590 \r
591                 public void removeActionListener(ActionListener l) {\r
592                         listeners.remove(ActionListener.class, l);\r
593                 }\r
594 \r
595                 public boolean isEnableRandom() {\r
596                         return label.isSelected();\r
597                 }\r
598 \r
599                 public void setEnableRandom(boolean selected) {\r
600                         label.setSelected(selected);\r
601                 }\r
602 \r
603                 public PartsIdentifier getSelectedPartsIdentifier() {\r
604                         return (PartsIdentifier) partsCombo.getSelectedItem();\r
605                 }\r
606 \r
607                 public void setSelectedPartsIdentifier(PartsIdentifier partsIdentifier) {\r
608                         partsCombo.setSelectedItem(partsIdentifier);\r
609                 }\r
610 \r
611                 protected void updateButtonState() {\r
612                         PartsIdentifier partsIdentifier = getSelectedPartsIdentifier();\r
613                         if (partsIdentifier == null) {\r
614                                 btnReject.setEnabled(false);\r
615                                 return;\r
616                         }\r
617                         boolean exclude = partsSync\r
618                                         .isExcludePartsIdentifier(partsIdentifier);\r
619                         btnReject.setSelected(exclude);\r
620                         btnReject.setEnabled(true);\r
621                 }\r
622 \r
623                 protected void onSelectChangePartsIdentifier(ActionEvent e) {\r
624                         updateButtonState();\r
625 \r
626                         ActionEvent evt = new ActionEvent(this,\r
627                                         ActionEvent.ACTION_PERFORMED, "selectChangePartsIdentifier");\r
628                         for (ActionListener l : listeners\r
629                                         .getListeners(ActionListener.class)) {\r
630                                 l.actionPerformed(evt);\r
631                         }\r
632                 }\r
633 \r
634                 protected void onClickReject(ActionEvent e) {\r
635                         PartsIdentifier partsIdentifier = getSelectedPartsIdentifier();\r
636                         if (partsIdentifier == null) {\r
637                                 return;\r
638                         }\r
639                         boolean exclude = partsSync\r
640                                         .isExcludePartsIdentifier(partsIdentifier);\r
641                         partsSync.setExcludePartsIdentifier(partsIdentifier, !exclude);\r
642                         updateButtonState();\r
643                 }\r
644 \r
645                 protected void onClickRandom(ActionEvent e) {\r
646                         selectRandom();\r
647                 }\r
648 \r
649                 public void selectRandom() {\r
650                         ArrayList<PartsIdentifier> partsIdentifiers = new ArrayList<PartsIdentifier>();\r
651                         int mx = partsCombo.getItemCount();\r
652                         for (int idx = 0; idx < mx; idx++) {\r
653                                 PartsIdentifier partsIdentifier = (PartsIdentifier) partsCombo\r
654                                                 .getItemAt(idx);\r
655                                 if (partsIdentifier != null) {\r
656                                         if (!partsSync.isExcludePartsIdentifier(partsIdentifier)) {\r
657                                                 partsIdentifiers.add(partsIdentifier);\r
658                                         }\r
659                                 }\r
660                         }\r
661 \r
662                         int len = partsIdentifiers.size();\r
663                         if (len == 0) {\r
664                                 // 選択しようがないので何もしない.\r
665                                 return;\r
666                         }\r
667 \r
668                         Random rng = new Random();\r
669                         int selidx = rng.nextInt(len);\r
670 \r
671                         setSelectedPartsIdentifier(partsIdentifiers.get(selidx));\r
672                 }\r
673 \r
674                 protected void onClickAdd(ActionEvent e) {\r
675                         // 何もしない.\r
676                 }\r
677         }\r
678 \r
679         /**\r
680          * カテゴリのパーツのランダム選択パネルを作成する.<br>\r
681          * パネルが追加ボタンをもつときには、作成されたパネルにもパーツ変更リスナは適用される.<br>\r
682          *\r
683          * @param centerPnl\r
684          *            追加されるパネル\r
685          * @param addPos\r
686          *            追加する位置\r
687          * @param category\r
688          *            カテゴリ\r
689          * @param lastInCategory\r
690          *            作成するパネルに、追加ボタンをつけるか?\r
691          * @param changePartsIdentifierListener\r
692          *            パーツ選択が変わった場合のリスナ\r
693          * @return 作成されたランダム選択パネル\r
694          */\r
695         protected RandomChooserPanel addPartsChooserPanel(final Box centerPnl,\r
696                         final int addPos,\r
697                         final PartsCategory category,\r
698                         final boolean lastInCategory,\r
699                         final ActionListener changePartsIdentifierListener) {\r
700                 RandomChooserPanel pnl = new RandomChooserPanel(category,\r
701                                 lastInCategory) {\r
702                         private static final long serialVersionUID = 1L;\r
703 \r
704                         @Override\r
705                         protected void onClickAdd(ActionEvent e) {\r
706                                 int mx = centerPnl.getComponentCount();\r
707                                 for (int idx = 0; idx < mx; idx++) {\r
708                                         Component comp = centerPnl.getComponent(idx);\r
709                                         if (comp.equals(this)) {\r
710                                                 // 同じカテゴリのものを追加する\r
711                                                 addPartsChooserPanel(centerPnl, idx + 1, category,\r
712                                                                 lastInCategory, changePartsIdentifierListener);\r
713                                                 centerPnl.validate();\r
714                                                 // Addボタンを非表示にする.\r
715                                                 ((JButton) e.getSource()).setVisible(false);\r
716                                                 break;\r
717                                         }\r
718                                 }\r
719                         }\r
720                 };\r
721 \r
722                 // パーツ選択変更を通知するリスナを設定する.\r
723                 pnl.addActionListener(changePartsIdentifierListener);\r
724 \r
725                 centerPnl.add(pnl, addPos);\r
726                 return pnl;\r
727         }\r
728 }\r