OSDN Git Service

プロファイル作成時にデフォルトのキャラクターデータのダウンロードを問い合わせる
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / ui / ExportWizardDialog.java
1 package charactermanaj.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.CardLayout;
5 import java.awt.Color;
6 import java.awt.Component;
7 import java.awt.Container;
8 import java.awt.Cursor;
9 import java.awt.Dimension;
10 import java.awt.Font;
11 import java.awt.GridBagConstraints;
12 import java.awt.GridBagLayout;
13 import java.awt.Insets;
14 import java.awt.Rectangle;
15 import java.awt.Toolkit;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.event.ComponentAdapter;
19 import java.awt.event.ComponentEvent;
20 import java.awt.event.ComponentListener;
21 import java.awt.event.KeyEvent;
22 import java.awt.event.WindowAdapter;
23 import java.awt.event.WindowEvent;
24 import java.awt.image.BufferedImage;
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.io.StringWriter;
29 import java.sql.Timestamp;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.LinkedList;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Properties;
41
42 import javax.swing.AbstractAction;
43 import javax.swing.Action;
44 import javax.swing.ActionMap;
45 import javax.swing.BorderFactory;
46 import javax.swing.Box;
47 import javax.swing.BoxLayout;
48 import javax.swing.InputMap;
49 import javax.swing.JButton;
50 import javax.swing.JCheckBox;
51 import javax.swing.JComponent;
52 import javax.swing.JDialog;
53 import javax.swing.JFrame;
54 import javax.swing.JLabel;
55 import javax.swing.JOptionPane;
56 import javax.swing.JPanel;
57 import javax.swing.JPopupMenu;
58 import javax.swing.JRootPane;
59 import javax.swing.JScrollPane;
60 import javax.swing.JTable;
61 import javax.swing.JTextArea;
62 import javax.swing.JTextField;
63 import javax.swing.KeyStroke;
64 import javax.swing.ListSelectionModel;
65 import javax.swing.event.ChangeEvent;
66 import javax.swing.event.ChangeListener;
67 import javax.swing.event.TableModelEvent;
68 import javax.swing.event.TableModelListener;
69 import javax.swing.table.TableCellRenderer;
70 import javax.swing.table.TableColumnModel;
71
72 import charactermanaj.Main;
73 import charactermanaj.model.AppConfig;
74 import charactermanaj.model.CharacterData;
75 import charactermanaj.model.CustomLayerOrder;
76 import charactermanaj.model.CustomLayerOrderKey;
77 import charactermanaj.model.PartsCategory;
78 import charactermanaj.model.PartsIdentifier;
79 import charactermanaj.model.PartsSet;
80 import charactermanaj.model.PartsSpec;
81 import charactermanaj.model.PartsSpecResolver;
82 import charactermanaj.model.io.CharacterDataFileReaderWriterFactory;
83 import charactermanaj.model.io.CharacterDataWriter;
84 import charactermanaj.model.io.CustomLayerOrderPersist;
85 import charactermanaj.model.io.ExportInfoKeys;
86 import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;
87 import charactermanaj.ui.progress.ProgressHandle;
88 import charactermanaj.ui.progress.Worker;
89 import charactermanaj.ui.progress.WorkerException;
90 import charactermanaj.ui.progress.WorkerWithProgessDialog;
91 import charactermanaj.ui.util.ScaleSupport;
92 import charactermanaj.util.ErrorMessageHelper;
93 import charactermanaj.util.LocalizedResourcePropertyLoader;
94
95 public class ExportWizardDialog extends JDialog {
96
97         private static final long serialVersionUID = 1L;
98
99         protected static final String STRINGS_RESOURCE = "languages/exportwizdialog";
100
101         protected static ArchiveFileDialog archiveFileDialog = new ArchiveFileDialog();
102
103         private JPanel activePanel;
104
105         private AbstractAction actNext;
106
107         private AbstractAction actPrev;
108
109         private AbstractAction actFinish;
110
111         private ExportInformationPanel basicPanel;
112
113         private ExportPartsSelectPanel partsSelectPanel;
114
115         private ExportPresetSelectPanel presetSelectPanel;
116
117         private CharacterData source;
118
119         public static File getLastUsedDir() {
120                 return archiveFileDialog.getLastUSedDir();
121         }
122
123         public static void setLastUsedDir(File lastUsedDir) {
124                 archiveFileDialog.setLastUSedDir(lastUsedDir);
125         }
126
127         public ExportWizardDialog(JFrame parent, CharacterData characterData, BufferedImage samplePicture) {
128                 super(parent, true);
129                 initComponent(parent, characterData, samplePicture);
130         }
131
132         public ExportWizardDialog(JDialog parent, CharacterData characterData, BufferedImage samplePicture) {
133                 super(parent, true);
134                 initComponent(parent, characterData, samplePicture);
135         }
136
137         private void initComponent(Component parent, CharacterData characterData, BufferedImage samplePicture) {
138                 if (characterData == null) {
139                         throw new IllegalArgumentException();
140                 }
141                 this.source = characterData;
142
143                 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
144                 addWindowListener(new WindowAdapter() {
145                         @Override
146                         public void windowClosing(WindowEvent e) {
147                                 onClose();
148                         }
149                 });
150
151                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
152                                 .getLocalizedProperties(STRINGS_RESOURCE);
153
154                 // タイトル
155                 setTitle(strings.getProperty("title"));
156
157                 // メインパネル
158
159                 ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
160
161                 Container contentPane = getContentPane();
162                 contentPane.setLayout(new BorderLayout());
163
164                 final JPanel mainPanel = new JPanel();
165                 mainPanel.setBorder(BorderFactory.createEtchedBorder());
166                 int mergin = (int)(5 * scaleSupport.getManualScaleX());
167                 final CardLayout mainPanelLayout = new CardLayout(mergin, mergin);
168                 mainPanel.setLayout(mainPanelLayout);
169                 contentPane.add(mainPanel, BorderLayout.CENTER);
170
171                 ComponentListener componentListener = new ComponentAdapter() {
172                         public void componentShown(ComponentEvent e) {
173                                 onComponentShown((JPanel) e.getComponent());
174                         }
175                 };
176
177
178                 // アクション
179
180                 this.actNext = new AbstractAction(strings.getProperty("btn.next")) {
181                         private static final long serialVersionUID = 1L;
182                         public void actionPerformed(ActionEvent e) {
183                                 mainPanelLayout.next(mainPanel);
184                         }
185                 };
186                 this.actPrev = new AbstractAction(strings.getProperty("btn.prev")) {
187                         private static final long serialVersionUID = 1L;
188                         public void actionPerformed(ActionEvent e) {
189                                 mainPanelLayout.previous(mainPanel);
190                         }
191                 };
192                 this.actFinish = new AbstractAction(strings.getProperty("btn.finish")) {
193                         private static final long serialVersionUID = 1L;
194                         public void actionPerformed(ActionEvent e) {
195                                 onFinish();
196                         }
197                 };
198                 AbstractAction actCancel = new AbstractAction(strings.getProperty("btn.cancel")) {
199                         private static final long serialVersionUID = 1L;
200                         public void actionPerformed(ActionEvent e) {
201                                 onClose();
202                         }
203                 };
204
205                 ChangeListener actChangeValue = new ChangeListener() {
206                         public void stateChanged(ChangeEvent e) {
207                                 updateBtnPanelState();
208                         }
209                 };
210
211                 ChangeListener actPanelEnabler = new ChangeListener() {
212                         public void stateChanged(ChangeEvent e) {
213                                 updatePanelStatus();
214                         }
215                 };
216
217                 // panel1 : basic
218                 this.basicPanel = new ExportInformationPanel(characterData, samplePicture, scaleSupport);
219                 this.basicPanel.addComponentListener(componentListener);
220                 this.basicPanel.addChangeListener(actChangeValue);
221                 this.basicPanel.addChangeListener(actPanelEnabler);
222                 mainPanel.add(this.basicPanel, "basicPanel");
223
224                 // panel2 : panelSelectPanel
225                 this.partsSelectPanel = new ExportPartsSelectPanel(characterData, scaleSupport);
226                 this.partsSelectPanel.addComponentListener(componentListener);
227                 this.partsSelectPanel.addChangeListener(actChangeValue);
228                 mainPanel.add(this.partsSelectPanel, "partsSelectPanel");
229
230                 // panel3 : presetSelectPanel
231                 this.presetSelectPanel = new ExportPresetSelectPanel(
232                                 this.partsSelectPanel,
233                                 this.basicPanel,
234                                 characterData.getPartsSets().values(),
235                                 characterData.getDefaultPartsSetId(),
236                                 scaleSupport);
237                 this.presetSelectPanel.addComponentListener(componentListener);
238                 this.presetSelectPanel.addChangeListener(actChangeValue);
239                 mainPanel.add(this.presetSelectPanel, "presetSelectPanel");
240
241
242                 // button panel
243
244                 JPanel btnPanel = new JPanel();
245                 int gap = (int)(3 * scaleSupport.getManualScaleX());
246                 btnPanel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap * 15)); // 3, 3, 3, 45
247                 GridBagLayout btnPanelLayout = new GridBagLayout();
248                 btnPanel.setLayout(btnPanelLayout);
249
250                 actPrev.setEnabled(false);
251                 actNext.setEnabled(false);
252                 actFinish.setEnabled(false);
253
254                 GridBagConstraints gbc = new GridBagConstraints();
255
256                 gbc.gridx = 0;
257                 gbc.gridy = 0;
258                 gbc.gridheight = 1;
259                 gbc.gridwidth = 1;
260                 gbc.anchor = GridBagConstraints.EAST;
261                 gbc.fill = GridBagConstraints.BOTH;
262                 gbc.ipadx = 0;
263                 gbc.ipady = 0;
264                 gbc.insets = new Insets(gap, gap, gap, gap);
265                 gbc.weightx = 1.;
266                 gbc.weighty = 0.;
267                 btnPanel.add(Box.createHorizontalGlue(), gbc);
268
269                 gbc.gridx = Main.isLinuxOrMacOSX() ? 2 : 1;
270                 gbc.gridy = 0;
271                 gbc.weightx = 0.;
272                 btnPanel.add(new JButton(this.actPrev), gbc);
273
274                 gbc.gridx = Main.isLinuxOrMacOSX() ? 3 : 2;
275                 gbc.gridy = 0;
276                 JButton btnNext = new JButton(this.actNext);
277                 btnPanel.add(btnNext, gbc);
278
279                 gbc.gridx = Main.isLinuxOrMacOSX() ? 4 : 3;
280                 gbc.gridy = 0;
281                 btnPanel.add(new JButton(this.actFinish), gbc);
282
283                 gbc.gridx = Main.isLinuxOrMacOSX() ? 1 : 4;
284                 gbc.gridy = 0;
285                 JButton btnCancel = new JButton(actCancel);
286                 btnPanel.add(btnCancel, gbc);
287
288                 contentPane.add(btnPanel, BorderLayout.SOUTH);
289
290                 // インプットマップ/アクションマップ
291
292                 Toolkit tk = Toolkit.getDefaultToolkit();
293                 JRootPane rootPane = getRootPane();
294
295                 rootPane.setDefaultButton(btnNext);
296
297                 InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
298                 ActionMap am = rootPane.getActionMap();
299                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeExportWizDialog");
300                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeExportWizDialog");
301                 am.put("closeExportWizDialog", actCancel);
302
303                 // 表示
304
305                 // HiDpi環境でのスケールを考慮したウィンドウサイズに補正する
306                 setSize(scaleSupport.manualScaled(new Dimension(500, 500)));
307                 setLocationRelativeTo(parent);
308
309                 mainPanelLayout.first(mainPanel);
310                 updateBtnPanelState();
311                 updatePanelStatus();
312         }
313
314         protected void onComponentShown(JPanel panel) {
315                 activePanel = panel;
316                 updateBtnPanelState();
317         }
318
319         protected void updatePanelStatus() {
320                 partsSelectPanel.setEnabled(basicPanel.isExportPartsImages());
321                 presetSelectPanel.setEnabled(basicPanel.isExportPresets());
322         }
323
324         protected void updateBtnPanelState() {
325                 actPrev.setEnabled(activePanel != null && activePanel != basicPanel);
326                 actNext.setEnabled(activePanel != null && activePanel != presetSelectPanel);
327                 actFinish.setEnabled(isComplete());
328         }
329
330         protected void checkMissingParts(Collection<PartsSet> partaSets) {
331                 if (partaSets == null) {
332                         partaSets = presetSelectPanel.getSelectedPresets();
333                 }
334                 partsSelectPanel.checkMissingPartsList(partaSets);
335         }
336
337         protected boolean isComplete() {
338
339                 if (basicPanel.isExportPartsImages()) {
340                         if (partsSelectPanel.getSelectedCount() == 0) {
341                                 // パーツイメージのエクスポートを指定した場合、エクスポートするパーツの選択は必須
342                                 return false;
343                         }
344                 }
345                 if (basicPanel.isExportPresets()) {
346                         if (presetSelectPanel.getSelectedCount() == 0) {
347                                 // プリセットのエクスポートを指定した場合、エクスポートするプリセットの選択は必須
348                                 return false;
349                         }
350                 }
351
352                 return true;
353         }
354
355         protected void onClose() {
356                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
357                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
358
359                 if (JOptionPane.showConfirmDialog(this,
360                                 strings.getProperty("confirm.close"),
361                                 strings.getProperty("confirm"),
362                                 JOptionPane.YES_NO_OPTION,
363                                 JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
364                         return;
365                 }
366
367                 dispose();
368         }
369
370         protected void onFinish() {
371                 if (!isComplete()) {
372                         Toolkit tk = Toolkit.getDefaultToolkit();
373                         tk.beep();
374                         return;
375                 }
376
377                 try {
378                         final File outFile = archiveFileDialog.showSaveDialog(this);
379                         if (outFile == null) {
380                                 return;
381                         }
382
383                         // 出力
384                         setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
385                         try {
386                                 Worker<Object> worker = new Worker<Object>() {
387                                         public Object doWork(ProgressHandle progressHandle) throws IOException {
388                                                 doExport(outFile);
389                                                 return null;
390                                         }
391                                 };
392
393                                 WorkerWithProgessDialog<Object> dlg
394                                         = new WorkerWithProgessDialog<Object>(this, worker);
395                                 dlg.startAndWait();
396
397                         } finally {
398                                 setCursor(Cursor.getDefaultCursor());
399                         }
400
401                         // 完了メッセージ
402                         Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
403                                         .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
404                         JOptionPane.showMessageDialog(this, strings.getProperty("complete"));
405
406                         // 完了後、ウィンドウを閉じる.
407                         dispose();
408
409                 } catch (WorkerException ex) {
410                         ErrorMessageHelper.showErrorDialog(this, ex.getCause());
411
412                 } catch (Exception ex) {
413                         ErrorMessageHelper.showErrorDialog(this, ex);
414                 }
415         }
416
417         protected void doExport(File outFile) throws IOException {
418                 CharacterDataFileReaderWriterFactory writerFactory = CharacterDataFileReaderWriterFactory.getInstance();
419                 CharacterDataWriter exportWriter = writerFactory.createWriter(outFile);
420                 try {
421                         // 基本情報のみをコピーし、これをエクスポートするキャラクター定義のベースとする。
422                         // (プリセットとパーツイメージはリセットされている状態。)
423                         CharacterData cd = source.duplicateBasicInfo();
424                         cd.clearPartsSets(false);
425
426                         boolean exportPresets = basicPanel.isExportPresets();
427                         boolean exportSamplePicture = basicPanel.isExportSamplePicture();
428                         boolean exportCharacterData = true;
429                         boolean exportPartsImages = basicPanel.isExportPartsImages();
430
431                         // 基本情報を設定する.
432                         cd.setAuthor(basicPanel.getAuthor());
433                         cd.setDescription(basicPanel.getDescription());
434
435                         // エクスポート情報を出力する.
436                         Properties exportProp = new Properties();
437                         exportProp.setProperty(ExportInfoKeys.EXPORT_PRESETS, Boolean.toString(exportPresets));
438                         exportProp.setProperty(ExportInfoKeys.EXPORT_SAMPLE_PICTURE, Boolean.toString(exportSamplePicture));
439                         exportProp.setProperty(ExportInfoKeys.EXPORT_CHARACTER_DATA, Boolean.toString(exportCharacterData));
440                         exportProp.setProperty(ExportInfoKeys.EXPORT_PARTS_IMAGES, Boolean.toString(exportPartsImages));
441                         exportProp.setProperty(ExportInfoKeys.EXPORT_TIMESTAMP, Long.toString(System.currentTimeMillis()));
442
443                         exportWriter.writeExportProp(exportProp);
444
445                         // プリセットをエクスポートする場合、プリセット情報を登録する.
446                         if (exportPresets) {
447                                 HashSet<String> registered = new HashSet<String>();
448                                 for (PartsSet partsSet : presetSelectPanel.getSelectedPresets()) {
449                                         registered.add(partsSet.getPartsSetId());
450                                         cd.addPartsSet(partsSet);
451                                 }
452                                 // プリセットとして登録済みのもののみ既定に設定可能
453                                 String defaultPresetId = presetSelectPanel.getDefaultPresetId();
454                                 if (registered.contains(defaultPresetId)) {
455                                         cd.setDefaultPartsSetId(defaultPresetId);
456                                 }
457                         }
458
459                         // キャラクターデータを出力する.
460                         exportWriter.writeCharacterData(cd);
461
462                         // カスタムレイヤーパターンを出力する(有効であれば)
463                         if (cd.isEnableCustonLayerPattern()) {
464                                 Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatternMap = CustomLayerOrderPersist
465                                                 .newInstance(cd).load();
466                                 if (customLayerPatternMap != null) {
467                                         exportWriter.writeCustomLayerPatterns(customLayerPatternMap);
468                                 }
469                         }
470
471                         // readme.txtを出力する.
472                         String readmeContents = cd.getDescription();
473                         if (readmeContents != null && readmeContents.trim().length() > 0) {
474                                 AppConfig appConfig = AppConfig.getInstance();
475                                 StringWriter sw = new StringWriter();
476                                 PrintWriter pw = new PrintWriter(sw);
477                                 pw.println("exported by CharacterManaJ (version "
478                                                 + appConfig.getSpecificationVersion() + " "
479                                                 + appConfig.getImplementationVersion() + ")");
480                                 pw.println();
481                                 pw.println(readmeContents);
482                                 pw.close();
483                                 exportWriter.writeTextUTF16LE("readme.txt", sw.toString());
484                         }
485
486                         // サンプルピクチャをエクスポートする
487                         if (exportSamplePicture) {
488                                 BufferedImage pic = null;
489                                 pic = basicPanel.getSamplePicture();
490                                 if (pic != null) {
491                                         exportWriter.writeSamplePicture(pic);
492                                 }
493                         }
494
495                         if (exportPartsImages) {
496                                 Map<PartsIdentifier, PartsSpec> partsSpecMap = partsSelectPanel.getSelectedParts();
497
498                                 // パーツ管理情報を出力する
499                                 exportWriter.writePartsManageData(partsSpecMap);
500
501                                 // パーツイメージを出力する
502                                 exportWriter.writePartsImages(partsSpecMap);
503                         }
504
505                 } finally {
506                         exportWriter.close();
507                 }
508         }
509 }
510
511 interface ExportResolverBase {
512
513         void addChangeListener(ChangeListener l);
514
515         void removeChangeListener(ChangeListener l);
516
517 }
518
519
520
521 /**
522  * 基本情報
523  * @author seraphy
524  */
525 interface ExportInformationResolver extends ExportResolverBase {
526
527         BufferedImage getSamplePicture();
528
529         boolean isExportSamplePicture();
530
531         boolean isExportPartsImages();
532
533         boolean isExportPresets();
534
535         String getAuthor();
536
537         String getDescription();
538 }
539
540 abstract class AbstractImportPanel extends JPanel implements ExportResolverBase {
541
542         private static final long serialVersionUID = 1L;
543
544         protected LinkedList<ChangeListener> listeners = new LinkedList<ChangeListener>();
545
546         public void addChangeListener(ChangeListener l) {
547                 if (l != null) {
548                         listeners.add(l);
549                 }
550         }
551
552         public void removeChangeListener(ChangeListener l) {
553                 if (l != null) {
554                         listeners.remove(l);
555                 }
556         }
557
558         protected void fireChangeEvent() {
559                 ChangeEvent e = new ChangeEvent(this);
560                 for (ChangeListener listener : listeners) {
561                         listener.stateChanged(e);
562                 }
563         }
564 }
565
566 /**
567  * 基本情報パネル
568  * @author seraphy
569  */
570 class ExportInformationPanel extends AbstractImportPanel implements ExportInformationResolver {
571
572         private static final long serialVersionUID = 1L;
573
574         private BufferedImage samplePicture;
575
576         private SamplePicturePanel sampleImagePanel;
577
578         private JTextField txtAuthor;
579
580         private JTextArea txtDescription;
581
582         private JCheckBox chkPartsImages;
583
584         private JCheckBox chkPresets;
585
586         private JCheckBox chkSampleImage;
587
588
589
590         protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture,
591                         ScaleSupport scaleSupport) {
592                 if (characterData == null) {
593                         throw new IllegalArgumentException();
594                 }
595
596                 setName("basicPanel");
597                 this.samplePicture = samplePicture;
598
599                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
600                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
601
602                 GridBagLayout basicPanelLayout = new GridBagLayout();
603                 GridBagConstraints gbc = new GridBagConstraints();
604                 setLayout(basicPanelLayout);
605
606                 JPanel contentsSpecPanel = new JPanel();
607                 int mergin = (int)(5 * scaleSupport.getManualScaleX());
608                 contentsSpecPanel.setBorder(BorderFactory.createCompoundBorder(
609                                 BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
610                                 BorderFactory.createTitledBorder(strings.getProperty("basic.contentsSpec"))));
611                 BoxLayout contentsSpecPanelLayout = new BoxLayout(contentsSpecPanel, BoxLayout.PAGE_AXIS);
612                 contentsSpecPanel.setLayout(contentsSpecPanelLayout);
613
614                 JCheckBox chkCharacterDef = new JCheckBox(strings.getProperty("characterdef"));
615                 chkPartsImages = new JCheckBox(strings.getProperty("basic.chk.partsImages"));
616                 chkPresets = new JCheckBox(strings.getProperty("basic.chk.presets"));
617                 chkSampleImage = new JCheckBox(strings.getProperty("basic.chk.samplePicture"));
618
619                 chkCharacterDef.setEnabled(false); // キャラクター定義は固定
620                 chkCharacterDef.setSelected(true);
621
622                 contentsSpecPanel.add(chkCharacterDef);
623                 contentsSpecPanel.add(chkPartsImages);
624                 contentsSpecPanel.add(chkPresets);
625                 contentsSpecPanel.add(chkSampleImage);
626
627                 ///
628
629                 JPanel commentPanel = new JPanel();
630                 Dimension archiveInfoPanelMinSize = scaleSupport.manualScaled(new Dimension(300, 200));
631                 commentPanel.setMinimumSize(archiveInfoPanelMinSize);
632                 commentPanel.setPreferredSize(archiveInfoPanelMinSize);
633                 commentPanel.setBorder(BorderFactory.createCompoundBorder(
634                                 BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
635                                 BorderFactory.createTitledBorder(strings.getProperty("basic.comment"))));
636                 GridBagLayout commentPanelLayout = new GridBagLayout();
637                 commentPanel.setLayout(commentPanelLayout);
638
639                 gbc.gridx = 0;
640                 gbc.gridy = 0;
641                 gbc.gridheight = 1;
642                 gbc.gridwidth = 1;
643                 gbc.weightx = 0.;
644                 gbc.weighty = 0.;
645                 int gap = (int)(3 * scaleSupport.getManualScaleX());
646                 gbc.insets = new Insets(gap, gap, gap, gap);
647                 gbc.anchor = GridBagConstraints.WEST;
648                 gbc.fill = GridBagConstraints.BOTH;
649                 commentPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc);
650
651                 gbc.gridx = 1;
652                 gbc.gridy = 0;
653                 gbc.gridwidth = 1;
654                 gbc.weightx = 1.;
655                 gbc.weighty = 0.;
656                 txtAuthor = new JTextField();
657                 commentPanel.add(txtAuthor, gbc);
658
659                 gbc.gridx = 0;
660                 gbc.gridy = 1;
661                 gbc.gridwidth = 1;
662                 gbc.gridheight = 1;
663                 gbc.weightx = 0.;
664                 gbc.weighty = 0.;
665                 commentPanel.add(new JLabel(strings.getProperty("description"), JLabel.RIGHT), gbc);
666
667                 gbc.gridx = 1;
668                 gbc.gridy = 1;
669                 gbc.gridwidth = 1;
670                 gbc.gridheight = 2;
671                 gbc.weighty = 1.;
672                 gbc.weightx = 1.;
673                 txtDescription = new JTextArea();
674                 commentPanel.add(new JScrollPane(txtDescription), gbc);
675
676                 ///
677
678                 sampleImagePanel = new SamplePicturePanel(samplePicture);
679                 sampleImagePanel.setBorder(BorderFactory.createCompoundBorder(
680                                 BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
681                                 BorderFactory.createTitledBorder(strings.getProperty("basic.sampleImage"))));
682
683
684                 ///
685
686                 gbc.gridx = 0;
687                 gbc.gridy = 0;
688                 gbc.gridheight = 1;
689                 gbc.gridwidth = 2;
690                 gbc.weightx = 1.;
691                 gbc.weighty = 0.;
692                 gbc.anchor = GridBagConstraints.WEST;
693                 gbc.fill = GridBagConstraints.BOTH;
694                 add(contentsSpecPanel, gbc);
695
696                 gbc.gridx = 0;
697                 gbc.gridy = 1;
698                 gbc.gridheight = 1;
699                 gbc.gridwidth = 1;
700                 gbc.weightx = 0.;
701                 gbc.weighty = 1.;
702                 gbc.anchor = GridBagConstraints.WEST;
703                 gbc.fill = GridBagConstraints.BOTH;
704                 add(commentPanel, gbc);
705
706                 gbc.gridx = 1;
707                 gbc.gridy = 1;
708                 gbc.gridheight = 1;
709                 gbc.gridwidth = 1;
710                 gbc.weightx = 1.;
711                 gbc.weighty = 1.;
712                 gbc.anchor = GridBagConstraints.WEST;
713                 gbc.fill = GridBagConstraints.BOTH;
714                 add(sampleImagePanel, gbc);
715
716                 loadBasicInfo(characterData);
717
718                 // アクションリスナ
719
720                 ActionListener modListener = new ActionListener() {
721                         public void actionPerformed(ActionEvent e) {
722                                 updateSamplePicture();
723                                 fireChangeEvent();
724                         }
725                 };
726                 chkPartsImages.addActionListener(modListener);
727                 chkPresets.addActionListener(modListener);
728                 chkSampleImage.addActionListener(modListener);
729         }
730
731         protected void updateSamplePicture() {
732                 sampleImagePanel.setVisiblePicture(chkSampleImage.isSelected());
733         }
734
735         protected void loadBasicInfo(CharacterData characterData) {
736                 if (samplePicture == null) {
737                         // サンプルイメージがなければディセーブル
738                         chkSampleImage.setEnabled(false);
739                         chkSampleImage.setSelected(false);
740                         sampleImagePanel.setVisiblePicture(false);
741                 } else {
742                         chkSampleImage.setSelected(true);
743                         sampleImagePanel.setVisiblePicture(true);
744                 }
745                 chkPartsImages.setSelected(true);
746                 chkPresets.setSelected(true);
747
748                 String author = characterData.getAuthor();
749                 String description = characterData.getDescription();
750                 txtAuthor.setText(author == null ? "" : author);
751                 txtDescription.setText(description == null ? "" : description);
752         }
753
754         public BufferedImage getSamplePicture() {
755                 return samplePicture;
756         }
757
758         public boolean isExportSamplePicture() {
759                 return chkSampleImage.isSelected();
760         }
761
762         public boolean isExportPartsImages() {
763                 return chkPartsImages.isSelected();
764         }
765
766         public boolean isExportPresets() {
767                 return chkPresets.isSelected();
768         }
769
770         public String getAuthor() {
771                 return txtAuthor.getText();
772         }
773
774         public String getDescription() {
775                 return txtDescription.getText();
776         }
777 }
778
779 /**
780  * エクスポート対象パーツ
781  * @author seraphy
782  */
783 interface ExportPartsResolver extends ExportResolverBase {
784
785         int getSelectedCount();
786
787         void selectByPartsSet(Collection<PartsSet> partsSet);
788
789         Map<PartsIdentifier, PartsSpec> getSelectedParts();
790
791         Map<PartsSet, List<PartsIdentifier>> checkMissingPartsList(Collection<PartsSet> partsSets);
792
793 }
794
795 /**
796  * エクスポート対象パーツ選択パネル
797  * @author seraphy
798  */
799 class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsResolver {
800
801         private static final long serialVersionUID = 1L;
802
803         private ExportPartsTableModel partsTableModel;
804
805         private JTable partsTable;
806
807         private Action actSelectAll;
808
809         private Action actDeselectAll;
810
811         private Action actSort;
812
813         private Action actSortByTimestamp;
814
815         protected ExportPartsSelectPanel(PartsSpecResolver partsSpecResolver, ScaleSupport scaleSupport) {
816                 if (partsSpecResolver == null) {
817                         throw new IllegalArgumentException();
818                 }
819
820                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
821                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
822
823                 setName("choosePartsPanel");
824                 setBorder(BorderFactory.createTitledBorder(strings.getProperty("parts.title")));
825
826                 setLayout(new BorderLayout());
827
828                 partsTableModel = new ExportPartsTableModel();
829
830                 partsTableModel.addTableModelListener(new TableModelListener() {
831                         public void tableChanged(TableModelEvent e) {
832                                 fireChangeEvent();
833                         }
834                 });
835
836                 loadPartsInfo(partsSpecResolver);
837
838                 AppConfig appConfig = AppConfig.getInstance();
839
840                 final Color disabledForeground = appConfig.getDisabledCellForgroundColor();
841
842                 partsTable = new JTable(partsTableModel) {
843                         private static final long serialVersionUID = 1L;
844                         @Override
845                         public Component prepareRenderer(TableCellRenderer renderer,
846                                         int row, int column) {
847                                 Component comp = super.prepareRenderer(renderer, row, column);
848                                 if (comp instanceof JCheckBox) {
849                                         // BooleanのデフォルトのレンダラーはKCheckBoxを継承したJTable$BooleanRenderer
850                                         comp.setEnabled(isCellEditable(row, column) && isEnabled());
851                                 }
852                                 comp.setForeground(isEnabled() ?
853                                                 (isCellSelected(row,column) ? getSelectionForeground() : getForeground())
854                                                 : disabledForeground);
855                                 return comp;
856                         }
857                 };
858                 partsTable.setShowGrid(true);
859                 partsTable.setGridColor(appConfig.getGridColor());
860                 partsTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
861                 partsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
862                 partsTable.setRowSelectionAllowed(true);
863
864                 // 行の高さをフォントの高さにする
865                 partsTable.setRowHeight((int)(partsTable.getFont().getSize() * 1.2));
866
867                 // 列幅を調整する
868                 partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
869                 partsTableModel.adjustColumnModel(partsTable.getColumnModel(), scaleSupport.getManualScaleX());
870
871                 Action actPartsSetCheck = new AbstractAction(strings.getProperty("parts.popup.check")) {
872                         private static final long serialVersionUID = 1L;
873                         public void actionPerformed(ActionEvent e) {
874                                 int[] selRows = partsTable.getSelectedRows();
875                                 partsTableModel.setCheck(selRows, true);
876                         }
877                 };
878                 Action actPartsUnsetCheck = new AbstractAction(strings.getProperty("parts.popup.uncheck")) {
879                         private static final long serialVersionUID = 1L;
880                         public void actionPerformed(ActionEvent e) {
881                                 int[] selRows = partsTable.getSelectedRows();
882                                 partsTableModel.setCheck(selRows, false);
883                         }
884                 };
885
886                 final JPopupMenu partsTablePopupMenu = new JPopupMenu();
887                 partsTablePopupMenu.add(actPartsSetCheck);
888                 partsTablePopupMenu.add(actPartsUnsetCheck);
889
890                 partsTable.setComponentPopupMenu(partsTablePopupMenu);
891
892                 add(new JScrollPane(partsTable), BorderLayout.CENTER);
893
894                 actSelectAll = new AbstractAction(strings.getProperty("parts.btn.selectAll")) {
895                         private static final long serialVersionUID = 1L;
896                         public void actionPerformed(ActionEvent e) {
897                                 onSelectAll();
898                         }
899                 };
900                 actDeselectAll = new AbstractAction(strings.getProperty("parts.btn.deselectAll")) {
901                         private static final long serialVersionUID = 1L;
902                         public void actionPerformed(ActionEvent e) {
903                                 onDeselectAll();
904                         }
905                 };
906                 actSort = new AbstractAction(strings.getProperty("parts.btn.sort")) {
907                         private static final long serialVersionUID = 1L;
908                         public void actionPerformed(ActionEvent e) {
909                                 onSort();
910                         }
911                 };
912                 actSortByTimestamp = new AbstractAction(strings.getProperty("parts.btn.sortByTimestamp")) {
913                         private static final long serialVersionUID = 1L;
914                         public void actionPerformed(ActionEvent e) {
915                                 onSortByTimestamp();
916                         }
917                 };
918
919
920                 JPanel btnPanel = new JPanel();
921                 GridBagLayout btnPanelLayout = new GridBagLayout();
922                 btnPanel.setLayout(btnPanelLayout);
923
924                 GridBagConstraints gbc = new GridBagConstraints();
925
926                 gbc.gridx = 0;
927                 gbc.gridy = 0;
928                 gbc.gridheight = 1;
929                 gbc.gridwidth = 1;
930                 gbc.anchor = GridBagConstraints.EAST;
931                 gbc.fill = GridBagConstraints.BOTH;
932                 int gap = (int)(3 * scaleSupport.getManualScaleX());
933                 gbc.insets = new Insets(gap, gap, gap, gap);
934                 gbc.ipadx = 0;
935                 gbc.ipady = 0;
936                 JButton btnSelectAll = new JButton(actSelectAll);
937                 btnPanel.add(btnSelectAll, gbc);
938
939                 gbc.gridx = 1;
940                 gbc.gridy = 0;
941                 JButton btnDeselectAll = new JButton(actDeselectAll);
942                 btnPanel.add(btnDeselectAll, gbc);
943
944                 gbc.gridx = 2;
945                 gbc.gridy = 0;
946                 JButton btnSort = new JButton(actSort);
947                 btnPanel.add(btnSort, gbc);
948
949                 gbc.gridx = 3;
950                 gbc.gridy = 0;
951                 JButton btnSortByTimestamp = new JButton(actSortByTimestamp);
952                 btnPanel.add(btnSortByTimestamp, gbc);
953
954                 gbc.gridx = 4;
955                 gbc.gridy = 0;
956                 gbc.weightx = 1.;
957                 btnPanel.add(Box.createHorizontalGlue(), gbc);
958
959                 add(btnPanel, BorderLayout.SOUTH);
960         }
961
962         protected void loadPartsInfo(PartsSpecResolver partsSpecResolver) {
963                 partsTableModel.clear();
964                 for (PartsCategory partsCategory : partsSpecResolver.getPartsCategories()) {
965                         Map<PartsIdentifier, PartsSpec> partsSpecMap = partsSpecResolver.getPartsSpecMap(partsCategory);
966                         for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsSpecMap.entrySet()) {
967                                 PartsIdentifier partsIdentifier = entry.getKey();
968                                 PartsSpec partsSpec = entry.getValue();
969                                 ExportPartsSelectModel model = new ExportPartsSelectModel(partsIdentifier, partsSpec, false);
970                                 partsTableModel.addRow(model);
971                         }
972                 }
973                 partsTableModel.sort();
974         }
975
976         protected void onSelectAll() {
977                 partsTableModel.selectAll();
978         }
979
980         protected void onDeselectAll() {
981                 partsTableModel.deselectAll();
982         }
983
984         protected void onSort() {
985                 partsTableModel.sort();
986                 if (partsTableModel.getRowCount() > 0) {
987                         Rectangle rct = partsTable.getCellRect(0, 0, true);
988                         partsTable.scrollRectToVisible(rct);
989                 }
990         }
991
992         protected void onSortByTimestamp() {
993                 partsTableModel.sortByTimestamp();
994                 if (partsTableModel.getRowCount() > 0) {
995                         Rectangle rct = partsTable.getCellRect(0, 0, true);
996                         partsTable.scrollRectToVisible(rct);
997                 }
998         }
999
1000         public Map<PartsIdentifier, PartsSpec> getSelectedParts() {
1001                 return partsTableModel.getSelectedParts();
1002         }
1003
1004         public Map<PartsSet, List<PartsIdentifier>> checkMissingPartsList(Collection<PartsSet> partsSets) {
1005                 return partsTableModel.checkMissingPartsList(partsSets);
1006         }
1007
1008         public void selectByPartsSet(Collection<PartsSet> partsSets) {
1009                 partsTableModel.selectByPartsSet(partsSets);
1010         }
1011
1012         public int getSelectedCount() {
1013                 return partsTableModel.getSelectedCount();
1014         }
1015
1016         @Override
1017         public void setEnabled(boolean enabled) {
1018                 partsTable.setEnabled(enabled);
1019                 partsTableModel.setEnabled(enabled);
1020                 actSelectAll.setEnabled(enabled);
1021                 actDeselectAll.setEnabled(enabled);
1022                 super.setEnabled(enabled);
1023         }
1024 }
1025
1026
1027 interface ExportPresetResolve extends ExportResolverBase {
1028
1029         int getSelectedCount();
1030
1031         List<PartsSet> getSelectedPresets();
1032 }
1033
1034 class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPresetResolve {
1035
1036         private static final long serialVersionUID = 1L;
1037
1038         private ExportPartsResolver exportPartsResolver;
1039
1040         private ExportPresetTableModel presetTableModel;
1041
1042         private JTable presetTable;
1043
1044         private Action actSelectAll;
1045
1046         private Action actDeselectAll;
1047
1048
1049         protected ExportPresetSelectPanel(
1050                         final ExportPartsResolver exportPartsResolver,
1051                         final ExportInformationResolver exportInfoResolver,
1052                         Collection<PartsSet> partsSets, String defaultPresetId,
1053                         ScaleSupport scaleSupport) {
1054
1055                 this.exportPartsResolver = exportPartsResolver;
1056
1057                 setName("presetSelectPanel");
1058
1059                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
1060                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
1061
1062                 setName("choosePartsPanel");
1063                 setBorder(BorderFactory.createTitledBorder(strings.getProperty("preset.title")));
1064
1065                 setLayout(new BorderLayout());
1066
1067                 presetTableModel = new ExportPresetTableModel();
1068
1069                 presetTableModel.addTableModelListener(new TableModelListener() {
1070                         public void tableChanged(TableModelEvent e) {
1071                                 if (e.getType() == TableModelEvent.UPDATE) {
1072                                         fireChangeEvent();
1073                                 }
1074                         }
1075                 });
1076                 exportPartsResolver.addChangeListener(new ChangeListener() {
1077                         public void stateChanged(ChangeEvent e) {
1078                                 checkMissingParts();
1079                         }
1080                 });
1081
1082                 loadPresetInfo(partsSets, defaultPresetId);
1083
1084                 AppConfig appConfig = AppConfig.getInstance();
1085                 final Color warningForegroundColor = appConfig.getExportPresetWarningsForegroundColor();
1086                 final Color disabledForeground = appConfig.getDisabledCellForgroundColor();
1087
1088                 presetTable = new JTable(presetTableModel) {
1089                         private static final long serialVersionUID = 1L;
1090                         @Override
1091                         public Component prepareRenderer(TableCellRenderer renderer,
1092                                         int row, int column) {
1093                                 Component comp = super.prepareRenderer(renderer, row, column);
1094
1095                                 if (comp instanceof JCheckBox) {
1096                                         // BooleanのデフォルトのレンダラーはKCheckBoxを継承したJTable$BooleanRenderer
1097                                         comp.setEnabled(isCellEditable(row, column) && isEnabled());
1098                                 }
1099
1100                                 ExportPresetModel presetModel = presetTableModel.getRow(row);
1101                                 if (presetModel.isPresetParts()) {
1102                                         comp.setFont(getFont().deriveFont(Font.BOLD));
1103                                 } else {
1104                                         comp.setFont(getFont());
1105                                 }
1106
1107                                 if (!isEnabled()) {
1108                                         comp.setForeground(disabledForeground);
1109
1110                                 } else {
1111                                         if (presetModel.isSelected() && presetModel.getMissingPartsIdentifiers().size() > 0) {
1112                                                 comp.setForeground(warningForegroundColor);
1113                                         } else {
1114                                                 comp.setForeground(getForeground());
1115                                         }
1116                                 }
1117                                 return comp;
1118                         }
1119                 };
1120                 presetTable.setShowGrid(true);
1121                 presetTable.setGridColor(appConfig.getGridColor());
1122                 presetTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
1123
1124                 // 行の高さをフォントの高さにする
1125                 presetTable.setRowHeight((int)(presetTable.getFont().getSize() * 1.2));
1126
1127                 // 列幅を調整する
1128                 presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
1129                 presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
1130
1131                 final Action actSelectUsedParts = new AbstractAction(
1132                                 strings.getProperty("preset.popup.selectUsedParts")) {
1133                         private static final long serialVersionUID = 1L;
1134                         public void actionPerformed(ActionEvent e) {
1135                                 exportUsedParts();
1136                         }
1137                 };
1138
1139                 final JPopupMenu popupMenu = new JPopupMenu();
1140                 popupMenu.add(actSelectUsedParts);
1141
1142                 presetTable.setComponentPopupMenu(popupMenu);
1143
1144
1145                 add(new JScrollPane(presetTable), BorderLayout.CENTER);
1146
1147                 actSelectAll = new AbstractAction(strings.getProperty("parts.btn.selectAll")) {
1148                         private static final long serialVersionUID = 1L;
1149                         public void actionPerformed(ActionEvent e) {
1150                                 onSelectAll();
1151                         }
1152                 };
1153                 actDeselectAll = new AbstractAction(strings.getProperty("parts.btn.deselectAll")) {
1154                         private static final long serialVersionUID = 1L;
1155                         public void actionPerformed(ActionEvent e) {
1156                                 onDeselectAll();
1157                         }
1158                 };
1159                 Action actSort = new AbstractAction(strings.getProperty("parts.btn.sort")) {
1160                         private static final long serialVersionUID = 1L;
1161                         public void actionPerformed(ActionEvent e) {
1162                                 onSort();
1163                         }
1164                 };
1165
1166
1167                 JPanel btnPanel = new JPanel();
1168                 GridBagLayout btnPanelLayout = new GridBagLayout();
1169                 btnPanel.setLayout(btnPanelLayout);
1170
1171                 GridBagConstraints gbc = new GridBagConstraints();
1172
1173                 gbc.gridx = 0;
1174                 gbc.gridy = 0;
1175                 gbc.gridheight = 1;
1176                 gbc.gridwidth = 1;
1177                 gbc.anchor = GridBagConstraints.EAST;
1178                 gbc.fill = GridBagConstraints.BOTH;
1179                 int gap = (int)(3 * scaleSupport.getManualScaleX());
1180                 gbc.insets = new Insets(gap, gap, gap, gap);
1181                 gbc.ipadx = 0;
1182                 gbc.ipady = 0;
1183                 JButton btnSelectAll = new JButton(actSelectAll);
1184                 btnPanel.add(btnSelectAll, gbc);
1185
1186                 gbc.gridx = 1;
1187                 gbc.gridy = 0;
1188                 JButton btnDeselectAll = new JButton(actDeselectAll);
1189                 btnPanel.add(btnDeselectAll, gbc);
1190
1191                 gbc.gridx = 2;
1192                 gbc.gridy = 0;
1193                 JButton btnSort = new JButton(actSort);
1194                 btnPanel.add(btnSort, gbc);
1195
1196                 gbc.gridx = 3;
1197                 gbc.gridy = 0;
1198                 gbc.weightx = 1.;
1199                 btnPanel.add(Box.createHorizontalGlue(), gbc);
1200
1201                 add(btnPanel, BorderLayout.SOUTH);
1202         }
1203
1204         protected void loadPresetInfo(Collection<PartsSet> partsSets, String defaultPresetId) {
1205                 presetTableModel.clear();
1206                 for (PartsSet orgPartsSet : partsSets) {
1207                         PartsSet partsSet = orgPartsSet.clone();
1208                         ExportPresetModel model = new ExportPresetModel(partsSet, partsSet.isPresetParts());
1209                         presetTableModel.addRow(model);
1210                 }
1211                 presetTableModel.setDefaultPresetId(defaultPresetId);
1212                 presetTableModel.sort();
1213                 checkMissingParts();
1214         }
1215
1216         public void checkMissingParts() {
1217                 ArrayList<PartsSet> changedPartsSets = new ArrayList<PartsSet>();
1218                 HashMap<PartsSet, ExportPresetModel> partsSetModelMap = new HashMap<PartsSet, ExportPresetModel>();
1219                 int mx = presetTableModel.getRowCount();
1220                 for (int idx = 0; idx < mx; idx++) {
1221                         ExportPresetModel presetModel = presetTableModel.getRow(idx);
1222                         PartsSet partsSet = presetModel.getPartsSet();
1223                         partsSetModelMap.put(partsSet, presetModel);
1224                         changedPartsSets.add(partsSet);
1225                 }
1226                 Map<PartsSet, List<PartsIdentifier>> missingPartsIdentifiersMap = exportPartsResolver
1227                                 .checkMissingPartsList(changedPartsSets);
1228                 for (Map.Entry<PartsSet, List<PartsIdentifier>> entry : missingPartsIdentifiersMap.entrySet()) {
1229                         PartsSet partsSet = entry.getKey();
1230                         List<PartsIdentifier> missingPartsIdentifiers = entry.getValue();
1231                         ExportPresetModel presetModel = partsSetModelMap.get(partsSet);
1232                         presetModel.setMissingPartsIdentifiers(missingPartsIdentifiers);
1233                 }
1234                 if (!missingPartsIdentifiersMap.isEmpty()) {
1235                         presetTableModel.fireTableDataChanged();
1236                 }
1237         }
1238
1239         protected void onSelectAll() {
1240                 presetTableModel.selectAll();
1241         }
1242
1243         protected void onDeselectAll() {
1244                 presetTableModel.deselectAll();
1245         }
1246
1247         protected void onSort() {
1248                 presetTableModel.sort();
1249                 if (presetTableModel.getRowCount() > 0) {
1250                         Rectangle rct = presetTable.getCellRect(0, 0, true);
1251                         presetTable.scrollRectToVisible(rct);
1252                 }
1253         }
1254
1255         public List<PartsSet> getSelectedPresets() {
1256                 return presetTableModel.getSelectedPresets();
1257         }
1258
1259         protected void exportUsedParts() {
1260                 ArrayList<PartsSet> partsSets = new ArrayList<PartsSet>();
1261                 int[] selRows = presetTable.getSelectedRows();
1262                 for (int selRow : selRows) {
1263                         ExportPresetModel presetModel = presetTableModel.getRow(selRow);
1264                         partsSets.add(presetModel.getPartsSet());
1265                 }
1266
1267                 exportPartsResolver.selectByPartsSet(partsSets);
1268         }
1269
1270         public int getSelectedCount() {
1271                 return presetTableModel.getSelectedCount();
1272         }
1273
1274         public String getDefaultPresetId() {
1275                 return presetTableModel.getDefaultPresetId();
1276         }
1277
1278         @Override
1279         public void setEnabled(boolean enabled) {
1280                 this.presetTable.setEnabled(enabled);
1281                 this.presetTableModel.setEnabled(enabled);
1282                 this.actSelectAll.setEnabled(enabled);
1283                 this.actDeselectAll.setEnabled(enabled);
1284                 super.setEnabled(enabled);
1285         }
1286 }
1287
1288
1289
1290 class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPartsSelectModel> {
1291
1292         private static final long serialVersionUID = 1L;
1293
1294         private static final String[] columnNames;
1295
1296         private static final int[] columnWidths;
1297
1298         private boolean enabled = true;
1299
1300         static {
1301                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
1302                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
1303
1304                 columnNames = new String[] {
1305                                 strings.getProperty("parts.column.selected"),
1306                                 strings.getProperty("parts.column.category"),
1307                                 strings.getProperty("parts.column.name"),
1308                                 strings.getProperty("parts.column.timestamp"),
1309                                 strings.getProperty("parts.column.author"),
1310                                 strings.getProperty("parts.column.version"),
1311                 };
1312
1313                 columnWidths = new int[] {
1314                                 Integer.parseInt(strings.getProperty("parts.column.selected.width")),
1315                                 Integer.parseInt(strings.getProperty("parts.column.category.width")),
1316                                 Integer.parseInt(strings.getProperty("parts.column.name.width")),
1317                                 Integer.parseInt(strings.getProperty("parts.column.timestamp.width")),
1318                                 Integer.parseInt(strings.getProperty("parts.column.author.width")),
1319                                 Integer.parseInt(strings.getProperty("parts.column.version.width")),
1320                 };
1321         }
1322
1323         public void adjustColumnModel(TableColumnModel columnModel, double scale) {
1324                 for (int idx = 0; idx < columnWidths.length; idx++) {
1325                         columnModel.getColumn(idx).setPreferredWidth((int)(columnWidths[idx] * scale));
1326                 }
1327         }
1328
1329         public int getColumnCount() {
1330                 return columnNames.length;
1331         }
1332
1333         @Override
1334         public String getColumnName(int column) {
1335                 return columnNames[column];
1336         }
1337
1338         public Object getValueAt(int rowIndex, int columnIndex) {
1339                 ExportPartsSelectModel partsSelectModel = getRow(rowIndex);
1340                 switch (columnIndex) {
1341                 case 0:
1342                         return Boolean.valueOf(partsSelectModel.isChecked() && enabled);
1343                 case 1:
1344                         return partsSelectModel.getPartsCategory().getLocalizedCategoryName();
1345                 case 2:
1346                         return partsSelectModel.getPartsName();
1347                 case 3:
1348                         Timestamp tm = partsSelectModel.getTimestamp();
1349                         if (tm != null) {
1350                                 return tm.toString();
1351                         }
1352                         return "";
1353                 case 4:
1354                         return partsSelectModel.getAuthor();
1355                 case 5:
1356                         return partsSelectModel.getVersion();
1357                 default:
1358                 }
1359                 return "";
1360         }
1361
1362         @Override
1363         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
1364                 ExportPartsSelectModel partsSelectModel = getRow(rowIndex);
1365                 switch (columnIndex) {
1366                 case 0:
1367                         partsSelectModel.setChecked(((Boolean) aValue).booleanValue());
1368                         break;
1369                 default:
1370                         return;
1371                 }
1372                 fireTableRowsUpdated(rowIndex, rowIndex);
1373         }
1374
1375         @Override
1376         public boolean isCellEditable(int rowIndex, int columnIndex) {
1377                 if (columnIndex == 0) {
1378                         return isEnabled();
1379                 }
1380                 return false;
1381         }
1382
1383         @Override
1384         public Class<?> getColumnClass(int columnIndex) {
1385                 switch (columnIndex) {
1386                 case 0:
1387                         return Boolean.class;
1388                 case 1:
1389                         return String.class;
1390                 case 2:
1391                         return String.class;
1392                 case 3:
1393                         return String.class;
1394                 default:
1395                 }
1396                 return String.class;
1397         }
1398
1399         public void sort() {
1400                 Collections.sort(elements);
1401                 fireTableDataChanged();
1402         }
1403
1404         public void sortByTimestamp() {
1405                 Collections.sort(elements, new Comparator<ExportPartsSelectModel>() {
1406                         public int compare(ExportPartsSelectModel o1, ExportPartsSelectModel o2) {
1407                                 int ret = 0;
1408                                 Timestamp t1 = o1.getTimestamp();
1409                                 Timestamp t2 = o2.getTimestamp();
1410                                 if (t1 == null || t2 == null) {
1411                                         if (t1 == null && t2 == null) {
1412                                                 ret = 0;
1413                                         } else if (t1 == null) {
1414                                                 ret = 1;
1415                                         } else {
1416                                                 ret = -1;
1417                                         }
1418                                 } else {
1419                                         ret = t2.compareTo(t1); // 逆順(日付の新しいもの順)
1420                                 }
1421                                 if (ret == 0) {
1422                                         ret = o1.compareTo(o2);
1423                                 }
1424                                 return ret;
1425                         }
1426                 });
1427                 fireTableDataChanged();
1428         }
1429
1430         public void selectAll() {
1431                 for (ExportPartsSelectModel model : elements) {
1432                         model.setChecked(true);
1433                 }
1434                 fireTableDataChanged();
1435         }
1436
1437         public void deselectAll() {
1438                 for (ExportPartsSelectModel model : elements) {
1439                         model.setChecked(false);
1440                 }
1441                 fireTableDataChanged();
1442         }
1443
1444         /**
1445          * 選択されているパーツイメージのマップを返す.<br>
1446          * @return 選択されているパーツイメージのマップ
1447          */
1448         public Map<PartsIdentifier, PartsSpec> getSelectedParts() {
1449                 HashMap<PartsIdentifier, PartsSpec> selectedPartsMap = new HashMap<PartsIdentifier, PartsSpec>();
1450                 for (ExportPartsSelectModel partsSelectModel : elements) {
1451                         if (partsSelectModel.isChecked() && isEnabled()) {
1452                                 selectedPartsMap.put(partsSelectModel.getPartsIdentifier(), partsSelectModel.getPartsSpec());
1453                         }
1454                 }
1455                 return selectedPartsMap;
1456         }
1457
1458         /**
1459          * パーツセットのコレクションを指定し、パーツセットの各パーツがすべてエクスポート対象になっているものだけを返す.<br>
1460          * @param partsSets パーツセットのリスト
1461          * @return 不足しているパーツセットと、その不足しているパーツリストを返す.
1462          */
1463         public Map<PartsSet, List<PartsIdentifier>> checkMissingPartsList(Collection<PartsSet> partsSets) {
1464                 if (partsSets == null) {
1465                         throw new IllegalArgumentException();
1466                 }
1467                 Map<PartsIdentifier, PartsSpec> selectedPartsMap = getSelectedParts();
1468                 HashMap<PartsSet, List<PartsIdentifier>> missingPartsMap = new HashMap<PartsSet, List<PartsIdentifier>>();
1469
1470                 for (PartsSet partsSet : partsSets) {
1471                         ArrayList<PartsIdentifier> missingPartss = new ArrayList<PartsIdentifier>();
1472                         for (List<PartsIdentifier> partsIdentifiers : partsSet.values()) {
1473                                 for (PartsIdentifier requirePartsIdentifier : partsIdentifiers) {
1474                                         if (!selectedPartsMap.containsKey(requirePartsIdentifier)) {
1475                                                 missingPartss.add(requirePartsIdentifier);
1476                                         }
1477                                 }
1478                         }
1479                         Collections.sort(missingPartss);
1480                         missingPartsMap.put(partsSet, missingPartss);
1481                 }
1482
1483                 return missingPartsMap;
1484         }
1485
1486         /**
1487          * パーツセットで使用されているパーツを選択状態にする.
1488          * @param partsSet パーツセットのコレクション
1489          */
1490         public void selectByPartsSet(Collection<PartsSet> partsSets) {
1491                 if (partsSets == null) {
1492                         throw new IllegalArgumentException();
1493                 }
1494                 HashSet<PartsIdentifier> requirePartsIdentifiers = new HashSet<PartsIdentifier>();
1495                 for (PartsSet partsSet : partsSets) {
1496                         for (List<PartsIdentifier> partsIdentifiers : partsSet.values()) {
1497                                 for (PartsIdentifier partsIdentifier : partsIdentifiers) {
1498                                         requirePartsIdentifiers.add(partsIdentifier);
1499                                 }
1500                         }
1501                 }
1502                 for (ExportPartsSelectModel partsSelectModel : elements) {
1503                         if (requirePartsIdentifiers.contains(partsSelectModel.getPartsIdentifier())) {
1504                                 partsSelectModel.setChecked(true);
1505                         }
1506                 }
1507                 fireTableDataChanged();
1508         }
1509
1510         /**
1511          * 選択されているパーツ数を返す.
1512          * @return パーツ数
1513          */
1514         public int getSelectedCount() {
1515                 int count = 0;
1516                 for (ExportPartsSelectModel partsSelectModel : elements) {
1517                         if (partsSelectModel.isChecked() && isEnabled()) {
1518                                 count++;
1519                         }
1520                 }
1521                 return count;
1522         }
1523
1524         public void setEnabled(boolean enabled) {
1525                 if (this.enabled != enabled) {
1526                         this.enabled = enabled;
1527                         fireTableDataChanged();
1528                 }
1529         }
1530
1531         public boolean isEnabled() {
1532                 return enabled;
1533         }
1534
1535         public void setCheck(int[] selRows, boolean checked) {
1536                 if (selRows == null || selRows.length == 0) {
1537                         return;
1538                 }
1539                 Arrays.sort(selRows);
1540                 for (int selRow : selRows) {
1541                         ExportPartsSelectModel rowModel = getRow(selRow);
1542                         rowModel.setChecked(checked);
1543                 }
1544                 fireTableRowsUpdated(selRows[0], selRows[selRows.length - 1]);
1545         }
1546 }
1547
1548
1549 class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
1550
1551         private boolean checked;
1552
1553         private PartsIdentifier partsIdentifier;
1554
1555         private PartsSpec partsSpec;
1556
1557         private Timestamp timestamp;
1558
1559         public ExportPartsSelectModel(PartsIdentifier partsIdentifier, PartsSpec partsSpec, boolean selected) {
1560                 if (partsIdentifier == null || partsSpec == null) {
1561                         throw new IllegalArgumentException();
1562                 }
1563                 this.partsIdentifier = partsIdentifier;
1564                 this.partsSpec = partsSpec;
1565                 this.checked = selected;
1566
1567                 long maxLastModified = partsSpec.getPartsFiles().lastModified();
1568                 if (maxLastModified > 0) {
1569                         timestamp = new Timestamp(maxLastModified);
1570                 } else {
1571                         timestamp = null;
1572                 }
1573         }
1574
1575         @Override
1576         public int hashCode() {
1577                 return partsIdentifier.hashCode();
1578         }
1579
1580         @Override
1581         public boolean equals(Object obj) {
1582                 if (obj == this) {
1583                         return true;
1584                 }
1585                 if (obj != null && obj instanceof ExportPartsSelectModel) {
1586                         ExportPartsSelectModel o = (ExportPartsSelectModel) obj;
1587                         return partsIdentifier.equals(o.partsIdentifier);
1588                 }
1589                 return false;
1590         }
1591
1592         public int compareTo(ExportPartsSelectModel o) {
1593                 int ret = (checked ? 0 : 1) - (o.checked ? 0 : 1); // 逆順
1594                 if (ret == 0) {
1595                         ret = partsIdentifier.compareTo(o.partsIdentifier);
1596                 }
1597                 return ret;
1598         }
1599
1600         public PartsIdentifier getPartsIdentifier() {
1601                 return this.partsIdentifier;
1602         }
1603
1604         public PartsSpec getPartsSpec() {
1605                 return this.partsSpec;
1606         }
1607
1608         public boolean isChecked() {
1609                 return checked;
1610         }
1611
1612         public void setChecked(boolean checked) {
1613                 this.checked = checked;
1614         }
1615
1616         public PartsCategory getPartsCategory() {
1617                 return this.partsIdentifier.getPartsCategory();
1618         }
1619
1620         public String getPartsName() {
1621                 return this.partsIdentifier.getLocalizedPartsName();
1622         }
1623
1624         public Timestamp getTimestamp() {
1625                 return timestamp == null ? null : (Timestamp) timestamp.clone();
1626         }
1627
1628         public String getAuthor() {
1629                 return partsSpec.getAuthor();
1630         }
1631
1632         public String getVersion() {
1633                 double version = partsSpec.getVersion();
1634                 if (version <= 0) {
1635                         return "";
1636                 }
1637                 return Double.toString(version);
1638         }
1639 }
1640
1641
1642 class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportPresetModel> {
1643
1644         private static final long serialVersionUID = 1L;
1645
1646         private static final String[] columnNames;
1647
1648         private static final int[] columnWidths;
1649
1650         private boolean enabled = true;
1651
1652         static {
1653                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
1654                                 .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
1655
1656                 columnNames = new String[] {
1657                                 strings.getProperty("preset.column.selected"),
1658                                 strings.getProperty("preset.column.default"),
1659                                 strings.getProperty("preset.column.name"),
1660                                 strings.getProperty("preset.column.missingparts"),
1661                 };
1662
1663                 columnWidths = new int[] {
1664                                 Integer.parseInt(strings.getProperty("preset.column.selected.width")),
1665                                 Integer.parseInt(strings.getProperty("preset.column.default.width")),
1666                                 Integer.parseInt(strings.getProperty("preset.column.name.width")),
1667                                 Integer.parseInt(strings.getProperty("preset.column.missingparts.width")),
1668                 };
1669         }
1670
1671         private String defaultPresetId;
1672
1673         public void adjustColumnModel(TableColumnModel columnModel, double scale) {
1674                 for (int idx = 0; idx < columnWidths.length; idx++) {
1675                         columnModel.getColumn(idx).setPreferredWidth((int)(columnWidths[idx] * scale));
1676                 }
1677         }
1678
1679         public int getColumnCount() {
1680                 return columnNames.length;
1681         }
1682
1683         @Override
1684         public String getColumnName(int column) {
1685                 return columnNames[column];
1686         }
1687
1688         public Object getValueAt(int rowIndex, int columnIndex) {
1689                 ExportPresetModel presetModel = getRow(rowIndex);
1690                 switch (columnIndex) {
1691                 case 0:
1692                         return Boolean.valueOf(presetModel.isSelected() && isEnabled());
1693                 case 1:
1694                         return Boolean.valueOf(presetModel.getPartsSet().getPartsSetId().equals(defaultPresetId) && isEnabled());
1695                 case 2:
1696                         return presetModel.getPartsSetName();
1697                 case 3:
1698                         StringBuilder buf = new StringBuilder();
1699                         for (PartsIdentifier partsIdentifier : presetModel.getMissingPartsIdentifiers()) {
1700                                 if (buf.length() > 0) {
1701                                         buf.append(", ");
1702                                 }
1703                                 buf.append(partsIdentifier.getLocalizedPartsName());
1704                         }
1705                         return buf.toString();
1706                 default:
1707                 }
1708                 return "";
1709         }
1710
1711         @Override
1712         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
1713                 ExportPresetModel presetModel = getRow(rowIndex);
1714                 switch (columnIndex) {
1715                 case 0:
1716                         if (((Boolean) aValue).booleanValue()) {
1717                                 presetModel.setSelected(true);
1718                         } else {
1719                                 presetModel.setSelected(false);
1720                                 if (presetModel.getPartsSet().getPartsSetId().equals(defaultPresetId)) {
1721                                         // 選択解除したものが既定のパーツセットであった場合、既定も解除する.
1722                                         defaultPresetId = null;
1723                                         fireTableRowsUpdated(rowIndex, rowIndex);
1724                                         return;
1725                                 }
1726                         }
1727                         break;
1728                 case 1:
1729                         if (((Boolean) aValue).booleanValue()) {
1730                                 defaultPresetId = presetModel.getPartsSet().getPartsSetId();
1731                                 presetModel.setSelected(true); // 既定のパーツセットにした場合は自動的にエクスポート対象にもする。
1732                                 fireTableDataChanged();
1733                                 return;
1734                         }
1735                 default:
1736                         return;
1737                 }
1738                 fireTableRowsUpdated(rowIndex, rowIndex);
1739         }
1740
1741         @Override
1742         public Class<?> getColumnClass(int columnIndex) {
1743                 switch (columnIndex) {
1744                 case 0:
1745                         return Boolean.class;
1746                 case 1:
1747                         return Boolean.class;
1748                 case 2:
1749                         return String.class;
1750                 case 3:
1751                         return String.class;
1752                 default:
1753                 }
1754                 return String.class;
1755         }
1756
1757         @Override
1758         public boolean isCellEditable(int rowIndex, int columnIndex) {
1759                 if (columnIndex == 0 || columnIndex == 1) {
1760                         return isEnabled();
1761                 }
1762                 return false;
1763         }
1764
1765         public void sort() {
1766                 Collections.sort(elements);
1767                 fireTableDataChanged();
1768         }
1769
1770         public void selectAll() {
1771                 for (ExportPresetModel model : elements) {
1772                         model.setSelected(true);
1773                 }
1774                 fireTableDataChanged();
1775         }
1776
1777         public void deselectAll() {
1778                 for (ExportPresetModel model : elements) {
1779                         model.setSelected(false);
1780                 }
1781                 fireTableDataChanged();
1782         }
1783
1784         /**
1785          * 選択されているパーツセットのリストを返す.<br>
1786          * なにもなければ空.<br>
1787          * @return 選択されているパーツセットのリスト
1788          */
1789         public List<PartsSet> getSelectedPresets() {
1790                 ArrayList<PartsSet> partsSets = new ArrayList<PartsSet>();
1791                 for (ExportPresetModel presetModel : elements) {
1792                         if (presetModel.isSelected() && isEnabled()) {
1793                                 PartsSet partsSet = presetModel.getPartsSet().clone();
1794                                 partsSet.setPresetParts(true);
1795                                 partsSets.add(partsSet);
1796                         }
1797                 }
1798                 return partsSets;
1799         }
1800
1801         public int getSelectedCount() {
1802                 int count = 0;
1803                 for (ExportPresetModel presetModel : elements) {
1804                         if (presetModel.isSelected() && isEnabled()) {
1805                                 count++;
1806                         }
1807                 }
1808                 return count;
1809         }
1810
1811         public String getDefaultPresetId() {
1812                 return defaultPresetId;
1813         }
1814
1815         /**
1816          * デフォルトのプリセットを設定する.<br>
1817          * @param defaultPresetId
1818          */
1819         public void setDefaultPresetId(String defaultPresetId) {
1820                 this.defaultPresetId = defaultPresetId;
1821         }
1822
1823         public void setEnabled(boolean enabled) {
1824                 if (this.enabled != enabled) {
1825                         this.enabled = enabled;
1826                         fireTableDataChanged();
1827                 }
1828         }
1829
1830         public boolean isEnabled() {
1831                 return enabled;
1832         }
1833 }
1834
1835 class ExportPresetModel implements Comparable<ExportPresetModel> {
1836
1837         private boolean selected;
1838
1839         private PartsSet partsSet;
1840
1841         private List<PartsIdentifier> missingPartsIdentifiers;
1842
1843         public ExportPresetModel(PartsSet partsSet, boolean selected) {
1844                 if (partsSet == null) {
1845                         throw new IllegalArgumentException();
1846                 }
1847                 this.partsSet = partsSet;
1848                 this.selected = selected;
1849         }
1850
1851         @Override
1852         public int hashCode() {
1853                 return partsSet.hashCode();
1854         }
1855
1856         @Override
1857         public boolean equals(Object obj) {
1858                 if (obj == this) {
1859                         return true;
1860                 }
1861                 if (obj != null && obj instanceof ExportPresetModel) {
1862                         ExportPresetModel o = (ExportPresetModel) obj;
1863                         return partsSet.equals(o.partsSet);
1864                 }
1865                 return false;
1866         }
1867
1868         public int compareTo(ExportPresetModel o) {
1869                 int ret = (selected ? 0 : 1) - (o.selected ? 0 : 1);
1870                 if (ret == 0) {
1871                         ret = getPartsSetName().compareTo(o.getPartsSetName());
1872                 }
1873                 return ret;
1874         }
1875
1876         public String getPartsSetName() {
1877                 String name = partsSet.getLocalizedName();
1878                 return name == null ? "" : name;
1879         }
1880
1881         public boolean isPresetParts() {
1882                 return partsSet.isPresetParts();
1883         }
1884
1885         public boolean isSelected() {
1886                 return selected;
1887         }
1888
1889         public void setSelected(boolean selected) {
1890                 this.selected = selected;
1891         }
1892
1893         public PartsSet getPartsSet() {
1894                 return partsSet;
1895         }
1896
1897         public void setMissingPartsIdentifiers(
1898                         List<PartsIdentifier> missingPartsIdentifiers) {
1899                 this.missingPartsIdentifiers = Collections.unmodifiableList(missingPartsIdentifiers);
1900         }
1901
1902         public List<PartsIdentifier> getMissingPartsIdentifiers() {
1903                 if (missingPartsIdentifiers == null) {
1904                         return Collections.emptyList();
1905                 }
1906                 return missingPartsIdentifiers;
1907         }
1908
1909 }