OSDN Git Service

カスタムレイヤーのインポート・エクスポート、テンプレート登録に対応。
authorseraphy <seraphy@users.osdn.me>
Thu, 27 Dec 2018 22:01:28 +0000 (07:01 +0900)
committerseraphy <seraphy@users.osdn.me>
Thu, 27 Dec 2018 22:01:28 +0000 (07:01 +0900)
および、
・ maven wrapper
・ osxで起動時に-Xcode:name の指定
・ アプリケーション設定ダイアログの操作性改善

58 files changed:
.classpath
.mvn/wrapper/MavenWrapperDownloader.java [new file with mode: 0644]
.mvn/wrapper/maven-wrapper.jar [new file with mode: 0644]
.mvn/wrapper/maven-wrapper.properties [new file with mode: 0644]
.settings/org.eclipse.jdt.core.prefs
mvnw [new file with mode: 0755]
mvnw.cmd [new file with mode: 0644]
src/main/attachment/bundle/CharacterManaJ.app/Contents/MacOS/java_launch.sh
src/main/java/charactermanaj/CharacterManaJ.java
src/main/java/charactermanaj/Main.java
src/main/java/charactermanaj/graphics/io/EmbeddedImageResource.java
src/main/java/charactermanaj/model/AppConfig.java
src/main/java/charactermanaj/model/CharacterData.java
src/main/java/charactermanaj/model/io/AbstractCharacterDataArchiveFile.java
src/main/java/charactermanaj/model/io/AbstractCharacterDataArchivedFileWriter.java
src/main/java/charactermanaj/model/io/AbstractCharacterDataFileWriter.java
src/main/java/charactermanaj/model/io/CharacterDataArchiveFile.java
src/main/java/charactermanaj/model/io/CharacterDataDefaultProvider.java
src/main/java/charactermanaj/model/io/CharacterDataDirectoryFile.java
src/main/java/charactermanaj/model/io/CharacterDataJarArchiveFile.java
src/main/java/charactermanaj/model/io/CharacterDataPersistent.java
src/main/java/charactermanaj/model/io/CharacterDataWriter.java
src/main/java/charactermanaj/model/io/CharacterDataZipArchiveFile.java
src/main/java/charactermanaj/model/io/CustomLayerOrderPersist.java
src/main/java/charactermanaj/model/io/CustomLayerOrderXMLReader.java
src/main/java/charactermanaj/model/io/ImportModel.java
src/main/java/charactermanaj/model/io/WorkingSetPersist.java
src/main/java/charactermanaj/ui/AppConfigDialog.java
src/main/java/charactermanaj/ui/ColorDialog.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/ProfileEditDialog.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/util/ApplicationLogHandler.java
src/main/java/charactermanaj/util/ErrorMessageHelper.java
src/main/java/charactermanaj/util/FileUserData.java
src/main/java/charactermanaj/util/FileUtilities.java [new file with mode: 0644]
src/main/java/charactermanaj/util/LocalizedResourcePropertyLoader.java
src/main/java/charactermanaj/util/LocalizedResourceTextLoader.java
src/main/java/charactermanaj/util/ResourceLoader.java
src/main/java/charactermanaj/util/ResourceNames.java
src/main/java/charactermanaj/util/SetupLocalization.java
src/main/java/charactermanaj/util/UIHelper.java
src/main/resources/languages/appconfigdialog.xml
src/main/resources/languages/appconfigdialog_ja.xml
src/main/resources/languages/appconfigdialog_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/languages/profileditdialog.xml
src/main/resources/languages/profileditdialog_ja.xml
src/main/resources/languages/profileditdialog_zh.xml
src/main/resources/languages/profileselectordialog.xml
src/main/resources/languages/profileselectordialog_ja.xml
src/main/resources/languages/profileselectordialog_zh.xml

