OSDN Git Service

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