OSDN Git Service

プロファイル作成時にデフォルトのキャラクターデータのダウンロードを問い合わせる
authorseraphy <seraphy@users.osdn.me>
Tue, 5 Feb 2019 17:02:02 +0000 (02:02 +0900)
committerseraphy <seraphy@users.osdn.me>
Tue, 5 Feb 2019 17:02:02 +0000 (02:02 +0900)
および、HiDPI対応、リソース文字列漏れ、リファクタなど。

27 files changed:
src/main/java/charactermanaj/model/AppConfig.java
src/main/java/charactermanaj/model/CharacterData.java
src/main/java/charactermanaj/model/IndependentWorkingSet.java
src/main/java/charactermanaj/model/WorkingSet.java
src/main/java/charactermanaj/model/io/CharacterDataFileReaderWriterFactory.java
src/main/java/charactermanaj/model/io/WorkingSetPersist.java
src/main/java/charactermanaj/model/io/WorkingSetXMLReader.java
src/main/java/charactermanaj/model/io/WorkingSetXMLWriter.java
src/main/java/charactermanaj/ui/ExportWizardDialog.java
src/main/java/charactermanaj/ui/ImportWizardDialog.java
src/main/java/charactermanaj/ui/MainFrame.java
src/main/java/charactermanaj/ui/PartsRandomChooserDialog.java
src/main/java/charactermanaj/ui/ProfileListManager.java
src/main/java/charactermanaj/ui/ProfileSelectorDialog.java
src/main/java/charactermanaj/ui/SelectCharatersDirDialog.java
src/main/java/charactermanaj/ui/progress/WorkerWithProgessDialog.java
src/main/java/charactermanaj/util/DownloadUtils.java [new file with mode: 0644]
src/main/resources/languages/appconfigdialog.xml
src/main/resources/languages/appconfigdialog_ja.xml
src/main/resources/languages/appconfigdialog_zh.xml
src/main/resources/languages/importwizdialog.xml
src/main/resources/languages/importwizdialog_ja.xml
src/main/resources/languages/importwizdialog_zh.xml
src/main/resources/languages/mainframe.xml
src/main/resources/languages/mainframe_ja.xml
src/main/resources/languages/mainframe_zh.xml
src/main/resources/template/character3.xml

index 22217c9..43c3e1f 100644 (file)
@@ -1559,4 +1559,37 @@ public final class AppConfig {
                        propChangeSupport.firePropertyChange(USE_RLE_COMPRESSION_FOR_PSD, old, useRLECompressionForPSD);
                }
        }
+
+       private String impersonateUserAgent = "curl/7.58.0";
+
+       private static final String IMPERSONATE_USER_AGENT = "impersonateUserAgent";
+
+       public String getImpersonateUserAgent() {
+               return impersonateUserAgent;
+       }
+
+       public void setImpersonateUserAgent(String impersonateUserAgent) {
+               String old = this.impersonateUserAgent;
+               this.impersonateUserAgent = impersonateUserAgent;
+               if (old == null ? impersonateUserAgent != null : !old.equals(impersonateUserAgent)) {
+                       this.impersonateUserAgent = impersonateUserAgent;
+                       propChangeSupport.firePropertyChange(IMPERSONATE_USER_AGENT, old, impersonateUserAgent);
+               }
+       }
+
+       private boolean deleteDownloadFileOnExit = true;
+
+       private static final String DELETE_DOWNLOAD_FILE_ON_EXIT = "deleteDownloadFileOnExit";
+
+       public boolean isDeleteDownloadFileOnExit() {
+               return deleteDownloadFileOnExit;
+       }
+
+       public void setDeleteDownloadFileOnExit(boolean deleteDownloadFileOnExit) {
+               boolean old = this.deleteDownloadFileOnExit;
+               if (old != deleteDownloadFileOnExit) {
+                       this.deleteDownloadFileOnExit = deleteDownloadFileOnExit;
+                       propChangeSupport.firePropertyChange(DELETE_DOWNLOAD_FILE_ON_EXIT, old, deleteDownloadFileOnExit);
+               }
+       }
 }
index 9d48792..462f563 100644 (file)
@@ -770,6 +770,21 @@ public class CharacterData implements PartsSpecResolver {
        }
 
        /**
+        * 全カテゴリー中のパーツデータの個数を取得する。
+        * @return パーツの個数
+        */
+       public int getPartsCount() {
+               int cnt = 0;
+               for (PartsCategory category : partsCategories.asList()) {
+                       Map<PartsIdentifier, PartsSpec> partsMap = images.get(category);
+                       if (partsMap != null) {
+                               cnt += partsMap.size();
+                       }
+               }
+               return cnt;
+       }
+
+       /**
         * パーツデータをリロードする.<br>
         * ロード時に使用したローダーを使ってパーツを再ロードします.<br>
         * まだ一度もロードしていない場合はIllegalStateException例外が発生します.<br>
index a97ed12..96818ad 100644 (file)
@@ -80,6 +80,11 @@ public class IndependentWorkingSet {
         */
        private Rectangle windowRect;
 
+       /**
+        * ダウンロード不要フラグ
+        */
+       private boolean noNeedDataDownload;
+
 
        public void setCharacterDocBase(URI characterDocBase) {
                this.characterDocBase = characterDocBase;
@@ -178,6 +183,14 @@ public class IndependentWorkingSet {
                this.lastUsePresetParts = lastUsePresetParts;
        }
 
+       public boolean isNoNeedDataDownload() {
+               return noNeedDataDownload;
+       }
+
+       public void setNoNeedDataDownload(boolean noNeedDataDownload) {
+               this.noNeedDataDownload = noNeedDataDownload;
+       }
+
        /**
         * キャラクターデータを指定して、指定されたキャラクターデータ上のインスタンスと関連づけられた
         * カテゴリおよびパーツ名などのインスタンスで構成されるパーツ識別名とカラー情報を、 引数で指定したマップに出力する.
@@ -229,6 +242,7 @@ public class IndependentWorkingSet {
                buf.append(", currentPartsSet=").append(currentPartsSet);
                buf.append(", lastUsedSaveDir=").append(lastUsedSaveDir);
                buf.append(", lastUsedExportDir=").append(lastUsedExportDir);
+               buf.append(", noNeedDataDownload=").append(noNeedDataDownload);
                buf.append(")");
                return buf.toString();
        }
index 3f5c02b..0ed24e8 100644 (file)
@@ -58,6 +58,11 @@ public class WorkingSet {
         */
        private Rectangle windowRect;
 
+       /**
+        * データのダウンロードが不要である
+        */
+       private boolean noNeedDataDownload;
+
        public void setCharacterDataRev(String characterDataRev) {
                this.characterDataRev = characterDataRev;
        }
@@ -200,6 +205,14 @@ public class WorkingSet {
                this.windowRect = windowRect;
        }
 
+       public boolean isNoNeedDataDownload() {
+               return noNeedDataDownload;
+       }
+
+       public void setNoNeedDataDownload(boolean noNeedDataDownload) {
+               this.noNeedDataDownload = noNeedDataDownload;
+       }
+
        @Override
        public String toString() {
                return "docBase:" + characterDocBase + "/rev:" + characterDataRev;
index 8ead56f..0338198 100644 (file)
@@ -12,11 +12,11 @@ public class CharacterDataFileReaderWriterFactory {
        private CharacterDataFileReaderWriterFactory() {
                super();
        }
-       
+
        public static CharacterDataFileReaderWriterFactory getInstance() {
                return singleton;
        }
-       
+
        /**
         * ファイルの拡張子に応じてzip/cmj形式でのライターを構築して帰します.<br>
         * 拡張子がjarとcmjは同じ意味で、ともにjarファイル形式となります.<br>
@@ -29,7 +29,7 @@ public class CharacterDataFileReaderWriterFactory {
                if (outfile == null) {
                        throw new IllegalArgumentException();
                }
-               
+
                String name = outfile.getName().toLowerCase();
                if (name.endsWith(".jar") || name.endsWith(".cmj")) {
                        return new CharacterDataJarFileWriter(outfile);
@@ -37,10 +37,10 @@ public class CharacterDataFileReaderWriterFactory {
                } else if (name.endsWith(".zip")) {
                        return new CharacterDataZipFileWriter(outfile);
                }
-               
+
                throw new IOException("unsupported file type: " + name);
        }
-       
+
        public CharacterDataArchiveFile openArchive(URI archiveFile) throws IOException {
                if (archiveFile == null) {
                        throw new IllegalArgumentException();
@@ -55,8 +55,8 @@ public class CharacterDataFileReaderWriterFactory {
                // file以外は現在のところサポートしない。
                throw new UnsupportedOperationException();
        }
-       
-       
+
+
        public CharacterDataArchiveFile openArchive(File archiveFile) throws IOException {
                if (archiveFile == null) {
                        throw new IllegalArgumentException();
@@ -66,7 +66,7 @@ public class CharacterDataFileReaderWriterFactory {
                        // ディレクトリの場合
                        return new CharacterDataDirectoryFile(archiveFile);
                }
-               
+
                // zipまたはcmjファイルの場合
                String name = archiveFile.getName().toLowerCase();
                if (name.endsWith(".jar") || name.endsWith(".cmj")) {
@@ -75,8 +75,20 @@ public class CharacterDataFileReaderWriterFactory {
                } else if (name.endsWith(".zip")) {
                        return new CharacterDataZipArchiveFile(archiveFile);
                }
-               
+
                throw new IOException("unsupported file type: " + name);
        }
-       
+
+       /**
+        * ファイルの拡張子からアーカイブとしてサポートされているタイプであるか判断する
+        * @param fileName ファイル名
+        * @return サポートされている場合はtrue、そうでなければfalse
+        */
+       public boolean isSupportedFile(String fileName) {
+               if (fileName != null) {
+                       fileName = fileName.toLowerCase();
+                       return fileName.endsWith(".zip") || fileName.endsWith(".jar") || fileName.endsWith(".cmj");
+               }
+               return false;
+       }
 }
index c73048c..a4310ba 100644 (file)
@@ -210,6 +210,8 @@ public class WorkingSetPersist {
                ws.setZoomFactor(workingSet2.getZoomFactor());
                ws.setViewPosition(workingSet2.getViewPosition());
                ws.setWindowRect(workingSet2.getWindowRect());
+               
+               ws.setNoNeedDataDownload(workingSet2.isNoNeedDataDownload());
 
                return ws;
        }
index 63a4f20..95c6871 100644 (file)
@@ -229,6 +229,15 @@ public class WorkingSetXMLReader {
                                break; // 最初の一要素のみ
                        }
 
+                       // ダウンロード不要フラグ
+                       boolean noNeedDataDownload = false;
+                       for (Element noNeedDataDownloadElm : getChildElements(docElm,
+                                       "noNeedDataDownload")) {
+                               noNeedDataDownload = Boolean.parseBoolean(noNeedDataDownloadElm.getTextContent());
+                               break; // 最初の一要素のみ
+                       }
+                       workingSet.setNoNeedDataDownload(noNeedDataDownload);
+
                        return workingSet;
 
                } catch (RuntimeException ex) {
index d1989a8..c75d27e 100644 (file)
@@ -206,6 +206,13 @@ public class WorkingSetXMLWriter {
                // ズーム情報等
                root.appendChild(writeViewSettings(doc, ws.getZoomFactor(), ws.getViewPosition(), ws.getWindowRect()));
 
+               // データダウンロード不要フラグ
+               if (ws.isNoNeedDataDownload()) {
+                       Element noNeedDataDownloadElm = doc.createElement("noNeedDataDownload");
+                       noNeedDataDownloadElm.setTextContent(Boolean.toString(ws.isNoNeedDataDownload()));
+                       root.appendChild(noNeedDataDownloadElm);
+               }
+
                doc.appendChild(root);
                return doc;
        }
index b337f18..ebd8939 100644 (file)
@@ -156,12 +156,15 @@ public class ExportWizardDialog extends JDialog {
 
                // メインパネル
 
+               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
+
                Container contentPane = getContentPane();
                contentPane.setLayout(new BorderLayout());
 
                final JPanel mainPanel = new JPanel();
                mainPanel.setBorder(BorderFactory.createEtchedBorder());
-               final CardLayout mainPanelLayout = new CardLayout(5, 5);
+               int mergin = (int)(5 * scaleSupport.getManualScaleX());
+               final CardLayout mainPanelLayout = new CardLayout(mergin, mergin);
                mainPanel.setLayout(mainPanelLayout);
                contentPane.add(mainPanel, BorderLayout.CENTER);
 
@@ -212,14 +215,14 @@ public class ExportWizardDialog extends JDialog {
                };
 
                // panel1 : basic
-               this.basicPanel = new ExportInformationPanel(characterData, samplePicture);
+               this.basicPanel = new ExportInformationPanel(characterData, samplePicture, scaleSupport);
                this.basicPanel.addComponentListener(componentListener);
                this.basicPanel.addChangeListener(actChangeValue);
                this.basicPanel.addChangeListener(actPanelEnabler);
                mainPanel.add(this.basicPanel, "basicPanel");
 
                // panel2 : panelSelectPanel
-               this.partsSelectPanel = new ExportPartsSelectPanel(characterData);
+               this.partsSelectPanel = new ExportPartsSelectPanel(characterData, scaleSupport);
                this.partsSelectPanel.addComponentListener(componentListener);
                this.partsSelectPanel.addChangeListener(actChangeValue);
                mainPanel.add(this.partsSelectPanel, "partsSelectPanel");
@@ -229,7 +232,8 @@ public class ExportWizardDialog extends JDialog {
                                this.partsSelectPanel,
                                this.basicPanel,
                                characterData.getPartsSets().values(),
-                               characterData.getDefaultPartsSetId());
+                               characterData.getDefaultPartsSetId(),
+                               scaleSupport);
                this.presetSelectPanel.addComponentListener(componentListener);
                this.presetSelectPanel.addChangeListener(actChangeValue);
                mainPanel.add(this.presetSelectPanel, "presetSelectPanel");
@@ -238,7 +242,8 @@ public class ExportWizardDialog extends JDialog {
                // button panel
 
                JPanel btnPanel = new JPanel();
-               btnPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 45));
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               btnPanel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap * 15)); // 3, 3, 3, 45
                GridBagLayout btnPanelLayout = new GridBagLayout();
                btnPanel.setLayout(btnPanelLayout);
 
