From: seraphy Date: Sun, 20 Feb 2011 18:11:54 +0000 (+0000) Subject: ver0.95リリース 2011/02/21 X-Git-Tag: ver0.95 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=refs%2Ftags%2Fver0.95;p=charactermanaj%2FCharacterManaJ.git ver0.95リリース 2011/02/21 git-svn-id: https://svn.sourceforge.jp/svnroot/charactermanaj/tags/ver0_95@9 5b6e9025-a2e8-4882-b233-f889982098c5 --- diff --git a/.fbprefs b/.fbprefs new file mode 100644 index 0000000..69980f7 --- /dev/null +++ b/.fbprefs @@ -0,0 +1,127 @@ +#FindBugs User Preferences +#Sun Feb 20 00:21:29 JST 2011 +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true +detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUseObjectEquals=UseObjectEquals|false +detectorUselessSubclassMethod=UselessSubclassMethod|false +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=2 +effort=default +filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false +filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL| +run_at_full_build=false diff --git a/CharacterManaJ.app/Contents/Resources/Java/CharacterManaJ.jar b/CharacterManaJ.app/Contents/Resources/Java/CharacterManaJ.jar index 0b6bf22..ba29a7a 100644 Binary files a/CharacterManaJ.app/Contents/Resources/Java/CharacterManaJ.jar and b/CharacterManaJ.app/Contents/Resources/Java/CharacterManaJ.jar differ diff --git a/CharacterManaJ.jar b/CharacterManaJ.jar index bdf9143..ba29a7a 100644 Binary files a/CharacterManaJ.jar and b/CharacterManaJ.jar differ diff --git a/charactermanaj.exe b/charactermanaj.exe index 52b889c..d58a9ff 100644 Binary files a/charactermanaj.exe and b/charactermanaj.exe differ diff --git a/resources/appinfo/about.html b/resources/appinfo/about.html index 87b206e..5dc43d7 100644 --- a/resources/appinfo/about.html +++ b/resources/appinfo/about.html @@ -69,7 +69,7 @@ h2 { 本ソフトウェアの使用に起因する一切の直接損害、間接損害、偶発的損害、特別損害、懲戒的損害、派生的損害について作者(seraphy)および再配布者、共同開発者は一切の責任を負わないものとします。

このアプリケーションのプロジェクトは、SourceForgeにてオープンソースとして管理しております。

http://sourceforge.jp/projects/charactermanaj/より最新バージョンおよびソースコードを取得することができます。

-

The Apache License Version 2.0に従い、だれでも自由に利用でき、且つ、改変することができます。

+

The Apache License Version 2.0に従い、だれでも自由に利用でき、且つ、改変することができます。

このアプリケーション自身(javaコード)はseraphyによって書かれました。
人的リソースに限りがあるため、メンテナンスや改善に時間がかかることが予想されます。
このアプリケーションをより良くしようという有志がおられましたら、ぜひ、ご協力・ご連絡くださいますようお願いいたします。歓迎いたします。