index f3164ae..483fd54 100644 (file)
@@ -1,37 +1,39 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<classpath>\r
-       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>\r
-       <classpathentry kind="src" output="target/classes" path="src/main/java">\r
-               <attributes>\r
-                       <attribute name="optional" value="true"/>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">\r
-               <attributes>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry kind="src" output="target/test-classes" path="src/test/java">\r
-               <attributes>\r
-                       <attribute name="optional" value="true"/>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">\r
-               <attributes>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">\r
-               <attributes>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">\r
-               <attributes>\r
-                       <attribute name="maven.pomderived" value="true"/>\r
-               </attributes>\r
-       </classpathentry>\r
-       <classpathentry kind="output" path="target/classes"/>\r
-</classpath>\r
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644 (file)
index 0000000..fa4f7b4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL =
+            "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if(mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if(mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: : " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if(!outputFile.getParentFile().exists()) {
+            if(!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644 (file)
index 0000000..01e6799
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644 (file)
index 0000000..7179346
--- /dev/null
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
index 05cfd62..66c458b 100644 (file)
@@ -1,74 +1,75 @@
-eclipse.preferences.version=1\r
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate\r
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\r
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\r
-org.eclipse.jdt.core.compiler.compliance=1.6\r
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate\r
-org.eclipse.jdt.core.compiler.debug.localVariable=generate\r
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate\r
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning\r
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore\r
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning\r
-org.eclipse.jdt.core.compiler.problem.deadCode=warning\r
-org.eclipse.jdt.core.compiler.problem.deprecation=warning\r
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled\r
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled\r
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning\r
-org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore\r
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore\r
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled\r
-org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore\r
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning\r
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning\r
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=ignore\r
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning\r
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning\r
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore\r
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore\r
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore\r
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning\r
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore\r
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore\r
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore\r
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning\r
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore\r
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning\r
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning\r
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore\r
-org.eclipse.jdt.core.compiler.problem.nullReference=warning\r
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning\r
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore\r
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore\r
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore\r
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning\r
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore\r
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore\r
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled\r
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning\r
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled\r
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore\r
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning\r
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning\r
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore\r
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning\r
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore\r
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore\r
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore\r
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore\r
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled\r
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled\r
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled\r
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning\r
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning\r
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning\r
-org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore\r
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled\r
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled\r
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled\r
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning\r
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning\r
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning\r
-org.eclipse.jdt.core.compiler.source=1.6\r
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=ignore
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/mvnw b/mvnw
new file mode 100755 (executable)
index 0000000..5551fde
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,286 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+  # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        wget "$jarUrl" -O "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        curl -o "$wrapperJarPath" "$jarUrl"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644 (file)
index 0000000..e5cfb0a
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,161 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
+       IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    echo Found %WRAPPER_JAR%
+) else (
+    echo Couldn't find %WRAPPER_JAR%, downloading it ...
+       echo Downloading from: %DOWNLOAD_URL%
+    powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
+    echo Finished downloading %WRAPPER_JAR%
+)
+@REM End of extension
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
index 2f986e9..c5caf04 100755 (executable)
@@ -58,6 +58,10 @@ else
     if [[ -z "$JAVA_HOME" ]]; then
         if [[ -x "/usr/libexec/java_home" ]]; then
             JAVA_HOME="$(/usr/libexec/java_home)"
+            if [[ $? -ne 0 ]]; then
+                # java_homeがエラーを返すならばJavaが無いものと見なす
+                JAVA_HOME=""
+            fi
         fi
     fi
 fi
@@ -79,7 +83,16 @@ fi
 
 # JAVAをオプションとJARを指定して起動する
 if [[ -f $RESOURCE_DIR/$JARNAME ]]; then
-    "$JAVA_CMD" ${jvm_options[@]} -jar "$RESOURCE_DIR/$JARNAME" || ShowMessage "Failed to launch ${APPNAME}"
+       # アプリケーション名とロケールの取得
+       APPTITLE=( $("$JAVA_CMD" -jar "$RESOURCE_DIR/$JARNAME" --show-appinfo) )
+       if [[ $? -ne 0 ]]; then
+               # JAVAの起動に失敗した場合
+        ShowMessage "Could not launch Java SE Runtime Environment. ${APPNAME}"
+        exit 1
+       fi
+
+       # アプリケーションの起動
+    "$JAVA_CMD" ${jvm_options[@]} "-Xdock:name=${APPTITLE[0]}" -jar "$RESOURCE_DIR/$JARNAME" || ShowMessage "Failed to launch ${APPNAME}"
 else
     ShowMessage "$JARNAME is not found."
 fi
index 079c81d..c467cd6 100644 (file)
@@ -1,14 +1,42 @@
 package charactermanaj;
 
+import java.util.Locale;
+import java.util.Properties;
+
+import charactermanaj.model.AppConfig;
+import charactermanaj.util.LocalizedResourcePropertyLoader;
+
 /**
  * Java7 on OSX で、クラス名がメニューの「〜の終了」「〜について」の起動クラス名がアプリ名に使われており、
  * info.pinfoのBundleNameでも変更できないため、回避方法がみつかるまで、本クラス名を表示させることにする。
- * 
+ *
  * @author seraphy
  */
 public class CharacterManaJ {
 
        public static void main(String[] args) throws Exception {
+               // アプリケーション名表示オプションか?
+               for (String arg : args) {
+                       if ("--show-appinfo".equals(arg)) {
+                               showAppinfo();
+                               return;
+                       }
+               }
+
                Main.main(args);
        }
+
+       /**
+        * アプリケーション名の表示
+        */
+       public static void showAppinfo() {
+               Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+                               .getLocalizedProperties("languages/mainframe");
+               AppConfig appConfig = AppConfig.getInstance();
+
+               System.out.println(strings.getProperty("title"));
+               System.out.println(Locale.getDefault().getLanguage());
+               System.out.println(appConfig.getSpecificationVersion());
+               System.out.println(appConfig.getImplementationVersion());
+       }
 }
index 932ab9b..e04d348 100644 (file)
@@ -318,7 +318,7 @@ public final class Main implements Runnable {
                        }
 
                        // スタートアップ時の初期化
-                       // ver0.999ではキャラクターデータディレクトリに依存しない初期化部しかないので最初に移動する。
+                       // ver0.999ではキャラクターデータディレクトリに依存しない初期化部しかないので最初に実行する。
                        // (APPDATAからLOCALAPPDATAへの移動処理などがあるため、先に行う必要がある。)
                        StartupSupport.getInstance().doStartup();
 
@@ -334,13 +334,11 @@ public final class Main implements Runnable {
 
                        if (currentCharacterDir == null) {
                                // キャラクターセットディレクトリの選択
-                               File defaultCharacterDir = ConfigurationDirUtilities
-                                               .getDefaultCharactersDir();
-                               currentCharacterDir = SelectCharatersDirDialog
-                                               .getCharacterDir(defaultCharacterDir);
+                               File defaultCharacterDir = ConfigurationDirUtilities.getDefaultCharactersDir();
+                               currentCharacterDir = SelectCharatersDirDialog.getCharacterDir(defaultCharacterDir);
                                if (currentCharacterDir == null) {
                                        // キャンセルされたので終了する.
-                                       logger.info("luncher canceled.");
+                                       logger.info("launcher canceled.");
                                        return;
                                }
                        }
@@ -363,10 +361,8 @@ public final class Main implements Runnable {
                                                // OSXにしか存在しないクラスを利用するためリフレクションとしている.
                                                // ただしJDKによっては、Apple Java Extensionsがないことも予想されるので、
                                                // その場合はエラーにしない。
-                                               Class<?> clz = Class
-                                                               .forName("charactermanaj.ui.MainFramePartialForMacOSX");
-                                               Method mtd = clz.getMethod("setupScreenMenu",
-                                                               MainFrame.class);
+                                               Class<?> clz = Class.forName("charactermanaj.ui.MainFramePartialForMacOSX");
+                                               Method mtd = clz.getMethod("setupScreenMenu", MainFrame.class);
                                                mtd.invoke(null, mainFrame);
                                        }
 
index 61f52b8..d613622 100644 (file)
@@ -15,7 +15,7 @@ import charactermanaj.util.ResourceLoader;
  * クラスローダからリソースを読み込むイメージリソース.<br>
  * @author seraphy
  */
-public class EmbeddedImageResource extends ResourceLoader implements ImageResource {
+public class EmbeddedImageResource implements ImageResource {
 
        /**
         * ロガー
@@ -23,6 +23,11 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
        private static final Logger logger = Logger.getLogger(EmbeddedImageResource.class.getName());
 
        /**
+        * クラスパス上からのリソースローダー
+        */
+       private final ResourceLoader resourceLoader = new ResourceLoader(ResourceLoader.getDefaultClassLoader());
+
+       /**
         * ファイル
         */
        private String resourceName;
@@ -35,6 +40,7 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
                this.resourceName = resourceName;
        }
 
+       @Override
        public int compareTo(ImageResource o) {
                return getFullName().compareTo(o.getFullName());
        }
@@ -56,10 +62,12 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
                return false;
        }
 
+       @Override
        public String getFullName() {
                return resourceName;
        }
 
+       @Override
        public URI getURI() {
                URL url = getResource(resourceName);
                if (url != null) {
@@ -78,6 +86,7 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
         * リソースが実在すれば日付は常に1を返す.<br>
         * リソースが存在しなければ0を返す.<br>
         */
+       @Override
        public long lastModified() {
                URL url = getResource(resourceName);
                if (url == null) {
@@ -86,6 +95,7 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
                return 0;
        }
 
+       @Override
        public InputStream openStream() throws IOException {
                URL url = getResource(resourceName);
                if (url == null) {
@@ -98,4 +108,8 @@ public class EmbeddedImageResource extends ResourceLoader implements ImageResour
        public String toString() {
                return getFullName();
        }
+
+       private URL getResource(String resourceName) {
+               return resourceLoader.getResource(resourceName);
+       }
 }
index f7bfeff..e205f13 100644 (file)
@@ -1527,4 +1527,20 @@ public final class AppConfig {
                        propChangeSupport.firePropertyChange(ENABLE_COLOR_ADVANCED_SETTINGS, old, enableRestoreWindow);
                }
        }
+
+       private boolean useRecycleBinIfSupported = true;
+
+       public boolean isUseRecycleBinIfSupported() {
+               return useRecycleBinIfSupported;
+       }
+
+       public static final String USE_RECYCLE_BIN_IF_SUPPORTED = "useRecycleBinIfSupported";
+
+       public void setUseRecycleBinIfSupported(boolean useRecycleBinIfSupported) {
+               boolean old = this.useRecycleBinIfSupported;
+               if (old != useRecycleBinIfSupported) {
+                       this.useRecycleBinIfSupported = useRecycleBinIfSupported;
+                       propChangeSupport.firePropertyChange(USE_RECYCLE_BIN_IF_SUPPORTED, old, useRecycleBinIfSupported);
+               }
+       }
 }
index 38f7ac6..9d48792 100644 (file)
@@ -180,6 +180,7 @@ public class CharacterData implements PartsSpecResolver {
                cd.setImageSize((Dimension)(this.imageSize == null ? null : this.imageSize.clone()));
 
                cd.setWatchDirectory(this.isWatchDirectory());
+               cd.setEnableCustomLayerPattern(this.isEnableCustonLayerPattern());
 
                ArrayList<RecommendationURL> recommendationURLList = null;
                if (this.recommendationURLList != null) {
@@ -514,18 +515,20 @@ public class CharacterData implements PartsSpecResolver {
                return docBase;
        }
 
+       private static final String PROPNAME_WATCH_DIR = "watch-dir";
+
        /**
         * ディレクトリを監視するか? (デフォルトは監視する)
         * @return ディレクトリを監視する場合はtrue
         */
        public boolean isWatchDirectory() {
                try {
-                       String value = properties.getProperty("watch-dir");
+                       String value = properties.getProperty(PROPNAME_WATCH_DIR);
                        if (value != null) {
                                return Boolean.parseBoolean(value);
                        }
                } catch (RuntimeException ex) {
-                       logger.log(Level.WARNING, "watch-dir property is invalid.", ex);
+                       logger.log(Level.WARNING, PROPNAME_WATCH_DIR + " property is invalid.", ex);
                }
                // デフォルトは監視する.
                return true;
@@ -536,7 +539,34 @@ public class CharacterData implements PartsSpecResolver {
         * @param watchDir 監視する場合はtrue、しない場合はfalse
         */
        public void setWatchDirectory(boolean watchDir) {
-               properties.setProperty("watch-dir", Boolean.toString(watchDir));
+               properties.setProperty(PROPNAME_WATCH_DIR, Boolean.toString(watchDir));
+       }
+
+       private static final String PROPNAME_ENABLE_CUSTOM_LAYER_PATTERN = "custom-layer-pattern";
+
+       /**
+        * カスタムレイヤーパターンが有効であるか?(デフォルトは有効)
+        * @return カスタムレイヤーパターンが有効である場合はtrue
+        */
+       public boolean isEnableCustonLayerPattern() {
+               try {
+                       String value = properties.getProperty(PROPNAME_ENABLE_CUSTOM_LAYER_PATTERN);
+                       if (value != null) {
+                               return Boolean.parseBoolean(value);
+                       }
+               } catch (RuntimeException ex) {
+                       logger.log(Level.WARNING, PROPNAME_ENABLE_CUSTOM_LAYER_PATTERN + " property is invalid.", ex);
+               }
+               // デフォルトは有効.
+               return true;
+       }
+
+       /**
+        * カスタムレイヤーパターンの有効・無効を設定する。
+        * @param enableCustomLayerPattern 有効にする場合はtrue、無効にする場合はfalse
+        */
+       public void setEnableCustomLayerPattern(boolean enableCustomLayerPattern) {
+               properties.setProperty(PROPNAME_ENABLE_CUSTOM_LAYER_PATTERN, Boolean.toString(enableCustomLayerPattern));
        }
 
        public String getProperty(String key) {
index 53da634..28d5d02 100644 (file)
@@ -11,6 +11,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.logging.Level;
@@ -21,14 +22,14 @@ import javax.imageio.ImageIO;
 import charactermanaj.graphics.io.PNGFileImageHeader;
 import charactermanaj.graphics.io.PNGFileImageHeaderReader;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.Layer;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsManageData;
 import charactermanaj.model.io.CharacterDataDefaultProvider.DefaultCharacterDataVersion;
 
-public abstract class AbstractCharacterDataArchiveFile
-               implements
-                       CharacterDataArchiveFile {
+public abstract class AbstractCharacterDataArchiveFile implements CharacterDataArchiveFile {
 
        private static final Logger logger = Logger
                        .getLogger(AbstractCharacterDataArchiveFile.class.getName());
@@ -43,8 +44,9 @@ public abstract class AbstractCharacterDataArchiveFile
 
                long lastModified();
 
-               InputStream openStream() throws IOException;
+               long length();
 
+               InputStream openStream() throws IOException;
        }
 
        @Override
@@ -114,7 +116,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
                /**
                 * パーツイメージを示す.<br>
-                * 
+                *
                 * @param fileContent
                 *            ファイルコンテンツ
                 * @param categoryLayerPairs
@@ -170,6 +172,7 @@ public abstract class AbstractCharacterDataArchiveFile
                        return dirName;
                }
 
+               @Override
                public String getEntryName() {
                        return fileContent.getEntryName();
                }
@@ -186,10 +189,17 @@ public abstract class AbstractCharacterDataArchiveFile
                        return pngFileImageHeader;
                }
 
+               @Override
                public long lastModified() {
                        return fileContent.lastModified();
                }
 
+               @Override
+               public long length() {
+                       return fileContent.length();
+               }
+
+               @Override
                public InputStream openStream() throws IOException {
                        return fileContent.openStream();
                }
@@ -225,7 +235,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * アーカイブファイルをベースとしたURIを返す.<br>
-        * 
+        *
         * @param name
         *            コンテンツ名
         * @return URI
@@ -246,11 +256,12 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * 指定したコンテンツが存在するか?
-        * 
+        *
         * @param name
         *            コンテンツ名
         * @return 存在すればtrue、存在しなければfalse
         */
+       @Override
        public boolean hasContent(String name) {
                return entries.containsKey(name);
        }
@@ -258,7 +269,7 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * 指定したコンテンツを取得する.<br>
         * 存在しない場合はnullを返す.<br>
-        * 
+        *
         * @param name
         *            コンテンツ名
         * @return 存在すればコンテンツ、存在しなければnull
@@ -267,6 +278,7 @@ public abstract class AbstractCharacterDataArchiveFile
                return entries.get(name);
        }
 
+       @Override
        public String getRootPrefix() {
                return this.rootPrefix;
        }
@@ -304,7 +316,7 @@ public abstract class AbstractCharacterDataArchiveFile
         * 指定したディレクトリ(フルパス指定)のファイルのみを取り出す.<br>
         * サブフォルダは含まない.<br>
         * ディレクトリに空文字またはnullまたは「/」を指定した場合はルートを意味する.<br>
-        * 
+        *
         * @param dir
         *            ディレクトリ
         * @return ファイルへのフルパスのコレクション
@@ -341,9 +353,10 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * キャラクター定義を読み込む.<br>
         * アーカイブに存在しないか、妥当性がない場合はnullを返す.<br>
-        * 
+        *
         * @return キャラクター定義、もしくはnull
         */
+       @Override
        public CharacterData readCharacterData() {
                FileContent characterFile = entries.get(rootPrefix
                                + CharacterDataPersistent.CONFIG_FILE);
@@ -382,9 +395,10 @@ public abstract class AbstractCharacterDataArchiveFile
         * 「キャラクターなんとか機 v2.2」の設定ファイルを想定している.<br>
         * ただし、character.iniの下にeye_colorフォルダがある場合は「ver3」とみなす。<br>
         * (設定ファイルの形式はv2, v3の間で変わらず)
-        * 
+        *
         * @return キャラクター定義、もしくはnull
         */
+       @Override
        public CharacterData readCharacterINI() {
                FileContent characterFile = null;
                characterFile = entries.get(rootPrefix
@@ -450,14 +464,41 @@ public abstract class AbstractCharacterDataArchiveFile
                }
        }
 
+       @Override
+       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> readCustomLayerPatterns(CharacterData cd)
+                       throws IOException {
+               FileContent mapFile = entries.get(rootPrefix
+                               + CustomLayerOrderXMLPersist.CUSTOM_LAYER_ORDERS_XML_FILE);
+               if (mapFile == null) {
+                       // ファイルがない
+                       return null;
+               }
+
+               if (mapFile.length() == 0) {
+                       // 空ファイルは空マップを返す
+                       return Collections.emptyMap();
+               }
+
+               CustomLayerOrderXMLReader xmlReader = new CustomLayerOrderXMLReader(cd);
+               try {
+                       // character.xmlを読み込む
+                       return xmlReader.read(mapFile.openStream());
+
+               } catch (Exception ex) {
+                       logger.log(Level.INFO, CustomLayerOrderXMLPersist.CUSTOM_LAYER_ORDERS_XML_FILE + " load failed.", ex);
+                       return null;
+               }
+       }
+
        /**
         * お気に入りを読み込みキャラクター定義に追加する.<br>
         * アーカイブにお気に入り(favorites.xml)が存在しなければ何もしない.<br>
         * 読み取り中に失敗した場合は、その時点で読み込みを止めて戻る.<br>
-        * 
+        *
         * @param characterData
         *            キャラクター定義(お気に入りが追加される)
         */
+       @Override
        public void readFavorites(CharacterData characterData) {
                if (characterData == null) {
                        throw new IllegalArgumentException("characterDataにnullは指定できません。");
@@ -491,9 +532,10 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * サンプルピクチャを読み込む.<br>
         * アーカイブに存在しないか、画像として読み取れなかった場合はnull
-        * 
+        *
         * @return サンプルピクチャ、もしくはnull
         */
+       @Override
        public BufferedImage readSamplePicture() {
                FileContent samplePictureFile = entries.get(rootPrefix + "preview.png");
                if (samplePictureFile == null) {
@@ -541,9 +583,10 @@ public abstract class AbstractCharacterDataArchiveFile
         * readme.txtが優先される。ともに存在しない場合はnull.<br>
         * 改行コードはプラットフォーム固有の改行コードに変換して返される.<br>
         * 読み取れなかった場合はnull.<br>
-        * 
+        *
         * @return テキスト、もしくはnull
         */
+       @Override
        public String readReadMe() {
 
                Locale locale = Locale.getDefault();
@@ -590,13 +633,14 @@ public abstract class AbstractCharacterDataArchiveFile
         * BOMがない場合はUTF-16/8ともに判定できない.<br>
         * BOMがなければMS932もしくはEUC_JPであると仮定して読み込む.<br>
         * 改行コードはプラットフォーム固有の改行コードに変換して返される.<br>
-        * 
+        *
         * @param name
         *            コンテンツ名
         * @return テキスト、コンテンツが存在しない場合はnull
         * @throws IOException
         *             読み込みに失敗した場合
         */
+       @Override
        public String readTextFile(String name) throws IOException {
                if (name == null) {
                        throw new IllegalArgumentException();
@@ -613,7 +657,7 @@ public abstract class AbstractCharacterDataArchiveFile
         * UTF-16LE/BE/UTF-8についてはBOMにより判定する.<br>
         * BOMがない場合はUTF-16/8ともに判定できない.<br>
         * BOMがなければMS932もしくはEUC_JPであると仮定して読み込む.<br>
-        * 
+        *
         * @param content
         *            コンテンツ
         * @return テキスト
@@ -633,12 +677,12 @@ public abstract class AbstractCharacterDataArchiveFile
         * enabledRootPefixがtrueの場合、ディレクトリの先頭はアーカイブのコンテンツルートとなる.<br>
         * 同一のディレクトリに対して複数のレイヤー(複数カテゴリを含む)が参照している場合、それらを含めて返す.<br>
         * 参照されているディレクトリがない場合は返される結果には含まれない.<br>
-        * 
+        *
         * @param characterData
         *            キャラクター定義
         * @param enabledRootPrefix
         *            ルートプレフィックス(アーカイブのコンテンツルート)を付与する場合
-        * @return 
+        * @return
         *         パーツで使用する画像のディレクトリとして認識されるディレクトリの一覧、キーはアーカイブのディレクトリ位置、値は参照する1つ以上のレイヤー
         */
        protected Map<String, Collection<CategoryLayerPair>> getLayerDirs(
@@ -673,7 +717,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * アーカイブに含まれるフォルダをもつpngファイルからパーツイメージを取得する.<br>
-        * 
+        *
         * @param インポート先のキャラクターデータ
         *            、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
         * @param newly
@@ -681,6 +725,7 @@ public abstract class AbstractCharacterDataArchiveFile
         *            (アーカイブファイルからの読み込みでは無視される)
         * @return パーツイメージコンテンツのコレクション、なければ空
         */
+       @Override
        public Collection<PartsImageContent> getPartsImageContents(
                        CharacterData characterData, boolean newly) {
                // コンテンツルートからの絶対位置指定でパーツイメージを取得する.
@@ -694,7 +739,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * コンテンツルートからの絶対位置のフォルダからpngファイルからパーツイメージを取得する.<br>
-        * 
+        *
         * @param インポート先のキャラクターデータ
         *            、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
         * @return パーツイメージコンテンツのコレクション、なければ空
@@ -705,6 +750,7 @@ public abstract class AbstractCharacterDataArchiveFile
                                characterData, true);
 
                CategoryLayerPairResolveStrategy strategy = new CategoryLayerPairResolveStrategy() {
+                       @Override
                        public Collection<CategoryLayerPair> resolveCategoryLayerPairs(
                                        String dir) {
                                Collection<CategoryLayerPair> categoryLayerPairs = layerDirMap
@@ -723,7 +769,7 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * アーカイブに含まれる任意のフォルダからpngファイルからパーツイメージを取得する.<br>
         * ディレクトリ名の大文字・小文字は区別されません.<br>
-        * 
+        *
         * @param インポート先のキャラクターデータ
         *            、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
         * @return パーツイメージコンテンツのコレクション、なければ空
@@ -734,6 +780,7 @@ public abstract class AbstractCharacterDataArchiveFile
                                characterData, false);
 
                CategoryLayerPairResolveStrategy strategy = new CategoryLayerPairResolveStrategy() {
+                       @Override
                        public Collection<CategoryLayerPair> resolveCategoryLayerPairs(
                                        String dir) {
                                dir = (dir == null) ? "" : dir.toLowerCase();
@@ -755,7 +802,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * ディレクトリ名からカテゴリとレイヤーを取得するためのインターフェイス.<br>
-        * 
+        *
         * @author seraphy
         */
        protected interface CategoryLayerPairResolveStrategy {
@@ -765,7 +812,7 @@ public abstract class AbstractCharacterDataArchiveFile
                 * 同一のディレクトリに対して複数のレイヤーが割り当てられている可能性があるためコレクションで返されます.<br>
                 * 空のコレクションにはなりません.<br>
                 * レイヤーとして認識されていないディレクトリの場合はnullを返します.<br>
-                * 
+                *
                 * @param dir
                 *            ディレクトリ
                 * @return カテゴリ・レイヤーのペアのコレクション、またはnull (空のコレクションにはならない。)
@@ -776,7 +823,7 @@ public abstract class AbstractCharacterDataArchiveFile
 
        /**
         * アーカイブに含まれるフォルダをもつpngファイルからパーツイメージを取得する。
-        * 
+        *
         * @param strategy
         *            ディレクトリが売れ入れ可能であるか判断するストラテジー
         * @return パーツイメージコンテンツのコレクション、なければ空
@@ -839,7 +886,7 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * PNGファイルとしてファイルを読み込みPNGヘッダ情報を返します.<br>
         * PNGでないか、ファイルの読み込みに失敗した場合はnullを返します.<br>
-        * 
+        *
         * @param fileContent
         *            画像ファイル
         * @return PNGヘッダ情報、またはnull
@@ -866,10 +913,11 @@ public abstract class AbstractCharacterDataArchiveFile
        /**
         * アーカイブに含まれるparts-info.xmlを読み込み返す.<br>
         * 存在しなければ空のインスタンスを返す.<br>
-        * 
+        *
         * @return パーツ管理データ
         * @throws IOException
         */
+       @Override
        public PartsManageData getPartsManageData() throws IOException {
                FileContent content = entries.get(rootPrefix + "parts-info.xml");
                if (content == null) {
index ef33bd8..2d6d3cb 100644 (file)
@@ -20,6 +20,8 @@ import charactermanaj.graphics.io.ImageResource;
 import charactermanaj.graphics.io.ImageSaveHelper;
 import charactermanaj.model.AppConfig;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.Layer;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsFiles;
@@ -29,16 +31,34 @@ import charactermanaj.model.PartsManageDataConverter;
 import charactermanaj.model.PartsSet;
 import charactermanaj.model.PartsSpec;
 
+/**
+ * キャラクターデータをアーカイブ(zip, jar形式)で保存するための共通クラス.
+ */
 public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCharacterDataFileWriter {
 
        protected AbstractCharacterDataArchivedFileWriter(File outFile) throws IOException {
                super(outFile);
        }
-       
+
+       /**
+        * 現在のエントリの出力ストリームを取得する
+        * @return
+        * @throws IOException
+        */
        protected abstract OutputStream getOutputStream() throws IOException;
-       
+
+       /**
+        * 次のエントリに進む
+        * @param name エントリ名
+        * @param lastModified 最終更新日時(0以下の場合はデフォルトとする)
+        * @throws IOException
+        */
        protected abstract void putNextEntry(String name, long lastModified) throws IOException;
-       
+
+       /**
+        * 現在のエントリを閉じる
+        * @throws IOException
+        */
        protected abstract void closeEntry() throws IOException;
 
        @Override
@@ -52,19 +72,19 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
        protected void internalWriteCharacterData(CharacterData characterData)
                        throws IOException {
                CharacterDataXMLWriter xmlWriter = new CharacterDataXMLWriter();
-               
+
                // character.xmlの出力
                putNextEntry(CharacterDataPersistent.CONFIG_FILE, 0);
                xmlWriter.writeXMLCharacterData(characterData, getOutputStream());
                closeEntry();
-               
+
                // character.iniの出力
                internalWriteCharacterIni(characterData);
        }
 
        /**
         * character.iniを出力します.<br>
-        * 
+        *
         * @param characterData
         *            キャラクターデータ
         * @throws IOException
@@ -75,7 +95,7 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
 
                buf.append("; created by charactermanaj "
                                + new Timestamp(System.currentTimeMillis()) + "\r\n");
-               
+
                buf.append("[Size]\r\n");
                Dimension dim = characterData.getImageSize();
                if (dim == null) {
@@ -86,13 +106,13 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
 
                buf.append("\r\n");
                buf.append("[Parts]\r\n");
-               
+
                Map<String, String> partsMap = new HashMap<String, String>();
                for (PartsCategory partsCategory : characterData.getPartsCategories()) {
                        String categoryId = partsCategory.getCategoryId();
                        partsMap.put(categoryId, "");
                }
-               
+
                Map<String, PartsSet> partsSets = characterData.getPartsSets();
                PartsSet partsSet = partsSets.get(characterData.getDefaultPartsSetId());
                if (partsSet == null && !partsSets.isEmpty()) {
@@ -135,7 +155,21 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
                // UTF16LEで出力する.
                internalWriteTextUTF16LE(CharacterDataPersistent.COMPATIBLE_CONFIG_NAME, buf.toString());
        }
-       
+
+       @Override
+       protected void internalWriteCustomLayerPatterns(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map)
+                       throws IOException {
+               if (map == null) {
+                       return;
+               }
+
+               // カスタムレイヤーパターンを出力する
+               CustomLayerOrderXMLWriter xmlWriter = new CustomLayerOrderXMLWriter();
+               putNextEntry(CustomLayerOrderPersist.CUSTOM_LAYER_ORDERS_XML_FILE, 0);
+               xmlWriter.write(map, getOutputStream());
+               closeEntry();
+       }
+
        @Override
        protected void internalWriteTextUTF16LE(String name, String contents) throws IOException {
                if (contents == null) {
@@ -146,7 +180,7 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
                contents = contents.replace("\r\n", "\n");
                contents = contents.replace("\r", "\n");
                contents = contents.replace("\n", "\r\n");
-               
+
                putNextEntry(name, 0);
                OutputStream os = getOutputStream();
                os.write((byte) 0xff);
@@ -168,7 +202,7 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
                        wr.close();
                }
        }
-       
+
        @Override
        protected void internalWriteSamplePicture(BufferedImage samplePicture)
                        throws IOException {
@@ -177,25 +211,25 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
                imageSaveHelper.savePicture(samplePicture, Color.white, getOutputStream(), "image/png", null);
                closeEntry();
        }
-       
+
        @Override
        protected void internalWritePartsImages(
                        Map<PartsIdentifier, PartsSpec> partsImages) throws IOException {
                AppConfig appConfig = AppConfig.getInstance();
                byte[] buf = new byte[appConfig.getJarTransferBufferSize()];
-               
+
                for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsImages.entrySet()) {
                        PartsIdentifier partsIdentifier = entry.getKey();
                        PartsSpec partsSpec = entry.getValue();
                        PartsFiles partsFiles = partsSpec.getPartsFiles();
-                       
+
                        for (Map.Entry<Layer, ImageResource> imageEntry : partsFiles.entrySet()) {
                                Layer layer = imageEntry.getKey();
                                ImageResource imageResource = imageEntry.getValue();
 
                                String name = layer.getDir() + "/" + partsIdentifier.getPartsName() + ".png";
                                name = name.replace("//", "/");
-                               
+
                                putNextEntry(name, imageResource.lastModified());
                                OutputStream os = getOutputStream();
                                InputStream is = imageResource.openStream();
@@ -211,19 +245,19 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh
                        }
                }
        }
-       
+
        @Override
        protected void internalWritePartsManageData(
                        Map<PartsIdentifier, PartsSpec> partsImages) throws IOException {
-               
+
                PartsManageDataConverter partsManageDataConverter = new PartsManageDataConverter();
-               
+
                for (Map.Entry<PartsIdentifier, PartsSpec> entry : partsImages.entrySet()) {
                        PartsIdentifier partsIdentifier = entry.getKey();
                        PartsSpec partsSpec = entry.getValue();
                        partsManageDataConverter.convert(partsIdentifier, partsSpec);
                }
-               
+
                PartsManageData partsManageData = partsManageDataConverter.getPartsManageData();
                PartsInfoXMLWriter xmlWriter = new PartsInfoXMLWriter();
 
index a9f8ad8..ab243aa 100644 (file)
@@ -3,14 +3,18 @@ package charactermanaj.model.io;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSpec;
+import charactermanaj.util.FileUtilities;
 
 public abstract class AbstractCharacterDataFileWriter implements CharacterDataWriter {
 
@@ -20,35 +24,36 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
        private static final Logger logger = Logger.getLogger(AbstractCharacterDataFileWriter.class.getName());
 
        protected File outFile;
-       
+
        protected File tmpFile;
-       
+
        protected Exception occureException;
-       
+
        protected AbstractCharacterDataFileWriter(File outFile) throws IOException {
                if (outFile == null) {
                        throw new IllegalArgumentException();
                }
-               
+
                if (outFile.exists()) {
                        if (!outFile.canWrite()) {
                                throw new IOException("not writable: " + outFile);
                        }
                }
                File tmpFile = new File(outFile.getPath() + ".tmp");
-               
+
                this.tmpFile = tmpFile;
                this.outFile = outFile;
        }
-       
+
+       @Override
        public void writeExportProp(Properties prop) throws IOException {
                if (prop == null) {
                        throw new IllegalArgumentException();
                }
-               
+
                try {
                        internalWriteExportProp(prop);
-                       
+
                } catch (IOException ex) {
                        occureException = ex;
                        throw ex;
@@ -60,18 +65,19 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        throw ex2;
                }
        }
-       
+
        protected abstract void internalWriteExportProp(Properties prop) throws IOException;
-       
 
+
+       @Override
        public void writeCharacterData(CharacterData characterData) throws IOException {
                if (characterData == null) {
                        throw new IllegalArgumentException();
                }
-               
+
                try {
                        internalWriteCharacterData(characterData);
-                       
+
                } catch (IOException ex) {
                        occureException = ex;
                        throw ex;
@@ -83,17 +89,39 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        throw ex2;
                }
        }
-       
+
        protected abstract void internalWriteCharacterData(CharacterData characterData) throws IOException;
-       
+
+
+       @Override
+       public void writeCustomLayerPatterns(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) throws IOException {
+               try {
+                       internalWriteCustomLayerPatterns(map);
+
+               } catch (IOException ex) {
+                       occureException = ex;
+                       throw ex;
+
+               } catch (Exception ex) {
+                       occureException = ex;
+                       IOException ex2 = new IOException("write characterdata failed.");
+                       ex2.initCause(ex);
+                       throw ex2;
+               }
+       }
+
+       protected abstract void internalWriteCustomLayerPatterns(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map)
+                       throws IOException;
+
+       @Override
        public void writeTextUTF16LE(String name, String contents) throws IOException {
                if (name == null) {
                        throw new IllegalArgumentException();
                }
-               
+
                try {
                        internalWriteTextUTF16LE(name, contents);
-                       
+
                } catch (IOException ex) {
                        occureException = ex;
                        throw ex;
@@ -105,9 +133,10 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        throw ex2;
                }
        }
-       
+
        protected abstract void internalWriteTextUTF16LE(String name, String contents) throws IOException;
-       
+
+       @Override
        public void writeSamplePicture(BufferedImage samplePicture) throws IOException {
                if (samplePicture == null) {
                        throw new IllegalArgumentException();
@@ -130,6 +159,7 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
 
        protected abstract void internalWriteSamplePicture(BufferedImage samplePicture) throws IOException;
 
+       @Override
        public void writePartsImages(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException {
                if (partsImages == null) {
                        throw new IllegalArgumentException();
@@ -149,9 +179,10 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        throw ex2;
                }
        }
-       
+
        protected abstract void internalWritePartsImages(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException;
 
+       @Override
        public void writePartsManageData(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException {
                if (partsImages == null) {
                        throw new IllegalArgumentException();
@@ -171,18 +202,17 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        throw ex2;
                }
        }
-       
+
        protected abstract void internalWritePartsManageData(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException;
 
-       
+
+       @Override
        public void close() throws IOException {
                try {
                        internalClose();
-                       
+
                        if (outFile.exists()) {
-                               if (!outFile.delete()) {
-                                       throw new IOException("old file can't delete. " + outFile);
-                               }
+                               FileUtilities.delete(outFile);
                        }
 
                } catch (Exception ex) {
@@ -196,11 +226,11 @@ public abstract class AbstractCharacterDataFileWriter implements CharacterDataWr
                        }
                        return;
                }
-               
+
                if (!tmpFile.renameTo(outFile)) {
                        throw new IOException("rename failed. " + tmpFile);
                }
        }
-       
+
        protected abstract void internalClose() throws IOException;
 }
index 98a3ef6..764f4a7 100644 (file)
@@ -3,8 +3,12 @@ package charactermanaj.model.io;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsManageData;
 import charactermanaj.model.io.AbstractCharacterDataArchiveFile.PartsImageContent;
 
@@ -20,7 +24,7 @@ public interface CharacterDataArchiveFile {
         * @throws IOException
         */
        void close() throws IOException;
-       
+
        /**
         * コンテンツルートへのプレフィックスを取得する.<br>
         * アーカイブの実際のルートに単一のフォルダしかない場合、そのフォルダがルートプレフィックスとなる.<br>
@@ -28,14 +32,14 @@ public interface CharacterDataArchiveFile {
         * @return コンテンツルートへのプレフィックス
         */
        String getRootPrefix();
-       
+
        /**
         * 指定したコンテンツが存在するか?
         * @param name コンテンツ名
         * @return 存在すればtrue、存在しなければfalse
         */
        boolean hasContent(String name);
-       
+
        /**
         * キャラクター定義を読み込む.<br>
         * アーカイブに存在しなければnull
@@ -43,7 +47,16 @@ public interface CharacterDataArchiveFile {
         * @throws IOException 読み取りに失敗した場合
         */
        CharacterData readCharacterData() throws IOException;
-       
+
+       /**
+        * カスタムレイヤーパターン定義を読みこむ
+        * アーカイブに存在しないければnull
+        * @param cd キャラクターデータ(カテゴリ情報が必要なため)
+        * @return カスタムレイヤーパターン、もしくはnull
+        * @throws IOException 読み取りに失敗した場合
+        */
+       Map<CustomLayerOrderKey, List<CustomLayerOrder>> readCustomLayerPatterns(CharacterData cd) throws IOException;
+
        /**
         * お気に入りを読み込みキャラクター定義に追加する.<br>
         * アーカイブにお気に入りが存在しなければ何もしない.<br>
@@ -51,7 +64,7 @@ public interface CharacterDataArchiveFile {
         * @throws IOException 読み取りに失敗した場合
         */
        void readFavorites(CharacterData characterData) throws IOException;
-       
+
        /**
         * キャラクター定義をINIファイルより読み取る.<br>
         * character.iniが存在しなければnull.<br>
@@ -60,7 +73,7 @@ public interface CharacterDataArchiveFile {
         * @throws IOException 読み取りに失敗した場合
         */
        CharacterData readCharacterINI() throws IOException;
-       
+
        /**
         * サンプルピクチャを読み込む.<br>
         * アーカイブに存在しなければnull.
@@ -68,26 +81,26 @@ public interface CharacterDataArchiveFile {
         * @throws IOException 読み取りに失敗した場合
         */
        BufferedImage readSamplePicture() throws IOException;
-       
+
        /**
         * アーカイブにある「readme.txt」、もしくは「readme」というファイルをテキストファイルとして読み込む.<br>
-        * readme.txtが優先される。ともに存在しない場合はnull. 
+        * readme.txtが優先される。ともに存在しない場合はnull.
         * @return テキスト、もしくはnull
         * @throws 読み込みに失敗した場合
         */
        String readReadMe() throws IOException;
-       
+
        /**
         * ファイルをテキストとして読み取り、返す.<br>
         * UTF-16LE/BE/UTF-8についてはBOMにより判定する.<br>
         * BOMがない場合はUTF-16/8ともに判定できない.<br>
         * BOMがなければMS932もしくはEUC_JPであると仮定して読み込む.<br>
-        * @param name コンテンツ名 
+        * @param name コンテンツ名
         * @return テキスト、コンテンツが存在しない場合はnull
         * @throws IOException 読み込みに失敗した場合
         */
        String readTextFile(String name) throws IOException;
-       
+
        /**
         * アーカイブに含まれる、キャラクター定義と同じフォルダをもつpngファイルからパーツイメージを取得する.<br>
         * パーツの探索は引数で指定したキャラクター定義によって行われます.<br>
@@ -96,7 +109,7 @@ public interface CharacterDataArchiveFile {
         * @return パーツイメージコンテンツのコレクション、なければ空
         */
        Collection<PartsImageContent> getPartsImageContents(CharacterData characterData, boolean newly);
-       
+
        /**
         * アーカイブに含まれるparts-info.xmlを読み込み返す.<br>
         * 存在しなければ空のインスタンスを返す.<br>
index 91d1d50..8b2c9bb 100644 (file)
@@ -1,7 +1,9 @@
 package charactermanaj.model.io;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -9,7 +11,6 @@ import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
 import java.sql.Timestamp;
-import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -21,10 +22,10 @@ import java.util.logging.Logger;
 import charactermanaj.model.CharacterData;
 import charactermanaj.model.CustomLayerOrder;
 import charactermanaj.model.CustomLayerOrderKey;
+import charactermanaj.model.PartsCategoryResolver;
 import charactermanaj.util.ConfigurationDirUtilities;
 import charactermanaj.util.LocalizedResourcePropertyLoader;
 import charactermanaj.util.ResourceLoader;
-import charactermanaj.util.ResourceNames;
 import charactermanaj.util.SetupLocalization;
 
 /**
@@ -61,6 +62,12 @@ public class CharacterDataDefaultProvider {
        private static final String CUSTOM_LAYER_ORDERS_SUFFIX = "-customlayerorders.xml";
 
        /**
+        * リソースローダー
+        * ローカルファイルをクラスパスより優先する。
+        */
+       private final ResourceLoader resourceLoader = new ResourceLoader(true);
+
+       /**
         * ロガー
         */
        private static final Logger logger = Logger
@@ -94,13 +101,13 @@ public class CharacterDataDefaultProvider {
                        }
                }
 
-               public Map<CustomLayerOrderKey, List<CustomLayerOrder>> createCustomLayerOrderMap(CharacterData cd,
-                               CharacterDataDefaultProvider prov) {
+               public Map<CustomLayerOrderKey, List<CustomLayerOrder>> createCustomLayerOrderMap(
+                               PartsCategoryResolver partsCategoryResolver, CharacterDataDefaultProvider prov) {
                        if (prov == null) {
                                throw new IllegalArgumentException();
                        }
                        try {
-                               return prov.loadPredefinedCustomLayerOrder(cd, reskey);
+                               return prov.loadPredefinedCustomLayerOrder(partsCategoryResolver, reskey);
 
                        } catch (IOException ex) {
                                throw new RuntimeException(
@@ -127,86 +134,79 @@ public class CharacterDataDefaultProvider {
                return version.create(this);
        }
 
-       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> createDefaultCustomLayerOrderMap(
-                       CharacterData cd, DefaultCharacterDataVersion version) {
-               if (version == null) {
-                       throw new IllegalArgumentException();
-               }
-               return version.createCustomLayerOrderMap(cd, this);
-       }
-
        /**
-        * テンプレートリストの定義プロパティを読み込む.<br>
-        * neutral引数がfalseの場合は現在のロケールを優先する.<br>
-        * 引数がtrueの場合は読み込み順を逆転させ、言語中立を優先する.<br>
+        * デフォルトのキャラクター定義に付随するカスタムレイヤーパターンを生成して返す。
         *
-        * @param neutral
-        *            言語中立を優先する場合
-        * @return テンプレートリストのプロパティ
+        * 引数のpartsCategoryResolverは、カスタムレイヤーパターンはパーツカテゴリインスタンスを保持するため、
+        * そのキャラクターデータと同じインスタンスのパーツカテゴリインスタンスを取得できるようにするためのものである。
+        * @param partsCategoryResolver カテゴリIDでカテゴリを索引するリゾルバ
+        * @param version バージョン
+        * @return 付随するカスタムレイヤーパターン、なければnull
         */
-       private Properties getTemplateListProperties(boolean neutral) {
-               // テンプレートリソースは実行中に増減する可能性があるため、
-               // 共有キャッシュには入れない.
-               LocalizedResourcePropertyLoader loader = new LocalizedResourcePropertyLoader(
-                               null);
-               String name = DEFAULT_CHARACTER_PREFIX + TEMPLATE_LIST_XML;
-               ResourceNames resNames = LocalizedResourcePropertyLoader
-                               .getResourceNames(name, null);
-               if (neutral) {
-                       // 言語中立を優先する場合は、プロパティの重ね順を逆転させて言語中立で最後に上書きする.
-                       resNames = resNames.reverse();
+       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> createDefaultCustomLayerOrderMap(
+                       PartsCategoryResolver partsCategoryResolver, DefaultCharacterDataVersion version) {
+               if (partsCategoryResolver == null) {
+                       throw new NullPointerException("categories is required.");
                }
-               return loader.getLocalizedProperties(name);
+               if (version == null) {
+                       throw new NullPointerException("version is required.");
+               }
+               return version.createCustomLayerOrderMap(partsCategoryResolver, this);
        }
 
        /**
         * キャラクターデータのxmlファイル名をキーとし、表示名を値とするマップ.<br>
         * 表示順序でアクセス可能.<br>
         *
-        * @return 順序付マップ
+        * @return 順序付マップ(キーはテンプレートxmlのファイル名、値は表示名)
         */
        public Map<String, String> getCharacterDataTemplates() {
                // キャラクターデータのxmlファイル名をキーとし、表示名を値とするマップ
                final LinkedHashMap<String, String> templateNameMap = new LinkedHashMap<String, String>();
 
                // テンプレートの定義プロパティのロード
-               Properties props = getTemplateListProperties(false);
+               // テンプレートリソースは実行中に増減する可能性があるため、共有キャッシュには入れない.
+               LocalizedResourcePropertyLoader propLoader = LocalizedResourcePropertyLoader.getNonCachedInstance();
+               Properties props = propLoader.getLocalizedProperties(DEFAULT_CHARACTER_PREFIX + TEMPLATE_LIST_XML, null);
 
-               // 順序優先
+               // 順序優先のキーに、テンプレート名がカンマ区切りになっているので、
+               // このキーにあるものを先に順番に登録する
                String strOrders = props.getProperty("displayOrder");
                if (strOrders != null) {
-                       for (String key : strOrders.split(",")) {
-                               key = key.trim();
-                               String val = props.getProperty(key);
-                               if (val != null && val.trim().length() > 0) {
-                                       String resKey = DEFAULT_CHARACTER_PREFIX + key;
+                       for (String templateFileName : strOrders.split(",")) {
+                               templateFileName = templateFileName.trim();
+                               String displayName = props.getProperty(templateFileName);
+                               if (displayName != null && displayName.trim().length() > 0) {
+                                       String resKey = DEFAULT_CHARACTER_PREFIX + templateFileName;
                                        if (getResource(resKey) != null) {
                                                // 現存するテンプレートのみ登録
-                                               templateNameMap.put(key, val);
+                                               templateNameMap.put(templateFileName, displayName);
                                        }
                                }
                        }
                }
 
-               // é \86åº\8fã\81\8c指定されていないアイテムの追加
+               // é \86åº\8fã\81§指定されていないアイテムの追加
                Enumeration<?> enm = props.propertyNames();
                while (enm.hasMoreElements()) {
-                       String key = (String) enm.nextElement();
-                       String val = props.getProperty(key);
-                       if (key.endsWith(".xml")) {
-                               String resKey = DEFAULT_CHARACTER_PREFIX + key;
-                               if (getResource(resKey) != null) {
-                                       // 現存するテンプレートのみ登録
-                                       templateNameMap.put(key, val);
+                       String templateFileName = (String) enm.nextElement();
+                       if (!templateNameMap.containsKey(templateFileName)) {
+                               if (templateFileName.endsWith(".xml")) {
+                                       String displayName = props.getProperty(templateFileName);
+                                       String resKey = DEFAULT_CHARACTER_PREFIX + templateFileName;
+                                       if (getResource(resKey) != null) {
+                                               // 現存するテンプレートのみ登録
+                                               templateNameMap.put(templateFileName, displayName);
+                                       }
                                }
                        }
                }
 
-               // フォルダにある未登録のxmlファイルもテンプレート一覧に加える
-               // (ただし、テンプレートリストプロパティを除く)
+               // ã\83­ã\83¼ã\82«ã\83«ã\83\95ã\82©ã\83«ã\83\80ã\81«ã\81\82ã\82\8bæ\9cªç\99»é\8c²ã\81®xmlã\83\95ã\82¡ã\82¤ã\83«ã\82\82ã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ä¸\80覧ã\81«å\8a ã\81\88ã\82\8b
+               // (ã\81\9fã\81 ã\81\97ã\80\81ã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ã\83ªã\82¹ã\83\88ã\83\97ã\83­ã\83\91ã\83\86ã\82£ã\80\81ã\82«ã\82¹ã\82¿ã\83 ã\83¬ã\82¤ã\83¤ã\83¼ã\83\91ã\82¿ã\83¼ã\83³å®\9a義ã\82\92é\99¤ã\81\8f)
                try {
-                       File templDir = getTemplateDir(false);
-                       if (templDir.isDirectory()) {
+                       File templDir = getUserTemplateDir();
+                       if (templDir.exists() && templDir.isDirectory()) {
                                File[] files = templDir.listFiles(new java.io.FileFilter() {
                                        public boolean accept(File pathname) {
                                                String name = pathname.getName();
@@ -218,6 +218,10 @@ public class CharacterDataDefaultProvider {
                                                        // テンプレートリストプロパティファイルは除外する.
                                                        return false;
                                                }
+                                               if (name.endsWith(CUSTOM_LAYER_ORDERS_SUFFIX)) {
+                                                       // カスタムレイヤーパターン定義ファイルは除外する.
+                                                       return false;
+                                               }
                                                return pathname.isFile() && name.endsWith(".xml");
                                        }
                                });
@@ -284,13 +288,13 @@ public class CharacterDataDefaultProvider {
         * XMLリソースファイルから、定義済みのカスタムレイヤーパターンを生成して返す。
         * 指定されたリソース名に対して「-customlayerorders.xml」のような末尾に変えたリソースで検索される。
         * 定義がない場合はnullを返す。
-        * @param cd
+        * @param partsCategoryResolver
         * @param name リソース名
         * @return
         * @throws IOException
         */
-       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> loadPredefinedCustomLayerOrder(CharacterData cd, String name)
-                       throws IOException {
+       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> loadPredefinedCustomLayerOrder(
+                       PartsCategoryResolver partsCategoryResolver, String name) throws IOException {
                // キャラクター定義xmlへのリソース名から、カスタムレイヤー定義のリソース名を組み立てる
                int pt = name.lastIndexOf(".");
                String nameBody = name.substring(0, pt);
@@ -306,7 +310,7 @@ public class CharacterDataDefaultProvider {
                InputStream is = predefinedCharacter.openStream();
                try {
                        logger.log(Level.INFO, "load a predefined custom layer orders. resKey=" + resKey);
-                       CustomLayerOrderXMLReader xmlReader = new CustomLayerOrderXMLReader(cd);
+                       CustomLayerOrderXMLReader xmlReader = new CustomLayerOrderXMLReader(partsCategoryResolver);
                        return xmlReader.read(is);
 
                } finally {
@@ -322,33 +326,19 @@ public class CharacterDataDefaultProvider {
         * @return リソース、なければnull
         */
        protected URL getResource(String resKey) {
-               ResourceLoader resourceLoader = new ResourceLoader();
                return resourceLoader.getResource(resKey);
        }
 
        /**
-        * カスタマイズ用のテンプレートディレクトリを取得する.<br>
-        * まだ作成されていない場合で、prepareが指示されている場合はフォルダを準備し、 既定のファイルを作成する.<br>
+        * ã\83¦ã\83¼ã\82¶ã\83¼å®\9a義ã\81®ã\82«ã\82¹ã\82¿ã\83\9eã\82¤ã\82ºç\94¨ã\81®ã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ã\83\87ã\82£ã\83¬ã\82¯ã\83\88ã\83ªã\82\92å\8f\96å¾\97ã\81\99ã\82\8b.<br>
+        * (ディレクトリが実在しない場合もありえる)
         *
-        * @param prepare
-        *            実際にディレクトリを準備する場合はtrue
         * @return テンプレートディレクトリ
         */
-       public File getTemplateDir(boolean prepare) throws IOException {
+       public File getUserTemplateDir() throws IOException {
                File baseDir = ConfigurationDirUtilities.getUserDataDir();
                SetupLocalization setup = new SetupLocalization(baseDir);
                File resourceDir = setup.getResourceDir();
-
-               if (prepare) {
-                       // テンプレートリソースが未設定であれば設定する.
-                       setup.setupToLocal(
-                                       EnumSet.of(SetupLocalization.Resources.Template), false);
-
-                       // ディレクトリがなければ作成しておく
-                       if (resourceDir.exists()) {
-                               resourceDir.mkdirs();
-                       }
-               }
                return new File(resourceDir, DEFAULT_CHARACTER_PREFIX);
        }
 
@@ -375,17 +365,21 @@ public class CharacterDataDefaultProvider {
         *            キャラクターデータ
         * @param localizedName
         *            表示名
+        * @param customLayerPattern
+        *            カスタムレイヤーパターン、なければnull
         * @throws IOException
         */
-       public void saveTemplate(String name, CharacterData cd, String localizedName)
-                       throws IOException {
+       public void saveTemplate(String name, CharacterData cd, String localizedName,
+                       Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatterns) throws IOException {
                if (name == null || !canFileSave(name)) {
                        throw new IllegalArgumentException();
                }
 
                // テンプレートファイル位置の準備
-               // (リソースが、まだファイルに展開されていなれば展開する)
-               File templDir = getTemplateDir(true);
+               // (ディレクトリが存在しない場合は作成する)
+               File templDir = getUserTemplateDir();
+               templDir.mkdirs();
+
                File templFile = new File(templDir, name);
 
                // キャラクターデータをXML形式でテンプレートファイルへ保存
@@ -403,22 +397,65 @@ public class CharacterDataDefaultProvider {
                        bos.close();
                }
 
-               // テンプレートの定義プロパティのロード(言語中立を優先)
-               Properties neutralProps = getTemplateListProperties(true);
+               // カスタムレイヤーパターンを保存する
+               if (customLayerPatterns != null && cd.isEnableCustonLayerPattern()) {
+                       // 拡張子を取り除いた名前を取得する
+                       int pt = name.lastIndexOf(".");
+                       String nameBody = (pt > 0) ? name.substring(0, pt) : name;
+                       // カスタムレイヤーパターンとして識別される末尾文字列を付与する
+                       File templCustomLayerFile = new File(templDir, nameBody + CUSTOM_LAYER_ORDERS_SUFFIX);
+
+                       // カスタムレイヤーパターンXMLファイルを作成する
+                       CustomLayerOrderXMLWriter xmlWriter = new CustomLayerOrderXMLWriter();
+                       BufferedOutputStream bos2 = new BufferedOutputStream(new FileOutputStream(templCustomLayerFile));
+                       try {
+                               xmlWriter.write(customLayerPatterns, bos2);
+                       } finally {
+                               bos2.close();
+                       }
+               }
+
+               // ユーザー定義テンプレートのプロパティファイルをロードする
+               Properties userTemplDefProp = loadUserDefineTemplateDef();
 
                // テンプレート一覧の更新
-               neutralProps.put(name, localizedName);
+               userTemplDefProp.put(name, localizedName);
+               saveUserDefineTemplateDef(userTemplDefProp);
+       }
+
+       private File getUserTemplateDefPropertyFile() throws IOException {
+               File templDir = getUserTemplateDir();
+               return new File(templDir, TEMPLATE_LIST_XML + ".xml");
+       }
+
+       private Properties loadUserDefineTemplateDef() throws IOException {
+               File userTemplDefPropFile = getUserTemplateDefPropertyFile();
+               Properties userTemplDefProp = new Properties();
+               if (userTemplDefPropFile.exists() && userTemplDefPropFile.length() > 0) {
+                       InputStream is = new BufferedInputStream(new FileInputStream(userTemplDefPropFile));
+                       try {
+                               userTemplDefProp.loadFromXML(is);
+                       } finally {
+                               is.close();
+                       }
+               }
+               return userTemplDefProp;
+       }
+
+       private void saveUserDefineTemplateDef(Properties userTemplDefProp) throws IOException {
+               if (userTemplDefProp == null) {
+                       userTemplDefProp = new Properties();
+               }
+
+               File userTemplDefPropFile = getUserTemplateDefPropertyFile();
 
                // テンプレート一覧の保存
-               File neutralPropsFile = new File(templDir, TEMPLATE_LIST_XML + ".xml");
-               BufferedOutputStream fos = new BufferedOutputStream(
-                               new FileOutputStream(neutralPropsFile));
+               BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(userTemplDefPropFile));
                try {
-                       neutralProps.storeToXML(fos,
-                                       new Timestamp(System.currentTimeMillis()).toString());
+                       userTemplDefProp.storeToXML(fos, new Timestamp(System.currentTimeMillis()).toString());
 
                } finally {
-                       bos.close();
+                       fos.close();
                }
        }
 }
index 6b1b131..49b53f7 100644 (file)
@@ -15,25 +15,25 @@ import charactermanaj.util.FileNameNormalizer;
 
 /**
  * ディレクトリをアーカイブと見立てる
- * 
+ *
  * @author seraphy
  */
 public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile {
-       
+
        /**
         * ロガー
         */
        private static final Logger logger = Logger.getLogger(CharacterDataDirectoryFile.class.getName());
 
-       
+
        /**
         * 対象ディレクトリ
         */
        protected File baseDir;
-       
+
        /**
         * ディレクトリ上のファイルコンテンツ
-        * 
+        *
         * @author seraphy
         */
        protected static class DirFileContent implements FileContent {
@@ -48,28 +48,33 @@ public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile
                 * 実際のファイルへのパス
                 */
                private File entry;
-               
+
                protected DirFileContent(File entry, String entryName) {
                        this.entry = entry;
                        this.entryName = entryName;
                }
-               
+
                public String getEntryName() {
                        return entryName;
                }
-               
+
                public long lastModified() {
                        return entry.lastModified();
                }
 
+               @Override
+               public long length() {
+                       return entry.length();
+               }
+
                public InputStream openStream() throws IOException {
                        return new FileInputStream(entry);
                }
        }
-       
+
        /**
         * アーカイブファイルをベースとしたURIを返す.<br>
-        * 
+        *
         * @param name
         *            コンテンツ名
         * @return URI
@@ -79,7 +84,7 @@ public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile
        protected URI getContentURI(String name) throws IOException {
                return new File(baseDir, name).toURI();
        }
-       
+
        @Override
        public Collection<PartsImageContent> getPartsImageContents(
                        CharacterData characterData, boolean newly) {
@@ -91,10 +96,10 @@ public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile
                }
                return super.getPartsImageContents(characterData, newly);
        }
-       
+
        /**
         * このディレクトリに対してターゲットプロファイルのディレクトリがかぶっているか? つまり、ターゲット自身のディレクトリをソースとしていないか?
-        * 
+        *
         * @param characterData
         *            ソースプロファイル
         * @return 被っている場合はtrue、被っていない場合はfalse
@@ -111,34 +116,34 @@ public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile
                String folderPlace = File.separator;
                String basePath = new File(docBase).getParent() + folderPlace;
                String sourcePath = baseDir.getPath() + folderPlace;
-               
+
                // インポートもとディレクトリがインポート先のキャラクター定義のディレクトリを含むか?
                boolean result = basePath.contains(sourcePath);
                logger.log(Level.FINE, "checkOverlapped: " + basePath + " * " + sourcePath + " :" + result);
-               
+
                return result;
        }
 
        public void close() throws IOException {
                // ディレクトリなのでクローズ処理は必要ない.
        }
-       
+
        public CharacterDataDirectoryFile(File file) throws IOException {
                super(file);
                baseDir = file;
                load(baseDir, "");
                searchRootPrefix();
        }
-       
+
        private void load(File dir, String prefix) {
                if (!dir.exists() || !dir.isDirectory()) {
                        // ディレクトリでなければ何もせず戻る
                        return;
                }
-               
+
                // ファイル名をノーマライズする
                FileNameNormalizer normalizer = FileNameNormalizer.getDefault();
-               
+
                File[] files = dir.listFiles();
                if (files != null) {
                        for (File file : files) {
index 955c4a1..684902c 100644 (file)
@@ -11,39 +11,46 @@ import java.util.jar.JarFile;
 public class CharacterDataJarArchiveFile extends AbstractCharacterDataArchiveFile {
 
        protected JarFile jarFile;
-       
+
        protected class JarFileContent implements FileContent {
 
                private JarEntry entry;
-               
+
                protected JarFileContent(JarEntry entry) {
                        this.entry = entry;
                }
-               
+
+               @Override
                public String getEntryName() {
                        return entry.getName();
                }
-               
+
+               @Override
                public long lastModified() {
                        return entry.getTime();
                }
-               
+
+               @Override
+               public long length() {
+                       return entry.getSize();
+               }
+
+               @Override
                public InputStream openStream() throws IOException {
                        return jarFile.getInputStream(entry);
                }
-               
        }
-       
+
        public void close() throws IOException {
                jarFile.close();
        }
-       
+
        public CharacterDataJarArchiveFile(File file) throws IOException {
                super(file);
                jarFile = new JarFile(file);
                load();
        }
-       
+
        private void load() {
                Enumeration<JarEntry> enm = jarFile.entries();
                while (enm.hasMoreElements()) {
index 10409ea..42fa09e 100644 (file)
@@ -11,14 +11,12 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
-import java.net.URL;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
-import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -29,21 +27,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
 import charactermanaj.graphics.io.FileImageResource;
 import charactermanaj.graphics.io.ImageLoader;
 import charactermanaj.graphics.io.ImageSaveHelper;
 import charactermanaj.graphics.io.LoadedImage;
 import charactermanaj.model.AppConfig;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.Layer;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.io.CharacterDataDefaultProvider.DefaultCharacterDataVersion;
 import charactermanaj.util.DirectoryConfig;
 import charactermanaj.util.FileNameNormalizer;
 import charactermanaj.util.FileUserData;
+import charactermanaj.util.FileUtilities;
 import charactermanaj.util.UserData;
 
 public class CharacterDataPersistent {
@@ -216,6 +214,24 @@ public class CharacterDataPersistent {
        }
 
        /**
+        * {@link #createProfile(CharacterData)}に加えて、同時にカスタムレイヤーパターンを登録します。
+        * @param characterData キャラクターデータ
+        * @param customLayerPatterns カスタムレイヤーパターン、なければnull可
+        * @throws IOException 失敗
+        */
+       public void createProfile(CharacterData characterData,
+                       Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatterns) throws IOException {
+               // キャラクターデータの登録
+               createProfile(characterData);
+
+               // カスタムレイヤーパターンの登録
+               if (characterData.isEnableCustonLayerPattern() && customLayerPatterns != null) {
+                       CustomLayerOrderPersist persist = CustomLayerOrderPersist.newInstance(characterData);
+                       persist.save(customLayerPatterns);
+               }
+       }
+
+       /**
         * リビジョンを生成して返す.
         *
         * @return リビジョン用文字列
@@ -720,106 +736,70 @@ public class CharacterDataPersistent {
                        return;
                }
 
-               // favories.xmlの削除
-               if (forceRemove) {
-                       UserData[] favoritesDatas = new UserData[]{getFavoritesUserData(cd)};
-                       for (UserData favoriteData : favoritesDatas) {
-                               if (favoriteData != null && favoriteData.exists()) {
-                                       logger.log(Level.INFO, "remove file: " + favoriteData);
-                                       favoriteData.delete();
-                               }
-                       }
+               // ディレクトリ
+               File baseDir = xmlFile.getParentFile();
+               if (!baseDir.exists()) {
+                       // すでに存在しない場合
+                       return;
                }
 
+// ver0.998以降では、お気に入りはキャラクターデータと同一ディレクトリにあるので、別個の処理は不要
+//             // favories.xmlの削除
+//             if (forceRemove) {
+//                     UserData[] favoritesDatas = new UserData[] { getFavoritesUserData(cd) };
+//                     for (UserData favoriteData : favoritesDatas) {
+//                             if (favoriteData != null && favoriteData.exists()) {
+//                                     logger.log(Level.INFO, "remove file: " + favoriteData);
+//                                     favoriteData.delete();
+//                             }
+//                     }
+//             }
+
                // ワーキングセットの削除
                // XML形式でのワーキングセットの保存
                WorkingSetPersist workingSetPersist = WorkingSetPersist.getInstance();
                workingSetPersist.removeWorkingSet(cd);
 
-               // xmlファイルの拡張子を変更することでキャラクター定義として認識させない.
-               // (削除に失敗するケースに備えて先にリネームする.)
-               String suffix = "." + System.currentTimeMillis() + ".deleted";
-               File bakFile = new File(xmlFile.getPath() + suffix);
-               if (!xmlFile.renameTo(bakFile)) {
-                       throw new IOException("can not rename configuration file.:"
-                                       + xmlFile);
-               }
-
-               // ディレクトリ
-               File baseDir = xmlFile.getParentFile();
-
                if (!forceRemove) {
-                       // 削除されたディレクトリであることを識別できるようにディレクトリ名も変更する.
-                       File parentBak = new File(baseDir.getPath() + suffix);
-                       if (!baseDir.renameTo(parentBak)) {
-                               throw new IOException("can't rename directory. " + baseDir);
-                       }
+                       // 論理削除
+                       logicalDelete(xmlFile);
 
                } else {
-                       // 完全に削除する
-                       removeRecursive(baseDir);
+                       // 完全に削除する(物理削除)
+                       try {
+                               FileUtilities.delete(baseDir);
+
+                       } catch (IOException ex) {
+                               // 削除に失敗した場合は論理削除を試行する
+                               logicalDelete(xmlFile);
+                               throw ex; // 論理削除でエラーがでない場合は物理削除エラーをそのまま返す
+                       }
                }
        }
 
        /**
-        * 指定したファイルを削除します.<br>
-        * 指定したファイルがディレクトリを示す場合、このディレクトリを含む配下のすべてのファイルとディレクトリを削除します.<br>
-        *
-        * @param file
-        *            ファイル、またはディレクトリ
+        * キャラクターデータxmlファイルをリネームすることで論理的に削除する。
+        * 削除されたキャラクターデータであることを示すためにフォルダ名も.deletedとづける
+        * @param xmlFile
         * @throws IOException
-        *             削除できない場合
         */
-       protected void removeRecursive(File file) throws IOException {
-               if (file == null) {
-                       throw new IllegalArgumentException();
-               }
-               if (!file.exists()) {
-                       return;
-               }
-               if (file.isDirectory()) {
-                       File[] children = file.listFiles();
-                       if (children != null) {
-                               for (File child : children) {
-                                       removeRecursive(child);
-                               }
-                       }
-               }
-               if (!file.delete()) {
-                       throw new IOException("can't delete file. " + file);
-               }
-       }
+       private void logicalDelete(File xmlFile) throws IOException {
+               File baseDir = xmlFile.getParentFile();
 
-       protected Iterable<Node> iterable(final NodeList nodeList) {
-               final int mx;
-               if (nodeList == null) {
-                       mx = 0;
-               } else {
-                       mx = nodeList.getLength();
+               // xmlファイルの拡張子を変更することでキャラクター定義として認識させない.
+               // (削除に失敗するケースに備えて先にリネームする.)
+               String suffix = "." + System.currentTimeMillis() + ".deleted";
+               File bakFile = new File(xmlFile.getPath() + suffix);
+               if (!xmlFile.renameTo(bakFile)) {
+                       throw new IOException("can not rename configuration file.:"
+                                       + xmlFile);
                }
-               return new Iterable<Node>() {
-                       public Iterator<Node> iterator() {
-                               return new Iterator<Node>() {
-                                       private int idx = 0;
-                                       public boolean hasNext() {
-                                               return idx < mx;
-                                       }
-                                       public Node next() {
-                                               if (idx >= mx) {
-                                                       throw new NoSuchElementException();
-                                               }
-                                               return nodeList.item(idx++);
-                                       }
-                                       public void remove() {
-                                               throw new UnsupportedOperationException();
-                                       }
-                               };
-                       }
-               };
-       }
 
-       protected URL getEmbeddedResourceURL(String schemaName) {
-               return this.getClass().getResource(schemaName);
+               // 削除されたディレクトリであることを識別できるようにディレクトリ名も変更する.
+               File parentBak = new File(baseDir.getPath() + suffix);
+               if (!baseDir.renameTo(parentBak)) {
+                       throw new IOException("can't rename directory. " + baseDir);
+               }
        }
 
        /**
@@ -933,10 +913,7 @@ public class CharacterDataPersistent {
                } else {
                        // 削除
                        if (sampleImageFile.exists()) {
-                               if (!sampleImageFile.delete()) {
-                                       throw new IOException("sample pucture delete failed. :"
-                                                       + sampleImageFile);
-                               }
+                               FileUtilities.delete(sampleImageFile);
                        }
                }
        }
index 63729ba..6f6b62d 100644 (file)
@@ -2,25 +2,30 @@ package charactermanaj.model.io;
 
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSpec;
 
 public interface CharacterDataWriter {
 
        void writeExportProp(Properties prop) throws IOException;
-       
+
        void writeCharacterData(CharacterData characterData) throws IOException;
-       
+
+       void writeCustomLayerPatterns(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) throws IOException;
+
        void writeTextUTF16LE(String name, String contents) throws IOException;
-       
+
        void writeSamplePicture(BufferedImage samplePicture) throws IOException;
-       
+
        void writePartsImages(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException;
-       
+
        void writePartsManageData(Map<PartsIdentifier, PartsSpec> partsImages) throws IOException;
 
        void close() throws IOException;
index 1b79dc4..4aced56 100644 (file)
@@ -14,43 +14,50 @@ import charactermanaj.model.AppConfig;
 public class CharacterDataZipArchiveFile extends AbstractCharacterDataArchiveFile {
 
        protected ZipFile zipFile;
-       
+
        protected class ZipFileContent implements FileContent {
 
                private ZipEntry entry;
-               
+
                protected ZipFileContent(ZipEntry entry) {
                        this.entry = entry;
                }
-               
+
+               @Override
                public String getEntryName() {
                        return entry.getName();
                }
-               
+
+               @Override
                public long lastModified() {
                        return entry.getTime();
                }
-               
+
+               @Override
+               public long length() {
+                       return entry.getSize();
+               }
+
+               @Override
                public InputStream openStream() throws IOException {
                        return zipFile.getInputStream(entry);
                }
-               
        }
-       
+
        public void close() throws IOException {
                zipFile.close();
        }
-       
+
        public CharacterDataZipArchiveFile(File file) throws IOException {
                super(file);
-               
+
                AppConfig appConfig = AppConfig.getInstance();
                String encoding = appConfig.getZipNameEncoding();
-               
+
                zipFile = new ZipFile(file, encoding);
                load();
        }
-       
+
        private void load() {
                @SuppressWarnings("unchecked")
                Enumeration<ZipEntry> enm = zipFile.getEntries();
index 31e7ebd..3de51db 100644 (file)
@@ -5,6 +5,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
+import java.util.Collections;
 import java.util.EventListener;
 import java.util.EventObject;
 import java.util.List;
@@ -20,8 +21,19 @@ import charactermanaj.model.io.CustomLayerOrderPersist.CustomLayerOrderPersistLi
 import charactermanaj.util.FileUserData;
 import charactermanaj.util.UserData;
 
+/**
+ * カスタムレイヤーパターンの読み込みと保存を行う。
+ */
 public abstract class CustomLayerOrderPersist {
 
+       /**
+        * レイヤーパターンのXMLファイル名
+        */
+       public static final String CUSTOM_LAYER_ORDERS_XML_FILE = "customlayerorders.xml";
+
+       /**
+        * カスタムレイヤーパターンが変更(保存)されたことを通知するリスナ
+        */
        public interface CustomLayerOrderPersistListener extends EventListener {
 
                public static class Change extends EventObject {
@@ -47,8 +59,15 @@ public abstract class CustomLayerOrderPersist {
                void notifyChangeCustomLayerOrder(Change e);
        }
 
+       /**
+        * 対象としているキャラクターデータ
+        */
        protected final CharacterData characterData;
 
+       /**
+        * コンストラクタ
+        * @param characterData
+        */
        protected CustomLayerOrderPersist(CharacterData characterData) {
                if (characterData == null) {
                        throw new NullPointerException();
@@ -56,23 +75,55 @@ public abstract class CustomLayerOrderPersist {
                this.characterData = characterData;
        }
 
+       /**
+        * 対象としているキャラクターデータ
+        * @return
+        */
        public CharacterData getCharacterData() {
                return characterData;
        }
 
+       /**
+        * キャラクターデータを指定してインスタンスを構築する
+        * @param characterData
+        * @return
+        */
        public static CustomLayerOrderPersist newInstance(CharacterData characterData) {
                return new CustomLayerOrderXMLPersist(characterData);
        }
 
+       /**
+        * カスタムレイヤーパターンが存在するか?
+        * @return 存在する場合はtrue(空の場合でもtrueとなる)
+        */
        public abstract boolean exist();
 
+       /**
+        * カスタムレイヤーパターンを保存する
+        * @param map
+        * @throws IOException
+        */
        public abstract void save(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) throws IOException;
 
+       /**
+        * カスタムレイヤーパターンをロードする。
+        * 存在しない場合はnullを返す。
+        * @return パターン、もしくはnull
+        * @throws IOException
+        */
        public abstract Map<CustomLayerOrderKey, List<CustomLayerOrder>> load() throws IOException;
 
+
+       /**
+        * リスナーのマップ
+        */
        private static final Map<URI, Queue<CustomLayerOrderPersistListener>> listenersMap =
                        new ConcurrentHashMap<URI, Queue<CustomLayerOrderPersistListener>>();
 
+       /**
+        * このキャラクターデータのカスタムレイヤーパターンの保存の通知を受け取るリスナを登録する
+        * @param l
+        */
        public void addCustomLayerOrderPersistListener(CustomLayerOrderPersistListener l) {
                URI uri = characterData.getDocBase();
                if (l != null && uri != null) {
@@ -87,6 +138,10 @@ public abstract class CustomLayerOrderPersist {
                }
        }
 
+       /**
+        * このキャラクターデータのカスタムレイヤーパターンの保存の通知を受け取るリスナを登録解除する
+        * @param l
+        */
        public void removeCustomLayerOrderPersistListener(CustomLayerOrderPersistListener l) {
                URI uri = characterData.getDocBase();
                if (l != null && uri != null) {
@@ -95,6 +150,7 @@ public abstract class CustomLayerOrderPersist {
                                if (listeners != null) {
                                        listeners.remove(l);
                                        if (listeners.isEmpty()) {
+                                               // これが最後のリスナであればマップのエントリも消す
                                                listenersMap.remove(uri);
                                        }
                                }
@@ -102,6 +158,10 @@ public abstract class CustomLayerOrderPersist {
                }
        }
 
+       /**
+        * このキャラクターデータのカスタムレイヤーパターンの保存を全てのリスナーに通知する
+        * @param map
+        */
        protected void fireEvent(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) {
                URI uri = characterData.getDocBase();
                if (uri != null) {
@@ -116,17 +176,19 @@ public abstract class CustomLayerOrderPersist {
        }
 }
 
+/**
+ * カスタムレイヤーパターンをXML形式で保存する実装クラス
+ */
 class CustomLayerOrderXMLPersist extends CustomLayerOrderPersist {
 
-       /**
-        * レイヤーパターンのXMLファイル名
-        */
-       public static final String CUSTOM_LAYER_ORDERS_XML_FILE = "customlayerorders.xml";
-
        public CustomLayerOrderXMLPersist(CharacterData characterData) {
                super(characterData);
        }
 
+       /**
+        * 実際のXMLの保存先と関連づけられたユーザーデータ型を返す
+        * @return
+        */
        private UserData getCustomLayerOrdersUserData() {
                // xml形式の場合、キャラクターディレクトリ上に設定する.
                URI docBase = characterData.getDocBase();
@@ -140,6 +202,10 @@ class CustomLayerOrderXMLPersist extends CustomLayerOrderPersist {
 
        @Override
        public void save(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) throws IOException {
+               if (map == null) {
+                       map = Collections.emptyMap();
+               }
+
                UserData xmlData = getCustomLayerOrdersUserData();
                OutputStream outstm = xmlData.getOutputStream();
                try {
@@ -155,7 +221,12 @@ class CustomLayerOrderXMLPersist extends CustomLayerOrderPersist {
        @Override
        public Map<CustomLayerOrderKey, List<CustomLayerOrder>> load() throws IOException {
                UserData xmlData = getCustomLayerOrdersUserData();
-               if (xmlData.exists() && xmlData.length() > 0) {
+               if (xmlData.exists()) {
+                       if (xmlData.length() == 0) {
+                               // 空ファイルは空エントリとみなす
+                               return Collections.emptyMap();
+                       }
+                       // XMLの読み取り
                        InputStream is = xmlData.openStream();
                        try {
                                CustomLayerOrderXMLReader xmlReader = new CustomLayerOrderXMLReader(characterData);
@@ -167,4 +238,4 @@ class CustomLayerOrderXMLPersist extends CustomLayerOrderPersist {
                }
                return null;
        }
-}
\ No newline at end of file
+}
index 0b65fa5..0e6e264 100644 (file)
@@ -14,21 +14,21 @@ import java.util.TreeMap;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import charactermanaj.model.CharacterData;
 import charactermanaj.model.CustomLayerOrder;
 import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.Layer;
 import charactermanaj.model.PartsCategory;
+import charactermanaj.model.PartsCategoryResolver;
 
 public class CustomLayerOrderXMLReader {
 
-       private CharacterData cd;
+       private PartsCategoryResolver categoryResolver;
 
-       public CustomLayerOrderXMLReader(CharacterData cd) {
-               if (cd == null) {
+       public CustomLayerOrderXMLReader(PartsCategoryResolver categoryResolver) {
+               if (categoryResolver == null) {
                        throw new NullPointerException("categories is required.");
                }
-               this.cd = cd;
+               this.categoryResolver = categoryResolver;
        }
 
        public Map<CustomLayerOrderKey, List<CustomLayerOrder>> read(InputStream is) throws IOException {
@@ -84,7 +84,7 @@ public class CustomLayerOrderXMLReader {
                                        String layerId = elmMapping.getAttribute("layer");
                                        float layerOrder = Float.parseFloat(elmMapping.getAttribute("order"));
 
-                                       PartsCategory category = cd.getPartsCategory(categoryId);
+                                       PartsCategory category = categoryResolver.getPartsCategory(categoryId);
                                        if (category != null) {
                                                Layer layer = category.getLayer(layerId);
                                                if (layer != null) {
index 23c1a7e..488d528 100644 (file)
@@ -10,11 +10,15 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import charactermanaj.model.AppConfig;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsAuthorInfo;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsManageData;
@@ -33,44 +37,49 @@ public class ImportModel {
         * インポートもとファイル
         */
        private URI importSource;
-       
+
        /**
         * プロファイル先のキャラクター定義、新規の場合はnull
         */
        private CharacterData currentCharacterData;
-       
-       
+
+
        /**
         * インポートもとアーカイブ. ロードされた場合に非nullとなる.
         */
        private CharacterDataArchiveFile archiveFile;
-       
-       
+
+
        /**
         * インポートされたキャラクター定義、なければnull
         */
        private CharacterData sourceCharacterData;
-       
+
+       /**
+        * インポートされたカスタムレイヤーパターン定義、なければnull
+        */
+       private Map<CustomLayerOrderKey, List<CustomLayerOrder>> sourceCustomLayerPatternMap;
+
        /**
         * インポートされたサンプルピクチャ、なければnull
         */
        private BufferedImage samplePicture;
-       
+
        /**
         * インポートされたreadme
         */
        private String readme;
-       
+
        /**
         * インポート先のキャラクター定義、もしくは現在のキャラクター定義のディレクトリ構成から 読み取ることのできるパーツのコレクション、なければ空.<br>
         */
        private Collection<PartsImageContent> partsImageContentsMap;
-       
+
        /**
         * パーツ管理データ、なければ空
         */
        private PartsManageData partsManageData;
-       
+
 
        public void openImportSource(URI importSource, CharacterData currentCharacterData) throws IOException {
                if (archiveFile != null || importSource == null) {
@@ -79,7 +88,7 @@ public class ImportModel {
                this.importSource = importSource;
                this.currentCharacterData = currentCharacterData;
        }
-       
+
        public void closeImportSource() throws IOException {
                if (archiveFile != null) {
                        try {
@@ -90,7 +99,7 @@ public class ImportModel {
                        }
                }
        }
-       
+
        public void loadContents(ProgressHandle progressHandle) throws IOException {
                if (archiveFile != null) {
                        throw new IllegalStateException("既にアーカイブがオープンされています。");
@@ -100,7 +109,7 @@ public class ImportModel {
                }
 
                CharacterDataFileReaderWriterFactory factory = CharacterDataFileReaderWriterFactory.getInstance();
-               
+
                progressHandle.setCaption("open archive...");
                archiveFile = factory.openArchive(importSource);
 
@@ -118,16 +127,21 @@ public class ImportModel {
                                        sourceCharacterData.setDescription(readme);
                                }
                        }
-                       
+
                } else {
                        // character.xmlがあった場合、favorites.xmlもあれば読み込む.
                        archiveFile.readFavorites(sourceCharacterData);
                }
 
+               if (sourceCharacterData != null && sourceCharacterData.isEnableCustonLayerPattern()) {
+                       // キャラクターデータの読み取りができ、且つ、カスタムレイヤーパターンが有効であれば
+                       sourceCustomLayerPatternMap = archiveFile.readCustomLayerPatterns(sourceCharacterData);
+               }
+
                // サンプルピクチャの読み込み、なければnull
                progressHandle.setCaption("load sample picture...");
                samplePicture = archiveFile.readSamplePicture();
-               
+
                // パーツセットの読み込み、なければ空
                progressHandle.setCaption("load partssets...");
                if (currentCharacterData != null) {
@@ -138,7 +152,7 @@ public class ImportModel {
                        // インポート元にあるキャラクター定義をもとにパーツディレクトリを探索する場合
                        partsImageContentsMap = archiveFile.getPartsImageContents(sourceCharacterData, true);
                }
-               
+
                // パーツ管理データの読み込み
                progressHandle.setCaption("load parts definitions...");
                partsManageData = archiveFile.getPartsManageData();
@@ -153,24 +167,29 @@ public class ImportModel {
                partsImageContentsMap = null;
                partsManageData = null;
        }
-       
+
 
 
        public URI getImportSource() {
                return importSource;
        }
-       
+
        protected void checkArchiveOpened() {
                if (archiveFile == null) {
                        throw new IllegalStateException("アーカイブはオープンされていません。");
                }
        }
-       
+
        public CharacterData getCharacterData() {
                checkArchiveOpened();
                return sourceCharacterData;
        }
 
+       public Map<CustomLayerOrderKey, List<CustomLayerOrder>> getCustomLayerPatternMap() {
+               checkArchiveOpened();
+               return sourceCustomLayerPatternMap;
+       }
+
        public BufferedImage getSamplePicture() {
                checkArchiveOpened();
                return samplePicture;
@@ -191,11 +210,11 @@ public class ImportModel {
                return partsManageData;
        }
 
-       
-       
+
+
        /**
         * パーツデータをプロファイルの画像ディレクトリに一括コピーする.<br>
-        * 
+        *
         * @param partsImageContents
         *            コピー対象のパーツデータ
         * @param cd
@@ -223,7 +242,7 @@ public class ImportModel {
 
                AppConfig appConfig = AppConfig.getInstance();
                byte[] stmbuf = new byte[appConfig.getFileTransferBufferSize()];
-               
+
                // ファイルコピー
                for (PartsImageContent content : partsImageContents) {
                        InputStream is = new BufferedInputStream(content.openStream());
@@ -247,11 +266,11 @@ public class ImportModel {
                                } finally {
                                        os.close();
                                }
-                               
+
                                if (!outFile.setLastModified(content.lastModified())) {
                                        logger.log(Level.WARNING, "can't change the modified-date: " + outFile);
                                }
-                               
+
                        } finally {
                                is.close();
                        }
@@ -263,7 +282,7 @@ public class ImportModel {
         * パーツ管理情報がnullまたは空であれば何もしない.<br>
         * そうでなければインポートするパーツに該当するパーツ管理情報を、現在のプロファイルのパーツ管理情報に追記・更新し、 それを書き出す.<br>
         * インポートもとにパーツ管理情報がなく、既存にある場合、インポートしても管理情報は削除されません.(上書きセマンティクスのため)
-        * 
+        *
         * @param partsImageContents
         *            インポートするパーツ
         * @param partsManageData
@@ -282,7 +301,7 @@ public class ImportModel {
                if (target == null || !target.isValid()) {
                        throw new IllegalArgumentException();
                }
-               
+
                if (partsImageContents == null || partsImageContents.isEmpty()
                                || partsManageData == null || partsManageData.isEmpty()) {
                        // インポートするパーツが存在しないか、管理情報がない場合は更新しようがないので何もしないで戻る.
@@ -290,7 +309,7 @@ public class ImportModel {
                }
 
                PartsInfoXMLReader xmlReader = new PartsInfoXMLReader();
-               
+
                PartsManageData mergedPartsManagedData;
                if (current != null && current.isValid()) {
                        // 現在のプロファイルからパーツ管理情報を取得する.
@@ -299,20 +318,20 @@ public class ImportModel {
                        // 新規の場合は空
                        mergedPartsManagedData = new PartsManageData();
                }
-               
+
                // インポート対象のパーツに該当するパーツ管理情報のみを取り出して追記する.
                for (PartsImageContent partsImageContent : partsImageContents) {
                        String partsName = partsImageContent.getPartsName();
                        for (CategoryLayerPair catLayerPair : partsImageContent.getCategoryLayerPairs()) {
                                PartsCategory partsCategory = catLayerPair.getPartsCategory();
                                String categoryId = partsCategory.getCategoryId();
-                               
+
                                PartsManageData.PartsKey partsKey = new PartsManageData.PartsKey(partsName, categoryId);
-                               
+
                                PartsAuthorInfo partsAuthorInfo = partsManageData.getPartsAuthorInfo(partsKey);
                                PartsManageData.PartsVersionInfo versionInfo = partsManageData.getVersion(partsKey);
                                String localizedName = partsManageData.getLocalizedName(partsKey);
-                               
+
                                if (partsAuthorInfo != null || versionInfo != null || localizedName != null) {
                                        // いずれかの情報の登録がある場合、パーツ管理情報として追記する.
                                        mergedPartsManagedData.putPartsInfo(partsKey, localizedName, partsAuthorInfo, versionInfo);
index 03a7105..961fb34 100644 (file)
@@ -19,6 +19,7 @@ import charactermanaj.model.PartsColorInfo;
 import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSet;
 import charactermanaj.model.WorkingSet;
+import charactermanaj.util.FileUtilities;
 import charactermanaj.util.UserData;
 import charactermanaj.util.UserDataFactory;
 
@@ -64,9 +65,12 @@ public class WorkingSetPersist {
                                return;
                        }
                        for (File file : files) {
-                               boolean success = file.delete();
-                               logger.log(Level.INFO, "remove file: " + file + " /success="
-                                               + success);
+                               logger.log(Level.INFO, "remove file: " + file);
+                               try {
+                                       FileUtilities.delete(file);
+                               } catch (Exception ex) {
+                                       logger.log(Level.WARNING, "failed to remove file: " + file, ex);
+                               }
                        }
                }
        }
index ce9bfae..c8e3eb7 100644 (file)
@@ -12,6 +12,7 @@ import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
@@ -49,6 +50,8 @@ import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.KeyStroke;
 import javax.swing.SwingConstants;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
 import javax.swing.event.TableModelEvent;
 import javax.swing.event.TableModelListener;
 import javax.swing.table.AbstractTableModel;
@@ -266,6 +269,11 @@ public class AppConfigDialog extends JDialog {
                 * @return 編集されていればtrue、そうでなければfalse
                 */
                public boolean isModified() {
+                       for (AppConfigRow rowItem : items) {
+                               if (rowItem.isModified()) {
+                                       return true;
+                               }
+                       }
                        return false;
                }
 
@@ -564,6 +572,7 @@ public class AppConfigDialog extends JDialog {
                appConfigTable.setGridColor(appConfig.getGridColor());
                appConfigTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
                appConfigTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+               appConfigTable.setCellSelectionEnabled(true);
 
                // データタイプがColorの場合のセルレンダラーとエディタを設定する
                appConfigTable.setDefaultRenderer(Color.class, new ColorCellRender());
@@ -883,22 +892,43 @@ class ColorCell extends JPanel {
                label = new JLabel();
                label.setHorizontalAlignment(SwingConstants.CENTER);
                box.add(label, BorderLayout.CENTER);
-               box.setBorder(BorderFactory.createEtchedBorder());
 
-               button = new JButton();
-               Dimension dim = button.getPreferredSize();
-               dim.width = 24;
-               button.setPreferredSize(dim);
-               button.addActionListener(new ActionListener() {
+               AbstractAction actColorChoose = new AbstractAction() {
+                       private static final long serialVersionUID = 1L;
+
                        @Override
                        public void actionPerformed(ActionEvent e) {
                                onClick(e);
                        }
-               });
+               };
+
+               button = new JButton(actColorChoose);
+               Dimension dim = button.getPreferredSize();
+               dim.width = 24;
+               button.setPreferredSize(dim);
 
                add(box, BorderLayout.CENTER);
                add(button, BorderLayout.EAST);
                setSelectedColor(Color.BLACK);
+
+               // ボックスのダブルクリックで、ボタンクリックと同じ動きとする
+               box.addMouseListener(new MouseAdapter() {
+                       @Override
+                       public void mouseClicked(MouseEvent e) {
+                               if (e.getClickCount() == 2) {
+                                       onClick(null);
+                                       e.consume();
+                               }
+                       }
+               });
+
+               // このセルのスペースキー押下で、ボタンクリックと同じ動きとする
+               InputMap im = getInputMap(WHEN_FOCUSED);
+               im.put(KeyStroke.getKeyStroke(' '), "ON_CLICK_COLOR_CHOOSER");
+               ActionMap am = getActionMap();
+               am.put("ON_CLICK_COLOR_CHOOSER", actColorChoose);
+
+               setMinimumSize(new Dimension(50, 30));
        }
 
        public String getTitle() {
@@ -913,7 +943,15 @@ class ColorCell extends JPanel {
                }
        }
 
-       protected void onClick(ActionEvent e) {
+       public Border getBoxBorder() {
+               return box.getBorder();
+       }
+
+       public void setBoxBorder(Border border) {
+               box.setBorder(border);
+       }
+
+       public void onClick(ActionEvent e) {
                // ※ カラー選択ダイアログは、Java7以降でないとアルファ値の設定はできない。
                // Java6で実行するとアルファチャネルが消されたものになる。
                // (設定ファイルとしては手作業では設定可能なので、とりあえず、このまま。)
@@ -926,6 +964,8 @@ class ColorCell extends JPanel {
                }
        }
 
+       private Color bgColor = Color.WHITE;
+
        private Color selectedColor;
 
        public Color getSelectedColor() {
@@ -933,27 +973,33 @@ class ColorCell extends JPanel {
        }
 
        public void setSelectedColor(Color color) {
-               if (color == null) {
-                       color = Color.BLACK;
-               }
                Color old = this.selectedColor;
                if (old == null ? color != null : !old.equals(color)) {
                        this.selectedColor = color;
 
+                       // nullの場合は黒と見なして処理をつづける
+                       // (プロパティにはnullを格納したまま)
+                       boolean dummyColor = false;
+                       if (color == null) {
+                               color = Color.BLACK;
+                               dummyColor = true;
+                       }
+
+                       // テキスト色は塗りつぶし色と反転色にする (同系で重なりにくくするため)
                        Color colorForeground = new Color(color.getRGB() ^ 0xffffff).brighter();
 
                        int alpha = color.getAlpha();
 
                        // JPanelの背景色としてアルファの透過色をそのまま使うと
                        // 親コンポーネントの背景色と混じり、色のカタログとして用をなさないので
-                       // 白を背景色とした合成済みに補正しておく
-                       // (アルファが255の場合はそのままで良い)
+                       // 固定された背景色(たとえば白)と予め合成済みに補正しておく
+                       // (アルファが255の場合は合成する必要はない)
                        Color premultipliedColor;
                        if (alpha == 255) {
                                premultipliedColor = color;
                        } else {
                                float[] rgb = color.getRGBColorComponents(null);
-                               float[] bgRgb = Color.WHITE.getRGBColorComponents(null); // 背景色 = 白
+                               float[] bgRgb = bgColor.getRGBColorComponents(null); // 背景
                                // アルファを合成済みにする
                                float a = ((float) alpha) / 255f;
                                rgb[0] = rgb[0] * a + bgRgb[0] * (1 - a);
@@ -966,7 +1012,10 @@ class ColorCell extends JPanel {
                        label.setForeground(colorForeground);
 
                        String msg;
-                       if (alpha != 255) {
+                       if (dummyColor) {
+                               // NULLの場合はテキストは表示しない
+                               msg = "";
+                       } else if (alpha != 255) {
                                // アルファが255以外の場合はアルファ値も含めてARGBで表示する
                                msg = String.format("#%08X", ((long) color.getRGB()) & 0xffffffffL);
                        } else {
@@ -982,18 +1031,30 @@ class ColorCell extends JPanel {
 
 /**
  * カラーセルのレンダラー
+ * 参考: https://github.com/haifengl/smile/blob/master/plot/src/main/java/smile/swing/table/ButtonCellRenderer.java
  */
 class ColorCellRender extends DefaultTableCellRenderer {
 
        private static final long serialVersionUID = 1L;
 
-       private ColorCell panel = new ColorCell();
+       private ColorCell colorCell = new ColorCell();
 
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {
-               panel.setSelectedColor((Color) value);
-               return panel;
+               Color color = (Color) value;
+               colorCell.setSelectedColor(color);
+
+               LineBorder focusedBorder = null;
+               if (hasFocus) {
+                       // フォーカスがある場合はボーダーをつける
+                       Color colorBorder = Color.CYAN;
+                       focusedBorder = new LineBorder(colorBorder, 2);
+               }
+
+               colorCell.setBoxBorder(focusedBorder);
+
+               return colorCell;
        }
 }
 
@@ -1004,6 +1065,8 @@ class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {
 
        private static final long serialVersionUID = 1L;
 
+       private Border focusedBorder = BorderFactory.createLineBorder(Color.WHITE, 2);
+
        private ColorCell colorCell = new ColorCell(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
@@ -1014,6 +1077,7 @@ class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {
        public Component getTableCellEditorComponent(final JTable table, final Object value,
                        final boolean isSelected, final int row, final int column) {
                colorCell.setSelectedColor((Color) value);
+               colorCell.setBoxBorder(focusedBorder); // 編集中はボーターをつける
                return colorCell;
        }
 
index 73537d3..7609323 100644 (file)
@@ -489,10 +489,8 @@ public class ColorDialog extends JDialog {
                }
 
                try {
-                       // クリップボードでサポートされているフォーマットでもっともテキストに適したフレーバーを取得する
-                       DataFlavor[] flavors = trans.getTransferDataFlavors();
-                   final DataFlavor textFlavor = DataFlavor.selectBestTextFlavor(flavors);
-                   if (textFlavor == null) {
+                       // 文字列を格納しているか?
+                   if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                                // テキストを持っていない
                                tk.beep();
                                return;
@@ -503,7 +501,7 @@ public class ColorDialog extends JDialog {
                        ColorConvertParameter org = param.clone(); // 変更有無の判定のため現在値をコピーする
 
                        List<String> errorLines = new ArrayList<String>();
-                       BufferedReader rd = new BufferedReader(textFlavor.getReaderForText(trans));
+                       BufferedReader rd = new BufferedReader(DataFlavor.stringFlavor.getReaderForText(trans));
                        try {
                                // テキストを読み込みつつ、対応するパラメータを設定する。
                                for (;;) {
index e1e9a46..0148e8f 100644 (file)
@@ -72,6 +72,8 @@ import javax.swing.table.TableColumnModel;
 import charactermanaj.Main;
 import charactermanaj.model.AppConfig;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsIdentifier;
 import charactermanaj.model.PartsSet;
@@ -79,6 +81,7 @@ import charactermanaj.model.PartsSpec;
 import charactermanaj.model.PartsSpecResolver;
 import charactermanaj.model.io.CharacterDataFileReaderWriterFactory;
 import charactermanaj.model.io.CharacterDataWriter;
+import charactermanaj.model.io.CustomLayerOrderPersist;
 import charactermanaj.model.io.ExportInfoKeys;
 import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;
 import charactermanaj.ui.progress.ProgressHandle;
@@ -91,40 +94,40 @@ import charactermanaj.util.LocalizedResourcePropertyLoader;
 public class ExportWizardDialog extends JDialog {
 
        private static final long serialVersionUID = 1L;
-       
+
        protected static final String STRINGS_RESOURCE = "languages/exportwizdialog";
-       
+
        protected static ArchiveFileDialog archiveFileDialog = new ArchiveFileDialog();
-       
+
        private JPanel activePanel;
-       
+
        private AbstractAction actNext;
-       
+
        private AbstractAction actPrev;
 
        private AbstractAction actFinish;
-       
+
        private ExportInformationPanel basicPanel;
-       
+
        private ExportPartsSelectPanel partsSelectPanel;
-       
+
        private ExportPresetSelectPanel presetSelectPanel;
 
        private CharacterData source;
-       
+
        public static File getLastUsedDir() {
                return archiveFileDialog.getLastUSedDir();
        }
-       
+
        public static void setLastUsedDir(File lastUsedDir) {
                archiveFileDialog.setLastUSedDir(lastUsedDir);
        }
-       
+
        public ExportWizardDialog(JFrame parent, CharacterData characterData, BufferedImage samplePicture) {
                super(parent, true);
                initComponent(parent, characterData, samplePicture);
        }
-       
+
        public ExportWizardDialog(JDialog parent, CharacterData characterData, BufferedImage samplePicture) {
                super(parent, true);
                initComponent(parent, characterData, samplePicture);
@@ -143,15 +146,15 @@ public class ExportWizardDialog extends JDialog {
                                onClose();
                        }
                });
-               
+
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                .getLocalizedProperties(STRINGS_RESOURCE);
 
                // タイトル
                setTitle(strings.getProperty("title"));
-               
+
                // メインパネル
-               
+
                Container contentPane = getContentPane();
                contentPane.setLayout(new BorderLayout());
 
@@ -160,16 +163,16 @@ public class ExportWizardDialog extends JDialog {
                final CardLayout mainPanelLayout = new CardLayout(5, 5);
                mainPanel.setLayout(mainPanelLayout);
                contentPane.add(mainPanel, BorderLayout.CENTER);
-               
+
                ComponentListener componentListener = new ComponentAdapter() {
                        public void componentShown(ComponentEvent e) {
                                onComponentShown((JPanel) e.getComponent());
                        }
                };
 
-               
+
                // アクション
-               
+
                this.actNext = new AbstractAction(strings.getProperty("btn.next")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
@@ -200,7 +203,7 @@ public class ExportWizardDialog extends JDialog {
                                updateBtnPanelState();
                        }
                };
-               
+
                ChangeListener actPanelEnabler = new ChangeListener() {
                        public void stateChanged(ChangeEvent e) {
                                updatePanelStatus();
@@ -213,13 +216,13 @@ public class ExportWizardDialog extends JDialog {
                this.basicPanel.addChangeListener(actChangeValue);
                this.basicPanel.addChangeListener(actPanelEnabler);
                mainPanel.add(this.basicPanel, "basicPanel");
-               
+
                // panel2 : panelSelectPanel
                this.partsSelectPanel = new ExportPartsSelectPanel(characterData);
                this.partsSelectPanel.addComponentListener(componentListener);
                this.partsSelectPanel.addChangeListener(actChangeValue);
                mainPanel.add(this.partsSelectPanel, "partsSelectPanel");
-               
+
                // panel3 : presetSelectPanel
                this.presetSelectPanel = new ExportPresetSelectPanel(
                                this.partsSelectPanel,
@@ -229,8 +232,8 @@ public class ExportWizardDialog extends JDialog {
                this.presetSelectPanel.addComponentListener(componentListener);
                this.presetSelectPanel.addChangeListener(actChangeValue);
                mainPanel.add(this.presetSelectPanel, "presetSelectPanel");
-               
-               
+
+
                // button panel
 
                JPanel btnPanel = new JPanel();
@@ -241,9 +244,9 @@ public class ExportWizardDialog extends JDialog {
                actPrev.setEnabled(false);
                actNext.setEnabled(false);
                actFinish.setEnabled(false);
-               
+
                GridBagConstraints gbc = new GridBagConstraints();
-               
+
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridheight = 1;
@@ -256,17 +259,17 @@ public class ExportWizardDialog extends JDialog {
                gbc.weightx = 1.;
                gbc.weighty = 0.;
                btnPanel.add(Box.createHorizontalGlue(), gbc);
-               
+
                gbc.gridx = Main.isLinuxOrMacOSX() ? 2 : 1;
                gbc.gridy = 0;
                gbc.weightx = 0.;
                btnPanel.add(new JButton(this.actPrev), gbc);
-               
+
                gbc.gridx = Main.isLinuxOrMacOSX() ? 3 : 2;
                gbc.gridy = 0;
                JButton btnNext = new JButton(this.actNext);
                btnPanel.add(btnNext, gbc);
-               
+
                gbc.gridx = Main.isLinuxOrMacOSX() ? 4 : 3;
                gbc.gridy = 0;
                btnPanel.add(new JButton(this.actFinish), gbc);
@@ -277,14 +280,14 @@ public class ExportWizardDialog extends JDialog {
                btnPanel.add(btnCancel, gbc);
 
                contentPane.add(btnPanel, BorderLayout.SOUTH);
-               
+
                // インプットマップ/アクションマップ
-               
+
                Toolkit tk = Toolkit.getDefaultToolkit();
                JRootPane rootPane = getRootPane();
-               
+
                rootPane.setDefaultButton(btnNext);
-               
+
                InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
                ActionMap am = rootPane.getActionMap();
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeExportWizDialog");
@@ -292,40 +295,40 @@ public class ExportWizardDialog extends JDialog {
                am.put("closeExportWizDialog", actCancel);
 
                // 表示
-               
+
                setSize(500, 500);
                setLocationRelativeTo(parent);
-               
+
                mainPanelLayout.first(mainPanel);
                updateBtnPanelState();
                updatePanelStatus();
        }
-       
+
        protected void onComponentShown(JPanel panel) {
                activePanel = panel;
                updateBtnPanelState();
        }
-       
+
        protected void updatePanelStatus() {
                partsSelectPanel.setEnabled(basicPanel.isExportPartsImages());
                presetSelectPanel.setEnabled(basicPanel.isExportPresets());
        }
-       
+
        protected void updateBtnPanelState() {
                actPrev.setEnabled(activePanel != null && activePanel != basicPanel);
                actNext.setEnabled(activePanel != null && activePanel != presetSelectPanel);
                actFinish.setEnabled(isComplete());
        }
-       
+
        protected void checkMissingParts(Collection<PartsSet> partaSets) {
                if (partaSets == null) {
                        partaSets = presetSelectPanel.getSelectedPresets();
                }
                partsSelectPanel.checkMissingPartsList(partaSets);
        }
-       
+
        protected boolean isComplete() {
-               
+
                if (basicPanel.isExportPartsImages()) {
                        if (partsSelectPanel.getSelectedCount() == 0) {
                                // パーツイメージのエクスポートを指定した場合、エクスポートするパーツの選択は必須
@@ -338,10 +341,10 @@ public class ExportWizardDialog extends JDialog {
                                return false;
                        }
                }
-               
+
                return true;
        }
-       
+
        protected void onClose() {
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
@@ -353,10 +356,10 @@ public class ExportWizardDialog extends JDialog {
                                JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
                        return;
                }
-               
+
                dispose();
        }
-       
+
        protected void onFinish() {
                if (!isComplete()) {
                        Toolkit tk = Toolkit.getDefaultToolkit();
@@ -379,7 +382,7 @@ public class ExportWizardDialog extends JDialog {
                                                return null;
                                        }
                                };
-                               
+
                                WorkerWithProgessDialog<Object> dlg
                                        = new WorkerWithProgessDialog<Object>(this, worker);
                                dlg.startAndWait();
@@ -392,18 +395,18 @@ public class ExportWizardDialog extends JDialog {
                        Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                                        .getLocalizedProperties(ExportWizardDialog.STRINGS_RESOURCE);
                        JOptionPane.showMessageDialog(this, strings.getProperty("complete"));
-                       
+
                        // 完了後、ウィンドウを閉じる.
                        dispose();
 
                } catch (WorkerException ex) {
                        ErrorMessageHelper.showErrorDialog(this, ex.getCause());
-                       
+
                } catch (Exception ex) {
                        ErrorMessageHelper.showErrorDialog(this, ex);
                }
        }
-       
+
        protected void doExport(File outFile) throws IOException {
                CharacterDataFileReaderWriterFactory writerFactory = CharacterDataFileReaderWriterFactory.getInstance();
                CharacterDataWriter exportWriter = writerFactory.createWriter(outFile);
@@ -412,7 +415,7 @@ public class ExportWizardDialog extends JDialog {
                        // (プリセットとパーツイメージはリセットされている状態。)
                        CharacterData cd = source.duplicateBasicInfo();
                        cd.clearPartsSets(false);
-                       
+
                        boolean exportPresets = basicPanel.isExportPresets();
                        boolean exportSamplePicture = basicPanel.isExportSamplePicture();
                        boolean exportCharacterData = true;
@@ -421,7 +424,7 @@ public class ExportWizardDialog extends JDialog {
                        // 基本情報を設定する.
                        cd.setAuthor(basicPanel.getAuthor());
                        cd.setDescription(basicPanel.getDescription());
-                       
+
                        // エクスポート情報を出力する.
                        Properties exportProp = new Properties();
                        exportProp.setProperty(ExportInfoKeys.EXPORT_PRESETS, Boolean.toString(exportPresets));
@@ -431,7 +434,7 @@ public class ExportWizardDialog extends JDialog {
                        exportProp.setProperty(ExportInfoKeys.EXPORT_TIMESTAMP, Long.toString(System.currentTimeMillis()));
 
                        exportWriter.writeExportProp(exportProp);
-                       
+
                        // プリセットをエクスポートする場合、プリセット情報を登録する.
                        if (exportPresets) {
                                HashSet<String> registered = new HashSet<String>();
@@ -445,10 +448,19 @@ public class ExportWizardDialog extends JDialog {
                                        cd.setDefaultPartsSetId(defaultPresetId);
                                }
                        }
-                       
+
                        // キャラクターデータを出力する.
                        exportWriter.writeCharacterData(cd);
-                       
+
+                       // カスタムレイヤーパターンを出力する(有効であれば)
+                       if (cd.isEnableCustonLayerPattern()) {
+                               Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatternMap = CustomLayerOrderPersist
+                                               .newInstance(cd).load();
+                               if (customLayerPatternMap != null) {
+                                       exportWriter.writeCustomLayerPatterns(customLayerPatternMap);
+                               }
+                       }
+
                        // readme.txtを出力する.
                        String readmeContents = cd.getDescription();
                        if (readmeContents != null && readmeContents.trim().length() > 0) {
@@ -475,10 +487,10 @@ public class ExportWizardDialog extends JDialog {
 
                        if (exportPartsImages) {
                                Map<PartsIdentifier, PartsSpec> partsSpecMap = partsSelectPanel.getSelectedParts();
-                               
+
                                // パーツ管理情報を出力する
                                exportWriter.writePartsManageData(partsSpecMap);
-                               
+
                                // パーツイメージを出力する
                                exportWriter.writePartsImages(partsSpecMap);
                        }
@@ -490,11 +502,11 @@ public class ExportWizardDialog extends JDialog {
 }
 
 interface ExportResolverBase {
-       
+
        void addChangeListener(ChangeListener l);
-       
+
        void removeChangeListener(ChangeListener l);
-       
+
 }
 
 
@@ -504,22 +516,22 @@ interface ExportResolverBase {
  * @author seraphy
  */
 interface ExportInformationResolver extends ExportResolverBase {
-       
+
        BufferedImage getSamplePicture();
-       
+
        boolean isExportSamplePicture();
-       
+
        boolean isExportPartsImages();
-       
+
        boolean isExportPresets();
-       
+
        String getAuthor();
-       
+
        String getDescription();
 }
 
 abstract class AbstractImportPanel extends JPanel implements ExportResolverBase {
-       
+
        private static final long serialVersionUID = 1L;
 
        protected LinkedList<ChangeListener> listeners = new LinkedList<ChangeListener>();
@@ -529,13 +541,13 @@ abstract class AbstractImportPanel extends JPanel implements ExportResolverBase
                        listeners.add(l);
                }
        }
-       
+
        public void removeChangeListener(ChangeListener l) {
                if (l != null) {
                        listeners.remove(l);
                }
        }
-       
+
        protected void fireChangeEvent() {
                ChangeEvent e = new ChangeEvent(this);
                for (ChangeListener listener : listeners) {
@@ -553,21 +565,21 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
        private static final long serialVersionUID = 1L;
 
        private BufferedImage samplePicture;
-       
+
        private SamplePicturePanel sampleImagePanel;
-       
+
        private JTextField txtAuthor;
-       
+
        private JTextArea txtDescription;
-       
+
        private JCheckBox chkPartsImages;
-       
+
        private JCheckBox chkPresets;
-       
+
        private JCheckBox chkSampleImage;
-       
 
-       
+
+
        protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture) {
                if (characterData == null) {
                        throw new IllegalArgumentException();
@@ -582,16 +594,16 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                GridBagLayout basicPanelLayout = new GridBagLayout();
                GridBagConstraints gbc = new GridBagConstraints();
                setLayout(basicPanelLayout);
-               
+
                JPanel contentsSpecPanel = new JPanel();
                contentsSpecPanel.setBorder(BorderFactory.createCompoundBorder(
                                BorderFactory.createEmptyBorder(5, 5, 5, 5),
                                BorderFactory.createTitledBorder(strings.getProperty("basic.contentsSpec"))));
                BoxLayout contentsSpecPanelLayout = new BoxLayout(contentsSpecPanel, BoxLayout.PAGE_AXIS);
                contentsSpecPanel.setLayout(contentsSpecPanelLayout);
-               
+
                JCheckBox chkCharacterDef = new JCheckBox(strings.getProperty("characterdef"));
-               chkPartsImages = new JCheckBox(strings.getProperty("basic.chk.partsImages")); 
+               chkPartsImages = new JCheckBox(strings.getProperty("basic.chk.partsImages"));
                chkPresets = new JCheckBox(strings.getProperty("basic.chk.presets"));
                chkSampleImage = new JCheckBox(strings.getProperty("basic.chk.samplePicture"));
 
@@ -604,8 +616,8 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                contentsSpecPanel.add(chkSampleImage);
 
                ///
-               
-               JPanel commentPanel = new JPanel(); 
+
+               JPanel commentPanel = new JPanel();
                Dimension archiveInfoPanelMinSize = new Dimension(300, 200);
                commentPanel.setMinimumSize(archiveInfoPanelMinSize);
                commentPanel.setPreferredSize(archiveInfoPanelMinSize);
@@ -614,7 +626,7 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                                BorderFactory.createTitledBorder(strings.getProperty("basic.comment"))));
                GridBagLayout commentPanelLayout = new GridBagLayout();
                commentPanel.setLayout(commentPanelLayout);
-               
+
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridheight = 1;
@@ -641,7 +653,7 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                gbc.weightx = 0.;
                gbc.weighty = 0.;
                commentPanel.add(new JLabel(strings.getProperty("description"), JLabel.RIGHT), gbc);
-               
+
                gbc.gridx = 1;
                gbc.gridy = 1;
                gbc.gridwidth = 1;
@@ -652,15 +664,15 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                commentPanel.add(new JScrollPane(txtDescription), gbc);
 
                ///
-               
+
                sampleImagePanel = new SamplePicturePanel(samplePicture);
                sampleImagePanel.setBorder(BorderFactory.createCompoundBorder(
                                BorderFactory.createEmptyBorder(5, 5, 5, 5),
                                BorderFactory.createTitledBorder(strings.getProperty("basic.sampleImage"))));
-               
-               
+
+
                ///
-               
+
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridheight = 1;
@@ -690,11 +702,11 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                gbc.anchor = GridBagConstraints.WEST;
                gbc.fill = GridBagConstraints.BOTH;
                add(sampleImagePanel, gbc);
-               
+
                loadBasicInfo(characterData);
 
                // アクションリスナ
-               
+
                ActionListener modListener = new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                updateSamplePicture();
@@ -705,11 +717,11 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
                chkPresets.addActionListener(modListener);
                chkSampleImage.addActionListener(modListener);
        }
-       
+
        protected void updateSamplePicture() {
                sampleImagePanel.setVisiblePicture(chkSampleImage.isSelected());
        }
-       
+
        protected void loadBasicInfo(CharacterData characterData) {
                if (samplePicture == null) {
                        // サンプルイメージがなければディセーブル
@@ -732,23 +744,23 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
        public BufferedImage getSamplePicture() {
                return samplePicture;
        }
-       
+
        public boolean isExportSamplePicture() {
                return chkSampleImage.isSelected();
        }
-       
+
        public boolean isExportPartsImages() {
                return chkPartsImages.isSelected();
        }
-       
+
        public boolean isExportPresets() {
                return chkPresets.isSelected();
        }
-       
+
        public String getAuthor() {
                return txtAuthor.getText();
        }
-       
+
        public String getDescription() {
                return txtDescription.getText();
        }
@@ -761,11 +773,11 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform
 interface ExportPartsResolver extends ExportResolverBase {
 
        int getSelectedCount();
-       
+
        void selectByPartsSet(Collection<PartsSet> partsSet);
-       
+
        Map<PartsIdentifier, PartsSpec> getSelectedParts();
-       
+
        Map<PartsSet, List<PartsIdentifier>> checkMissingPartsList(Collection<PartsSet> partsSets);
 
 }
@@ -779,11 +791,11 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
        private static final long serialVersionUID = 1L;
 
        private ExportPartsTableModel partsTableModel;
-       
+
        private JTable partsTable;
-       
+
        private Action actSelectAll;
-       
+
        private Action actDeselectAll;
 
        private Action actSort;
@@ -802,9 +814,9 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                setBorder(BorderFactory.createTitledBorder(strings.getProperty("parts.title")));
 
                setLayout(new BorderLayout());
-               
+
                partsTableModel = new ExportPartsTableModel();
-               
+
                partsTableModel.addTableModelListener(new TableModelListener() {
                        public void tableChanged(TableModelEvent e) {
                                fireChangeEvent();
@@ -812,11 +824,11 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                });
 
                loadPartsInfo(partsSpecResolver);
-               
+
                AppConfig appConfig = AppConfig.getInstance();
-               
+
                final Color disabledForeground = appConfig.getDisabledCellForgroundColor();
-               
+
                partsTable = new JTable(partsTableModel) {
                        private static final long serialVersionUID = 1L;
                        @Override
@@ -840,7 +852,7 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                partsTableModel.adjustColumnModel(partsTable.getColumnModel());
                partsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
                partsTable.setRowSelectionAllowed(true);
-               
+
                Action actPartsSetCheck = new AbstractAction(strings.getProperty("parts.popup.check")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
@@ -855,11 +867,11 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                                partsTableModel.setCheck(selRows, false);
                        }
                };
-               
+
                final JPopupMenu partsTablePopupMenu = new JPopupMenu();
                partsTablePopupMenu.add(actPartsSetCheck);
                partsTablePopupMenu.add(actPartsUnsetCheck);
-               
+
                partsTable.setComponentPopupMenu(partsTablePopupMenu);
 
                add(new JScrollPane(partsTable), BorderLayout.CENTER);
@@ -888,14 +900,14 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                                onSortByTimestamp();
                        }
                };
-               
+
 
                JPanel btnPanel = new JPanel();
                GridBagLayout btnPanelLayout = new GridBagLayout();
                btnPanel.setLayout(btnPanelLayout);
-               
+
                GridBagConstraints gbc = new GridBagConstraints();
-               
+
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridheight = 1;
@@ -907,12 +919,12 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
                btnPanel.add(btnSelectAll, gbc);
-               
+
                gbc.gridx = 1;
                gbc.gridy = 0;
                JButton btnDeselectAll = new JButton(actDeselectAll);
                btnPanel.add(btnDeselectAll, gbc);
-               
+
                gbc.gridx = 2;
                gbc.gridy = 0;
                JButton btnSort = new JButton(actSort);
@@ -930,7 +942,7 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
 
                add(btnPanel, BorderLayout.SOUTH);
        }
-       
+
        protected void loadPartsInfo(PartsSpecResolver partsSpecResolver) {
                partsTableModel.clear();
                for (PartsCategory partsCategory : partsSpecResolver.getPartsCategories()) {
@@ -944,15 +956,15 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                }
                partsTableModel.sort();
        }
-       
+
        protected void onSelectAll() {
                partsTableModel.selectAll();
        }
-       
+
        protected void onDeselectAll() {
                partsTableModel.deselectAll();
        }
-       
+
        protected void onSort() {
                partsTableModel.sort();
                if (partsTableModel.getRowCount() > 0) {
@@ -960,7 +972,7 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                        partsTable.scrollRectToVisible(rct);
                }
        }
-       
+
        protected void onSortByTimestamp() {
                partsTableModel.sortByTimestamp();
                if (partsTableModel.getRowCount() > 0) {
@@ -968,23 +980,23 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
                        partsTable.scrollRectToVisible(rct);
                }
        }
-       
+
        public Map<PartsIdentifier, PartsSpec> getSelectedParts() {
                return partsTableModel.getSelectedParts();
        }
-       
+
        public Map<PartsSet, List<PartsIdentifier>> checkMissingPartsList(Collection<PartsSet> partsSets) {
                return partsTableModel.checkMissingPartsList(partsSets);
        }
-       
+
        public void selectByPartsSet(Collection<PartsSet> partsSets) {
                partsTableModel.selectByPartsSet(partsSets);
        }
-       
+
        public int getSelectedCount() {
                return partsTableModel.getSelectedCount();
        }
-       
+
        @Override
        public void setEnabled(boolean enabled) {
                partsTable.setEnabled(enabled);
@@ -997,9 +1009,9 @@ class ExportPartsSelectPanel extends AbstractImportPanel implements ExportPartsR
 
 
 interface ExportPresetResolve extends ExportResolverBase {
-       
+
        int getSelectedCount();
-       
+
        List<PartsSet> getSelectedPresets();
 }
 
@@ -1008,13 +1020,13 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
        private static final long serialVersionUID = 1L;
 
        private ExportPartsResolver exportPartsResolver;
-       
+
        private ExportPresetTableModel presetTableModel;
-       
+
        private JTable presetTable;
-       
+
        private Action actSelectAll;
-       
+
        private Action actDeselectAll;
 
 
@@ -1024,7 +1036,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                        Collection<PartsSet> partsSets, String defaultPresetId) {
 
                this.exportPartsResolver = exportPartsResolver;
-               
+
                setName("presetSelectPanel");
 
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
@@ -1034,7 +1046,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                setBorder(BorderFactory.createTitledBorder(strings.getProperty("preset.title")));
 
                setLayout(new BorderLayout());
-               
+
                presetTableModel = new ExportPresetTableModel();
 
                presetTableModel.addTableModelListener(new TableModelListener() {
@@ -1051,23 +1063,23 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                });
 
                loadPresetInfo(partsSets, defaultPresetId);
-               
+
                AppConfig appConfig = AppConfig.getInstance();
                final Color warningForegroundColor = appConfig.getExportPresetWarningsForegroundColor();
                final Color disabledForeground = appConfig.getDisabledCellForgroundColor();
-               
+
                presetTable = new JTable(presetTableModel) {
                        private static final long serialVersionUID = 1L;
                        @Override
                        public Component prepareRenderer(TableCellRenderer renderer,
                                        int row, int column) {
                                Component comp = super.prepareRenderer(renderer, row, column);
-                               
+
                                if (comp instanceof JCheckBox) {
                                        // BooleanのデフォルトのレンダラーはKCheckBoxを継承したJTable$BooleanRenderer
                                        comp.setEnabled(isCellEditable(row, column) && isEnabled());
                                }
-                               
+
                                ExportPresetModel presetModel = presetTableModel.getRow(row);
                                if (presetModel.isPresetParts()) {
                                        comp.setFont(getFont().deriveFont(Font.BOLD));
@@ -1077,7 +1089,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
 
                                if (!isEnabled()) {
                                        comp.setForeground(disabledForeground);
-                               
+
                                } else {
                                        if (presetModel.isSelected() && presetModel.getMissingPartsIdentifiers().size() > 0) {
                                                comp.setForeground(warningForegroundColor);
@@ -1091,7 +1103,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                presetTable.setShowGrid(true);
                presetTable.setGridColor(appConfig.getGridColor());
                presetTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
-               
+
                final Action actSelectUsedParts = new AbstractAction(
                                strings.getProperty("preset.popup.selectUsedParts")) {
                        private static final long serialVersionUID = 1L;
@@ -1099,13 +1111,13 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                                exportUsedParts();
                        }
                };
-               
+
                final JPopupMenu popupMenu = new JPopupMenu();
                popupMenu.add(actSelectUsedParts);
-               
+
                presetTable.setComponentPopupMenu(popupMenu);
-               
-               
+
+
                presetTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                presetTableModel.adjustColumnModel(presetTable.getColumnModel());
 
@@ -1129,14 +1141,14 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                                onSort();
                        }
                };
-               
+
 
                JPanel btnPanel = new JPanel();
                GridBagLayout btnPanelLayout = new GridBagLayout();
                btnPanel.setLayout(btnPanelLayout);
-               
+
                GridBagConstraints gbc = new GridBagConstraints();
-               
+
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.gridheight = 1;
@@ -1148,12 +1160,12 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                gbc.ipady = 0;
                JButton btnSelectAll = new JButton(actSelectAll);
                btnPanel.add(btnSelectAll, gbc);
-               
+
                gbc.gridx = 1;
                gbc.gridy = 0;
                JButton btnDeselectAll = new JButton(actDeselectAll);
                btnPanel.add(btnDeselectAll, gbc);
-               
+
                gbc.gridx = 2;
                gbc.gridy = 0;
                JButton btnSort = new JButton(actSort);
@@ -1166,7 +1178,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
 
                add(btnPanel, BorderLayout.SOUTH);
        }
-       
+
        protected void loadPresetInfo(Collection<PartsSet> partsSets, String defaultPresetId) {
                presetTableModel.clear();
                for (PartsSet orgPartsSet : partsSets) {
@@ -1178,7 +1190,7 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                presetTableModel.sort();
                checkMissingParts();
        }
-       
+
        public void checkMissingParts() {
                ArrayList<PartsSet> changedPartsSets = new ArrayList<PartsSet>();
                HashMap<PartsSet, ExportPresetModel> partsSetModelMap = new HashMap<PartsSet, ExportPresetModel>();
@@ -1201,15 +1213,15 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                        presetTableModel.fireTableDataChanged();
                }
        }
-       
+
        protected void onSelectAll() {
                presetTableModel.selectAll();
        }
-       
+
        protected void onDeselectAll() {
                presetTableModel.deselectAll();
        }
-       
+
        protected void onSort() {
                presetTableModel.sort();
                if (presetTableModel.getRowCount() > 0) {
@@ -1217,11 +1229,11 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                        presetTable.scrollRectToVisible(rct);
                }
        }
-       
+
        public List<PartsSet> getSelectedPresets() {
                return presetTableModel.getSelectedPresets();
        }
-       
+
        protected void exportUsedParts() {
                ArrayList<PartsSet> partsSets = new ArrayList<PartsSet>();
                int[] selRows = presetTable.getSelectedRows();
@@ -1229,18 +1241,18 @@ class ExportPresetSelectPanel extends AbstractImportPanel implements ExportPrese
                        ExportPresetModel presetModel = presetTableModel.getRow(selRow);
                        partsSets.add(presetModel.getPartsSet());
                }
-               
+
                exportPartsResolver.selectByPartsSet(partsSets);
        }
-       
+
        public int getSelectedCount() {
                return presetTableModel.getSelectedCount();
        }
-       
+
        public String getDefaultPresetId() {
                return presetTableModel.getDefaultPresetId();
        }
-       
+
        @Override
        public void setEnabled(boolean enabled) {
                this.presetTable.setEnabled(enabled);
@@ -1258,9 +1270,9 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
        private static final long serialVersionUID = 1L;
 
        private static final String[] columnNames;
-       
+
        private static final int[] columnWidths;
-       
+
        private boolean enabled = true;
 
        static {
@@ -1275,7 +1287,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                                strings.getProperty("parts.column.author"),
                                strings.getProperty("parts.column.version"),
                };
-               
+
                columnWidths = new int[] {
                                Integer.parseInt(strings.getProperty("parts.column.selected.width")),
                                Integer.parseInt(strings.getProperty("parts.column.category.width")),
@@ -1285,7 +1297,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                                Integer.parseInt(strings.getProperty("parts.column.version.width")),
                };
        }
-       
+
        public void adjustColumnModel(TableColumnModel columnModel) {
                for (int idx = 0; idx < columnWidths.length; idx++) {
                        columnModel.getColumn(idx).setPreferredWidth(columnWidths[idx]);
@@ -1295,12 +1307,12 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
        public int getColumnCount() {
                return columnNames.length;
        }
-       
+
        @Override
        public String getColumnName(int column) {
                return columnNames[column];
        }
-       
+
        public Object getValueAt(int rowIndex, int columnIndex) {
                ExportPartsSelectModel partsSelectModel = getRow(rowIndex);
                switch (columnIndex) {
@@ -1324,7 +1336,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                return "";
        }
-       
+
        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
                ExportPartsSelectModel partsSelectModel = getRow(rowIndex);
@@ -1337,7 +1349,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                fireTableRowsUpdated(rowIndex, rowIndex);
        }
-       
+
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
                if (columnIndex == 0) {
@@ -1345,7 +1357,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                return false;
        }
-       
+
        @Override
        public Class<?> getColumnClass(int columnIndex) {
                switch (columnIndex) {
@@ -1361,12 +1373,12 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                return String.class;
        }
-       
+
        public void sort() {
                Collections.sort(elements);
                fireTableDataChanged();
        }
-       
+
        public void sortByTimestamp() {
                Collections.sort(elements, new Comparator<ExportPartsSelectModel>() {
                        public int compare(ExportPartsSelectModel o1, ExportPartsSelectModel o2) {
@@ -1392,21 +1404,21 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                });
                fireTableDataChanged();
        }
-       
+
        public void selectAll() {
                for (ExportPartsSelectModel model : elements) {
                        model.setChecked(true);
                }
                fireTableDataChanged();
        }
-       
+
        public void deselectAll() {
                for (ExportPartsSelectModel model : elements) {
                        model.setChecked(false);
                }
                fireTableDataChanged();
        }
-       
+
        /**
         * 選択されているパーツイメージのマップを返す.<br>
         * @return 選択されているパーツイメージのマップ
@@ -1420,7 +1432,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                return selectedPartsMap;
        }
-       
+
        /**
         * パーツセットのコレクションを指定し、パーツセットの各パーツがすべてエクスポート対象になっているものだけを返す.<br>
         * @param partsSets パーツセットのリスト
@@ -1431,7 +1443,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                        throw new IllegalArgumentException();
                }
                Map<PartsIdentifier, PartsSpec> selectedPartsMap = getSelectedParts();
-               HashMap<PartsSet, List<PartsIdentifier>> missingPartsMap = new HashMap<PartsSet, List<PartsIdentifier>>(); 
+               HashMap<PartsSet, List<PartsIdentifier>> missingPartsMap = new HashMap<PartsSet, List<PartsIdentifier>>();
 
                for (PartsSet partsSet : partsSets) {
                        ArrayList<PartsIdentifier> missingPartss = new ArrayList<PartsIdentifier>();
@@ -1445,10 +1457,10 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                        Collections.sort(missingPartss);
                        missingPartsMap.put(partsSet, missingPartss);
                }
-               
+
                return missingPartsMap;
        }
-       
+
        /**
         * パーツセットで使用されているパーツを選択状態にする.
         * @param partsSet パーツセットのコレクション
@@ -1472,7 +1484,7 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                fireTableDataChanged();
        }
-       
+
        /**
         * 選択されているパーツ数を返す.
         * @return パーツ数
@@ -1486,14 +1498,14 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
                }
                return count;
        }
-       
+
        public void setEnabled(boolean enabled) {
                if (this.enabled != enabled) {
                        this.enabled = enabled;
                        fireTableDataChanged();
                }
        }
-       
+
        public boolean isEnabled() {
                return enabled;
        }
@@ -1513,13 +1525,13 @@ class ExportPartsTableModel extends AbstractTableModelWithComboBoxModel<ExportPa
 
 
 class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
-       
+
        private boolean checked;
-       
+
        private PartsIdentifier partsIdentifier;
-       
+
        private PartsSpec partsSpec;
-       
+
        private Timestamp timestamp;
 
        public ExportPartsSelectModel(PartsIdentifier partsIdentifier, PartsSpec partsSpec, boolean selected) {
@@ -1529,7 +1541,7 @@ class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
                this.partsIdentifier = partsIdentifier;
                this.partsSpec = partsSpec;
                this.checked = selected;
-               
+
                long maxLastModified = partsSpec.getPartsFiles().lastModified();
                if (maxLastModified > 0) {
                        timestamp = new Timestamp(maxLastModified);
@@ -1537,12 +1549,12 @@ class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
                        timestamp = null;
                }
        }
-       
+
        @Override
        public int hashCode() {
                return partsIdentifier.hashCode();
        }
-       
+
        @Override
        public boolean equals(Object obj) {
                if (obj == this) {
@@ -1554,7 +1566,7 @@ class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
                }
                return false;
        }
-       
+
        public int compareTo(ExportPartsSelectModel o) {
                int ret = (checked ? 0 : 1) - (o.checked ? 0 : 1); // 逆順
                if (ret == 0) {
@@ -1562,39 +1574,39 @@ class ExportPartsSelectModel implements Comparable<ExportPartsSelectModel> {
                }
                return ret;
        }
-       
+
        public PartsIdentifier getPartsIdentifier() {
                return this.partsIdentifier;
        }
-       
+
        public PartsSpec getPartsSpec() {
                return this.partsSpec;
        }
-       
+
        public boolean isChecked() {
                return checked;
        }
-       
+
        public void setChecked(boolean checked) {
                this.checked = checked;
        }
-       
+
        public PartsCategory getPartsCategory() {
                return this.partsIdentifier.getPartsCategory();
        }
-       
+
        public String getPartsName() {
                return this.partsIdentifier.getLocalizedPartsName();
        }
-       
+
        public Timestamp getTimestamp() {
                return timestamp == null ? null : (Timestamp) timestamp.clone();
        }
-       
+
        public String getAuthor() {
                return partsSpec.getAuthor();
        }
-       
+
        public String getVersion() {
                double version = partsSpec.getVersion();
                if (version <= 0) {
@@ -1610,9 +1622,9 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
        private static final long serialVersionUID = 1L;
 
        private static final String[] columnNames;
-       
+
        private static final int[] columnWidths;
-       
+
        private boolean enabled = true;
 
        static {
@@ -1625,7 +1637,7 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                                strings.getProperty("preset.column.name"),
                                strings.getProperty("preset.column.missingparts"),
                };
-               
+
                columnWidths = new int[] {
                                Integer.parseInt(strings.getProperty("preset.column.selected.width")),
                                Integer.parseInt(strings.getProperty("preset.column.default.width")),
@@ -1633,9 +1645,9 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                                Integer.parseInt(strings.getProperty("preset.column.missingparts.width")),
                };
        }
-       
+
        private String defaultPresetId;
-       
+
        public void adjustColumnModel(TableColumnModel columnModel) {
                for (int idx = 0; idx < columnWidths.length; idx++) {
                        columnModel.getColumn(idx).setPreferredWidth(columnWidths[idx]);
@@ -1645,12 +1657,12 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
        public int getColumnCount() {
                return columnNames.length;
        }
-       
+
        @Override
        public String getColumnName(int column) {
                return columnNames[column];
        }
-       
+
        public Object getValueAt(int rowIndex, int columnIndex) {
                ExportPresetModel presetModel = getRow(rowIndex);
                switch (columnIndex) {
@@ -1673,7 +1685,7 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                return "";
        }
-       
+
        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
                ExportPresetModel presetModel = getRow(rowIndex);
@@ -1703,7 +1715,7 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                fireTableRowsUpdated(rowIndex, rowIndex);
        }
-       
+
        @Override
        public Class<?> getColumnClass(int columnIndex) {
                switch (columnIndex) {
@@ -1719,7 +1731,7 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                return String.class;
        }
-       
+
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
                if (columnIndex == 0 || columnIndex == 1) {
@@ -1727,26 +1739,26 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                return false;
        }
-       
+
        public void sort() {
                Collections.sort(elements);
                fireTableDataChanged();
        }
-       
+
        public void selectAll() {
                for (ExportPresetModel model : elements) {
                        model.setSelected(true);
                }
                fireTableDataChanged();
        }
-       
+
        public void deselectAll() {
                for (ExportPresetModel model : elements) {
                        model.setSelected(false);
                }
                fireTableDataChanged();
        }
-       
+
        /**
         * 選択されているパーツセットのリストを返す.<br>
         * なにもなければ空.<br>
@@ -1763,7 +1775,7 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                return partsSets;
        }
-       
+
        public int getSelectedCount() {
                int count = 0;
                for (ExportPresetModel presetModel : elements) {
@@ -1773,11 +1785,11 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
                }
                return count;
        }
-       
+
        public String getDefaultPresetId() {
                return defaultPresetId;
        }
-       
+
        /**
         * デフォルトのプリセットを設定する.<br>
         * @param defaultPresetId
@@ -1785,14 +1797,14 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
        public void setDefaultPresetId(String defaultPresetId) {
                this.defaultPresetId = defaultPresetId;
        }
-       
+
        public void setEnabled(boolean enabled) {
                if (this.enabled != enabled) {
                        this.enabled = enabled;
                        fireTableDataChanged();
                }
        }
-       
+
        public boolean isEnabled() {
                return enabled;
        }
@@ -1801,11 +1813,11 @@ class ExportPresetTableModel extends AbstractTableModelWithComboBoxModel<ExportP
 class ExportPresetModel implements Comparable<ExportPresetModel> {
 
        private boolean selected;
-       
+
        private PartsSet partsSet;
-       
+
        private List<PartsIdentifier> missingPartsIdentifiers;
-       
+
        public ExportPresetModel(PartsSet partsSet, boolean selected) {
                if (partsSet == null) {
                        throw new IllegalArgumentException();
@@ -1813,12 +1825,12 @@ class ExportPresetModel implements Comparable<ExportPresetModel> {
                this.partsSet = partsSet;
                this.selected = selected;
        }
-       
+
        @Override
        public int hashCode() {
                return partsSet.hashCode();
        }
-       
+
        @Override
        public boolean equals(Object obj) {
                if (obj == this) {
@@ -1830,7 +1842,7 @@ class ExportPresetModel implements Comparable<ExportPresetModel> {
                }
                return false;
        }
-       
+
        public int compareTo(ExportPresetModel o) {
                int ret = (selected ? 0 : 1) - (o.selected ? 0 : 1);
                if (ret == 0) {
@@ -1838,38 +1850,38 @@ class ExportPresetModel implements Comparable<ExportPresetModel> {
                }
                return ret;
        }
-       
+
        public String getPartsSetName() {
                String name = partsSet.getLocalizedName();
                return name == null ? "" : name;
        }
-       
+
        public boolean isPresetParts() {
                return partsSet.isPresetParts();
        }
-       
+
        public boolean isSelected() {
                return selected;
        }
-       
+
        public void setSelected(boolean selected) {
                this.selected = selected;
        }
-       
+
        public PartsSet getPartsSet() {
                return partsSet;
        }
-       
+
        public void setMissingPartsIdentifiers(
                        List<PartsIdentifier> missingPartsIdentifiers) {
                this.missingPartsIdentifiers = Collections.unmodifiableList(missingPartsIdentifiers);
        }
-       
+
        public List<PartsIdentifier> getMissingPartsIdentifiers() {
                if (missingPartsIdentifiers == null) {
                        return Collections.emptyList();
                }
                return missingPartsIdentifiers;
        }
-       
+
 }
index 69cc6d4..d76f22e 100644 (file)
@@ -83,6 +83,8 @@ import charactermanaj.Main;
 import charactermanaj.graphics.io.PNGFileImageHeader;
 import charactermanaj.model.AppConfig;
 import charactermanaj.model.CharacterData;
+import charactermanaj.model.CustomLayerOrder;
+import charactermanaj.model.CustomLayerOrderKey;
 import charactermanaj.model.PartsAuthorInfo;
 import charactermanaj.model.PartsCategory;
 import charactermanaj.model.PartsIdentifier;
@@ -530,7 +532,8 @@ public class ImportWizardDialog extends JDialog {
 
                CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
 
-               CharacterData characterData = cd.duplicateBasicInfo();
+               CharacterData characterData = cd.duplicateBasicInfo(); // インポートしたキャラクターデータ
+               Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatterns = importModel.getCustomLayerPatternMap();
 
                // キャラクターセット名と作者名を設定する
                characterData.setName(importTypeSelectPanel.getCharacterName());
@@ -549,7 +552,7 @@ public class ImportWizardDialog extends JDialog {
 
                // プロファイルの新規作成
                // docBaseが設定されて返される.
-               persist.createProfile(characterData);
+               persist.createProfile(characterData, customLayerPatterns);
 
                // インポートするパーツの更新
                if (importTypeSelectPanel.isImportPartsImages()) {
@@ -588,7 +591,7 @@ public class ImportWizardDialog extends JDialog {
 
                CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
 
-               CharacterData characterData = current.duplicateBasicInfo();
+               CharacterData characterData = current.duplicateBasicInfo(); // 現在のもの。(インポートしたものではない)
 
                boolean imported = false;
                boolean modCharacterDef = false;
index a9521a3..4a2db81 100644 (file)
@@ -320,6 +320,9 @@ public class MainFrame extends JFrame
 
                                                                // 画面構成の再構築
                                                                initComponent(cd);
+
+                                                               // メニューの状態の更新
+                                                               menuUpdater.run();
                                                        }
 
                                                        if (e.isReloadPartsAndFavorites()) {
@@ -424,8 +427,7 @@ public class MainFrame extends JFrame
 
                        // 画面コンポーネント作成
                        initComponent(characterData);
-                       JMenuBar menuBar = createMenuBar();
-                       setJMenuBar(menuBar);
+                       initMenubar();
 
                        // お気に入り変更通知を受け取る
                        FavoritesChangeObserver.getDefault().addFavoritesChangeListener(
@@ -608,7 +610,7 @@ public class MainFrame extends JFrame
                        // Mac OS Xの場合はウィンドウにタイトルはつけない。
                        title = "";
                } else {
-                       title = strings.getProperty("title");
+                       title = strings.getProperty("title") + " - ";
                }
                setTitle(title + characterData.getName());
 
@@ -2694,10 +2696,8 @@ public class MainFrame extends JFrame
 
        /**
         * メニューバーを構築します.
-        *
-        * @return メニューバー
         */
-       protected JMenuBar createMenuBar() {
+       protected void initMenubar() {
                final Properties strings = LocalizedResourcePropertyLoader
                                .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);
 
@@ -2955,7 +2955,22 @@ public class MainFrame extends JFrame
                        }
                });
 
-               return menuBar;
+               // メニューバーの設置
+               setJMenuBar(menuBar);
+
+               // メニューの状態を更新するハンドラ
+               menuUpdater = new Runnable() {
+                       @Override
+                       public void run() {
+                               // カスタムレイヤーの有効状態によってメニュー項目の表示制御を行う.
+                               mnuCustomLayer.setVisible(characterData.isEnableCustonLayerPattern());
+                       }
+               };
+               menuUpdater.run();
        }
 
+       /**
+        * メニューの状態を更新する
+        */
+       private Runnable menuUpdater;
 }
index 243362c..153fec1 100644 (file)
@@ -170,6 +170,11 @@ public class ProfileEditDialog extends JDialog {
         */
        private JCheckBox chkWatchDir;
 
+       /**
+        * カスタムレイヤーの有効・無効
+        */
+       private JCheckBox chkEnableCustomLayer;
+
 
        /**
         * カラーグループのモデル
@@ -878,7 +883,12 @@ public class ProfileEditDialog extends JDialog {
                layersPanel.add(layersBtnPanel, BorderLayout.EAST);
 
                chkWatchDir = new JCheckBox(strings.getProperty("layers.watchdir"));
-               layersPanel.add(chkWatchDir, BorderLayout.SOUTH);
+               chkEnableCustomLayer = new JCheckBox(strings.getProperty("layers.enableCustomLayer"));
+
+               Box chkboxs = Box.createVerticalBox();
+               chkboxs.add(chkWatchDir);
+               chkboxs.add(chkEnableCustomLayer);
+               layersPanel.add(chkboxs, BorderLayout.SOUTH);
 
                // Presets
                JPanel partssetsPanel = new JPanel(new BorderLayout());
@@ -1172,6 +1182,8 @@ public class ProfileEditDialog extends JDialog {
 
                // ディレクトリ監視有無
                chkWatchDir.setSelected(original.isWatchDirectory());
+               // カスタムレイヤーパターンの有効・無効
+               chkEnableCustomLayer.setSelected(original.isEnableCustonLayerPattern());
 
                // パーツセット
                ArrayList<PartsSet> partsSets = new ArrayList<PartsSet>();
@@ -1408,6 +1420,8 @@ public class ProfileEditDialog extends JDialog {
 
                // ディレクトリの監視
                cd.setWatchDirectory(chkWatchDir.isSelected());
+               // カスタムレイヤーパターンの有効・無効
+               cd.setEnableCustomLayerPattern(chkEnableCustomLayer.isSelected());
 
                // パーツセット情報
                int mxPartssets = partssetsTableModel.getRowCount();
index a02256e..f55513c 100644 (file)
@@ -185,8 +185,10 @@ public final class ProfileListManager {
                        throw new IOException("開くことのできないキャラクターデータです。:" + characterData);
                }
 
-               // キャラクターデータのロード
+               // キャラクターデータの準備(バージョンアップに伴う補正等)
                prepare(characterData);
+
+               // キャラクターデータのロード
                loadCharacterData(characterData);
                loadFavorites(characterData);
 
@@ -313,8 +315,10 @@ public final class ProfileListManager {
                try {
                        characterData = loadRecent();
                        if (characterData != null) {
-                               // ã\82­ã\83£ã\83©ã\82¯ã\82¿ã\83¼ã\83\87ã\83¼ã\82¿ã\82\92読ã\81¿è¾¼ã\82\80
+                               // ã\82­ã\83£ã\83©ã\82¯ã\82¿ã\83¼ã\83\87ã\83¼ã\82¿ã\81®æº\96å\82\99\83\90ã\83¼ã\82¸ã\83§ã\83³ã\82¢ã\83\83ã\83\97ã\81«ä¼´ã\81\86è£\9cæ­£ç­\89)
                                prepare(characterData);
+
+                               // キャラクターデータを読み込む
                                loadCharacterData(characterData);
                                loadFavorites(characterData);
                        }
@@ -374,9 +378,10 @@ public final class ProfileListManager {
                        logger.info("オープンできるプロファイルがないため、新規プロファイルを作成します。");
                        try {
                                CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
-                               characterData = defProv
-                                               .createDefaultCharacterData(DefaultCharacterDataVersion.V3);
-                               persistent.createProfile(characterData);
+                               characterData = defProv.createDefaultCharacterData(DefaultCharacterDataVersion.V3);
+                               Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatterns =
+                                               defProv.createDefaultCustomLayerOrderMap(characterData, DefaultCharacterDataVersion.V3);
+                               persistent.createProfile(characterData, customLayerPatterns);
 
                        } catch (IOException ex) {
                                // デフォルトのプロファイルが作成できないことは致命的であるが、
@@ -397,30 +402,34 @@ public final class ProfileListManager {
        }
 
        /**
-        * キャラクターデータの準備を行う
+        *  キャラクターデータの準備を行う。(バージョンアップに伴う補正等)
         * @param characterData
         */
        public static void prepare(CharacterData characterData) {
-               CustomLayerOrderPersist customLayerOrderPersist = CustomLayerOrderPersist.newInstance(characterData);
-               if (!customLayerOrderPersist.exist()) {
-                       // まだカスタムレイヤーが登録されていない場合(空ファイルの登録は無視する)
-                       // カスタムレイヤーをまだ使ったことがないキャラクターデータを最初に開いた場合
-                       String structureSig = characterData.toStructureString();
-
-                       Map<CustomLayerOrderKey, List<CustomLayerOrder>> map = Collections.emptyMap();
-
-                       CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
-                       CharacterData v3 = defProv.createDefaultCharacterData(DefaultCharacterDataVersion.V3);
-                       if (v3.toStructureString().equals(structureSig)) {
-                               // デフォルトのキャラクターセット(v3)と同一構造であれば、
-                               // V3デフォルト用のカスタムレイヤー定義を事前にセットする
-                               map = defProv.createDefaultCustomLayerOrderMap(characterData, DefaultCharacterDataVersion.V3);
-                       }
+               if (characterData.isEnableCustonLayerPattern()) {
+                       CustomLayerOrderPersist customLayerOrderPersist = CustomLayerOrderPersist.newInstance(characterData);
+                       if (!customLayerOrderPersist.exist()) {
+                               // まだカスタムレイヤーが登録されていない場合(空ファイルの登録は無視する)
+                               // カスタムレイヤーをまだ使ったことがないキャラクターデータを最初に開いた場合
+                               String structureSig = characterData.toStructureString();
 
-                       try {
-                               customLayerOrderPersist.save(map);
-                       } catch (Exception ex) {
-                               logger.log(Level.WARNING, "failed to save the custom layer mapping.", ex);
+                               // 既定は空のパターン
+                               Map<CustomLayerOrderKey, List<CustomLayerOrder>> map = Collections.emptyMap();
+
+                               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);
+                               }
                        }
                }
        }
index dba8ea0..9a35f7a 100644 (file)
@@ -26,6 +26,7 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.net.URI;
 import java.text.MessageFormat;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -84,6 +85,7 @@ import charactermanaj.model.CharacterData;
 import charactermanaj.model.CharacterDataChangeObserver;
 import charactermanaj.model.CustomLayerOrder;
 import charactermanaj.model.CustomLayerOrderKey;
+import charactermanaj.model.RecommendationURL;
 import charactermanaj.model.io.CharacterDataDefaultProvider;
 import charactermanaj.model.io.CharacterDataPersistent;
 import charactermanaj.model.io.CustomLayerOrderPersist;
@@ -859,17 +861,35 @@ public class ProfileSelectorDialog extends JDialog {
                                JComboBox comboTemplates = new JComboBox();
                                comboTemplates.setEditable(false);
 
-                               final JLabel lbl = new JLabel();
+                               // テンプレート一覧のEntryを表示するので、カスタムレンダラーで描画する
                                comboTemplates.setRenderer(new ListCellRenderer() {
-                                       public Component getListCellRendererComponent(JList list,
-                                                       Object value, int index, boolean isSelected,
-                                                       boolean cellHasFocus) {
-                                               @SuppressWarnings("unchecked")
-                                               Map.Entry<String, String> entry = (Map.Entry<String, String>) value;
-                                               if (entry != null) {
-                                                       lbl.setText(entry.getValue());
+
+                                       private JLabel label = new JLabel();
+
+                                       @Override
+                                       public Component getListCellRendererComponent(JList list, Object value, int index,
+                                                       boolean isSelected, boolean cellHasFocus) {
+                                               // 背景色透過制御
+                                               label.setOpaque(isSelected && index >= 0);
+
+                                               if (isSelected) {
+                                                       label.setBackground(list.getSelectionBackground());
+                                                       label.setForeground(list.getSelectionForeground());
+                                               } else {
+                                                       label.setBackground(list.getBackground());
+                                                       label.setForeground(list.getForeground());
                                                }
-                                               return lbl;
+
+                                               if (value == null) {
+                                                       label.setText("");
+                                               } else {
+                                                       @SuppressWarnings("unchecked")
+                                                       Map.Entry<String, String> entry = (Map.Entry<String, String>) value;
+
+                                                       label.setFont(list.getFont());
+                                                       label.setText(entry.getValue());
+                                               }
+                                               return label;
                                        }
                                });
 
@@ -880,6 +900,10 @@ public class ProfileSelectorDialog extends JDialog {
                                        comboTemplates.addItem(entry);
                                }
 
+                               // ブランク用のダミーテンプレートを追加する
+                               comboTemplates.addItem(new AbstractMap.SimpleEntry<String, String>("",
+                                               strings.getProperty("template.blank")));
+
                                // コンボボックスの幅を広げる.
                                // (短いとInputBoxのタイトルが隠れるため)
                                Dimension preferredSize = comboTemplates.getPreferredSize();
@@ -904,10 +928,20 @@ public class ProfileSelectorDialog extends JDialog {
                                        return;
                                }
 
-                               // テンプレートを読み込む
+                               // 選択したテンプレートを取得
                                String characterXmlName = selection.getKey();
-                               cd = defProv.loadPredefinedCharacterData(characterXmlName);
-                               customLayerOrderMap = defProv.loadPredefinedCustomLayerOrder(cd, characterXmlName);
+                               if (characterXmlName != null && characterXmlName.length() > 0) {
+                                       // テンプレートを読み込む
+                                       cd = defProv.loadPredefinedCharacterData(characterXmlName);
+                                       customLayerOrderMap = defProv.loadPredefinedCustomLayerOrder(cd, characterXmlName);
+
+                               } else {
+                                       // プランクを選択している場合は空のキャラクターデータを作成する
+                                       cd = new CharacterData();
+                                       // お勧めURLは空にする。(nullの場合は旧形式とみなして取得時にデフォルトのURLが設定されるため)
+                                       cd.setRecommendationURLList(new ArrayList<RecommendationURL>());
+                                       customLayerOrderMap = null;
+                               }
 
                        } catch (Exception ex) {
                                ErrorMessageHelper.showErrorDialog(this, ex);
@@ -934,16 +968,11 @@ public class ProfileSelectorDialog extends JDialog {
 
                // 新規プロファイルを保存する.
                try {
-                       persist.createProfile(newCd);
-                       persist.saveFavorites(newCd);
-
-                       // カスタムレイヤーマッピングを保存する
+                       // キャラクターデータと、カスタムレイヤーマッピングを保存する
                        // ※ 設定ダイアログでキャラクターデータ構造を変更している場合には
                        // 最初にロードしたカスタムレイヤーの定義と合致しないかもしれないが、とりあえず登録しておく。
-                       if (customLayerOrderMap == null) {
-                               customLayerOrderMap = Collections.emptyMap();
-                       }
-                       CustomLayerOrderPersist.newInstance(newCd).save(customLayerOrderMap);
+                       persist.createProfile(newCd, customLayerOrderMap);
+                       persist.saveFavorites(newCd);
 
                } catch (Exception ex) {
                        ErrorMessageHelper.showErrorDialog(this, ex);
@@ -1228,9 +1257,14 @@ public class ProfileSelectorDialog extends JDialog {
                                defualtName = defualtName.replace(c, '_');
                        }
 
+                       // カスタムレイヤーパターンのロード(なければnull)
+                       CustomLayerOrderPersist customLayerPersist = CustomLayerOrderPersist.newInstance(cd);
+                       Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatterns = customLayerPersist.load();
+
                        // カスタマイズ用テンプレートファイルの格納場所を取得する.
                        final CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider();
-                       final File templDir = defProv.getTemplateDir(true);
+                       final File templDir = defProv.getUserTemplateDir();
+                       templDir.mkdirs();
 
                        // 指定されたディレクトリ以外に表示・移動できないファイルシステムビューを使用したファイルチューザ
                        JFileChooser fileChooser = new JFileChooser(
@@ -1270,14 +1304,14 @@ public class ProfileSelectorDialog extends JDialog {
                                }
                        };
 
-                       // 保存先ファイル名
+                       // 保存先ファイル名の入力
                        fileChooser.setSelectedFile(new File(templDir, defualtName));
                        int ret = fileChooser.showSaveDialog(this);
                        if (ret != JFileChooser.APPROVE_OPTION) {
                                return;
                        }
 
-                       // テンプレート名
+                       // テンプレート名の入力
                        String localizedName = cd.getName();
                        final Properties strings = LocalizedResourcePropertyLoader
                                        .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);
@@ -1287,8 +1321,9 @@ public class ProfileSelectorDialog extends JDialog {
                                return;
                        }
 
+                       // テンプレートファイルの作成(上書き保存)
                        File outFile = fileChooser.getSelectedFile();
-                       defProv.saveTemplate(outFile.getName(), cd, localizedName);
+                       defProv.saveTemplate(outFile.getName(), cd, localizedName, customLayerPatterns);
 
                } catch (Exception ex) {
                        ErrorMessageHelper.showErrorDialog(this, ex);
index 6ebd589..97e94bb 100644 (file)
@@ -4,6 +4,7 @@ import java.awt.BorderLayout;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Font;
+import java.awt.GraphicsEnvironment;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
@@ -43,10 +44,11 @@ import charactermanaj.model.io.WorkingSetPersist;
 import charactermanaj.ui.util.FileDropTarget;
 import charactermanaj.util.ErrorMessageHelper;
 import charactermanaj.util.LocalizedResourcePropertyLoader;
+import charactermanaj.util.UIHelper;
 
 /**
  * 起動時にキャラクターデータディレクトリを選択するためのモーダルダイアログ.<br>
- * 
+ *
  * @author seraphy
  */
 public class SelectCharatersDirDialog extends JDialog {
@@ -59,36 +61,36 @@ public class SelectCharatersDirDialog extends JDialog {
         * 最後に使用したキャラクターデータディレクトリと、その履歴情報.
         */
        private final RecentCharactersDir recentCharactersDir;
-       
+
        /**
         * 既定のディレクトリ
         */
        private File defaultCharactersDir;
-       
-       
+
+
        /**
         * 選択されたディレクトリ
         */
        private File selectedCharacterDir;
-       
+
        /**
         * 次回起動時に問い合わせない
         */
        private boolean doNotAskAgain;
-       
-       
+
+
        /**
         * ディレクトリ選択コンボ
         */
        private JComboBox combDir;
-       
+
        /**
         * 次回起動時に問い合わせないチェックボックス
         */
        private JCheckBox chkDoNotAsk;
-       
-       
-       
+
+
+
        public File getDefaultCharactersDir() {
                return defaultCharactersDir;
        }
@@ -96,18 +98,18 @@ public class SelectCharatersDirDialog extends JDialog {
        public void setDefaultCharactersDir(File defaultCharactersDir) {
                this.defaultCharactersDir = defaultCharactersDir;
        }
-       
+
        public File getSelectedCharacterDir() {
                return selectedCharacterDir;
        }
-       
+
        public boolean isDoNotAskAgain() {
                return doNotAskAgain;
        }
-       
+
        /**
         * コンストラクタ
-        * 
+        *
         * @param parent
         *            親(通常は、null)
         * @param recentCharactersDir
@@ -121,7 +123,7 @@ public class SelectCharatersDirDialog extends JDialog {
                                                "recentCharactersDirにnullは指定できません。");
                        }
                        this.recentCharactersDir = recentCharactersDir;
-                       
+
                        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
                        addWindowListener(new WindowAdapter() {
                                @Override
@@ -130,28 +132,28 @@ public class SelectCharatersDirDialog extends JDialog {
                                }
                        });
                        initComponent();
-                       
+
                } catch (RuntimeException ex) {
                        logger.log(Level.SEVERE, "キャラクターディレクトリ選択ダイアログの生成に失敗しました。", ex);
                        dispose();
                        throw ex;
                }
        }
-       
+
        private void initComponent() {
                Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
                        .getLocalizedProperties("languages/selectCharatersDirDialog");
-               
+
                Container contentPane = getContentPane();
                contentPane.setLayout(new BorderLayout(3, 3));
-               
+
                AbstractAction actOk = new AbstractAction(strings.getProperty("btn.ok")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
                                onOK();
                        }
                };
-               
+
                AbstractAction actClose = new AbstractAction(strings.getProperty("btn.cancel")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
@@ -173,8 +175,7 @@ public class SelectCharatersDirDialog extends JDialog {
                        }
                };
 
-               AbstractAction actRemoveWorkingSets = new AbstractAction(
-                               strings.getProperty("btn.clearWorkingSets")) {
+               AbstractAction actRemoveWorkingSets = new AbstractAction(strings.getProperty("btn.clearWorkingSets")) {
                        private static final long serialVersionUID = 1L;
                        public void actionPerformed(ActionEvent e) {
                                onRemoveWorkingSets();
@@ -205,7 +206,7 @@ public class SelectCharatersDirDialog extends JDialog {
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "close");
                rootPane.getActionMap().put("close", actClose);
-               
+
                btnRemoveWorkingSets.addFocusListener(focusAdapter);
                btnRemoveRecent.addFocusListener(focusAdapter);
                btnOK.addFocusListener(focusAdapter);
@@ -215,7 +216,7 @@ public class SelectCharatersDirDialog extends JDialog {
 
                JPanel dirPanel = new JPanel(new BorderLayout(3, 3));
                dirPanel.setBorder(BorderFactory.createEmptyBorder(3, 10, 3, 3));
-               
+
                JLabel lbl = new JLabel(strings.getProperty("caption"), JLabel.CENTER);
                lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
                lbl.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
@@ -226,21 +227,21 @@ public class SelectCharatersDirDialog extends JDialog {
 
                combDir = new JComboBox();
                combDir.setEditable(true);
-               
+
                dirPanel.add(combDir, BorderLayout.CENTER);
-               
+
                dirPanel.add(new JLabel(strings.getProperty("lbl.dir")), BorderLayout.WEST);
                dirPanel.add(btnBroseForDir, BorderLayout.EAST);
-               
+
                contentPane.add(dirPanel, BorderLayout.NORTH);
-               
+
                JPanel btnPanel = new JPanel();
                GridBagLayout gbl = new GridBagLayout();
                btnPanel.setLayout(gbl);
-               
+
                chkDoNotAsk = new JCheckBox(strings.getProperty("chk.doNotAskAgein"));
                chkDoNotAsk.setSelected(recentCharactersDir.isDoNotAskAgain());
-               
+
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
@@ -253,7 +254,7 @@ public class SelectCharatersDirDialog extends JDialog {
                gbc.ipadx = 0;
                gbc.ipady = 0;
                gbc.insets = new Insets(3, 3, 3, 3);
-               
+
                btnPanel.add(chkDoNotAsk, gbc);
 
                gbc.gridx = 0;
@@ -278,7 +279,7 @@ public class SelectCharatersDirDialog extends JDialog {
                gbc.gridheight = 1;
                gbc.weightx = 1.;
                gbc.weighty = 0.;
-               
+
                btnPanel.add(Box.createGlue(), gbc);
 
                gbc.gridx = Main.isLinuxOrMacOSX() ? 4 : 3;
@@ -298,7 +299,7 @@ public class SelectCharatersDirDialog extends JDialog {
                gbc.weightx = 0.;
                gbc.weighty = 0.;
                btnPanel.add(btnCancel, gbc);
-               
+
                gbc.gridx = 5;
                gbc.gridy = 1;
                gbc.gridwidth = 1;
@@ -307,12 +308,12 @@ public class SelectCharatersDirDialog extends JDialog {
                gbc.weighty = 0.;
                gbc.ipadx = 32;
                gbc.ipady = 0;
-               
+
                btnPanel.add(Box.createGlue(), gbc);
 
                setTitle(strings.getProperty("title"));
                setResizable(false);
-               
+
                contentPane.add(btnPanel, BorderLayout.SOUTH);
 
                // フォルダのドロップによる入力を許可
@@ -331,12 +332,12 @@ public class SelectCharatersDirDialog extends JDialog {
                pack();
                setLocationRelativeTo(null);
        }
-       
+
        /**
         * ドロップによるファイル名の設定.<br>
         * 最初の1つだけを使用する.<br>
         * リストが空であるか、最初のファイルが、フォルダでなければ何もしない.<br>
-        * 
+        *
         * @param dropFiles
         *            ドロップされたファイルリスト
         */
@@ -355,14 +356,14 @@ public class SelectCharatersDirDialog extends JDialog {
                selectedCharacterDir = null;
                dispose();
        }
-       
+
        protected void onOK() {
                try {
                        Object value = combDir.getSelectedItem();
                        if (value != null && value instanceof String) {
                                value = new File((String) value);
                        }
-                       
+
                        if (value != null && value instanceof File) {
                                File file = (File) value;
                                if (!file.exists()) {
@@ -386,7 +387,7 @@ public class SelectCharatersDirDialog extends JDialog {
                        ErrorMessageHelper.showErrorDialog(this, ex);
                }
        }
-       
+
        protected void onBrowse() {
                try {
                        Object selectedItem = combDir.getSelectedItem();
@@ -425,15 +426,14 @@ public class SelectCharatersDirDialog extends JDialog {
                        }
 
                        // 全てのワーキングセットをクリアする.
-                       WorkingSetPersist workingSetPersist = WorkingSetPersist
-                                       .getInstance();
+                       WorkingSetPersist workingSetPersist = WorkingSetPersist.getInstance();
                        workingSetPersist.removeAllWorkingSet();
 
                } catch (Exception ex) {
                        ErrorMessageHelper.showErrorDialog(this, ex);
                }
        }
-       
+
        protected void onRemoveRecent() {
                try {
                        Object current = combDir.getSelectedItem();
@@ -449,7 +449,7 @@ public class SelectCharatersDirDialog extends JDialog {
                        ErrorMessageHelper.showErrorDialog(this, ex);
                }
        }
-       
+
        protected void setRecents() {
                // 現在の候補をクリア.
                while (combDir.getItemCount() > 0) {
@@ -485,14 +485,14 @@ public class SelectCharatersDirDialog extends JDialog {
                        combDir.setSelectedIndex(0);
                }
        }
-       
+
 
        /**
         * キャラクターデータディレクトリを履歴および既定のディレクトリから、任意の使用するディレクトリを選択する.<br>
         * 既定のディレクトリは常に選択候補とする.<br>
         * 新しいディレクトリを指定した場合は、履歴に追加される.<br>
         * 「再度問い合わせなし」を選択している場合で、そのディレクトリが実在すれば、選択ダイアログを表示せず、それを返す.<br>
-        * 
+        *
         * @param defaultCharacterDir
         *            既定のディレクトリ
         * @return 選択したディレクトリ、キャンセルした場合はnull
@@ -520,12 +520,25 @@ public class SelectCharatersDirDialog extends JDialog {
                        recentChars.setDoNotAskAgain(false); // 不正である場合は「再度問い合わせ無し」をリセットする.
                }
 
+               // タスクバーに表示するため、サイズ0の見えないダミーのフレームを作成する
+               // (タスクバーにないとダイアログが他のウィンドウの下にはいったら探すのが大変なため)
+               JFrame dummyFrame = new JFrame();
+               dummyFrame.setUndecorated(true);
+               dummyFrame.setSize(0, 0);
+               dummyFrame.setLocation(GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint()); // 主画面の中央
+               dummyFrame.setIconImage(UIHelper.getInstance().getImage("icons/icon.png")); // アイコンの設定(MainFrameと同じ)
+               dummyFrame.setVisible(true);
+
+               // キャラクターデータディレクトリ選択ダイアログを表示する
                File selectedCharacterDir;
-               SelectCharatersDirDialog dlg = new SelectCharatersDirDialog(null, recentChars);
+               SelectCharatersDirDialog dlg = new SelectCharatersDirDialog(dummyFrame, recentChars);
                dlg.setDefaultCharactersDir(defaultCharacterDir);
                dlg.setRecents();
                dlg.setVisible(true);
-               
+
+               // ダミーのフレームを破棄する
+               dummyFrame.dispose();
+
                selectedCharacterDir = dlg.getSelectedCharacterDir();
                if (selectedCharacterDir != null) {
                        recentChars.setLastUseCharacterDir(selectedCharacterDir);
index 9db9db7..2cdfb45 100644 (file)
@@ -166,7 +166,7 @@ public class ApplicationLogHandler extends Handler {
                                if (file.isFile() && file.canWrite() && name.endsWith(".log")) {
                                        long lastModified = file.lastModified();
                                        if (lastModified > 0 && lastModified < expiredDate) {
-                                               boolean result = file.delete();
+                                               boolean result = file.delete(); // 直接消す。ユーザ走査ではないのでゴミ箱にはいれない。
                                                logger.log(Level.INFO, "remove file " + file + "/succeeded=" + result);
                                        }
                                }
index 281167c..c8205a3 100644 (file)
@@ -1,12 +1,21 @@
 package charactermanaj.util;
 
+import java.awt.BorderLayout;
 import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dialog;
 import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.HeadlessException;
+import java.awt.Window;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javax.swing.JDialog;
 import javax.swing.JOptionPane;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
@@ -23,7 +32,7 @@ public final class ErrorMessageHelper {
         */
        private static final Logger logger = Logger.getLogger(ErrorMessageHelper.class.getName());
 
-       
+
        private ErrorMessageHelper() {
                super();
        }
@@ -40,28 +49,73 @@ public final class ErrorMessageHelper {
 
                // ログに記録する.
                logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
-               
+
                // 例外を表示するパネルの生成
                JTextArea textArea = new JTextArea();
-               
+
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                ex.printStackTrace(pw); // 例外のコールスタックをパネルに表示できるように出力
                pw.close();
 
                textArea.setText(sw.toString());
-               
+
                textArea.setSelectionStart(0);
                textArea.setSelectionEnd(0);
                textArea.setEditable(false);
-               
+
                JScrollPane scr = new JScrollPane(textArea);
                scr.setPreferredSize(new Dimension(400, 150));
                scr.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
                scr.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
 
                // ダイアログの表示
-               JOptionPane.showMessageDialog(parent, scr, "ERROR", JOptionPane.ERROR_MESSAGE);
+               //JOptionPane.showMessageDialog(parent, scr, "ERROR", JOptionPane.ERROR_MESSAGE);
+
+               // 以下、JOptionPaneの既定の処理を真似つつ、リサイズ可能なダイアログを作成する
+
+               JOptionPane pane = new JOptionPane(scr, JOptionPane.ERROR_MESSAGE);
+
+               Window window = getWindowForComponent(parent);
+               System.out.println("window=" + window);
+
+               final JDialog dialog;
+               String title = "ERROR";
+               if (window instanceof Frame) {
+                       dialog = new JDialog((Frame) window, title, true);
+               } else {
+                       dialog = new JDialog((Dialog) window, title, true);
+               }
+               Container contentPane = dialog.getContentPane();
+
+               // xボタンでダイアログを破棄する
+               dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+
+               contentPane.setLayout(new BorderLayout());
+               contentPane.add(pane, BorderLayout.CENTER);
+               dialog.setResizable(true);
+
+               dialog.pack();
+               dialog.setLocationRelativeTo(parent);
+
+        pane.addPropertyChangeListener(JOptionPane.VALUE_PROPERTY, new PropertyChangeListener() {
+            public void propertyChange(PropertyChangeEvent event) {
+                if (dialog.isVisible() && event.getNewValue() != null) {
+                       // ボタン押下等によりダイアログの結果が確定したらダイアログを破棄する
+                    dialog.dispose();
+                }
+            }
+        });
+
+        dialog.setVisible(true);
        }
 
+       static Window getWindowForComponent(Component parentComponent)
+                       throws HeadlessException {
+               if (parentComponent == null)
+                       return JOptionPane.getRootFrame();
+               if (parentComponent instanceof Frame || parentComponent instanceof Dialog)
+                       return (Window) parentComponent;
+               return getWindowForComponent(parentComponent.getParent());
+       }
 }
index 1f8f321..f2e88de 100644 (file)
@@ -34,10 +34,12 @@ public class FileUserData implements UserData {
                this.file = file;
        }
 
+       @Override
        public boolean exists() {
                return file.exists() && file.isFile();
        }
 
+       @Override
        public long lastModified() {
                return file.lastModified();
        }
@@ -47,17 +49,23 @@ public class FileUserData implements UserData {
                return file.length();
        }
 
+       @Override
        public InputStream openStream() throws IOException {
                return new BufferedInputStream(new FileInputStream(file));
        }
 
+       @Override
        public OutputStream getOutputStream() throws IOException {
                return new BufferedOutputStream(new FileOutputStream(file));
        }
 
+       @Override
        public boolean delete() {
                try {
-                       return file.delete();
+                       if (exists()) {
+                               FileUtilities.delete(file);
+                       }
+                       return true;
 
                } catch (Exception ex) {
                        // セキュリティ例外ぐらい.
@@ -70,4 +78,4 @@ public class FileUserData implements UserData {
        public String toString() {
                return "FileUserData{file:" + file + "}";
        }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/charactermanaj/util/FileUtilities.java b/src/main/java/charactermanaj/util/FileUtilities.java
new file mode 100644 (file)
index 0000000..df62ea5
--- /dev/null
@@ -0,0 +1,123 @@
+package charactermanaj.util;
+
+import java.awt.Desktop;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import charactermanaj.model.AppConfig;
+
+public final class FileUtilities {
+
+       private static final Method methodMoveToTrash = getMethodMoveToTrash();
+
+       private FileUtilities() {
+               super();
+       }
+
+       private static Method getMethodMoveToTrash() {
+               try {
+                       // MOVE_TO_TRASHアクションを取得する(定義されていないバージョンの場合は実行時例外)
+                       Desktop.Action moveToTrashType = Enum.valueOf(Desktop.Action.class, "MOVE_TO_TRASH");
+
+                       // MOVE_TO_TRASHアクションがサポートされているか?
+                       Desktop desktop = Desktop.getDesktop();
+                       if (!desktop.isSupported(moveToTrashType)) {
+                               return null;
+                       }
+
+                       // moveToTrashメソッドを取得する。(定義されていなければ実行時例外)
+                       return Desktop.class.getMethod("moveToTrash", File.class);
+
+               } catch (Exception ex) {
+                       return null;
+               }
+       }
+
+       /**
+        * ゴミ箱が有効であるか?
+        * @return 有効であればtrue
+        */
+       public static boolean isSupportMoveToTrash() {
+               return methodMoveToTrash != null;
+       }
+
+       /**
+        * ゴミ箱にファイルまたはディレクトリを捨てる。
+        *
+        * @param file
+        * @throws IOException 削除に失敗した場合
+        */
+       public static void moveToTrash(File file) throws IOException {
+               if (file == null) {
+                       throw new NullPointerException();
+               }
+
+               if (!isSupportMoveToTrash()) {
+                       throw new UnsupportedOperationException("moveToTrash is not supported.");
+               }
+
+               if (!file.exists()) {
+                       return;
+               }
+
+               try {
+                       Desktop desktop = Desktop.getDesktop();
+                       Boolean ret = (Boolean) methodMoveToTrash.invoke(desktop, file);
+                       if (ret == null || !ret) {
+                               throw new IOException("failed to move to recyclebin. " + file);
+                       }
+
+               } catch (IllegalAccessException ex) {
+                       throw new RuntimeException(ex);
+
+               } catch (InvocationTargetException ex) {
+                       Throwable cause = ex.getCause();
+                       throw new IOException("failed to move to recyclebin. " + file, cause);
+               }
+       }
+
+       /**
+        * 指定したファイルまたはディレクトリを削除します.<br>
+        * 指定した引数がディレクトリを示す場合、このディレクトリを含む配下のすべてのファイルとディレクトリを削除します.<br>
+        *
+        * @param file
+        *            ファイル、またはディレクトリ
+        * @throws IOException
+        *             削除できない場合
+        */
+       public static void deleteRecursive(File file) throws IOException {
+               if (file == null) {
+                       throw new IllegalArgumentException();
+               }
+               if (!file.exists()) {
+                       return;
+               }
+               if (file.isDirectory()) {
+                       File[] children = file.listFiles();
+                       if (children != null) {
+                               for (File child : children) {
+                                       deleteRecursive(child);
+                               }
+                       }
+               }
+               if (!file.delete()) {
+                       throw new IOException("can't delete file. " + file);
+               }
+       }
+
+       /**
+        * ファイルまたはフォルダを削除します。
+        * @param file
+        * @throws IOException
+        */
+       public static void delete(File file) throws IOException {
+               AppConfig appConfig = AppConfig.getInstance();
+               if (isSupportMoveToTrash() && appConfig.isUseRecycleBinIfSupported()) {
+                       moveToTrash(file);
+               } else {
+                       deleteRecursive(file);
+               }
+       }
+}
index d679bf9..dc757e5 100644 (file)
@@ -13,43 +13,60 @@ import java.util.Properties;
  * 順番に読み込んで重ね合わせる.<br>
  * 一度読み込んだものはキャッシュに保存され次回以降は、それが用いられる.<br>
  */
-public class LocalizedResourcePropertyLoader extends ResourceLoader {
-       
+public class LocalizedResourcePropertyLoader {
+
        /**
         * プロパティファイル群と、それに対するキャッシュ
         */
        private Map<ResourceNames, Properties> propCache;
-       
+
        /**
         * キャッシュを共有するシングルトンインスタンス.
         */
        private static final LocalizedResourcePropertyLoader inst = new LocalizedResourcePropertyLoader(
                        new HashMap<ResourceNames, Properties>());
-       
+
+       /**
+        * キャッシュしないローカライズされた読み込み順序をもつプロパティローダー
+        */
+       protected LocalizedResourcePropertyLoader() {
+               this.propCache = null;
+       }
+
        /**
         * 独立したキャッシュを指定することのできるコンストラクタ.<br>
-        * 
+        *
         * @param propCache
         *            キャッシュ、不要であればnull可
         */
-       public LocalizedResourcePropertyLoader(
-                       Map<ResourceNames, Properties> propCache) {
+       protected LocalizedResourcePropertyLoader(Map<ResourceNames, Properties> propCache) {
+               if (propCache == null) {
+                       throw new NullPointerException("propCache is required.");
+               }
                this.propCache = propCache;
        }
-       
+
        /**
-        * インスタンスを取得する
-        * 
+        * ã\83\97ã\83­ã\83\91ã\83\86ã\82£ã\82\92ã\82­ã\83£ã\83\83ã\82·ã\83¥ã\81\99ã\82\8bã\82¤ã\83³ã\82¹ã\82¿ã\83³ã\82¹ã\82\92å\8f\96å¾\97ã\81\99ã\82\8b
+        *
         * @return インスタンス
         */
        public static LocalizedResourcePropertyLoader getCachedInstance() {
                return inst;
        }
-       
+
+       /**
+        * プロパティをキャッシュしないインスタンスを取得する。
+        * @return
+        */
+       public static LocalizedResourcePropertyLoader getNonCachedInstance() {
+               return new LocalizedResourcePropertyLoader();
+       }
+
        /**
         * リソース名を指定してデフォルトのロケールでローカライズされたリソースプロパティを読み込む.<br>
         * リソースはxml形式である。リソース名には.xmlを付与しない.(自動的に内部で付与される.)
-        * 
+        *
         * @param name
         *            リソース名
         * @return プロパティ
@@ -57,11 +74,11 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
        public Properties getLocalizedProperties(String name) {
                return getLocalizedProperties(name, null);
        }
-       
+
        /**
         * リソース名を指定して指定したロケールでローカライズされたリソースプロパティを読み込む.<br>
         * リソースはxml形式である。リソース名には.xmlを付与しない.(自動的に内部で付与される.)
-        * 
+        *
         * @param name
         *            リソース名
         * @param locale
@@ -69,7 +86,27 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
         * @return プロパティ
         */
        public Properties getLocalizedProperties(String name, Locale locale) {
-               return getProperties(getResourceNames(name, locale));
+               return getLocalizedProperties(name, locale, false);
+       }
+
+       /**
+        * リソース名を指定して指定したロケールでローカライズされたリソースプロパティを読み込む.<br>
+        * リソースはxml形式である。リソース名には.xmlを付与しない.(自動的に内部で付与される.)
+        *
+        * @param name
+        *            リソース名
+        * @param locale
+        *            ロケール、nullの場合はデフォルトのロケール
+        * @param preferredNatural
+        *            リソースの適用優先度を逆順にして、Naturalを優先させる場合はtrue
+        * @return プロパティ
+        */
+       public Properties getLocalizedProperties(String name, Locale locale, boolean preferredNatural) {
+               ResourceNames resNames = getResourceNames(name, locale);
+               if (preferredNatural) {
+                       resNames = resNames.reverse();
+               }
+               return getProperties(resNames);
        }
 
        /**
@@ -77,14 +114,14 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
         * リソースはxml形式である。リソース名には.xmlを付与しない.(自動的に内部で付与される.)<br>
         * 返される順序は、読み込み順となる。(順番に読み込んで上書きしてゆくことを想定する).<br>
         * ロケール中立のものが先頭となり、指定したロケールにもっとも一致するものが最後となる.<br>
-        * 
+        *
         * @param name
         *            リソース名
         * @param locale
         *            ロケール、nullの場合はデフォルトのロケール
         * @return プロパティリソースの一覧(読み込み順)
         */
-       public static ResourceNames getResourceNames(String name, Locale locale) {
+       protected static ResourceNames getResourceNames(String name, Locale locale) {
                if (name == null || name.length() == 0) {
                        throw new IllegalArgumentException();
                }
@@ -95,7 +132,7 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
                String language = locale.getLanguage();
                String country = locale.getCountry();
                String variant = locale.getVariant();
-               
+
                String[] resourceNames = {
                        name + ".xml",
                        name + "_" + language + ".xml",
@@ -110,7 +147,7 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
         * キャッシュされていない場合はプロパティをロードして、それをキャッシュに格納する.<br>
         * (共有キャッシュ時、もしくは独自のキャッシュが指定されている場合).<br>
         * リソースが一つも存在しない場合は実行時例外を発生させる.<br>
-        * 
+        *
         * @param resourceNames
         *            リソース名群
         * @return プロパティ
@@ -139,8 +176,9 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
 
        /**
         * リソース名群からリソースプロパティをロードして返す.<br>
+        * リソースはクラスパス上のリソースを探索した結果に、ローカルディレクトリ上の探索結果を上書きしたものが返される。
         * 一つも存在しない場合はnullを返す.<br>
-        * 
+        *
         * @param resourceNames
         *            リソース群名
         * @return プロパティ
@@ -154,10 +192,10 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
                // バージョンアップによりキーが増えて、既存のローカルファイル上のプロパティファイルにキーが存在しない場合でも
                // 安全なようにするためのもの。
                ClassLoader[] loaders = new ClassLoader[] {
-                               getDefaultClassLoader(),
-                               getLocalizedClassLoader(null),
+                               ResourceLoader.getDefaultClassLoader(), // クラスパス上のクラスローダー
+                               ResourceLoader.getUsersResourceDirClassLoader(null, true), // ローカル用クラスローダ(親指定なし)
                                };
-               
+
                boolean foundResource = false;
                Properties props = new Properties();
                for (ClassLoader loader : loaders) {
@@ -179,12 +217,12 @@ public class LocalizedResourcePropertyLoader extends ResourceLoader {
                                                throw new RuntimeException("resource loading error." + resource, ex);
                                        }
                                        foundResource = true;
-                                       
+
                                        props.putAll(org);
                                }
                        }
                }
-               
+
                if (foundResource) {
                        return props;
                }
index 515d157..dc86434 100644 (file)
@@ -7,34 +7,37 @@ import java.util.Locale;
 
 /**
  * リソースからローカライズされたテキストを取得する.<br>
- * 
+ *
  * @author seraphy
- * 
+ *
  */
-public class LocalizedResourceTextLoader extends ResourceLoader {
+@Deprecated
+public class LocalizedResourceTextLoader {
 
        private static final LocalizedResourceTextLoader inst = new LocalizedResourceTextLoader();
-       
+
+       private final ResourceLoader resourceLoader = new ResourceLoader();
+
        private LocalizedTextResource textResource = new LocalizedTextResource() {
                @Override
                protected URL getResource(String resourceName) {
-                       return LocalizedResourceTextLoader.this.getResource(resourceName);
+                       return resourceLoader.getResource(resourceName);
                }
        };
 
        private LocalizedResourceTextLoader() {
                super();
        }
-       
+
        public static LocalizedResourceTextLoader getInstance() {
                return inst;
        }
-       
+
        /**
         * リソース名を指定して、テキストファイルを読み込んで、その文字列を返す.<br>
         * リソースは現在のデフォルトロケールを優先で検索されます.<br>
         * ファイルエンコーディングを引数csで指定する.<br>
-        * 
+        *
         * @param name
         *            リソース名
         * @param cs
index ddf20a1..b7a672f 100644 (file)
@@ -8,32 +8,75 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
 
-
 /**
- * リソースをロードするための抽象基底クラス.
- * 
+ * リソースをロードするためのクラス.
+ *
  * @author seraphy
  */
 public class ResourceLoader {
 
+       private final ClassLoader classLoader;
+
+       /**
+        * ローカル優先でリソースを探索するリソースローダーを構築します。
+        */
+       public ResourceLoader() {
+               this(true);
+       }
+
+       /**
+        * ローカル優先か、クラス優先のいずれかを指定してリソースローダーを構築します。
+        * @param preferredLocal
+        */
+       public ResourceLoader(boolean preferredLocal) {
+               this(getUsersResourceDirClassLoader(getDefaultClassLoader(), preferredLocal));
+       }
+
+       /**
+        * クラスローダーを指定してリソースローダーを構築します。
+        * @param classLoader
+        */
+       public ResourceLoader(ClassLoader classLoader) {
+               if (classLoader == null) {
+                       throw new IllegalArgumentException("classLoader is required.");
+               }
+               this.classLoader = classLoader;
+       }
+
        /**
         * クラスローダを取得する.<br>
         * まずローカルファイル上のリソースディレクトリがあれば、それを検索する.<br>
         * つぎにスレッドに関連づけられているコンテキストクラスローダか、もしなければ、このクラスをロードしたクラスローダを用いて検索する.<br>
-        * 
+        *
         * @return クラスローダ
         */
        public ClassLoader getClassLoader() {
-               return getLocalizedClassLoader(getDefaultClassLoader());
+               return classLoader;
        }
-       
+
+       /**
+        * クラスローダによりリソースをロードする.<br>
+        * 該当するリソースが存在しない場合はnullを返す.<br>
+        * リソース名がnullの場合もnullを返す.<br>
+        *
+        * @param name
+        *            リソース名またはnull
+        * @return リソースがあれば、そのURL。なければnull
+        */
+       public URL getResource(String name) {
+               if (name == null) {
+                       return null;
+               }
+               return getClassLoader().getResource(name);
+       }
+
        /**
         * クラスローダを取得する.<br>
         * スレッドに関連づけられているコンテキストクラスローダか、もしなければ、このクラスをロードしたクラスローダを返す.<br>
-        * 
+        *
         * @return クラスローダ
         */
-       public ClassLoader getDefaultClassLoader() {
+       public static ClassLoader getDefaultClassLoader() {
                return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                        public ClassLoader run() {
                                ClassLoader cl = Thread.currentThread().getContextClassLoader();
@@ -44,17 +87,20 @@ public class ResourceLoader {
                        }
                });
        }
-       
+
        /**
-        * ローカルファイル上のリソースディレクトリにアクセスするクラスローダ取得する.<br>
+        * ã\83¦ã\83¼ã\82¶ã\83¼ç\94¨ã\81®ã\83­ã\83¼ã\82«ã\83«ã\83\95ã\82¡ã\82¤ã\83«ä¸\8aã\81®ã\83ªã\82½ã\83¼ã\82¹ã\83\87ã\82£ã\83¬ã\82¯ã\83\88ã\83ªã\81«ã\82¢ã\82¯ã\82»ã\82¹ã\81\99ã\82\8bã\82¯ã\83©ã\82¹ã\83­ã\83¼ã\83\80å\8f\96å¾\97ã\81\99ã\82\8b.<br>
         * 作成されていなければparentをそのまま返す.<br>
-        * リソースはローカルファイル上のパスで検索されたのちにparentで検索されます.(標準のURLClassLoaderとは違う探索方法)<br>
-        * 
+        * ローカル優先の場合、リソースはローカルファイル上のパスで検索されたのちにparentで検索されます.(標準のURLClassLoaderとは違う探索方法)<br>
+        * ローカル優先ではない場合は通常どおり、親クラスローダを優先して検索されます.<br>
+        *
         * @param parent
         *            親クラスローダ、nullの場合は親の探索をしない.
+        * @param preferredLocal
+        *            ローカル優先か?
         * @return ローカルシステム上のリソースディレクトリにアクセスするクラスローダ、なければparentのまま
         */
-       public ClassLoader getLocalizedClassLoader(final ClassLoader parent) {
+       public static ClassLoader getUsersResourceDirClassLoader(final ClassLoader parent, final boolean preferredLocal) {
                try {
                        File baseDir = ConfigurationDirUtilities.getUserDataDir();
                        SetupLocalization localize = new SetupLocalization(baseDir);
@@ -65,19 +111,25 @@ public class ResourceLoader {
                        URLClassLoader cl = AccessController.doPrivileged(new PrivilegedExceptionAction<URLClassLoader>() {
                                public URLClassLoader run() throws MalformedURLException {
                                        URL[] urls = new URL[] { resourceDir.toURI().toURL() };
-                                       return new URLClassLoader(urls, parent) {
-                                               @Override
-                                               public URL getResource(String name) {
-                                                                       URL url = findResource(name); // 子が優先 (標準と逆)
-                                                       if (url == null) {
-                                                               ClassLoader parent = getParent();
-                                                               if (parent != null) {
-                                                                       url = parent.getResource(name);
+                                       if (preferredLocal) {
+                                               // リソースの探索順序をローカル優先にするURLクラスローダ
+                                               return new URLClassLoader(urls, parent) {
+                                                       @Override
+                                                       public URL getResource(String name) {
+                                                               URL url = findResource(name); // 子が優先 (標準と逆)
+                                                               if (url == null) {
+                                                                       ClassLoader parent = getParent();
+                                                                       if (parent != null) {
+                                                                               url = parent.getResource(name);
+                                                                       }
                                                                }
+                                                               return url;
                                                        }
-                                                       return url;
-                                               }
-                                       };
+                                               };
+                                       } else {
+                                               // リソースの探索順序を親優先(標準)にするURLクラスローダー
+                                               return new URLClassLoader(urls, parent);
+                                       }
                                }
                        });
                        return cl;
@@ -87,20 +139,4 @@ public class ResourceLoader {
                        return null;
                }
        }
-
-       /**
-        * クラスローダによりリソースをロードする.<br>
-        * 該当するリソースが存在しない場合はnullを返す.<br>
-        * リソース名がnullの場合もnullを返す.<br>
-        * 
-        * @param name
-        *            リソース名またはnull
-        * @return リソースがあれば、そのURL。なければnull
-        */
-       public URL getResource(String name) {
-               if (name == null) {
-                       return null;
-               }
-               return getClassLoader().getResource(name);
-       }
 }
index ad48d0e..541b71c 100644 (file)
@@ -5,23 +5,23 @@ import java.util.Arrays;
 
 /**
  * 関連もしくは類似するリソースをまとめて取り扱うためにグループ化するためのクラス.<br>
- * 
+ *
  * @author seraphy
  */
 public class ResourceNames extends AbstractList<String> {
-       
+
        private final String[] resourceNames;
-       
+
        public ResourceNames(String[] resourceNames) {
                if (resourceNames == null) {
                        throw new IllegalArgumentException();
                }
                this.resourceNames = resourceNames;
        }
-       
+
        /**
         * 順次を逆転させた新しいインスタンスを返す
-        * 
+        *
         * @return 順序を逆転させたインスタンス
         */
        public ResourceNames reverse() {
@@ -37,7 +37,7 @@ public class ResourceNames extends AbstractList<String> {
        public int hashCode() {
                return Arrays.hashCode(resourceNames);
        }
-       
+
        @Override
        public boolean equals(Object obj) {
                if (obj == this) {
@@ -49,12 +49,12 @@ public class ResourceNames extends AbstractList<String> {
                }
                return false;
        }
-       
+
        @Override
        public int size() {
                return resourceNames.length;
        }
-       
+
        @Override
        public String get(int index) {
                return resourceNames[index];
index 4c6000c..620a56b 100644 (file)
@@ -1,6 +1,7 @@
 package charactermanaj.util;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -20,13 +21,13 @@ import java.util.logging.Logger;
 
 /**
  * 言語リソースを管理する.
- * 
+ *
  * @author seraphy
  */
-public class SetupLocalization extends ResourceLoader {
-       
+public class SetupLocalization {
+
        private final Logger logger = Logger.getLogger(getClass().getName());
-       
+
        public static final String DIRNAME_RESOURCES = "resources";
 
        /**
@@ -52,10 +53,10 @@ public class SetupLocalization extends ResourceLoader {
        }
 
        private File baseDir;
-       
+
        /**
         * アプリケーションデータ用ディレクトリを指定して構築する.
-        * 
+        *
         * @param baseDir
         *            データディレクトリ
         */
@@ -65,10 +66,10 @@ public class SetupLocalization extends ResourceLoader {
                }
                this.baseDir = baseDir;
        }
-       
+
        /**
         * コピー対象とするリソース一覧を取得する.<br>
-        * 
+        *
         * @param resourceSet
         *            リソースディレクトリのサブディレクトリ名のリスト
         * @return リソース一覧(言語関連リソース、テンプレートなど)
@@ -143,10 +144,10 @@ public class SetupLocalization extends ResourceLoader {
                logger.log(Level.FINE, "resource list: " +resources);
                return resources;
        }
-       
+
        /**
         * リソースをファイルにコピーする.<br>
-        * 
+        *
         * @param fromURL
         * @param toFile
         * @throws IOException
@@ -184,10 +185,10 @@ public class SetupLocalization extends ResourceLoader {
                        is.close();
                }
        }
-       
+
        /**
         * リソースディレクトリを返す.
-        * 
+        *
         * @return リソースディレクトリ
         */
        public File getResourceDir() {
@@ -197,28 +198,33 @@ public class SetupLocalization extends ResourceLoader {
                        throw new RuntimeException(ex);
                }
        }
-       
+
        /**
         * ローカルシステム上のアプリケーションデータディレクトリに言語リソースをコピーする.
-        * 
+        *
         * @param resourceSet
         *            コピーするリソースセット.
         * @param overwrite
         *            上書きを許可する場合はtrue、スキップする場合はfalse
+        * @param filter
+        *             ファイルの出力先パスを得てコピーの有無を判断するフィルタ、nullの場合はすべて許可する
         * @throws IOException
         *             失敗
         */
-       public void setupToLocal(EnumSet<Resources> resourceSet, boolean overwrite)
+       public void setupToLocal(EnumSet<Resources> resourceSet, boolean overwrite, FileFilter filter)
                        throws IOException {
                File toDir = getResourceDir();
-               ClassLoader cl = getDefaultClassLoader();
+               ClassLoader cl = ResourceLoader.getDefaultClassLoader();
                for (String resourceName : getResourceList(resourceSet)) {
                        URL url = cl.getResource(resourceName);
                        if (url != null) {
                                File toFile = new File(toDir, resourceName).getCanonicalFile();
                                if (overwrite || !toFile.exists()) {
-                                       // 上書き許可か、まだファイルが存在しなければコピーする.
-                                       copyResource(url, toFile);
+                                       // 上書き許可か、まだファイルが存在しなければ
+                                       if (filter == null || filter.accept(toFile)) {
+                                               // フィルタが指定されていないか、フィルタによって許可された場合はコピーする
+                                               copyResource(url, toFile);
+                                       }
                                }
 
                        } else {
@@ -226,4 +232,18 @@ public class SetupLocalization extends ResourceLoader {
                        }
                }
        }
+
+       /**
+        * ローカルシステム上のアプリケーションデータディレクトリに言語リソースをコピーする.
+        *
+        * @param resourceSet
+        *            コピーするリソースセット.
+        * @param overwrite
+        *            上書きを許可する場合はtrue、スキップする場合はfalse
+        * @throws IOException
+        *             失敗
+        */
+       public void setupToLocal(EnumSet<Resources> resourceSet, boolean overwrite) throws IOException {
+               setupToLocal(resourceSet, overwrite, null);
+       }
 }
index 24c63f7..3c858ad 100644 (file)
@@ -17,18 +17,23 @@ import javax.swing.JButton;
 import javax.swing.JMenu;
 
 
-public final class UIHelper extends ResourceLoader {
+public final class UIHelper {
 
        private static final UIHelper singleton = new UIHelper();
-       
+
+       /**
+        * クラスパスからのみ探索するリソースローダー
+        */
+       private final ResourceLoader resourceLoader = new ResourceLoader(ResourceLoader.getDefaultClassLoader());
+
        private UIHelper() {
                super();
        }
-       
+
        public static final UIHelper getInstance() {
                return singleton;
        }
-       
+
        /**
         * 指定したコンテナに含まれる指定したコンポーネント型のすべてのコンポーネントを返す.<br>
         * 一つも該当するものがなければ空を返す
@@ -46,7 +51,7 @@ public final class UIHelper extends ResourceLoader {
                getDescendantOfClass(clz, container, components);
                return (Collection<T>) components;
        }
-       
+
        private void getDescendantOfClass(Class<?> clz, Container container, Collection<Component> results) {
                if (container == null) {
                        return;
@@ -63,7 +68,7 @@ public final class UIHelper extends ResourceLoader {
                        }
                }
        }
-       
+
        /**
         * 2つのステートをもつアイコンを作成します.<br>
         * このアイコンは、使用するコンポーネントがAbstractButton派生クラスであれば、isSelectedの結果が
@@ -81,7 +86,7 @@ public final class UIHelper extends ResourceLoader {
                }
                final BufferedImage pinIcon1 = getImage(iconName1);
                final BufferedImage pinIcon2 = getImage(iconName2);
-               
+
                Icon icon = new Icon() {
                        public void paintIcon(Component c, Graphics g, int x, int y) {
                                boolean selected = false;
@@ -106,7 +111,7 @@ public final class UIHelper extends ResourceLoader {
                                return pinIcon1.getWidth();
                        }
                };
-               
+
                return icon;
        }
 
@@ -122,10 +127,10 @@ public final class UIHelper extends ResourceLoader {
                }
                JButton btn = new JButton();
                btn.setIcon(new ImageIcon(getImage(iconName)));
-               
+
                return btn;
        }
-       
+
        /**
         * 通常時の画像のみをもつ透過ボタンを作成して返す.<br>
         * リソースが取得できない場合は実行時例外が返される.<br>
@@ -163,7 +168,7 @@ public final class UIHelper extends ResourceLoader {
 
                return btn;
        }
-       
+
        /**
         * リソースから画像を取得する.<br>
         * 画像が取得できない場合は実行時例外を返す.<br>
@@ -171,7 +176,7 @@ public final class UIHelper extends ResourceLoader {
         * @return 画像
         */
        public BufferedImage getImage(String name) {
-               URL url = getResource(name);
+               URL url = resourceLoader.getResource(name);
                if (url == null) {
                        throw new RuntimeException("resource not found. " + name);
                }
@@ -182,5 +187,5 @@ public final class UIHelper extends ResourceLoader {
                        throw new RuntimeException("image load error." + ex.getMessage(), ex);
                }
        }
-               
+
 }
index bbb4f14..d6c0a3d 100644 (file)
@@ -56,6 +56,7 @@ If the file already exists, the file is overwritten.]]></entry>
 <entry key="noRemoveLog">34;No Remove Log</entry>
 <entry key="purgeLogDays">35;a number in days until purge log</entry>
 <entry key="informationDialogOpenMethod">36;Information Dialog Open Mode</entry>
+<entry key="useRecycleBinIfSupported">37;Use the recycle bin if it is supported.</entry>
 
 <entry key="selectedItemBgColor">50;Selected Item's Background Color</entry>
 <entry key="exportPresetWarningsForegroundColor">51;Export Preset's Warnings Foreground Color</entry>
index 035e97f..fd68865 100644 (file)
@@ -56,6 +56,7 @@
 <entry key="noRemoveLog">34;正常時でもログを終了時に消去しない.</entry>
 <entry key="purgeLogDays">35;起動時に古いログを消去するまでの日数。(0の場合は削除しない)</entry>
 <entry key="informationDialogOpenMethod">36;情報ダイアログのアクションを「開く」にする。(false時は「編集」)</entry>
+<entry key="useRecycleBinIfSupported">37;ファイルの削除にゴミ箱を使う。(サポートされている場合)</entry>
 
 <entry key="selectedItemBgColor">50;アイテム選択行(フォーカス行)の背景色</entry>
 <entry key="exportPresetWarningsForegroundColor">51;パーツセットのエクスポート時の警告色</entry>
index 5b91eb7..c3c233b 100644 (file)
@@ -1,7 +1,6 @@
 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
 <properties>
 <comment>appConfig.xml</comment>
-<!--  だれか翻訳助けて Could someone please translate this.-->
 
 <entry key="title">程序设置</entry>
 <entry key="btn.apply">应用</entry>
@@ -16,7 +15,7 @@
 <entry key="error.caption">错误</entry>
 <entry key="error.message">请填充未填项</entry>
 <entry key="caution">应用新设置需要重新启动程序</entry>
-<entry key="table.caption">设置/entry>
+<entry key="table.caption">设置</entry>
 
 <entry key="btn.setupLocalization">自定义语言</entry>
 <entry key="setupLocalization"><![CDATA[解压语言文件
@@ -56,6 +55,7 @@
 <entry key="noRemoveLog">34;退出时不清除log</entry>
 <entry key="purgeLogDays">35;启动时清除多少天以上的log(0表示不清除)</entry>
 <entry key="informationDialogOpenMethod">36;信息栏动作(true为打开;false为编辑)</entry>
+<entry key="useRecycleBinIfSupported">37;使用回收站(如果支持)</entry>
 
 <entry key="selectedItemBgColor">50;选择时的背景色</entry>
 <entry key="exportPresetWarningsForegroundColor">51;警告时的背景色</entry>
index a8e0d80..ae0e3fe 100644 (file)
@@ -1,6 +1,6 @@
 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
 <properties version="1.0">
-       <entry key="title">CharacterManaJ - </entry>
+       <entry key="title">CharacterManaJ</entry>
        <entry key="defaultPartsSetTitle">no name</entry>
        <entry key="help.url">http://charactermanaj.sourceforge.jp/help/0.9/</entry>
        <entry key="help.show">Help Document is here.</entry>
index 84299b2..6969f7d 100644 (file)
@@ -1,6 +1,6 @@
 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
 <properties version="1.0">
-       <entry key="title">キャラクターなんとかJ - </entry>
+       <entry key="title">キャラクターなんとかJ</entry>
        <entry key="defaultPartsSetTitle">無題</entry>
        <entry key="help.url">http://charactermanaj.sourceforge.jp/help/0.9/</entry>
        <entry key="help.show">ヘルプドキュメントは以下のURLにあります。</entry>
index eb81aee..e0b28c0 100644 (file)
@@ -1,6 +1,6 @@
 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
 <properties version="1.0">
-       <entry key="title">CharacterManaJ - </entry>
+       <entry key="title">CharacterManaJ</entry>
        <entry key="defaultPartsSetTitle">无标题</entry>
        <entry key="help.url">http://charactermanaj.sourceforge.jp/help/0.9/</entry>
        <entry key="help.show">帮助文档(日)</entry>
index 75da77a..1fa6d2c 100644 (file)
@@ -35,6 +35,7 @@
        <entry key="layers.moveup.caption">Up</entry>
        <entry key="layers.movedown.caption">Down</entry>
        <entry key="layers.watchdir">Watch directories</entry>
+       <entry key="layers.enableCustomLayer">Enable the Custom layer pattern</entry>
        <entry key="panel.basicinfomation">Basic</entry>
        <entry key="panel.colorgroup">Color Group</entry>
        <entry key="panel.categories">Categories</entry>
index 2826d85..4cf5ff5 100644 (file)
@@ -35,6 +35,7 @@
        <entry key="layers.moveup.caption">上へ</entry>
        <entry key="layers.movedown.caption">下へ</entry>
        <entry key="layers.watchdir">フォルダを監視し、パーツ画像の変更を検知できるようにする。</entry>
+       <entry key="layers.enableCustomLayer">カスタムレイヤーを有効にする</entry>
        <entry key="panel.basicinfomation">基本</entry>
        <entry key="panel.colorgroup">カラーグループ</entry>
        <entry key="panel.categories">カテゴリ</entry>
index b660cc8..65352bc 100644 (file)
@@ -35,6 +35,7 @@
        <entry key="layers.moveup.caption">向上</entry>
        <entry key="layers.movedown.caption">向下</entry>
        <entry key="layers.watchdir">显示目录</entry>
+       <entry key="layers.enableCustomLayer">启用自定义图层模式</entry>
        <entry key="panel.basicinfomation">基本设置</entry>
        <entry key="panel.colorgroup">颜色组</entry>
        <entry key="panel.categories">分类</entry>
index 27c5cf0..42bec6c 100644 (file)
@@ -50,6 +50,7 @@
        <entry key="profile.column.location.width">300</entry>
 
        <entry key="inputTemplateName">Template Name</entry>
+       <entry key="template.blank">Blank</entry>
        <entry key="confirm">Confirm</entry>
        <entry key="confirmOverwrite"><![CDATA[file already exists.
 Do you want to replace it?]]></entry>
index 6b90ff6..a87723d 100644 (file)
@@ -33,7 +33,7 @@
        <entry key="confirmUpdateProfile">プロファイルの選択</entry>
        <entry key="importToUpdateProfile">選択されたプロファイルへのインポートを行う。</entry>
        <entry key="importToCreateProfile">新規にプロファイルを作成してインポートを行う。</entry>
-       
+
        <entry key="profile.column.name">名前</entry>
        <entry key="profile.column.id">ID</entry>
        <entry key="profile.column.revision">リビジョン</entry>
@@ -50,6 +50,7 @@
        <entry key="profile.column.location.width">300</entry>
 
        <entry key="inputTemplateName">テンプレートの名前</entry>
+       <entry key="template.blank">空</entry>
        <entry key="confirm">確認</entry>
        <entry key="confirmOverwrite"><![CDATA[ファイルは既に存在します。
 上書きしてもよろしいですか?]]></entry>
index f96b101..4474df1 100644 (file)
@@ -50,6 +50,7 @@
        <entry key="profile.column.location.width">300</entry>
 
        <entry key="inputTemplateName">缓存名称</entry>
+       <entry key="template.blank">空白</entry>
        <entry key="confirm">确认</entry>
        <entry key="confirmOverwrite"><![CDATA[文件已存在
 是否覆盖?]]></entry>