@@ -256,7 +261,7 @@ public class ExportWizardDialog extends JDialog {
                gbc.fill = GridBagConstraints.BOTH;
                gbc.ipadx = 0;
                gbc.ipady = 0;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.weightx = 1.;
                gbc.weighty = 0.;
                btnPanel.add(Box.createHorizontalGlue(), gbc);
@@ -297,13 +302,8 @@ public class ExportWizardDialog extends JDialog {
 
                // 表示
 
-               Dimension dim = new Dimension(500, 500);
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               if (scaleSupport != null) {
-                       // HiDpi環境でのスケールを考慮したウィンドウサイズに補正する
-                       dim = scaleSupport.manualScaled(dim);
-               }
-               setSize(dim);
+               // HiDpi環境でのスケールを考慮したウィンドウサイズに補正する
+               setSize(scaleSupport.manualScaled(new Dimension(500, 500)));
                setLocationRelativeTo(parent);
 
                mainPanelLayout.first(mainPanel);
@@ -587,7 +587,8 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
 
 
 
-       protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture) {
+       protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture,
+                       ScaleSupport scaleSupport) {
                if (characterData == null) {
                        throw new IllegalArgumentException();
                }
@@ -603,8 +604,9 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                setLayout(basicPanelLayout);
 
                JPanel contentsSpecPanel = new JPanel();
+               int mergin = (int)(5 * scaleSupport.getManualScaleX());
                contentsSpecPanel.setBorder(BorderFactory.createCompoundBorder(
-                               BorderFactory.createEmptyBorder(5, 5, 5, 5),
+                               BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
                                BorderFactory.createTitledBorder(strings.getProperty("basic.contentsSpec"))));
                BoxLayout contentsSpecPanelLayout = new BoxLayout(contentsSpecPanel, BoxLayout.PAGE_AXIS);
                contentsSpecPanel.setLayout(contentsSpecPanelLayout);
@@ -625,11 +627,11 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                ///
 
                JPanel commentPanel = new JPanel();
-               Dimension archiveInfoPanelMinSize = new Dimension(300, 200);
+               Dimension archiveInfoPanelMinSize = scaleSupport.manualScaled(new Dimension(300, 200));
                commentPanel.setMinimumSize(archiveInfoPanelMinSize);
                commentPanel.setPreferredSize(archiveInfoPanelMinSize);
                commentPanel.setBorder(BorderFactory.createCompoundBorder(
-                               BorderFactory.createEmptyBorder(5, 5, 5, 5),
+                               BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
                                BorderFactory.createTitledBorder(strings.getProperty("basic.comment"))));
                GridBagLayout commentPanelLayout = new GridBagLayout();
                commentPanel.setLayout(commentPanelLayout);
@@ -640,7 +642,8 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                gbc.gridwidth = 1;
                gbc.weightx = 0.;
                gbc.weighty = 0.;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.anchor = GridBagConstraints.WEST;
                gbc.fill = GridBagConstraints.BOTH;
                commentPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc);
@@ -674,7 +677,7 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
 
                sampleImagePanel = new SamplePicturePanel(samplePicture);
                sampleImagePanel.setBorder(BorderFactory.createCompoundBorder(
-                               BorderFactory.createEmptyBorder(5, 5, 5, 5),
+                               BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
                                BorderFactory.createTitledBorder(strings.getProperty("basic.sampleImage"))));
 
 
@@ -809,7 +812,7 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
 
        private Action actSortByTimestamp;
 
-       protected ExportPartsSelectPanel(PartsSpecResolver partsSpecResolver) {
+       protected ExportPartsSelectPanel(PartsSpecResolver partsSpecResolver, ScaleSupport scaleSupport) {
                if (partsSpecResolver == null) {
                        throw new IllegalArgumentException();
                }
@@ -854,7 +857,6 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                };
                partsTable.setShowGrid(true);
                partsTable.setGridColor(appConfig.getGridColor());
-               partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                partsTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
                partsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
                partsTable.setRowSelectionAllowed(true);
@@ -862,6 +864,10 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                // 行の高さをフォントの高さにする
                partsTable.setRowHeight((int)(partsTable.getFont().getSize() * 1.2));
 
+               // 列幅を調整する
+               partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+               partsTableModel.adjustColumnModel(partsTable.getColumnModel(), scaleSupport.getManualScaleX());
+
                Action actPartsSetCheck = new AbstractAction(strings.getProperty("parts.popup.check")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
@@ -923,7 +929,8 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                gbc.gridwidth = 1;
                gbc.anchor = GridBagConstraints.EAST;
                gbc.fill = GridBagConstraints.BOTH;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.ipadx = 0;
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
@@ -952,13 +959,6 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                add(btnPanel, BorderLayout.SOUTH);
        }
 
-       @Override
-       public void addNotify() {
-               super.addNotify();
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               partsTableModel.adjustColumnModel(partsTable.getColumnModel(), scaleSupport.getManualScaleX());
-       }
-
        protected void loadPartsInfo(PartsSpecResolver partsSpecResolver) {
                partsTableModel.clear();
                for (PartsCategory partsCategory : partsSpecResolver.getPartsCategories()) {
@@ -1049,7 +1049,8 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
        protected ExportPresetSelectPanel(
                        final ExportPartsResolver exportPartsResolver,
                        final ExportInformationResolver exportInfoResolver,
-                       Collection<PartsSet> partsSets, String defaultPresetId) {
+                       Collection<PartsSet> partsSets, String defaultPresetId,
+                       ScaleSupport scaleSupport) {
 
                this.exportPartsResolver = exportPartsResolver;
 
@@ -1123,6 +1124,10 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                // 行の高さをフォントの高さにする
                presetTable.setRowHeight((int)(presetTable.getFont().getSize() * 1.2));
 
+               // 列幅を調整する
+               presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+               presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
+
                final Action actSelectUsedParts = new AbstractAction(
                                strings.getProperty("preset.popup.selectUsedParts")) {
                        private static final long serialVersionUID = 1L;
@@ -1137,8 +1142,6 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                presetTable.setComponentPopupMenu(popupMenu);
 
 
-               presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
-
                add(new JScrollPane(presetTable), BorderLayout.CENTER);
 
                actSelectAll = new AbstractAction(strings.getProperty("parts.btn.selectAll")) {
@@ -1173,7 +1176,8 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                gbc.gridwidth = 1;
                gbc.anchor = GridBagConstraints.EAST;
                gbc.fill = GridBagConstraints.BOTH;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.ipadx = 0;
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
@@ -1197,13 +1201,6 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                add(btnPanel, BorderLayout.SOUTH);
        }
 
-       @Override
-       public void addNotify() {
-               super.addNotify();
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
-       }
-
        protected void loadPresetInfo(Collection<PartsSet> partsSets, String defaultPresetId) {
                presetTableModel.clear();
                for (PartsSet orgPartsSet : partsSets) {
index a67a6be..6fe34b6 100644 (file)
@@ -24,6 +24,7 @@ import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.awt.image.BufferedImage;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.URI;
 import java.sql.Timestamp;
@@ -93,6 +94,7 @@ import charactermanaj.model.PartsSet;
 import charactermanaj.model.PartsSpec;
 import charactermanaj.model.io.AbstractCharacterDataArchiveFile.CategoryLayerPair;
 import charactermanaj.model.io.AbstractCharacterDataArchiveFile.PartsImageContent;
+import charactermanaj.model.io.CharacterDataFileReaderWriterFactory;
 import charactermanaj.model.io.CharacterDataPersistent;
 import charactermanaj.model.io.ImportModel;
 import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;
@@ -102,6 +104,8 @@ import charactermanaj.ui.progress.WorkerException;
 import charactermanaj.ui.progress.WorkerWithProgessDialog;
 import charactermanaj.ui.util.FileDropTarget;
 import charactermanaj.ui.util.ScaleSupport;
+import charactermanaj.util.DownloadUtils;
+import charactermanaj.util.DownloadUtils.HeadResponse;
 import charactermanaj.util.ErrorMessageHelper;
 import charactermanaj.util.LocalizedResourcePropertyLoader;
 
@@ -172,14 +176,10 @@ public class ImportWizardDialog extends JDialog {
         *            親フレーム
         * @param current
         *            更新対象となる現在のプロファイル(新規インポートの場合はnull)
-        * @param initFiles
-        *            アーカイブファィルまたはディレクトリの初期選択、なければnullまたは空
         */
-       public ImportWizardDialog(JFrame parent, CharacterData current, List<File> initFiles) {
+       public ImportWizardDialog(JFrame parent, CharacterData current) {
                super(parent, true);
                initComponent(parent, current);
-
-               importFileSelectPanel.setSelectFile(initFiles);
        }
 
        /**
@@ -196,6 +196,18 @@ public class ImportWizardDialog extends JDialog {
        }
 
        /**
+        * @param initFiles
+        *            アーカイブファィルまたはディレクトリの初期選択、なければnullまたは空
+        */
+       public void initSelectFile(File initFile) {
+               importFileSelectPanel.setSelectFile(initFile);
+       }
+
+       public void initSelectURL(String url) {
+               importFileSelectPanel.initSelectURL(url);
+       }
+
+       /**
         * ウィザードダイアログのコンポーネントを初期化します.<br>
         * currentがnullの場合は新規インポート、そうでない場合は更新インポートとります。
         *
@@ -227,12 +239,15 @@ public class ImportWizardDialog extends JDialog {
 
                // メインパネル
 
+               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
+
                Container contentPane = getContentPane();
                contentPane.setLayout(new BorderLayout());
 
                final JPanel mainPanel = new JPanel();
                mainPanel.setBorder(BorderFactory.createEtchedBorder());
-               this.mainPanelLayout = new CardLayout(5, 5);
+               int gap = (int)(5 * scaleSupport.getManualScaleX());
+               this.mainPanelLayout = new CardLayout(gap, gap);
                mainPanel.setLayout(mainPanelLayout);
                contentPane.add(mainPanel, BorderLayout.CENTER);
 
@@ -291,25 +306,25 @@ public class ImportWizardDialog extends JDialog {
                };
 
                // ImportFileSelectPanel
-               this.importFileSelectPanel = new ImportFileSelectPanel();
+               this.importFileSelectPanel = new ImportFileSelectPanel(scaleSupport);
                this.importFileSelectPanel.addComponentListener(componentListener);
                this.importFileSelectPanel.addChangeListener(changeListener);
                mainPanel.add(this.importFileSelectPanel, ImportFileSelectPanel.PANEL_NAME);
 
                // ImportTypeSelectPanel
-               this.importTypeSelectPanel = new ImportTypeSelectPanel();
+               this.importTypeSelectPanel = new ImportTypeSelectPanel(scaleSupport);
                this.importTypeSelectPanel.addComponentListener(componentListener);
                this.importTypeSelectPanel.addChangeListener(changeListener);
                mainPanel.add(this.importTypeSelectPanel, ImportTypeSelectPanel.PANEL_NAME);
 
                // ImportPartsSelectPanel
-               this.importPartsSelectPanel = new ImportPartsSelectPanel();
+               this.importPartsSelectPanel = new ImportPartsSelectPanel(scaleSupport);
                this.importPartsSelectPanel.addComponentListener(componentListener);
                this.importPartsSelectPanel.addChangeListener(changeListener);
                mainPanel.add(this.importPartsSelectPanel, ImportPartsSelectPanel.PANEL_NAME);
 
                // ImportPresetSelectPanel
-               this.importPresetSelectPanel = new ImportPresetSelectPanel();
+               this.importPresetSelectPanel = new ImportPresetSelectPanel(scaleSupport);
                this.importPresetSelectPanel.addComponentListener(componentListener);
                this.importPresetSelectPanel.addChangeListener(changeListener);
                mainPanel.add(this.importPresetSelectPanel, ImportPresetSelectPanel.PANEL_NAME);
@@ -318,7 +333,8 @@ public class ImportWizardDialog extends JDialog {
                // button panel
 
                JPanel btnPanel = new JPanel();
-               btnPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 45));
+               int mergin = (int)(3 * scaleSupport.getManualScaleX());
+               btnPanel.setBorder(BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin * 15)); // 3,3,3,45
                GridBagLayout btnPanelLayout = new GridBagLayout();
                btnPanel.setLayout(btnPanelLayout);
 
@@ -336,7 +352,7 @@ public class ImportWizardDialog extends JDialog {
                gbc.fill = GridBagConstraints.BOTH;
                gbc.ipadx = 0;
                gbc.ipady = 0;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               gbc.insets = new Insets(mergin, mergin, mergin, mergin);
                gbc.weightx = 1.;
                gbc.weighty = 0.;
                btnPanel.add(Box.createHorizontalGlue(), gbc);
@@ -378,11 +394,8 @@ public class ImportWizardDialog extends JDialog {
                // 表示
 
                Dimension dim = new Dimension(500, 500);
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               if (scaleSupport != null) {
-                       // HiDpi環境でのスケールを考慮したウィンドウサイズに補正する
-                       dim = scaleSupport.manualScaled(dim);
-               }
+               // HiDpi環境でのスケールを考慮したウィンドウサイズに補正する
+               dim = scaleSupport.manualScaled(dim);
                setSize(dim);
                setLocationRelativeTo(parent);
 
@@ -778,11 +791,21 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
        private Action actChooseDirectory;
 
 
+       /**
+        * URLを指定してインポート
+        */
+       private JRadioButton radioURL;
+
+       /**
+        * URL入力ボックス
+        */
+       private JTextField txtURL;
+
 
        /* 以下、対象ファイルの読み取り結果 */
 
 
-       public ImportFileSelectPanel() {
+       public ImportFileSelectPanel(ScaleSupport scaleSupport) {
                setLayout(new BorderLayout());
 
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
@@ -805,9 +828,11 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
 
                txtArchiveFile = new JTextField();
                txtDirectory = new JTextField();
+               txtURL = new JTextField();
 
                txtArchiveFile.getDocument().addDocumentListener(documentListener);
                txtDirectory.getDocument().addDocumentListener(documentListener);
+               txtURL.getDocument().addDocumentListener(documentListener);
 
                actChooseFile = new AbstractAction(strings.getProperty("browse")) {
                        private static final long serialVersionUID = 1L;
@@ -830,6 +855,7 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
 
                radioArchiveFile = new JRadioButton(strings.getProperty("importingArchiveFile"));
                radioDirectory = new JRadioButton(strings.getProperty("importingDirectory"));
+               radioURL = new JRadioButton(strings.getProperty("importingURL"));
 
                ChangeListener radioChangeListener = new ChangeListener() {
                        public void stateChanged(ChangeEvent e) {
@@ -839,27 +865,32 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                };
                radioArchiveFile.addChangeListener(radioChangeListener);
                radioDirectory.addChangeListener(radioChangeListener);
+               radioURL.addChangeListener(radioChangeListener);
 
                ButtonGroup btnGroup = new ButtonGroup();
                btnGroup.add(radioArchiveFile);
                btnGroup.add(radioDirectory);
+               btnGroup.add(radioURL);
 
                // アーカイブからのインポートをデフォルトとする
                radioArchiveFile.setSelected(true);
 
                GridBagConstraints gbc = new GridBagConstraints();
 
+               gbc.ipadx = 0;
+               gbc.ipady = 0;
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
+               gbc.anchor = GridBagConstraints.WEST;
+               gbc.fill = GridBagConstraints.BOTH;
+
+               // アーカイブ
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.weightx = 1.;
                gbc.weighty = 0.;
                gbc.gridheight = 1;
                gbc.gridwidth = 3;
-               gbc.ipadx = 0;
-               gbc.ipady = 0;
-               gbc.insets = new Insets(3, 3, 3, 3);
-               gbc.anchor = GridBagConstraints.WEST;
-               gbc.fill = GridBagConstraints.BOTH;
                fileChoosePanel.add(radioArchiveFile, gbc);
 
                gbc.gridx = 0;
@@ -881,6 +912,7 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                gbc.weightx = 0.;
                fileChoosePanel.add(new JButton(actChooseFile), gbc);
 
+               // ディレクトり
                gbc.gridx = 0;
                gbc.gridy = 2;
                gbc.ipadx = 0;
@@ -907,8 +939,31 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                gbc.weightx = 0.;
                fileChoosePanel.add(new JButton(actChooseDirectory), gbc);
 
+               // URL
+               gbc.gridx = 0;
+               gbc.gridy = 4;
+               gbc.weightx = 1.;
+               gbc.weighty = 0.;
+               gbc.gridheight = 1;
+               gbc.gridwidth = 3;
+               fileChoosePanel.add(radioURL, gbc);
+
                gbc.gridx = 0;
                gbc.gridy = 4;
+               gbc.gridwidth = 1;
+               gbc.ipadx = 45;
+               gbc.weightx = 0;
+               fileChoosePanel.add(Box.createHorizontalGlue(), gbc);
+
+               gbc.gridx = 1;
+               gbc.gridy = 5;
+               gbc.ipadx = 0;
+               gbc.weightx = 1.;
+               fileChoosePanel.add(txtURL, gbc);
+
+               // パディング
+               gbc.gridx = 0;
+               gbc.gridy = 6;
                gbc.ipadx = 0;
                gbc.gridwidth = 3;
                gbc.weightx = 1.;
@@ -924,7 +979,8 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                                if (dropFiles == null || dropFiles.isEmpty()) {
                                        return;
                                }
-                               setSelectFile(dropFiles);
+                               File dropFile = dropFiles.get(0);
+                               setSelectFile(dropFile);
                        }
 
                        @Override
@@ -943,19 +999,15 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
         * @param dropFile
         *            アーカイブファイルまたはディレクトリ、もしくはnull
         */
-       public void setSelectFile(List<File> dropFiles) {
-
-               File dropFile = null;
-               if (dropFiles != null && dropFiles.size() > 0) {
-                       dropFile = dropFiles.get(0);
-               }
-
+       public void setSelectFile(File dropFile) {
                if (dropFile == null) {
                        // 選択なしの場合
                        txtDirectory.setText("");
                        txtArchiveFile.setText("");
+                       txtURL.setText("");
                        radioDirectory.setSelected(false);
                        radioArchiveFile.setSelected(false);
+                       radioURL.setSelected(false);
 
                } else if (dropFile.isDirectory()) {
                        // ディレクトリの場合
@@ -969,15 +1021,39 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                }
        }
 
+       /**
+        * URL入力を選択状態とする
+        * @param url URL
+        */
+       public void initSelectURL(String url) {
+               if (url != null && (url.startsWith("http://") || url.startsWith("https://"))) {
+                       // URL指定の場合
+                       txtURL.setText(url);
+                       radioURL.setSelected(true);
+
+               } else {
+                       // 選択なしの場合
+                       txtDirectory.setText("");
+                       txtArchiveFile.setText("");
+                       txtURL.setText("");
+                       radioDirectory.setSelected(false);
+                       radioArchiveFile.setSelected(false);
+                       radioURL.setSelected(false);
+               }
+       }
+
        protected void updateUIState() {
                boolean enableArchiveFile = radioArchiveFile.isSelected();
                boolean enableDirectory = radioDirectory.isSelected();
+               boolean enableURL = radioURL.isSelected();
 
                txtArchiveFile.setEnabled(enableArchiveFile);
                actChooseFile.setEnabled(enableArchiveFile);
 
                txtDirectory.setEnabled(enableDirectory);
                actChooseDirectory.setEnabled(enableDirectory);
+
+               txtURL.setEnabled(enableURL);
        }
 
        protected void onChooseFile() {
@@ -1019,6 +1095,11 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                        if (directoryTxt != null && directoryTxt.trim().length() > 0) {
                                return true;
                        }
+               } else if (radioURL.isSelected()) {
+                       String url = txtURL.getText();
+                       if (url != null && (url.startsWith("http://") || url.startsWith("https://"))) {
+                               return true;
+                       }
                }
                return false;
        }
@@ -1076,6 +1157,15 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                        }
                        importArchive = file.toURI();
 
+               } else if (radioURL.isSelected()) {
+                       // URLからダウンロード
+                       File tmpFile = loadTemporaryFromURL(txtURL.getText());
+                       if (tmpFile == null) {
+                               // エラーメッセージは表示済みのため、単にnullでnextを拒否する
+                               return null;
+                       }
+                       importArchive = tmpFile.toURI();
+
                } else {
                        // それ以外はサポートしていない.
                        return null;
@@ -1085,16 +1175,14 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
                        parent.importModel.openImportSource(importArchive, parent.current);
 
                        // ワーカースレッドでアーカイブの読み込みを行う.
-                       Worker<Object> worker = new Worker<Object>() {
+                       Worker<Void> worker = new Worker<Void>() {
                                public Void doWork(ProgressHandle progressHandle) throws IOException {
                                        parent.importModel.loadContents(progressHandle);
                                        return null;
                                }
                        };
 
-                       WorkerWithProgessDialog<Object> dlg
-                               = new WorkerWithProgessDialog<Object>(parent, worker);
-
+                       WorkerWithProgessDialog<Void> dlg = new WorkerWithProgessDialog<Void>(parent, worker);
                        dlg.startAndWait();
 
                        // 読み込めたら次ページへ
@@ -1109,6 +1197,70 @@ class ImportFileSelectPanel extends ImportWizardCardPanel {
 
                return null;
        }
+
+       /**
+        * 指定されたURLからテンポラリにダウンロードして、そのファイルを返す。
+        * URLが適切でない場合、もしくはエラーが発生した場合はnullを返す。
+        * @param url URL
+        * @return ダウンロードしたファイル
+        */
+       private File loadTemporaryFromURL(final String url) {
+               final Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+                               .getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
+               final AppConfig appConfig = AppConfig.getInstance();
+               final DownloadUtils downloader = new DownloadUtils();
+               downloader.setImpersonateUserAgent(appConfig.getImpersonateUserAgent());
+               downloader.setDeleteDownloadFileOnExit(appConfig.isDeleteDownloadFileOnExit());
+
+               try {
+                       // ワーカースレッドでアーカイブの読み込みを行う.
+                       Worker<File> worker = new Worker<File>() {
+                               public File doWork(ProgressHandle progressHandle) throws IOException {
+                                       progressHandle.setIndeterminate(true);
+                                       progressHandle.setCaption(strings.getProperty("downloading.checkhead"));
+
+                                       HeadResponse headResponse = downloader.getHead(url);
+
+                                       String contentType = headResponse.getContentType();
+                                       String ext = headResponse.getDotExtension();
+                                       CharacterDataFileReaderWriterFactory archiveRdWrFactory =
+                                                       CharacterDataFileReaderWriterFactory.getInstance();
+                                       if (contentType == null || !contentType.startsWith("application/") ||
+                                                       !archiveRdWrFactory.isSupportedFile(ext)) {
+                                               // コンテンツタイプが不明、もしくはバイナリではない
+                                               // あるいは、ファイル名がzip, jar, cmjのいずれでもない場合
+                                               return null;
+                                       }
+
+                                       progressHandle.setCaption(strings.getProperty("downloading.waitForDownload"));
+                                       return downloader.downloadTemporary(headResponse);
+                               }
+                       };
+
+                       WorkerWithProgessDialog<File> dlg = new WorkerWithProgessDialog<File>(parent, worker);
+                       try {
+                               File tempFile = dlg.startAndWait();
+                               if (tempFile != null) {
+                                       return tempFile;
+                               }
+                               JOptionPane.showMessageDialog(this, strings.getProperty("downloading.invalidFileType"),
+                                               "ERROR", JOptionPane.ERROR_MESSAGE);
+                               return null;
+
+                       } catch (WorkerException ex) {
+                               Throwable iex = ex.getCause();
+                               throw (iex == null) ? ex : (Exception) iex;
+                       }
+
+               } catch (FileNotFoundException ex) {
+                       JOptionPane.showMessageDialog(this, strings.getProperty("downloading.notFound"),
+                                       "ERROR", JOptionPane.ERROR_MESSAGE);
+
+               } catch (Exception ex) {
+                       ErrorMessageHelper.showErrorDialog(this, ex);
+               }
+               return null;
+       }
 }
 
 class URLTableRow {
@@ -1145,7 +1297,7 @@ class URLTableModel extends AbstractTableModelWithComboBoxModel<URLTableRow> {
 
        static {
                COLUMN_NAMES = new String[] {
-"作者",
+                               "作者",
                                "URL",
                };
                COLUMN_WIDTHS = new int[] {
@@ -1268,7 +1420,7 @@ class ImportTypeSelectPanel extends ImportWizardCardPanel {
 
        /* 以下、選択結果 */
 
-       public ImportTypeSelectPanel() {
+       public ImportTypeSelectPanel(ScaleSupport scaleSupport) {
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                .getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
 
@@ -1277,9 +1429,10 @@ class ImportTypeSelectPanel extends ImportWizardCardPanel {
                setLayout(basicPanelLayout);
 
                JPanel contentsSpecPanel = new JPanel();
+               int mergin = (int)(5 * scaleSupport.getManualScaleX());
                contentsSpecPanel.setBorder(BorderFactory.createCompoundBorder(
-                               BorderFactory.createEmptyBorder(5, 5, 5, 5), BorderFactory
-                                               .createTitledBorder(strings.getProperty("basic.contentsSpec"))));
+                               BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
+                               BorderFactory.createTitledBorder(strings.getProperty("basic.contentsSpec"))));
                BoxLayout contentsSpecPanelLayout = new BoxLayout(contentsSpecPanel, BoxLayout.PAGE_AXIS);
                contentsSpecPanel.setLayout(contentsSpecPanelLayout);
 
@@ -1295,9 +1448,9 @@ class ImportTypeSelectPanel extends ImportWizardCardPanel {
 
                JPanel archiveInfoPanel = new JPanel();
                archiveInfoPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory
-                               .createEmptyBorder(5, 5, 5, 5), BorderFactory
-                               .createTitledBorder(strings.getProperty("basic.archiveInfo"))));
-               Dimension archiveInfoPanelMinSize = new Dimension(300, 200);
+                               .createEmptyBorder(mergin, mergin, mergin, mergin),
+                               BorderFactory.createTitledBorder(strings.getProperty("basic.archiveInfo"))));
+               Dimension archiveInfoPanelMinSize = scaleSupport.manualScaled(new Dimension(300, 200));
                archiveInfoPanel.setMinimumSize(archiveInfoPanelMinSize);
                archiveInfoPanel.setPreferredSize(archiveInfoPanelMinSize);
                GridBagLayout commentPanelLayout = new GridBagLayout();
@@ -1309,7 +1462,8 @@ class ImportTypeSelectPanel extends ImportWizardCardPanel {
                gbc.gridwidth = 2;
                gbc.weightx = 0.;
                gbc.weighty = 0.;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.anchor = GridBagConstraints.WEST;
                gbc.fill = GridBagConstraints.BOTH;
 
@@ -1420,9 +1574,8 @@ class ImportTypeSelectPanel extends ImportWizardCardPanel {
                JPanel samplePictureTitledPanel = new JPanel(new BorderLayout());
                samplePictureTitledPanel.add(samplePicturePanelSP, BorderLayout.CENTER);
                samplePictureTitledPanel.setBorder(BorderFactory.createCompoundBorder(
-                               BorderFactory.createEmptyBorder(5, 5, 5, 5), BorderFactory
-                                               .createTitledBorder(strings
-                                                               .getProperty("basic.sampleImage"))));
+                               BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin),
+                               BorderFactory.createTitledBorder(strings.getProperty("basic.sampleImage"))));
 
                // /
 
@@ -1719,7 +1872,7 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
        private Action actSortByTimestamp;
 
 
-       public ImportPartsSelectPanel() {
+       public ImportPartsSelectPanel(ScaleSupport scaleSupport) {
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                .getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
 
@@ -1728,8 +1881,7 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                profileSizePanel = new JPanel();
                GridBagLayout profileSizePanelLayout = new GridBagLayout();
                profileSizePanel.setLayout(profileSizePanelLayout);
-               profileSizePanel.setBorder(BorderFactory
-                               .createTitledBorder("プロファイルのサイズ"));
+               profileSizePanel.setBorder(BorderFactory.createTitledBorder(strings.getProperty("sizeOfProfile")));
 
                GridBagConstraints gbc = new GridBagConstraints();
 
@@ -1739,12 +1891,13 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                gbc.gridwidth = 1;
                gbc.anchor = GridBagConstraints.EAST;
                gbc.fill = GridBagConstraints.BOTH;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.weightx = 0.;
                gbc.weighty = 0.;
                gbc.ipadx = 0;
                gbc.ipady = 0;
-               profileSizePanel.add(new JLabel("幅:", JLabel.RIGHT), gbc);
+               profileSizePanel.add(new JLabel(strings.getProperty("widthOfProfile"), JLabel.RIGHT), gbc);
 
                txtProfileWidth = new JTextField();
                txtProfileWidth.setEditable(false);
@@ -1754,7 +1907,7 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
 
                gbc.gridx = 2;
                gbc.gridy = 0;
-               profileSizePanel.add(new JLabel("高さ:", JLabel.RIGHT), gbc);
+               profileSizePanel.add(new JLabel(strings.getProperty("heightOfProfile"), JLabel.RIGHT), gbc);
 
                txtProfileHeight = new JTextField();
                txtProfileHeight.setEditable(false);
@@ -1847,7 +2000,6 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                                return comp;
                        }
                };
-               partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                partsTable.setShowGrid(true);
                partsTable.setGridColor(appConfig.getGridColor());
                partsTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
@@ -1857,6 +2009,10 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                // 行の高さをフォントの高さにする
                partsTable.setRowHeight((int)(partsTable.getFont().getSize() * 1.2));
 
+               // 列幅の調整
+               partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+               partsTableModel.adjustColumnModel(partsTable.getColumnModel(), scaleSupport.getManualScaleX());
+
                Action actPartsSetCheck = new AbstractAction(strings.getProperty("parts.popup.check")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
@@ -1929,7 +2085,7 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                gbc.gridwidth = 1;
                gbc.anchor = GridBagConstraints.EAST;
                gbc.fill = GridBagConstraints.BOTH;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.ipadx = 0;
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
@@ -1959,13 +2115,6 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
        }
 
        @Override
-       public void addNotify() {
-               super.addNotify();
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               partsTableModel.adjustColumnModel(partsTable.getColumnModel(), scaleSupport.getDefaultScaleX());
-       }
-
-       @Override
        public void onActive(ImportWizardDialog parent, ImportWizardCardPanel previousPanel) {
                this.parent = parent;
                if (previousPanel == parent.importPresetSelectPanel) {
@@ -2022,6 +2171,7 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
 
                String defaultPartsSetId;
                CharacterData presetImportTarget;
+               boolean selectAllPreset;
                if (parent.current == null) {
                        presetImportTarget = null;
                        CharacterData cd = parent.importModel.getCharacterData();
@@ -2030,12 +2180,14 @@ class ImportPartsSelectPanel extends ImportWizardCardPanel {
                        } else {
                                defaultPartsSetId = null;
                        }
+                       selectAllPreset = true; // 新規プロファイルの場合はプリセットをインポートする
                } else {
                        presetImportTarget = parent.current;
                        defaultPartsSetId = null; // 既存の場合はデフォルトのパーツセットであるかは表示する必要ないのでnullにする.
+                       selectAllPreset = (parent.current.getPartsCount() == 0); // パーツが空の場合はプリセットをインポートする
                }
 
-               parent.importPresetSelectPanel.initModel(partsSets, defaultPartsSetId, presetImportTarget);
+               parent.importPresetSelectPanel.initModel(partsSets, defaultPartsSetId, presetImportTarget, selectAllPreset);
        }
 
        @Override
@@ -2739,7 +2891,7 @@ class ImportPartsTableModel extends AbstractTableModelWithComboBoxModel<ImportPa
        public void adjustColumnModel(TableColumnModel columnModel, double scale) {
                int mx = columnModel.getColumnCount();
                for (int idx = 0; idx < mx; idx++) {
-                       columnModel.getColumn(idx).setWidth((int)(COLUMN_WIDTHS[idx] * scale));
+                       columnModel.getColumn(idx).setPreferredWidth((int)(COLUMN_WIDTHS[idx] * scale));
                }
        }
 
@@ -2870,7 +3022,7 @@ class ImportPresetSelectPanel extends ImportWizardCardPanel {
 
        private Action actSelectUsedParts;
 
-       public ImportPresetSelectPanel() {
+       public ImportPresetSelectPanel(ScaleSupport scaleSupport) {
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                .getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
 
@@ -2948,7 +3100,10 @@ class ImportPresetSelectPanel extends ImportWizardCardPanel {
                popupMenu.add(actSelectUsedParts);
 
                presetTable.setComponentPopupMenu(popupMenu);
+
+               // 列幅の調整
                presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+               presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
 
                add(new JScrollPane(presetTable), BorderLayout.CENTER);
 
@@ -2987,7 +3142,8 @@ class ImportPresetSelectPanel extends ImportWizardCardPanel {
                gbc.gridwidth = 1;
                gbc.anchor = GridBagConstraints.EAST;
                gbc.fill = GridBagConstraints.BOTH;
-               gbc.insets = new Insets(3, 3, 3, 3);
+               int gap = (int)(3 * scaleSupport.getManualScaleX());
+               gbc.insets = new Insets(gap, gap, gap, gap);
                gbc.ipadx = 0;
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
@@ -3012,13 +3168,6 @@ class ImportPresetSelectPanel extends ImportWizardCardPanel {
        }
 
        @Override
-       public void addNotify() {
-               super.addNotify();
-               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
-               presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
-       }
-
-       @Override
        public void onActive(ImportWizardDialog parent,
                        ImportWizardCardPanel previousPanel) {
                this.parent= parent;
@@ -3123,8 +3272,12 @@ class ImportPresetSelectPanel extends ImportWizardCardPanel {
                return defaultPartsSetId;
        }
 
-       public void initModel(Collection<PartsSet> partsSets, String defaultPartsSetId, CharacterData presetImportTarget) {
+       public void initModel(Collection<PartsSet> partsSets, String defaultPartsSetId, CharacterData presetImportTarget,
+                       boolean selectAll) {
                presetTableModel.initModel(partsSets, defaultPartsSetId, presetImportTarget);
+               if (selectAll) {
+                       presetTableModel.selectAll();
+               }
        }
 }
 
@@ -3328,7 +3481,7 @@ class ImportPresetTableModel extends AbstractTableModelWithComboBoxModel<ImportP
        public void adjustColumnModel(TableColumnModel columnModel, double scale) {
                int mx = columnModel.getColumnCount();
                for (int idx = 0; idx < mx; idx++) {
-                       columnModel.getColumn(idx).setWidth((int)(COLUMN_WIDTHS[idx] * scale));
+                       columnModel.getColumn(idx).setPreferredWidth((int)(COLUMN_WIDTHS[idx] * scale));
                }
        }
 
index 89fb379..7666ea9 100644 (file)
@@ -43,16 +43,19 @@ import java.util.logging.Logger;
 
 import javax.swing.Box;
 import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
 import javax.swing.JCheckBox;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JColorChooser;
 import javax.swing.JFrame;
+import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
+import javax.swing.JRadioButton;
 import javax.swing.JScrollBar;
 import javax.swing.JScrollPane;
 import javax.swing.JSeparator;
@@ -95,6 +98,7 @@ import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSet;
 import charactermanaj.model.RecommendationURL;
 import charactermanaj.model.WorkingSet;
+import charactermanaj.model.io.CharacterDataFileReaderWriterFactory;
 import charactermanaj.model.io.CharacterDataPersistent;
 import charactermanaj.model.io.CustomLayerOrderPersist;
 import charactermanaj.model.io.CustomLayerOrderPersist.CustomLayerOrderPersistListener;
@@ -129,6 +133,8 @@ import charactermanaj.ui.util.FileDropTarget;
 import charactermanaj.ui.util.ScaleSupport;
 import charactermanaj.ui.util.WindowAdjustLocationSupport;
 import charactermanaj.util.DesktopUtilities;
+import charactermanaj.util.DownloadUtils;
+import charactermanaj.util.DownloadUtils.HeadResponse;
 import charactermanaj.util.ErrorMessageHelper;
 import charactermanaj.util.LocalizedResourcePropertyLoader;
 import charactermanaj.util.SystemUtil;
@@ -285,6 +291,11 @@ public class MainFrame extends JFrame
        private final ActiveCustomLayerPatternMgr customLayerPatternMgr = new ActiveCustomLayerPatternMgr();
 
        /**
+        * ダウンロード不要フラグ
+        */
+       private boolean noNeedDataDownload;
+
+       /**
         * アクティブなメインフレームを設定する.
         *
         * @param mainFrame
@@ -457,6 +468,18 @@ public class MainFrame extends JFrame
                        // アプリケーション設定の変更で画面の再表示を試行する
                        AppConfig.getInstance().addPropertyChangeListener(appConfigChangeListener);
 
+                       // パーツがひとつも登録されていない場合
+                       // (ただし、すでにダウンロード不要フラグを設定してあればスキップする)
+                       // (データ構造変更時の再ロードでは、この処理は行わない。一時的にパーツ数が0になるため)
+                       if (characterData.getPartsCount() == 0 && !noNeedDataDownload) {
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       @Override
+                                       public void run() {
+                                               confirmDefaultCharacterDataDownload();
+                                       }
+                               });
+                       }
+
                } catch (RuntimeException ex) {
                        logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex);
                        dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.
@@ -875,12 +898,19 @@ public class MainFrame extends JFrame
                                if (dropFiles == null || dropFiles.isEmpty()) {
                                        return;
                                }
+                               final File dropFile = dropFiles.get(0);
+
                                // インポートダイアログを開く.
                                // ドロップソースの処理がブロッキングしないように、
                                // ドロップハンドラの処理を終了してからインポートダイアログが開くようにする.
                                SwingUtilities.invokeLater(new Runnable() {
                                        public void run() {
-                                               onImport(dropFiles);
+                                               onImport(new ImportSourceCallback() {
+                                                       @Override
+                                                       public void onInit(ImportWizardDialog dlg) {
+                                                               dlg.initSelectFile(dropFile);
+                                                       }
+                                               });
                                        }
                                });
                        }
@@ -924,6 +954,94 @@ public class MainFrame extends JFrame
        }
 
        /**
+        * おすすめURLの表示名がアスタリスクで始まっている、urlがzipの最初の定義を
+        * デフォルトのキャラクターセットのダウンロードurlとしてインポートするか問い合わせる
+        */
+       private void confirmDefaultCharacterDataDownload() {
+               List<RecommendationURL> recommendations = characterData.getRecommendationURLList();
+               List<RecommendationURL> downloadUrls = new ArrayList<RecommendationURL>();
+               if (recommendations != null) {
+                       CharacterDataFileReaderWriterFactory archiveRdWrFactory =
+                                       CharacterDataFileReaderWriterFactory.getInstance();
+                       for (RecommendationURL recommendation : recommendations) {
+                               String name = recommendation.getDisplayName();
+                               String url = recommendation.getUrl();
+                               if (name.startsWith("*") && archiveRdWrFactory.isSupportedFile(url)) {
+                                       downloadUrls.add(recommendation);
+                               }
+                       }
+               }
+               if (downloadUrls.isEmpty()) {
+                       // デフォルトのキャラクターデータが定義されていない場合
+                       return;
+               }
+
+               Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+                               .getLocalizedProperties(STRINGS_RESOURCE);
+
+               Box box = Box.createVerticalBox();
+               box.add(new JLabel(strings.getProperty("defaultdatadownload.confirm.message")));
+
+               ButtonGroup grp = new ButtonGroup();
+               for (RecommendationURL recommendation : downloadUrls) {
+                       String name = recommendation.getDisplayName();
+                       String url = recommendation.getUrl();
+                       if (name.startsWith("*")) {
+                               name = name.substring(1);
+                       }
+                       JRadioButton radio = new JRadioButton(name);
+                       radio.setToolTipText(url);
+                       radio.setActionCommand(url);
+                       if (grp.getButtonCount() == 0) {
+                               // 最初のアイテムを選択状態にする
+                               radio.setSelected(true);
+                       }
+                       grp.add(radio);
+                       box.add(radio);
+               }
+
+               JRadioButton chkNoAskAgain = new JRadioButton(strings.getProperty("noDownloadAndDoNotAskAgain"));
+               grp.add(chkNoAskAgain);
+               box.add(chkNoAskAgain);
+
+               int ret = JOptionPane.showConfirmDialog(MainFrame.this, box,
+                               strings.getProperty("defaultdatadownload.confirm.title"), JOptionPane.YES_NO_OPTION);
+               if (ret != JOptionPane.YES_OPTION) {
+                       return;
+               }
+
+               if (chkNoAskAgain.isSelected()) {
+                       // ダウンロードしないし、今後、この問い合わせは不要
+                       noNeedDataDownload = true;
+                       return;
+               }
+
+               // リダイレクト先の実際のURLを取得する
+               String downloadUrl = grp.getSelection().getActionCommand();
+               final String actualDownloadUrl;
+               try {
+                       AppConfig appConfig = AppConfig.getInstance();
+                       DownloadUtils downloader = new DownloadUtils();
+                       downloader.setImpersonateUserAgent(appConfig.getImpersonateUserAgent());
+                       HeadResponse response = downloader.getHead(downloadUrl);
+                       actualDownloadUrl = response.getLocation();
+
+               } catch (Exception ex) {
+                       // アクセスできなかった場合はエラーを表示してインポートはしない
+                       ErrorMessageHelper.showErrorDialog(this, ex);
+                       return;
+               }
+
+               // リダイレクト先URLをインポートダイアログに表示する
+               onImport(new ImportSourceCallback() {
+                       @Override
+                       public void onInit(ImportWizardDialog dlg) {
+                               dlg.initSelectURL(actualDownloadUrl);
+                       }
+               });
+       }
+
+       /**
         * パーツが変更されたことを検知した場合.<br>
         * パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.<br>
         */
@@ -1395,6 +1513,10 @@ public class MainFrame extends JFrame
                        for (RecommendationURL recommendation : recommendations) {
                                String displayName = recommendation.getDisplayName();
                                String url = recommendation.getUrl();
+                               if (displayName.startsWith("*") && url.endsWith(".zip")) {
+                                       // デフォルトのデータダウンロードURLはメニューには表示しない
+                                       continue;
+                               }
 
                                JMenuItem mnuItem = menuBuilder.createJMenuItem();
                                mnuItem.setText(displayName);
@@ -2027,7 +2149,7 @@ public class MainFrame extends JFrame
 
                try {
                        // インポートウィザードの実行(新規モード)
-                       ImportWizardDialog importWizDialog = new ImportWizardDialog(this, null, null);
+                       ImportWizardDialog importWizDialog = new ImportWizardDialog(this, null);
                        importWizDialog.setVisible(true);
                        int exitCode = importWizDialog.getExitCode();
                        if (exitCode == ImportWizardDialog.EXIT_PROFILE_CREATED) {
@@ -2050,15 +2172,20 @@ public class MainFrame extends JFrame
                }
        }
 
+       private interface ImportSourceCallback {
+
+               void onInit(ImportWizardDialog dlg);
+       }
+
        /**
         * 現在のプロファイルに対するインポートウィザードを実行する.<br>
         * インポートが実行された場合は、パーツをリロードする.<br>
         * インポートウィザード表示中は監視スレッドは停止される.<br>
         *
-        * @param initFile
-        *            ã\82¢ã\83¼ã\82«ã\82¤ã\83\96ã\83\95ã\82¡ã\82£ã\83«ã\81¾ã\81\9fã\81¯ã\83\87ã\82£ã\83¬ã\82¯ã\83\88ã\83ªã\80\81æ\8c\87å®\9aã\81\8cã\81ªã\81\91ã\82\8cã\81°null
+        * @param initCallback
+        *            ã\82¢ã\83¼ã\82«ã\82¤ã\83\96ã\83\95ã\82¡ã\82£ã\83«ã\81¾ã\81\9fã\81¯ã\83\87ã\82£ã\83¬ã\82¯ã\83\88ã\83ªã\82\92å\88\9dæ\9c\9f設å®\9aã\81\99ã\82\8bã\82³ã\83¼ã\83«ã\83\90ã\83\83ã\82¯
         */
-       protected void onImport(List<File> initFiles) {
+       protected void onImport(ImportSourceCallback initCallback) {
                if (!characterData.isValid()) {
                        Toolkit tk = Toolkit.getDefaultToolkit();
                        tk.beep();
@@ -2069,7 +2196,10 @@ public class MainFrame extends JFrame
                        watchAgent.suspend();
                        try {
                                // インポートウィザードの実行
-                               ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles);
+                               ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData);
+                               if (initCallback != null) {
+                                       initCallback.onInit(importWizDialog);
+                               }
                                importWizDialog.setVisible(true);
 
                                if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {
@@ -2272,6 +2402,8 @@ public class MainFrame extends JFrame
                        Rectangle windowRect = new Rectangle(windowPos, windowSize);
                        workingSet.setWindowRect(windowRect);
 
+                       workingSet.setNoNeedDataDownload(noNeedDataDownload);
+
                        // XML形式でのワーキングセットの保存
                        WorkingSetPersist workingSetPersist = WorkingSetPersist
                                        .getInstance();
@@ -2387,6 +2519,9 @@ public class MainFrame extends JFrame
                                }
                        }
 
+                       // ダウンロード不要フラグ
+                       noNeedDataDownload = workingSet.isNoNeedDataDownload();
+
                        return true;
 
                } catch (Exception ex) {
index f513eca..8aab86b 100644 (file)
@@ -47,6 +47,7 @@ import charactermanaj.model.CharacterData;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSet;
+import charactermanaj.ui.util.ScaleSupport;
 import charactermanaj.util.LocalizedResourcePropertyLoader;
 
 /**
@@ -195,6 +196,8 @@ public class PartsRandomChooserDialog extends JDialog {
 
                setTitle(strings.getProperty("partsRandomChooser"));
 
+               ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
+               
                Container contentPane = getContentPane();
                contentPane.setLayout(new BorderLayout());
 
@@ -232,7 +235,8 @@ public class PartsRandomChooserDialog extends JDialog {
                                        int idx = centerPnl.getComponentCount();
                                        RandomChooserPanel pnl = addPartsChooserPanel(centerPnl,
                                                        idx, category, lastInCategory,
-                                                       changePartsIdentifierListener);
+                                                       changePartsIdentifierListener,
+                                                       scaleSupport);
 
                                        // 未選択の場合、もしくは複数選択カテゴリの場合はランダムはディセーブルとする
                                        pnl.setEnableRandom(enable
@@ -290,7 +294,8 @@ public class PartsRandomChooserDialog extends JDialog {
                JButton btnBack = new JButton(actBack);
 
                Box btnPanel = Box.createHorizontalBox();
-               btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 42));
+               int mergin = (int)(5 * scaleSupport.getManualScaleX());
+               btnPanel.setBorder(BorderFactory.createEmptyBorder(mergin, mergin, mergin, mergin * 8)); // 5, 5, 5, 40
 
                btnPanel.add(btnRandomAll);
                btnPanel.add(btnBack);
@@ -493,16 +498,18 @@ public class PartsRandomChooserDialog extends JDialog {
                private JToggleButton btnReject;
 
                public RandomChooserPanel(final PartsCategory category,
-                               final boolean lastInCategory) {
+                               final boolean lastInCategory,
+                               final ScaleSupport scaleSupport) {
                        Properties strings = LocalizedResourcePropertyLoader
                                        .getCachedInstance().getLocalizedProperties(
                                                        STRINGS_RESOURCE);
 
+                       int gap = (int)(3 * scaleSupport.getManualScaleX());
                        setBorder(BorderFactory.createCompoundBorder(
-                                       BorderFactory.createEmptyBorder(3, 3, 3, 3),
+                                       BorderFactory.createEmptyBorder(gap, gap, gap, gap),
                                        BorderFactory.createCompoundBorder(
                                                        BorderFactory.createEtchedBorder(),
-                                                       BorderFactory.createEmptyBorder(3, 3, 3, 3))));
+                                                       BorderFactory.createEmptyBorder(gap, gap, gap, gap))));
                        setLayout(new GridBagLayout());
 
                        GridBagConstraints gbc = new GridBagConstraints();
@@ -696,9 +703,10 @@ public class PartsRandomChooserDialog extends JDialog {
                        final int addPos,
                        final PartsCategory category,
                        final boolean lastInCategory,
-                       final ActionListener changePartsIdentifierListener) {
+                       final ActionListener changePartsIdentifierListener,
+                       final ScaleSupport scaleSupport) {
                RandomChooserPanel pnl = new RandomChooserPanel(category,
-                               lastInCategory) {
+                               lastInCategory, scaleSupport) {
                        private static final long serialVersionUID = 1L;
 
                        @Override
@@ -709,7 +717,8 @@ public class PartsRandomChooserDialog extends JDialog {
                                        if (comp.equals(this)) {
                                                // 同じカテゴリのものを追加する
                                                addPartsChooserPanel(centerPnl, idx + 1, category,
-                                                               lastInCategory, changePartsIdentifierListener);
+                                                               lastInCategory, changePartsIdentifierListener,
+                                                               scaleSupport);
                                                centerPnl.validate();
                                                // Addボタンを非表示にする.
                                                ((JButton) e.getSource()).setVisible(false);
index f55513c..928455c 100644 (file)
@@ -406,29 +406,46 @@ public final class ProfileListManager {
         * @param characterData
         */
        public static void prepare(CharacterData characterData) {
-               if (characterData.isEnableCustonLayerPattern()) {
-                       CustomLayerOrderPersist customLayerOrderPersist = CustomLayerOrderPersist.newInstance(characterData);
-                       if (!customLayerOrderPersist.exist()) {
-                               // まだカスタムレイヤーが登録されていない場合(空ファイルの登録は無視する)
-                               // カスタムレイヤーをまだ使ったことがないキャラクターデータを最初に開いた場合
-                               String structureSig = characterData.toStructureString();
-
-                               // 既定は空のパターン
-                               Map<CustomLayerOrderKey, List<CustomLayerOrder>> map = Collections.emptyMap();
+               if (characterData.isEnableCustonLayerPattern() ||
+                               characterData.getRecommendationURLList() == null) {
+                       String structureSig = characterData.toStructureString();
+                       CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
+                       CharacterData v3 = defProv.createDefaultCharacterData(DefaultCharacterDataVersion.V3);
+
+                       if (characterData.isEnableCustonLayerPattern()) {
+                               CustomLayerOrderPersist customLayerOrderPersist = CustomLayerOrderPersist.newInstance(characterData);
+                               if (!customLayerOrderPersist.exist()) {
+                                       // まだカスタムレイヤーが登録されていない場合(空ファイルの登録は無視する)
+                                       // カスタムレイヤーをまだ使ったことがないキャラクターデータを最初に開いた場合
+
+                                       // 既定は空のパターン
+                                       Map<CustomLayerOrderKey, List<CustomLayerOrder>> map = Collections.emptyMap();
+
+                                       if (v3.toStructureString().equals(structureSig)) {
+                                               // デフォルトのキャラクターセット(v3)と同一構造であれば、
+                                               // V3デフォルト用のカスタムレイヤー定義をセットする。
+                                               map = defProv.createDefaultCustomLayerOrderMap(characterData, DefaultCharacterDataVersion.V3);
+                                       }
 
-                               CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
-                               CharacterData v3 = defProv.createDefaultCharacterData(DefaultCharacterDataVersion.V3);
-                               if (v3.toStructureString().equals(structureSig)) {
-                                       // デフォルトのキャラクターセット(v3)と同一構造であれば、
-                                       // V3デフォルト用のカスタムレイヤー定義をセットする。
-                                       map = defProv.createDefaultCustomLayerOrderMap(characterData, DefaultCharacterDataVersion.V3);
+                                       // カスタムレイヤーパターンをファイルに保存する
+                                       try {
+                                               customLayerOrderPersist.save(map);
+                                       } catch (Exception ex) {
+                                               logger.log(Level.WARNING, "failed to save the custom layer mapping.", ex);
+                                       }
                                }
+                       }
 
-                               // カスタムレイヤーパターンをファイルに保存する
-                               try {
-                                       customLayerOrderPersist.save(map);
-                               } catch (Exception ex) {
-                                       logger.log(Level.WARNING, "failed to save the custom layer mapping.", ex);
+                       // お薦めリンクがnullの場合
+                       // (旧形式、またはお薦めリンクの定義がデフォルト定義と同一である場合はnullになる。)
+                       // rev:c587663f3dda3a4a874ef6810a336126f07d482c まではMainFrameのお薦めリンクメニュー構築時に補完していた。
+                       // キャラクターデータのダウンロード問い合わせ対応のため、ここで先に補完しておく。
+                       if (characterData.getRecommendationURLList() == null) {
+                               CharacterData v2 = defProv.createDefaultCharacterData(DefaultCharacterDataVersion.V3);
+                               if (v2.toStructureString().equals(structureSig) || v3.toStructureString().equals(structureSig)) {
+                                       // デフォルトのキャラクターセット(v2, v3)と同一構造であれば、デフォルトで補完する
+                                       final CharacterDataPersistent persistent = CharacterDataPersistent.getInstance();
+                                       persistent.compensateRecommendationList(characterData);
                                }
                        }
                }
index 0172d40..560bde9 100644 (file)
@@ -517,8 +517,9 @@ public class ProfileSelectorDialog extends JDialog {
 
                JSplitPane centerPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
                centerPane.setResizeWeight(1.f); // ウィンドウサイズ変更時に上を可変とする.
-               centerPane.setDividerLocation(Integer.parseInt(strings
-                               .getProperty("dividerLocation")));
+               int divLocation = Integer.parseInt(strings.getProperty("dividerLocation"));
+               divLocation = (int)(scaleSupport.getManualScaleY() * divLocation);
+               centerPane.setDividerLocation(divLocation);
 
                centerPane.add(pnlProfilesGroup);
                centerPane.add(infoPanel);
index 6fd5275..8cebc2c 100644 (file)
@@ -1,6 +1,5 @@
 package charactermanaj.ui;
 
-import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.GraphicsEnvironment;
@@ -211,11 +210,13 @@ public class SelectCharatersDirDialog extends JDialog {
                btnCancel.addFocusListener(focusAdapter);
                btnBroseForDir.addFocusListener(focusAdapter);
 
-               Container contentPane = getContentPane();
-               contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
-
                ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
 
+               JPanel contentPane = (JPanel) getContentPane();
+               int borderSize = (int)(5 * scaleSupport.getManualScaleX());
+               contentPane.setBorder(BorderFactory.createEmptyBorder(0, borderSize, borderSize, borderSize));
+               contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
+
                {
                        JLabel lbl = new JLabel(strings.getProperty("caption"));
                        lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
@@ -274,6 +275,14 @@ public class SelectCharatersDirDialog extends JDialog {
                        btnsBox.add(btnRemoveWorkingSets);
                        btnsBox.add(Box.createHorizontalGlue());
 
+                       // OK, CANCELボタンのサイズを合わせる
+                       Dimension dim = btnOK.getPreferredSize();
+                       int btnWidth = Math.max(btnOK.getPreferredSize().width,
+                                       btnCancel.getPreferredSize().width);
+                       dim.setSize(btnWidth, dim.height);
+                       btnOK.setPreferredSize(dim);
+                       btnCancel.setPreferredSize(dim);
+
                        if (!Main.isLinuxOrMacOSX()) {
                                btnsBox.add(btnOK);
                                btnsBox.add(btnCancel);
index e4e44dc..e3634e5 100644 (file)
@@ -35,32 +35,32 @@ public class WorkerWithProgessDialog<T> extends JDialog {
         * ワーカースレッドが停止したことを示すフラグ
         */
        private volatile boolean exitThread;
-       
+
        /**
         * ワーカースレッドの戻り値
         */
        private volatile T result;
-       
+
        /**
         * ワーカースレッドが例外により終了した場合の例外
         */
        private volatile Throwable occuredException;
-       
+
        /**
         * ワーカースレッド
         */
        private Thread thread;
-       
+
        /**
         * ワーカースレッドの状態を監視しプログレスに反映させるタイマー
         */
        private Timer timer;
-       
+
        /**
         * プログレスの更新頻度(タイマーのインターバル)
         */
        private static int interval = 200;
-       
+
 
        /**
         * 親フレームとワーカーを指定して構築する.<br>
@@ -73,15 +73,15 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        if (worker == null) {
                                throw new IllegalArgumentException();
                        }
-                       
+
                        initComponent(parent, worker);
-                       
+
                } catch (RuntimeException ex) {
                        dispose();
                        throw ex;
                }
        }
-       
+
        /**
         * 親ダイアログとワーカーを指定して構築する.<br>
         * @param parent 親フレーム
@@ -93,9 +93,9 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        if (worker == null) {
                                throw new IllegalArgumentException();
                        }
-                       
+
                        initComponent(parent, worker);
-                       
+
                } catch (RuntimeException ex) {
                        dispose();
                        throw ex;
@@ -122,10 +122,10 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                final JProgressBar progressBar = new JProgressBar();
                progressBar.setIndeterminate(true);
                progressBar.setStringPainted(false);
-               
+
                container.add(progressBar, BorderLayout.SOUTH);
-               
-               // デフォルトのラベル表示 
+
+               // デフォルトのラベル表示
                String title = "please wait for a while.";
                final JLabel lblCaption = new JLabel(title);
                container.add(lblCaption, BorderLayout.NORTH);
@@ -144,7 +144,7 @@ public class WorkerWithProgessDialog<T> extends JDialog {
 
                // パックする.
                pack();
-               
+
                // 親の中央に表示
                setLocationRelativeTo(parent);
 
@@ -178,11 +178,11 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                                                }
                                        });
                                }
-                               
+
                                super.flush();
                        }
                };
-               
+
                // プログレスダイアログに状態を反映させるためのタイマー
                timer = new Timer(interval, new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
@@ -210,7 +210,7 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        }
                });
        }
-       
+
        /**
         * プログレスの表示間隔を取得する
         * @return 表示間隔
@@ -239,7 +239,7 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        public void run() {
                                try {
                                        try {
-                                               worker.doWork(progressHandle);
+                                               result = worker.doWork(progressHandle);
 
                                        } catch (Throwable ex) {
                                                occuredException = ex;
@@ -251,7 +251,7 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        }
                };
        }
-       
+
        /**
         * ワーカースレッドより、スレッドが終了したことを通知される.<br>
         * ワーカースレッド自身か、ワーカースレッドの例外ハンドラか、
@@ -269,18 +269,18 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                        }
                });
        }
-       
+
        /**
         * ワーカースレッドを開始し、プログレスダイアログを表示し、
         * ワーカースレッドの完了まで待機する.<br>
         * @throws WorkerException ワーカースレッドが例外により終了した場合
         */
-       public void startAndWait() throws WorkerException {
+       public T startAndWait() throws WorkerException {
                // 初期化
                result = null;
                occuredException = null;
                exitThread = false;
-               
+
                // ワーカースレッドの開始
                thread.start();
                try {
@@ -316,6 +316,8 @@ public class WorkerWithProgessDialog<T> extends JDialog {
                                        occuredException
                                        );
                }
+
+               return getResult();
        }
 
        /**
diff --git a/src/main/java/charactermanaj/util/DownloadUtils.java b/src/main/java/charactermanaj/util/DownloadUtils.java
new file mode 100644 (file)
index 0000000..e81501a
--- /dev/null
@@ -0,0 +1,401 @@
+package charactermanaj.util;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * ダウンロードをサポートする
+ */
+public final class DownloadUtils {
+
+       /**
+        * ロガー
+        */
+       private static final Logger logger = Logger.getLogger(DownloadUtils.class.getName());
+
+       /**
+        * 偽装するユーザーエージェント名(nullまたは空文字の場合は偽装しない)
+        */
+       private String impersonateUserAgent;
+
+       /**
+        * 最大ホップ数
+        */
+       private int maxHop = 10;
+
+       /**
+        * ダウンロードファイルを終了時に削除するか?
+        */
+       private boolean deleteDownloadFileOnExit = true;
+
+       public void setImpersonateUserAgent(String impersonateUserAgent) {
+               this.impersonateUserAgent = impersonateUserAgent;
+       }
+
+       public String getImpersonateUserAgent() {
+               return impersonateUserAgent;
+       }
+
+       public void setMaxHop(int maxHop) {
+               this.maxHop = maxHop;
+       }
+
+       public int getMaxHop() {
+               return maxHop;
+       }
+
+       public void setDeleteDownloadFileOnExit(boolean deleteOnExit) {
+               deleteDownloadFileOnExit = deleteOnExit;
+       }
+
+       public boolean isDeleteDownloadFileOnExit() {
+               return deleteDownloadFileOnExit;
+       }
+
+       /**
+        * ヘッドレスポンス
+        */
+       public static final class HeadResponse {
+
+               String location;
+
+               String contentType;
+
+               String fileName;
+
+               public String getLocation() {
+                       return location;
+               }
+
+               public String getContentType() {
+                       return contentType;
+               }
+
+               public String getFileName() {
+                       return fileName;
+               }
+
+               /**
+                * ファイルの拡張子、なければ空。
+                * 返される拡張子はドットを含む。
+                * @return ドットで始まる拡張子、もしくは空
+                */
+               public String getDotExtension() {
+                       String name = fileName;
+                       int pos = name.lastIndexOf('/');
+                       if (pos >= 0) {
+                               name = name.substring(pos + 1);
+                       }
+                       pos = name.lastIndexOf('\\');
+                       if (pos >= 0) {
+                               name = name.substring(pos + 1);
+                       }
+
+                       int extPos = name.lastIndexOf(".");
+                       String ext = "";
+                       if (extPos > 0) {
+                               // ドットから始まる拡張子に切り取る
+                               ext = name.substring(extPos).toLowerCase();
+                       }
+                       return ext;
+               }
+
+               @Override
+               public String toString() {
+                       return "location=" + location + ", contentType=" + contentType + ", fileName=" + fileName;
+               }
+       }
+
+       /**
+        * 指定したURLのコンテンツをダウンロードする
+        * @param location URL
+        * @param os 出力先
+        * @throws IOException 失敗した場合
+        */
+       public void loadContents(String location, OutputStream os) throws IOException {
+               loadContents(getHead(location), os);
+       }
+
+       public void loadContents(HeadResponse headResponse, OutputStream os) throws IOException {
+               String realLoction = headResponse.getLocation();
+
+               URL url = new URL(realLoction);
+               HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+               if (impersonateUserAgent != null && impersonateUserAgent.length() > 0) {
+                       conn.setRequestProperty("User-Agent", impersonateUserAgent);
+               }
+               conn.connect();
+               try {
+                       int status = conn.getResponseCode();
+                       if (status == HttpURLConnection.HTTP_NOT_FOUND) { // 404
+                               throw new FileNotFoundException("Failed to load contents. status=" + status + ", url=" + url);
+                       }
+                       if (status < HttpURLConnection.HTTP_OK || status >= HttpURLConnection.HTTP_MULT_CHOICE) { // 200未満、300以上
+                               throw new IOException("Failed to load contents. status=" + status + ", url=" + url);
+                       }
+
+                       byte[] buf = new byte[4096];
+                       InputStream is = conn.getInputStream();
+                       try {
+                               for (;;) {
+                                       int rd = is.read(buf);
+                                       if (rd < 0) {
+                                               break;
+                                       }
+                                       os.write(buf, 0, rd);
+                               }
+                               os.flush();
+                       } finally {
+                               is.close();
+                       }
+               } finally {
+                       conn.disconnect();
+               }
+       }
+
+       /**
+        * テンポラリディレクトりにコンテンツをダウンロードする
+        * @param headResponse
+        * @return テンポラリファイル
+        * @throws IOException
+        */
+       public File downloadTemporary(HeadResponse headResponse) throws IOException {
+               String ext = headResponse.getDotExtension();
+               if (ext == null || ext.length() == 0) {
+                       ext = ".tmp";
+               }
+               File tmpFile = File.createTempFile("cmj-", ext);
+
+               if (isDeleteDownloadFileOnExit()) {
+                       tmpFile.deleteOnExit(); // 終了時にファイルを消す。(気休め程度)
+               }
+
+               logger.log(Level.INFO, "Create temporary file: " + tmpFile);
+               try {
+                       OutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile));
+                       try {
+                               loadContents(headResponse, bos);
+
+                       } finally {
+                               bos.close();
+                       }
+
+               } catch (RuntimeException ex) {
+                       tmpFile.delete();
+                       logger.log(Level.INFO, "Delete temporary file: " + tmpFile);
+                       throw ex;
+
+               } catch (IOException ex) {
+                       logger.log(Level.INFO, "Delete temporary file: " + tmpFile);
+                       tmpFile.delete();
+                       throw ex;
+               }
+               return tmpFile;
+       }
+
+       /**
+        * URLを指定してリダイレクトがある場合はリダイレクトでなくなるまで探索した最後のURLを返す。
+        * @param location 開始するURL
+        * @return 探索されたURL
+        * @throws IOException 読み込みに失敗した場合、もしくは最大ホップ数を超えた場合
+        */
+       public HeadResponse getHead(String location) throws IOException {
+               String initLocation = location;
+               int hopCount = 0;
+               for (;;) {
+                       logger.log(Level.INFO, "Connect to " + location);
+                       URL url = new URL(location);
+                       HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+
+                       int status;
+                       conn.setRequestMethod("HEAD");
+                       conn.setInstanceFollowRedirects(false); // 自動リダイレクトはしない
+                       if (impersonateUserAgent != null && impersonateUserAgent.length() > 0) {
+                               conn.setRequestProperty("User-Agent", impersonateUserAgent);
+                       }
+                       conn.connect();
+                       try {
+                               status = conn.getResponseCode();
+
+                       } finally {
+                               conn.disconnect();
+                       }
+
+                       if (status == HttpURLConnection.HTTP_MOVED_TEMP || // 302
+                                       status == HttpURLConnection.HTTP_MOVED_PERM || // 301
+                                       status == HttpURLConnection.HTTP_SEE_OTHER) { // 303
+                               if (hopCount > maxHop) {
+                                       // 転送回数が多すぎる!
+                                       throw new IOException("too many hops! " + hopCount);
+                               }
+                               location = conn.getHeaderField("Location");
+                               if (location == null || location.isEmpty()) {
+                                       // locationヘッダがない
+                                       throw new IOException("bad response. location not found.");
+                               }
+                               hopCount++;
+                               logger.log(Level.INFO, "Location to " + location);
+                               continue;
+                       }
+
+                       if (status >= HttpURLConnection.HTTP_OK && status < HttpURLConnection.HTTP_MULT_CHOICE) { // 200以上 300未満
+                               HeadResponse response = new HeadResponse();
+                               response.location = location;
+                               response.contentType = conn.getContentType();
+
+                               String contentDisposition = conn.getHeaderField("Content-Disposition");
+                               String fileName = null;
+                               if (contentDisposition != null && contentDisposition.length() > 0) {
+                                       fileName = parseAttachmentFileName(contentDisposition);
+                               }
+                               if (fileName == null || fileName.length() == 0) {
+                                       fileName = initLocation; // ファイル名の指定がない場合は最初のロケーション名を使用する
+                               }
+                               response.fileName = fileName;
+                               logger.log(Level.INFO, "response success. " + response);
+                               return response;
+                       }
+
+                       if (status == HttpURLConnection.HTTP_NOT_FOUND) { // 404
+                               // ファイルが見つからない場合
+                               throw new FileNotFoundException("Failed to load contents. status=" + status + ", url=" + url);
+                       }
+
+                       // 何らかのエラー
+                       logger.log(Level.WARNING, "response failed. status=" + status);
+                       throw new IOException("response failed. status=" + status);
+               }
+       }
+
+       /**
+        * セミコロンで行を区切る。
+        * (ダブルクォートがある場合は、閉じられるまではセミコロンは無視する。)
+        * @param line
+        * @return
+        */
+       private static List<String> splitSemicolon(String line) {
+               List<String> lines = new ArrayList<String>();
+               StringBuilder buf = new StringBuilder();
+               int mode = 0;
+               for (char ch : line.toCharArray()) {
+                       if (mode == 0) {
+                               if (ch == '"') {
+                                       // ダブルクォートがある場合は閉じるまでセミコロンを無視する
+                                       buf.append((char) ch);
+                                       mode = 1;
+
+                               } else if (ch == ';') {
+                                       lines.add(buf.toString());
+                                       buf.setLength(0);
+
+                               } else {
+                                       buf.append((char) ch);
+                               }
+                       } else if (mode == 1) {
+                               if (ch == '"') {
+                                       buf.append((char) ch);
+                                       mode = 0;
+
+                               } else {
+                                       buf.append((char) ch);
+                               }
+                       }
+               }
+               if (buf.length() > 0) {
+                       lines.add(buf.toString());
+               }
+               return lines;
+       }
+
+       /**
+        * key=value形式の文字列のリストからマップを生成する。
+        * valueがダブルクォートで囲まれている場合はダブルクォートを除去する。
+        * @param lines
+        * @return
+        */
+       private static Map<String, String> parseKeyValuePair(List<String> lines) {
+               Map<String, String> keyValueMap = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+               for (String line : lines) {
+                       line = line.trim();
+                       if (!line.isEmpty()) {
+                               int pos = line.indexOf("=");
+                               String key, value;
+                               if (pos >= 0) {
+                                       key = line.substring(0, pos);
+                                       value = line.substring(pos + 1);
+                                       value = value.trim();
+                                       if (value.startsWith("\"") && value.endsWith("\"")) {
+                                               // ダブルクォートで囲まれている場合は外す
+                                               value = value.substring(1, value.length() - 1);
+                                       }
+                               } else {
+                                       key = line;
+                                       value = "";
+                               }
+                               keyValueMap.put(key, value);
+                       }
+               }
+               return keyValueMap;
+       }
+
+       /**
+        * Content-Dispositionのヘッダーパラメータからファイル名を取得する。
+        * @param contentDisposition
+        * @return
+        */
+       private static String parseAttachmentFileName(String contentDisposition) {
+               List<String> lines = splitSemicolon(contentDisposition);
+               logger.log(Level.FINE, "content-dispotion: " + lines);
+               Map<String, String> kv = parseKeyValuePair(lines);
+
+               String fileName = null;
+
+               // 文字コードつきファイル名パラメータがあれば、それを解析・取得する
+               String encodedFileName = kv.get("filename*");
+               if (encodedFileName != null && encodedFileName.length() > 0) {
+                       // 文字コードの取得(空の場合もありえる)
+                       int pos = encodedFileName.indexOf('\'');
+                       String encoding = encodedFileName.substring(0, pos);
+                       if (encoding.isEmpty()) {
+                               encoding = "utf-8"; // UTF-8をデフォルトとみなす
+                       }
+
+                       // 言語の取得(空の場合もありえる)
+                       int pos2 = encodedFileName.indexOf('\'', pos + 1);
+                       String language = encodedFileName.substring(pos + 1, pos2);
+
+                       // ファイル名
+                       try {
+                               fileName = URLDecoder.decode(encodedFileName.substring(pos2 + 1), encoding);
+
+                       } catch (UnsupportedEncodingException ex) {
+                               logger.log(Level.WARNING, "url encoding error: " + encodedFileName, ex);
+                               fileName = null;
+                       }
+                       logger.log(Level.INFO, "attachment filename*=" + encoding + "," + language + "," + fileName);
+               }
+
+               // 文字コードつきファイル名がなければ、文字コードなしファイル名を取得する
+               if (fileName == null || fileName.length() == 0) {
+                       fileName = kv.get("filename");
+                       logger.log(Level.INFO, "attachment filename=" + fileName);
+               }
+
+               return fileName;
+       }
+}
index caf7f18..cc48fc7 100644 (file)
@@ -73,6 +73,8 @@ If the file already exists, the file is overwritten.]]></entry>
 
 <entry key="jarTransferBufferSize">90;Jar File Buffer</entry>
 <entry key="fileTransferBufferSize">91;File Buffer</entry>
+<entry key="impersonateUserAgent">92;Impersonation of user agent name for download</entry>
+<entry key="deleteDownloadFileOnExit">93;Delete the downloaded file in temporary directory upon termination</entry>
 
 <entry key="drawGridMask">A0;Draw grid on preview</entry>
 <entry key="previewGridColor">A1;Grid color (ARGB)</entry>
index 8e4a600..2b328f7 100644 (file)
@@ -73,6 +73,8 @@
 
 <entry key="jarTransferBufferSize">90;JARファイル用バッファサイズ</entry>
 <entry key="fileTransferBufferSize">91;ファイル転送用バッファサイズ</entry>
+<entry key="impersonateUserAgent">92;ダウンロード時に偽装するユーザーエージェント名</entry>
+<entry key="deleteDownloadFileOnExit">93;終了時にテンポラリディレクトり上にダウンロードしたファイルを消す</entry>
 
 <entry key="drawGridMask">A0;グリッドを描画する確認モードのビットマスク(0-3, 0は無効にする場合)</entry>
 <entry key="previewGridColor">A1;プレビュー画面のグリッドカラー(ARGB)</entry>
index 6192c09..35e9e13 100644 (file)
@@ -72,6 +72,8 @@
 
 <entry key="jarTransferBufferSize">90;Jar文件缓存</entry>
 <entry key="fileTransferBufferSize">91;文件缓存</entry>
+<entry key="impersonateUserAgent">92;模拟用户代理名称以供下载</entry>
+<entry key="deleteDownloadFileOnExit">93;终止后,在临时目录中删除下载的文件</entry>
 
 <entry key="drawGridMask">A0;预览时显示网格</entry>
 <entry key="previewGridColor">A1;网格颜色(ARGB)</entry>
index c5220ae..52eb736 100644 (file)
        <entry key="browse">Browse</entry>
        <entry key="importingArchiveFile">Import archived File (zip, cmj)</entry>
        <entry key="importingDirectory">Import from Directory</entry>
+       <entry key="importingURL">Import from URL</entry>
        <entry key="fileNotFound">File not found.</entry>
        <entry key="directoryNotFound">Directory not found.</entry>
 
+       <entry key="downloading.checkhead">checking...</entry>
+       <entry key="downloading.waitForDownload">Downloading...</entry>
+       <entry key="downloading.invalidFileType">Unsupported file type.</entry>
+       <entry key="downloading.notFound">File not found.</entry>
+
        <entry key="confirm.close">Are you sure you want to cancel?</entry>
        <entry key="confirm">Confirm</entry>
 
        <entry key="description">Note</entry>
        <entry key="basic.sampleImage">Sample picture</entry>
        <entry key="appendDescription">Append this description to the profile.</entry>
-       
+
        <entry key="noContents">No contents.</entry>
        <entry key="notFormalArchive">This is not a formal archive, but may be containing some picture.</entry>
        <entry key="unmatchedProfileId">Profile ID mismatch. id="{0}"</entry>
        <entry key="unmatchedProfileRev">Profile REV mismatch. rev="{0}"</entry>
 
+       <entry key="sizeOfProfile">Profile Size</entry>
+       <entry key="widthOfProfile">Width: </entry>
+       <entry key="heightOfProfile">Height: </entry>
+
        <entry key="parts.title">Import parts</entry>
        <entry key="parts.btn.selectAll">select All</entry>
        <entry key="parts.btn.deselectAll">deselect All</entry>
@@ -75,8 +85,8 @@
        <entry key="preset.column.check.size">50</entry>
        <entry key="preset.column.name.size">100</entry>
        <entry key="preset.column.missings.size">200</entry>
-       
+
        <entry key="complete">Complete</entry>
-       
+
 </properties>
 
index 1bf0e2b..b84017c 100644 (file)
        <entry key="browse">参照...</entry>
        <entry key="importingArchiveFile">アーカイブファイル(zip,cmj)からインポート</entry>
        <entry key="importingDirectory">フォルダからインポート</entry>
+       <entry key="importingURL">URLからダウンロードしてインポート</entry>
        <entry key="file">ファイル:</entry>
        <entry key="fileNotFound">ファイルがありません。</entry>
        <entry key="directoryNotFound">フォルダがありません。</entry>
 
+       <entry key="downloading.checkhead">確認中...</entry>
+       <entry key="downloading.waitForDownload">ダウンロード中...</entry>
+       <entry key="downloading.invalidFileType">サポートされていない形式です</entry>
+       <entry key="downloading.notFound">ファイルがみつかりません</entry>
+
        <entry key="confirm.close">キャンセルしますか?</entry>
        <entry key="confirm">確認</entry>
 
        <entry key="unmatchedProfileId">プロファイルIDが一致しません。 id="{0}"</entry>
        <entry key="unmatchedProfileRev">プロファイルIDは一致しますが、リビジョンが一致しません。 rev="{0}"</entry>
 
+       <entry key="sizeOfProfile">プロファイルのサイズ</entry>
+       <entry key="widthOfProfile">幅: </entry>
+       <entry key="heightOfProfile">高さ: </entry>
+
        <entry key="parts.title">インポートするパーツ</entry>
        <entry key="parts.btn.selectAll">全て選択</entry>
        <entry key="parts.btn.deselectAll">全て解除</entry>
        <entry key="parts.btn.sort">名前順で整列</entry>
        <entry key="parts.btn.sortByTimestamp">日付順で整列</entry>
-       
+
        <entry key="parts.popup.check">チェックする</entry>
        <entry key="parts.popup.uncheck">チェックを外す</entry>
 
@@ -67,7 +77,7 @@
        <entry key="parts.column.org-author.size">80</entry>
        <entry key="parts.column.version.size">50</entry>
        <entry key="parts.column.org-version.size">50</entry>
-       
+
        <entry key="preset.title">インポートするお気に入り</entry>
        <entry key="preset.popup.selectUsedParts">使用しているパーツのインポート</entry>
        <entry key="preset.column.check">選択</entry>
@@ -78,6 +88,6 @@
        <entry key="preset.column.missings.size">200</entry>
 
        <entry key="complete">インポートが完了しました。</entry>
-       
+
 </properties>
 
index a154933..71fd65c 100644 (file)
        <entry key="browse">浏览</entry>
        <entry key="importingArchiveFile">导入压缩文件(zip,cmj)</entry>
        <entry key="importingDirectory">从文件夹导入</entry>
+       <entry key="importingURL">通过指定URL导入</entry>
        <entry key="fileNotFound">找不到文件</entry>
        <entry key="directoryNotFound">找不到文件夹</entry>
 
+       <entry key="downloading.checkhead">检查...</entry>
+       <entry key="downloading.waitForDownload">在下载...</entry>
+       <entry key="downloading.invalidFileType">不支持的格式</entry>
+       <entry key="downloading.notFound">找不到档案</entry>
+
        <entry key="confirm.close">你确认要取消么?</entry>
        <entry key="confirm">确认</entry>
 
@@ -75,8 +81,8 @@
        <entry key="preset.column.check.size">50</entry>
        <entry key="preset.column.name.size">100</entry>
        <entry key="preset.column.missings.size">200</entry>
-       
+
        <entry key="complete">完成</entry>
-       
+
 </properties>
 
index ae0e3fe..0b521d1 100644 (file)
@@ -15,4 +15,9 @@
        <entry key="help.reportbugs.url">http://osdn.net/projects/charactermanaj/ticket/</entry>
        <entry key="help.forum.description">Forum</entry>
        <entry key="help.forum.url">http://osdn.net/projects/charactermanaj/forums/</entry>
+
+       <entry key="defaultdatadownload.confirm.message">There is default character data. Do you want to download it?</entry>
+       <entry key="defaultdatadownload.confirm.title">Default character data download</entry>
+       <entry key="noDownloadAndDoNotAskAgain">I do not want to download, and do not ask me again.</entry>
 </properties>
+
index 6969f7d..9563c73 100644 (file)
@@ -15,4 +15,8 @@
        <entry key="help.reportbugs.url">http://osdn.net/projects/charactermanaj/ticket/</entry>
        <entry key="help.forum.description">フォーラムは以下のURLにあります。</entry>
        <entry key="help.forum.url">http://osdn.net/projects/charactermanaj/forums/</entry>
+
+       <entry key="defaultdatadownload.confirm.message">デフォルトのキャラクターデータがあります。ダウンロードしますか?</entry>
+       <entry key="defaultdatadownload.confirm.title">キャラクターデータのダウンロード</entry>
+       <entry key="noDownloadAndDoNotAskAgain">ダウンロードしない。今後の確認も不要である。</entry>
 </properties>
index e0b28c0..3038be1 100644 (file)
@@ -15,4 +15,8 @@
        <entry key="help.reportbugs.url">http://osdn.net/projects/charactermanaj/ticket/</entry>
        <entry key="help.forum.description">论坛(日)</entry>
        <entry key="help.forum.url">http://osdn.net/projects/charactermanaj/forums/</entry>
+
+       <entry key="defaultdatadownload.confirm.message">有默认数据。你想下载吗?</entry>
+       <entry key="defaultdatadownload.confirm.title">下载默认数据</entry>
+       <entry key="noDownloadAndDoNotAskAgain">我不想下载,也不要再问我了</entry>
 </properties>
index 66684af..becb8de 100644 (file)
             <description xml:lang="zh_TW">パーツ保管庫 (零件的保管庫)</description>
             <URL xml:lang="en">https://charactermanaj.osdn.jp/upload.html</URL>
         </recommendation>
+        <recommendation>
+            <description xml:lang="en">*Default PartsSet (K.Hmix 1st Edition)</description>
+            <description xml:lang="ja">*キャラクターなんとか機のパーツデータをダウンロードする (K.Hmix 1st Edition)</description>
+            <URL xml:lang="en">http://charactermanaj.osdn.jp/ext/default_characterset_v3.zip</URL>
+        </recommendation>
     </recommendations>
 
 </character>