diff --git a/resources/appinfo/about_ja.html b/resources/appinfo/about_ja.html index 9c3c9ec..24c7dc6 100644 --- a/resources/appinfo/about_ja.html +++ b/resources/appinfo/about_ja.html @@ -32,7 +32,7 @@ h2 { 本ソフトウェアの使用に起因する一切の直接損害、間接損害、偶発的損害、特別損害、懲戒的損害、派生的損害について作者(seraphy)および再配布者、共同開発者は一切の責任を負わないものとします。

このアプリケーションのプロジェクトは、SourceForgeにてオープンソースとして管理しております。

http://sourceforge.jp/projects/charactermanaj/より最新バージョンおよびソースコードを取得することができます。

-

The Apache License Version 2.0に従い、だれでも自由に利用でき、且つ、改変することができます。

+

The Apache License Version 2.0に従い、だれでも自由に利用でき、且つ、改変することができます。

このアプリケーション自身(javaコード)はseraphyによって書かれました。
人的リソースに限りがあるため、メンテナンスや改善に時間がかかることが予想されます。
このアプリケーションをより良くしようという有志がおられましたら、ぜひ、ご協力・ご連絡くださいますようお願いいたします。歓迎いたします。

diff --git a/resources/strings/appconfigdialog.xml b/resources/strings/appconfigdialog.xml index 405b1d7..38c176a 100644 --- a/resources/strings/appconfigdialog.xml +++ b/resources/strings/appconfigdialog.xml @@ -17,8 +17,9 @@ Settings 01;Compression Quality -02;Zip File Encoding +02;ZIP File Encoding 03;Common Character Data Directory +04;The judgment pattern of a color group.('@' is color group name) 10;Preview Max-Width 11;Preview Max-Height diff --git a/resources/strings/appconfigdialog_ja.xml b/resources/strings/appconfigdialog_ja.xml index 1a72e45..5e03dca 100644 --- a/resources/strings/appconfigdialog_ja.xml +++ b/resources/strings/appconfigdialog_ja.xml @@ -19,6 +19,7 @@ 01;JPEG圧縮時のクオリティ(1が最大、0.1が最小) 02;ZIPファイルに格納されているファイル名のエンコーディング(csWindows31Jが標準) 03;全ユーザー用キャラクター定義ディレクトリ(空はデフォルト使用) +04;パーツ名からカラーグループを判定するパターン(正規表現)(@がカラーグループ名の場所になります.) 10;プレビューの初期表示の最大幅 11;プレビューの初期表示の最大高さ diff --git a/src/charactermanaj/Main.java b/src/charactermanaj/Main.java index f2642e4..65b52da 100644 --- a/src/charactermanaj/Main.java +++ b/src/charactermanaj/Main.java @@ -96,10 +96,9 @@ public final class Main { Method mtd = clz.getMethod("setupScreenMenu", MainFrame.class); mtd.invoke(null, mainFrame); } - - // メインウィンドウを表示. - // (Mainメソッドは、このあと終了するが、Swingはアクティブなウィンドウがいるかぎりアプリケーションを終了しない.) - mainFrame.setVisible(true); + + // 表示(および位置あわせ) + mainFrame.showMainFrame(); } catch (Throwable ex) { // なんらかの致命的な初期化エラーがあった場合、ログとコンソールに表示 @@ -109,7 +108,9 @@ public final class Main { logger.log(Level.SEVERE, "Application initiation failed.", ex); } ErrorMessageHelper.showErrorDialog(null, ex); - System.exit(1); + + // メインフレームを破棄します. + MainFrame.closeAllProfiles(); } } diff --git a/src/charactermanaj/model/AppConfig.java b/src/charactermanaj/model/AppConfig.java index 8afe40b..f7e975e 100644 --- a/src/charactermanaj/model/AppConfig.java +++ b/src/charactermanaj/model/AppConfig.java @@ -15,6 +15,7 @@ import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import charactermanaj.util.ApplicationLogHandler; import charactermanaj.util.BeanPropertiesUtilities; @@ -165,7 +166,10 @@ public final class AppConfig { // 無視する } } - } catch (Exception ex) { + + } catch (IOException ex) { + throw new RuntimeException("appConfig.xml loading failed.", ex); + } catch (RuntimeException ex) { throw new RuntimeException("appConfig.xml loading failed.", ex); } BeanPropertiesUtilities.loadFromProperties(this, config); @@ -696,4 +700,16 @@ public final class AppConfig { private long purgeLogDays = 10; + public String getPartsColorGroupPattern() { + return partsColorGroupPattern; + } + + public void setPartsColorGroupPattern(String pattern) { + if (pattern != null && pattern.trim().length() > 0) { + Pattern.compile(pattern); + } + partsColorGroupPattern = pattern; + } + + private String partsColorGroupPattern = "^.*\\(@\\).*$"; } diff --git a/src/charactermanaj/model/CharacterData.java b/src/charactermanaj/model/CharacterData.java index e6c47fb..46ddd1a 100644 --- a/src/charactermanaj/model/CharacterData.java +++ b/src/charactermanaj/model/CharacterData.java @@ -209,34 +209,23 @@ public class CharacterData implements Serializable, PartsSpecResolver { } /** - * キャラクターデータが同じ構造であるか? - * サイズ、カラーグループ、カテゴリ、レイヤーの各情報が等しければtrue、それ以外はfalse.
+ * キャラクターデータが同じ構造であるか?
+ * カラーグループ、カテゴリ、レイヤーの各情報が等しければtrue、それ以外はfalse.
* 上記以外の項目(コメントや作者、プリセット等)については判定しない.
- * カラーグループの表示名や順序、カテゴリの順序や表示名、複数アイテム可などの違いは構造の変更とみなさない.
+ * サイズ、カラーグループの表示名や順序、カテゴリの順序や表示名、 + * 複数アイテム可などの違いは構造の変更とみなさない.
* レイヤーはレイヤーID、重ね合わせ順、対象ディレクトリの3点が変更されている場合は構造の変更とみなす.
* いずれも個数そのものが変わっている場合は変更とみなす.
* 自分または相手がValidでなければ常にfalseを返す.
- * @param other 比較対象 + * @param other 比較対象, null可 * @return 同じ構造であればtrue、そうでなければfalse */ public boolean isSameStructure(CharacterData other) { - if (other == null) { - throw new IllegalArgumentException(); - } - if (!this.isValid() || !other.isValid()) { + if (!this.isValid() || other == null || !other.isValid()) { // 自分または相手がinvalidであれば構造的には常に不一致と見なす. return false; } - // サイズが等しいか? - if (imageSize == null) { - if (other.images != null) { - return false; - } - } else if (other.images == null || !imageSize.equals(other.imageSize)) { - return false; - } - // カラーグループが等しいか? (順序は問わない) // IDのみによって判定する ArrayList colorGroup1 = new ArrayList(getColorGroups()); @@ -261,12 +250,24 @@ public class CharacterData implements Serializable, PartsSpecResolver { return ret; } }; - // カテゴリID順に並び替えて比較する. + // カテゴリID順に並び替えて, IDのみを比較する. Collections.sort(categories1, sortCategoryId); Collections.sort(categories2, sortCategoryId); - if (!categories1.equals(categories2)) { + int numOfCategories = categories1.size(); + if (numOfCategories != categories2.size()) { + // カテゴリ数不一致 return false; } + for (int idx = 0; idx < numOfCategories; idx++) { + PartsCategory category1 = categories1.get(idx); + PartsCategory category2 = categories2.get(idx); + String categoryId1 = category1.getCategoryId(); + String categoryId2 = category2.getCategoryId(); + if ( !categoryId1.equals(categoryId2)) { + // カテゴリID不一致 + return false; + } + } // レイヤーが等しいか? // ID、重ね順序、dirによってのみ判定する. @@ -291,7 +292,9 @@ public class CharacterData implements Serializable, PartsSpecResolver { Collections.sort(layers1, sortLayerId); Collections.sort(layers2, sortLayerId); - if (!layers1.equals(layers2)) { + // ID、順序、Dirで判断する.(それ以外のレイヤー情報はequalsでは比較されない) + if ( !layers1.equals(layers2)) { + // レイヤー不一致 return false; } } @@ -304,11 +307,80 @@ public class CharacterData implements Serializable, PartsSpecResolver { * 構造が同一であるか、サイズ違い、もしくはレイヤーの順序、カテゴリの順序、 * もしくはレイヤーまたはカテゴリが増えている場合で、減っていない場合はtrueとなる.
* 引数がnullの場合は常にfalseとなる. - * @param previous 前の状態のキャラクター定義、null可 + * @param other 前の状態のキャラクター定義、null可 * @return アッパーコンパチブルであればtrue、そうでなければfalse */ - public boolean isUpperCompatibleStructure(CharacterData previous) { - return isSameStructure(previous); // TODO: 実装する。いまは仮 + public boolean isUpperCompatibleStructure(CharacterData other) { + if (!this.isValid() || other == null || !other.isValid()) { + // 自分または相手がinvalidであれば構造的には常に互換性なしと見なす. + return false; + } + + // カラーグループが等しいか? (順序は問わない) + // IDのみによって判定する + ArrayList colorGroupNew = new ArrayList(getColorGroups()); + ArrayList colorGroupOld = new ArrayList(other.getColorGroups()); + if (!colorGroupNew.containsAll(colorGroupOld)) { + // 自分が相手分のすべてのものを持っていなければ互換性なし. + return false; + } + + // カテゴリをすべて含むか? (順序は問わない) + // IDによってのみ判定する. + Map categoriesNew = new HashMap(); + for (PartsCategory category : getPartsCategories()) { + categoriesNew.put(category.getCategoryId(), category); + } + Map categoriesOld = new HashMap(); + for (PartsCategory category : other.getPartsCategories()) { + categoriesOld.put(category.getCategoryId(), category); + } + if ( !categoriesNew.keySet().containsAll(categoriesOld.keySet())) { + // 自分が相手のすべてのカテゴリを持っていなければ互換性なし. + return false; + } + + // レイヤーをすべて含むか? + // ID、Dirによってのみ判定する. + for (Map.Entry categoryOldEntry : categoriesOld.entrySet()) { + String categoryId = categoryOldEntry.getKey(); + PartsCategory categoryOld = categoryOldEntry.getValue(); + PartsCategory categoryNew = categoriesNew.get(categoryId); + if (categoryNew == null) { + return false; + } + + Map layersNew = new HashMap(); + for (Layer layer : categoryNew.getLayers()) { + layersNew.put(layer.getId(), layer); + } + Map layersOld = new HashMap(); + for (Layer layer : categoryOld.getLayers()) { + layersOld.put(layer.getId(), layer); + } + + if ( !layersNew.keySet().containsAll(layersOld.keySet())) { + // 自分が相手のすべてのレイヤー(ID)を持っていなければ互換性なし. + return false; + } + for (Map.Entry layerOldEntry : layersOld.entrySet()) { + String layerId = layerOldEntry.getKey(); + Layer layerOld = layerOldEntry.getValue(); + Layer layerNew = layersNew.get(layerId); + if (layerNew == null) { + return false; + } + String dirOld = layerOld.getDir(); + String dirNew = layerNew.getDir(); + if ( !dirOld.equals(dirNew)) { + // ディレクトリが一致しなければ互換性なし. + // (XXX: 大文字・小文字も厳格に区別している. Windows環境はどうするべきか?) + return false; + } + } + } + + return true; } /** diff --git a/src/charactermanaj/model/PartsColorInfo.java b/src/charactermanaj/model/PartsColorInfo.java index 3886631..527e993 100644 --- a/src/charactermanaj/model/PartsColorInfo.java +++ b/src/charactermanaj/model/PartsColorInfo.java @@ -6,6 +6,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import charactermanaj.graphics.filters.ColorConvertParameter; @@ -17,6 +19,11 @@ import charactermanaj.graphics.filters.ColorConvertParameter; */ public final class PartsColorInfo extends AbstractMap implements Serializable, Cloneable { + /** + * ロガー + */ + private static final Logger logger = Logger.getLogger(PartsColorInfo.class.getName()); + private static final long serialVersionUID = -8639109147043912257L; /** @@ -42,22 +49,45 @@ public final class PartsColorInfo extends AbstractMap implemen init(); } - protected PartsColorInfo(PartsColorInfo src) { - if (src == null) { + @Override + public PartsColorInfo clone() { + try { + PartsColorInfo inst = (PartsColorInfo) super.clone(); + inst.colorInfoMap = new HashMap(); + for (Map.Entry entry : colorInfoMap.entrySet()) { + Layer layer = entry.getKey(); + ColorInfo colorInfo = entry.getValue(); + inst.colorInfoMap.put(layer, colorInfo.clone()); + } + return inst; + + } catch (CloneNotSupportedException ex) { + throw new RuntimeException(ex); + } + } + + /** + * パーツカラー情報を指定したパーツカテゴリに存在するレイヤーに正規化して返す.
+ * カテゴリに存在しないレイヤーの情報は破棄され、結果は有効なレイヤーのみの色情報となる.
+ * @param partsCategory パーツカテゴリ + * @return 正規化されたパーツカラー情報 + */ + public PartsColorInfo createCompatible(PartsCategory partsCategory) { + if (partsCategory == null) { throw new IllegalArgumentException(); } - this.partsCategory = src.partsCategory; - init(); - for (Map.Entry entry : src.colorInfoMap.entrySet()) { + PartsColorInfo newInfo = new PartsColorInfo(partsCategory); + newInfo.init(); + for (Map.Entry entry : colorInfoMap.entrySet()) { Layer layer = entry.getKey(); ColorInfo colorInfo = entry.getValue(); - colorInfoMap.put(layer, colorInfo.clone()); + if (partsCategory.hasLayer(layer)) { + newInfo.put(layer, colorInfo.clone()); + } else { + logger.log(Level.INFO, "missing layer '" + layer + "' in " + partsCategory); + } } - } - - @Override - public PartsColorInfo clone() { - return new PartsColorInfo(this); + return newInfo; } /** diff --git a/src/charactermanaj/model/PartsColorManager.java b/src/charactermanaj/model/PartsColorManager.java index fc95491..a8d5fca 100644 --- a/src/charactermanaj/model/PartsColorManager.java +++ b/src/charactermanaj/model/PartsColorManager.java @@ -20,24 +20,58 @@ public class PartsColorManager { private static final Logger logger = Logger.getLogger(PartsColorManager.class.getName()); /** + * カテゴリごとのパーツカラー情報.
+ * @author seraphy + */ + public static final class CategoryColorInfo { + + private final PartsColorInfo partsColorInfo; + + private final boolean applyAll; + + public CategoryColorInfo(PartsColorInfo partsColorInfo, boolean applyAll) { + this.partsColorInfo = partsColorInfo; + this.applyAll = applyAll; + } + + public PartsColorInfo getPartsColorInfo() { + return partsColorInfo; + } + + public boolean isApplyAll() { + return applyAll; + } + } + + /** * パーツ単位でのカラーグループを含む色情報.
* カテゴリ全体に適用される場合はパーツ単位の色情報はリセットする.
*/ - private HashMap partsColorInfoMap = new HashMap(); + private HashMap partsColorInfoMap + = new HashMap(); /** * カテゴリごとに共通となる場合のカラーグループを含む色情報.
* パーツ単位の色情報が定義されていない場合、カテゴリ単位での情報が使用される.
*/ - private HashMap categoryColorInfoMap = new HashMap(); + private HashMap categoryColorInfoMap + = new HashMap(); /** * カラーグループごとの色情報.
*/ - private HashMap recentColorGroupMap = new HashMap(); + private HashMap recentColorGroupMap + = new HashMap(); + /** + * パーツ設定のリゾルバ + */ private PartsSpecResolver partsSpecResolver; + /** + * パーツ設定リゾルバを指定して構築する. + * @param partsSpecResolver リゾルバ + */ public PartsColorManager(PartsSpecResolver partsSpecResolver) { if (partsSpecResolver == null) { throw new IllegalArgumentException(); @@ -87,15 +121,16 @@ public class PartsColorManager { PartsCategory partsCategory = partsIdentifier.getPartsCategory(); if (applyAll) { - // カテゴリ指定 - resetPartsColorInfo(partsCategory); // パーツ個別色をリセットすることでカテゴリを優先させる. - + // カテゴリ指定の場合 + // パーツ個別色をリセットすることでカテゴリを優先させる. + resetPartsColorInfo(partsCategory); + if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "setPartsColorInfo(Category): " + partsIdentifier + "=" + partsColorInfo); } } else { - // パーツ個別指定 + // パーツ個別指定の場合 partsColorInfoMap.put(partsIdentifier, partsColorInfo); if (logger.isLoggable(Level.FINEST)) { @@ -103,12 +138,27 @@ public class PartsColorManager { } } - setRecentColorGroup(partsColorInfo); - categoryColorInfoMap.put(partsCategory, partsColorInfo); + // カラーグループとしての最新のカラー情報を保存する.(有効なカラーグループで連動指定がある場合のみ) + // ただし、「すべてに適用」でない場合は保存しない. + if (applyAll) { + setRecentColorGroup(partsColorInfo); + } + + // カテゴリごとの最新の色情報を設定する. + // (「すべてに適用」であるか、単数選択カテゴリで、まだ「すべてに適用」の色情報がない場合のみ.) + // (複数選択カテゴリの場合は明示的に「すべてに適用」を選択していないかぎり保存されない.) + CategoryColorInfo categoryColorInfo = categoryColorInfoMap.get(partsCategory); + if (applyAll || + (!partsCategory.isMultipleSelectable() && + (categoryColorInfo == null || !categoryColorInfo.isApplyAll()))) { + categoryColorInfo = new CategoryColorInfo(partsColorInfo, applyAll); + categoryColorInfoMap.put(partsCategory, categoryColorInfo); + } } /** * パーツの色情報を指定して、パーツ識別子の各レイーヤの色グループ情報を保存します.
+ * 連動指定が有効であり、有効なカラーグループである場合のみ保存されます.
* @param partsColorInfo パーツ識別子 */ protected void setRecentColorGroup(PartsColorInfo partsColorInfo) { @@ -158,7 +208,7 @@ public class PartsColorManager { * @param partsCategory パーツカテゴリ * @return 指定したパーツカテゴリの色情報、またはnull */ - public PartsColorInfo getPartsColorInfo(PartsCategory partsCategory) { + public CategoryColorInfo getPartsColorInfo(PartsCategory partsCategory) { return categoryColorInfoMap.get(partsCategory); } @@ -170,16 +220,19 @@ public class PartsColorManager { } /** - * 指定したカテゴリに属するパーツ識別子ごとの色情報をリセットします.
- * 引数partsCategoryがnullの場合は全パーツ識別子をリセットします.
+ * 指定したカテゴリと、カテゴリに属するパーツ識別子ごとの色情報をリセットします.
+ * 引数partsCategoryがnullの場合は全パーツ識別子、全カテゴリと、すべてのカラーグループをリセットします.
* @param partsCategory パーツカテゴリまたはnull */ public void resetPartsColorInfo(PartsCategory partsCategory) { if (partsCategory == null) { + recentColorGroupMap.clear(); partsColorInfoMap.clear(); + categoryColorInfoMap.clear(); return; } + categoryColorInfoMap.remove(partsCategory); Iterator> ite = partsColorInfoMap.entrySet().iterator(); while (ite.hasNext()) { Map.Entry entry = ite.next(); @@ -199,34 +252,48 @@ public class PartsColorManager { PartsCategory category = partsIdentifier.getPartsCategory(); PartsColorInfo partsColorInfo = new PartsColorInfo(category); - // 同一カテゴリの最近設定されたカラー情報を適用する. - PartsColorInfo categoryPartsColorInfo = categoryColorInfoMap.get(category); - if (categoryPartsColorInfo != null) { - for (Map.Entry entry : categoryPartsColorInfo.entrySet()) { - Layer layer = entry.getKey(); - ColorInfo colorInfo = entry.getValue(); - if (colorInfo != null && partsColorInfo.containsKey(layer)) { - partsColorInfo.put(layer, colorInfo.clone()); - } - } - } - - // パーツ固有の設定があり、パーツ固定のカラーグループの指定があれば - // 全レイヤーを該当カラーグループに設定する. + // パーツ固有のカラーグループの指定があるか? PartsSpec partsSpec = partsSpecResolver.getPartsSpec(partsIdentifier); + ColorGroup partsSpecColorGroup = null; if (partsSpec != null) { - ColorGroup colorGroup = partsSpec.getColorGroup(); - if (colorGroup != null && colorGroup.isEnabled()) { - for (Map.Entry entry : partsColorInfo.entrySet()) { + partsSpecColorGroup = partsSpec.getColorGroup(); + } + + if (partsSpecColorGroup != null && partsSpecColorGroup.isEnabled()) { + // パーツ固定のカラーグループの指定があれば + // 全レイヤーを該当カラーグループに設定する. + for (Map.Entry entry : partsColorInfo.entrySet()) { + ColorInfo colorInfo = entry.getValue(); + colorInfo = colorInfo.clone(); + colorInfo.setColorGroup(partsSpecColorGroup); + colorInfo.setSyncColorGroup(true); + entry.setValue(colorInfo); + } + + } else { + // パーツ固有のカラーグループがなければ + // 同一カテゴリの最近設定されたカラー情報をもとに、パーツカラー情報を作成する. + CategoryColorInfo categoryColorInfo = categoryColorInfoMap.get(category); + if (categoryColorInfo != null) { + PartsColorInfo categoryPartsColorInfo = categoryColorInfo.getPartsColorInfo(); + for (Map.Entry entry : categoryPartsColorInfo.entrySet()) { + Layer layer = entry.getKey(); ColorInfo colorInfo = entry.getValue(); - if (colorInfo != null) { + if (colorInfo != null && partsColorInfo.containsKey(layer)) { colorInfo = colorInfo.clone(); - } else { - colorInfo = new ColorInfo(); + + // ただし、同一カテゴリに設定されたカラー情報が「すべてに適用」でない場合は、 + // レイヤー固有のカラーグループを維持する. + if ( !categoryColorInfo.isApplyAll()) { + ColorGroup layerColorGroup = layer.getColorGroup(); + if (layerColorGroup == null) { + layerColorGroup = ColorGroup.NA; + } + colorInfo.setColorGroup(layerColorGroup); + } + + partsColorInfo.put(layer, colorInfo); } - colorInfo.setColorGroup(colorGroup); - colorInfo.setSyncColorGroup(true); - entry.setValue(colorInfo); } } } diff --git a/src/charactermanaj/model/PartsSet.java b/src/charactermanaj/model/PartsSet.java index ebf5123..41bfe92 100644 --- a/src/charactermanaj/model/PartsSet.java +++ b/src/charactermanaj/model/PartsSet.java @@ -100,6 +100,7 @@ public final class PartsSet extends AbstractMap partsColorInfoEntry : org.partsColorInfoMap.entrySet()) { PartsIdentifier partsIdentifier = partsColorInfoEntry.getKey(); if (resolver != null) { @@ -117,11 +118,13 @@ public final class PartsSet extends AbstractMap> partsEntry : org.parts.entrySet()) { PartsCategory orgPartsCategory = partsEntry.getKey(); PartsCategory partsCategory = orgPartsCategory; @@ -153,7 +156,7 @@ public final class PartsSet extends AbstractMap ownList = get(category); + // カテゴリが一致し、各カテゴリに登録されているパーツ識別子のリストも順序を含めて一致する場合、 + // 構造的に一致すると判定する. + for (Map.Entry> entry : entrySet()) { + PartsCategory category = entry.getKey(); + List ownList = entry.getValue(); List otherList = other.get(category); if (ownList == null || otherList == null || !ownList.equals(otherList)) { return false; } } - // カテゴリが一致し、各カテゴリに登録されているパーツ識別子のリストも順序を含めて一致する場合、 - // 構造的に一致すると判定する. return true; } return false; diff --git a/src/charactermanaj/model/io/AbstractCharacterDataArchivedFileWriter.java b/src/charactermanaj/model/io/AbstractCharacterDataArchivedFileWriter.java index 651e315..ccafac8 100644 --- a/src/charactermanaj/model/io/AbstractCharacterDataArchivedFileWriter.java +++ b/src/charactermanaj/model/io/AbstractCharacterDataArchivedFileWriter.java @@ -1,13 +1,18 @@ package charactermanaj.model.io; import java.awt.Color; +import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.Writer; import java.nio.charset.Charset; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -16,10 +21,12 @@ import charactermanaj.graphics.io.ImageSaveHelper; import charactermanaj.model.AppConfig; import charactermanaj.model.CharacterData; import charactermanaj.model.Layer; +import charactermanaj.model.PartsCategory; import charactermanaj.model.PartsFiles; import charactermanaj.model.PartsIdentifier; import charactermanaj.model.PartsManageData; import charactermanaj.model.PartsManageDataConverter; +import charactermanaj.model.PartsSet; import charactermanaj.model.PartsSpec; public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCharacterDataFileWriter { @@ -46,10 +53,84 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh throws IOException { CharacterDataPersistent persist = CharacterDataPersistent.getInstance(); - // character data + // character.xmlの出力 putNextEntry("character.xml", 0); persist.writeXMLCharacterData(characterData, getOutputStream()); closeEntry(); + + // character.iniの出力 + internalWriteCharacterIni(characterData); + } + + /** + * character.iniを出力します.
+ * @param characterData キャラクターデータ + * @throws IOException 出力に失敗した場合 + */ + protected void internalWriteCharacterIni(CharacterData characterData) throws IOException { + StringBuilder buf = new StringBuilder(); + + buf.append("; created by charactermanaj " + + new Timestamp(System.currentTimeMillis()) + "\r\n"); + + buf.append("[Size]\r\n"); + Dimension dim = characterData.getImageSize(); + if (dim == null) { + dim = new Dimension(300, 400); + } + buf.append("size_x=" + dim.width + "\r\n"); + buf.append("size_y=" + dim.height + "\r\n"); + + buf.append("\r\n"); + buf.append("[Parts]\r\n"); + + Map partsMap = new HashMap(); + for (PartsCategory partsCategory : characterData.getPartsCategories()) { + String categoryId = partsCategory.getCategoryId(); + partsMap.put(categoryId, ""); + } + + Map partsSets = characterData.getPartsSets(); + PartsSet partsSet = partsSets.get(characterData.getDefaultPartsSetId()); + if (partsSet == null && !partsSets.isEmpty()) { + // デフォルトのパーツセットが指定されていない場合は、どれか1つを選択する. + partsSet = partsSets.values().iterator().next(); + } + if (partsSet != null) { + for (Map.Entry> entry : partsSet + .entrySet()) { + PartsCategory partsCategory = entry.getKey(); + StringBuilder partsNames = new StringBuilder(); + for (PartsIdentifier partsIdentifier : entry.getValue()) { + if (partsNames.length() > 0) { + partsNames.append(","); + } + partsNames.append(partsIdentifier.getPartsName()); + } + String categoryId = partsCategory.getCategoryId(); + partsMap.put(categoryId, partsNames.toString()); + } + } + for (PartsCategory partsCategory : characterData.getPartsCategories()) { + String categoryId = partsCategory.getCategoryId(); + String partsNames = partsMap.get(categoryId); + buf.append(categoryId + "=" + partsNames + "\r\n"); + } + + // 色情報はすべてダミー(character.iniは色情報を省略しても問題ないようだが、一応) + buf.append("\r\n"); + buf.append("[Color]\r\n"); + buf.append("hair_rgb=0\r\n"); + buf.append("hair_gray=0\r\n"); + buf.append("eye_rgb=0\r\n"); + buf.append("eye_gray=0\r\n"); + buf.append("skin_rgb=0\r\n"); + buf.append("skin_gray=0\r\n"); + buf.append("body_rgb=0\r\n"); + buf.append("body_gray=0\r\n"); + + // UTF16LEで出力する. + internalWriteTextUTF16LE("character.ini", buf.toString()); } @Override @@ -68,12 +149,20 @@ public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCh os.write((byte) 0xff); os.write((byte) 0xfe); os.flush(); - OutputStreamWriter wr = new OutputStreamWriter(os, Charset.forName("UTF-16LE")); + Writer wr = new OutputStreamWriter(os, Charset.forName("UTF-16LE")) { + @Override + public void close() throws IOException { + // ZipのOutputStreamをクローズしてはならないため + // OutputStreamWriter自身はクローズは呼び出さない. + flush(); + closeEntry(); + } + }; try { wr.append(contents); wr.flush(); } finally { - closeEntry(); + wr.close(); } } diff --git a/src/charactermanaj/model/io/ExportInfoKeys.java b/src/charactermanaj/model/io/ExportInfoKeys.java index 0d0e680..8a0d8c4 100644 --- a/src/charactermanaj/model/io/ExportInfoKeys.java +++ b/src/charactermanaj/model/io/ExportInfoKeys.java @@ -8,8 +8,6 @@ public interface ExportInfoKeys { String EXPORT_CHARACTER_DATA = "EXPORT_CHARACTER_DATA"; - String EXPORT_SUBSET = "EXPORT_SUBSET"; - String EXPORT_PARTS_IMAGES = "EXPORT_PARTS_IMAGES"; String EXPORT_TIMESTAMP = "EXPORT_TIMESTAMP"; diff --git a/src/charactermanaj/model/io/PartsSpecDecorateLoader.java b/src/charactermanaj/model/io/PartsSpecDecorateLoader.java index fd0fef3..dc27b4b 100644 --- a/src/charactermanaj/model/io/PartsSpecDecorateLoader.java +++ b/src/charactermanaj/model/io/PartsSpecDecorateLoader.java @@ -3,7 +3,10 @@ package charactermanaj.model.io; import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import charactermanaj.model.AppConfig; import charactermanaj.model.ColorGroup; import charactermanaj.model.PartsCategory; import charactermanaj.model.PartsIdentifier; @@ -47,16 +50,24 @@ public class PartsSpecDecorateLoader implements PartsDataLoader { } /** - * パーツ識別子の表示名の末尾がカラーグループの表示名を括弧でくくったものと等しい場合、 + * パーツ識別子の表示名に、カラーグループの表示名により判定されるパターンに合致する場合、 * パーツ設定のカラーグループを、そのカラーグループとして設定する. * @param partsSpecs パーツマップ */ protected void decolatePartsSpec(Map partsSpecs) { + String templ = AppConfig.getInstance().getPartsColorGroupPattern(); + if (templ == null || templ.trim().length() == 0) { + // パターンが設定されていない場合は無視する. + return; + } // パーツ名にカラーグループが含まれる場合、それを登録する. for (ColorGroup colorGroup : colorGroups) { - String suffix = "(" + colorGroup.getLocalizedName() + ")"; + String pattern = templ.replace("@", colorGroup.getLocalizedName()); + Pattern pat = Pattern.compile(pattern); for (PartsSpec partsSpec : partsSpecs.values()) { - if (partsSpec.getPartsIdentifier().getLocalizedPartsName().endsWith(suffix)) { + Matcher mat = pat.matcher(partsSpec.getPartsIdentifier() + .getLocalizedPartsName()); + if (mat.matches()) { partsSpec.setColorGroup(colorGroup); } } diff --git a/src/charactermanaj/model/util/StartupSupport.java b/src/charactermanaj/model/util/StartupSupport.java index ecd9319..0a149f0 100644 --- a/src/charactermanaj/model/util/StartupSupport.java +++ b/src/charactermanaj/model/util/StartupSupport.java @@ -2,8 +2,10 @@ package charactermanaj.model.util; import java.io.File; import java.net.URI; +import java.net.URL; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -36,13 +38,15 @@ public abstract class StartupSupport { new PurgeUnusedCache(), }; for (StartupSupport startup : startups) { - logger.log(Level.FINE, "startup operation start."); + logger.log(Level.FINE, "startup operation start. class=" + + startup.getClass().getSimpleName()); try { startup.doStartup(); + logger.log(Level.INFO, "startup operation is done."); + } catch (Exception ex) { logger.log(Level.WARNING, "startup operation failed.", ex); } - logger.log(Level.INFO, "startup operation is done."); } } }; @@ -123,12 +127,69 @@ class UpgradeCache extends StartupSupport { abstract class StartupSupportForDocBasedData extends StartupSupport { /** + * character.xmlのファイル位置を示すUUID表現を算定するためのアルゴリズムの選択肢.
+ * @author seraphy + */ + protected enum DocBaseSignatureStoratage { + + /** + * 新形式のcharacter.xmlのUUIDを取得する. + * charatcer.xmlファイルのURIを文字列にしたもののタイプ3-UUID表現.
+ */ + NEW_FORMAT() { + @Override + public String getDocBaseSignature(File characterXmlFile) { + URI docBase = characterXmlFile.toURI(); + UserDataFactory userDataFactory = UserDataFactory.getInstance(); + return userDataFactory.getMangledNamedPrefix(docBase); + } + }, + + /** + * 旧形式のcharacter.xmlのUUIDを取得する.
+ * charatcer.xmlファイルのURLを文字列にしたもののタイプ3-UUID表現.
+ */ + OLD_FORMAT() { + @Override + public String getDocBaseSignature(File characterXmlFile) { + try { + @SuppressWarnings("deprecation") + URL url = characterXmlFile.toURL(); + return UUID.nameUUIDFromBytes(url.toString().getBytes()).toString(); + + } catch (Exception ex) { + logger.log(Level.WARNING, + "character.xmlのファイル位置をUUID化できません。:" + + characterXmlFile, ex); + return null; + } + } + }, + ; + + /** + * ロガー + */ + private static Logger logger = Logger.getLogger( + DocBaseSignatureStoratage.class.getName()); + + /** + * character.xmlからuuid表現のプレフィックスを算定する. + * @param characterXmlFile character.xmlのファイル + * @return UUID + */ + public abstract String getDocBaseSignature(File characterXmlFile); + } + + /** * すべてのユーザおよびシステムのキャラクターデータのDocBaseをもととしたハッシュ値(Prefix)の文字列をキーとし、 * キャラクターディレクトリを値とするマップを返す.
* @return DocBaseをもととしたハッシュ値の文字列表記をキー、キャラクターディレクトリを値とするマップ */ - protected Map getDocBaseMap() { - UserDataFactory userDataFactory = UserDataFactory.getInstance(); + protected Map getDocBaseMap(DocBaseSignatureStoratage storatage) { + if (storatage == null) { + throw new IllegalArgumentException(); + } AppConfig appConfig = AppConfig.getInstance(); File[] charactersDirs = { @@ -151,9 +212,10 @@ abstract class StartupSupportForDocBasedData extends StartupSupport { if ( !characterXml.exists()) { continue; } - URI docBase = characterXml.toURI(); - String docBaseSig = userDataFactory.getMangledNamedPrefix(docBase); - docBaseSignatures.put(docBaseSig, characterDir); + String docBaseSig = storatage.getDocBaseSignature(characterXml); + if (docBaseSig != null) { + docBaseSignatures.put(docBaseSig, characterDir); + } } } return docBaseSignatures; @@ -213,10 +275,11 @@ class UpgradeFavoritesXml extends StartupSupportForDocBasedData { File appData = userDataFactory.getSpecialDataDir(null); // キャラクターデータディレクトリを走査しdocBaseの識別子の一覧を取得する - Map docBaseSignatures = getDocBaseMap(); + Map docBaseSignatures = getDocBaseMap( + DocBaseSignatureStoratage.OLD_FORMAT); // ver0.94までは*.favorite.xmlはユーザディレクトリ直下に配備していたが - // ver0.95以降は各キャラクターディレクトリに移動するため、旧docbase-favorites.xmlが残っていれば移動する + // ver0.95以降は各キャラクターディレクトリに移動するため、旧docbase-id-favorites.xmlが残っていれば移動する // ユーザディレクトリ直下にある*-facotites.xmlを列挙する Map favorites = getUUIDMangledNamedMap(appData, "-favorites.xml"); @@ -253,7 +316,8 @@ class PurgeUnusedCache extends StartupSupportForDocBasedData { @Override public void doStartup() { // キャラクターデータディレクトリを走査しdocBaseの識別子の一覧を取得する - Map docBaseSignatures = getDocBaseMap(); + Map docBaseSignatures = getDocBaseMap( + DocBaseSignatureStoratage.NEW_FORMAT); // キャッシュの保存先を取得する. UserDataFactory userDataFactory = UserDataFactory.getInstance(); @@ -272,8 +336,9 @@ class PurgeUnusedCache extends StartupSupportForDocBasedData { File cacheFile = cacheEntry.getValue(); try { if ( !docBaseSignatures.containsKey(mangledUUID)) { - cacheFile.delete(); - logger.log(Level.INFO, "purge unused cache: " + cacheFile); + boolean result = cacheFile.delete(); + logger.log(Level.INFO, "purge unused cache: " + cacheFile + + "/succeeded=" + result); } } catch (Exception ex) { @@ -310,8 +375,9 @@ class PurgeOldLogs extends StartupSupport { if (file.isFile() && file.canWrite() && name.endsWith(".log")) { long lastModified = file.lastModified(); if (lastModified > 0 && lastModified < purgeThresold) { - file.delete(); - logger.log(Level.INFO, "remove file " + file); + boolean result = file.delete(); + logger.log(Level.INFO, "remove file " + file + + "/succeeded=" + result); } } diff --git a/src/charactermanaj/ui/ColorDialog.java b/src/charactermanaj/ui/ColorDialog.java index 03a1b05..b8aaccf 100644 --- a/src/charactermanaj/ui/ColorDialog.java +++ b/src/charactermanaj/ui/ColorDialog.java @@ -203,6 +203,12 @@ public class ColorDialog extends JDialog { gbc.weighty = 0.; chkApplyAll = new JCheckBox(strings.getProperty("checkbox.applyAllItems")); chkApplyAll.setSelected(!partsCategory.isMultipleSelectable()); + chkApplyAll.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // すべてに適用のチェックが変更された場合は全レイヤーの色の変更通知を出す. + apply(); + } + }); btnPanel.add(chkApplyAll, gbc); gbc.gridx = colIdx++; diff --git a/src/charactermanaj/ui/ExportWizardDialog.java b/src/charactermanaj/ui/ExportWizardDialog.java index d0912b0..49ccaf4 100644 --- a/src/charactermanaj/ui/ExportWizardDialog.java +++ b/src/charactermanaj/ui/ExportWizardDialog.java @@ -320,13 +320,6 @@ public class ExportWizardDialog extends JDialog { protected boolean isComplete() { - if (basicPanel.isExportSubset() && !basicPanel.isExportPartsImages() - && !basicPanel.isExportPresets() - && !basicPanel.isExportSamplePicture()) { - // サブセットを指定した場合、パーツイメージ、パーツセット、またはサンプルピクチャのいずれかはエクスポートする必要がある. - return false; - } - if (basicPanel.isExportPartsImages()) { if (partsSelectPanel.getSelectedCount() == 0) { // パーツイメージのエクスポートを指定した場合、エクスポートするパーツの選択は必須 @@ -405,7 +398,6 @@ public class ExportWizardDialog extends JDialog { boolean exportSamplePicture = basicPanel.isExportSamplePicture(); boolean exportCharacterData = true; boolean exportPartsImages = basicPanel.isExportPartsImages(); - boolean exportSubset = basicPanel.isExportSubset(); // 基本情報を設定する. cd.setAuthor(basicPanel.getAuthor()); @@ -416,7 +408,6 @@ public class ExportWizardDialog extends JDialog { exportProp.setProperty(ExportInfoKeys.EXPORT_PRESETS, Boolean.toString(exportPresets)); exportProp.setProperty(ExportInfoKeys.EXPORT_SAMPLE_PICTURE, Boolean.toString(exportSamplePicture)); exportProp.setProperty(ExportInfoKeys.EXPORT_CHARACTER_DATA, Boolean.toString(exportCharacterData)); - exportProp.setProperty(ExportInfoKeys.EXPORT_SUBSET, Boolean.toString(exportSubset)); exportProp.setProperty(ExportInfoKeys.EXPORT_PARTS_IMAGES, Boolean.toString(exportPartsImages)); exportProp.setProperty(ExportInfoKeys.EXPORT_TIMESTAMP, Long.toString(System.currentTimeMillis())); @@ -497,8 +488,6 @@ interface ExportInformationResolver extends ExportResolverBase { BufferedImage getSamplePicture(); - boolean isExportSubset(); - boolean isExportSamplePicture(); boolean isExportPartsImages(); @@ -558,8 +547,6 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform private JCheckBox chkSampleImage; - private JCheckBox chkExportSubset; - protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture) { @@ -612,41 +599,34 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform gbc.gridx = 0; gbc.gridy = 0; gbc.gridheight = 1; - gbc.gridwidth = 2; + gbc.gridwidth = 1; gbc.weightx = 0.; gbc.weighty = 0.; gbc.insets = new Insets(3, 3, 3, 3); gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.BOTH; - chkExportSubset = new JCheckBox(strings.getProperty("exportSubset")); - commentPanel.add(chkExportSubset, gbc); - - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridheight = 1; - gbc.gridwidth = 1; - gbc.weightx = 0.; - gbc.weighty = 0.; commentPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc); gbc.gridx = 1; - gbc.gridy = 1; + gbc.gridy = 0; gbc.gridwidth = 1; gbc.weightx = 1.; + gbc.weighty = 0.; txtAuthor = new JTextField(); commentPanel.add(txtAuthor, gbc); gbc.gridx = 0; - gbc.gridy = 2; + gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 0.; + gbc.weighty = 0.; commentPanel.add(new JLabel(strings.getProperty("description"), JLabel.RIGHT), gbc); gbc.gridx = 1; - gbc.gridy = 2; + gbc.gridy = 1; gbc.gridwidth = 1; - gbc.gridheight = 5; + gbc.gridheight = 2; gbc.weighty = 1.; gbc.weightx = 1.; txtDescription = new JTextArea(); @@ -706,7 +686,6 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform chkPartsImages.addActionListener(modListener); chkPresets.addActionListener(modListener); chkSampleImage.addActionListener(modListener); - chkExportSubset.addActionListener(modListener); } protected void updateSamplePicture() { @@ -730,18 +709,12 @@ class ExportInformationPanel extends AbstractImportPanel implements ExportInform String description = characterData.getDescription(); txtAuthor.setText(author == null ? "" : author); txtDescription.setText(description == null ? "" : description); - - chkExportSubset.setSelected(true); } public BufferedImage getSamplePicture() { return samplePicture; } - public boolean isExportSubset() { - return chkExportSubset.isSelected(); - } - public boolean isExportSamplePicture() { return chkSampleImage.isSelected(); } diff --git a/src/charactermanaj/ui/MainFrame.java b/src/charactermanaj/ui/MainFrame.java index 7a408e9..b323d5c 100644 --- a/src/charactermanaj/ui/MainFrame.java +++ b/src/charactermanaj/ui/MainFrame.java @@ -11,7 +11,9 @@ import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; +import java.awt.GraphicsEnvironment; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; @@ -211,16 +213,59 @@ public class MainFrame extends JFrame { return activedMainFrame; } + public static void notifyImportedPartsOrFavorites(CharacterData cd, + CharacterData newCd, Component caller) throws IOException { + if (cd == null || newCd == null || caller == null) { + throw new IllegalArgumentException(); + } + + if (!cd.isValid() || !newCd.isValid()) { + // 変更前もしくは変更後が無効なキャラクターデータであれば + // 反映する必要ない. + return; + } + logger.log(Level.INFO, "parts imported for active profiles: " + newCd); + + + if ( !cd.isSameStructure(newCd)) { + // キャラクターデータそのものが変更されている場合 + notifyChangeCharacterData(cd, newCd, caller); + + } else { + // パーツ構成は変更されていない場合 + + // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査 + for (Frame frame : JFrame.getFrames()) { + if (frame.isDisplayable() && frame instanceof MainFrame) { + MainFrame mainFrame = (MainFrame) frame; + if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) { + // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外 + continue; + } + // パーツ及びお気に入りを再取得する場合. + caller.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + mainFrame.reloadPartsAndFavorites(newCd, true); + + } finally { + caller.setCursor(Cursor.getDefaultCursor()); + } + } + } + } + } /** * キャラクターデータが変更されたことを通知される.
* @param cd キャラクターデータ(変更前) * @param newCd キャラクターデータ(変更後) + * @param caller 呼び出しもとコンポーネント(ウェイトカーソル表示用) * @param structureCompatible 構造が同一であるか? * @throws IOException 新しいキャラクターデータのパーツセットのロードに失敗した場合 (メインフレームは変更されていません.) */ - public static void notifyChangeCharacterData(CharacterData cd, CharacterData newCd) throws IOException { - if (cd == null || newCd == null) { + public static void notifyChangeCharacterData(CharacterData cd, + CharacterData newCd, Component caller) throws IOException { + if (cd == null || newCd == null || caller == null) { throw new IllegalArgumentException(); } @@ -229,45 +274,54 @@ public class MainFrame extends JFrame { // 反映する必要ない. return; } + logger.log(Level.INFO, "change active profile: " + newCd); if (!ProfileListManager.isUsingCharacterData(cd)) { // 使用中のプロファイルではないので何もしない. return; } - - // キャラクターデータが、まだ読み込まれていなければ読み込む. - if (!newCd.isPartsLoaded()) { - ProfileListManager.loadCharacterData(newCd); - ProfileListManager.loadFavorites(newCd); - } - // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査 - for (Frame frame : JFrame.getFrames()) { - if (frame.isDisplayable() && frame instanceof MainFrame) { - MainFrame mainFrame = (MainFrame) frame; - if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) { - // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外 - continue; - } - - // メインフレームが保持しているキャラクターデータのdocbaseと - // 変更対象となったキャラクターデータのdocbaseが等しければ、メインフレームを更新する必要がある. - String docbaseOrg = cd.getDocBase().toString(); - String docbaseMine = mainFrame.characterData.getDocBase().toString(); - if (docbaseOrg.equals(docbaseMine)) { - mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - // 現在情報の保存 - mainFrame.saveWorkingSet(); - - // 画面構成の再構築 - mainFrame.initComponent(newCd); + caller.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + // キャラクターデータが、まだ読み込まれていなければ読み込む. + if (!newCd.isPartsLoaded()) { + ProfileListManager.loadCharacterData(newCd); + ProfileListManager.loadFavorites(newCd); + } + + // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査 + for (Frame frame : JFrame.getFrames()) { + if (frame.isDisplayable() && frame instanceof MainFrame) { + MainFrame mainFrame = (MainFrame) frame; + if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) { + // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外 + continue; + } - } finally { - mainFrame.setCursor(Cursor.getDefaultCursor()); + // メインフレームが保持しているキャラクターデータのdocbaseと + // 変更対象となったキャラクターデータのdocbaseが等しければ、メインフレームを更新する必要がある. + String docbaseOrg = cd.getDocBase().toString(); + String docbaseMine = mainFrame.characterData.getDocBase().toString(); + if (docbaseOrg.equals(docbaseMine)) { + mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + // 現在情報の保存 + mainFrame.saveWorkingSet(); + + // 画面構成の再構築 + mainFrame.initComponent(newCd); + + } finally { + if (mainFrame != caller) { + mainFrame.setCursor(Cursor.getDefaultCursor()); + } + } } } } + + } finally { + caller.setCursor(Cursor.getDefaultCursor()); } } @@ -303,60 +357,153 @@ public class MainFrame extends JFrame { * @param characterData キャラクターデータ */ public MainFrame(CharacterData characterData) { - if (characterData == null) { - throw new IllegalArgumentException(); - } - - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - onCloseProfile(); + try { + if (characterData == null) { + throw new IllegalArgumentException(); } - @Override - public void windowClosed(WindowEvent e) { - imageBuilder.stop(); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + onCloseProfile(); + } + @Override + public void windowClosed(WindowEvent e) { + stopAgents(); + } + @Override + public void windowActivated(WindowEvent e) { + setActivedMainFrame(MainFrame.this); + } + @Override + public void windowOpened(WindowEvent e) { + // do nothing. + } + }); + + icon = UIHelper.getInstance().getImage("icons/icon.png"); + setIconImage(icon); + + // 画面コンポーネント作成 + initComponent(characterData); + JMenuBar menuBar = createMenuBar(); + setJMenuBar(menuBar); + + // メインスクリーンサイズを取得する. + GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ) + logger.log(Level.INFO, "desktopSize=" + desktopSize); + + Dimension imageSize = characterData.getImageSize(); + // 画像サイズ300x400を基準サイズとして、それ以下にはならない. + // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる + AppConfig appConfig = AppConfig.getInstance(); + int maxWidth = min(desktopSize.width, appConfig.getMainFrameMaxWidth()); + int maxHeight = min(desktopSize.height, appConfig.getMainFrameMaxHeight()); + int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0)); + int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0)); + // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ. + // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく. + setSize(imageWidth - 300 + 600, imageHeight - 400 + 550); + + // 次回表示時にプラットフォーム固有位置に表示するように予約 + setLocationByPlatform(true); + + } catch (RuntimeException ex) { + logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex); + dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある. + throw ex; + } catch (Error ex) { + logger.log(Level.SEVERE, "メインフレームの構築中に致命的な例外が発生しました。", ex); + dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある. + throw ex; + } + } + + /** + * メインフレームを表示する.
+ * デスクトップ領域からはみ出した場合は位置を補正する.
+ */ + public void showMainFrame() { + // メインスクリーンサイズを取得する. + GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ) + logger.log(Level.INFO, "desktopSize=" + desktopSize); + + // プラットフォーム固有の位置あわせで表示する. + // 表示した結果、はみ出している場合は0,0に補正する. + setVisible(true); + Point loc = getLocation(); + logger.log(Level.INFO, "windowLocation=" + loc); + Dimension windowSize = getSize(); + if (loc.y + windowSize.height >= desktopSize.height) { + loc.y = 0; + } + if (loc.x + windowSize.width >= desktopSize.width) { + loc.x = 0; + } + if (loc.x == 0 || loc.y == 0) { + setLocation(loc); + } + + // デスクトップよりも大きい場合は小さくする. + boolean resize = false; + Dimension dim = getSize(); + if (dim.height > desktopSize.height) { + dim.height = desktopSize.height; + resize = true; + } + if (dim.width > desktopSize.width) { + dim.width = desktopSize.width; + resize = true; + } + if (resize) { + setSize(dim); + } + } + + /** + * このメインフレームに関連づけられているエージェントスレッドを停止します.
+ * すでに停止している場合は何もしません。 + */ + protected void stopAgents() { + // エージェントを停止 + if (watchAgent != null) { + try { watchAgent.stop(); + + } catch (Throwable ex) { + logger.log(Level.SEVERE, "フォルダ監視スレッドの停止に失敗しました。", ex); } - @Override - public void windowActivated(WindowEvent e) { - setActivedMainFrame(MainFrame.this); - } - @Override - public void windowOpened(WindowEvent e) { - // do nothing. + watchAgent = null; + } + // イメージビルダを停止 + if (imageBuilder != null) { + try { + imageBuilder.stop(); + + } catch (Throwable ex) { + logger.log(Level.SEVERE, "非同期イメージビルダスレッドの停止に失敗しました。", ex); } - }); + imageBuilder = null; + } + } - icon = UIHelper.getInstance().getImage("icons/icon.png"); - setIconImage(icon); - - // 画面コンポーネント作成 - initComponent(characterData); - JMenuBar menuBar = createMenuBar(); - setJMenuBar(menuBar); - - Dimension imageSize = characterData.getImageSize(); - // 画像サイズ300x400を基準サイズとして、それ以下にはならない. - // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる - AppConfig appConfig = AppConfig.getInstance(); - int maxWidth = appConfig.getMainFrameMaxWidth(); - int maxHeight = appConfig.getMainFrameMaxHeight(); - int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0)); - int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0)); - // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ. - // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく. - setSize(imageWidth - 300 + 600, imageHeight - 400 + 550); - - //setLocationRelativeTo(null); - setLocationByPlatform(true); + /** + * メインフレームを破棄します.
+ */ + @Override + public void dispose() { + stopAgents(); + super.dispose(); } /** * 画面コンポーネントを設定します.
* すでに設定されている場合は一旦削除されたのちに再作成されます.
*/ - private void initComponent(CharacterData characterData) { + private synchronized void initComponent(CharacterData characterData) { CharacterData oldCd; synchronized (this) { @@ -388,15 +535,9 @@ public class MainFrame extends JFrame { // デフォルトのパーツセット表示名 defaultPartsSetTitle = strings.getProperty("defaultPartsSetTitle"); - - // エージェントを停止 - if (watchAgent != null) { - watchAgent.stop(); - } - // イメージビルダを停止 - if (imageBuilder != null) { - imageBuilder.stop(); - } + + // エージェントの停止 + stopAgents(); // コンポーネント配置 Container contentPane = getContentPane(); @@ -521,7 +662,9 @@ public class MainFrame extends JFrame { // 保存されているワーキングセットを復元する. // 復元できなかった場合はパーツセットを初期選択する. if ( !loadWorkingSet()) { - showDefaultParts(true); + if (showDefaultParts(true)) { + requestPreview(); + } } // 選択されているパーツを見える状態にする @@ -586,9 +729,11 @@ public class MainFrame extends JFrame { * パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.
*/ protected void onDetectPartsImageChange() { - if (characterData.reloadPartsData()) { - partsSelectionManager.loadParts(); - requestPreview(); + try { + reloadPartsAndFavorites(null, true); + + } catch (IOException ex) { + logger.log(Level.SEVERE, "parts reload failed. " + characterData, ex); } } @@ -602,59 +747,75 @@ public class MainFrame extends JFrame { /** * デフォルトパーツを選択する.
* デフォルトパーツがなければお気に入りの最初のものを選択する.
- * それもなければ空として表示する. + * それもなければ空として表示する.
+ * パーツの適用に失敗した場合はfalseを返します.(例外は返されません.)
* @param force すでに選択があっても選択しなおす場合はtrue、falseの場合は選択があれば何もしない. - * @return パーツ選択されなかった場合。force=trueの場合は常にtrueとなります。 + * @return パーツ選択された場合。force=trueの場合はエラーがなければ常にtrueとなります。 */ protected boolean showDefaultParts(boolean force) { - if (!force) { - PartsSet sel = partsSelectionManager.createPartsSet(); - if (!sel.isEmpty()) { - // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない. - return false; + try { + if (!force) { + // 現在選択中のパーツを取得する.(なければ空) + PartsSet sel = partsSelectionManager.createPartsSet(); + if (!sel.isEmpty()) { + // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない. + return false; + } } - } - - // デフォルトのパーツセットを取得する - String defaultPresetId = characterData.getDefaultPartsSetId(); - PartsSet partsSet = null; - if (defaultPresetId != null) { - partsSet = characterData.getPartsSets().get(defaultPresetId); - } - - // デフォルトのパーツセットがなければ、お気に入りの最初を選択する. - if (partsSet == null) { - List partssets = getPartsSetList(); - if (!partssets.isEmpty()) { - partsSet = partssets.get(0); + + // デフォルトのパーツセットを取得する + String defaultPresetId = characterData.getDefaultPartsSetId(); + PartsSet partsSet = null; + if (defaultPresetId != null) { + partsSet = characterData.getPartsSets().get(defaultPresetId); } - } + + // デフォルトのパーツセットがなければ、お気に入りの最初を選択する. + if (partsSet == null) { + List partssets = getPartsSetList(); + if (!partssets.isEmpty()) { + partsSet = partssets.get(0); + } + } + + // パーツセットがあれば、それを表示要求する. + // パーツセットがなければカラーダイアログを初期化するのみ + if (partsSet == null) { + partsColorCoordinator.initColorDialog(); - // パーツセットがあれば、それを表示要求する. - // パーツセットがなければカラーダイアログを初期化するのみ - if (partsSet == null) { - partsColorCoordinator.initColorDialog(); - requestPreview(); - } else { - selectPresetParts(partsSet); + } else { + selectPresetParts(partsSet); + } + + } catch (Exception ex) { + logger.log(Level.WARNING, "パーツのデフォルト適用に失敗しました。", ex); + return false; } - return true; } /** - * プリセットを適用しキャラクターイメージを再構築します. - * @param presetParts + * プリセットを適用しキャラクターイメージを再構築します.
+ * 実行時エラーは画面のレポートされます.
+ * @param presetParts パーツセット, nullの場合は何もしない. */ protected void selectPresetParts(PartsSet presetParts) { - // 最後に使用したプリセットとして記憶する. - lastUsePresetParts = presetParts; - // プリセットパーツで選択を変える - partsSelectionManager.selectPartsSet(presetParts); - // カラーパネルを選択されているアイテムをもとに再設定する - partsColorCoordinator.initColorDialog(); - // 再表示 - requestPreview(); + if (presetParts == null) { + return; + } + try { + // 最後に使用したプリセットとして記憶する. + lastUsePresetParts = presetParts; + // プリセットパーツで選択を変える + partsSelectionManager.selectPartsSet(presetParts); + // カラーパネルを選択されているアイテムをもとに再設定する + partsColorCoordinator.initColorDialog(); + // 再表示 + requestPreview(); + + } catch (Exception ex) { + ErrorMessageHelper.showErrorDialog(this, ex); + } } /** @@ -867,10 +1028,7 @@ public class MainFrame extends JFrame { try { MainFrame main2 = ProfileListManager.openProfile(this); if (main2 != null) { - Point pt = getLocation(); - pt.x += 100; - main2.setLocation(pt); - main2.setVisible(true); + main2.showMainFrame(); } } catch (Exception ex) { @@ -964,13 +1122,7 @@ public class MainFrame extends JFrame { CharacterData cd = this.characterData; CharacterData newCd = ProfileListManager.editProfile(this, cd); if (newCd != null) { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - MainFrame.notifyChangeCharacterData(cd, newCd); //TODO:重複 - - } finally { - setCursor(Cursor.getDefaultCursor()); - } + MainFrame.notifyChangeCharacterData(cd, newCd, this); } } catch (Exception ex) { @@ -1104,49 +1256,62 @@ public class MainFrame extends JFrame { return; } - // 監視スレッドを停止してからインポート処理を始める - boolean stopped = watchAgent.stop(); try { // インポートウィザードの実行 ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles); importWizDialog.setVisible(true); if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) { - try { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - // インポートが実行された場合、パーツデータをリロードする. - if (characterData.reloadPartsData()) { - partsSelectionManager.loadParts(); - - // パーツデータが変更された可能性があるので、 - // 監視状態をリセットしてから再開するようにする. - watchAgent.reset(); - } - - // お気に入りをリロードする. - CharacterDataPersistent persiste = CharacterDataPersistent.getInstance(); - persiste.loadFavorites(characterData); - - } finally { - setCursor(Cursor.getDefaultCursor()); - } - - } catch (Exception ex) { - ErrorMessageHelper.showErrorDialog(this, ex); - } - - // 再表示 - if (showDefaultParts(false)) { - requestPreview(); - } + CharacterData importedCd = importWizDialog.getImportedCharacterData(); + notifyImportedPartsOrFavorites(characterData, importedCd, this); } - } finally { - // 監視スレッドを再開する. - if (stopped) { - watchAgent.start(); + } catch (Exception ex) { + ErrorMessageHelper.showErrorDialog(this, ex); + } + } + + /** + * パーツとお気に入りをリロードする.
+ * まだロードされていない場合はあらたにロードする.
+ * 引数newCdが指定されている場合は、現在のキャラクター定義の説明文を更新する.
+ * (説明文の更新以外には使用されない.)
+ * + * @param newCd + * 説明文更新のための更新されたキャラクターデータを指定する。null可 + * @param forceRepaint + * 必ず再描画する場合 + * @throws IOException + * 失敗 + */ + protected synchronized void reloadPartsAndFavorites(CharacterData newCd, + boolean forceRepaint) throws IOException { + if (newCd != null) { + // (インポート画面では説明文のみ更新するので、それだけ取得) + characterData.setDescription(newCd.getDescription()); + } + + if ( !characterData.isPartsLoaded()) { + // キャラクターデータが、まだ読み込まれていなければ読み込む. + ProfileListManager.loadCharacterData(characterData); + ProfileListManager.loadFavorites(characterData); + partsSelectionManager.loadParts(); + + } else { + // パーツデータをリロードする. + if (characterData.reloadPartsData()) { + partsSelectionManager.loadParts(); } + + // お気に入りをリロードする. + CharacterDataPersistent persiste = CharacterDataPersistent.getInstance(); + persiste.loadFavorites(characterData); + notifyChangeFavorites(characterData); + } + + // 現在選択されているパーツセットがない場合はデフォルトのパーツセットを選択する. + if (showDefaultParts(false) || forceRepaint) { + requestPreview(); } } @@ -1344,16 +1509,20 @@ public class MainFrame extends JFrame { // 現在のパーツ色情報にワーキングセットで保存した内容を設定する. Map partsColorInfoMap = characterData.getPartsColorManager().getPartsColorInfoMap(); - for (Map.Entry entry : workingSet.getPartsColorInfoMap().entrySet()) { - PartsIdentifier partsIdentifier = entry.getKey(); - PartsColorInfo partsColorInfo = entry.getValue(); - partsColorInfoMap.put(partsIdentifier, partsColorInfo); + Map workingPartsColorInfoMap = workingSet.getPartsColorInfoMap(); + if (workingPartsColorInfoMap != null) { + for (Map.Entry entry : workingPartsColorInfoMap.entrySet()) { + PartsIdentifier partsIdentifier = entry.getKey(); + PartsColorInfo partsColorInfo = entry.getValue(); + partsColorInfoMap.put(partsIdentifier, partsColorInfo); + } } // 選択されているパーツの復元 PartsSet partsSet = workingSet.getPartsSet(); if (partsSet != null) { - selectPresetParts(partsSet.createCompatible(characterData)); + partsSet = partsSet.createCompatible(characterData); + selectPresetParts(partsSet); // 最後に選択したお気に入り情報の復元 PartsSet lastUsePresetParts = workingSet.getLastUsePresetParts(); @@ -1599,9 +1768,9 @@ public class MainFrame extends JFrame { } }), null, - new MenuDataFactory("file.manageParts", new ActionListener() { + new MenuDataFactory("file.editprofile", new ActionListener() { public void actionPerformed(ActionEvent e) { - onManageParts(); + onEditProfile(); } }), new MenuDataFactory("file.opendir", new ActionListener() { @@ -1609,11 +1778,6 @@ public class MainFrame extends JFrame { onBrowseProfileDir(); } }), - new MenuDataFactory("file.editprofile", new ActionListener() { - public void actionPerformed(ActionEvent e) { - onEditProfile(); - } - }), new MenuDataFactory("file.import", new MenuDataFactory[] { new MenuDataFactory("file.importMe", new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -1631,6 +1795,11 @@ public class MainFrame extends JFrame { onExport(); }; }), + new MenuDataFactory("file.manageParts", new ActionListener() { + public void actionPerformed(ActionEvent e) { + onManageParts(); + } + }), new MenuDataFactory("file.preferences", new ActionListener() { public void actionPerformed(ActionEvent e) { onPreferences(); diff --git a/src/charactermanaj/ui/MainFramePartialForMacOSX.java b/src/charactermanaj/ui/MainFramePartialForMacOSX.java index b96a2bc..88dbc54 100644 --- a/src/charactermanaj/ui/MainFramePartialForMacOSX.java +++ b/src/charactermanaj/ui/MainFramePartialForMacOSX.java @@ -12,6 +12,12 @@ import com.apple.eawt.Application; import com.apple.eawt.ApplicationAdapter; import com.apple.eawt.ApplicationEvent; +/** + * Mac OS X用のメインフレームサポートクラス.
+ * スクリーンメニューのハンドラなどを接続している.
+ * @author seraphy + * + */ public class MainFramePartialForMacOSX { /** diff --git a/src/charactermanaj/ui/ProfileEditDialog.java b/src/charactermanaj/ui/ProfileEditDialog.java index cfe9429..e9297b4 100644 --- a/src/charactermanaj/ui/ProfileEditDialog.java +++ b/src/charactermanaj/ui/ProfileEditDialog.java @@ -1158,8 +1158,8 @@ public class ProfileEditDialog extends JDialog { newCd.setRev(persist.generateRev()); } - } else { - // 新規ではなく、構造が変更されていることを通知する. + } else if ( !newCd.isUpperCompatibleStructure(original)){ + // 上位互換のない構造が変更されていることを通知する. if (JOptionPane.showConfirmDialog(this, strings.get("confirm.changestructre"), strings.getProperty("confirm"), diff --git a/src/charactermanaj/ui/ProfileSelectorDialog.java b/src/charactermanaj/ui/ProfileSelectorDialog.java index 1a06c0a..a1b4ee0 100644 --- a/src/charactermanaj/ui/ProfileSelectorDialog.java +++ b/src/charactermanaj/ui/ProfileSelectorDialog.java @@ -745,13 +745,7 @@ public class ProfileSelectorDialog extends JDialog { } // 現在開いているメインフレームに対してキャラクター定義が変更されたことを通知する. - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - MainFrame.notifyChangeCharacterData(cd, newCd); //TODO:重複 - - } finally { - setCursor(Cursor.getDefaultCursor()); - } + MainFrame.notifyChangeCharacterData(cd, newCd, this); // プロファイル一覧画面も更新する. characterListModel.set(rowIndex, newCd); @@ -853,61 +847,71 @@ public class ProfileSelectorDialog extends JDialog { * インポート */ protected void onProfileImport() { - CharacterData selCd = (CharacterData) characterList.getSelectedValue(); - - // 選択したプロファイルを更新するか、新規にプロファイルを作成するか選択できるようにする - if (selCd != null) { - final Properties strings = LocalizedResourcePropertyLoader - .getInstance().getLocalizedProperties(STRINGS_RESOURCE); - - JPanel radioPanel = new JPanel(new BorderLayout()); - JRadioButton btnUpdate = new JRadioButton(strings.getProperty("importToUpdateProfile")); - JRadioButton btnNew = new JRadioButton(strings.getProperty("importToCreateProfile")); - ButtonGroup radios = new ButtonGroup(); - radios.add(btnUpdate); - radios.add(btnNew); - btnUpdate.setSelected(true); - radioPanel.add(btnUpdate, BorderLayout.NORTH); - radioPanel.add(btnNew, BorderLayout.SOUTH); + try { + CharacterData selCd = (CharacterData) characterList.getSelectedValue(); + + // 選択したプロファイルを更新するか、新規にプロファイルを作成するか選択できるようにする + if (selCd != null) { + final Properties strings = LocalizedResourcePropertyLoader + .getInstance().getLocalizedProperties(STRINGS_RESOURCE); + + JPanel radioPanel = new JPanel(new BorderLayout()); + JRadioButton btnUpdate = new JRadioButton(strings.getProperty("importToUpdateProfile")); + JRadioButton btnNew = new JRadioButton(strings.getProperty("importToCreateProfile")); + ButtonGroup radios = new ButtonGroup(); + radios.add(btnUpdate); + radios.add(btnNew); + btnUpdate.setSelected(true); + radioPanel.add(btnUpdate, BorderLayout.NORTH); + radioPanel.add(btnNew, BorderLayout.SOUTH); + + int ret = JOptionPane.showConfirmDialog(this, radioPanel, + strings.getProperty("confirmUpdateProfile"), + JOptionPane.OK_CANCEL_OPTION); + if (ret != JOptionPane.OK_OPTION) { + return; + } - int ret = JOptionPane.showConfirmDialog(this, radioPanel, - strings.getProperty("confirmUpdateProfile"), - JOptionPane.OK_CANCEL_OPTION); - if (ret != JOptionPane.OK_OPTION) { - return; - } - - if (btnNew.isSelected()) { - // 選択されていないことにする. - selCd = null; + if (btnNew.isSelected()) { + // 選択されていないことにする. + selCd = null; + } } - } - - // キャラクターデータをロードし直す. - CharacterData cd; - if (selCd != null) { - cd = selCd.duplicateBasicInfo(); - try { - ProfileListManager.loadCharacterData(cd); - ProfileListManager.loadFavorites(cd); - - } catch (IOException ex) { - ErrorMessageHelper.showErrorDialog(this, ex); - // 継続する. + + // キャラクターデータをロードし直す. + CharacterData cd; + if (selCd != null) { + cd = selCd.duplicateBasicInfo(); + try { + ProfileListManager.loadCharacterData(cd); + ProfileListManager.loadFavorites(cd); + + } catch (IOException ex) { + ErrorMessageHelper.showErrorDialog(this, ex); + // 継続する. + } + } else { + cd = null; } - } else { - cd = null; - } - - // インポートウィザードの実行 - ImportWizardDialog importWizDialog = new ImportWizardDialog(this, cd); - importWizDialog.setVisible(true); - - if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_CREATED) { + + // インポートウィザードの実行 + ImportWizardDialog importWizDialog = new ImportWizardDialog(this, cd); + importWizDialog.setVisible(true); + CharacterData newCd = importWizDialog.getImportedCharacterData(); + if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_CREATED) { + + // 作成されたプロファイルを一覧に追加する. + characterListModel.addElement(newCd); + + } else if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) { - // 作成されたプロファイルを一覧に追加する. - characterListModel.addElement(newCd); + // 更新されたプロファイルを通知する + MainFrame.notifyImportedPartsOrFavorites(cd, newCd, this); + } + + } catch (Exception ex) { + ErrorMessageHelper.showErrorDialog(this, ex); } } diff --git a/src/charactermanaj/util/DesktopUtilities.java b/src/charactermanaj/util/DesktopUtilities.java index 19ddf06..c80609c 100644 --- a/src/charactermanaj/util/DesktopUtilities.java +++ b/src/charactermanaj/util/DesktopUtilities.java @@ -165,12 +165,6 @@ public class DesktopUtilities { */ public static void browse(final Component parent, final String url, final String description) { try { - browse(new URI(url)); - - } catch (Exception ex) { - ErrorMessageHelper.showErrorDialog(parent, ex); - } - try { URI helpURI = new URI(url); if (!DesktopUtilities.browse(helpURI) ){ diff --git a/src/charactermanaj/util/UserDataFactory.java b/src/charactermanaj/util/UserDataFactory.java index 1a7795c..b0d43ee 100644 --- a/src/charactermanaj/util/UserDataFactory.java +++ b/src/charactermanaj/util/UserDataFactory.java @@ -3,6 +3,8 @@ package charactermanaj.util; import java.io.File; import java.net.URI; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; /** @@ -10,6 +12,11 @@ import java.util.UUID; * @author seraphy */ public class UserDataFactory { + + /** + * ロガー + */ + private static final Logger logger = Logger.getLogger(UserDataFactory.class.getName()); /** * シングルトン @@ -57,7 +64,8 @@ public class UserDataFactory { // フォルダがなければ作成する. if (!userDataDir.exists()) { - userDataDir.mkdirs(); + boolean result = userDataDir.mkdirs(); + logger.log(Level.INFO, "makeDir: " + userDataDir + " /succeeded=" + result); } return userDataDir;