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);
+ }
+ }
}
}
/**
+ * 全カテゴリー中のパーツデータの個数を取得する。
+ * @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>
*/
private Rectangle windowRect;
+ /**
+ * ダウンロード不要フラグ
+ */
+ private boolean noNeedDataDownload;
+
public void setCharacterDocBase(URI characterDocBase) {
this.characterDocBase = characterDocBase;
this.lastUsePresetParts = lastUsePresetParts;
}
+ public boolean isNoNeedDataDownload() {
+ return noNeedDataDownload;
+ }
+
+ public void setNoNeedDataDownload(boolean noNeedDataDownload) {
+ this.noNeedDataDownload = noNeedDataDownload;
+ }
+
/**
* キャラクターデータを指定して、指定されたキャラクターデータ上のインスタンスと関連づけられた
* カテゴリおよびパーツ名などのインスタンスで構成されるパーツ識別名とカラー情報を、 引数で指定したマップに出力する.
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();
}
*/
private Rectangle windowRect;
+ /**
+ * データのダウンロードが不要である
+ */
+ private boolean noNeedDataDownload;
+
public void setCharacterDataRev(String characterDataRev) {
this.characterDataRev = characterDataRev;
}
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;
private CharacterDataFileReaderWriterFactory() {
super();
}
-
+
public static CharacterDataFileReaderWriterFactory getInstance() {
return singleton;
}
-
+
/**
* ファイルの拡張子に応じてzip/cmj形式でのライターを構築して帰します.<br>
* 拡張子がjarとcmjは同じ意味で、ともにjarファイル形式となります.<br>
if (outfile == null) {
throw new IllegalArgumentException();
}
-
+
String name = outfile.getName().toLowerCase();
if (name.endsWith(".jar") || name.endsWith(".cmj")) {
return new CharacterDataJarFileWriter(outfile);
} 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();
// file以外は現在のところサポートしない。
throw new UnsupportedOperationException();
}
-
-
+
+
public CharacterDataArchiveFile openArchive(File archiveFile) throws IOException {
if (archiveFile == null) {
throw new IllegalArgumentException();
// ディレクトリの場合
return new CharacterDataDirectoryFile(archiveFile);
}
-
+
// zipまたはcmjファイルの場合
String name = archiveFile.getName().toLowerCase();
if (name.endsWith(".jar") || name.endsWith(".cmj")) {
} 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;
+ }
}
ws.setZoomFactor(workingSet2.getZoomFactor());
ws.setViewPosition(workingSet2.getViewPosition());
ws.setWindowRect(workingSet2.getWindowRect());
+
+ ws.setNoNeedDataDownload(workingSet2.isNoNeedDataDownload());
return ws;
}
break; // 最初の一要素のみ
}
+ // ダウンロード不要フラグ
+ boolean noNeedDataDownload = false;
+ for (Element noNeedDataDownloadElm : getChildElements(docElm,
+ "noNeedDataDownload")) {
+ noNeedDataDownload = Boolean.parseBoolean(noNeedDataDownloadElm.getTextContent());
+ break; // 最初の一要素のみ
+ }
+ workingSet.setNoNeedDataDownload(noNeedDataDownload);
+
return workingSet;
} catch (RuntimeException ex) {
// ズーム情報等
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;
}
// メインパネル
+ 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);
};
// 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");
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");
// 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);
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);
// 表示
- 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);
- protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture) {
+ protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture,
+ ScaleSupport scaleSupport) {
if (characterData == null) {
throw new IllegalArgumentException();
}
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);
///
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);
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);
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"))));
private Action actSortByTimestamp;
- protected ExportPartsSelectPanel(PartsSpecResolver partsSpecResolver) {
+ protected ExportPartsSelectPanel(PartsSpecResolver partsSpecResolver, ScaleSupport scaleSupport) {
if (partsSpecResolver == null) {
throw new IllegalArgumentException();
}
};
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);
// 行の高さをフォントの高さにする
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) {
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);
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()) {
protected ExportPresetSelectPanel(
final ExportPartsResolver exportPartsResolver,
final ExportInformationResolver exportInfoResolver,
- Collection<PartsSet> partsSets, String defaultPresetId) {
+ Collection<PartsSet> partsSets, String defaultPresetId,
+ ScaleSupport scaleSupport) {
this.exportPartsResolver = exportPartsResolver;
// 行の高さをフォントの高さにする
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;
presetTable.setComponentPopupMenu(popupMenu);
- presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
-
add(new JScrollPane(presetTable), BorderLayout.CENTER);
actSelectAll = new AbstractAction(strings.getProperty("parts.btn.selectAll")) {
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);
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) {
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;
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;
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;
* 親フレーム
* @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);
}
/**
}
/**
+ * @param initFiles
+ * アーカイブファィルまたはディレクトリの初期選択、なければnullまたは空
+ */
+ public void initSelectFile(File initFile) {
+ importFileSelectPanel.setSelectFile(initFile);
+ }
+
+ public void initSelectURL(String url) {
+ importFileSelectPanel.initSelectURL(url);
+ }
+
+ /**
* ウィザードダイアログのコンポーネントを初期化します.<br>
* currentがnullの場合は新規インポート、そうでない場合は更新インポートとります。
*
// メインパネル
+ 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);
};
// 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);
// 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);
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);
// 表示
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);
private Action actChooseDirectory;
+ /**
+ * URLを指定してインポート
+ */
+ private JRadioButton radioURL;
+
+ /**
+ * URL入力ボックス
+ */
+ private JTextField txtURL;
+
/* 以下、対象ファイルの読み取り結果 */
- public ImportFileSelectPanel() {
+ public ImportFileSelectPanel(ScaleSupport scaleSupport) {
setLayout(new BorderLayout());
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
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;
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) {
};
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;
gbc.weightx = 0.;
fileChoosePanel.add(new JButton(actChooseFile), gbc);
+ // ディレクトり
gbc.gridx = 0;
gbc.gridy = 2;
gbc.ipadx = 0;
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.;
if (dropFiles == null || dropFiles.isEmpty()) {
return;
}
- setSelectFile(dropFiles);
+ File dropFile = dropFiles.get(0);
+ setSelectFile(dropFile);
}
@Override
* @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()) {
// ディレクトリの場合
}
}
+ /**
+ * 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() {
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;
}
}
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;
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();
// 読み込めたら次ページへ
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 {
static {
COLUMN_NAMES = new String[] {
-"作者",
+ "作者",
"URL",
};
COLUMN_WIDTHS = new int[] {
/* 以下、選択結果 */
- public ImportTypeSelectPanel() {
+ public ImportTypeSelectPanel(ScaleSupport scaleSupport) {
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
.getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
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);
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();
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;
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"))));
// /
private Action actSortByTimestamp;
- public ImportPartsSelectPanel() {
+ public ImportPartsSelectPanel(ScaleSupport scaleSupport) {
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
.getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
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();
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);
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);
return comp;
}
};
- partsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
partsTable.setShowGrid(true);
partsTable.setGridColor(appConfig.getGridColor());
partsTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
// 行の高さをフォントの高さにする
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) {
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);
}
@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) {
String defaultPartsSetId;
CharacterData presetImportTarget;
+ boolean selectAllPreset;
if (parent.current == null) {
presetImportTarget = null;
CharacterData cd = parent.importModel.getCharacterData();
} 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
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));
}
}
private Action actSelectUsedParts;
- public ImportPresetSelectPanel() {
+ public ImportPresetSelectPanel(ScaleSupport scaleSupport) {
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
.getLocalizedProperties(ImportWizardDialog.STRINGS_RESOURCE);
popupMenu.add(actSelectUsedParts);
presetTable.setComponentPopupMenu(popupMenu);
+
+ // 列幅の調整
presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ presetTableModel.adjustColumnModel(presetTable.getColumnModel(), scaleSupport.getManualScaleX());
add(new JScrollPane(presetTable), BorderLayout.CENTER);
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);
}
@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;
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();
+ }
}
}
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));
}
}
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;
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;
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;
private final ActiveCustomLayerPatternMgr customLayerPatternMgr = new ActiveCustomLayerPatternMgr();
/**
+ * ダウンロード不要フラグ
+ */
+ private boolean noNeedDataDownload;
+
+ /**
* アクティブなメインフレームを設定する.
*
* @param mainFrame
// アプリケーション設定の変更で画面の再表示を試行する
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の必要がある.
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);
+ }
+ });
}
});
}
}
/**
+ * おすすめ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>
*/
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);
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) {
}
}
+ 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();
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) {
Rectangle windowRect = new Rectangle(windowPos, windowSize);
workingSet.setWindowRect(windowRect);
+ workingSet.setNoNeedDataDownload(noNeedDataDownload);
+
// XML形式でのワーキングセットの保存
WorkingSetPersist workingSetPersist = WorkingSetPersist
.getInstance();
}
}
+ // ダウンロード不要フラグ
+ noNeedDataDownload = workingSet.isNoNeedDataDownload();
+
return true;
} catch (Exception ex) {
import charactermanaj.model.PartsCategory;
import charactermanaj.model.PartsIdentifier;
import charactermanaj.model.PartsSet;
+import charactermanaj.ui.util.ScaleSupport;
import charactermanaj.util.LocalizedResourcePropertyLoader;
/**
setTitle(strings.getProperty("partsRandomChooser"));
+ ScaleSupport scaleSupport = ScaleSupport.getInstance(this);
+
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
int idx = centerPnl.getComponentCount();
RandomChooserPanel pnl = addPartsChooserPanel(centerPnl,
idx, category, lastInCategory,
- changePartsIdentifierListener);
+ changePartsIdentifierListener,
+ scaleSupport);
// 未選択の場合、もしくは複数選択カテゴリの場合はランダムはディセーブルとする
pnl.setEnableRandom(enable
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);
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();
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
if (comp.equals(this)) {
// 同じカテゴリのものを追加する
addPartsChooserPanel(centerPnl, idx + 1, category,
- lastInCategory, changePartsIdentifierListener);
+ lastInCategory, changePartsIdentifierListener,
+ scaleSupport);
centerPnl.validate();
// Addボタンを非表示にする.
((JButton) e.getSource()).setVisible(false);
* @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);
}
}
}
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);
package charactermanaj.ui;
-import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
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));
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);
* ワーカースレッドが停止したことを示すフラグ
*/
private volatile boolean exitThread;
-
+
/**
* ワーカースレッドの戻り値
*/
private volatile T result;
-
+
/**
* ワーカースレッドが例外により終了した場合の例外
*/
private volatile Throwable occuredException;
-
+
/**
* ワーカースレッド
*/
private Thread thread;
-
+
/**
* ワーカースレッドの状態を監視しプログレスに反映させるタイマー
*/
private Timer timer;
-
+
/**
* プログレスの更新頻度(タイマーのインターバル)
*/
private static int interval = 200;
-
+
/**
* 親フレームとワーカーを指定して構築する.<br>
if (worker == null) {
throw new IllegalArgumentException();
}
-
+
initComponent(parent, worker);
-
+
} catch (RuntimeException ex) {
dispose();
throw ex;
}
}
-
+
/**
* 親ダイアログとワーカーを指定して構築する.<br>
* @param parent 親フレーム
if (worker == null) {
throw new IllegalArgumentException();
}
-
+
initComponent(parent, worker);
-
+
} catch (RuntimeException ex) {
dispose();
throw ex;
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);
// パックする.
pack();
-
+
// 親の中央に表示
setLocationRelativeTo(parent);
}
});
}
-
+
super.flush();
}
};
-
+
// プログレスダイアログに状態を反映させるためのタイマー
timer = new Timer(interval, new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
}
-
+
/**
* プログレスの表示間隔を取得する
* @return 表示間隔
public void run() {
try {
try {
- worker.doWork(progressHandle);
+ result = worker.doWork(progressHandle);
} catch (Throwable ex) {
occuredException = ex;
}
};
}
-
+
/**
* ワーカースレッドより、スレッドが終了したことを通知される.<br>
* ワーカースレッド自身か、ワーカースレッドの例外ハンドラか、
}
});
}
-
+
/**
* ワーカースレッドを開始し、プログレスダイアログを表示し、
* ワーカースレッドの完了まで待機する.<br>
* @throws WorkerException ワーカースレッドが例外により終了した場合
*/
- public void startAndWait() throws WorkerException {
+ public T startAndWait() throws WorkerException {
// 初期化
result = null;
occuredException = null;
exitThread = false;
-
+
// ワーカースレッドの開始
thread.start();
try {
occuredException
);
}
+
+ return getResult();
}
/**
--- /dev/null
+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;
+ }
+}
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<entry key="preset.column.missings.size">200</entry>
<entry key="complete">インポートが完了しました。</entry>
-
+
</properties>
<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>
<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>
<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>
+
<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>
<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>
<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>